1 /* $NetBSD: kern_history.c,v 1.11 2017/01/05 03:40:33 pgoyette 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.11 2017/01/05 03:40:33 pgoyette 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 #ifdef DDB 86 87 /* 88 * prototypes 89 */ 90 91 void kernhist_dump(struct kern_history *, 92 void (*)(const char *, ...) __printflike(1, 2)); 93 void kernhist_dumpmask(uint32_t); 94 static void kernhist_dump_histories(struct kern_history *[], 95 void (*)(const char *, ...) __printflike(1, 2)); 96 97 static int sysctl_kernhist_helper(SYSCTLFN_PROTO); 98 99 /* 100 * call this from ddb 101 * 102 * expects the system to be quiesced, no locking 103 */ 104 void 105 kernhist_dump(struct kern_history *l, void (*pr)(const char *, ...)) 106 { 107 int lcv; 108 109 lcv = l->f; 110 do { 111 if (l->e[lcv].fmt) 112 kernhist_entry_print(&l->e[lcv], pr); 113 lcv = (lcv + 1) % l->n; 114 } while (lcv != l->f); 115 } 116 117 /* 118 * print a merged list of kern_history structures 119 */ 120 static void 121 kernhist_dump_histories(struct kern_history *hists[], void (*pr)(const char *, ...)) 122 { 123 struct bintime bt; 124 int cur[MAXHISTS]; 125 int lcv, hi; 126 127 /* find the first of each list */ 128 for (lcv = 0; hists[lcv]; lcv++) 129 cur[lcv] = hists[lcv]->f; 130 131 /* 132 * here we loop "forever", finding the next earliest 133 * history entry and printing it. cur[X] is the current 134 * entry to test for the history in hists[X]. if it is 135 * -1, then this history is finished. 136 */ 137 for (;;) { 138 hi = -1; 139 bt.sec = 0; bt.frac = 0; 140 141 /* loop over each history */ 142 for (lcv = 0; hists[lcv]; lcv++) { 143 restart: 144 if (cur[lcv] == -1) 145 continue; 146 if (!hists[lcv]->e) 147 continue; 148 149 /* 150 * if the format is empty, go to the next entry 151 * and retry. 152 */ 153 if (hists[lcv]->e[cur[lcv]].fmt == NULL) { 154 cur[lcv] = (cur[lcv] + 1) % (hists[lcv]->n); 155 if (cur[lcv] == hists[lcv]->f) 156 cur[lcv] = -1; 157 goto restart; 158 } 159 160 /* 161 * if the time hasn't been set yet, or this entry is 162 * earlier than the current bt, set the time and history 163 * index. 164 */ 165 if (bt.sec == 0 || 166 bintimecmp(&hists[lcv]->e[cur[lcv]].bt, &bt, <)) { 167 bt = hists[lcv]->e[cur[lcv]].bt; 168 hi = lcv; 169 } 170 } 171 172 /* if we didn't find any entries, we must be done */ 173 if (hi == -1) 174 break; 175 176 /* print and move to the next entry */ 177 kernhist_entry_print(&hists[hi]->e[cur[hi]], pr); 178 cur[hi] = (cur[hi] + 1) % (hists[hi]->n); 179 if (cur[hi] == hists[hi]->f) 180 cur[hi] = -1; 181 } 182 } 183 184 /* 185 * call this from ddb. `bitmask' is from <sys/kernhist.h>. it 186 * merges the named histories. 187 * 188 * expects the system to be quiesced, no locking 189 */ 190 void 191 kernhist_dumpmask(uint32_t bitmask) /* XXX only support 32 hists */ 192 { 193 struct kern_history *hists[MAXHISTS + 1]; 194 int i = 0; 195 196 #ifdef UVMHIST 197 if ((bitmask & KERNHIST_UVMMAPHIST) || bitmask == 0) 198 hists[i++] = &maphist; 199 200 if ((bitmask & KERNHIST_UVMPDHIST) || bitmask == 0) 201 hists[i++] = &pdhist; 202 203 if ((bitmask & KERNHIST_UVMUBCHIST) || bitmask == 0) 204 hists[i++] = &ubchist; 205 206 if ((bitmask & KERNHIST_UVMLOANHIST) || bitmask == 0) 207 hists[i++] = &loanhist; 208 #endif 209 210 #ifdef USB_DEBUG 211 if ((bitmask & KERNHIST_USBHIST) || bitmask == 0) 212 hists[i++] = &usbhist; 213 #endif 214 215 #ifdef SYSCALL_DEBUG 216 if ((bitmask & KERNHIST_SCDEBUGHIST) || bitmask == 0) 217 hists[i++] = &scdebughist; 218 #endif 219 220 #ifdef BIOHIST 221 if ((bitmask & KERNHIST_BIOHIST) || bitmask == 0) 222 hists[i++] = &biohist; 223 #endif 224 225 hists[i] = NULL; 226 227 kernhist_dump_histories(hists, printf); 228 } 229 230 /* 231 * kernhist_print: ddb hook to print kern history 232 */ 233 void 234 kernhist_print(void *addr, void (*pr)(const char *, ...) __printflike(1,2)) 235 { 236 struct kern_history *h; 237 238 LIST_FOREACH(h, &kern_histories, list) { 239 if (h == addr) 240 break; 241 } 242 243 if (h == NULL) { 244 struct kern_history *hists[MAXHISTS + 1]; 245 int i = 0; 246 #ifdef UVMHIST 247 hists[i++] = &maphist; 248 hists[i++] = &pdhist; 249 hists[i++] = &ubchist; 250 hists[i++] = &loanhist; 251 #endif 252 #ifdef USB_DEBUG 253 hists[i++] = &usbhist; 254 #endif 255 256 #ifdef SYSCALL_DEBUG 257 hists[i++] = &scdebughist; 258 #endif 259 #ifdef BIOHIST 260 hists[i++] = &biohist; 261 #endif 262 hists[i] = NULL; 263 264 kernhist_dump_histories(hists, pr); 265 } else { 266 kernhist_dump(h, pr); 267 } 268 } 269 270 #endif 271 272 /* 273 * sysctl interface 274 */ 275 276 /* 277 * sysctl_hist_new() 278 * 279 * Scan the list of histories; for any history that does not already 280 * have a sysctl node (under kern.hist) we create a new one and record 281 * it's node number. 282 */ 283 static void 284 sysctl_hist_new(void) 285 { 286 int error; 287 struct kern_history *h; 288 const struct sysctlnode *rnode = NULL; 289 290 LIST_FOREACH(h, &kern_histories, list) { 291 if (h->s != 0) 292 continue; 293 error = sysctl_createv(NULL, 0, NULL, &rnode, 294 CTLFLAG_PERMANENT, 295 CTLTYPE_STRUCT, h->name, 296 SYSCTL_DESCR("history data"), 297 sysctl_kernhist_helper, 0, NULL, 0, 298 CTL_KERN, sysctl_hist_node, CTL_CREATE, CTL_EOL); 299 if (error == 0) 300 h->s = rnode->sysctl_num; 301 } 302 } 303 304 /* 305 * sysctl_kerhnist_init() 306 * 307 * Create the 2nd level "hw.hist" sysctl node 308 */ 309 void 310 sysctl_kernhist_init(void) 311 { 312 const struct sysctlnode *rnode = NULL; 313 314 sysctl_createv(NULL, 0, NULL, &rnode, 315 CTLFLAG_PERMANENT, 316 CTLTYPE_NODE, "hist", 317 SYSCTL_DESCR("kernel history tables"), 318 sysctl_kernhist_helper, 0, NULL, 0, 319 CTL_KERN, CTL_CREATE, CTL_EOL); 320 sysctl_hist_node = rnode->sysctl_num; 321 322 sysctl_hist_new(); 323 } 324 325 /* 326 * find_string() 327 * 328 * Search the address-to-offset translation table for matching an 329 * address and len, and return the index of the entry we found. If 330 * not found, returns index 0 which points to the "?" entry. (We 331 * start matching at index 1, ignoring any matches of the "?" entry 332 * itself.) 333 */ 334 static int 335 find_string(struct addr_xlt table[], size_t *count, const char *string, 336 size_t len) 337 { 338 int i; 339 340 for (i = 1; i < *count; i++) 341 if (string == table[i].addr && len == table[i].len) 342 return i; 343 344 return 0; 345 } 346 347 /* 348 * add_string() 349 * 350 * If the string and len are unique, add a new address-to-offset 351 * entry in the translation table and set the offset of the next 352 * entry. 353 */ 354 static void 355 add_string(struct addr_xlt table[], size_t *count, const char *string, 356 size_t len) 357 { 358 359 if (find_string(table, count, string, len) == 0) { 360 table[*count].addr = string; 361 table[*count].len = len; 362 table[*count + 1].offset = table[*count].offset + len + 1; 363 (*count)++; 364 } 365 } 366 367 /* 368 * sysctl_kernhist_helper 369 * 370 * This helper routine is called for all accesses to the kern.hist 371 * hierarchy. 372 */ 373 static int 374 sysctl_kernhist_helper(SYSCTLFN_ARGS) 375 { 376 struct kern_history *h; 377 struct kern_history_ent *in_evt; 378 struct sysctl_history_event *out_evt; 379 struct sysctl_history *buf; 380 struct addr_xlt *xlate_t, *xlt; 381 size_t bufsize, xlate_s; 382 size_t xlate_c; 383 const char *strp; 384 char *next; 385 int i, j; 386 int error; 387 388 sysctl_hist_new(); /* make sure we're up to date */ 389 390 if (namelen == 1 && name[0] == CTL_QUERY) 391 return sysctl_query(SYSCTLFN_CALL(rnode)); 392 393 /* 394 * Disallow userland updates, verify that we arrived at a 395 * valid history rnode 396 */ 397 if (newp) 398 return EPERM; 399 if (namelen != 1 || name[0] != CTL_EOL) 400 return EINVAL; 401 402 /* Find the correct kernhist for this sysctl node */ 403 LIST_FOREACH(h, &kern_histories, list) { 404 if (h->s == rnode->sysctl_num) 405 break; 406 } 407 if (h == NULL) 408 return ENOENT; 409 410 /* 411 * Worst case is two string pointers per history entry, plus 412 * two for the history name and "?" string; allocate an extra 413 * entry since we pre-set the "next" entry's offset member. 414 */ 415 xlate_s = sizeof(struct addr_xlt) * h->n * 2 + 3; 416 xlate_t = kmem_alloc(xlate_s, KM_SLEEP); 417 xlate_c = 0; 418 419 /* offset 0 reserved for NULL pointer, ie unused history entry */ 420 xlate_t[0].offset = 1; 421 422 /* 423 * If the history gets updated and an unexpected string is 424 * found later, we'll point it here. Otherwise, we'd have to 425 * repeat this process iteratively, and it could take multiple 426 * iterations before terminating. 427 */ 428 add_string(xlate_t, &xlate_c, "?", 0); 429 430 /* Copy the history name itself to the export structure */ 431 add_string(xlate_t, &xlate_c, h->name, h->namelen); 432 433 /* 434 * Loop through all used history entries to find the unique 435 * fn and fmt strings 436 */ 437 for (i = 0, in_evt = h->e; i < h->n; i++, in_evt++) { 438 if (in_evt->fn == NULL) 439 continue; 440 add_string(xlate_t, &xlate_c, in_evt->fn, in_evt->fnlen); 441 add_string(xlate_t, &xlate_c, in_evt->fmt, in_evt->fmtlen); 442 } 443 444 /* Total buffer size includes header, events, and string table */ 445 bufsize = sizeof(struct sysctl_history) + 446 h->n * sizeof(struct sysctl_history_event) + 447 xlate_t[xlate_c].offset; 448 buf = kmem_alloc(bufsize, KM_SLEEP); 449 450 /* 451 * Copy history header info to the export structure 452 */ 453 j = find_string(xlate_t, &xlate_c, h->name, h->namelen); 454 buf->sh_listentry.shle_nameoffset = xlate_t[j].offset; 455 buf->sh_listentry.shle_numentries = h->n; 456 buf->sh_listentry.shle_nextfree = h->f; 457 458 /* 459 * Loop through the history events again, copying the data to 460 * the export structure 461 */ 462 for (i = 0, in_evt = h->e, out_evt = buf->sh_events; i < h->n; 463 i++, in_evt++, out_evt++) { 464 if (in_evt->fn == NULL) { /* skip unused entries */ 465 out_evt->she_funcoffset = 0; 466 out_evt->she_fmtoffset = 0; 467 continue; 468 } 469 out_evt->she_bintime = in_evt->bt; 470 out_evt->she_callnumber = in_evt->call; 471 out_evt->she_cpunum = in_evt->cpunum; 472 out_evt->she_values[0] = in_evt->v[0]; 473 out_evt->she_values[1] = in_evt->v[1]; 474 out_evt->she_values[2] = in_evt->v[2]; 475 out_evt->she_values[3] = in_evt->v[3]; 476 j = find_string(xlate_t, &xlate_c, in_evt->fn, in_evt->fnlen); 477 out_evt->she_funcoffset = xlate_t[j].offset; 478 j = find_string(xlate_t, &xlate_c, in_evt->fmt, in_evt->fmtlen); 479 out_evt->she_fmtoffset = xlate_t[j].offset; 480 } 481 482 /* 483 * Finally, fill the text string area with all the unique 484 * strings we found earlier. 485 * 486 * Skip the initial byte, since we use an offset of 0 to mean 487 * a NULL pointer (which means an unused history event). 488 */ 489 strp = next = (char *)(&buf->sh_events[h->n]); 490 *next++ = '\0'; 491 492 /* 493 * Then copy each string into the export structure, making 494 * sure to terminate each string with a '\0' character 495 */ 496 for (i = 0, xlt = xlate_t; i < xlate_c; i++, xlt++) { 497 KASSERTMSG((next - strp) == xlt->offset, 498 "entry %d at wrong offset %"PRIu32, i, xlt->offset); 499 memcpy(next, xlt->addr, xlt->len); 500 next += xlt->len; 501 *next++ = '\0'; 502 } 503 504 /* Copy data to userland */ 505 error = copyout(buf, oldp, min(bufsize, *oldlenp)); 506 507 /* If copyout was successful but only partial, report ENOMEM */ 508 if (error == 0 && *oldlenp < bufsize) 509 error = ENOMEM; 510 511 *oldlenp = bufsize; /* inform userland of space requirements */ 512 513 /* Free up the stuff we allocated */ 514 kmem_free(buf, bufsize); 515 kmem_free(xlate_t, xlate_s); 516 517 return error; 518 } 519