1 /**
2  * hiccup.c
3  * Written by Michael Barker and released to the public domain,
4  * as explained at http://creativecommons.org/publicdomain/zero/1.0/
5  */
6 
7 #include <stdint.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include <pthread.h>
11 #include <sys/timerfd.h>
12 #include <poll.h>
13 #include <string.h>
14 #include <signal.h>
15 #include <ctype.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19 
20 #include <hdr_histogram.h>
21 #include <hdr_histogram_log.h>
22 #include <hdr_interval_recorder.h>
23 #include <hdr_time.h>
24 
diff(struct timespec t0, struct timespec t1)25 static int64_t diff(struct timespec t0, struct timespec t1)
26 {
27     int64_t delta_us = 0;
28     delta_us = (t1.tv_sec - t0.tv_sec) * 1000000;
29     delta_us += (t1.tv_nsec - t0.tv_nsec) / 1000;
30 
31     return delta_us;
32 }
33 
record_hiccups(void* thread_context)34 static void* record_hiccups(void* thread_context)
35 {
36     struct pollfd fd;
37     struct timespec t0;
38     struct timespec t1;
39     struct itimerspec timeout;
40     struct hdr_interval_recorder* r = thread_context;
41 
42     memset(&fd, 0, sizeof(struct pollfd));
43     memset(&timeout, 0, sizeof(struct itimerspec));
44     memset(&t0, 0, sizeof(struct timespec));
45     memset(&t1, 0, sizeof(struct timespec));
46 
47     fd.fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
48     fd.events = POLLIN|POLLPRI|POLLRDHUP;
49     fd.revents = 0;
50 
51 #pragma clang diagnostic push
52 #pragma clang diagnostic ignored "-Wmissing-noreturn"
53     while (true)
54     {
55         int64_t delta_us;
56 
57         timeout.it_value.tv_sec = 0;
58         timeout.it_value.tv_nsec = 1000000;
59         timerfd_settime(fd.fd, 0, &timeout, NULL);
60 
61         hdr_gettime(&t0);
62         poll(&fd, 1, -1);
63         hdr_gettime(&t1);
64 
65         delta_us = diff(t0, t1) - 1000;
66         delta_us = delta_us < 0 ? 0 : delta_us;
67 
68         hdr_interval_recorder_record_value(r, delta_us);
69     }
70 #pragma clang diagnostic pop
71 
72     pthread_exit(NULL);
73 }
74 
75 struct config_t
76 {
77     int interval;
78     const char* filename;
79 };
80 
81 const char* USAGE =
82 "hiccup [-i <interval>] [-f <filename>]\n"
83 "  interval: <number> Time in seconds between samples (default 1).\n"
84 "  filename: <string> Name of the file to log to (default stdout).\n";
85 
handle_opts(int argc, char** argv, struct config_t* config)86 static int handle_opts(int argc, char** argv, struct config_t* config)
87 {
88     int c;
89     int interval = 1;
90 
91     while ((c = getopt(argc, argv, "i:f:")) != -1)
92     {
93         switch (c)
94         {
95         case 'h':
96             return 0;
97 
98         case 'i':
99             interval = atoi(optarg);
100             if (interval < 1)
101             {
102                 return 0;
103             }
104 
105             break;
106         case 'f':
107             config->filename = optarg;
108             break;
109         default:
110             return 0;
111         }
112     }
113 
114     config->interval = interval < 1 ? 1 : interval;
115     return 1;
116 }
117 
main(int argc, char** argv)118 int main(int argc, char** argv)
119 {
120     struct timespec timestamp;
121     struct timespec start_timestamp;
122     struct timespec end_timestamp;
123     struct hdr_interval_recorder recorder;
124     struct hdr_log_writer log_writer;
125     struct config_t config;
126     struct hdr_histogram* inactive = NULL;
127     pthread_t recording_thread;
128     FILE* output = stdout;
129 
130     memset(&config, 0, sizeof(struct config_t));
131     if (!handle_opts(argc, argv, &config))
132     {
133         printf("%s", USAGE);
134         return 0;
135     }
136 
137     if (config.filename)
138     {
139         output = fopen(config.filename, "a+");
140         if (!output)
141         {
142             fprintf(
143                 stderr, "Failed to open/create file: %s, %s",
144                 config.filename, strerror(errno));
145 
146             return -1;
147         }
148     }
149 
150     if (0 != hdr_interval_recorder_init_all(&recorder, 1, INT64_C(24) * 60 * 60 * 1000000, 3))
151     {
152         fprintf(stderr, "%s\n", "Failed to init phaser");
153         return -1;
154     }
155 
156     if (pthread_create(&recording_thread, NULL, record_hiccups, &recorder))
157     {
158         fprintf(stderr, "%s\n", "Failed to create thread");
159         return -1;
160     }
161 
162     hdr_gettime(&start_timestamp);
163     hdr_getnow(&timestamp);
164     hdr_log_writer_init(&log_writer);
165     hdr_log_write_header(&log_writer, output, "foobar", &timestamp);
166 
167 #pragma clang diagnostic push
168 #pragma clang diagnostic ignored "-Wmissing-noreturn"
169     while (true)
170     {
171         sleep(config.interval);
172 
173         inactive = hdr_interval_recorder_sample(&recorder);
174 
175         hdr_gettime(&end_timestamp);
176         timestamp = start_timestamp;
177 
178         hdr_gettime(&start_timestamp);
179 
180         hdr_log_write(&log_writer, output, &timestamp, &end_timestamp, inactive);
181         fflush(output);
182 
183         hdr_reset(inactive);
184     }
185 #pragma clang diagnostic pop
186 
187     pthread_exit(NULL);
188 }
189