xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_whatis.c (revision 4a1c24318fe7c9bdae38ce58a2e4624597d297e2)
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