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 "kcollect.h" 33 34 #define SLEEP_INTERVAL 60 /* minimum is KCOLLECT_INTERVAL */ 35 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); 40 41 FILE *OutFP; 42 int UseGMT; 43 int OutputWidth = 1024; 44 int OutputHeight = 1024; 45 int SmoothOpt; 46 47 int 48 main(int ac, char **av) 49 { 50 kcollect_t *ary; 51 size_t bytes = 0; 52 size_t count; 53 size_t total_count; 54 const char *datafile = NULL; 55 const char *fields = NULL; 56 int cmd = 't'; 57 int ch; 58 int keepalive = 0; 59 int last_ticks; 60 int loops = 0; 61 int maxtime = 0; 62 63 OutFP = stdout; 64 65 sysctlbyname("kern.collect_data", NULL, &bytes, NULL, 0); 66 if (bytes == 0) { 67 fprintf(stderr, "kern.collect_data not available\n"); 68 exit(1); 69 } 70 71 while ((ch = getopt(ac, av, "o:b:flsgt:xw:GW:H:")) != -1) { 72 char *suffix; 73 74 switch(ch) { 75 case 'o': 76 fields = optarg; 77 break; 78 case 'b': 79 datafile = optarg; 80 cmd = 'b'; 81 break; 82 case 'f': 83 keepalive = 1; 84 break; 85 case 'l': 86 cmd = 'l'; 87 break; 88 case 's': 89 SmoothOpt = 1; 90 break; 91 case 'w': 92 datafile = optarg; 93 cmd = 'w'; 94 break; 95 case 'g': 96 cmd = 'g'; 97 break; 98 case 'x': 99 cmd = 'x'; 100 break; 101 case 't': 102 maxtime = strtol(optarg, &suffix, 0); 103 switch(*suffix) { 104 case 'd': 105 maxtime *= 24; 106 /* fall through */ 107 case 'h': 108 maxtime *= 60; 109 /* fall through */ 110 case 'm': 111 maxtime *= 60; 112 break; 113 case 0: 114 break; 115 default: 116 fprintf(stderr, 117 "Illegal suffix in -t option\n"); 118 exit(1); 119 } 120 break; 121 case 'G': 122 UseGMT = 1; 123 break; 124 case 'W': 125 OutputWidth = strtol(optarg, NULL, 0); 126 break; 127 case 'H': 128 OutputHeight = strtol(optarg, NULL, 0); 129 break; 130 default: 131 fprintf(stderr, "Unknown option %c\n", ch); 132 exit(1); 133 /* NOT REACHED */ 134 } 135 } 136 if (cmd != 'x' && ac != optind) { 137 fprintf(stderr, "Unknown argument %s\n", av[optind]); 138 exit(1); 139 /* NOT REACHED */ 140 } 141 142 total_count = 0; 143 last_ticks = 0; 144 145 if (cmd == 'x' || cmd == 'w') 146 start_gnuplot(ac - optind, av + optind, datafile); 147 148 do { 149 /* 150 * Snarf as much data as we can. If we are looping, 151 * snarf less (no point snarfing stuff we already have). 152 */ 153 bytes = 0; 154 sysctlbyname("kern.collect_data", NULL, &bytes, NULL, 0); 155 if (cmd == 'l') 156 bytes = sizeof(kcollect_t) * 2; 157 158 if (loops) { 159 size_t loop_bytes; 160 161 loop_bytes = sizeof(kcollect_t) * 162 (4 + SLEEP_INTERVAL / KCOLLECT_INTERVAL); 163 if (bytes > loop_bytes) 164 bytes = loop_bytes; 165 } 166 167 ary = malloc(bytes); 168 sysctlbyname("kern.collect_data", ary, &bytes, NULL, 0); 169 170 if (fields) 171 adjust_fields(&ary[1], fields); 172 173 count = bytes / sizeof(kcollect_t); 174 175 /* 176 * Delete duplicate entries when looping 177 */ 178 if (loops) { 179 while (count > 2) { 180 if ((int)(ary[count-1].ticks - last_ticks) > 0) 181 break; 182 --count; 183 } 184 } 185 186 /* 187 * Delete any entries beyond the time limit 188 */ 189 if (maxtime) { 190 maxtime *= ary[0].hz; 191 while (count > 2) { 192 if ((int)(ary[0].ticks - ary[count-1].ticks) < 193 maxtime) { 194 break; 195 } 196 --count; 197 } 198 } 199 200 switch(cmd) { 201 case 't': 202 if (count > 2) 203 dump_text(ary, count, total_count); 204 break; 205 case 'b': 206 if (count > 2) 207 dump_dbm(ary, count, datafile); 208 break; 209 case 'l': 210 dump_fields(ary); 211 exit(0); 212 break; /* NOT REACHED */ 213 case 'g': 214 if (count > 2) 215 dump_gnuplot(ary, count); 216 break; 217 case 'w': 218 if (count >= 2) 219 dump_gnuplot(ary, count); 220 break; 221 case 'x': 222 if (count > 2) 223 dump_gnuplot(ary, count); 224 break; 225 } 226 if (keepalive) { 227 fflush(OutFP); 228 fflush(stdout); 229 switch(cmd) { 230 case 't': 231 sleep(1); 232 break; 233 case 'x': 234 case 'g': 235 case 'w': 236 sleep(60); 237 break; 238 default: 239 sleep(10); 240 break; 241 } 242 } 243 last_ticks = ary[2].ticks; 244 if (count >= 2) 245 total_count += count - 2; 246 247 /* 248 * Loop for incremental aquisition. When outputting to 249 * gunplot, we have to send the whole data-set again so 250 * do not increment loops in that case. 251 */ 252 if (cmd != 'g' && cmd != 'x' && cmd != 'w') 253 ++loops; 254 255 free(ary); 256 } while (keepalive); 257 258 if (cmd == 'x') 259 pclose(OutFP); 260 } 261 262 static 263 void 264 dump_text(kcollect_t *ary, size_t count, size_t total_count) 265 { 266 int j; 267 int i; 268 uintmax_t scale; 269 uintmax_t value; 270 char fmt; 271 char buf[9]; 272 struct tm *tmv; 273 time_t t; 274 275 for (i = count - 1; i >= 2; --i) { 276 if ((total_count & 15) == 0) { 277 printf("%8.8s", "time"); 278 for (j = 0; j < KCOLLECT_ENTRIES; ++j) { 279 if (ary[1].data[j]) { 280 printf(" %8.8s", 281 (char *)&ary[1].data[j]); 282 } 283 } 284 printf("\n"); 285 } 286 287 /* 288 * Timestamp 289 */ 290 t = ary[i].realtime.tv_sec; 291 if (UseGMT) 292 tmv = gmtime(&t); 293 else 294 tmv = localtime(&t); 295 strftime(buf, sizeof(buf), "%H:%M:%S", tmv); 296 printf("%8.8s", buf); 297 298 for (j = 0; j < KCOLLECT_ENTRIES; ++j) { 299 if (ary[1].data[j] == 0) 300 continue; 301 302 /* 303 * NOTE: kernel does not have to provide the scale 304 * (that is, the highest likely value), nor 305 * does it make sense in all cases. 306 * 307 * Example scale - kernel provides total amount 308 * of memory available for memory related 309 * statistics in the scale field. 310 */ 311 value = ary[i].data[j]; 312 scale = KCOLLECT_GETSCALE(ary[0].data[j]); 313 fmt = KCOLLECT_GETFMT(ary[0].data[j]); 314 315 printf(" "); 316 317 switch(fmt) { 318 case '2': 319 /* 320 * fractional x100 321 */ 322 printf("%5ju.%02ju", value / 100, value % 100); 323 break; 324 case 'p': 325 /* 326 * Percentage fractional x100 (100% = 10000) 327 */ 328 printf("%4ju.%02ju%%", 329 value / 100, value % 100); 330 break; 331 case 'm': 332 /* 333 * Megabytes 334 */ 335 humanize_number(buf, sizeof(buf), value, "", 336 2, 337 HN_FRACTIONAL | 338 HN_NOSPACE); 339 printf("%8.8s", buf); 340 break; 341 case 'c': 342 /* 343 * Raw count over period (this is not total) 344 */ 345 humanize_number(buf, sizeof(buf), value, "", 346 HN_AUTOSCALE, 347 HN_FRACTIONAL | 348 HN_NOSPACE | 349 HN_DIVISOR_1000); 350 printf("%8.8s", buf); 351 break; 352 case 'b': 353 /* 354 * Total bytes (this is a total), output 355 * in megabytes. 356 */ 357 if (scale > 100000000) { 358 humanize_number(buf, sizeof(buf), 359 value, "", 360 3, 361 HN_FRACTIONAL | 362 HN_NOSPACE); 363 } else { 364 humanize_number(buf, sizeof(buf), 365 value, "", 366 2, 367 HN_FRACTIONAL | 368 HN_NOSPACE); 369 } 370 printf("%8.8s", buf); 371 break; 372 default: 373 printf(" "); 374 break; 375 } 376 } 377 printf("\n"); 378 ++total_count; 379 } 380 } 381 382 static void 383 dump_dbm(kcollect_t *ary __unused, size_t count __unused, const char *datafile __unused) 384 { 385 } 386 387 static void 388 dump_fields(kcollect_t *ary) 389 { 390 int j; 391 392 for (j = 0; j < KCOLLECT_ENTRIES; ++j) { 393 if (ary[1].data[j] == 0) 394 continue; 395 printf("%8.8s %c\n", 396 (char *)&ary[1].data[j], 397 KCOLLECT_GETFMT(ary[0].data[j])); 398 } 399 } 400 401 static void 402 adjust_fields(kcollect_t *ent, const char *fields) 403 { 404 char *copy = strdup(fields); 405 char *word; 406 int selected[KCOLLECT_ENTRIES]; 407 int j; 408 409 bzero(selected, sizeof(selected)); 410 411 word = strtok(copy, ", \t"); 412 while (word) { 413 for (j = 0; j < KCOLLECT_ENTRIES; ++j) { 414 if (strncmp(word, (char *)&ent->data[j], 8) == 0) { 415 selected[j] = 1; 416 break; 417 } 418 } 419 word = strtok(NULL, ", \t"); 420 } 421 free(copy); 422 for (j = 0; j < KCOLLECT_ENTRIES; ++j) { 423 if (!selected[j]) 424 ent->data[j] = 0; 425 } 426 } 427