Blame rt/tst-cpuclock2.c

Packit 6c4009
/* Test program for process and thread CPU clocks.
Packit 6c4009
   Copyright (C) 2005-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <stdint.h>
Packit 6c4009
Packit 6c4009
#if (_POSIX_THREADS - 0) <= 0
Packit 6c4009
Packit 6c4009
# define TEST_FUNCTION 0
Packit 6c4009
Packit 6c4009
#else
Packit 6c4009
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <time.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <pthread.h>
Packit 6c4009
Packit 6c4009
static pthread_barrier_t barrier;
Packit 6c4009
Packit 6c4009
/* This function is intended to rack up both user and system time.  */
Packit 6c4009
static void *
Packit 6c4009
chew_cpu (void *arg)
Packit 6c4009
{
Packit 6c4009
  pthread_barrier_wait (&barrier);
Packit 6c4009
Packit 6c4009
  while (1)
Packit 6c4009
    {
Packit 6c4009
      static volatile char buf[4096];
Packit 6c4009
      for (int i = 0; i < 100; ++i)
Packit 6c4009
	for (size_t j = 0; j < sizeof buf; ++j)
Packit 6c4009
	  buf[j] = 0xaa;
Packit 6c4009
      int nullfd = open ("/dev/null", O_WRONLY);
Packit 6c4009
      for (int i = 0; i < 100; ++i)
Packit 6c4009
	for (size_t j = 0; j < sizeof buf; ++j)
Packit 6c4009
	  buf[j] = 0xbb;
Packit 6c4009
      write (nullfd, (char *) buf, sizeof buf);
Packit 6c4009
      close (nullfd);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static unsigned long long int
Packit 6c4009
tsdiff (const struct timespec *before, const struct timespec *after)
Packit 6c4009
{
Packit 6c4009
  struct timespec diff = { .tv_sec = after->tv_sec - before->tv_sec,
Packit 6c4009
			   .tv_nsec = after->tv_nsec - before->tv_nsec };
Packit 6c4009
  while (diff.tv_nsec < 0)
Packit 6c4009
    {
Packit 6c4009
      --diff.tv_sec;
Packit 6c4009
      diff.tv_nsec += 1000000000;
Packit 6c4009
    }
Packit 6c4009
  return diff.tv_sec * 1000000000ULL + diff.tv_nsec;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static unsigned long long int
Packit 6c4009
test_nanosleep (clockid_t clock, const char *which,
Packit 6c4009
		const struct timespec *before, int *bad)
Packit 6c4009
{
Packit 6c4009
  const struct timespec sleeptime = { .tv_nsec = 100000000 };
Packit 6c4009
  int e = clock_nanosleep (clock, 0, &sleeptime, NULL);
Packit 6c4009
  if (e == EINVAL || e == ENOTSUP || e == ENOSYS)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_nanosleep not supported for %s CPU clock: %s\n",
Packit 6c4009
	      which, strerror (e));
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
  if (e != 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_nanosleep on %s CPU clock: %s\n", which, strerror (e));
Packit 6c4009
      *bad = 1;
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct timespec after;
Packit 6c4009
  if (clock_gettime (clock, &after) < 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_gettime on %s CPU clock %lx => %s\n",
Packit 6c4009
	      which, (unsigned long int) clock, strerror (errno));
Packit 6c4009
      *bad = 1;
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  unsigned long long int diff = tsdiff (before, &after);
Packit 6c4009
  if (diff < sleeptime.tv_nsec || diff > sleeptime.tv_nsec * 2)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_nanosleep on %s slept %llu (outside reasonable range)\n",
Packit 6c4009
	      which, diff);
Packit 6c4009
      *bad = 1;
Packit 6c4009
      return diff;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct timespec sleeptimeabs = sleeptime;
Packit 6c4009
  sleeptimeabs.tv_sec += after.tv_sec;
Packit 6c4009
  sleeptimeabs.tv_nsec += after.tv_nsec;
Packit 6c4009
  while (sleeptimeabs.tv_nsec >= 1000000000)
Packit 6c4009
    {
Packit 6c4009
      ++sleeptimeabs.tv_sec;
Packit 6c4009
      sleeptimeabs.tv_nsec -= 1000000000;
Packit 6c4009
    }
Packit 6c4009
  e = clock_nanosleep (clock, TIMER_ABSTIME, &sleeptimeabs, NULL);
Packit 6c4009
  if (e != 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("absolute clock_nanosleep on %s CPU clock: %s\n",
Packit 6c4009
	      which, strerror (e));
Packit 6c4009
      *bad = 1;
Packit 6c4009
      return diff;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct timespec afterabs;
Packit 6c4009
  if (clock_gettime (clock, &afterabs) < 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_gettime on %s CPU clock %lx => %s\n",
Packit 6c4009
	      which, (unsigned long int) clock, strerror (errno));
Packit 6c4009
      *bad = 1;
Packit 6c4009
      return diff;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  unsigned long long int sleepdiff = tsdiff (&sleeptimeabs, &afterabs);
Packit 6c4009
  if (sleepdiff > sleeptime.tv_nsec)
Packit 6c4009
    {
Packit 6c4009
      printf ("\
Packit 6c4009
absolute clock_nanosleep on %s %llu past target (outside reasonable range)\n",
Packit 6c4009
	      which, sleepdiff);
Packit 6c4009
      *bad = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  unsigned long long int diffabs = tsdiff (&after, &afterabs);
Packit 6c4009
  if (diffabs < sleeptime.tv_nsec || diffabs > sleeptime.tv_nsec * 2)
Packit 6c4009
    {
Packit 6c4009
      printf ("\
Packit 6c4009
absolute clock_nanosleep on %s slept %llu (outside reasonable range)\n",
Packit 6c4009
	      which, diffabs);
Packit 6c4009
      *bad = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return diff + diffabs;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
do_test (void)
Packit 6c4009
{
Packit 6c4009
  int result = 0;
Packit 6c4009
  clockid_t process_clock, th_clock, my_thread_clock;
Packit 6c4009
  int e;
Packit 6c4009
  pthread_t th;
Packit 6c4009
Packit 6c4009
  e = clock_getcpuclockid (0, &process_clock);
Packit 6c4009
  if (e != 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_getcpuclockid on self => %s\n", strerror (e));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  e = pthread_getcpuclockid (pthread_self (), &my_thread_clock);
Packit 6c4009
  if (e != 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("pthread_getcpuclockid on self => %s\n", strerror (e));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* This is a kludge.  This test fails if the semantics of thread and
Packit 6c4009
     process clocks are wrong.  The old code using hp-timing without kernel
Packit 6c4009
     support has bogus semantics if there are context switches.  We don't
Packit 6c4009
     fail to report failure when the proper functionality is not available
Packit 6c4009
     in the kernel.  It so happens that Linux kernels without correct CPU
Packit 6c4009
     clock support also lack CPU timer support, so we use use that to guess
Packit 6c4009
     that we are using the bogus code and not test it.  */
Packit 6c4009
  timer_t t;
Packit 6c4009
  if (timer_create (my_thread_clock, NULL, &t) != 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("timer_create: %m\n");
Packit 6c4009
      puts ("No support for CPU clocks with good semantics, skipping test");
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
  timer_delete (t);
Packit 6c4009
Packit 6c4009
Packit 6c4009
  pthread_barrier_init (&barrier, NULL, 2);
Packit 6c4009
Packit 6c4009
  e = pthread_create (&th, NULL, chew_cpu, NULL);
Packit 6c4009
  if (e != 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("pthread_create: %s\n", strerror (e));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  e = pthread_getcpuclockid (th, &th_clock);
Packit 6c4009
  if (e == ENOENT || e == ENOSYS || e == ENOTSUP)
Packit 6c4009
    {
Packit 6c4009
      puts ("pthread_getcpuclockid does not support other threads");
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  pthread_barrier_wait (&barrier);
Packit 6c4009
Packit 6c4009
  struct timespec res;
Packit 6c4009
  if (clock_getres (th_clock, &res) < 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_getres on live thread clock %lx => %s\n",
Packit 6c4009
	      (unsigned long int) th_clock, strerror (errno));
Packit 6c4009
      result = 1;
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
  printf ("live thread clock %lx resolution %ju.%.9ju\n",
Packit 6c4009
	  (unsigned long int) th_clock,
Packit 6c4009
	  (uintmax_t) res.tv_sec, (uintmax_t) res.tv_nsec);
Packit 6c4009
Packit 6c4009
  struct timespec process_before, process_after;
Packit 6c4009
  if (clock_gettime (process_clock, &process_before) < 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_gettime on process clock %lx => %s\n",
Packit 6c4009
	      (unsigned long int) process_clock, strerror (errno));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  struct timespec before, after;
Packit 6c4009
  if (clock_gettime (th_clock, &before) < 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_gettime on live thread clock %lx => %s\n",
Packit 6c4009
	      (unsigned long int) th_clock, strerror (errno));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
  printf ("live thread before sleep => %ju.%.9ju\n",
Packit 6c4009
	  (uintmax_t) before.tv_sec, (uintmax_t) before.tv_nsec);
Packit 6c4009
Packit 6c4009
  struct timespec me_before, me_after;
Packit 6c4009
  if (clock_gettime (my_thread_clock, &me_before) < 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_gettime on self thread clock %lx => %s\n",
Packit 6c4009
	      (unsigned long int) my_thread_clock, strerror (errno));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
  printf ("self thread before sleep => %ju.%.9ju\n",
Packit 6c4009
	  (uintmax_t) me_before.tv_sec, (uintmax_t) me_before.tv_nsec);
Packit 6c4009
Packit 6c4009
  struct timespec sleeptime = { .tv_nsec = 500000000 };
Packit 6c4009
  if (nanosleep (&sleeptime, NULL) != 0)
Packit 6c4009
    {
Packit 6c4009
      perror ("nanosleep");
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (clock_gettime (th_clock, &after) < 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_gettime on live thread clock %lx => %s\n",
Packit 6c4009
	      (unsigned long int) th_clock, strerror (errno));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
  printf ("live thread after sleep => %ju.%.9ju\n",
Packit 6c4009
	  (uintmax_t) after.tv_sec, (uintmax_t) after.tv_nsec);
Packit 6c4009
Packit 6c4009
  if (clock_gettime (process_clock, &process_after) < 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_gettime on process clock %lx => %s\n",
Packit 6c4009
	      (unsigned long int) process_clock, strerror (errno));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (clock_gettime (my_thread_clock, &me_after) < 0)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_gettime on self thread clock %lx => %s\n",
Packit 6c4009
	      (unsigned long int) my_thread_clock, strerror (errno));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
  printf ("self thread after sleep => %ju.%.9ju\n",
Packit 6c4009
	  (uintmax_t) me_after.tv_sec, (uintmax_t) me_after.tv_nsec);
Packit 6c4009
Packit 6c4009
  unsigned long long int th_diff = tsdiff (&before, &after);
Packit 6c4009
  unsigned long long int pdiff = tsdiff (&process_before, &process_after);
Packit 6c4009
  unsigned long long int my_diff = tsdiff (&me_before, &me_after);
Packit 6c4009
Packit 6c4009
  if (th_diff < 100000000 || th_diff > 600000000)
Packit 6c4009
    {
Packit 6c4009
      printf ("live thread before - after %llu outside reasonable range\n",
Packit 6c4009
	      th_diff);
Packit 6c4009
      result = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (my_diff > 100000000)
Packit 6c4009
    {
Packit 6c4009
      printf ("self thread before - after %llu outside reasonable range\n",
Packit 6c4009
	      my_diff);
Packit 6c4009
      result = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (pdiff < th_diff)
Packit 6c4009
    {
Packit 6c4009
      printf ("process before - after %llu outside reasonable range (%llu)\n",
Packit 6c4009
	      pdiff, th_diff);
Packit 6c4009
      result = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  process_after.tv_nsec += test_nanosleep (th_clock, "live thread",
Packit 6c4009
					   &after, &result);
Packit 6c4009
  process_after.tv_nsec += test_nanosleep (process_clock, "process",
Packit 6c4009
					   &process_after, &result);
Packit 6c4009
  test_nanosleep (CLOCK_PROCESS_CPUTIME_ID,
Packit 6c4009
		  "PROCESS_CPUTIME_ID", &process_after, &result);
Packit 6c4009
Packit 6c4009
  pthread_cancel (th);
Packit 6c4009
Packit 6c4009
  e = clock_nanosleep (CLOCK_THREAD_CPUTIME_ID, 0, &sleeptime, NULL);
Packit 6c4009
  if (e != EINVAL)
Packit 6c4009
    {
Packit 6c4009
      printf ("clock_nanosleep CLOCK_THREAD_CPUTIME_ID: %s\n",
Packit 6c4009
	      strerror (e));
Packit 6c4009
      result = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
# define TIMEOUT 8
Packit 6c4009
# define TEST_FUNCTION do_test ()
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
#include "../test-skeleton.c"