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
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
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
38 #define SLEEP_INTERVAL 60 /* minimum is KCOLLECT_INTERVAL */
40 #define DISPLAY_TIME_ONLY "%H:%M:%S"
41 #define DISPLAY_FULL_DATE "%F %H:%M:%S"
42 #define HDR_FMT "HEADER0"
43 #define HDR_TITLE "HEADER1"
45 static void format_output(uintmax_t value,char fmt,uintmax_t scale, char* ret);
46 static void dump_text(kcollect_t *ary, size_t count,
47 size_t total_count, const char* display_fmt);
48 static void dump_dbm(kcollect_t *ary, size_t count, const char *datafile);
49 static int str2unix(const char* str, const char* fmt);
50 static int rec_comparator(const void *c1, const void *c2);
51 static void load_dbm(const char *datafile,
52 kcollect_t **ret_ary, size_t *counter);
53 static void dump_fields(kcollect_t *ary);
54 static void adjust_fields(kcollect_t *ent, const char *fields);
55 static void restore_headers(kcollect_t *ary, const char *datafile);
59 int OutputWidth = 1024;
60 int OutputHeight = 1024;
65 main(int ac, char **av)
71 const char *datafile = NULL;
72 const char *fields = NULL;
80 kcollect_t *dbmAry = NULL;
81 const char *dbmFile = NULL;
86 sysctlbyname("kern.collect_data", NULL, &bytes, NULL, 0);
88 fprintf(stderr, "kern.collect_data not available\n");
92 while ((ch = getopt(ac, av, "o:b:d:r:flsgt:xw:GW:H:")) != -1) {
131 maxtime = strtol(optarg, &suffix, 0);
146 "Illegal suffix in -t option\n");
154 OutputWidth = strtol(optarg, NULL, 0);
157 OutputHeight = strtol(optarg, NULL, 0);
160 fprintf(stderr, "Unknown option %c\n", ch);
165 if (cmd != 'x' && ac != optind) {
166 fprintf(stderr, "Unknown argument %s\n", av[optind]);
174 if (cmd == 'x' || cmd == 'w')
175 start_gnuplot(ac - optind, av + optind, datafile);
179 * Snarf as much data as we can. If we are looping,
180 * snarf less (no point snarfing stuff we already have).
183 sysctlbyname("kern.collect_data", NULL, &bytes, NULL, 0);
185 bytes = sizeof(kcollect_t) * 2;
190 loop_bytes = sizeof(kcollect_t) *
191 (4 + SLEEP_INTERVAL / KCOLLECT_INTERVAL);
192 if (bytes > loop_bytes)
197 sysctlbyname("kern.collect_data", ary, &bytes, NULL, 0);
198 count = bytes / sizeof(kcollect_t);
201 * If we got specified a file to load from: replace the data
205 load_dbm(dbmFile, &dbmAry, &count);
211 adjust_fields(&ary[1], fields);
215 * Delete duplicate entries when looping
219 if ((int)(ary[count-1].ticks - last_ticks) > 0)
226 * Delete any entries beyond the time limit
229 maxtime *= ary[0].hz;
231 if ((int)(ary[0].ticks - ary[count-1].ticks) <
242 dump_text(ary, count, total_count,
243 (fromFile ? DISPLAY_FULL_DATE :
249 dump_dbm(ary, count, datafile);
253 restore_headers(ary, datafile);
258 break; /* NOT REACHED */
261 dump_gnuplot(ary, count);
265 dump_gnuplot(ary, count);
269 dump_gnuplot(ary, count);
272 if (keepalive && !fromFile) {
289 last_ticks = ary[2].ticks;
291 total_count += count - 2;
294 * Loop for incremental aquisition. When outputting to
295 * gunplot, we have to send the whole data-set again so
296 * do not increment loops in that case.
298 if (cmd != 'g' && cmd != 'x' && cmd != 'w')
310 format_output(uintmax_t value,char fmt,uintmax_t scale, char* ret)
319 sprintf(ret, "%5ju.%02ju",
320 value / 100, value % 100);
324 * Percentage fractional x100 (100% = 10000)
326 sprintf(ret,"%4ju.%02ju%%",
327 value / 100, value % 100);
333 humanize_number(buf, sizeof(buf), value, "",
337 sprintf(ret,"%8.8s", buf);
341 * Raw count over period (this is not total)
343 humanize_number(buf, sizeof(buf), value, "",
348 sprintf(ret,"%8.8s", buf);
352 * Total bytes (this is a total), output
355 if (scale > 100000000) {
356 humanize_number(buf, sizeof(buf),
362 humanize_number(buf, sizeof(buf),
368 sprintf(ret,"%8.8s", buf);
371 sprintf(ret,"%s"," ");
378 dump_text(kcollect_t *ary, size_t count, size_t total_count,
379 const char* display_fmt)
390 for (i = count - 1; i >= 2; --i) {
391 if ((total_count & 15) == 0) {
392 if (!strcmp(display_fmt, DISPLAY_FULL_DATE)) {
393 printf("%20s", "timestamp ");
395 printf("%8.8s", "time");
397 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
398 if (ary[1].data[j]) {
400 (char *)&ary[1].data[j]);
409 t = ary[i].realtime.tv_sec;
414 strftime(sbuf, sizeof(sbuf), display_fmt, tmv);
417 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
418 if (ary[1].data[j] == 0)
422 * NOTE: kernel does not have to provide the scale
423 * (that is, the highest likely value), nor
424 * does it make sense in all cases.
426 * Example scale - kernel provides total amount
427 * of memory available for memory related
428 * statistics in the scale field.
430 value = ary[i].data[j];
431 scale = KCOLLECT_GETSCALE(ary[0].data[j]);
432 fmt = KCOLLECT_GETFMT(ary[0].data[j]);
436 format_output(value, fmt, scale, sbuf);
445 /* restores the DBM database header records to current machine */
448 restore_headers(kcollect_t *ary, const char *datafile)
451 char hdr_fmt[] = HDR_FMT;
452 char hdr_title[] = HDR_TITLE;
455 db = dbm_open(datafile, (O_RDWR),
456 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP));
462 "[ERR] database file \"%s\" is read-only, "
463 "check permissions. (%i)\n",
468 "[ERR] opening our database file \"%s\" "
469 "produced an error. (%i)\n",
475 key.dsize = sizeof(HDR_FMT);
476 value.dptr = &ary[0].data;
477 value.dsize = sizeof(uint64_t) * KCOLLECT_ENTRIES;
478 if (dbm_store(db,key,value,DBM_REPLACE) == -1) {
480 "[ERR] error storing the value in "
481 "the database file \"%s\" (%i)\n",
487 key.dptr = hdr_title;
488 key.dsize = sizeof(HDR_FMT);
489 value.dptr = &ary[1].data;
490 value.dsize = sizeof(uint64_t) * KCOLLECT_ENTRIES;
491 if (dbm_store(db,key,value,DBM_REPLACE) == -1) {
493 "[ERR] error storing the value in "
494 "the database file \"%s\" (%i)\n",
505 * Store the array of kcollect_t records in a dbm db database,
506 * path passed in datafile
510 dump_dbm(kcollect_t *ary, size_t count, const char *datafile)
514 db = dbm_open(datafile, (O_RDWR | O_CREAT),
515 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP));
520 "[ERR] database file \"%s\" is read-only, "
521 "check permissions. (%i)\n",
526 "[ERR] opening our database file \"%s\" "
527 "produced an error. (%i)\n",
538 char hdr_fmt[] = HDR_FMT;
539 char hdr_title[] = HDR_TITLE;
541 for (i = 0; i < (count); ++i) {
542 /* first 2 INFO records are special and get 0|1 */
548 key.dptr = hdr_title;
549 key.dsize = sizeof(HDR_FMT);
551 t = ary[i].realtime.tv_sec;
556 strftime(buf, sizeof(buf),
557 DISPLAY_FULL_DATE, tmv);
559 key.dsize = sizeof(buf);
561 value.dptr = ary[i].data;
562 value.dsize = sizeof(uint64_t) * KCOLLECT_ENTRIES;
563 if (dbm_store(db, key, value, DBM_INSERT) == -1) {
565 "[ERR] error storing the value in "
566 "the database file \"%s\" (%i)\n",
578 * Transform a string (str) matching a format string (fmt) into a unix
579 * timestamp and return it used by load_dbm()
583 str2unix(const char* str, const char* fmt){
588 * Reset all the fields because strptime only sets what it
589 * finds, which may lead to undefined members
591 memset(&tm, 0, sizeof(struct tm));
592 strptime(str, fmt, &tm);
599 * Sorts the ckollect_t records by time, to put youngest first,
600 * so desc by timestamp used by load_dbm()
604 rec_comparator(const void *c1, const void *c2)
606 const kcollect_t *k1 = (const kcollect_t*)c1;
607 const kcollect_t *k2 = (const kcollect_t*)c2;
609 if (k1->realtime.tv_sec < k2->realtime.tv_sec)
611 if (k1->realtime.tv_sec > k2->realtime.tv_sec)
617 * Loads the ckollect records from a dbm DB database specified in datafile.
618 * returns the resulting array in ret_ary and the array counter in counter
622 load_dbm(const char* datafile, kcollect_t **ret_ary,
625 DBM * db = dbm_open(datafile,(O_RDONLY),(S_IRUSR|S_IRGRP));
628 size_t recCounter = 0;
629 int headersFound = 0;
633 "[ERR] opening our database \"%s\" produced "
639 for (key = dbm_firstkey(db); key.dptr; key = dbm_nextkey(db)) {
640 value = dbm_fetch(db, key);
641 if (value.dptr != NULL)
645 /* with the count allocate enough memory */
648 *ret_ary = malloc(sizeof(kcollect_t) * recCounter);
649 bzero(*ret_ary, sizeof(kcollect_t) * recCounter);
650 if (*ret_ary == NULL) {
652 "[ERR] failed to allocate enough memory to "
653 "hold the database! Aborting.\n");
660 * Actual data retrieval but only of recCounter
664 key = dbm_firstkey(db);
665 while (key.dptr && c < recCounter) {
666 value = dbm_fetch(db, key);
667 if (value.dptr != NULL) {
668 if (!strcmp(key.dptr, HDR_FMT)) {
672 else if (!strcmp(key.dptr, HDR_TITLE)) {
681 memcpy((*ret_ary)[sc].data,
683 sizeof(uint64_t) * KCOLLECT_ENTRIES);
684 (*ret_ary)[sc].realtime.tv_sec =
688 key = dbm_nextkey(db);
695 * and sort the non-header records.
697 *counter = recCounter;
698 qsort(&(*ret_ary)[2], recCounter - 2, sizeof(kcollect_t), rec_comparator);
701 if (headersFound != 3) {
702 fprintf(stderr, "We could not retrieve all necessary headers, might be the database file is corrupted? (%i)\n", headersFound);
709 dump_fields(kcollect_t *ary)
713 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
714 if (ary[1].data[j] == 0)
717 (char *)&ary[1].data[j],
718 KCOLLECT_GETFMT(ary[0].data[j]));
723 adjust_fields(kcollect_t *ent, const char *fields)
725 char *copy = strdup(fields);
727 int selected[KCOLLECT_ENTRIES];
730 bzero(selected, sizeof(selected));
732 word = strtok(copy, ", \t");
734 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {
735 if (strncmp(word, (char *)&ent->data[j], 8) == 0) {
740 word = strtok(NULL, ", \t");
743 for (j = 0; j < KCOLLECT_ENTRIES; ++j) {