1 /* Test program for process and thread CPU clocks.
2 Copyright (C) 2005-2022 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
24 static int
do_test()25 do_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 static pthread_barrier_t barrier;
41
42 /* This function is intended to rack up both user and system time. */
43 static void *
chew_cpu(void * arg)44 chew_cpu (void *arg)
45 {
46 pthread_barrier_wait (&barrier);
47
48 while (1)
49 {
50 static volatile char buf[4096];
51 for (int i = 0; i < 100; ++i)
52 for (size_t j = 0; j < sizeof buf; ++j)
53 buf[j] = 0xaa;
54 int nullfd = open ("/dev/null", O_WRONLY);
55 for (int i = 0; i < 100; ++i)
56 for (size_t j = 0; j < sizeof buf; ++j)
57 buf[j] = 0xbb;
58 write (nullfd, (char *) buf, sizeof buf);
59 close (nullfd);
60 }
61
62 return NULL;
63 }
64
65 static void
test_nanosleep(clockid_t clock,const char * which,int * bad)66 test_nanosleep (clockid_t clock, const char *which,
67 int *bad)
68 {
69 const struct timespec sleeptime = { .tv_nsec = 100000000 };
70 int e = clock_nanosleep (clock, 0, &sleeptime, NULL);
71 if (e == EINVAL || e == ENOTSUP || e == ENOSYS)
72 {
73 printf ("clock_nanosleep not supported for %s CPU clock: %s\n",
74 which, strerror (e));
75 return;
76 }
77 if (e != 0)
78 {
79 printf ("clock_nanosleep on %s CPU clock: %s\n", which, strerror (e));
80 *bad = 1;
81 return;
82 }
83
84 struct timespec after;
85 if (clock_gettime (clock, &after) < 0)
86 {
87 printf ("clock_gettime on %s CPU clock %lx => %s\n",
88 which, (unsigned long int) clock, strerror (errno));
89 *bad = 1;
90 return;
91 }
92
93 struct timespec sleeptimeabs = sleeptime;
94 sleeptimeabs.tv_sec += after.tv_sec;
95 sleeptimeabs.tv_nsec += after.tv_nsec;
96 while (sleeptimeabs.tv_nsec >= 1000000000)
97 {
98 ++sleeptimeabs.tv_sec;
99 sleeptimeabs.tv_nsec -= 1000000000;
100 }
101 e = clock_nanosleep (clock, TIMER_ABSTIME, &sleeptimeabs, NULL);
102 if (e != 0)
103 {
104 printf ("absolute clock_nanosleep on %s CPU clock: %s\n",
105 which, strerror (e));
106 *bad = 1;
107 return;
108 }
109
110 struct timespec afterabs;
111 if (clock_gettime (clock, &afterabs) < 0)
112 {
113 printf ("clock_gettime on %s CPU clock %lx => %s\n",
114 which, (unsigned long int) clock, strerror (errno));
115 *bad = 1;
116 return;
117 }
118
119 return;
120 }
121
122
123
124 static int
do_test(void)125 do_test (void)
126 {
127 int result = 0;
128 clockid_t process_clock, th_clock, my_thread_clock;
129 int e;
130 pthread_t th;
131
132 e = clock_getcpuclockid (0, &process_clock);
133 if (e != 0)
134 {
135 printf ("clock_getcpuclockid on self => %s\n", strerror (e));
136 return 1;
137 }
138
139 e = pthread_getcpuclockid (pthread_self (), &my_thread_clock);
140 if (e != 0)
141 {
142 printf ("pthread_getcpuclockid on self => %s\n", strerror (e));
143 return 1;
144 }
145
146 /* This is a kludge. This test fails if the semantics of thread and
147 process clocks are wrong. The old code using hp-timing without kernel
148 support has bogus semantics if there are context switches. We don't
149 fail to report failure when the proper functionality is not available
150 in the kernel. It so happens that Linux kernels without correct CPU
151 clock support also lack CPU timer support, so we use use that to guess
152 that we are using the bogus code and not test it. */
153 timer_t t;
154 if (timer_create (my_thread_clock, NULL, &t) != 0)
155 {
156 printf ("timer_create: %m\n");
157 puts ("No support for CPU clocks with good semantics, skipping test");
158 return 0;
159 }
160 timer_delete (t);
161
162
163 pthread_barrier_init (&barrier, NULL, 2);
164
165 e = pthread_create (&th, NULL, chew_cpu, NULL);
166 if (e != 0)
167 {
168 printf ("pthread_create: %s\n", strerror (e));
169 return 1;
170 }
171
172 e = pthread_getcpuclockid (th, &th_clock);
173 if (e == ENOENT || e == ENOSYS || e == ENOTSUP)
174 {
175 puts ("pthread_getcpuclockid does not support other threads");
176 return 1;
177 }
178
179 pthread_barrier_wait (&barrier);
180
181 struct timespec res;
182 if (clock_getres (th_clock, &res) < 0)
183 {
184 printf ("clock_getres on live thread clock %lx => %s\n",
185 (unsigned long int) th_clock, strerror (errno));
186 result = 1;
187 return 1;
188 }
189 printf ("live thread clock %lx resolution %ju.%.9ju\n",
190 (unsigned long int) th_clock,
191 (uintmax_t) res.tv_sec, (uintmax_t) res.tv_nsec);
192
193 struct timespec process_before, process_after;
194 if (clock_gettime (process_clock, &process_before) < 0)
195 {
196 printf ("clock_gettime on process clock %lx => %s\n",
197 (unsigned long int) process_clock, strerror (errno));
198 return 1;
199 }
200
201 struct timespec before, after;
202 if (clock_gettime (th_clock, &before) < 0)
203 {
204 printf ("clock_gettime on live thread clock %lx => %s\n",
205 (unsigned long int) th_clock, strerror (errno));
206 return 1;
207 }
208 printf ("live thread before sleep => %ju.%.9ju\n",
209 (uintmax_t) before.tv_sec, (uintmax_t) before.tv_nsec);
210
211 struct timespec me_before, me_after;
212 if (clock_gettime (my_thread_clock, &me_before) < 0)
213 {
214 printf ("clock_gettime on self thread clock %lx => %s\n",
215 (unsigned long int) my_thread_clock, strerror (errno));
216 return 1;
217 }
218 printf ("self thread before sleep => %ju.%.9ju\n",
219 (uintmax_t) me_before.tv_sec, (uintmax_t) me_before.tv_nsec);
220
221 struct timespec sleeptime = { .tv_nsec = 500000000 };
222 if (nanosleep (&sleeptime, NULL) != 0)
223 {
224 perror ("nanosleep");
225 return 1;
226 }
227
228 if (clock_gettime (th_clock, &after) < 0)
229 {
230 printf ("clock_gettime on live thread clock %lx => %s\n",
231 (unsigned long int) th_clock, strerror (errno));
232 return 1;
233 }
234 printf ("live thread after sleep => %ju.%.9ju\n",
235 (uintmax_t) after.tv_sec, (uintmax_t) after.tv_nsec);
236
237 if (clock_gettime (process_clock, &process_after) < 0)
238 {
239 printf ("clock_gettime on process clock %lx => %s\n",
240 (unsigned long int) process_clock, strerror (errno));
241 return 1;
242 }
243
244 if (clock_gettime (my_thread_clock, &me_after) < 0)
245 {
246 printf ("clock_gettime on self thread clock %lx => %s\n",
247 (unsigned long int) my_thread_clock, strerror (errno));
248 return 1;
249 }
250 printf ("self thread after sleep => %ju.%.9ju\n",
251 (uintmax_t) me_after.tv_sec, (uintmax_t) me_after.tv_nsec);
252
253 test_nanosleep (th_clock, "live thread",
254 &result);
255 test_nanosleep (process_clock, "process",
256 &result);
257 test_nanosleep (CLOCK_PROCESS_CPUTIME_ID,
258 "PROCESS_CPUTIME_ID", &result);
259
260 pthread_cancel (th);
261
262 e = clock_nanosleep (CLOCK_THREAD_CPUTIME_ID, 0, &sleeptime, NULL);
263 if (e != EINVAL)
264 {
265 printf ("clock_nanosleep CLOCK_THREAD_CPUTIME_ID: %s\n",
266 strerror (e));
267 result = 1;
268 }
269
270 return result;
271 }
272 #endif
273
274 #include <support/test-driver.c>
275