Annotation of src/usr.bin/kstat/kstat.c, Revision 1.10
1.10 ! cheloha 1: /* $OpenBSD: kstat.c,v 1.9 2022/04/22 00:29:20 dlg Exp $ */
1.1 dlg 2:
3: /*
4: * Copyright (c) 2020 David Gwynne <dlg@openbsd.org>
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15: */
16:
17: #include <ctype.h>
1.10 ! cheloha 18: #include <limits.h>
! 19: #include <signal.h>
1.1 dlg 20: #include <stdio.h>
21: #include <stdlib.h>
22: #include <stddef.h>
23: #include <string.h>
24: #include <inttypes.h>
1.2 dlg 25: #include <fnmatch.h>
1.1 dlg 26: #include <fcntl.h>
1.3 dlg 27: #include <unistd.h>
1.1 dlg 28: #include <errno.h>
29: #include <err.h>
30: #include <vis.h>
31:
32: #include <sys/tree.h>
33: #include <sys/ioctl.h>
34: #include <sys/time.h>
1.2 dlg 35: #include <sys/queue.h>
1.1 dlg 36:
37: #include <sys/kstat.h>
38:
39: #ifndef roundup
1.2 dlg 40: #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
1.1 dlg 41: #endif
42:
1.2 dlg 43: #ifndef nitems
44: #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
45: #endif
46:
47: #ifndef ISSET
48: #define ISSET(_i, _m) ((_i) & (_m))
49: #endif
50:
51: #ifndef SET
52: #define SET(_i, _m) ((_i) |= (_m))
53: #endif
54:
55: #define str_is_empty(_str) (*(_str) == '\0')
56:
1.1 dlg 57: #define DEV_KSTAT "/dev/kstat"
58:
1.2 dlg 59: struct kstat_filter {
60: TAILQ_ENTRY(kstat_filter) kf_entry;
61: const char *kf_provider;
62: const char *kf_name;
63: unsigned int kf_flags;
64: #define KSTAT_FILTER_F_INST (1 << 0)
65: #define KSTAT_FILTER_F_UNIT (1 << 1)
66: unsigned int kf_instance;
67: unsigned int kf_unit;
68: };
69:
70: TAILQ_HEAD(kstat_filters, kstat_filter);
71:
1.4 dlg 72: struct kstat_entry {
73: struct kstat_req kstat;
74: RBT_ENTRY(kstat_entry) entry;
75: int serrno;
76: };
77:
78: RBT_HEAD(kstat_tree, kstat_entry);
79:
80: static inline int
81: kstat_cmp(const struct kstat_entry *ea, const struct kstat_entry *eb)
82: {
83: const struct kstat_req *a = &ea->kstat;
84: const struct kstat_req *b = &eb->kstat;
85: int rv;
86:
87: rv = strncmp(a->ks_provider, b->ks_provider, sizeof(a->ks_provider));
88: if (rv != 0)
89: return (rv);
90: if (a->ks_instance > b->ks_instance)
91: return (1);
92: if (a->ks_instance < b->ks_instance)
93: return (-1);
94:
95: rv = strncmp(a->ks_name, b->ks_name, sizeof(a->ks_name));
96: if (rv != 0)
97: return (rv);
98: if (a->ks_unit > b->ks_unit)
99: return (1);
100: if (a->ks_unit < b->ks_unit)
101: return (-1);
102:
103: return (0);
104: }
105:
106: RBT_PROTOTYPE(kstat_tree, kstat_entry, entry, kstat_cmp);
107: RBT_GENERATE(kstat_tree, kstat_entry, entry, kstat_cmp);
108:
1.10 ! cheloha 109: static void handle_alrm(int);
1.2 dlg 110: static struct kstat_filter *
111: kstat_filter_parse(char *);
112: static int kstat_filter_entry(struct kstat_filters *,
113: const struct kstat_req *);
114:
1.3 dlg 115: static void kstat_list(struct kstat_tree *, int, unsigned int,
116: struct kstat_filters *);
117: static void kstat_print(struct kstat_tree *);
1.5 dlg 118: static void kstat_read(struct kstat_tree *, int);
1.1 dlg 119:
120: __dead static void
121: usage(void)
122: {
123: extern char *__progname;
1.2 dlg 124:
1.5 dlg 125: fprintf(stderr, "usage: %s [-w wait] "
1.6 schwarze 126: "[name | provider:0:name:unit] ...\n", __progname);
1.2 dlg 127:
1.1 dlg 128: exit(1);
129: }
130:
131: int
132: main(int argc, char *argv[])
133: {
1.2 dlg 134: struct kstat_filters kfs = TAILQ_HEAD_INITIALIZER(kfs);
1.3 dlg 135: struct kstat_tree kt = RBT_INITIALIZER();
1.1 dlg 136: unsigned int version;
137: int fd;
1.5 dlg 138: const char *errstr;
139: int ch;
1.10 ! cheloha 140: struct itimerval itv;
! 141: sigset_t empty, mask;
1.2 dlg 142: int i;
1.10 ! cheloha 143: unsigned int wait = 0;
1.2 dlg 144:
1.5 dlg 145: while ((ch = getopt(argc, argv, "w:")) != -1) {
146: switch (ch) {
147: case 'w':
1.10 ! cheloha 148: wait = strtonum(optarg, 1, UINT_MAX, &errstr);
1.5 dlg 149: if (errstr != NULL)
1.10 ! cheloha 150: errx(1, "wait is %s: %s", errstr, optarg);
1.5 dlg 151: break;
152: default:
153: usage();
154: }
155: }
156:
157: argc -= optind;
158: argv += optind;
159:
160: for (i = 0; i < argc; i++) {
1.2 dlg 161: struct kstat_filter *kf = kstat_filter_parse(argv[i]);
162: TAILQ_INSERT_TAIL(&kfs, kf, kf_entry);
163: }
1.1 dlg 164:
165: fd = open(DEV_KSTAT, O_RDONLY);
166: if (fd == -1)
167: err(1, "%s", DEV_KSTAT);
168:
169: if (ioctl(fd, KSTATIOC_VERSION, &version) == -1)
170: err(1, "kstat version");
171:
1.3 dlg 172: kstat_list(&kt, fd, version, &kfs);
173: kstat_print(&kt);
1.2 dlg 174:
1.10 ! cheloha 175: if (wait == 0)
1.5 dlg 176: return (0);
177:
1.10 ! cheloha 178: if (signal(SIGALRM, handle_alrm) == SIG_ERR)
! 179: err(1, "signal");
! 180: sigemptyset(&empty);
! 181: sigemptyset(&mask);
! 182: sigaddset(&mask, SIGALRM);
! 183: if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
! 184: err(1, "sigprocmask");
! 185:
! 186: itv.it_value.tv_sec = wait;
! 187: itv.it_value.tv_usec = 0;
! 188: itv.it_interval = itv.it_value;
! 189: if (setitimer(ITIMER_REAL, &itv, NULL) == -1)
! 190: err(1, "setitimer");
! 191:
1.5 dlg 192: for (;;) {
1.10 ! cheloha 193: sigsuspend(&empty);
1.5 dlg 194: kstat_read(&kt, fd);
195: kstat_print(&kt);
196: }
197:
1.2 dlg 198: return (0);
199: }
200:
201: static struct kstat_filter *
202: kstat_filter_parse(char *arg)
203: {
204: struct kstat_filter *kf;
205: const char *errstr;
206: char *argv[4];
207: size_t argc;
208:
209: for (argc = 0; argc < nitems(argv); argc++) {
210: char *s = strsep(&arg, ":");
211: if (s == NULL)
212: break;
213:
214: argv[argc] = s;
215: }
216: if (arg != NULL)
217: usage();
218:
219: kf = malloc(sizeof(*kf));
220: if (kf == NULL)
221: err(1, NULL);
222:
223: memset(kf, 0, sizeof(*kf));
224:
225: switch (argc) {
226: case 1:
227: if (str_is_empty(argv[0]))
228: errx(1, "empty name");
229:
230: kf->kf_name = argv[0];
231: break;
232: case 4:
233: if (!str_is_empty(argv[0]))
234: kf->kf_provider = argv[0];
235: if (!str_is_empty(argv[1])) {
236: kf->kf_instance =
237: strtonum(argv[1], 0, 0xffffffffU, &errstr);
238: if (errstr != NULL) {
239: errx(1, "%s:%s:%s:%s: instance %s: %s",
240: argv[0], argv[1], argv[2], argv[3],
241: argv[1], errstr);
242: }
243: SET(kf->kf_flags, KSTAT_FILTER_F_INST);
244: }
245: if (!str_is_empty(argv[2]))
246: kf->kf_name = argv[2];
247: if (!str_is_empty(argv[3])) {
248: kf->kf_unit =
249: strtonum(argv[3], 0, 0xffffffffU, &errstr);
250: if (errstr != NULL) {
1.8 dlg 251: errx(1, "%s:%s:%s:%s: unit %s: %s",
1.2 dlg 252: argv[0], argv[1], argv[2], argv[3],
1.8 dlg 253: argv[3], errstr);
1.2 dlg 254: }
1.8 dlg 255: SET(kf->kf_flags, KSTAT_FILTER_F_UNIT);
1.2 dlg 256: }
257: break;
258: default:
259: usage();
260: }
261:
262: return (kf);
263: }
264:
265: static int
266: kstat_filter_entry(struct kstat_filters *kfs, const struct kstat_req *ksreq)
267: {
268: struct kstat_filter *kf;
269:
270: if (TAILQ_EMPTY(kfs))
271: return (1);
272:
273: TAILQ_FOREACH(kf, kfs, kf_entry) {
274: if (kf->kf_provider != NULL) {
275: if (fnmatch(kf->kf_provider, ksreq->ks_provider,
276: FNM_NOESCAPE | FNM_LEADING_DIR) == FNM_NOMATCH)
277: continue;
278: }
279: if (ISSET(kf->kf_flags, KSTAT_FILTER_F_INST)) {
280: if (kf->kf_instance != ksreq->ks_instance)
281: continue;
282: }
283: if (kf->kf_name != NULL) {
284: if (fnmatch(kf->kf_name, ksreq->ks_name,
285: FNM_NOESCAPE | FNM_LEADING_DIR) == FNM_NOMATCH)
286: continue;
287: }
288: if (ISSET(kf->kf_flags, KSTAT_FILTER_F_UNIT)) {
289: if (kf->kf_unit != ksreq->ks_unit)
290: continue;
291: }
292:
293: return (1);
294: }
1.1 dlg 295:
296: return (0);
297: }
298:
299: static int
300: printable(int ch)
301: {
302: if (ch == '\0')
303: return ('_');
304: if (!isprint(ch))
305: return ('~');
306: return (ch);
307: }
308:
309: static void
310: hexdump(const void *d, size_t datalen)
311: {
312: const uint8_t *data = d;
313: size_t i, j = 0;
314:
315: for (i = 0; i < datalen; i += j) {
316: printf("%4zu: ", i);
317:
318: for (j = 0; j < 16 && i+j < datalen; j++)
319: printf("%02x ", data[i + j]);
320: while (j++ < 16)
321: printf(" ");
322: printf("|");
323:
324: for (j = 0; j < 16 && i+j < datalen; j++)
325: putchar(printable(data[i + j]));
326: printf("|\n");
327: }
328: }
329:
330: static void
331: strdump(const void *s, size_t len)
332: {
333: const char *str = s;
334: char dst[8];
335: size_t i;
336:
337: for (i = 0; i < len; i++) {
338: char ch = str[i];
339: if (ch == '\0')
340: break;
341:
342: vis(dst, ch, VIS_TAB | VIS_NL, 0);
343: printf("%s", dst);
344: }
345: }
346:
347: static void
348: strdumpnl(const void *s, size_t len)
349: {
350: strdump(s, len);
351: printf("\n");
352: }
353:
354: static void
355: kstat_kv(const void *d, ssize_t len)
356: {
357: const uint8_t *buf;
358: const struct kstat_kv *kv;
359: ssize_t blen;
360: void (*trailer)(const void *, size_t);
361: double f;
362:
363: if (len < (ssize_t)sizeof(*kv)) {
364: warn("short kv (len %zu < size %zu)", len, sizeof(*kv));
365: return;
366: }
367:
368: buf = d;
369: do {
370: kv = (const struct kstat_kv *)buf;
371:
372: buf += sizeof(*kv);
373: len -= sizeof(*kv);
374:
375: blen = 0;
376: trailer = hexdump;
377:
378: printf("%16.16s: ", kv->kv_key);
379:
380: switch (kv->kv_type) {
381: case KSTAT_KV_T_NULL:
382: printf("null");
383: break;
384: case KSTAT_KV_T_BOOL:
385: printf("%s", kstat_kv_bool(kv) ? "true" : "false");
386: break;
387: case KSTAT_KV_T_COUNTER64:
388: case KSTAT_KV_T_UINT64:
389: printf("%" PRIu64, kstat_kv_u64(kv));
390: break;
391: case KSTAT_KV_T_INT64:
392: printf("%" PRId64, kstat_kv_s64(kv));
393: break;
394: case KSTAT_KV_T_COUNTER32:
395: case KSTAT_KV_T_UINT32:
396: printf("%" PRIu32, kstat_kv_u32(kv));
397: break;
398: case KSTAT_KV_T_INT32:
399: printf("%" PRId32, kstat_kv_s32(kv));
1.9 dlg 400: break;
401: case KSTAT_KV_T_COUNTER16:
402: case KSTAT_KV_T_UINT16:
403: printf("%" PRIu16, kstat_kv_u16(kv));
404: break;
405: case KSTAT_KV_T_INT16:
406: printf("%" PRId16, kstat_kv_s16(kv));
1.1 dlg 407: break;
408: case KSTAT_KV_T_STR:
409: blen = kstat_kv_len(kv);
410: trailer = strdumpnl;
411: break;
412: case KSTAT_KV_T_BYTES:
413: blen = kstat_kv_len(kv);
414: trailer = hexdump;
415:
416: printf("\n");
417: break;
418:
419: case KSTAT_KV_T_ISTR:
420: strdump(kstat_kv_istr(kv), sizeof(kstat_kv_istr(kv)));
421: break;
422:
423: case KSTAT_KV_T_TEMP:
424: f = kstat_kv_temp(kv);
425: printf("%.2f degC", (f - 273150000.0) / 1000000.0);
426: break;
427:
428: default:
429: printf("unknown type %u, stopping\n", kv->kv_type);
430: return;
431: }
432:
433: switch (kv->kv_unit) {
434: case KSTAT_KV_U_NONE:
435: break;
436: case KSTAT_KV_U_PACKETS:
437: printf(" packets");
438: break;
439: case KSTAT_KV_U_BYTES:
440: printf(" bytes");
441: break;
442: case KSTAT_KV_U_CYCLES:
443: printf(" cycles");
444: break;
445:
446: default:
447: printf(" unit-type-%u", kv->kv_unit);
448: break;
449: }
450:
451: if (blen > 0) {
452: if (blen > len) {
453: blen = len;
454: }
455:
456: (*trailer)(buf, blen);
457: } else
458: printf("\n");
459:
460: blen = roundup(blen, KSTAT_KV_ALIGN);
461: buf += blen;
462: len -= blen;
463: } while (len >= (ssize_t)sizeof(*kv));
464: }
465:
466: static void
1.3 dlg 467: kstat_list(struct kstat_tree *kt, int fd, unsigned int version,
468: struct kstat_filters *kfs)
1.1 dlg 469: {
470: struct kstat_entry *kse;
471: struct kstat_req *ksreq;
472: size_t len;
473: uint64_t id = 0;
474:
475: for (;;) {
476: kse = malloc(sizeof(*kse));
477: if (kse == NULL)
478: err(1, NULL);
479:
480: memset(kse, 0, sizeof(*kse));
481: ksreq = &kse->kstat;
482: ksreq->ks_version = version;
483: ksreq->ks_id = ++id;
484:
485: ksreq->ks_datalen = len = 64; /* magic */
486: ksreq->ks_data = malloc(len);
487: if (ksreq->ks_data == NULL)
488: err(1, "data alloc");
489:
490: if (ioctl(fd, KSTATIOC_NFIND_ID, ksreq) == -1) {
491: if (errno == ENOENT) {
492: free(ksreq->ks_data);
493: free(kse);
494: break;
495: }
496:
497: kse->serrno = errno;
1.2 dlg 498: } else
499: id = ksreq->ks_id;
500:
501: if (!kstat_filter_entry(kfs, ksreq)) {
502: free(ksreq->ks_data);
503: free(kse);
504: continue;
1.1 dlg 505: }
506:
1.3 dlg 507: if (RBT_INSERT(kstat_tree, kt, kse) != NULL)
1.2 dlg 508: errx(1, "duplicate kstat entry");
509:
510: if (kse->serrno != 0)
511: continue;
512:
1.1 dlg 513: while (ksreq->ks_datalen > len) {
514: len = ksreq->ks_datalen;
515: ksreq->ks_data = realloc(ksreq->ks_data, len);
516: if (ksreq->ks_data == NULL)
517: err(1, "data resize (%zu)", len);
518:
519: if (ioctl(fd, KSTATIOC_FIND_ID, ksreq) == -1)
1.2 dlg 520: err(1, "find id %llu", ksreq->ks_id);
1.1 dlg 521: }
522: }
1.3 dlg 523: }
524:
525: static void
526: kstat_print(struct kstat_tree *kt)
527: {
528: struct kstat_entry *kse;
529: struct kstat_req *ksreq;
1.1 dlg 530:
1.3 dlg 531: RBT_FOREACH(kse, kstat_tree, kt) {
1.1 dlg 532: ksreq = &kse->kstat;
533: printf("%s:%u:%s:%u\n",
534: ksreq->ks_provider, ksreq->ks_instance,
535: ksreq->ks_name, ksreq->ks_unit);
536: if (kse->serrno != 0) {
537: printf("\t%s\n", strerror(kse->serrno));
538: continue;
539: }
540: switch (ksreq->ks_type) {
541: case KSTAT_T_RAW:
542: hexdump(ksreq->ks_data, ksreq->ks_datalen);
543: break;
544: case KSTAT_T_KV:
545: kstat_kv(ksreq->ks_data, ksreq->ks_datalen);
546: break;
547: default:
548: hexdump(ksreq->ks_data, ksreq->ks_datalen);
549: break;
550: }
1.5 dlg 551: }
1.7 dlg 552:
553: fflush(stdout);
1.5 dlg 554: }
555:
556: static void
557: kstat_read(struct kstat_tree *kt, int fd)
558: {
559: struct kstat_entry *kse;
560: struct kstat_req *ksreq;
561:
562: RBT_FOREACH(kse, kstat_tree, kt) {
563: ksreq = &kse->kstat;
564: if (ioctl(fd, KSTATIOC_FIND_ID, ksreq) == -1)
565: err(1, "update id %llu", ksreq->ks_id);
1.1 dlg 566: }
1.10 ! cheloha 567: }
! 568:
! 569: static void
! 570: handle_alrm(int signo)
! 571: {
1.1 dlg 572: }