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