/** * hiccup.c * Written by Michael Barker and released to the public domain, * as explained at http://creativecommons.org/publicdomain/zero/1.0/ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int64_t diff(struct timespec t0, struct timespec t1) { int64_t delta_us = 0; delta_us = (t1.tv_sec - t0.tv_sec) * 1000000; delta_us += (t1.tv_nsec - t0.tv_nsec) / 1000; return delta_us; } static void* record_hiccups(void* thread_context) { struct pollfd fd; struct timespec t0; struct timespec t1; struct itimerspec timeout; struct hdr_interval_recorder* r = thread_context; memset(&fd, 0, sizeof(struct pollfd)); memset(&timeout, 0, sizeof(struct itimerspec)); memset(&t0, 0, sizeof(struct timespec)); memset(&t1, 0, sizeof(struct timespec)); fd.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); fd.events = POLLIN|POLLPRI|POLLRDHUP; fd.revents = 0; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-noreturn" while (true) { int64_t delta_us; timeout.it_value.tv_sec = 0; timeout.it_value.tv_nsec = 1000000; timerfd_settime(fd.fd, 0, &timeout, NULL); hdr_gettime(&t0); poll(&fd, 1, -1); hdr_gettime(&t1); delta_us = diff(t0, t1) - 1000; delta_us = delta_us < 0 ? 0 : delta_us; hdr_interval_recorder_record_value(r, delta_us); } #pragma clang diagnostic pop pthread_exit(NULL); } struct config_t { unsigned int interval; const char* filename; }; const char* USAGE = "hiccup [-i ] [-f ]\n" " interval: Time in seconds between samples (default 1).\n" " filename: Name of the file to log to (default stdout).\n"; static int handle_opts(int argc, char** argv, struct config_t* config) { int c; unsigned int interval = 1; while ((c = getopt(argc, argv, "i:f:")) != -1) { switch (c) { case 'h': return 0; case 'i': interval = (unsigned int) strtoul(optarg, NULL, 10); if (interval < 1) { return 0; } break; case 'f': config->filename = optarg; break; default: return 0; } } config->interval = interval < 1 ? 1 : interval; return 1; } int main(int argc, char** argv) { struct timespec timestamp; struct timespec start_timestamp; struct timespec end_timestamp; struct hdr_interval_recorder recorder; struct hdr_log_writer log_writer; struct config_t config; struct hdr_histogram* inactive = NULL; pthread_t recording_thread; FILE* output = stdout; memset(&config, 0, sizeof(struct config_t)); if (!handle_opts(argc, argv, &config)) { printf("%s", USAGE); return 0; } if (config.filename) { output = fopen(config.filename, "a+"); if (!output) { fprintf( stderr, "Failed to open/create file: %s, %s", config.filename, strerror(errno)); return -1; } } if (0 != hdr_interval_recorder_init_all(&recorder, 1, INT64_C(24) * 60 * 60 * 1000000, 3)) { fprintf(stderr, "%s\n", "Failed to init phaser"); return -1; } if (pthread_create(&recording_thread, NULL, record_hiccups, &recorder)) { fprintf(stderr, "%s\n", "Failed to create thread"); return -1; } hdr_gettime(&start_timestamp); hdr_getnow(×tamp); hdr_log_writer_init(&log_writer); hdr_log_write_header(&log_writer, output, "foobar", ×tamp); #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-noreturn" while (true) { sleep(config.interval); inactive = hdr_interval_recorder_sample_and_recycle(&recorder, inactive); hdr_gettime(&end_timestamp); timestamp = start_timestamp; hdr_gettime(&start_timestamp); hdr_log_write(&log_writer, output, ×tamp, &end_timestamp, inactive); fflush(output); } #pragma clang diagnostic pop pthread_exit(NULL); }