diff options
-rw-r--r-- | jittery.c | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/jittery.c b/jittery.c new file mode 100644 index 0000000..8edd42b --- /dev/null +++ b/jittery.c @@ -0,0 +1,165 @@ +#include <errno.h> +#include <fcntl.h> +#include <linux/rtc.h> +#include <sched.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + + +/* (c) 2007 Vito Caputo - <vcaputo@pengaru.com> */ +/* 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; +} |