1/* Test program for process and thread CPU clocks.
2 Copyright (C) 2005-2024 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
18
19#include <unistd.h>
20#include <stdint.h>
21
22#if (_POSIX_THREADS - 0) <= 0
23
24static int
25do_test ()
26{
27 return 0;
28}
29
30#else
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <time.h>
35#include <fcntl.h>
36#include <string.h>
37#include <errno.h>
38#include <pthread.h>
39
40#include <support/xunistd.h>
41
42static pthread_barrier_t barrier;
43
44/* This function is intended to rack up both user and system time. */
45static void *
46chew_cpu (void *arg)
47{
48 pthread_barrier_wait (barrier: &barrier);
49
50 while (1)
51 {
52 static volatile char buf[4096];
53 for (int i = 0; i < 100; ++i)
54 for (size_t j = 0; j < sizeof buf; ++j)
55 buf[j] = 0xaa;
56 int nullfd = open (file: "/dev/null", O_WRONLY);
57 for (int i = 0; i < 100; ++i)
58 for (size_t j = 0; j < sizeof buf; ++j)
59 buf[j] = 0xbb;
60 xwrite (nullfd, (char *) buf, sizeof buf);
61 close (fd: nullfd);
62 }
63
64 return NULL;
65}
66
67static void
68test_nanosleep (clockid_t clock, const char *which,
69 int *bad)
70{
71 const struct timespec sleeptime = { .tv_nsec = 100000000 };
72 int e = clock_nanosleep (clock_id: clock, flags: 0, req: &sleeptime, NULL);
73 if (e == EINVAL || e == ENOTSUP || e == ENOSYS)
74 {
75 printf (format: "clock_nanosleep not supported for %s CPU clock: %s\n",
76 which, strerror (errnum: e));
77 return;
78 }
79 if (e != 0)
80 {
81 printf (format: "clock_nanosleep on %s CPU clock: %s\n", which, strerror (errnum: e));
82 *bad = 1;
83 return;
84 }
85
86 struct timespec after;
87 if (clock_gettime (clock_id: clock, tp: &after) < 0)
88 {
89 printf (format: "clock_gettime on %s CPU clock %lx => %s\n",
90 which, (unsigned long int) clock, strerror (errno));
91 *bad = 1;
92 return;
93 }
94
95 struct timespec sleeptimeabs = sleeptime;
96 sleeptimeabs.tv_sec += after.tv_sec;
97 sleeptimeabs.tv_nsec += after.tv_nsec;
98 while (sleeptimeabs.tv_nsec >= 1000000000)
99 {
100 ++sleeptimeabs.tv_sec;
101 sleeptimeabs.tv_nsec -= 1000000000;
102 }
103 e = clock_nanosleep (clock_id: clock, TIMER_ABSTIME, req: &sleeptimeabs, NULL);
104 if (e != 0)
105 {
106 printf (format: "absolute clock_nanosleep on %s CPU clock: %s\n",
107 which, strerror (errnum: e));
108 *bad = 1;
109 return;
110 }
111
112 struct timespec afterabs;
113 if (clock_gettime (clock_id: clock, tp: &afterabs) < 0)
114 {
115 printf (format: "clock_gettime on %s CPU clock %lx => %s\n",
116 which, (unsigned long int) clock, strerror (errno));
117 *bad = 1;
118 return;
119 }
120
121 return;
122}
123
124
125
126static int
127do_test (void)
128{
129 int result = 0;
130 clockid_t process_clock, th_clock, my_thread_clock;
131 int e;
132 pthread_t th;
133
134 e = clock_getcpuclockid (pid: 0, clock_id: &process_clock);
135 if (e != 0)
136 {
137 printf (format: "clock_getcpuclockid on self => %s\n", strerror (errnum: e));
138 return 1;
139 }
140
141 e = pthread_getcpuclockid (thread_id: pthread_self (), clock_id: &my_thread_clock);
142 if (e != 0)
143 {
144 printf (format: "pthread_getcpuclockid on self => %s\n", strerror (errnum: e));
145 return 1;
146 }
147
148 /* This is a kludge. This test fails if the semantics of thread and
149 process clocks are wrong. The old code using hp-timing without kernel
150 support has bogus semantics if there are context switches. We don't
151 fail to report failure when the proper functionality is not available
152 in the kernel. It so happens that Linux kernels without correct CPU
153 clock support also lack CPU timer support, so we use use that to guess
154 that we are using the bogus code and not test it. */
155 timer_t t;
156 if (timer_create (clock_id: my_thread_clock, NULL, timerid: &t) != 0)
157 {
158 printf (format: "timer_create: %m\n");
159 puts (s: "No support for CPU clocks with good semantics, skipping test");
160 return 0;
161 }
162 timer_delete (timerid: t);
163
164
165 pthread_barrier_init (barrier: &barrier, NULL, count: 2);
166
167 e = pthread_create (newthread: &th, NULL, start_routine: chew_cpu, NULL);
168 if (e != 0)
169 {
170 printf (format: "pthread_create: %s\n", strerror (errnum: e));
171 return 1;
172 }
173
174 e = pthread_getcpuclockid (thread_id: th, clock_id: &th_clock);
175 if (e == ENOENT || e == ENOSYS || e == ENOTSUP)
176 {
177 puts (s: "pthread_getcpuclockid does not support other threads");
178 return 1;
179 }
180
181 pthread_barrier_wait (barrier: &barrier);
182
183 struct timespec res;
184 if (clock_getres (clock_id: th_clock, res: &res) < 0)
185 {
186 printf (format: "clock_getres on live thread clock %lx => %s\n",
187 (unsigned long int) th_clock, strerror (errno));
188 result = 1;
189 return 1;
190 }
191 printf (format: "live thread clock %lx resolution %ju.%.9ju\n",
192 (unsigned long int) th_clock,
193 (uintmax_t) res.tv_sec, (uintmax_t) res.tv_nsec);
194
195 struct timespec process_before, process_after;
196 if (clock_gettime (clock_id: process_clock, tp: &process_before) < 0)
197 {
198 printf (format: "clock_gettime on process clock %lx => %s\n",
199 (unsigned long int) process_clock, strerror (errno));
200 return 1;
201 }
202
203 struct timespec before, after;
204 if (clock_gettime (clock_id: th_clock, tp: &before) < 0)
205 {
206 printf (format: "clock_gettime on live thread clock %lx => %s\n",
207 (unsigned long int) th_clock, strerror (errno));
208 return 1;
209 }
210 printf (format: "live thread before sleep => %ju.%.9ju\n",
211 (uintmax_t) before.tv_sec, (uintmax_t) before.tv_nsec);
212
213 struct timespec me_before, me_after;
214 if (clock_gettime (clock_id: my_thread_clock, tp: &me_before) < 0)
215 {
216 printf (format: "clock_gettime on self thread clock %lx => %s\n",
217 (unsigned long int) my_thread_clock, strerror (errno));
218 return 1;
219 }
220 printf (format: "self thread before sleep => %ju.%.9ju\n",
221 (uintmax_t) me_before.tv_sec, (uintmax_t) me_before.tv_nsec);
222
223 struct timespec sleeptime = { .tv_nsec = 500000000 };
224 if (nanosleep (requested_time: &sleeptime, NULL) != 0)
225 {
226 perror ("nanosleep");
227 return 1;
228 }
229
230 if (clock_gettime (clock_id: th_clock, tp: &after) < 0)
231 {
232 printf (format: "clock_gettime on live thread clock %lx => %s\n",
233 (unsigned long int) th_clock, strerror (errno));
234 return 1;
235 }
236 printf (format: "live thread after sleep => %ju.%.9ju\n",
237 (uintmax_t) after.tv_sec, (uintmax_t) after.tv_nsec);
238
239 if (clock_gettime (clock_id: process_clock, tp: &process_after) < 0)
240 {
241 printf (format: "clock_gettime on process clock %lx => %s\n",
242 (unsigned long int) process_clock, strerror (errno));
243 return 1;
244 }
245
246 if (clock_gettime (clock_id: my_thread_clock, tp: &me_after) < 0)
247 {
248 printf (format: "clock_gettime on self thread clock %lx => %s\n",
249 (unsigned long int) my_thread_clock, strerror (errno));
250 return 1;
251 }
252 printf (format: "self thread after sleep => %ju.%.9ju\n",
253 (uintmax_t) me_after.tv_sec, (uintmax_t) me_after.tv_nsec);
254
255 test_nanosleep (clock: th_clock, which: "live thread",
256 bad: &result);
257 test_nanosleep (clock: process_clock, which: "process",
258 bad: &result);
259 test_nanosleep (CLOCK_PROCESS_CPUTIME_ID,
260 which: "PROCESS_CPUTIME_ID", bad: &result);
261
262 pthread_cancel (th: th);
263
264 e = clock_nanosleep (CLOCK_THREAD_CPUTIME_ID, flags: 0, req: &sleeptime, NULL);
265 if (e != EINVAL)
266 {
267 printf (format: "clock_nanosleep CLOCK_THREAD_CPUTIME_ID: %s\n",
268 strerror (errnum: e));
269 result = 1;
270 }
271
272 return result;
273}
274#endif
275
276#include <support/test-driver.c>
277

source code of glibc/rt/tst-cpuclock2.c