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