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
find_range(const void * r,const void * e)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
uintptr_cmp(const void * l,const void * r)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 *
mdb_whatis_search(mdb_whatis_t * w,uintptr_t base,size_t size)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
mdb_whatis_overlaps(mdb_whatis_t * w,uintptr_t base,size_t size)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
mdb_whatis_match(mdb_whatis_t * w,uintptr_t base,size_t size,uintptr_t * out)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
mdb_whatis_report_object(mdb_whatis_t * w,uintptr_t addr,uintptr_t base,const char * format,...)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
mdb_whatis_report_address(mdb_whatis_t * w,uintptr_t addr,const char * format,...)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
mdb_whatis_flags(mdb_whatis_t * w)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
mdb_whatis_done(mdb_whatis_t * w)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
whatis_cbcmp(const void * lhs,const void * rhs)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
whatis_init(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
mdb_whatis_register(const char * name,mdb_whatis_cb_f * func,void * arg,uint_t prio,uint_t flags)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
mdb_whatis_unregister_module(mdb_module_t * mod)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
cmd_whatis(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
whatis_help(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