xref: /openbsd/usr.bin/kstat/kstat.c (revision 4cfece93)
1 /* $OpenBSD: kstat.c,v 1.1 2020/07/06 07:09:50 dlg Exp $ */
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 }
341