static int do_test (void) { int result = 0; clockid_t cl; int e; pid_t dead_child, child; /* Fork a child and let it die, to give us a PID known not be valid (assuming PIDs don't wrap around during the test). */ { dead_child = fork (); if (dead_child == 0) _exit (0); if (dead_child < 0) { perror ("fork"); return 1; } int x; if (wait (&x) != dead_child) { perror ("wait"); return 2; } } /* POSIX says we should get ESRCH for this. */ e = clock_getcpuclockid (dead_child, &cl); if (e != ENOSYS && e != ESRCH && e != EPERM) { printf ("clock_getcpuclockid on dead PID %d => %s\n", dead_child, strerror (e)); result = 1; } /* Now give us a live child eating up CPU time. */ child = fork (); if (child == 0) { chew_cpu (); _exit (1); } if (child < 0) { perror ("fork"); return 1; } e = clock_getcpuclockid (child, &cl); if (e == EPERM) { puts ("clock_getcpuclockid does not support other processes"); goto done; } if (e != 0) { printf ("clock_getcpuclockid on live PID %d => %s\n", child, strerror (e)); result = 1; goto done; } const clockid_t child_clock = cl; struct timespec res; if (clock_getres (child_clock, &res) < 0) { printf ("clock_getres on live PID %d clock %lx => %s\n", child, (unsigned long int) child_clock, strerror (errno)); result = 1; goto done; } printf ("live PID %d clock %lx resolution %ju.%.9ju\n", child, (unsigned long int) child_clock, (uintmax_t) res.tv_sec, (uintmax_t) res.tv_nsec); struct timespec before, after; if (clock_gettime (child_clock, &before) < 0) { printf ("clock_gettime on live PID %d clock %lx => %s\n", child, (unsigned long int) child_clock, strerror (errno)); result = 1; goto done; } /* Should be close to 0.0. */ printf ("live PID %d before sleep => %ju.%.9ju\n", child, (uintmax_t) before.tv_sec, (uintmax_t) before.tv_nsec); struct timespec sleeptime = { .tv_nsec = 500000000 }; if (nanosleep (&sleeptime, NULL) != 0) { perror ("nanosleep"); result = 1; goto done; } if (clock_gettime (child_clock, &after) < 0) { printf ("clock_gettime on live PID %d clock %lx => %s\n", child, (unsigned long int) child_clock, strerror (errno)); result = 1; goto done; } /* Should be close to 0.5. */ printf ("live PID %d after sleep => %ju.%.9ju\n", child, (uintmax_t) after.tv_sec, (uintmax_t) after.tv_nsec); struct timespec diff = { .tv_sec = after.tv_sec - before.tv_sec, .tv_nsec = after.tv_nsec - before.tv_nsec }; if (diff.tv_nsec < 0) { --diff.tv_sec; diff.tv_nsec += 1000000000; } if (diff.tv_sec != 0 || diff.tv_nsec > 600000000 || diff.tv_nsec < 100000000) { printf ("before - after %ju.%.9ju outside reasonable range\n", (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); result = 1; } sleeptime.tv_nsec = 100000000; e = clock_nanosleep (child_clock, 0, &sleeptime, NULL); if (e == EINVAL || e == ENOTSUP || e == ENOSYS) { printf ("clock_nanosleep not supported for other process clock: %s\n", strerror (e)); } else if (e != 0) { printf ("clock_nanosleep on other process clock: %s\n", strerror (e)); result = 1; } else { struct timespec afterns; if (clock_gettime (child_clock, &afterns) < 0) { printf ("clock_gettime on live PID %d clock %lx => %s\n", child, (unsigned long int) child_clock, strerror (errno)); result = 1; } else { struct timespec d = { .tv_sec = afterns.tv_sec - after.tv_sec, .tv_nsec = afterns.tv_nsec - after.tv_nsec }; if (d.tv_nsec < 0) { --d.tv_sec; d.tv_nsec += 1000000000; } if (d.tv_sec > 0 || d.tv_nsec < sleeptime.tv_nsec || d.tv_nsec > sleeptime.tv_nsec * 2) { printf ("nanosleep time %ju.%.9ju outside reasonable range\n", (uintmax_t) d.tv_sec, (uintmax_t) d.tv_nsec); result = 1; } } } if (kill (child, SIGKILL) != 0) { perror ("kill"); result = 2; goto done; } /* Wait long enough to let the child finish dying. */ sleeptime.tv_nsec = 200000000; if (nanosleep (&sleeptime, NULL) != 0) { perror ("nanosleep"); result = 1; goto done; } struct timespec dead; if (clock_gettime (child_clock, &dead) < 0) { printf ("clock_gettime on dead PID %d clock %lx => %s\n", child, (unsigned long int) child_clock, strerror (errno)); result = 1; goto done; } /* Should be close to 0.6. */ printf ("dead PID %d => %ju.%.9ju\n", child, (uintmax_t) dead.tv_sec, (uintmax_t) dead.tv_nsec); diff.tv_sec = dead.tv_sec - after.tv_sec; diff.tv_nsec = dead.tv_nsec - after.tv_nsec; if (diff.tv_nsec < 0) { --diff.tv_sec; diff.tv_nsec += 1000000000; } if (diff.tv_sec != 0 || diff.tv_nsec > 200000000) { printf ("dead - after %ju.%.9ju outside reasonable range\n", (uintmax_t) diff.tv_sec, (uintmax_t) diff.tv_nsec); result = 1; } /* Now reap the child and verify that its clock is no longer valid. */ { int x; if (waitpid (child, &x, 0) != child) { perror ("waitpid"); result = 1; } } if (clock_gettime (child_clock, &dead) == 0) { printf ("clock_gettime on reaped PID %d clock %lx => %ju%.9ju\n", child, (unsigned long int) child_clock, (uintmax_t) dead.tv_sec, (uintmax_t) dead.tv_nsec); result = 1; } else { if (errno != EINVAL) result = 1; printf ("clock_gettime on reaped PID %d clock %lx => %s\n", child, (unsigned long int) child_clock, strerror (errno)); } if (clock_getres (child_clock, &dead) == 0) { printf ("clock_getres on reaped PID %d clock %lx => %ju%.9ju\n", child, (unsigned long int) child_clock, (uintmax_t) dead.tv_sec, (uintmax_t) dead.tv_nsec); result = 1; } else { if (errno != EINVAL) result = 1; printf ("clock_getres on reaped PID %d clock %lx => %s\n", child, (unsigned long int) child_clock, strerror (errno)); } return result; done: { if (kill (child, SIGKILL) != 0 && errno != ESRCH) { perror ("kill"); return 2; } int x; if (waitpid (child, &x, 0) != child && errno != ECHILD) { perror ("waitpid"); return 2; } } return result; }
static void spam_io(size_t ticks) { int fd = open("/dev/null", O_WRONLY); ssize_t i; for (i = ticks - 1; i >= 0; --i) { arm_desched_notification(); #if 0 sched_yield(); #elif 1 /* This is very very unlikely to desched us. */ //write(fd, i ? "." : "\n", 1); write(STDOUT_FILENO, i ? "." : "\n", 1); #elif 0 /* This has to desched us. */ system("sleep 1"); #else struct timespec req = { .tv_sec = 1, .tv_nsec = 0 }; struct timespec rem; nanosleep(&req, &rem); #endif disarm_desched_notification(); } close(fd); } static void child() { debug("child: execution info:\n" " child() = %p\n" " nr_switches = %u", child, nr_switches); if (IO == which) { debug(" spam_io() = %p, %u ticks", spam_io, io_ticks); } else { debug(" chew_cpu() = %p, %u ticks", chew_cpu, cpu_ticks); } counter_fd = open_counter(nr_switches); send_fd(counter_fd, child_socket); debug("child accepting ptrace"); ptrace(PTRACE_TRACEME, 0, 0, 0); raise(SIGSTOP); if (CPU == which) { chew_cpu(cpu_ticks); } else if (IO == which) { spam_io(io_ticks); } else { *((volatile int*)0) = 42; } _exit(0); }