#include #include #include #include #include #include #include #include #include #include #include #include /* (c) 2007 Vito Caputo - */ /* GPLv3 */ /* gcc -O2 -o jittery jittery.c */ /* This simple program gives some idea of how much overall latency there is * between a kernel event firing and user space responding. /dev/rtc increases * a counter at a programmed frequency. It becomes readable when the counter * increases, we put the fd into ASYNC mode so SIGIO is delivered when it * occurs. The time between the signal being generated, delivered, and * userspace reading the count from the fd would be the latency. During that * time, the counter may be increased (the timer is operating at a high * frequency defined in the HZ constant). The value read from the fd is the # * of timer events since the previous read. We increment an array of counters * using the value read as the index, producing a histogram of latency counts. * This gives you both a sense of the overall latency and the variance. * * We're essentially counting the number of RTC events we miss, to what degree, * and how many times. * * Some interesting things to experiment with are: * - while running, produce varying loads stressing different subsystems (disk * IO, network IO, audio, CPU/memory (for each cpu { yes > /dev/null})) * - run via nice --adjustment=-20 and observe the difference * - run with --sched-fifo * - if frequency scaling, try differing governors (performance, powersave) * - differ schedulers * - different kernel versions and configs: * (vary HZ, NO_HZ, CONFIG_PREEMPT(|_NONE|_VOLUNTARY|) * * The program periodically prints the latest counts every second until * interrupted via SIG(INTR|QUIT|TERM), then prints the total counts on exit. * * Note that on modern linux/PC's the RTC is emulated using HPET if enabled. */ #define RTCDEV "/dev/rtc" #define JITTER_FIELDS_CNT 4 #define HZ 8192 #define N_BUCKETS 256 static int rtc_fd = -1; static unsigned int counts[N_BUCKETS]; static unsigned int tcounts[N_BUCKETS]; static int max_ints, tmax_ints; static int ints; static volatile int report; static volatile int quit; void handle_sigio(int signum) { unsigned long t; read(rtc_fd, &t, sizeof(t)); t >>= 8; if(t < N_BUCKETS) { counts[t]++; if(t > max_ints) max_ints = t; } ints++; if (ints >= HZ) { ints = 0; report = 1; } } void handle_intr(int signum) { quit = 1; } int main(int argc, char *argv[]) { int old, i; sigset_t sigio_off, sigio_on; struct sched_param param = { .sched_priority = 1 }; rtc_fd = open(RTCDEV, O_RDONLY); if(rtc_fd == -1) { fprintf(stderr, "Failed to open PC-AT rtc file: \"%s\"\n", RTCDEV); goto error; } if(argc > 2 || (argc == 2 && strcmp(argv[1], "--sched-fifo"))) { fprintf(stderr, "usage: %s [--sched-fifo]\n", argv[0]); goto error; } if(argc == 2 && sched_setscheduler(getpid(), SCHED_FIFO, ¶m) == -1) { fprintf(stderr, "Failed to set SCHED_FIFO scheduler\n"); goto error; } /* block SIGIO until we're ready for it */ sigemptyset(&sigio_off); sigaddset(&sigio_off, SIGIO); sigprocmask(SIG_BLOCK, &sigio_off, &sigio_on); signal(SIGIO, handle_sigio); signal(SIGINT, handle_intr); signal(SIGTERM, handle_intr); signal(SIGQUIT, handle_intr); if(fcntl(rtc_fd, F_SETOWN, getpid()) < 0) { fprintf(stderr, "SETOWN failed\n"); goto error; } if((old = fcntl(rtc_fd, F_GETFL) < 0)) { fprintf(stderr, "GETFL failed\n"); goto error; } if(fcntl(rtc_fd, F_SETFL, old | FASYNC) < 0) { fprintf(stderr, "SETFL failed\n"); goto error; } ioctl(rtc_fd, RTC_IRQP_SET, HZ); while(!quit) { ioctl(rtc_fd, RTC_PIE_ON, 0); while(!report && !quit) sigsuspend(&sigio_on); ioctl(rtc_fd, RTC_PIE_OFF, 0); /* SIGIO is blocked to prevent report starvation */ for(i = 0; i <= max_ints; i++) { printf("%u ", counts[i]); tcounts[i] += counts[i]; counts[i] = 0; } puts(""); if(max_ints > tmax_ints) { tmax_ints = max_ints; } max_ints = 0; report = 0; } close(rtc_fd); printf("Totals:\n"); for(i = 0; i <= tmax_ints; i++) { printf("%u ", tcounts[i]); } puts(""); return 0; error: return 1; }