xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_set.c (revision f76ff24c761689d2859f3bc5faa7ec9e84f92234)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Copyright 2017 Joyent, Inc.
28  */
29 
30 /*
31  * Support for ::set dcmd.  The +/-o option processing code is provided in a
32  * stand-alone function so it can be used by the command-line option processing
33  * code in mdb_main.c.  This facility provides an easy way for us to add more
34  * configurable options without having to add a new dcmd each time.
35  */
36 
37 #include <mdb/mdb_target.h>
38 #include <mdb/mdb_modapi.h>
39 #include <mdb/mdb_string.h>
40 #include <mdb/mdb_debug.h>
41 #include <mdb/mdb.h>
42 
43 /*ARGSUSED*/
44 static int
45 opt_set_mflags(int enable, uint_t bits, const char *arg)
46 {
47 	mdb.m_flags = (mdb.m_flags & ~bits) | (bits & -enable);
48 	return (1);
49 }
50 
51 /*ARGSUSED*/
52 static int
53 opt_set_tflags(int enable, uint_t bits, const char *arg)
54 {
55 	mdb.m_tgtflags = (mdb.m_tgtflags & ~bits) | (bits & -enable);
56 	return (1);
57 }
58 
59 static int
60 opt_pager(int enable, uint_t bits, const char *arg)
61 {
62 	if (enable)
63 		mdb_iob_setflags(mdb.m_out, MDB_IOB_PGENABLE);
64 	else
65 		mdb_iob_clrflags(mdb.m_out, MDB_IOB_PGENABLE);
66 
67 	return (opt_set_mflags(enable, bits, arg));
68 }
69 
70 static int
71 opt_adb(int enable, uint_t bits, const char *arg)
72 {
73 	if (enable)
74 		(void) mdb_set_prompt("");
75 	else if (mdb.m_promptlen == 0)
76 		(void) mdb_set_prompt("> ");
77 
78 	(void) opt_pager(1 - enable, MDB_FL_PAGER, arg);
79 	return (opt_set_mflags(enable, bits, arg));
80 }
81 
82 /*ARGSUSED*/
83 static int
84 opt_armemlim(int enable, uint_t bits, const char *arg)
85 {
86 	if (strisnum(arg)) {
87 		mdb.m_armemlim = strtoi(arg);
88 		return (1);
89 	}
90 	if (strcmp(arg, "none") == 0) {
91 		mdb.m_armemlim = MDB_ARR_NOLIMIT;
92 		return (1);
93 	}
94 	return (0);
95 }
96 
97 /*ARGSUSED*/
98 static int
99 opt_arstrlim(int enable, uint_t bits, const char *arg)
100 {
101 	if (strisnum(arg)) {
102 		mdb.m_arstrlim = strtoi(arg);
103 		return (1);
104 	}
105 	if (strcmp(arg, "none") == 0) {
106 		mdb.m_arstrlim = MDB_ARR_NOLIMIT;
107 		return (1);
108 	}
109 	return (0);
110 }
111 
112 /*ARGSUSED*/
113 static int
114 opt_exec_mode(int enable, uint_t bits, const char *arg)
115 {
116 	if (strcmp(arg, "ask") == 0) {
117 		mdb.m_execmode = MDB_EM_ASK;
118 		return (1);
119 	} else if (strcmp(arg, "stop") == 0) {
120 		mdb.m_execmode = MDB_EM_STOP;
121 		return (1);
122 	} else if (strcmp(arg, "follow") == 0) {
123 		mdb.m_execmode = MDB_EM_FOLLOW;
124 		return (1);
125 	}
126 	return (0);
127 }
128 
129 /*ARGSUSED*/
130 static int
131 opt_fork_mode(int enable, uint_t bits, const char *arg)
132 {
133 	if (strcmp(arg, "ask") == 0) {
134 		mdb.m_forkmode = MDB_FM_ASK;
135 		return (1);
136 	} else if (strcmp(arg, "parent") == 0) {
137 		mdb.m_forkmode = MDB_FM_PARENT;
138 		return (1);
139 	} else if (strcmp(arg, "child") == 0) {
140 		mdb.m_forkmode = MDB_FM_CHILD;
141 		return (1);
142 	}
143 	return (0);
144 }
145 
146 /*ARGSUSED*/
147 static int
148 opt_set_term(int enable, uint_t bits, const char *arg)
149 {
150 	mdb.m_termtype = strdup(arg);
151 	mdb.m_flags &= ~MDB_FL_TERMGUESS;
152 
153 	return (1);
154 }
155 
156 int
157 mdb_set_options(const char *s, int enable)
158 {
159 	static const struct opdesc {
160 		const char *opt_name;
161 		int (*opt_func)(int, uint_t, const char *);
162 		uint_t opt_bits;
163 	} opdtab[] = {
164 		{ "adb", opt_adb, MDB_FL_REPLAST | MDB_FL_NOMODS | MDB_FL_ADB },
165 		{ "array_mem_limit", opt_armemlim, 0 },
166 		{ "array_str_limit", opt_arstrlim, 0 },
167 		{ "follow_exec_mode", opt_exec_mode, 0 },
168 		{ "follow_fork_mode", opt_fork_mode, 0 },
169 		{ "pager", opt_pager, MDB_FL_PAGER },
170 		{ "term", opt_set_term, 0 },
171 
172 		{ "autowrap", opt_set_mflags, MDB_FL_AUTOWRAP },
173 		{ "ignoreeof", opt_set_mflags, MDB_FL_IGNEOF },
174 		{ "repeatlast", opt_set_mflags, MDB_FL_REPLAST },
175 		{ "latest", opt_set_mflags, MDB_FL_LATEST },
176 		{ "noctf", opt_set_mflags, MDB_FL_NOCTF },
177 		{ "nomods", opt_set_mflags, MDB_FL_NOMODS },
178 		{ "showlmid", opt_set_mflags, MDB_FL_SHOWLMID },
179 		{ "lmraw", opt_set_mflags, MDB_FL_LMRAW },
180 		{ "stop_on_bpt_nosym", opt_set_mflags, MDB_FL_BPTNOSYMSTOP },
181 		{ "write_readback", opt_set_mflags, MDB_FL_READBACK },
182 
183 		{ "allow_io_access", opt_set_tflags, MDB_TGT_F_ALLOWIO },
184 		{ "nostop", opt_set_tflags, MDB_TGT_F_NOSTOP },
185 		{ NULL, NULL, 0 }
186 	};
187 
188 	const struct opdesc *opp;
189 	char *buf = strdup(s);
190 	char *opt, *arg;
191 	int status = 1;
192 
193 	for (opt = strtok(buf, ","); opt != NULL; opt = strtok(NULL, ",")) {
194 		if ((arg = strchr(opt, '=')) != NULL)
195 			*arg++ = '\0';
196 
197 		for (opp = opdtab; opp->opt_name != NULL; opp++) {
198 			if (strcmp(opt, opp->opt_name) == 0) {
199 				if (opp->opt_bits != 0 && arg != NULL) {
200 					mdb_warn("option does not accept an "
201 					    "argument -- %s\n", opt);
202 					status = 0;
203 				} else if (opp->opt_bits == 0 && arg == NULL) {
204 					mdb_warn("option requires an argument "
205 					    "-- %s\n", opt);
206 					status = 0;
207 				} else if (opp->opt_func(enable != 0,
208 				    opp->opt_bits, arg) == 0) {
209 					mdb_warn("invalid argument for option "
210 					    "%s -- %s\n", opt, arg);
211 					status = 0;
212 				}
213 				break;
214 			}
215 		}
216 
217 		if (opp->opt_name == NULL) {
218 			mdb_warn("invalid debugger option -- %s\n", opt);
219 			status = 0;
220 		}
221 	}
222 
223 	mdb_free(buf, strlen(s) + 1);
224 	return (status);
225 }
226 
227 static void
228 print_path(const char **path, int indent)
229 {
230 	if (path != NULL && *path != NULL) {
231 		for (mdb_printf("%s\n", *path++); *path != NULL; path++)
232 			mdb_printf("%*s%s\n", indent, " ", *path);
233 	}
234 	mdb_printf("\n");
235 }
236 
237 #define	LABEL_INDENT	26
238 
239 static void
240 print_properties(void)
241 {
242 	int tflags = mdb_tgt_getflags(mdb.m_target);
243 	uint_t oflags = mdb_iob_getflags(mdb.m_out) & MDB_IOB_AUTOWRAP;
244 
245 	mdb_iob_clrflags(mdb.m_out, MDB_IOB_AUTOWRAP);
246 	mdb_printf("\n  macro path: ");
247 	print_path(mdb.m_ipath, 14);
248 	mdb_printf(" module path: ");
249 	print_path(mdb.m_lpath, 14);
250 	mdb_iob_setflags(mdb.m_out, oflags);
251 
252 	mdb_printf("%*s %lr (%s)\n", LABEL_INDENT, "symbol matching distance:",
253 	    mdb.m_symdist, mdb.m_symdist ? "absolute mode" : "smart mode");
254 
255 	mdb_printf("%*s ", LABEL_INDENT, "array member print limit:");
256 	if (mdb.m_armemlim != MDB_ARR_NOLIMIT)
257 		mdb_printf("%u\n", mdb.m_armemlim);
258 	else
259 		mdb_printf("none\n");
260 
261 	mdb_printf(" array string print limit: ");
262 	if (mdb.m_arstrlim != MDB_ARR_NOLIMIT)
263 		mdb_printf("%u\n", mdb.m_arstrlim);
264 	else
265 		mdb_printf("none\n");
266 
267 	mdb_printf("%*s \"%s\"\n", LABEL_INDENT, "command prompt:",
268 	    mdb.m_prompt);
269 
270 	mdb_printf("%*s ", LABEL_INDENT, "debugger options:");
271 	(void) mdb_inc_indent(LABEL_INDENT + 1);
272 
273 	/*
274 	 * The ::set output implicitly relies on "autowrap" being enabled, so
275 	 * we enable it for the duration of the command.
276 	 */
277 	oflags = mdb.m_flags;
278 	mdb.m_flags |= MDB_FL_AUTOWRAP;
279 
280 	mdb_printf("follow_exec_mode=");
281 	switch (mdb.m_execmode) {
282 	case MDB_EM_ASK:
283 		mdb_printf("ask");
284 		break;
285 	case MDB_EM_STOP:
286 		mdb_printf("stop");
287 		break;
288 	case MDB_EM_FOLLOW:
289 		mdb_printf("follow");
290 		break;
291 	}
292 
293 #define	COMMAFLAG(name) { mdb_printf(", "); mdb_printf(name); }
294 
295 	COMMAFLAG("follow_fork_mode");
296 	switch (mdb.m_forkmode) {
297 	case MDB_FM_ASK:
298 		mdb_printf("ask");
299 		break;
300 	case MDB_FM_PARENT:
301 		mdb_printf("parent");
302 		break;
303 	case MDB_FM_CHILD:
304 		mdb_printf("child");
305 		break;
306 	}
307 
308 	if (mdb.m_flags & MDB_FL_ADB)
309 		COMMAFLAG("adb");
310 	if (oflags & MDB_FL_AUTOWRAP)
311 		COMMAFLAG("autowrap");
312 	if (mdb.m_flags & MDB_FL_IGNEOF)
313 		COMMAFLAG("ignoreeof");
314 	if (mdb.m_flags & MDB_FL_LMRAW)
315 		COMMAFLAG("lmraw");
316 	if (mdb.m_flags & MDB_FL_PAGER)
317 		COMMAFLAG("pager");
318 	if (mdb.m_flags & MDB_FL_REPLAST)
319 		COMMAFLAG("repeatlast");
320 	if (mdb.m_flags & MDB_FL_SHOWLMID)
321 		COMMAFLAG("showlmid");
322 	if (mdb.m_flags & MDB_FL_BPTNOSYMSTOP)
323 		COMMAFLAG("stop_on_bpt_nosym");
324 	if (mdb.m_flags & MDB_FL_READBACK)
325 		COMMAFLAG("write_readback");
326 	mdb_printf("\n");
327 	(void) mdb_dec_indent(LABEL_INDENT + 1);
328 
329 	mdb_printf("%*s ", LABEL_INDENT, "target options:");
330 	(void) mdb_inc_indent(LABEL_INDENT + 1);
331 
332 	if (tflags & MDB_TGT_F_RDWR)
333 		mdb_printf("read-write");
334 	else
335 		mdb_printf("read-only");
336 	if (tflags & MDB_TGT_F_ALLOWIO)
337 		COMMAFLAG("allow-io-access");
338 	if (tflags & MDB_TGT_F_FORCE)
339 		COMMAFLAG("force-attach");
340 	if (tflags & MDB_TGT_F_PRELOAD)
341 		COMMAFLAG("preload-syms");
342 	if (tflags & MDB_TGT_F_NOLOAD)
343 		COMMAFLAG("no-load-objs");
344 	if (tflags & MDB_TGT_F_NOSTOP)
345 		COMMAFLAG("no-stop");
346 	mdb_printf("\n");
347 	(void) mdb_dec_indent(LABEL_INDENT + 1);
348 
349 	mdb.m_flags = oflags;
350 }
351 
352 /*ARGSUSED*/
353 int
354 cmd_set(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
355 {
356 	const char *opt_I = NULL, *opt_L = NULL, *opt_P = NULL, *opt_o = NULL;
357 	const char *opt_plus_o = NULL, *opt_D = NULL;
358 	uint_t opt_w = FALSE, opt_plus_w = FALSE, opt_W = FALSE;
359 	uint_t opt_plus_W = FALSE, opt_F = FALSE;
360 	uintptr_t opt_s = (uintptr_t)(long)-1;
361 
362 	int tflags = 0;
363 	int i;
364 
365 	if (flags & DCMD_ADDRSPEC)
366 		return (DCMD_USAGE);
367 
368 	/*
369 	 * If no options are specified, print out the current set of target
370 	 * and debugger properties that can be modified with ::set.
371 	 */
372 	if (argc == 0) {
373 		print_properties();
374 		return (DCMD_OK);
375 	}
376 
377 	while ((i = mdb_getopts(argc, argv,
378 	    'F', MDB_OPT_SETBITS, TRUE, &opt_F,
379 	    'I', MDB_OPT_STR, &opt_I,
380 	    'L', MDB_OPT_STR, &opt_L,
381 	    'P', MDB_OPT_STR, &opt_P,
382 	    'o', MDB_OPT_STR, &opt_o,
383 	    's', MDB_OPT_UINTPTR, &opt_s,
384 	    'w', MDB_OPT_SETBITS, TRUE, &opt_w,
385 	    'W', MDB_OPT_SETBITS, TRUE, &opt_W,
386 	    'D', MDB_OPT_STR, &opt_D, NULL)) != argc) {
387 		uint_t n = 1;
388 
389 		argv += i; /* skip past args we processed */
390 		argc -= i; /* adjust argc */
391 
392 		if (argv[0].a_type != MDB_TYPE_STRING)
393 			return (DCMD_USAGE);
394 
395 		if (strcmp(argv->a_un.a_str, "+W") == 0)
396 			opt_plus_W = TRUE;
397 		else if (strcmp(argv->a_un.a_str, "+w") == 0)
398 			opt_plus_w = TRUE;
399 		else if (strcmp(argv->a_un.a_str, "+o") == 0 &&
400 		    argc >= 2 && argv[1].a_type == MDB_TYPE_STRING) {
401 			opt_plus_o = argv[1].a_un.a_str;
402 			n = 2;
403 		} else
404 			return (DCMD_USAGE);
405 
406 		/* remove the flag and possible argument */
407 		argv += n;
408 		argc -= n;
409 	}
410 
411 	if ((opt_w && opt_plus_w) || (opt_W && opt_plus_W))
412 		return (DCMD_USAGE);
413 
414 	/*
415 	 * Handle -w, -/+W and -F first: as these options modify the target,
416 	 * they are the only ::set changes that can potentially fail.  We'll
417 	 * use these flags to modify a copy of the target's t_flags, which we'll
418 	 * then pass to the target's setflags op.  This allows the target to
419 	 * detect newly-set and newly-cleared flags by comparing the passed
420 	 * value to the current t_flags.
421 	 */
422 	tflags = mdb_tgt_getflags(mdb.m_target);
423 
424 	if (opt_w)
425 		tflags |= MDB_TGT_F_RDWR;
426 	if (opt_plus_w)
427 		tflags &= ~MDB_TGT_F_RDWR;
428 	if (opt_W)
429 		tflags |= MDB_TGT_F_ALLOWIO;
430 	if (opt_plus_W)
431 		tflags &= ~MDB_TGT_F_ALLOWIO;
432 	if (opt_F)
433 		tflags |= MDB_TGT_F_FORCE;
434 
435 	if (tflags != mdb_tgt_getflags(mdb.m_target) &&
436 	    mdb_tgt_setflags(mdb.m_target, tflags) == -1)
437 		return (DCMD_ERR);
438 
439 	/*
440 	 * Now handle everything that either can't fail or we don't care if
441 	 * it does.  Note that we handle +/-o first in case another option
442 	 * overrides a change made implicity by a +/-o argument (e.g. -P).
443 	 */
444 	if (opt_o != NULL)
445 		(void) mdb_set_options(opt_o, TRUE);
446 	if (opt_plus_o != NULL)
447 		(void) mdb_set_options(opt_plus_o, FALSE);
448 	if (opt_I != NULL) {
449 #ifdef _KMDB
450 		mdb_warn("macro path cannot be set under kmdb\n");
451 #else
452 		mdb_set_ipath(opt_I);
453 #endif
454 	}
455 	if (opt_L != NULL)
456 		mdb_set_lpath(opt_L);
457 	if (opt_P != NULL)
458 		(void) mdb_set_prompt(opt_P);
459 	if (opt_s != (uintptr_t)-1)
460 		mdb.m_symdist = (size_t)opt_s;
461 	if (opt_D != NULL && (i = mdb_dstr2mode(opt_D)) != MDB_DBG_HELP)
462 		mdb_dmode((uint_t)i);
463 
464 	return (DCMD_OK);
465 }
466