summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordlg <dlg@openbsd.org>2020-08-10 01:13:28 +0000
committerdlg <dlg@openbsd.org>2020-08-10 01:13:28 +0000
commit59ead937f0e1e45dc43e4cc30e515aaa634b7527 (patch)
treefb7cef3d6cc05299e0e8fe2ecb9e2829a247894a
parenthardclock(9): fix race with setitimer(2) for ITIMER_VIRTUAL, ITIMER_PROF (diff)
downloadwireguard-openbsd-59ead937f0e1e45dc43e4cc30e515aaa634b7527.tar.xz
wireguard-openbsd-59ead937f0e1e45dc43e4cc30e515aaa634b7527.zip
add the ability to filter which kstats are displayed.
kstats are identified by a "provider:instance:name:unit" tuple, where provider and name are strings, and instance and unit are integers. you can pass tuples as arguments to kstat to select them for display, eg, to see the rxq and txq stats for the first rings on an em0 interface, you can run `kstat em0:0:rxq:0 em0:0:txq:0`. this can be pretty tedious if you want to select a group of stats quickly though, so there's some wildcard and pattern matching support. firstly, empty fields in the tuple are wildcarded, eg, if you want to see all the stats that an mcx0 interface provides then run `kstat mcx0:::`. secondly, strings in a tuple (ie, the provider and name fields) are compared using fnmatch(3) so you can use shell like patterns to match fields. to expand the last example to all mcx interfaces, you can run `kstat mcx*:::`. lastly, you can pass a bare name to match on all kstats with that name. eg, to see the txq stats for all interfaces, just run `kstat txq`.
-rw-r--r--usr.bin/kstat/kstat.c178
1 files changed, 162 insertions, 16 deletions
diff --git a/usr.bin/kstat/kstat.c b/usr.bin/kstat/kstat.c
index 1964d6c91ba..f9f47bf2575 100644
--- a/usr.bin/kstat/kstat.c
+++ b/usr.bin/kstat/kstat.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kstat.c,v 1.1 2020/07/06 07:09:50 dlg Exp $ */
+/* $OpenBSD: kstat.c,v 1.2 2020/08/10 01:13:28 dlg Exp $ */
/*
* Copyright (c) 2020 David Gwynne <dlg@openbsd.org>
@@ -20,6 +20,7 @@
#include <stddef.h>
#include <string.h>
#include <inttypes.h>
+#include <fnmatch.h>
#include <fcntl.h>
#include <errno.h>
#include <err.h>
@@ -28,32 +29,73 @@
#include <sys/tree.h>
#include <sys/ioctl.h>
#include <sys/time.h>
+#include <sys/queue.h>
#include <sys/kstat.h>
#ifndef roundup
-#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
#endif
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+#ifndef ISSET
+#define ISSET(_i, _m) ((_i) & (_m))
+#endif
+
+#ifndef SET
+#define SET(_i, _m) ((_i) |= (_m))
+#endif
+
+#define str_is_empty(_str) (*(_str) == '\0')
+
#define DEV_KSTAT "/dev/kstat"
-static void kstat_list(int, unsigned int);
+struct kstat_filter {
+ TAILQ_ENTRY(kstat_filter) kf_entry;
+ const char *kf_provider;
+ const char *kf_name;
+ unsigned int kf_flags;
+#define KSTAT_FILTER_F_INST (1 << 0)
+#define KSTAT_FILTER_F_UNIT (1 << 1)
+ unsigned int kf_instance;
+ unsigned int kf_unit;
+};
+
+TAILQ_HEAD(kstat_filters, kstat_filter);
+
+static struct kstat_filter *
+ kstat_filter_parse(char *);
+static int kstat_filter_entry(struct kstat_filters *,
+ const struct kstat_req *);
+
+static void kstat_list(int, unsigned int, struct kstat_filters *);
-#if 0
__dead static void
usage(void)
{
extern char *__progname;
- fprintf(stderr, "usage: %s\n", __progname);
+
+ fprintf(stderr, "usage: %s [name|provider:0:name:unit ...]\n",
+ __progname);
+
exit(1);
}
-#endif
int
main(int argc, char *argv[])
{
+ struct kstat_filters kfs = TAILQ_HEAD_INITIALIZER(kfs);
unsigned int version;
int fd;
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ struct kstat_filter *kf = kstat_filter_parse(argv[i]);
+ TAILQ_INSERT_TAIL(&kfs, kf, kf_entry);
+ }
fd = open(DEV_KSTAT, O_RDONLY);
if (fd == -1)
@@ -62,7 +104,105 @@ main(int argc, char *argv[])
if (ioctl(fd, KSTATIOC_VERSION, &version) == -1)
err(1, "kstat version");
- kstat_list(fd, version);
+ kstat_list(fd, version, &kfs);
+
+ return (0);
+}
+
+static struct kstat_filter *
+kstat_filter_parse(char *arg)
+{
+ struct kstat_filter *kf;
+ const char *errstr;
+ char *argv[4];
+ size_t argc;
+
+ for (argc = 0; argc < nitems(argv); argc++) {
+ char *s = strsep(&arg, ":");
+ if (s == NULL)
+ break;
+
+ argv[argc] = s;
+ }
+ if (arg != NULL)
+ usage();
+
+ kf = malloc(sizeof(*kf));
+ if (kf == NULL)
+ err(1, NULL);
+
+ memset(kf, 0, sizeof(*kf));
+
+ switch (argc) {
+ case 1:
+ if (str_is_empty(argv[0]))
+ errx(1, "empty name");
+
+ kf->kf_name = argv[0];
+ break;
+ case 4:
+ if (!str_is_empty(argv[0]))
+ kf->kf_provider = argv[0];
+ if (!str_is_empty(argv[1])) {
+ kf->kf_instance =
+ strtonum(argv[1], 0, 0xffffffffU, &errstr);
+ if (errstr != NULL) {
+ errx(1, "%s:%s:%s:%s: instance %s: %s",
+ argv[0], argv[1], argv[2], argv[3],
+ argv[1], errstr);
+ }
+ SET(kf->kf_flags, KSTAT_FILTER_F_INST);
+ }
+ if (!str_is_empty(argv[2]))
+ kf->kf_name = argv[2];
+ if (!str_is_empty(argv[3])) {
+ kf->kf_unit =
+ strtonum(argv[3], 0, 0xffffffffU, &errstr);
+ if (errstr != NULL) {
+ errx(1, "%s:%s:%s:%s: instance %s: %s",
+ argv[0], argv[1], argv[2], argv[3],
+ argv[1], errstr);
+ }
+ SET(kf->kf_flags, KSTAT_FILTER_F_INST);
+ }
+ break;
+ default:
+ usage();
+ }
+
+ return (kf);
+}
+
+static int
+kstat_filter_entry(struct kstat_filters *kfs, const struct kstat_req *ksreq)
+{
+ struct kstat_filter *kf;
+
+ if (TAILQ_EMPTY(kfs))
+ return (1);
+
+ TAILQ_FOREACH(kf, kfs, kf_entry) {
+ if (kf->kf_provider != NULL) {
+ if (fnmatch(kf->kf_provider, ksreq->ks_provider,
+ FNM_NOESCAPE | FNM_LEADING_DIR) == FNM_NOMATCH)
+ continue;
+ }
+ if (ISSET(kf->kf_flags, KSTAT_FILTER_F_INST)) {
+ if (kf->kf_instance != ksreq->ks_instance)
+ continue;
+ }
+ if (kf->kf_name != NULL) {
+ if (fnmatch(kf->kf_name, ksreq->ks_name,
+ FNM_NOESCAPE | FNM_LEADING_DIR) == FNM_NOMATCH)
+ continue;
+ }
+ if (ISSET(kf->kf_flags, KSTAT_FILTER_F_UNIT)) {
+ if (kf->kf_unit != ksreq->ks_unit)
+ continue;
+ }
+
+ return (1);
+ }
return (0);
}
@@ -265,7 +405,7 @@ kstat_kv(const void *d, ssize_t len)
}
static void
-kstat_list(int fd, unsigned int version)
+kstat_list(int fd, unsigned int version, struct kstat_filters *kfs)
{
struct kstat_entry *kse;
struct kstat_req *ksreq;
@@ -296,9 +436,21 @@ kstat_list(int fd, unsigned int version)
}
kse->serrno = errno;
- goto next;
+ } else
+ id = ksreq->ks_id;
+
+ if (!kstat_filter_entry(kfs, ksreq)) {
+ free(ksreq->ks_data);
+ free(kse);
+ continue;
}
+ if (RBT_INSERT(kstat_tree, &kstat_tree, kse) != NULL)
+ errx(1, "duplicate kstat entry");
+
+ if (kse->serrno != 0)
+ continue;
+
while (ksreq->ks_datalen > len) {
len = ksreq->ks_datalen;
ksreq->ks_data = realloc(ksreq->ks_data, len);
@@ -306,14 +458,8 @@ kstat_list(int fd, unsigned int version)
err(1, "data resize (%zu)", len);
if (ioctl(fd, KSTATIOC_FIND_ID, ksreq) == -1)
- err(1, "find id %llu", id);
+ err(1, "find id %llu", ksreq->ks_id);
}
-
-next:
- if (RBT_INSERT(kstat_tree, &kstat_tree, kse) != NULL)
- errx(1, "duplicate kstat entry");
-
- id = ksreq->ks_id;
}
RBT_FOREACH(kse, kstat_tree, &kstat_tree) {