1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <sys/mdb_modapi.h> 27 #include <mdb/mdb.h> 28 #include <mdb/mdb_io.h> 29 #include <mdb/mdb_module.h> 30 #include <mdb/mdb_string.h> 31 #include <mdb/mdb_whatis.h> 32 #include <mdb/mdb_whatis_impl.h> 33 #include <limits.h> 34 35 static int whatis_debug = 0; 36 37 /* for bsearch; r is an array of {base, size}, e points into w->w_addrs */ 38 static int 39 find_range(const void *r, const void *e) 40 { 41 const uintptr_t *range = r; 42 uintptr_t el = *(const uintptr_t *)e; 43 44 if (el < range[0]) 45 return (1); 46 47 if ((el - range[0]) >= range[1]) 48 return (-1); 49 50 return (0); 51 } 52 53 /* for qsort; simple uintptr comparator */ 54 static int 55 uintptr_cmp(const void *l, const void *r) 56 { 57 uintptr_t lhs = *(const uintptr_t *)l; 58 uintptr_t rhs = *(const uintptr_t *)r; 59 60 if (lhs < rhs) 61 return (-1); 62 if (lhs > rhs) 63 return (1); 64 return (0); 65 } 66 67 static const uintptr_t * 68 mdb_whatis_search(mdb_whatis_t *w, uintptr_t base, size_t size) 69 { 70 uintptr_t range[2]; 71 72 range[0] = base; 73 range[1] = size; 74 75 return (bsearch(range, w->w_addrs, w->w_naddrs, sizeof (*w->w_addrs), 76 find_range)); 77 } 78 79 /* 80 * Returns non-zero if and only if there is at least one address of interest 81 * in the range [base, base+size). 82 */ 83 int 84 mdb_whatis_overlaps(mdb_whatis_t *w, uintptr_t base, size_t size) 85 { 86 const uintptr_t *f; 87 uint_t offset, cur; 88 89 if (whatis_debug && w->w_magic != WHATIS_MAGIC) { 90 mdb_warn( 91 "mdb_whatis_overlaps(): bogus mdb_whatis_t pointer\n"); 92 return (0); 93 } 94 95 if (w->w_done || size == 0) 96 return (0); 97 98 if (base + size - 1 < base) { 99 mdb_warn("mdb_whatis_overlaps(): [%p, %p+%p) overflows\n", 100 base, base, size); 101 return (0); 102 } 103 104 f = mdb_whatis_search(w, base, size); 105 if (f == NULL) 106 return (0); 107 108 cur = offset = f - w->w_addrs; 109 110 /* 111 * We only return success if there's an address we'll actually 112 * match in the range. We can quickly check for the ALL flag 113 * or a non-found address at our match point. 114 */ 115 if ((w->w_flags & WHATIS_ALL) || !w->w_addrfound[cur]) 116 return (1); 117 118 /* Search backwards then forwards for a non-found address */ 119 while (cur > 0) { 120 cur--; 121 122 if (w->w_addrs[cur] < base) 123 break; 124 125 if (!w->w_addrfound[cur]) 126 return (1); 127 } 128 129 for (cur = offset + 1; cur < w->w_naddrs; cur++) { 130 if ((w->w_addrs[cur] - base) >= size) 131 break; 132 133 if (!w->w_addrfound[cur]) 134 return (1); 135 } 136 137 return (0); /* everything has already been seen */ 138 } 139 140 /* 141 * Iteratively search our list of addresses for matches in [base, base+size). 142 */ 143 int 144 mdb_whatis_match(mdb_whatis_t *w, uintptr_t base, size_t size, uintptr_t *out) 145 { 146 size_t offset; 147 148 if (whatis_debug) { 149 if (w->w_magic != WHATIS_MAGIC) { 150 mdb_warn( 151 "mdb_whatis_match(): bogus mdb_whatis_t pointer\n"); 152 goto done; 153 } 154 } 155 156 if (w->w_done || size == 0) 157 goto done; 158 159 if (base + size - 1 < base) { 160 mdb_warn("mdb_whatis_match(): [%p, %p+%x) overflows\n", 161 base, base, size); 162 return (0); 163 } 164 165 if ((offset = w->w_match_next) != 0 && 166 (base != w->w_match_base || size != w->w_match_size)) { 167 mdb_warn("mdb_whatis_match(): new range [%p, %p+%p) " 168 "while still searching [%p, %p+%p)\n", 169 base, base, size, 170 w->w_match_base, w->w_match_base, w->w_match_size); 171 offset = 0; 172 } 173 174 if (offset == 0) { 175 const uintptr_t *f = mdb_whatis_search(w, base, size); 176 177 if (f == NULL) 178 goto done; 179 180 offset = (f - w->w_addrs); 181 182 /* Walk backwards until we reach the first match */ 183 while (offset > 0 && w->w_addrs[offset - 1] >= base) 184 offset--; 185 186 w->w_match_base = base; 187 w->w_match_size = size; 188 } 189 190 for (; offset < w->w_naddrs && ((w->w_addrs[offset] - base) < size); 191 offset++) { 192 193 *out = w->w_addrs[offset]; 194 w->w_match_next = offset + 1; 195 196 if (w->w_addrfound[offset]) { 197 /* if we're not seeing everything, skip it */ 198 if (!(w->w_flags & WHATIS_ALL)) 199 continue; 200 201 return (1); 202 } 203 204 /* We haven't seen this address yet. */ 205 w->w_found++; 206 w->w_addrfound[offset] = 1; 207 208 /* If we've found them all, we're done */ 209 if (w->w_found == w->w_naddrs && !(w->w_flags & WHATIS_ALL)) 210 w->w_done = 1; 211 212 return (1); 213 } 214 215 done: 216 w->w_match_next = 0; 217 w->w_match_base = 0; 218 w->w_match_size = 0; 219 return (0); 220 } 221 222 /* 223 * Report a pointer (addr) in an object beginning at (base) in standard 224 * whatis-style. (format, ...) are mdb_printf() arguments, to be printed 225 * after the address information. The caller is responsible for printing 226 * a newline (either in format or after the call returns) 227 */ 228 /*ARGSUSED*/ 229 void 230 mdb_whatis_report_object(mdb_whatis_t *w, 231 uintptr_t addr, uintptr_t base, const char *format, ...) 232 { 233 va_list alist; 234 235 if (whatis_debug) { 236 if (mdb_whatis_search(w, addr, 1) == NULL) 237 mdb_warn("mdb_whatis_report_object(): addr " 238 "%p is not a pointer of interest.\n", addr); 239 } 240 241 if (addr < base) 242 mdb_warn("whatis: addr (%p) is less than base (%p)\n", 243 addr, base); 244 245 if (addr == base) 246 mdb_printf("%p is ", addr); 247 else 248 mdb_printf("%p is %p+%p, ", addr, base, addr - base); 249 250 if (format == NULL) 251 return; 252 253 va_start(alist, format); 254 mdb_iob_vprintf(mdb.m_out, format, alist); 255 va_end(alist); 256 } 257 258 /* 259 * Report an address (addr), with symbolic information if available, in 260 * standard whatis-style. (format, ...) are mdb_printf() arguments, to be 261 * printed after the address information. The caller is responsible for 262 * printing a newline (either in format or after the call returns) 263 */ 264 /*ARGSUSED*/ 265 void 266 mdb_whatis_report_address(mdb_whatis_t *w, uintptr_t addr, 267 const char *format, ...) 268 { 269 GElf_Sym sym; 270 va_list alist; 271 272 if (whatis_debug) { 273 if (mdb_whatis_search(w, addr, 1) == NULL) 274 mdb_warn("mdb_whatis_report_adddress(): addr " 275 "%p is not a pointer of interest.\n", addr); 276 } 277 278 mdb_printf("%p is ", addr); 279 280 if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY, NULL, 0, &sym) != -1 && 281 (addr - (uintptr_t)sym.st_value) < sym.st_size) { 282 mdb_printf("%a, ", addr); 283 } 284 285 va_start(alist, format); 286 mdb_iob_vprintf(mdb.m_out, format, alist); 287 va_end(alist); 288 } 289 290 uint_t 291 mdb_whatis_flags(mdb_whatis_t *w) 292 { 293 /* Mask out the internal-only flags */ 294 return (w->w_flags & WHATIS_PUBLIC); 295 } 296 297 uint_t 298 mdb_whatis_done(mdb_whatis_t *w) 299 { 300 return (w->w_done); 301 } 302 303 /* 304 * Whatis callback list management 305 */ 306 typedef struct whatis_callback { 307 uint64_t wcb_index; 308 mdb_module_t *wcb_module; 309 const char *wcb_modname; 310 char *wcb_name; 311 mdb_whatis_cb_f *wcb_func; 312 void *wcb_arg; 313 uint_t wcb_prio; 314 uint_t wcb_flags; 315 } whatis_callback_t; 316 317 static whatis_callback_t builtin_whatis[] = { 318 { 0, NULL, "mdb", "mappings", whatis_run_mappings, NULL, 319 WHATIS_PRIO_MIN, WHATIS_REG_NO_ID } 320 }; 321 #define NBUILTINS (sizeof (builtin_whatis) / sizeof (*builtin_whatis)) 322 323 static whatis_callback_t *whatis_cb_start[NBUILTINS]; 324 static whatis_callback_t **whatis_cb = NULL; /* callback array */ 325 static size_t whatis_cb_count; /* count of callbacks */ 326 static size_t whatis_cb_size; /* size of whatis_cb array */ 327 static uint64_t whatis_cb_index; /* global count */ 328 329 #define WHATIS_CB_SIZE_MIN 8 /* initial allocation size */ 330 331 static int 332 whatis_cbcmp(const void *lhs, const void *rhs) 333 { 334 whatis_callback_t *l = *(whatis_callback_t * const *)lhs; 335 whatis_callback_t *r = *(whatis_callback_t * const *)rhs; 336 int ret; 337 338 /* First, handle NULLs; we want them at the end */ 339 if (l == NULL && r == NULL) 340 return (0); 341 if (l == NULL) 342 return (1); 343 if (r == NULL) 344 return (-1); 345 346 /* Next, compare priorities */ 347 if (l->wcb_prio < r->wcb_prio) 348 return (-1); 349 if (l->wcb_prio > r->wcb_prio) 350 return (1); 351 352 /* then module name */ 353 if ((ret = strcmp(l->wcb_modname, r->wcb_modname)) != 0) 354 return (ret); 355 356 /* and finally insertion order */ 357 if (l->wcb_index < r->wcb_index) 358 return (-1); 359 if (l->wcb_index > r->wcb_index) 360 return (1); 361 362 mdb_warn("whatis_cbcmp(): can't happen: duplicate indices\n"); 363 return (0); 364 } 365 366 static void 367 whatis_init(void) 368 { 369 int idx; 370 371 for (idx = 0; idx < NBUILTINS; idx++) { 372 whatis_cb_start[idx] = &builtin_whatis[idx]; 373 whatis_cb_start[idx]->wcb_index = idx; 374 } 375 whatis_cb_index = idx; 376 377 whatis_cb = whatis_cb_start; 378 whatis_cb_count = whatis_cb_size = NBUILTINS; 379 380 qsort(whatis_cb, whatis_cb_count, sizeof (*whatis_cb), whatis_cbcmp); 381 } 382 383 void 384 mdb_whatis_register(const char *name, mdb_whatis_cb_f *func, void *arg, 385 uint_t prio, uint_t flags) 386 { 387 whatis_callback_t *wcp; 388 389 if (mdb.m_lmod == NULL) { 390 mdb_warn("mdb_whatis_register(): can only be called during " 391 "module load\n"); 392 return; 393 } 394 395 if (strbadid(name)) { 396 mdb_warn("mdb_whatis_register(): whatis name '%s' contains " 397 "illegal characters\n"); 398 return; 399 } 400 401 if ((flags & ~(WHATIS_REG_NO_ID|WHATIS_REG_ID_ONLY)) != 0) { 402 mdb_warn("mdb_whatis_register(): flags (%x) contain unknown " 403 "flags\n", flags); 404 return; 405 } 406 if ((flags & WHATIS_REG_NO_ID) && (flags & WHATIS_REG_ID_ONLY)) { 407 mdb_warn("mdb_whatis_register(): flags (%x) contains both " 408 "NO_ID and ID_ONLY.\n", flags); 409 return; 410 } 411 412 if (prio > WHATIS_PRIO_MIN) 413 prio = WHATIS_PRIO_MIN; 414 415 if (whatis_cb == NULL) 416 whatis_init(); 417 418 wcp = mdb_zalloc(sizeof (*wcp), UM_SLEEP); 419 420 wcp->wcb_index = whatis_cb_index++; 421 wcp->wcb_prio = prio; 422 wcp->wcb_module = mdb.m_lmod; 423 wcp->wcb_modname = mdb.m_lmod->mod_name; 424 wcp->wcb_name = strdup(name); 425 wcp->wcb_func = func; 426 wcp->wcb_arg = arg; 427 wcp->wcb_flags = flags; 428 429 /* 430 * See if we need to grow the array; note that at initialization 431 * time, whatis_cb_count is greater than whatis_cb_size; this clues 432 * us in to the fact that the array doesn't need to be freed. 433 */ 434 if (whatis_cb_count == whatis_cb_size) { 435 size_t nsize = MAX(2 * whatis_cb_size, WHATIS_CB_SIZE_MIN); 436 437 size_t obytes = sizeof (*whatis_cb) * whatis_cb_size; 438 size_t nbytes = sizeof (*whatis_cb) * nsize; 439 440 whatis_callback_t **narray = mdb_zalloc(nbytes, UM_SLEEP); 441 442 bcopy(whatis_cb, narray, obytes); 443 444 if (whatis_cb != whatis_cb_start) 445 mdb_free(whatis_cb, obytes); 446 whatis_cb = narray; 447 whatis_cb_size = nsize; 448 } 449 450 /* add it into the table and re-sort */ 451 whatis_cb[whatis_cb_count++] = wcp; 452 qsort(whatis_cb, whatis_cb_count, sizeof (*whatis_cb), whatis_cbcmp); 453 } 454 455 void 456 mdb_whatis_unregister_module(mdb_module_t *mod) 457 { 458 int found = 0; 459 int idx; 460 461 if (mod == NULL) 462 return; 463 464 for (idx = 0; idx < whatis_cb_count; idx++) { 465 whatis_callback_t *cur = whatis_cb[idx]; 466 467 if (cur->wcb_module == mod) { 468 found++; 469 whatis_cb[idx] = NULL; 470 471 strfree(cur->wcb_name); 472 mdb_free(cur, sizeof (*cur)); 473 } 474 } 475 /* If any were removed, compact the array */ 476 if (found != 0) { 477 qsort(whatis_cb, whatis_cb_count, sizeof (*whatis_cb), 478 whatis_cbcmp); 479 whatis_cb_count -= found; 480 } 481 } 482 483 int 484 cmd_whatis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 485 { 486 mdb_whatis_t w; 487 size_t idx; 488 int ret; 489 int keep = 0; 490 int list = 0; 491 492 if (flags & DCMD_PIPE_OUT) { 493 mdb_warn("whatis: cannot be output into a pipe\n"); 494 return (DCMD_ERR); 495 } 496 497 if (mdb.m_lmod != NULL) { 498 mdb_warn("whatis: cannot be called during module load\n"); 499 return (DCMD_ERR); 500 } 501 502 if (whatis_cb == NULL) 503 whatis_init(); 504 505 bzero(&w, sizeof (w)); 506 w.w_magic = WHATIS_MAGIC; 507 508 whatis_debug = 0; 509 510 if (mdb_getopts(argc, argv, 511 'D', MDB_OPT_SETBITS, TRUE, &whatis_debug, /* hidden */ 512 'b', MDB_OPT_SETBITS, WHATIS_BUFCTL, &w.w_flags, /* hidden */ 513 'l', MDB_OPT_SETBITS, TRUE, &list, /* hidden */ 514 'a', MDB_OPT_SETBITS, WHATIS_ALL, &w.w_flags, 515 'i', MDB_OPT_SETBITS, WHATIS_IDSPACE, &w.w_flags, 516 'k', MDB_OPT_SETBITS, TRUE, &keep, 517 'q', MDB_OPT_SETBITS, WHATIS_QUIET, &w.w_flags, 518 'v', MDB_OPT_SETBITS, WHATIS_VERBOSE, &w.w_flags, 519 NULL) != argc) 520 return (DCMD_USAGE); 521 522 if (list) { 523 mdb_printf("%<u>%-16s %-12s %4s %?s %?s %8s%</u>", 524 "NAME", "MODULE", "PRIO", "FUNC", "ARG", "FLAGS"); 525 526 for (idx = 0; idx < whatis_cb_count; idx++) { 527 whatis_callback_t *cur = whatis_cb[idx]; 528 529 const char *curfl = 530 (cur->wcb_flags & WHATIS_REG_NO_ID) ? "NO_ID" : 531 (cur->wcb_flags & WHATIS_REG_ID_ONLY) ? "ID_ONLY" : 532 "none"; 533 534 mdb_printf("%-16s %-12s %4d %-?p %-?p %8s\n", 535 cur->wcb_name, cur->wcb_modname, cur->wcb_prio, 536 cur->wcb_func, cur->wcb_arg, curfl); 537 } 538 return (DCMD_OK); 539 } 540 541 if (!(flags & DCMD_ADDRSPEC)) 542 return (DCMD_USAGE); 543 544 w.w_addrs = &addr; 545 w.w_naddrs = 1; 546 547 /* If our input is a pipe, try to slurp it all up. */ 548 if (!keep && (flags & DCMD_PIPE)) { 549 mdb_pipe_t p; 550 mdb_get_pipe(&p); 551 552 if (p.pipe_len != 0) { 553 w.w_addrs = p.pipe_data; 554 w.w_naddrs = p.pipe_len; 555 556 /* sort the address list */ 557 qsort(w.w_addrs, w.w_naddrs, sizeof (*w.w_addrs), 558 uintptr_cmp); 559 } 560 } 561 w.w_addrfound = mdb_zalloc(w.w_naddrs * sizeof (*w.w_addrfound), 562 UM_SLEEP | UM_GC); 563 564 if (whatis_debug) { 565 mdb_printf("Searching for:\n"); 566 for (idx = 0; idx < w.w_naddrs; idx++) 567 mdb_printf(" %p", w.w_addrs[idx]); 568 } 569 570 ret = 0; 571 572 /* call in to the registered handlers */ 573 for (idx = 0; idx < whatis_cb_count; idx++) { 574 whatis_callback_t *cur = whatis_cb[idx]; 575 576 /* Honor the ident flags */ 577 if (w.w_flags & WHATIS_IDSPACE) { 578 if (cur->wcb_flags & WHATIS_REG_NO_ID) 579 continue; 580 } else { 581 if (cur->wcb_flags & WHATIS_REG_ID_ONLY) 582 continue; 583 } 584 585 if (w.w_flags & WHATIS_VERBOSE) 586 mdb_printf("Searching %s`%s...\n", 587 cur->wcb_modname, cur->wcb_name); 588 589 if (cur->wcb_func(&w, cur->wcb_arg) != 0) 590 ret = 1; 591 592 /* reset the match state for the next callback */ 593 w.w_match_next = 0; 594 w.w_match_base = 0; 595 w.w_match_size = 0; 596 597 if (w.w_done) 598 break; 599 } 600 601 /* Report any unexplained pointers */ 602 for (idx = 0; idx < w.w_naddrs; idx++) { 603 uintptr_t addr = w.w_addrs[idx]; 604 605 if (w.w_addrfound[idx]) 606 continue; 607 608 mdb_whatis_report_object(&w, addr, addr, "unknown\n"); 609 } 610 611 return ((ret != 0) ? DCMD_ERR : DCMD_OK); 612 } 613 614 void 615 whatis_help(void) 616 { 617 int idx; 618 619 mdb_printf("%s\n", 620 "Given a virtual address (with -i, an identifier), report where it came\n" 621 "from.\n" 622 "\n" 623 "When fed from a pipeline, ::whatis will not maintain the order the input\n" 624 "comes in; addresses will be reported as it finds them. (-k prevents this;\n" 625 "the output will be in the same order as the input)\n"); 626 (void) mdb_dec_indent(2); 627 mdb_printf("%<b>OPTIONS%</b>\n"); 628 (void) mdb_inc_indent(2); 629 mdb_printf("%s", 630 " -a Report all information about each address/identifier. The default\n" 631 " behavior is to report only the first (most specific) source for each\n" 632 " address/identifier.\n" 633 " -i addr is an identifier, not a virtual address.\n" 634 " -k Do not re-order the input. (may be slower)\n" 635 " -q Quiet; don't print multi-line reports. (stack traces, etc.)\n" 636 " -v Verbose output; display information about the progress of the search\n"); 637 638 if (mdb.m_lmod != NULL) 639 return; 640 641 (void) mdb_dec_indent(2); 642 mdb_printf("\n%<b>SOURCES%</b>\n\n"); 643 (void) mdb_inc_indent(2); 644 mdb_printf("The following information sources will be used:\n\n"); 645 646 (void) mdb_inc_indent(2); 647 for (idx = 0; idx < whatis_cb_count; idx++) { 648 whatis_callback_t *cur = whatis_cb[idx]; 649 650 mdb_printf("%s`%s\n", cur->wcb_modname, cur->wcb_name); 651 } 652 (void) mdb_dec_indent(2); 653 } 654