#include #include #include #include #include #include #include #include #include #include #include #include static int verbose; const struct sched_param param = {.sched_priority = 15}; static struct option longopts[] = { { "period", required_argument, NULL, 'p' }, { "verbose", no_argument , &verbose, 1 }, { NULL, 0, NULL, 0 } }; void fd_close(int *fd) { close(*fd); *fd = -1; } #define _smartfd __attribute((cleanup(fd_close))) void file_close(FILE **fd) { fclose(*fd); *fd = NULL; } #define _smartfile __attribute((cleanup(file_close))) __attribute__((noreturn))static int usage(void) { fputs("kq_timer [--verbose] [--period ms]\n", stderr); exit (EXIT_SUCCESS); } __attribute__((const)) static double diff_ms(const struct timespec * restrict time1, const struct timespec * restrict time0) { return (double)(time1->tv_sec - time0->tv_sec) * 1000.0 + (double)(time1->tv_nsec - time0->tv_nsec) / 1000000.0; } static void sig_handler(int sig) { signal(SIGINT, SIG_DFL); } int main(int argc, char *argv[]) { struct kevent change; /* event we want to monitor */ struct kevent event; /* event that was triggered */ _smartfd int kq; int nev; /* handlers */ int ch; /* opt long */ int period = 1000; /* Default to 1 sec. */ size_t counter = 0; size_t total = 0; _smartfile FILE *fd = NULL; char template[64]; int rc = EXIT_FAILURE; struct timespec t0 = { 0, 0}; while ((ch = getopt_long(argc, argv, "hvp:", longopts, NULL)) != -1) { switch (ch) { case 'v': verbose = 1; break; case 'p': period = atoi(optarg); break; case 0: /* long option */ break; default: usage(); } } argc -= optind; argv += optind; if (*argv) usage(); /* create a new kernel event queue */ if ((kq = kqueue()) == -1) err(EXIT_FAILURE, "kqueue() failure"); /* initalise kevent structure */ EV_SET(&change, 1, EVFILT_TIMER, EV_ADD | EV_ENABLE, NOTE_MSECONDS, 1, 0); /* Dummy file to store measurements */ strncpy(template, "/tmp/timer.XXXXXX", sizeof template); if (mkstemp(template) == -1) { warn("mkstemp() failure"); return EXIT_FAILURE; } fd = fopen(template, "w"); if (fd == NULL) { warn("fopen() failure"); return EXIT_FAILURE; } printf("Log file: %s\n", template); /* process priority */ if (-1 == sched_setscheduler(0, SCHED_RR, ¶m)) { warn("set scheduler() failure"); } signal(SIGINT, sig_handler); while (true) { nev = kevent(kq, &change, 1, &event, 1, NULL); if (nev < 0) { if (errno != EINTR) warn("kevent wait:"); /* interrupted */ break; } if (nev > 0) { /* measurements */ struct timespec t1; clock_gettime(CLOCK_MONOTONIC, &t1); if (!(t0.tv_sec == 0 && t0.tv_nsec == 0)) { fprintf(fd, "%06.3f\n", diff_ms(&t1, &t0)); ++total; } t0 = t1; if (event.flags & EV_ERROR) { fprintf(stderr, "EV_ERROR: %s\n", strerror((int)event.data)); rc = EXIT_FAILURE; break; } /* verbose mode */ ++counter; if (counter == period) { if (verbose) { fputs(".", stdout); fflush(stdout); } counter = 0; } } } rc = EXIT_SUCCESS; fprintf(stdout, "\n%zu entries stored.\n", total); fprintf(stdout, "%zu cycles counted.\n", counter); return rc; }