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