d3f5c366812f9d71bf87cb46d2a7c862fcfc3011
[dragonfly.git] / usr.bin / kcollect / kcollect.c
1 /*
2  * Copyright (c) 2017 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
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.
17  *
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.
30  */
31
32 #include <sys/types.h>
33 #include <sys/sysctl.h>
34 #include <sys/kcollect.h>
35 #include <time.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <time.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <libutil.h>
42
43 #define SLEEP_INTERVAL  60      /* minimum is KCOLLECT_INTERVAL */
44
45 static void dump_text(kcollect_t *ary, size_t count, size_t total_count);
46 static void dump_gnuplot(kcollect_t *ary, size_t count);
47 static void dump_dbm(kcollect_t *ary, size_t count, const char *datafile);
48 static void dump_fields(kcollect_t *ary);
49 static void start_gnuplot(int ac, char **av);
50
51 static const char *Fields = NULL;
52 static int UseGmt = 0;
53
54 int
55 main(int ac, char **av)
56 {
57         kcollect_t *ary;
58         size_t bytes = 0;
59         size_t count;
60         size_t total_count;
61         const char *datafile = NULL;
62         int cmd = 't';
63         int ch;
64         int keepalive = 0;
65         int last_ticks;
66         int loops = 0;
67
68         sysctlbyname("kern.collect_data", NULL, &bytes, NULL, 0);
69         if (bytes == 0) {
70                 fprintf(stderr, "kern.collect_data not available\n");
71                 exit(1);
72         }
73
74         while ((ch = getopt(ac, av, "o:b:flgx")) != -1) {
75                 switch(ch) {
76                 case 'o':
77                         Fields = optarg;
78                         break;
79                 case 'b':
80                         datafile = optarg;
81                         cmd = 'b';
82                         break;
83                 case 'f':
84                         keepalive = 1;
85                         break;
86                 case 'l':
87                         cmd = 'l';
88                         break;
89                 case 'g':
90                         cmd = 'g';
91                         break;
92                 case 'x':
93                         cmd = 'x';
94                         break;
95                 case 'G':
96                         UseGmt = 1;
97                         break;
98                 default:
99                         fprintf(stderr, "Unknown option %c\n", ch);
100                         exit(1);
101                         /* NOT REACHED */
102                 }
103         }
104         if (cmd != 'x' && ac != optind) {
105                 fprintf(stderr, "Unknown argument %s\n", av[optind]);
106                 exit(1);
107                 /* NOT REACHED */
108         }
109
110         total_count = 0;
111
112         if (cmd == 'x')
113                 start_gnuplot(ac - optind, av + optind);
114         do {
115                 /*
116                  * Snarf as much data as we can.  If we are looping,
117                  * snarf less (no point snarfing stuff we already have).
118                  */
119                 bytes = 0;
120                 sysctlbyname("kern.collect_data", NULL, &bytes, NULL, 0);
121                 if (cmd == 'l')
122                         bytes = sizeof(kcollect_t) * 2;
123
124                 if (loops) {
125                         size_t loop_bytes;
126
127                         loop_bytes = sizeof(kcollect_t) *
128                                      (4 + SLEEP_INTERVAL / KCOLLECT_INTERVAL);
129                         if (bytes > loop_bytes)
130                                 bytes = loop_bytes;
131                 }
132
133                 ary = malloc(bytes);
134                 sysctlbyname("kern.collect_data", ary, &bytes, NULL, 0);
135
136                 count = bytes / sizeof(kcollect_t);
137                 if (loops) {
138                         while (count > 2) {
139                                 if ((int)(ary[count-1].ticks - last_ticks) > 0)
140                                         break;
141                                 --count;
142                         }
143                 }
144
145                 switch(cmd) {
146                 case 't':
147                         if (count > 2)
148                                 dump_text(ary, count, total_count);
149                         break;
150                 case 'b':
151                         if (count > 2)
152                                 dump_dbm(ary, count, datafile);
153                         break;
154                 case 'l':
155                         dump_fields(ary);
156                         exit(0);
157                         break;          /* NOT REACHED */
158                 case 'g':
159                         if (count > 2)
160                                 dump_gnuplot(ary, count);
161                         break;
162                 case 'x':
163                         if (count > 2)
164                                 dump_gnuplot(ary, count);
165                         break;
166                 }
167                 if (keepalive) {
168                         fflush(stdout);
169                         sleep(1);
170                 }
171                 last_ticks = ary[2].ticks;
172                 if (count >= 2)
173                         total_count += count - 2;
174                 ++loops;
175                 free(ary);
176         } while (keepalive);
177
178         if (cmd == 'x')
179                 pclose(stdout);
180 }
181
182 static
183 void
184 dump_text(kcollect_t *ary, size_t count, size_t total_count)
185 {
186         int j;
187         int i;
188         uintmax_t scale;
189         uintmax_t value;
190         char fmt;
191         char buf[9];
192         struct tm *tmv;
193         time_t t;
194
195         for (i = count - 1; i >= 2; --i) {
196                 if ((total_count & 15) == 0) {
197                         printf("%8.8s", "time");
198                         for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
199                                 if (ary[1].data[j]) {
200                                         printf(" %8.8s",
201                                                 (char *)&ary[1].data[j]);
202                                 }
203                         }
204                         printf("\n");
205                 }
206
207                 /*
208                  * Timestamp
209                  */
210                 t = ary[i].realtime.tv_sec;
211                 if (UseGmt)
212                         tmv = gmtime(&t);
213                 else
214                         tmv = localtime(&t);
215                 strftime(buf, sizeof(buf), "%H:%M:%S", tmv);
216                 printf("%8.8s", buf);
217
218                 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
219                         if (ary[1].data[j] == 0)
220                                 continue;
221
222                         /*
223                          * NOTE: kernel does not have to provide the scale
224                          *       (that is, the highest likely value), nor
225                          *       does it make sense in all cases.
226                          *
227                          *       Example scale - kernel provides total amount
228                          *       of memory available for memory related
229                          *       statistics in the scale field.
230                          */
231                         value = ary[i].data[j];
232                         scale = KCOLLECT_GETSCALE(ary[0].data[j]);
233                         fmt = KCOLLECT_GETFMT(ary[0].data[j]);
234
235                         printf(" ");
236
237                         switch(fmt) {
238                         case '2':
239                                 /*
240                                  * fractional x100
241                                  */
242                                 printf("%5ju.%02ju", value / 100, value % 100);
243                                 break;
244                         case 'p':
245                                 /*
246                                  * Percentage fractional x100 (100% = 10000)
247                                  */
248                                 printf("%4ju.%02ju%%",
249                                         value / 100, value % 100);
250                                 break;
251                         case 'm':
252                                 /*
253                                  * Megabytes
254                                  */
255                                 humanize_number(buf, sizeof(buf), value, "",
256                                                 2,
257                                                 HN_FRACTIONAL |
258                                                 HN_NOSPACE);
259                                 printf("%8.8s", buf);
260                                 break;
261                         case 'c':
262                                 /*
263                                  * Raw count over period (this is not total)
264                                  */
265                                 humanize_number(buf, sizeof(buf), value, "",
266                                                 HN_AUTOSCALE,
267                                                 HN_FRACTIONAL |
268                                                 HN_NOSPACE |
269                                                 HN_DIVISOR_1000);
270                                 printf("%8.8s", buf);
271                                 break;
272                         case 'b':
273                                 /*
274                                  * Total bytes (this is a total), output
275                                  * in megabytes.
276                                  */
277                                 if (scale > 100000000) {
278                                         humanize_number(buf, sizeof(buf),
279                                                         value, "",
280                                                         3,
281                                                         HN_FRACTIONAL |
282                                                         HN_NOSPACE);
283                                 } else {
284                                         humanize_number(buf, sizeof(buf),
285                                                         value, "",
286                                                         2,
287                                                         HN_FRACTIONAL |
288                                                         HN_NOSPACE);
289                                 }
290                                 printf("%8.8s", buf);
291                                 break;
292                         default:
293                                 printf("        ");
294                                 break;
295                         }
296                 }
297                 printf("\n");
298                 ++total_count;
299         }
300 }
301
302 static void
303 dump_gnuplot(kcollect_t *ary __unused, size_t count __unused)
304 {
305 }
306
307 static void
308 dump_dbm(kcollect_t *ary __unused, size_t count __unused, const char *datafile __unused)
309 {
310 }
311
312 static void
313 start_gnuplot(int ac __unused, char **av __unused)
314 {
315 }
316
317 static void
318 dump_fields(kcollect_t *ary)
319 {
320         int j;
321
322         for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
323                 if (ary[1].data[j] == 0)
324                         continue;
325                 printf("%8.8s %c\n",
326                        (char *)&ary[1].data[j],
327                        KCOLLECT_GETFMT(ary[0].data[j]));
328         }
329 }