Back to index

glibc  2.9
tst-cpuclock2.c
Go to the documentation of this file.
00001 /* Test program for process and thread CPU clocks.
00002    Copyright (C) 2005 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <unistd.h>
00021 
00022 #if (_POSIX_THREADS - 0) <= 0
00023 
00024 # define TEST_FUNCTION 0
00025 
00026 #else
00027 
00028 #include <stdio.h>
00029 #include <stdlib.h>
00030 #include <time.h>
00031 #include <fcntl.h>
00032 #include <string.h>
00033 #include <errno.h>
00034 #include <pthread.h>
00035 
00036 static pthread_barrier_t barrier;
00037 
00038 /* This function is intended to rack up both user and system time.  */
00039 static void *
00040 chew_cpu (void *arg)
00041 {
00042   pthread_barrier_wait (&barrier);
00043 
00044   while (1)
00045     {
00046       static volatile char buf[4096];
00047       for (int i = 0; i < 100; ++i)
00048        for (size_t j = 0; j < sizeof buf; ++j)
00049          buf[j] = 0xaa;
00050       int nullfd = open ("/dev/null", O_WRONLY);
00051       for (int i = 0; i < 100; ++i)
00052        for (size_t j = 0; j < sizeof buf; ++j)
00053          buf[j] = 0xbb;
00054       write (nullfd, (char *) buf, sizeof buf);
00055       close (nullfd);
00056     }
00057 
00058   return NULL;
00059 }
00060 
00061 static unsigned long long int
00062 tsdiff (const struct timespec *before, const struct timespec *after)
00063 {
00064   struct timespec diff = { .tv_sec = after->tv_sec - before->tv_sec,
00065                         .tv_nsec = after->tv_nsec - before->tv_nsec };
00066   while (diff.tv_nsec < 0)
00067     {
00068       --diff.tv_sec;
00069       diff.tv_nsec += 1000000000;
00070     }
00071   return diff.tv_sec * 1000000000ULL + diff.tv_nsec;
00072 }
00073 
00074 static unsigned long long int
00075 test_nanosleep (clockid_t clock, const char *which,
00076               const struct timespec *before, int *bad)
00077 {
00078   const struct timespec sleeptime = { .tv_nsec = 100000000 };
00079   int e = clock_nanosleep (clock, 0, &sleeptime, NULL);
00080   if (e == EINVAL || e == ENOTSUP || e == ENOSYS)
00081     {
00082       printf ("clock_nanosleep not supported for %s CPU clock: %s\n",
00083              which, strerror (e));
00084       return 0;
00085     }
00086   if (e != 0)
00087     {
00088       printf ("clock_nanosleep on %s CPU clock: %s\n", which, strerror (e));
00089       *bad = 1;
00090       return 0;
00091     }
00092 
00093   struct timespec after;
00094   if (clock_gettime (clock, &after) < 0)
00095     {
00096       printf ("clock_gettime on %s CPU clock %lx => %s\n",
00097              which, (unsigned long int) clock, strerror (errno));
00098       *bad = 1;
00099       return 0;
00100     }
00101 
00102   unsigned long long int diff = tsdiff (before, &after);
00103   if (diff < sleeptime.tv_nsec || diff > sleeptime.tv_nsec * 2)
00104     {
00105       printf ("clock_nanosleep on %s slept %llu (outside reasonable range)\n",
00106              which, diff);
00107       *bad = 1;
00108       return diff;
00109     }
00110 
00111   struct timespec sleeptimeabs = sleeptime;
00112   sleeptimeabs.tv_sec += after.tv_sec;
00113   sleeptimeabs.tv_nsec += after.tv_nsec;
00114   while (sleeptimeabs.tv_nsec > 1000000000)
00115     {
00116       ++sleeptimeabs.tv_sec;
00117       sleeptimeabs.tv_nsec -= 1000000000;
00118     }
00119   e = clock_nanosleep (clock, TIMER_ABSTIME, &sleeptimeabs, NULL);
00120   if (e != 0)
00121     {
00122       printf ("absolute clock_nanosleep on %s CPU clock: %s\n",
00123              which, strerror (e));
00124       *bad = 1;
00125       return diff;
00126     }
00127 
00128   struct timespec afterabs;
00129   if (clock_gettime (clock, &afterabs) < 0)
00130     {
00131       printf ("clock_gettime on %s CPU clock %lx => %s\n",
00132              which, (unsigned long int) clock, strerror (errno));
00133       *bad = 1;
00134       return diff;
00135     }
00136 
00137   unsigned long long int sleepdiff = tsdiff (&sleeptimeabs, &afterabs);
00138   if (sleepdiff > sleeptime.tv_nsec)
00139     {
00140       printf ("\
00141 absolute clock_nanosleep on %s %llu past target (outside reasonable range)\n",
00142              which, sleepdiff);
00143       *bad = 1;
00144     }
00145 
00146   unsigned long long int diffabs = tsdiff (&after, &afterabs);
00147   if (diffabs < sleeptime.tv_nsec || diffabs > sleeptime.tv_nsec * 2)
00148     {
00149       printf ("\
00150 absolute clock_nanosleep on %s slept %llu (outside reasonable range)\n",
00151              which, diffabs);
00152       *bad = 1;
00153     }
00154 
00155   return diff + diffabs;
00156 }
00157 
00158 
00159 
00160 static int
00161 do_test (void)
00162 {
00163   int result = 0;
00164   clockid_t process_clock, th_clock, my_thread_clock;
00165   int e;
00166   pthread_t th;
00167 
00168   e = clock_getcpuclockid (0, &process_clock);
00169   if (e != 0)
00170     {
00171       printf ("clock_getcpuclockid on self => %s\n", strerror (e));
00172       return 1;
00173     }
00174 
00175   e = pthread_getcpuclockid (pthread_self (), &my_thread_clock);
00176   if (e != 0)
00177     {
00178       printf ("pthread_getcpuclockid on self => %s\n", strerror (e));
00179       return 1;
00180     }
00181 
00182   /* This is a kludge.  This test fails if the semantics of thread and
00183      process clocks are wrong.  The old code using hp-timing without kernel
00184      support has bogus semantics if there are context switches.  We don't
00185      fail to report failure when the proper functionality is not available
00186      in the kernel.  It so happens that Linux kernels without correct CPU
00187      clock support also lack CPU timer support, so we use use that to guess
00188      that we are using the bogus code and not test it.  */
00189   timer_t t;
00190   if (timer_create (my_thread_clock, NULL, &t) != 0)
00191     {
00192       printf ("timer_create: %m\n");
00193       puts ("No support for CPU clocks with good semantics, skipping test");
00194       return 0;
00195     }
00196   timer_delete (t);
00197 
00198 
00199   pthread_barrier_init (&barrier, NULL, 2);
00200 
00201   e = pthread_create (&th, NULL, chew_cpu, NULL);
00202   if (e != 0)
00203     {
00204       printf ("pthread_create: %s\n", strerror (e));
00205       return 1;
00206     }
00207 
00208   e = pthread_getcpuclockid (th, &th_clock);
00209   if (e == ENOENT || e == ENOSYS || e == ENOTSUP)
00210     {
00211       puts ("pthread_getcpuclockid does not support other threads");
00212       return 1;
00213     }
00214 
00215   pthread_barrier_wait (&barrier);
00216 
00217   struct timespec res;
00218   if (clock_getres (th_clock, &res) < 0)
00219     {
00220       printf ("clock_getres on thread clock %lx => %s\n",
00221              (unsigned long int) th_clock, strerror (errno));
00222       result = 1;
00223       return 1;
00224     }
00225   printf ("live thread clock %lx resolution %lu.%.9lu\n",
00226          (unsigned long int) th_clock, res.tv_sec, res.tv_nsec);
00227 
00228   struct timespec process_before, process_after;
00229   if (clock_gettime (process_clock, &process_before) < 0)
00230     {
00231       printf ("clock_gettime on process clock %lx => %s\n",
00232              (unsigned long int) th_clock, strerror (errno));
00233       return 1;
00234     }
00235 
00236   struct timespec before, after;
00237   if (clock_gettime (th_clock, &before) < 0)
00238     {
00239       printf ("clock_gettime on live thread clock %lx => %s\n",
00240              (unsigned long int) th_clock, strerror (errno));
00241       return 1;
00242     }
00243   printf ("live thread before sleep => %lu.%.9lu\n",
00244          before.tv_sec, before.tv_nsec);
00245 
00246   struct timespec me_before, me_after;
00247   if (clock_gettime (my_thread_clock, &me_before) < 0)
00248     {
00249       printf ("clock_gettime on live thread clock %lx => %s\n",
00250              (unsigned long int) th_clock, strerror (errno));
00251       return 1;
00252     }
00253   printf ("self thread before sleep => %lu.%.9lu\n",
00254          me_before.tv_sec, me_before.tv_nsec);
00255 
00256   struct timespec sleeptime = { .tv_nsec = 500000000 };
00257   nanosleep (&sleeptime, NULL);
00258 
00259   if (clock_gettime (th_clock, &after) < 0)
00260     {
00261       printf ("clock_gettime on live thread clock %lx => %s\n",
00262              (unsigned long int) th_clock, strerror (errno));
00263       return 1;
00264     }
00265   printf ("live thread after sleep => %lu.%.9lu\n",
00266          after.tv_sec, after.tv_nsec);
00267 
00268   if (clock_gettime (process_clock, &process_after) < 0)
00269     {
00270       printf ("clock_gettime on process clock %lx => %s\n",
00271              (unsigned long int) th_clock, strerror (errno));
00272       return 1;
00273     }
00274 
00275   if (clock_gettime (my_thread_clock, &me_after) < 0)
00276     {
00277       printf ("clock_gettime on live thread clock %lx => %s\n",
00278              (unsigned long int) th_clock, strerror (errno));
00279       return 1;
00280     }
00281   printf ("self thread after sleep => %lu.%.9lu\n",
00282          me_after.tv_sec, me_after.tv_nsec);
00283 
00284   unsigned long long int th_diff = tsdiff (&before, &after);
00285   unsigned long long int pdiff = tsdiff (&process_before, &process_after);
00286   unsigned long long int my_diff = tsdiff (&me_before, &me_after);
00287 
00288   if (th_diff < 100000000 || th_diff > 600000000)
00289     {
00290       printf ("thread before - after %llu outside reasonable range\n",
00291              th_diff);
00292       result = 1;
00293     }
00294 
00295   if (my_diff > 100000000)
00296     {
00297       printf ("self thread before - after %llu outside reasonable range\n",
00298              my_diff);
00299       result = 1;
00300     }
00301 
00302   if (pdiff < th_diff)
00303     {
00304       printf ("process before - after %llu outside reasonable range (%llu)\n",
00305              pdiff, th_diff);
00306       result = 1;
00307     }
00308 
00309   process_after.tv_nsec += test_nanosleep (th_clock, "thread",
00310                                       &after, &result);
00311   process_after.tv_nsec += test_nanosleep (process_clock, "process",
00312                                       &process_after, &result);
00313   test_nanosleep (CLOCK_PROCESS_CPUTIME_ID,
00314                 "PROCESS_CPUTIME_ID", &process_after, &result);
00315 
00316   pthread_cancel (th);
00317 
00318   e = clock_nanosleep (CLOCK_THREAD_CPUTIME_ID, 0, &sleeptime, NULL);
00319   if (e != EINVAL)
00320     {
00321       printf ("clock_nanosleep CLOCK_THREAD_CPUTIME_ID: %s\n",
00322              strerror (e));
00323       result = 1;
00324     }
00325 
00326   return result;
00327 }
00328 # define TIMEOUT 8
00329 # define TEST_FUNCTION do_test ()
00330 #endif
00331 
00332 #include "../test-skeleton.c"