summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2018-12-20 15:04:15 -0800
committerVito Caputo <vcaputo@pengaru.com>2018-12-20 15:04:15 -0800
commit5ac18c25d1f9d39d44a73e1b69a11ed510366709 (patch)
treec66d3c68bb177d5eed2035c58aef597c22481a88
jittery: initial commit of this old hackHEADmaster
-rw-r--r--jittery.c165
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, &param) == -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;
+}
© All Rights Reserved