kcollect - Implement gnuplot output feature
[dragonfly.git] / usr.bin / kcollect / kcollect.c
blob9730cb2b07e052d2f2ec9bd808833bf75961d49a
1 /*
2 * Copyright (c) 2017 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
32 #include "kcollect.h"
34 #define SLEEP_INTERVAL 60 /* minimum is KCOLLECT_INTERVAL */
36 static void dump_text(kcollect_t *ary, size_t count, size_t total_count);
37 static void dump_dbm(kcollect_t *ary, size_t count, const char *datafile);
38 static void dump_fields(kcollect_t *ary);
39 static void adjust_fields(kcollect_t *ent, const char *fields);
41 FILE *OutFP;
42 int UseGMT;
43 int OutputWidth = 1024;
44 int OutputHeight = 1024;
46 int
47 main(int ac, char **av)
49 kcollect_t *ary;
50 size_t bytes = 0;
51 size_t count;
52 size_t total_count;
53 const char *datafile = NULL;
54 const char *fields = NULL;
55 int cmd = 't';
56 int ch;
57 int keepalive = 0;
58 int last_ticks;
59 int loops = 0;
61 OutFP = stdout;
63 sysctlbyname("kern.collect_data", NULL, &bytes, NULL, 0);
64 if (bytes == 0) {
65 fprintf(stderr, "kern.collect_data not available\n");
66 exit(1);
69 while ((ch = getopt(ac, av, "o:b:flgxw:GW:H:")) != -1) {
70 switch(ch) {
71 case 'o':
72 fields = optarg;
73 break;
74 case 'b':
75 datafile = optarg;
76 cmd = 'b';
77 break;
78 case 'f':
79 keepalive = 1;
80 break;
81 case 'l':
82 cmd = 'l';
83 break;
84 case 'w':
85 datafile = optarg;
86 cmd = 'w';
87 break;
88 case 'g':
89 cmd = 'g';
90 break;
91 case 'x':
92 cmd = 'x';
93 break;
94 case 'G':
95 UseGMT = 1;
96 break;
97 case 'W':
98 OutputWidth = strtol(optarg, NULL, 0);
99 break;
100 case 'H':
101 OutputHeight = strtol(optarg, NULL, 0);
102 break;
103 default:
104 fprintf(stderr, "Unknown option %c\n", ch);
105 exit(1);
106 /* NOT REACHED */
109 if (cmd != 'x' && ac != optind) {
110 fprintf(stderr, "Unknown argument %s\n", av[optind]);
111 exit(1);
112 /* NOT REACHED */
115 total_count = 0;
116 last_ticks = 0;
118 if (cmd == 'x' || cmd == 'w')
119 start_gnuplot(ac - optind, av + optind);
121 do {
123 * Snarf as much data as we can. If we are looping,
124 * snarf less (no point snarfing stuff we already have).
126 bytes = 0;
127 sysctlbyname("kern.collect_data", NULL, &bytes, NULL, 0);
128 if (cmd == 'l')
129 bytes = sizeof(kcollect_t) * 2;
131 if (loops) {
132 size_t loop_bytes;
134 loop_bytes = sizeof(kcollect_t) *
135 (4 + SLEEP_INTERVAL / KCOLLECT_INTERVAL);
136 if (bytes > loop_bytes)
137 bytes = loop_bytes;
140 ary = malloc(bytes);
141 sysctlbyname("kern.collect_data", ary, &bytes, NULL, 0);
143 if (fields)
144 adjust_fields(&ary[1], fields);
146 count = bytes / sizeof(kcollect_t);
147 if (loops) {
148 while (count > 2) {
149 if ((int)(ary[count-1].ticks - last_ticks) > 0)
150 break;
151 --count;
155 switch(cmd) {
156 case 't':
157 if (count > 2)
158 dump_text(ary, count, total_count);
159 break;
160 case 'b':
161 if (count > 2)
162 dump_dbm(ary, count, datafile);
163 break;
164 case 'l':
165 dump_fields(ary);
166 exit(0);
167 break; /* NOT REACHED */
168 case 'g':
169 if (count > 2)
170 dump_gnuplot(ary, count, NULL);
171 break;
172 case 'w':
173 if (count >= 2)
174 dump_gnuplot(ary, count, datafile);
175 break;
176 case 'x':
177 if (count > 2)
178 dump_gnuplot(ary, count, NULL);
179 break;
181 if (keepalive) {
182 fflush(OutFP);
183 fflush(stdout);
184 switch(cmd) {
185 case 't':
186 sleep(1);
187 break;
188 case 'x':
189 case 'g':
190 case 'w':
191 sleep(60);
192 break;
193 default:
194 sleep(10);
195 break;
198 last_ticks = ary[2].ticks;
199 if (count >= 2)
200 total_count += count - 2;
203 * Loop for incremental aquisition. When outputting to
204 * gunplot, we have to send the whole data-set again so
205 * do not increment loops in that case.
207 if (cmd != 'g' && cmd != 'x' && cmd != 'w')
208 ++loops;
210 free(ary);
211 } while (keepalive);
213 if (cmd == 'x')
214 pclose(OutFP);
217 static
218 void
219 dump_text(kcollect_t *ary, size_t count, size_t total_count)
221 int j;
222 int i;
223 uintmax_t scale;
224 uintmax_t value;
225 char fmt;
226 char buf[9];
227 struct tm *tmv;
228 time_t t;
230 for (i = count - 1; i >= 2; --i) {
231 if ((total_count & 15) == 0) {
232 printf("%8.8s", "time");
233 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
234 if (ary[1].data[j]) {
235 printf(" %8.8s",
236 (char *)&ary[1].data[j]);
239 printf("\n");
243 * Timestamp
245 t = ary[i].realtime.tv_sec;
246 if (UseGMT)
247 tmv = gmtime(&t);
248 else
249 tmv = localtime(&t);
250 strftime(buf, sizeof(buf), "%H:%M:%S", tmv);
251 printf("%8.8s", buf);
253 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
254 if (ary[1].data[j] == 0)
255 continue;
258 * NOTE: kernel does not have to provide the scale
259 * (that is, the highest likely value), nor
260 * does it make sense in all cases.
262 * Example scale - kernel provides total amount
263 * of memory available for memory related
264 * statistics in the scale field.
266 value = ary[i].data[j];
267 scale = KCOLLECT_GETSCALE(ary[0].data[j]);
268 fmt = KCOLLECT_GETFMT(ary[0].data[j]);
270 printf(" ");
272 switch(fmt) {
273 case '2':
275 * fractional x100
277 printf("%5ju.%02ju", value / 100, value % 100);
278 break;
279 case 'p':
281 * Percentage fractional x100 (100% = 10000)
283 printf("%4ju.%02ju%%",
284 value / 100, value % 100);
285 break;
286 case 'm':
288 * Megabytes
290 humanize_number(buf, sizeof(buf), value, "",
292 HN_FRACTIONAL |
293 HN_NOSPACE);
294 printf("%8.8s", buf);
295 break;
296 case 'c':
298 * Raw count over period (this is not total)
300 humanize_number(buf, sizeof(buf), value, "",
301 HN_AUTOSCALE,
302 HN_FRACTIONAL |
303 HN_NOSPACE |
304 HN_DIVISOR_1000);
305 printf("%8.8s", buf);
306 break;
307 case 'b':
309 * Total bytes (this is a total), output
310 * in megabytes.
312 if (scale > 100000000) {
313 humanize_number(buf, sizeof(buf),
314 value, "",
316 HN_FRACTIONAL |
317 HN_NOSPACE);
318 } else {
319 humanize_number(buf, sizeof(buf),
320 value, "",
322 HN_FRACTIONAL |
323 HN_NOSPACE);
325 printf("%8.8s", buf);
326 break;
327 default:
328 printf(" ");
329 break;
332 printf("\n");
333 ++total_count;
337 static void
338 dump_dbm(kcollect_t *ary __unused, size_t count __unused, const char *datafile __unused)
342 static void
343 dump_fields(kcollect_t *ary)
345 int j;
347 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
348 if (ary[1].data[j] == 0)
349 continue;
350 printf("%8.8s %c\n",
351 (char *)&ary[1].data[j],
352 KCOLLECT_GETFMT(ary[0].data[j]));
356 static void
357 adjust_fields(kcollect_t *ent, const char *fields)
359 char *copy = strdup(fields);
360 char *word;
361 int selected[KCOLLECT_ENTRIES];
362 int j;
364 bzero(selected, sizeof(selected));
366 word = strtok(copy, ", \t");
367 while (word) {
368 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
369 if (strncmp(word, (char *)&ent->data[j], 8) == 0) {
370 selected[j] = 1;
371 break;
374 word = strtok(NULL, ", \t");
376 free(copy);
377 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
378 if (!selected[j])
379 ent->data[j] = 0;