Annotation of src/usr.bin/kstat/kstat.c, Revision 1.1
1.1 ! dlg 1: /* $OpenBSD$ */
! 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>
! 23: #include <fcntl.h>
! 24: #include <errno.h>
! 25: #include <err.h>
! 26: #include <vis.h>
! 27:
! 28: #include <sys/tree.h>
! 29: #include <sys/ioctl.h>
! 30: #include <sys/time.h>
! 31:
! 32: #include <sys/kstat.h>
! 33:
! 34: #ifndef roundup
! 35: #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
! 36: #endif
! 37:
! 38: #define DEV_KSTAT "/dev/kstat"
! 39:
! 40: static void kstat_list(int, unsigned int);
! 41:
! 42: #if 0
! 43: __dead static void
! 44: usage(void)
! 45: {
! 46: extern char *__progname;
! 47: fprintf(stderr, "usage: %s\n", __progname);
! 48: exit(1);
! 49: }
! 50: #endif
! 51:
! 52: int
! 53: main(int argc, char *argv[])
! 54: {
! 55: unsigned int version;
! 56: int fd;
! 57:
! 58: fd = open(DEV_KSTAT, O_RDONLY);
! 59: if (fd == -1)
! 60: err(1, "%s", DEV_KSTAT);
! 61:
! 62: if (ioctl(fd, KSTATIOC_VERSION, &version) == -1)
! 63: err(1, "kstat version");
! 64:
! 65: kstat_list(fd, version);
! 66:
! 67: return (0);
! 68: }
! 69:
! 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:
! 107: static int
! 108: printable(int ch)
! 109: {
! 110: if (ch == '\0')
! 111: return ('_');
! 112: if (!isprint(ch))
! 113: return ('~');
! 114: return (ch);
! 115: }
! 116:
! 117: static void
! 118: hexdump(const void *d, size_t datalen)
! 119: {
! 120: const uint8_t *data = d;
! 121: size_t i, j = 0;
! 122:
! 123: for (i = 0; i < datalen; i += j) {
! 124: printf("%4zu: ", i);
! 125:
! 126: for (j = 0; j < 16 && i+j < datalen; j++)
! 127: printf("%02x ", data[i + j]);
! 128: while (j++ < 16)
! 129: printf(" ");
! 130: printf("|");
! 131:
! 132: for (j = 0; j < 16 && i+j < datalen; j++)
! 133: putchar(printable(data[i + j]));
! 134: printf("|\n");
! 135: }
! 136: }
! 137:
! 138: static void
! 139: strdump(const void *s, size_t len)
! 140: {
! 141: const char *str = s;
! 142: char dst[8];
! 143: size_t i;
! 144:
! 145: for (i = 0; i < len; i++) {
! 146: char ch = str[i];
! 147: if (ch == '\0')
! 148: break;
! 149:
! 150: vis(dst, ch, VIS_TAB | VIS_NL, 0);
! 151: printf("%s", dst);
! 152: }
! 153: }
! 154:
! 155: static void
! 156: strdumpnl(const void *s, size_t len)
! 157: {
! 158: strdump(s, len);
! 159: printf("\n");
! 160: }
! 161:
! 162: static void
! 163: kstat_kv(const void *d, ssize_t len)
! 164: {
! 165: const uint8_t *buf;
! 166: const struct kstat_kv *kv;
! 167: ssize_t blen;
! 168: void (*trailer)(const void *, size_t);
! 169: double f;
! 170:
! 171: if (len < (ssize_t)sizeof(*kv)) {
! 172: warn("short kv (len %zu < size %zu)", len, sizeof(*kv));
! 173: return;
! 174: }
! 175:
! 176: buf = d;
! 177: do {
! 178: kv = (const struct kstat_kv *)buf;
! 179:
! 180: buf += sizeof(*kv);
! 181: len -= sizeof(*kv);
! 182:
! 183: blen = 0;
! 184: trailer = hexdump;
! 185:
! 186: printf("%16.16s: ", kv->kv_key);
! 187:
! 188: switch (kv->kv_type) {
! 189: case KSTAT_KV_T_NULL:
! 190: printf("null");
! 191: break;
! 192: case KSTAT_KV_T_BOOL:
! 193: printf("%s", kstat_kv_bool(kv) ? "true" : "false");
! 194: break;
! 195: case KSTAT_KV_T_COUNTER64:
! 196: case KSTAT_KV_T_UINT64:
! 197: printf("%" PRIu64, kstat_kv_u64(kv));
! 198: break;
! 199: case KSTAT_KV_T_INT64:
! 200: printf("%" PRId64, kstat_kv_s64(kv));
! 201: break;
! 202: case KSTAT_KV_T_COUNTER32:
! 203: case KSTAT_KV_T_UINT32:
! 204: printf("%" PRIu32, kstat_kv_u32(kv));
! 205: break;
! 206: case KSTAT_KV_T_INT32:
! 207: printf("%" PRId32, kstat_kv_s32(kv));
! 208: break;
! 209: case KSTAT_KV_T_STR:
! 210: blen = kstat_kv_len(kv);
! 211: trailer = strdumpnl;
! 212: break;
! 213: case KSTAT_KV_T_BYTES:
! 214: blen = kstat_kv_len(kv);
! 215: trailer = hexdump;
! 216:
! 217: printf("\n");
! 218: break;
! 219:
! 220: case KSTAT_KV_T_ISTR:
! 221: strdump(kstat_kv_istr(kv), sizeof(kstat_kv_istr(kv)));
! 222: break;
! 223:
! 224: case KSTAT_KV_T_TEMP:
! 225: f = kstat_kv_temp(kv);
! 226: printf("%.2f degC", (f - 273150000.0) / 1000000.0);
! 227: break;
! 228:
! 229: default:
! 230: printf("unknown type %u, stopping\n", kv->kv_type);
! 231: return;
! 232: }
! 233:
! 234: switch (kv->kv_unit) {
! 235: case KSTAT_KV_U_NONE:
! 236: break;
! 237: case KSTAT_KV_U_PACKETS:
! 238: printf(" packets");
! 239: break;
! 240: case KSTAT_KV_U_BYTES:
! 241: printf(" bytes");
! 242: break;
! 243: case KSTAT_KV_U_CYCLES:
! 244: printf(" cycles");
! 245: break;
! 246:
! 247: default:
! 248: printf(" unit-type-%u", kv->kv_unit);
! 249: break;
! 250: }
! 251:
! 252: if (blen > 0) {
! 253: if (blen > len) {
! 254: blen = len;
! 255: }
! 256:
! 257: (*trailer)(buf, blen);
! 258: } else
! 259: printf("\n");
! 260:
! 261: blen = roundup(blen, KSTAT_KV_ALIGN);
! 262: buf += blen;
! 263: len -= blen;
! 264: } while (len >= (ssize_t)sizeof(*kv));
! 265: }
! 266:
! 267: static void
! 268: kstat_list(int fd, unsigned int version)
! 269: {
! 270: struct kstat_entry *kse;
! 271: struct kstat_req *ksreq;
! 272: size_t len;
! 273: uint64_t id = 0;
! 274: struct kstat_tree kstat_tree = RBT_INITIALIZER();
! 275:
! 276: for (;;) {
! 277: kse = malloc(sizeof(*kse));
! 278: if (kse == NULL)
! 279: err(1, NULL);
! 280:
! 281: memset(kse, 0, sizeof(*kse));
! 282: ksreq = &kse->kstat;
! 283: ksreq->ks_version = version;
! 284: ksreq->ks_id = ++id;
! 285:
! 286: ksreq->ks_datalen = len = 64; /* magic */
! 287: ksreq->ks_data = malloc(len);
! 288: if (ksreq->ks_data == NULL)
! 289: err(1, "data alloc");
! 290:
! 291: if (ioctl(fd, KSTATIOC_NFIND_ID, ksreq) == -1) {
! 292: if (errno == ENOENT) {
! 293: free(ksreq->ks_data);
! 294: free(kse);
! 295: break;
! 296: }
! 297:
! 298: kse->serrno = errno;
! 299: goto next;
! 300: }
! 301:
! 302: while (ksreq->ks_datalen > len) {
! 303: len = ksreq->ks_datalen;
! 304: ksreq->ks_data = realloc(ksreq->ks_data, len);
! 305: if (ksreq->ks_data == NULL)
! 306: err(1, "data resize (%zu)", len);
! 307:
! 308: if (ioctl(fd, KSTATIOC_FIND_ID, ksreq) == -1)
! 309: err(1, "find id %llu", id);
! 310: }
! 311:
! 312: next:
! 313: if (RBT_INSERT(kstat_tree, &kstat_tree, kse) != NULL)
! 314: errx(1, "duplicate kstat entry");
! 315:
! 316: id = ksreq->ks_id;
! 317: }
! 318:
! 319: RBT_FOREACH(kse, kstat_tree, &kstat_tree) {
! 320: ksreq = &kse->kstat;
! 321: printf("%s:%u:%s:%u\n",
! 322: ksreq->ks_provider, ksreq->ks_instance,
! 323: ksreq->ks_name, ksreq->ks_unit);
! 324: if (kse->serrno != 0) {
! 325: printf("\t%s\n", strerror(kse->serrno));
! 326: continue;
! 327: }
! 328: switch (ksreq->ks_type) {
! 329: case KSTAT_T_RAW:
! 330: hexdump(ksreq->ks_data, ksreq->ks_datalen);
! 331: break;
! 332: case KSTAT_T_KV:
! 333: kstat_kv(ksreq->ks_data, ksreq->ks_datalen);
! 334: break;
! 335: default:
! 336: hexdump(ksreq->ks_data, ksreq->ks_datalen);
! 337: break;
! 338: }
! 339: }
! 340: }