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 |
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 * |
44 | chew_cpu (void *arg) |
45 | { |
46 | pthread_barrier_wait (barrier: &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 (file: "/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 (fd: nullfd); |
60 | } |
61 | |
62 | return NULL; |
63 | } |
64 | |
65 | static void |
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_id: clock, flags: 0, req: &sleeptime, NULL); |
71 | if (e == EINVAL || e == ENOTSUP || e == ENOSYS) |
72 | { |
73 | printf (format: "clock_nanosleep not supported for %s CPU clock: %s\n" , |
74 | which, strerror (errnum: e)); |
75 | return; |
76 | } |
77 | if (e != 0) |
78 | { |
79 | printf (format: "clock_nanosleep on %s CPU clock: %s\n" , which, strerror (errnum: e)); |
80 | *bad = 1; |
81 | return; |
82 | } |
83 | |
84 | struct timespec after; |
85 | if (clock_gettime (clock_id: clock, tp: &after) < 0) |
86 | { |
87 | printf (format: "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_id: clock, TIMER_ABSTIME, req: &sleeptimeabs, NULL); |
102 | if (e != 0) |
103 | { |
104 | printf (format: "absolute clock_nanosleep on %s CPU clock: %s\n" , |
105 | which, strerror (errnum: e)); |
106 | *bad = 1; |
107 | return; |
108 | } |
109 | |
110 | struct timespec afterabs; |
111 | if (clock_gettime (clock_id: clock, tp: &afterabs) < 0) |
112 | { |
113 | printf (format: "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 |
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 (pid: 0, clock_id: &process_clock); |
133 | if (e != 0) |
134 | { |
135 | printf (format: "clock_getcpuclockid on self => %s\n" , strerror (errnum: e)); |
136 | return 1; |
137 | } |
138 | |
139 | e = pthread_getcpuclockid (thread_id: pthread_self (), clock_id: &my_thread_clock); |
140 | if (e != 0) |
141 | { |
142 | printf (format: "pthread_getcpuclockid on self => %s\n" , strerror (errnum: 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 (clock_id: my_thread_clock, NULL, timerid: &t) != 0) |
155 | { |
156 | printf (format: "timer_create: %m\n" ); |
157 | puts (s: "No support for CPU clocks with good semantics, skipping test" ); |
158 | return 0; |
159 | } |
160 | timer_delete (timerid: t); |
161 | |
162 | |
163 | pthread_barrier_init (barrier: &barrier, NULL, count: 2); |
164 | |
165 | e = pthread_create (newthread: &th, NULL, start_routine: chew_cpu, NULL); |
166 | if (e != 0) |
167 | { |
168 | printf (format: "pthread_create: %s\n" , strerror (errnum: e)); |
169 | return 1; |
170 | } |
171 | |
172 | e = pthread_getcpuclockid (thread_id: th, clock_id: &th_clock); |
173 | if (e == ENOENT || e == ENOSYS || e == ENOTSUP) |
174 | { |
175 | puts (s: "pthread_getcpuclockid does not support other threads" ); |
176 | return 1; |
177 | } |
178 | |
179 | pthread_barrier_wait (barrier: &barrier); |
180 | |
181 | struct timespec res; |
182 | if (clock_getres (clock_id: th_clock, res: &res) < 0) |
183 | { |
184 | printf (format: "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 (format: "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 (clock_id: process_clock, tp: &process_before) < 0) |
195 | { |
196 | printf (format: "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 (clock_id: th_clock, tp: &before) < 0) |
203 | { |
204 | printf (format: "clock_gettime on live thread clock %lx => %s\n" , |
205 | (unsigned long int) th_clock, strerror (errno)); |
206 | return 1; |
207 | } |
208 | printf (format: "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 (clock_id: my_thread_clock, tp: &me_before) < 0) |
213 | { |
214 | printf (format: "clock_gettime on self thread clock %lx => %s\n" , |
215 | (unsigned long int) my_thread_clock, strerror (errno)); |
216 | return 1; |
217 | } |
218 | printf (format: "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 (requested_time: &sleeptime, NULL) != 0) |
223 | { |
224 | perror ("nanosleep" ); |
225 | return 1; |
226 | } |
227 | |
228 | if (clock_gettime (clock_id: th_clock, tp: &after) < 0) |
229 | { |
230 | printf (format: "clock_gettime on live thread clock %lx => %s\n" , |
231 | (unsigned long int) th_clock, strerror (errno)); |
232 | return 1; |
233 | } |
234 | printf (format: "live thread after sleep => %ju.%.9ju\n" , |
235 | (uintmax_t) after.tv_sec, (uintmax_t) after.tv_nsec); |
236 | |
237 | if (clock_gettime (clock_id: process_clock, tp: &process_after) < 0) |
238 | { |
239 | printf (format: "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 (clock_id: my_thread_clock, tp: &me_after) < 0) |
245 | { |
246 | printf (format: "clock_gettime on self thread clock %lx => %s\n" , |
247 | (unsigned long int) my_thread_clock, strerror (errno)); |
248 | return 1; |
249 | } |
250 | printf (format: "self thread after sleep => %ju.%.9ju\n" , |
251 | (uintmax_t) me_after.tv_sec, (uintmax_t) me_after.tv_nsec); |
252 | |
253 | test_nanosleep (clock: th_clock, which: "live thread" , |
254 | bad: &result); |
255 | test_nanosleep (clock: process_clock, which: "process" , |
256 | bad: &result); |
257 | test_nanosleep (CLOCK_PROCESS_CPUTIME_ID, |
258 | which: "PROCESS_CPUTIME_ID" , bad: &result); |
259 | |
260 | pthread_cancel (th: th); |
261 | |
262 | e = clock_nanosleep (CLOCK_THREAD_CPUTIME_ID, flags: 0, req: &sleeptime, NULL); |
263 | if (e != EINVAL) |
264 | { |
265 | printf (format: "clock_nanosleep CLOCK_THREAD_CPUTIME_ID: %s\n" , |
266 | strerror (errnum: e)); |
267 | result = 1; |
268 | } |
269 | |
270 | return result; |
271 | } |
272 | #endif |
273 | |
274 | #include <support/test-driver.c> |
275 | |