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