[BZ #2526, BZ #3138, BZ #3143]
[platform/upstream/glibc.git] / rt / tst-cpuclock2.c
1 /* Test program for process and thread CPU clocks.
2    Copyright (C) 2005 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, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <unistd.h>
21
22 #if (_POSIX_THREADS - 0) <= 0
23
24 # define TEST_FUNCTION 0
25
26 #else
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <time.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <pthread.h>
35
36 static pthread_barrier_t barrier;
37
38 /* This function is intended to rack up both user and system time.  */
39 static void *
40 chew_cpu (void *arg)
41 {
42   pthread_barrier_wait (&barrier);
43
44   while (1)
45     {
46       static volatile char buf[4096];
47       for (int i = 0; i < 100; ++i)
48         for (size_t j = 0; j < sizeof buf; ++j)
49           buf[j] = 0xaa;
50       int nullfd = open ("/dev/null", O_WRONLY);
51       for (int i = 0; i < 100; ++i)
52         for (size_t j = 0; j < sizeof buf; ++j)
53           buf[j] = 0xbb;
54       write (nullfd, (char *) buf, sizeof buf);
55       close (nullfd);
56     }
57
58   return NULL;
59 }
60
61 static unsigned long long int
62 tsdiff (const struct timespec *before, const struct timespec *after)
63 {
64   struct timespec diff = { .tv_sec = after->tv_sec - before->tv_sec,
65                            .tv_nsec = after->tv_nsec - before->tv_nsec };
66   while (diff.tv_nsec < 0)
67     {
68       --diff.tv_sec;
69       diff.tv_nsec += 1000000000;
70     }
71   return diff.tv_sec * 1000000000ULL + diff.tv_nsec;
72 }
73
74 static unsigned long long int
75 test_nanosleep (clockid_t clock, const char *which,
76                 const struct timespec *before, int *bad)
77 {
78   const struct timespec sleeptime = { .tv_nsec = 100000000 };
79   int e = clock_nanosleep (clock, 0, &sleeptime, NULL);
80   if (e == EINVAL || e == ENOTSUP || e == ENOSYS)
81     {
82       printf ("clock_nanosleep not supported for %s CPU clock: %s\n",
83               which, strerror (e));
84       return 0;
85     }
86   if (e != 0)
87     {
88       printf ("clock_nanosleep on %s CPU clock: %s\n", which, strerror (e));
89       *bad = 1;
90       return 0;
91     }
92
93   struct timespec after;
94   if (clock_gettime (clock, &after) < 0)
95     {
96       printf ("clock_gettime on %s CPU clock %lx => %s\n",
97               which, (unsigned long int) clock, strerror (errno));
98       *bad = 1;
99       return 0;
100     }
101
102   unsigned long long int diff = tsdiff (before, &after);
103   if (diff < sleeptime.tv_nsec || diff > sleeptime.tv_nsec * 2)
104     {
105       printf ("clock_nanosleep on %s slept %llu (outside reasonable range)\n",
106               which, diff);
107       *bad = 1;
108       return diff;
109     }
110
111   struct timespec sleeptimeabs = sleeptime;
112   sleeptimeabs.tv_sec += after.tv_sec;
113   sleeptimeabs.tv_nsec += after.tv_nsec;
114   while (sleeptimeabs.tv_nsec > 1000000000)
115     {
116       ++sleeptimeabs.tv_sec;
117       sleeptimeabs.tv_nsec -= 1000000000;
118     }
119   e = clock_nanosleep (clock, TIMER_ABSTIME, &sleeptimeabs, NULL);
120   if (e != 0)
121     {
122       printf ("absolute clock_nanosleep on %s CPU clock: %s\n",
123               which, strerror (e));
124       *bad = 1;
125       return diff;
126     }
127
128   struct timespec afterabs;
129   if (clock_gettime (clock, &afterabs) < 0)
130     {
131       printf ("clock_gettime on %s CPU clock %lx => %s\n",
132               which, (unsigned long int) clock, strerror (errno));
133       *bad = 1;
134       return diff;
135     }
136
137   unsigned long long int sleepdiff = tsdiff (&sleeptimeabs, &afterabs);
138   if (sleepdiff > sleeptime.tv_nsec)
139     {
140       printf ("\
141 absolute clock_nanosleep on %s %llu past target (outside reasonable range)\n",
142               which, sleepdiff);
143       *bad = 1;
144     }
145
146   unsigned long long int diffabs = tsdiff (&after, &afterabs);
147   if (diffabs < sleeptime.tv_nsec || diffabs > sleeptime.tv_nsec * 2)
148     {
149       printf ("\
150 absolute clock_nanosleep on %s slept %llu (outside reasonable range)\n",
151               which, diffabs);
152       *bad = 1;
153     }
154
155   return diff + diffabs;
156 }
157
158
159
160 static int
161 do_test (void)
162 {
163   int result = 0;
164   clockid_t process_clock, th_clock, my_thread_clock;
165   int e;
166   pthread_t th;
167
168   e = clock_getcpuclockid (0, &process_clock);
169   if (e != 0)
170     {
171       printf ("clock_getcpuclockid on self => %s\n", strerror (e));
172       return 1;
173     }
174
175   e = pthread_getcpuclockid (pthread_self (), &my_thread_clock);
176   if (e != 0)
177     {
178       printf ("pthread_getcpuclockid on self => %s\n", strerror (e));
179       return 1;
180     }
181
182   /* This is a kludge.  This test fails if the semantics of thread and
183      process clocks are wrong.  The old code using hp-timing without kernel
184      support has bogus semantics if there are context switches.  We don't
185      fail to report failure when the proper functionality is not available
186      in the kernel.  It so happens that Linux kernels without correct CPU
187      clock support also lack CPU timer support, so we use use that to guess
188      that we are using the bogus code and not test it.  */
189   timer_t t;
190   if (timer_create (my_thread_clock, NULL, &t) != 0)
191     {
192       printf ("timer_create: %m\n");
193       puts ("No support for CPU clocks with good semantics, skipping test");
194       return 0;
195     }
196   timer_delete (t);
197
198
199   pthread_barrier_init (&barrier, NULL, 2);
200
201   e = pthread_create (&th, NULL, chew_cpu, NULL);
202   if (e != 0)
203     {
204       printf ("pthread_create: %s\n", strerror (e));
205       return 1;
206     }
207
208   e = pthread_getcpuclockid (th, &th_clock);
209   if (e == ENOENT || e == ENOSYS || e == ENOTSUP)
210     {
211       puts ("pthread_getcpuclockid does not support other threads");
212       return 1;
213     }
214
215   pthread_barrier_wait (&barrier);
216
217   struct timespec res;
218   if (clock_getres (th_clock, &res) < 0)
219     {
220       printf ("clock_getres on thread clock %lx => %s\n",
221               (unsigned long int) th_clock, strerror (errno));
222       result = 1;
223       return 1;
224     }
225   printf ("live thread clock %lx resolution %lu.%.9lu\n",
226           (unsigned long int) th_clock, res.tv_sec, res.tv_nsec);
227
228   struct timespec process_before, process_after;
229   if (clock_gettime (process_clock, &process_before) < 0)
230     {
231       printf ("clock_gettime on process clock %lx => %s\n",
232               (unsigned long int) th_clock, strerror (errno));
233       return 1;
234     }
235
236   struct timespec before, after;
237   if (clock_gettime (th_clock, &before) < 0)
238     {
239       printf ("clock_gettime on live thread clock %lx => %s\n",
240               (unsigned long int) th_clock, strerror (errno));
241       return 1;
242     }
243   printf ("live thread before sleep => %lu.%.9lu\n",
244           before.tv_sec, before.tv_nsec);
245
246   struct timespec me_before, me_after;
247   if (clock_gettime (my_thread_clock, &me_before) < 0)
248     {
249       printf ("clock_gettime on live thread clock %lx => %s\n",
250               (unsigned long int) th_clock, strerror (errno));
251       return 1;
252     }
253   printf ("self thread before sleep => %lu.%.9lu\n",
254           me_before.tv_sec, me_before.tv_nsec);
255
256   struct timespec sleeptime = { .tv_nsec = 500000000 };
257   nanosleep (&sleeptime, NULL);
258
259   if (clock_gettime (th_clock, &after) < 0)
260     {
261       printf ("clock_gettime on live thread clock %lx => %s\n",
262               (unsigned long int) th_clock, strerror (errno));
263       return 1;
264     }
265   printf ("live thread after sleep => %lu.%.9lu\n",
266           after.tv_sec, after.tv_nsec);
267
268   if (clock_gettime (process_clock, &process_after) < 0)
269     {
270       printf ("clock_gettime on process clock %lx => %s\n",
271               (unsigned long int) th_clock, strerror (errno));
272       return 1;
273     }
274
275   if (clock_gettime (my_thread_clock, &me_after) < 0)
276     {
277       printf ("clock_gettime on live thread clock %lx => %s\n",
278               (unsigned long int) th_clock, strerror (errno));
279       return 1;
280     }
281   printf ("self thread after sleep => %lu.%.9lu\n",
282           me_after.tv_sec, me_after.tv_nsec);
283
284   unsigned long long int th_diff = tsdiff (&before, &after);
285   unsigned long long int pdiff = tsdiff (&process_before, &process_after);
286   unsigned long long int my_diff = tsdiff (&me_before, &me_after);
287
288   if (th_diff < 100000000 || th_diff > 600000000)
289     {
290       printf ("thread before - after %llu outside reasonable range\n",
291               th_diff);
292       result = 1;
293     }
294
295   if (my_diff > 100000000)
296     {
297       printf ("self thread before - after %llu outside reasonable range\n",
298               my_diff);
299       result = 1;
300     }
301
302   if (pdiff < th_diff)
303     {
304       printf ("process before - after %llu outside reasonable range (%llu)\n",
305               pdiff, th_diff);
306       result = 1;
307     }
308
309   process_after.tv_nsec += test_nanosleep (th_clock, "thread",
310                                            &after, &result);
311   process_after.tv_nsec += test_nanosleep (process_clock, "process",
312                                            &process_after, &result);
313   test_nanosleep (CLOCK_PROCESS_CPUTIME_ID,
314                   "PROCESS_CPUTIME_ID", &process_after, &result);
315
316   pthread_cancel (th);
317
318   e = clock_nanosleep (CLOCK_THREAD_CPUTIME_ID, 0, &sleeptime, NULL);
319   if (e != EINVAL)
320     {
321       printf ("clock_nanosleep CLOCK_THREAD_CPUTIME_ID: %s\n",
322               strerror (e));
323       result = 1;
324     }
325
326   return result;
327 }
328 # define TIMEOUT 8
329 # define TEST_FUNCTION do_test ()
330 #endif
331
332 #include "../test-skeleton.c"