1 /* $NetBSD: kern_history.c,v 1.12 2017/01/08 19:49:25 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997 Charles D. Cranor and Washington University. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * from: NetBSD: uvm_stat.c,v 1.36 2011/02/02 15:13:34 chuck Exp 28 * from: Id: uvm_stat.c,v 1.1.2.3 1997/12/19 15:01:00 mrg Exp 29 */ 30 31 /* 32 * subr_kernhist.c 33 */ 34 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: kern_history.c,v 1.12 2017/01/08 19:49:25 christos Exp $"); 37 38 #include "opt_ddb.h" 39 #include "opt_kernhist.h" 40 #include "opt_syscall_debug.h" 41 #include "opt_usb.h" 42 #include "opt_uvmhist.h" 43 #include "opt_biohist.h" 44 #include "opt_sysctl.h" 45 46 #include <sys/param.h> 47 #include <sys/systm.h> 48 #include <sys/cpu.h> 49 #include <sys/sysctl.h> 50 #include <sys/kernhist.h> 51 #include <sys/kmem.h> 52 53 #ifdef UVMHIST 54 #include <uvm/uvm.h> 55 #endif 56 57 #ifdef USB_DEBUG 58 #include <dev/usb/usbhist.h> 59 #endif 60 61 #ifdef BIOHIST 62 #include <sys/biohist.h> 63 #endif 64 65 #ifdef SYSCALL_DEBUG 66 KERNHIST_DECL(scdebughist); 67 #endif 68 69 struct addr_xlt { 70 const char *addr; 71 size_t len; 72 uint32_t offset; 73 }; 74 75 /* 76 * globals 77 */ 78 79 struct kern_history_head kern_histories; 80 81 int kernhist_print_enabled = 1; 82 83 int sysctl_hist_node; 84 85 static int sysctl_kernhist_helper(SYSCTLFN_PROTO); 86 87 #ifdef DDB 88 89 /* 90 * prototypes 91 */ 92 93 void kernhist_dump(struct kern_history *, 94 void (*)(const char *, ...) __printflike(1, 2)); 95 void kernhist_dumpmask(uint32_t); 96 static void kernhist_dump_histories(struct kern_history *[], 97 void (*)(const char *, ...) __printflike(1, 2)); 98 99 100 /* 101 * call this from ddb 102 * 103 * expects the system to be quiesced, no locking 104 */ 105 void 106 kernhist_dump(struct kern_history *l, void (*pr)(const char *, ...)) 107 { 108 int lcv; 109 110 lcv = l->f; 111 do { 112 if (l->e[lcv].fmt) 113 kernhist_entry_print(&l->e[lcv], pr); 114 lcv = (lcv + 1) % l->n; 115 } while (lcv != l->f); 116 } 117 118 /* 119 * print a merged list of kern_history structures 120 */ 121 static void 122 kernhist_dump_histories(struct kern_history *hists[], void (*pr)(const char *, ...)) 123 { 124 struct bintime bt; 125 int cur[MAXHISTS]; 126 int lcv, hi; 127 128 /* find the first of each list */ 129 for (lcv = 0; hists[lcv]; lcv++) 130 cur[lcv] = hists[lcv]->f; 131 132 /* 133 * here we loop "forever", finding the next earliest 134 * history entry and printing it. cur[X] is the current 135 * entry to test for the history in hists[X]. if it is 136 * -1, then this history is finished. 137 */ 138 for (;;) { 139 hi = -1; 140 bt.sec = 0; bt.frac = 0; 141 142 /* loop over each history */ 143 for (lcv = 0; hists[lcv]; lcv++) { 144 restart: 145 if (cur[lcv] == -1) 146 continue; 147 if (!hists[lcv]->e) 148 continue; 149 150 /* 151 * if the format is empty, go to the next entry 152 * and retry. 153 */ 154 if (hists[lcv]->e[cur[lcv]].fmt == NULL) { 155 cur[lcv] = (cur[lcv] + 1) % (hists[lcv]->n); 156 if (cur[lcv] == hists[lcv]->f) 157 cur[lcv] = -1; 158 goto restart; 159 } 160 161 /* 162 * if the time hasn't been set yet, or this entry is 163 * earlier than the current bt, set the time and history 164 * index. 165 */ 166 if (bt.sec == 0 || 167 bintimecmp(&hists[lcv]->e[cur[lcv]].bt, &bt, <)) { 168 bt = hists[lcv]->e[cur[lcv]].bt; 169 hi = lcv; 170 } 171 } 172 173 /* if we didn't find any entries, we must be done */ 174 if (hi == -1) 175 break; 176 177 /* print and move to the next entry */ 178 kernhist_entry_print(&hists[hi]->e[cur[hi]], pr); 179 cur[hi] = (cur[hi] + 1) % (hists[hi]->n); 180 if (cur[hi] == hists[hi]->f) 181 cur[hi] = -1; 182 } 183 } 184 185 /* 186 * call this from ddb. `bitmask' is from <sys/kernhist.h>. it 187 * merges the named histories. 188 * 189 * expects the system to be quiesced, no locking 190 */ 191 void 192 kernhist_dumpmask(uint32_t bitmask) /* XXX only support 32 hists */ 193 { 194 struct kern_history *hists[MAXHISTS + 1]; 195 int i = 0; 196 197 #ifdef UVMHIST 198 if ((bitmask & KERNHIST_UVMMAPHIST) || bitmask == 0) 199 hists[i++] = &maphist; 200 201 if ((bitmask & KERNHIST_UVMPDHIST) || bitmask == 0) 202 hists[i++] = &pdhist; 203 204 if ((bitmask & KERNHIST_UVMUBCHIST) || bitmask == 0) 205 hists[i++] = &ubchist; 206 207 if ((bitmask & KERNHIST_UVMLOANHIST) || bitmask == 0) 208 hists[i++] = &loanhist; 209 #endif 210 211 #ifdef USB_DEBUG 212 if ((bitmask & KERNHIST_USBHIST) || bitmask == 0) 213 hists[i++] = &usbhist; 214 #endif 215 216 #ifdef SYSCALL_DEBUG 217 if ((bitmask & KERNHIST_SCDEBUGHIST) || bitmask == 0) 218 hists[i++] = &scdebughist; 219 #endif 220 221 #ifdef BIOHIST 222 if ((bitmask & KERNHIST_BIOHIST) || bitmask == 0) 223 hists[i++] = &biohist; 224 #endif 225 226 hists[i] = NULL; 227 228 kernhist_dump_histories(hists, printf); 229 } 230 231 /* 232 * kernhist_print: ddb hook to print kern history 233 */ 234 void 235 kernhist_print(void *addr, void (*pr)(const char *, ...) __printflike(1,2)) 236 { 237 struct kern_history *h; 238 239 LIST_FOREACH(h, &kern_histories, list) { 240 if (h == addr) 241 break; 242 } 243 244 if (h == NULL) { 245 struct kern_history *hists[MAXHISTS + 1]; 246 int i = 0; 247 #ifdef UVMHIST 248 hists[i++] = &maphist; 249 hists[i++] = &pdhist; 250 hists[i++] = &ubchist; 251 hists[i++] = &loanhist; 252 #endif 253 #ifdef USB_DEBUG 254 hists[i++] = &usbhist; 255 #endif 256 257 #ifdef SYSCALL_DEBUG 258 hists[i++] = &scdebughist; 259 #endif 260 #ifdef BIOHIST 261 hists[i++] = &biohist; 262 #endif 263 hists[i] = NULL; 264 265 kernhist_dump_histories(hists, pr); 266 } else { 267 kernhist_dump(h, pr); 268 } 269 } 270 271 #endif 272 273 /* 274 * sysctl interface 275 */ 276 277 /* 278 * sysctl_hist_new() 279 * 280 * Scan the list of histories; for any history that does not already 281 * have a sysctl node (under kern.hist) we create a new one and record 282 * it's node number. 283 */ 284 static void 285 sysctl_hist_new(void) 286 { 287 int error; 288 struct kern_history *h; 289 const struct sysctlnode *rnode = NULL; 290 291 LIST_FOREACH(h, &kern_histories, list) { 292 if (h->s != 0) 293 continue; 294 error = sysctl_createv(NULL, 0, NULL, &rnode, 295 CTLFLAG_PERMANENT, 296 CTLTYPE_STRUCT, h->name, 297 SYSCTL_DESCR("history data"), 298 sysctl_kernhist_helper, 0, NULL, 0, 299 CTL_KERN, sysctl_hist_node, CTL_CREATE, CTL_EOL); 300 if (error == 0) 301 h->s = rnode->sysctl_num; 302 } 303 } 304 305 /* 306 * sysctl_kerhnist_init() 307 * 308 * Create the 2nd level "hw.hist" sysctl node 309 */ 310 void 311 sysctl_kernhist_init(void) 312 { 313 const struct sysctlnode *rnode = NULL; 314 315 sysctl_createv(NULL, 0, NULL, &rnode, 316 CTLFLAG_PERMANENT, 317 CTLTYPE_NODE, "hist", 318 SYSCTL_DESCR("kernel history tables"), 319 sysctl_kernhist_helper, 0, NULL, 0, 320 CTL_KERN, CTL_CREATE, CTL_EOL); 321 sysctl_hist_node = rnode->sysctl_num; 322 323 sysctl_hist_new(); 324 } 325 326 /* 327 * find_string() 328 * 329 * Search the address-to-offset translation table for matching an 330 * address and len, and return the index of the entry we found. If 331 * not found, returns index 0 which points to the "?" entry. (We 332 * start matching at index 1, ignoring any matches of the "?" entry 333 * itself.) 334 */ 335 static int 336 find_string(struct addr_xlt table[], size_t *count, const char *string, 337 size_t len) 338 { 339 int i; 340 341 for (i = 1; i < *count; i++) 342 if (string == table[i].addr && len == table[i].len) 343 return i; 344 345 return 0; 346 } 347 348 /* 349 * add_string() 350 * 351 * If the string and len are unique, add a new address-to-offset 352 * entry in the translation table and set the offset of the next 353 * entry. 354 */ 355 static void 356 add_string(struct addr_xlt table[], size_t *count, const char *string, 357 size_t len) 358 { 359 360 if (find_string(table, count, string, len) == 0) { 361 table[*count].addr = string; 362 table[*count].len = len; 363 table[*count + 1].offset = table[*count].offset + len + 1; 364 (*count)++; 365 } 366 } 367 368 /* 369 * sysctl_kernhist_helper 370 * 371 * This helper routine is called for all accesses to the kern.hist 372 * hierarchy. 373 */ 374 static int 375 sysctl_kernhist_helper(SYSCTLFN_ARGS) 376 { 377 struct kern_history *h; 378 struct kern_history_ent *in_evt; 379 struct sysctl_history_event *out_evt; 380 struct sysctl_history *buf; 381 struct addr_xlt *xlate_t, *xlt; 382 size_t bufsize, xlate_s; 383 size_t xlate_c; 384 const char *strp __diagused; 385 char *next; 386 int i, j; 387 int error; 388 389 sysctl_hist_new(); /* make sure we're up to date */ 390 391 if (namelen == 1 && name[0] == CTL_QUERY) 392 return sysctl_query(SYSCTLFN_CALL(rnode)); 393 394 /* 395 * Disallow userland updates, verify that we arrived at a 396 * valid history rnode 397 */ 398 if (newp) 399 return EPERM; 400 if (namelen != 1 || name[0] != CTL_EOL) 401 return EINVAL; 402 403 /* Find the correct kernhist for this sysctl node */ 404 LIST_FOREACH(h, &kern_histories, list) { 405 if (h->s == rnode->sysctl_num) 406 break; 407 } 408 if (h == NULL) 409 return ENOENT; 410 411 /* 412 * Worst case is two string pointers per history entry, plus 413 * two for the history name and "?" string; allocate an extra 414 * entry since we pre-set the "next" entry's offset member. 415 */ 416 xlate_s = sizeof(struct addr_xlt) * h->n * 2 + 3; 417 xlate_t = kmem_alloc(xlate_s, KM_SLEEP); 418 xlate_c = 0; 419 420 /* offset 0 reserved for NULL pointer, ie unused history entry */ 421 xlate_t[0].offset = 1; 422 423 /* 424 * If the history gets updated and an unexpected string is 425 * found later, we'll point it here. Otherwise, we'd have to 426 * repeat this process iteratively, and it could take multiple 427 * iterations before terminating. 428 */ 429 add_string(xlate_t, &xlate_c, "?", 0); 430 431 /* Copy the history name itself to the export structure */ 432 add_string(xlate_t, &xlate_c, h->name, h->namelen); 433 434 /* 435 * Loop through all used history entries to find the unique 436 * fn and fmt strings 437 */ 438 for (i = 0, in_evt = h->e; i < h->n; i++, in_evt++) { 439 if (in_evt->fn == NULL) 440 continue; 441 add_string(xlate_t, &xlate_c, in_evt->fn, in_evt->fnlen); 442 add_string(xlate_t, &xlate_c, in_evt->fmt, in_evt->fmtlen); 443 } 444 445 /* Total buffer size includes header, events, and string table */ 446 bufsize = sizeof(struct sysctl_history) + 447 h->n * sizeof(struct sysctl_history_event) + 448 xlate_t[xlate_c].offset; 449 buf = kmem_alloc(bufsize, KM_SLEEP); 450 451 /* 452 * Copy history header info to the export structure 453 */ 454 j = find_string(xlate_t, &xlate_c, h->name, h->namelen); 455 buf->sh_listentry.shle_nameoffset = xlate_t[j].offset; 456 buf->sh_listentry.shle_numentries = h->n; 457 buf->sh_listentry.shle_nextfree = h->f; 458 459 /* 460 * Loop through the history events again, copying the data to 461 * the export structure 462 */ 463 for (i = 0, in_evt = h->e, out_evt = buf->sh_events; i < h->n; 464 i++, in_evt++, out_evt++) { 465 if (in_evt->fn == NULL) { /* skip unused entries */ 466 out_evt->she_funcoffset = 0; 467 out_evt->she_fmtoffset = 0; 468 continue; 469 } 470 out_evt->she_bintime = in_evt->bt; 471 out_evt->she_callnumber = in_evt->call; 472 out_evt->she_cpunum = in_evt->cpunum; 473 out_evt->she_values[0] = in_evt->v[0]; 474 out_evt->she_values[1] = in_evt->v[1]; 475 out_evt->she_values[2] = in_evt->v[2]; 476 out_evt->she_values[3] = in_evt->v[3]; 477 j = find_string(xlate_t, &xlate_c, in_evt->fn, in_evt->fnlen); 478 out_evt->she_funcoffset = xlate_t[j].offset; 479 j = find_string(xlate_t, &xlate_c, in_evt->fmt, in_evt->fmtlen); 480 out_evt->she_fmtoffset = xlate_t[j].offset; 481 } 482 483 /* 484 * Finally, fill the text string area with all the unique 485 * strings we found earlier. 486 * 487 * Skip the initial byte, since we use an offset of 0 to mean 488 * a NULL pointer (which means an unused history event). 489 */ 490 strp = next = (char *)(&buf->sh_events[h->n]); 491 *next++ = '\0'; 492 493 /* 494 * Then copy each string into the export structure, making 495 * sure to terminate each string with a '\0' character 496 */ 497 for (i = 0, xlt = xlate_t; i < xlate_c; i++, xlt++) { 498 KASSERTMSG((next - strp) == xlt->offset, 499 "entry %d at wrong offset %"PRIu32, i, xlt->offset); 500 memcpy(next, xlt->addr, xlt->len); 501 next += xlt->len; 502 *next++ = '\0'; 503 } 504 505 /* Copy data to userland */ 506 error = copyout(buf, oldp, min(bufsize, *oldlenp)); 507 508 /* If copyout was successful but only partial, report ENOMEM */ 509 if (error == 0 && *oldlenp < bufsize) 510 error = ENOMEM; 511 512 *oldlenp = bufsize; /* inform userland of space requirements */ 513 514 /* Free up the stuff we allocated */ 515 kmem_free(buf, bufsize); 516 kmem_free(xlate_t, xlate_s); 517 518 return error; 519 } 520