|
|
@@ -0,0 +1,149 @@
|
|
|
+#include <sys/event.h>
|
|
|
+#include <sys/time.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <sched.h>
|
|
|
+#include <err.h>
|
|
|
+#include <stdlib.h>
|
|
|
+#include <string.h>
|
|
|
+#include <unistd.h>
|
|
|
+#include <getopt.h>
|
|
|
+#include <signal.h>
|
|
|
+#include <stdbool.h>
|
|
|
+
|
|
|
+static int verbose;
|
|
|
+
|
|
|
+static int usage(void)
|
|
|
+{
|
|
|
+ fputs("kq_timer [--verbose] [--period ms]\n", stderr);
|
|
|
+ exit (EXIT_SUCCESS);
|
|
|
+}
|
|
|
+
|
|
|
+static double diff_ms(const struct timespec *time1, const struct timespec *time0)
|
|
|
+{
|
|
|
+ return (time1->tv_sec - time0->tv_sec) * 1000
|
|
|
+ + (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 */
|
|
|
+ int kq, nev; /* handlers */
|
|
|
+ int ch; /* opt long */
|
|
|
+
|
|
|
+ int period = 1000; /* Default to 1 sec. */
|
|
|
+ size_t counter = 0;
|
|
|
+ size_t total = 0;
|
|
|
+ FILE *fd = NULL;
|
|
|
+ char template[64];
|
|
|
+ int rc = EXIT_FAILURE;
|
|
|
+ struct timespec t0 = { 0, 0};
|
|
|
+ const struct sched_param param = {.sched_priority = 49};
|
|
|
+ static struct option longopts[] = {
|
|
|
+ { "period", required_argument, NULL, 'p' },
|
|
|
+ { "verbose", no_argument , &verbose, 1 },
|
|
|
+ { NULL, 0, NULL, 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");
|
|
|
+ goto exit_kqueue;
|
|
|
+ }
|
|
|
+
|
|
|
+ fd = fopen(template, "w");
|
|
|
+ if (fd == NULL) {
|
|
|
+ warn("fopen() failure");
|
|
|
+ goto exit_kqueue;
|
|
|
+ }
|
|
|
+
|
|
|
+ printf("Log file: %s\n", template);
|
|
|
+
|
|
|
+ /* process priority */
|
|
|
+ sched_setscheduler(0, SCHED_FIFO, ¶m);
|
|
|
+
|
|
|
+ signal(SIGINT, sig_handler);
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ nev = kevent(kq, &change, 1, &event, 1, NULL);
|
|
|
+
|
|
|
+ if (nev < 0) {
|
|
|
+ /* 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));
|
|
|
+
|
|
|
+ t0 = t1;
|
|
|
+
|
|
|
+ if (event.flags & EV_ERROR) {
|
|
|
+ fprintf(stderr, "EV_ERROR: %s\n", strerror(event.data));
|
|
|
+ rc = EXIT_FAILURE;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* verbose mode */
|
|
|
+ ++counter;
|
|
|
+ if (counter == period) {
|
|
|
+ if (verbose) {
|
|
|
+ fputs(".", stdout);
|
|
|
+ fflush(stdout);
|
|
|
+ }
|
|
|
+
|
|
|
+ counter = 0;
|
|
|
+ ++total;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ rc = 0;
|
|
|
+
|
|
|
+ fclose(fd);
|
|
|
+ fprintf(stdout, "\n%zu entries stored.\n", total);
|
|
|
+exit_kqueue:
|
|
|
+ close(kq);
|
|
|
+ return rc ? EXIT_FAILURE : EXIT_SUCCESS;
|
|
|
+}
|
|
|
+
|