[BACK]Return to kstat.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / kstat

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: }