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