xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_help.c (revision 1700af3a)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright (c) 2012, Joyent, Inc.  All rights reserved.
26  * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
27  */
28 
29 #include <mdb/mdb_modapi.h>
30 #include <mdb/mdb_macalias.h>
31 #include <mdb/mdb_fmt.h>
32 #include <mdb/mdb_err.h>
33 #include <mdb/mdb_help.h>
34 #include <mdb/mdb.h>
35 #include <regex.h>
36 
37 const char _mdb_help[] =
38 "\nEach debugger command in %s is structured as follows:\n\n"
39 "      [ address [, count]] verb [ arguments ... ]\n"
40 "             ^       ^      ^      ^\n"
41 " the start --+       |      |      +-- arguments are strings which can be\n"
42 " address can be an   |      |          quoted using \"\" or '' or\n"
43 " expression          |      |          expressions enclosed in $[ ]\n"
44 "                     |      |\n"
45 " the repeat count  --+      +--------- the verb is a name which begins\n"
46 " is also an expression                 with either $, :, or ::.  it can also\n"
47 "                                       be a format specifier (/ \\ ? or =)\n\n"
48 "For information on debugger commands (dcmds) and walkers, type:\n\n"
49 "      ::help cmdname ... for more detailed information on a command\n"
50 "      ::dcmds        ... for a list of dcmds and their descriptions\n"
51 "      ::walkers      ... for a list of walkers and their descriptions\n"
52 "      ::dmods -l     ... for a list of modules and their dcmds and walkers\n"
53 "      ::formats      ... for a list of format characters for / \\ ? and =\n\n"
54 "For information on command-line options, type:\n\n"
55 "      $ %s -?      ... in your shell for a complete list of options\n\n";
56 
57 /*ARGSUSED*/
58 static int
print_dcmd(mdb_var_t * v,void * ignored)59 print_dcmd(mdb_var_t *v, void *ignored)
60 {
61 	const mdb_idcmd_t *idcp = mdb_nv_get_cookie(v);
62 	if (idcp->idc_descr != NULL)
63 		mdb_printf("  dcmd %-20s - %s\n",
64 		    idcp->idc_name, idcp->idc_descr);
65 	return (0);
66 }
67 
68 /*ARGSUSED*/
69 static int
print_walk(mdb_var_t * v,void * ignored)70 print_walk(mdb_var_t *v, void *ignored)
71 {
72 	const mdb_iwalker_t *iwp = mdb_nv_get_cookie(v);
73 	if (iwp->iwlk_descr != NULL)
74 		mdb_printf("  walk %-20s - %s\n",
75 		    iwp->iwlk_name, iwp->iwlk_descr);
76 	return (0);
77 }
78 
79 /*ARGSUSED*/
80 static int
print_dmod_long(mdb_var_t * v,void * ignored)81 print_dmod_long(mdb_var_t *v, void *ignored)
82 {
83 	mdb_module_t *mod = mdb_nv_get_cookie(v);
84 
85 	mdb_printf("\n%<u>%-70s%</u>\n", mod->mod_name);
86 
87 	if (mod->mod_tgt_ctor != NULL) {
88 		mdb_printf("  ctor 0x%-18lx - target constructor\n",
89 		    (ulong_t)mod->mod_tgt_ctor);
90 	}
91 
92 	if (mod->mod_dis_ctor != NULL) {
93 		mdb_printf("  ctor 0x%-18lx - disassembler constructor\n",
94 		    (ulong_t)mod->mod_dis_ctor);
95 	}
96 
97 	mdb_nv_sort_iter(&mod->mod_dcmds, print_dcmd, NULL, UM_SLEEP | UM_GC);
98 	mdb_nv_sort_iter(&mod->mod_walkers, print_walk, NULL, UM_SLEEP | UM_GC);
99 
100 	return (0);
101 }
102 
103 /*ARGSUSED*/
104 static int
print_dmod_short(mdb_var_t * v,void * ignored)105 print_dmod_short(mdb_var_t *v, void *ignored)
106 {
107 	mdb_printf("%s\n", mdb_nv_get_name(v));
108 	return (0);
109 }
110 
111 /*ARGSUSED*/
112 int
cmd_dmods(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)113 cmd_dmods(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
114 {
115 	int (*func)(mdb_var_t *, void *);
116 	uint_t opt_l = FALSE;
117 	mdb_var_t *v;
118 	int i;
119 
120 	if (flags & DCMD_ADDRSPEC)
121 		return (DCMD_USAGE);
122 
123 	i = mdb_getopts(argc, argv, 'l', MDB_OPT_SETBITS, TRUE, &opt_l, NULL);
124 	func = opt_l ? print_dmod_long : print_dmod_short;
125 
126 	if (i != argc) {
127 		if (argc - i != 1 || argv[i].a_type != MDB_TYPE_STRING)
128 			return (DCMD_USAGE);
129 
130 		v = mdb_nv_lookup(&mdb.m_modules, argv[i].a_un.a_str);
131 
132 		if (v == NULL)
133 			mdb_warn("%s module not loaded\n", argv[i].a_un.a_str);
134 		else
135 			(void) func(v, NULL);
136 
137 	} else
138 		mdb_nv_sort_iter(&mdb.m_modules, func, NULL, UM_SLEEP | UM_GC);
139 
140 	return (DCMD_OK);
141 }
142 
143 #define	FILTER_NAMEONLY	0x1
144 
145 typedef struct filter_data {
146 	const char *pattern;
147 	int flags;
148 #ifndef _KMDB
149 	regex_t reg;
150 #endif
151 } filter_data_t;
152 
153 static void
filter_help(void)154 filter_help(void)
155 {
156 	mdb_printf("Options:\n"
157 	    "    -n       Match only the name, not the description.\n"
158 #ifdef _KMDB
159 	    "    pattern  Substring to match against name/description."
160 #else
161 	    "    pattern  RE to match against name/description."
162 #endif
163 	    "\n");
164 }
165 
166 void
cmd_dcmds_help(void)167 cmd_dcmds_help(void)
168 {
169 	mdb_printf(
170 	    "List all of the dcmds that are currently available. If a pattern\n"
171 	    "is provided then list only the commands that\n"
172 #ifdef _KMDB
173 	    "contain the provided substring."
174 #else
175 	    "match the provided regular expression."
176 #endif
177 	    "\n");
178 	filter_help();
179 }
180 
181 void
cmd_walkers_help(void)182 cmd_walkers_help(void)
183 {
184 	mdb_printf(
185 	    "List all of the walkers that are currently available. If a\n"
186 	    "pattern is provided then list only the walkers that\n"
187 #ifdef _KMDB
188 	    "contain the provided substring."
189 #else
190 	    "match the provided regular expression."
191 #endif
192 	    "\n");
193 	filter_help();
194 }
195 
196 static int
print_wdesc(mdb_var_t * v,void * data)197 print_wdesc(mdb_var_t *v, void *data)
198 {
199 	filter_data_t *f = data;
200 	mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
201 	const char *name = mdb_nv_get_name(v);
202 	boolean_t output = FALSE;
203 
204 	if (name == NULL || iwp->iwlk_descr == NULL)
205 		return (0);
206 
207 	if (f->pattern == NULL) {
208 		output = TRUE;
209 	} else {
210 #ifdef _KMDB
211 		/*
212 		 * kmdb doesn't have access to the reg* functions, so we fall
213 		 * back to strstr.
214 		 */
215 		if (strstr(name, f->pattern) != NULL ||
216 		    (!(f->flags & FILTER_NAMEONLY) &&
217 		    strstr(iwp->iwlk_descr, f->pattern) != NULL))
218 			output = TRUE;
219 #else
220 		regmatch_t pmatch;
221 
222 		if (regexec(&f->reg, name, 1, &pmatch, 0) == 0 ||
223 		    (!(f->flags & FILTER_NAMEONLY) &&
224 		    regexec(&f->reg, iwp->iwlk_descr, 1, &pmatch, 0) == 0))
225 			output = TRUE;
226 #endif
227 
228 	}
229 
230 	if (output)
231 		mdb_printf("%-24s - %s\n", name, iwp->iwlk_descr);
232 	return (0);
233 }
234 
235 /*ARGSUSED*/
236 int
cmd_walkers(uintptr_t addr __unused,uint_t flags,int argc,const mdb_arg_t * argv)237 cmd_walkers(uintptr_t addr __unused, uint_t flags, int argc,
238     const mdb_arg_t *argv)
239 {
240 	filter_data_t f;
241 	int i;
242 #ifndef _KMDB
243 	int err;
244 #endif
245 
246 	if (flags & DCMD_ADDRSPEC)
247 		return (DCMD_USAGE);
248 
249 	f.pattern = NULL;
250 	f.flags = 0;
251 
252 	i = mdb_getopts(argc, argv,
253 	    'n', MDB_OPT_SETBITS, FILTER_NAMEONLY, &f.flags,
254 	    NULL);
255 
256 	argc -= i;
257 	argv += i;
258 
259 	if (argc == 1) {
260 		if (argv->a_type != MDB_TYPE_STRING)
261 			return (DCMD_USAGE);
262 		f.pattern = argv->a_un.a_str;
263 
264 #ifndef _KMDB
265 		if ((err = regcomp(&f.reg, f.pattern, REG_EXTENDED)) != 0) {
266 			size_t nbytes;
267 			char *buf;
268 
269 			nbytes = regerror(err, &f.reg, NULL, 0);
270 			buf = mdb_alloc(nbytes + 1, UM_SLEEP | UM_GC);
271 			(void) regerror(err, &f.reg, buf, nbytes);
272 			mdb_warn("%s\n", buf);
273 
274 			return (DCMD_ERR);
275 		}
276 #endif
277 	} else if (argc != 0) {
278 		return (DCMD_USAGE);
279 	}
280 
281 	mdb_nv_sort_iter(&mdb.m_walkers, print_wdesc, &f, UM_SLEEP | UM_GC);
282 	return (DCMD_OK);
283 }
284 
285 static int
print_ddesc(mdb_var_t * v,void * data)286 print_ddesc(mdb_var_t *v, void *data)
287 {
288 	filter_data_t *f = data;
289 	mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
290 	const char *name = mdb_nv_get_name(v);
291 	boolean_t output = FALSE;
292 
293 	if (name == NULL || idcp->idc_descr == NULL)
294 		return (0);
295 
296 	if (f->pattern == NULL) {
297 		output = TRUE;
298 	} else {
299 #ifdef _KMDB
300 		/*
301 		 * kmdb doesn't have access to the reg* functions, so we fall
302 		 * back to strstr.
303 		 */
304 		if (strstr(name, f->pattern) != NULL ||
305 		    (!(f->flags & FILTER_NAMEONLY) &&
306 		    strstr(idcp->idc_descr, f->pattern) != NULL))
307 			output = TRUE;
308 #else
309 		regmatch_t pmatch;
310 
311 		if (regexec(&f->reg, name, 1, &pmatch, 0) == 0 ||
312 		    (!(f->flags & FILTER_NAMEONLY) &&
313 		    regexec(&f->reg, idcp->idc_descr, 1, &pmatch, 0) == 0))
314 			output = TRUE;
315 #endif
316 
317 	}
318 
319 	if (output)
320 		mdb_printf("%-24s - %s\n", name, idcp->idc_descr);
321 	return (0);
322 }
323 
324 /*ARGSUSED*/
325 int
cmd_dcmds(uintptr_t addr __unused,uint_t flags,int argc,const mdb_arg_t * argv)326 cmd_dcmds(uintptr_t addr __unused, uint_t flags, int argc,
327     const mdb_arg_t *argv)
328 {
329 	filter_data_t f;
330 	int i;
331 #ifndef _KMDB
332 	int err;
333 #endif
334 
335 	if (flags & DCMD_ADDRSPEC)
336 		return (DCMD_USAGE);
337 
338 	f.pattern = NULL;
339 	f.flags = 0;
340 
341 	i = mdb_getopts(argc, argv,
342 	    'n', MDB_OPT_SETBITS, FILTER_NAMEONLY, &f.flags,
343 	    NULL);
344 
345 	argc -= i;
346 	argv += i;
347 
348 	if (argc == 1) {
349 		if (argv->a_type != MDB_TYPE_STRING)
350 			return (DCMD_USAGE);
351 		f.pattern = argv->a_un.a_str;
352 
353 #ifndef _KMDB
354 		if ((err = regcomp(&f.reg, f.pattern, REG_EXTENDED)) != 0) {
355 			size_t nbytes;
356 			char *buf;
357 
358 			nbytes = regerror(err, &f.reg, NULL, 0);
359 			buf = mdb_alloc(nbytes + 1, UM_SLEEP | UM_GC);
360 			(void) regerror(err, &f.reg, buf, nbytes);
361 			mdb_warn("%s\n", buf);
362 
363 			return (DCMD_ERR);
364 		}
365 #endif
366 	} else if (argc != 0) {
367 		return (DCMD_USAGE);
368 	}
369 
370 	mdb_nv_sort_iter(&mdb.m_dcmds, print_ddesc, &f, UM_SLEEP | UM_GC);
371 	return (DCMD_OK);
372 }
373 
374 /*ARGSUSED*/
375 int
cmd_help(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)376 cmd_help(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
377 {
378 	const char *prefix, *usage;
379 	const mdb_idcmd_t *idcp;
380 
381 	if ((flags & DCMD_ADDRSPEC) || argc > 1)
382 		return (DCMD_USAGE);
383 
384 	if (argc == 0) {
385 		mdb_printf(_mdb_help, mdb.m_pname, mdb.m_pname);
386 		return (DCMD_OK);
387 	}
388 
389 	if (argv->a_type != MDB_TYPE_STRING) {
390 		warn("expected string argument\n");
391 		return (DCMD_USAGE);
392 	}
393 
394 	if (strncmp(argv->a_un.a_str, "::", 2) == 0)
395 		idcp = mdb_dcmd_lookup(argv->a_un.a_str + 2);
396 	else
397 		idcp = mdb_dcmd_lookup(argv->a_un.a_str);
398 
399 	if (idcp == NULL) {
400 		mdb_warn("unknown command: %s\n", argv->a_un.a_str);
401 		return (DCMD_ERR);
402 	}
403 
404 	prefix = strchr(":$=/\\?>", idcp->idc_name[0]) ? "" : "::";
405 	usage = idcp->idc_usage ? idcp->idc_usage : "";
406 
407 	mdb_printf("\n%<b>NAME%</b>\n  %s - %s\n\n",
408 	    idcp->idc_name, idcp->idc_descr);
409 
410 	mdb_printf("%<b>SYNOPSIS%</b>\n  ");
411 	if (usage[0] == '?') {
412 		mdb_printf("[ %<u>addr%</u> ] ");
413 		usage++;
414 	} else if (usage[0] == ':') {
415 		mdb_printf("%<u>addr%</u> ");
416 		usage++;
417 	}
418 
419 	mdb_printf("%s%s %s\n\n", prefix, idcp->idc_name, usage);
420 
421 	if (idcp->idc_help != NULL) {
422 		mdb_printf("%<b>DESCRIPTION%</b>\n");
423 		(void) mdb_inc_indent(2);
424 		idcp->idc_help();
425 		(void) mdb_dec_indent(2);
426 		mdb_printf("\n");
427 	}
428 
429 	/*
430 	 * For now, modules that are built-in mark their interfaces Evolving
431 	 * (documented in mdb(1)) and modules that are loaded mark their
432 	 * interfaces Unstable.  In the future we could extend the dmod linkage
433 	 * to include the module's intended stability and then show it here.
434 	 */
435 	mdb_printf("%<b>ATTRIBUTES%</b>\n\n");
436 	mdb_printf("  Target: %s\n", mdb_tgt_name(mdb.m_target));
437 	mdb_printf("  Module: %s\n", idcp->idc_modp->mod_name);
438 	mdb_printf("  Interface Stability: %s\n\n",
439 	    (idcp->idc_descr != NULL && idcp->idc_modp->mod_hdl == NULL) ?
440 	    "Evolving" : "Unstable");
441 
442 	return (DCMD_OK);
443 }
444 
445 int
cmd_help_tab(mdb_tab_cookie_t * mcp,uint_t flags,int argc,const mdb_arg_t * argv)446 cmd_help_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
447     const mdb_arg_t *argv)
448 {
449 	if (argc == 0 && !(flags & DCMD_TAB_SPACE))
450 		return (0);
451 
452 	if (argc > 1)
453 		return (0);
454 
455 	if (argc == 0)
456 		return (mdb_tab_complete_dcmd(mcp, NULL));
457 	else
458 		return (mdb_tab_complete_dcmd(mcp, argv[0].a_un.a_str));
459 }
460 
461 
462 static int
print_dcmd_def(mdb_var_t * v,void * private)463 print_dcmd_def(mdb_var_t *v, void *private)
464 {
465 	mdb_idcmd_t *idcp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
466 	int *ip = private;
467 
468 	mdb_printf("  [%d] %s`%s\n",
469 	    (*ip)++, idcp->idc_modp->mod_name, idcp->idc_name);
470 
471 	return (0);
472 }
473 
474 static int
print_walker_def(mdb_var_t * v,void * private)475 print_walker_def(mdb_var_t *v, void *private)
476 {
477 	mdb_iwalker_t *iwp = mdb_nv_get_cookie(mdb_nv_get_cookie(v));
478 	int *ip = private;
479 
480 	mdb_printf("  [%d] %s`%s\n",
481 	    (*ip)++, iwp->iwlk_modp->mod_name, iwp->iwlk_name);
482 
483 	return (0);
484 }
485 
486 /*ARGSUSED*/
487 int
cmd_which(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)488 cmd_which(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
489 {
490 	const char defn_hdr[] = "   >  definition list:\n";
491 	uint_t opt_v = FALSE;
492 	int i;
493 
494 	i = mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL);
495 
496 	for (; i < argc; i++) {
497 		const char *s = argv[i].a_un.a_str;
498 		int found = FALSE;
499 		mdb_iwalker_t *iwp;
500 		mdb_idcmd_t *idcp;
501 		const char *alias;
502 
503 		if (argv->a_type != MDB_TYPE_STRING)
504 			continue;
505 
506 		if (s[0] == '$' && s[1] == '<')
507 			s += 2;
508 
509 		if ((idcp = mdb_dcmd_lookup(s)) != NULL) {
510 			mdb_var_t *v = idcp->idc_var;
511 			int i = 1;
512 
513 			if (idcp->idc_modp != &mdb.m_rmod) {
514 				mdb_printf("%s is a dcmd from module %s\n",
515 				    s, idcp->idc_modp->mod_name);
516 			} else
517 				mdb_printf("%s is a built-in dcmd\n", s);
518 
519 			if (opt_v) {
520 				mdb_printf(defn_hdr);
521 				mdb_nv_defn_iter(v, print_dcmd_def, &i);
522 			}
523 			found = TRUE;
524 		}
525 
526 		if ((iwp = mdb_walker_lookup(s)) != NULL) {
527 			mdb_var_t *v = iwp->iwlk_var;
528 			int i = 1;
529 
530 			if (iwp->iwlk_modp != &mdb.m_rmod) {
531 				mdb_printf("%s is a walker from module %s\n",
532 				    s, iwp->iwlk_modp->mod_name);
533 			} else
534 				mdb_printf("%s is a built-in walker\n", s);
535 
536 			if (opt_v) {
537 				mdb_printf(defn_hdr);
538 				mdb_nv_defn_iter(v, print_walker_def, &i);
539 			}
540 			found = TRUE;
541 		}
542 
543 		if ((alias = mdb_macalias_lookup(s)) != NULL) {
544 			mdb_printf("%s is a macro alias for '%s'\n", s, alias);
545 			found = TRUE;
546 		}
547 
548 		if (!found)
549 			mdb_warn("%s not found\n", s);
550 	}
551 
552 	return (DCMD_OK);
553 }
554