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
37const 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*/
58static int
59print_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*/
69static int
70print_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*/
80static int
81print_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*/
104static int
105print_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*/
112int
113cmd_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
145typedef struct filter_data {
146	const char *pattern;
147	int flags;
148#ifndef _KMDB
149	regex_t reg;
150#endif
151} filter_data_t;
152
153static void
154filter_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
166void
167cmd_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
181void
182cmd_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
196static int
197print_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*/
236int
237cmd_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
285static int
286print_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*/
325int
326cmd_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*/
375int
376cmd_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
445int
446cmd_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
462static int
463print_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
474static int
475print_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*/
487int
488cmd_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