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