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