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