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 | |
24 | static int |
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 | #include <support/xunistd.h> |
41 | |
42 | static pthread_barrier_t barrier; |
43 | |
44 | /* This function is intended to rack up both user and system time. */ |
45 | static void * |
46 | chew_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 | |
67 | static void |
68 | test_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 | |
126 | static int |
127 | do_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 | |