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 * Modular Debugger (MDB)
28 *
29 * Refer to the white paper "A Modular Debugger for Solaris" for information
30 * on the design, features, and goals of MDB. See /shared/sac/PSARC/1999/169
31 * for copies of the paper and related documentation.
32 *
33 * This file provides the basic construction and destruction of the debugger's
34 * global state, as well as the main execution loop, mdb_run(). MDB maintains
35 * a stack of execution frames (mdb_frame_t's) that keep track of its current
36 * state, including a stack of input and output buffers, walk and memory
37 * garbage collect lists, and a list of commands (mdb_cmd_t's). As the
38 * parser consumes input, it fills in a list of commands to execute, and then
39 * invokes mdb_call(), below. A command consists of a dcmd, telling us
40 * what function to execute, and a list of arguments and other invocation-
41 * specific data. Each frame may have more than one command, kept on a list,
42 * when multiple commands are separated by | operators. New frames may be
43 * stacked on old ones by nested calls to mdb_run: this occurs when, for
44 * example, in the middle of processing one input source (such as a file
45 * or the terminal), we invoke a dcmd that in turn calls mdb_eval(). mdb_eval
46 * will construct a new frame whose input source is the string passed to
47 * the eval function, and then execute this frame to completion.
48 */
49
50 #include <sys/param.h>
51 #include <stropts.h>
52
53 #define _MDB_PRIVATE
54 #include <mdb/mdb.h>
55
56 #include <mdb/mdb_context.h>
57 #include <mdb/mdb_argvec.h>
58 #include <mdb/mdb_signal.h>
59 #include <mdb/mdb_macalias.h>
60 #include <mdb/mdb_module.h>
61 #include <mdb/mdb_modapi.h>
62 #include <mdb/mdb_string.h>
63 #include <mdb/mdb_callb.h>
64 #include <mdb/mdb_debug.h>
65 #include <mdb/mdb_frame.h>
66 #include <mdb/mdb_conf.h>
67 #include <mdb/mdb_err.h>
68 #include <mdb/mdb_lex.h>
69 #include <mdb/mdb_io.h>
70 #ifdef _KMDB
71 #include <kmdb/kmdb_module.h>
72 #endif
73
74 /*
75 * Macro for testing if a dcmd's return status (x) indicates that we should
76 * abort the current loop or pipeline.
77 */
78 #define DCMD_ABORTED(x) ((x) == DCMD_USAGE || (x) == DCMD_ABORT)
79
80 extern const mdb_dcmd_t mdb_dcmd_builtins[];
81 extern mdb_dis_ctor_f *const mdb_dis_builtins[];
82
83 /*
84 * Variable discipline for toggling MDB_FL_PSYM based on the value of the
85 * undocumented '_' variable. Once adb(1) has been removed from the system,
86 * we should just remove this functionality and always disable PSYM for macros.
87 */
88 static uintmax_t
psym_disc_get(const mdb_var_t * v)89 psym_disc_get(const mdb_var_t *v)
90 {
91 int i = (mdb.m_flags & MDB_FL_PSYM) ? 1 : 0;
92 int j = (MDB_NV_VALUE(v) != 0) ? 1 : 0;
93
94 if ((i ^ j) == 0)
95 MDB_NV_VALUE((mdb_var_t *)v) = j ^ 1;
96
97 return (MDB_NV_VALUE(v));
98 }
99
100 static void
psym_disc_set(mdb_var_t * v,uintmax_t value)101 psym_disc_set(mdb_var_t *v, uintmax_t value)
102 {
103 if (value == 0)
104 mdb.m_flags |= MDB_FL_PSYM;
105 else
106 mdb.m_flags &= ~MDB_FL_PSYM;
107
108 MDB_NV_VALUE(v) = value;
109 }
110
111 /*
112 * Variable discipline for making <1 (most recent offset) behave properly.
113 */
114 static uintmax_t
roff_disc_get(const mdb_var_t * v)115 roff_disc_get(const mdb_var_t *v)
116 {
117 return (MDB_NV_VALUE(v));
118 }
119
120 static void
roff_disc_set(mdb_var_t * v,uintmax_t value)121 roff_disc_set(mdb_var_t *v, uintmax_t value)
122 {
123 mdb_nv_set_value(mdb.m_proffset, MDB_NV_VALUE(v));
124 MDB_NV_VALUE(v) = value;
125 }
126
127 /*
128 * Variable discipline for exporting the representative thread.
129 */
130 static uintmax_t
thr_disc_get(const mdb_var_t * v)131 thr_disc_get(const mdb_var_t *v)
132 {
133 mdb_tgt_status_t s;
134
135 if (mdb.m_target != NULL && mdb_tgt_status(mdb.m_target, &s) == 0)
136 return (s.st_tid);
137
138 return (MDB_NV_VALUE(v));
139 }
140
141 const char **
mdb_path_alloc(const char * s,size_t * newlen)142 mdb_path_alloc(const char *s, size_t *newlen)
143 {
144 char *format = mdb_alloc(strlen(s) * 2 + 1, UM_NOSLEEP);
145 const char **path;
146 char *p, *q;
147
148 struct utsname uts;
149 size_t len;
150 int i;
151
152 mdb_arg_t arg_i, arg_m, arg_p, arg_r, arg_t, arg_R, arg_V;
153 mdb_argvec_t argv;
154
155 static const char *empty_path[] = { NULL };
156
157 if (format == NULL)
158 goto nomem;
159
160 while (*s == ':')
161 s++; /* strip leading delimiters */
162
163 if (*s == '\0') {
164 *newlen = 0;
165 return (empty_path);
166 }
167
168 (void) strcpy(format, s);
169 mdb_argvec_create(&argv);
170
171 /*
172 * %i embedded in path string expands to ISA.
173 */
174 arg_i.a_type = MDB_TYPE_STRING;
175 if (mdb.m_target != NULL)
176 arg_i.a_un.a_str = mdb_tgt_isa(mdb.m_target);
177 else
178 arg_i.a_un.a_str = mdb_conf_isa();
179
180 /*
181 * %p embedded in path string expands to the platform name.
182 */
183 arg_p.a_type = MDB_TYPE_STRING;
184 if (mdb.m_target != NULL)
185 arg_p.a_un.a_str = mdb_tgt_platform(mdb.m_target);
186 else
187 arg_p.a_un.a_str = mdb_conf_platform();
188
189 /*
190 * %r embedded in path string expands to root directory, or
191 * to the empty string if root is "/" (to avoid // in paths).
192 */
193 arg_r.a_type = MDB_TYPE_STRING;
194 arg_r.a_un.a_str = strcmp(mdb.m_root, "/") ? mdb.m_root : "";
195
196 /*
197 * %t embedded in path string expands to the target name, defaulting to
198 * kvm; this is so we can find mdb_kb, which is used during bootstrap.
199 */
200 arg_t.a_type = MDB_TYPE_STRING;
201 arg_t.a_un.a_str = mdb.m_target ? mdb_tgt_name(mdb.m_target) : "kvm";
202
203 /*
204 * %R and %V expand to uname -r (release) and uname -v (version).
205 */
206 if (mdb.m_target == NULL || mdb_tgt_uname(mdb.m_target, &uts) < 0)
207 mdb_conf_uname(&uts);
208
209 arg_m.a_type = MDB_TYPE_STRING;
210 arg_m.a_un.a_str = uts.machine;
211
212 arg_R.a_type = MDB_TYPE_STRING;
213 arg_R.a_un.a_str = uts.release;
214
215 arg_V.a_type = MDB_TYPE_STRING;
216 if (mdb.m_flags & MDB_FL_LATEST)
217 arg_V.a_un.a_str = "latest";
218 else
219 arg_V.a_un.a_str = uts.version;
220
221 /*
222 * In order to expand the buffer, we examine the format string for
223 * our % tokens and construct an argvec, replacing each % token
224 * with %s along the way. If we encounter an unknown token, we
225 * shift over the remaining format buffer and stick in %%.
226 */
227 for (q = format; (q = strchr(q, '%')) != NULL; q++) {
228 switch (q[1]) {
229 case 'i':
230 mdb_argvec_append(&argv, &arg_i);
231 *++q = 's';
232 break;
233 case 'm':
234 mdb_argvec_append(&argv, &arg_m);
235 *++q = 's';
236 break;
237 case 'p':
238 mdb_argvec_append(&argv, &arg_p);
239 *++q = 's';
240 break;
241 case 'r':
242 mdb_argvec_append(&argv, &arg_r);
243 *++q = 's';
244 break;
245 case 't':
246 mdb_argvec_append(&argv, &arg_t);
247 *++q = 's';
248 break;
249 case 'R':
250 mdb_argvec_append(&argv, &arg_R);
251 *++q = 's';
252 break;
253 case 'V':
254 mdb_argvec_append(&argv, &arg_V);
255 *++q = 's';
256 break;
257 default:
258 bcopy(q + 1, q + 2, strlen(q));
259 *++q = '%';
260 }
261 }
262
263 /*
264 * We're now ready to use our printf engine to format the final string.
265 * Take one lap with a NULL buffer to determine how long the final
266 * string will be, allocate it, and format it.
267 */
268 len = mdb_iob_asnprintf(NULL, 0, format, argv.a_data);
269 if ((p = mdb_alloc(len + 1, UM_NOSLEEP)) != NULL)
270 (void) mdb_iob_asnprintf(p, len + 1, format, argv.a_data);
271 else
272 goto nomem;
273
274 mdb_argvec_zero(&argv);
275 mdb_argvec_destroy(&argv);
276
277 mdb_free(format, strlen(s) * 2 + 1);
278 format = NULL;
279
280 /*
281 * Compress the string to exclude any leading delimiters.
282 */
283 for (q = p; *q == ':'; q++)
284 continue;
285 if (q != p)
286 bcopy(q, p, strlen(q) + 1);
287
288 /*
289 * Count up the number of delimited elements. A sequence of
290 * consecutive delimiters is only counted once.
291 */
292 for (i = 1, q = p; (q = strchr(q, ':')) != NULL; i++) {
293 while (*q == ':')
294 q++;
295 }
296
297 if ((path = mdb_alloc(sizeof (char *) * (i + 1), UM_NOSLEEP)) == NULL) {
298 mdb_free(p, len + 1);
299 goto nomem;
300 }
301
302 for (i = 0, q = strtok(p, ":"); q != NULL; q = strtok(NULL, ":"))
303 path[i++] = q;
304
305 path[i] = NULL;
306 *newlen = len + 1;
307 return (path);
308
309 nomem:
310 warn("failed to allocate memory for path");
311 if (format != NULL)
312 mdb_free(format, strlen(s) * 2 + 1);
313 *newlen = 0;
314 return (empty_path);
315 }
316
317 const char **
mdb_path_dup(const char * path[],size_t pathlen,size_t * npathlenp)318 mdb_path_dup(const char *path[], size_t pathlen, size_t *npathlenp)
319 {
320 char **npath;
321 int i, j;
322
323 for (i = 0; path[i] != NULL; i++)
324 continue; /* count the path elements */
325
326 npath = mdb_zalloc(sizeof (char *) * (i + 1), UM_SLEEP);
327 if (pathlen > 0) {
328 npath[0] = mdb_alloc(pathlen, UM_SLEEP);
329 bcopy(path[0], npath[0], pathlen);
330 }
331
332 for (j = 1; j < i; j++)
333 npath[j] = npath[0] + (path[j] - path[0]);
334 npath[i] = NULL;
335
336 *npathlenp = pathlen;
337 return ((const char **)npath);
338 }
339
340 void
mdb_path_free(const char * path[],size_t pathlen)341 mdb_path_free(const char *path[], size_t pathlen)
342 {
343 int i;
344
345 for (i = 0; path[i] != NULL; i++)
346 continue; /* count the path elements */
347
348 if (i > 0) {
349 mdb_free((void *)path[0], pathlen);
350 mdb_free(path, sizeof (char *) * (i + 1));
351 }
352 }
353
354 /*
355 * Convert path string "s" to canonical form, expanding any %o tokens that are
356 * found within the path. The old path string is specified by "path", a buffer
357 * of size MAXPATHLEN which is then overwritten with the new path string.
358 */
359 static const char *
path_canon(char * path,const char * s)360 path_canon(char *path, const char *s)
361 {
362 char *p = path;
363 char *q = p + MAXPATHLEN - 1;
364
365 char old[MAXPATHLEN];
366 char c;
367
368 (void) strcpy(old, p);
369 *q = '\0';
370
371 while (p < q && (c = *s++) != '\0') {
372 if (c == '%') {
373 if ((c = *s++) == 'o') {
374 (void) strncpy(p, old, (size_t)(q - p));
375 p += strlen(p);
376 } else {
377 *p++ = '%';
378 if (p < q && c != '\0')
379 *p++ = c;
380 else
381 break;
382 }
383 } else
384 *p++ = c;
385 }
386
387 *p = '\0';
388 return (path);
389 }
390
391 void
mdb_set_ipath(const char * path)392 mdb_set_ipath(const char *path)
393 {
394 if (mdb.m_ipath != NULL)
395 mdb_path_free(mdb.m_ipath, mdb.m_ipathlen);
396
397 path = path_canon(mdb.m_ipathstr, path);
398 mdb.m_ipath = mdb_path_alloc(path, &mdb.m_ipathlen);
399 }
400
401 void
mdb_set_lpath(const char * path)402 mdb_set_lpath(const char *path)
403 {
404 if (mdb.m_lpath != NULL)
405 mdb_path_free(mdb.m_lpath, mdb.m_lpathlen);
406
407 path = path_canon(mdb.m_lpathstr, path);
408 mdb.m_lpath = mdb_path_alloc(path, &mdb.m_lpathlen);
409
410 #ifdef _KMDB
411 kmdb_module_path_set(mdb.m_lpath, mdb.m_lpathlen);
412 #endif
413 }
414
415 static void
prompt_update(void)416 prompt_update(void)
417 {
418 (void) mdb_snprintf(mdb.m_prompt, sizeof (mdb.m_prompt),
419 mdb.m_promptraw);
420 mdb.m_promptlen = strlen(mdb.m_prompt);
421 }
422
423 const char *
mdb_get_prompt(void)424 mdb_get_prompt(void)
425 {
426 if (mdb.m_promptlen == 0)
427 return (NULL);
428 else
429 return (mdb.m_prompt);
430 }
431
432 int
mdb_set_prompt(const char * p)433 mdb_set_prompt(const char *p)
434 {
435 size_t len = strlen(p);
436
437 if (len > MDB_PROMPTLEN) {
438 warn("prompt may not exceed %d characters\n", MDB_PROMPTLEN);
439 return (0);
440 }
441
442 (void) strcpy(mdb.m_promptraw, p);
443 prompt_update();
444 return (1);
445 }
446
447 static mdb_frame_t frame0;
448
449 void
mdb_create(const char * execname,const char * arg0)450 mdb_create(const char *execname, const char *arg0)
451 {
452 static const mdb_nv_disc_t psym_disc = { psym_disc_set, psym_disc_get };
453 static const mdb_nv_disc_t roff_disc = { roff_disc_set, roff_disc_get };
454 static const mdb_nv_disc_t thr_disc = { NULL, thr_disc_get };
455
456 static char rootdir[MAXPATHLEN];
457
458 const mdb_dcmd_t *dcp;
459 int i;
460
461 bzero(&mdb, sizeof (mdb_t));
462
463 mdb.m_flags = MDB_FL_PSYM | MDB_FL_PAGER | MDB_FL_BPTNOSYMSTOP |
464 MDB_FL_READBACK;
465 mdb.m_radix = MDB_DEF_RADIX;
466 mdb.m_nargs = MDB_DEF_NARGS;
467 mdb.m_histlen = MDB_DEF_HISTLEN;
468 mdb.m_armemlim = MDB_DEF_ARRMEM;
469 mdb.m_arstrlim = MDB_DEF_ARRSTR;
470
471 mdb.m_pname = strbasename(arg0);
472 if (strcmp(mdb.m_pname, "adb") == 0) {
473 mdb.m_flags |= MDB_FL_NOMODS | MDB_FL_ADB | MDB_FL_REPLAST;
474 mdb.m_flags &= ~MDB_FL_PAGER;
475 }
476
477 mdb.m_ipathstr = mdb_zalloc(MAXPATHLEN, UM_SLEEP);
478 mdb.m_lpathstr = mdb_zalloc(MAXPATHLEN, UM_SLEEP);
479
480 (void) strncpy(rootdir, execname, sizeof (rootdir));
481 rootdir[sizeof (rootdir) - 1] = '\0';
482 (void) strdirname(rootdir);
483
484 if (strcmp(strbasename(rootdir), "sparcv9") == 0 ||
485 strcmp(strbasename(rootdir), "sparcv7") == 0 ||
486 strcmp(strbasename(rootdir), "amd64") == 0 ||
487 strcmp(strbasename(rootdir), "i86") == 0)
488 (void) strdirname(rootdir);
489
490 if (strcmp(strbasename(rootdir), "bin") == 0) {
491 (void) strdirname(rootdir);
492 if (strcmp(strbasename(rootdir), "usr") == 0)
493 (void) strdirname(rootdir);
494 } else
495 (void) strcpy(rootdir, "/");
496
497 mdb.m_root = rootdir;
498
499 mdb.m_rminfo.mi_dvers = MDB_API_VERSION;
500 mdb.m_rminfo.mi_dcmds = mdb_dcmd_builtins;
501 mdb.m_rminfo.mi_walkers = NULL;
502
503 (void) mdb_nv_create(&mdb.m_rmod.mod_walkers, UM_SLEEP);
504 (void) mdb_nv_create(&mdb.m_rmod.mod_dcmds, UM_SLEEP);
505
506 mdb.m_rmod.mod_name = mdb.m_pname;
507 mdb.m_rmod.mod_info = &mdb.m_rminfo;
508
509 (void) mdb_nv_create(&mdb.m_disasms, UM_SLEEP);
510 (void) mdb_nv_create(&mdb.m_modules, UM_SLEEP);
511 (void) mdb_nv_create(&mdb.m_dcmds, UM_SLEEP);
512 (void) mdb_nv_create(&mdb.m_walkers, UM_SLEEP);
513 (void) mdb_nv_create(&mdb.m_nv, UM_SLEEP);
514
515 mdb.m_dot = mdb_nv_insert(&mdb.m_nv, ".", NULL, 0, MDB_NV_PERSIST);
516 mdb.m_rvalue = mdb_nv_insert(&mdb.m_nv, "0", NULL, 0, MDB_NV_PERSIST);
517
518 mdb.m_roffset =
519 mdb_nv_insert(&mdb.m_nv, "1", &roff_disc, 0, MDB_NV_PERSIST);
520
521 mdb.m_proffset = mdb_nv_insert(&mdb.m_nv, "2", NULL, 0, MDB_NV_PERSIST);
522 mdb.m_rcount = mdb_nv_insert(&mdb.m_nv, "9", NULL, 0, MDB_NV_PERSIST);
523
524 (void) mdb_nv_insert(&mdb.m_nv, "b", NULL, 0, MDB_NV_PERSIST);
525 (void) mdb_nv_insert(&mdb.m_nv, "d", NULL, 0, MDB_NV_PERSIST);
526 (void) mdb_nv_insert(&mdb.m_nv, "e", NULL, 0, MDB_NV_PERSIST);
527 (void) mdb_nv_insert(&mdb.m_nv, "m", NULL, 0, MDB_NV_PERSIST);
528 (void) mdb_nv_insert(&mdb.m_nv, "t", NULL, 0, MDB_NV_PERSIST);
529 (void) mdb_nv_insert(&mdb.m_nv, "_", &psym_disc, 0, MDB_NV_PERSIST);
530 (void) mdb_nv_insert(&mdb.m_nv, "hits", NULL, 0, MDB_NV_PERSIST);
531
532 (void) mdb_nv_insert(&mdb.m_nv, "thread", &thr_disc, 0,
533 MDB_NV_PERSIST | MDB_NV_RDONLY);
534
535 mdb.m_prsym = mdb_gelf_symtab_create_mutable();
536
537 (void) mdb_nv_insert(&mdb.m_modules, mdb.m_pname, NULL,
538 (uintptr_t)&mdb.m_rmod, MDB_NV_RDONLY);
539
540 for (dcp = &mdb_dcmd_builtins[0]; dcp->dc_name != NULL; dcp++)
541 (void) mdb_module_add_dcmd(&mdb.m_rmod, dcp, 0);
542
543 for (i = 0; mdb_dis_builtins[i] != NULL; i++)
544 (void) mdb_dis_create(mdb_dis_builtins[i]);
545
546 mdb_macalias_create();
547
548 mdb_create_builtin_tgts();
549
550 (void) mdb_callb_add(NULL, MDB_CALLB_PROMPT, (mdb_callb_f)prompt_update,
551 NULL);
552
553 #ifdef _KMDB
554 (void) mdb_nv_create(&mdb.m_dmodctl, UM_SLEEP);
555 #endif
556 mdb_lex_state_create(&frame0);
557
558 mdb_list_append(&mdb.m_flist, &frame0);
559 mdb.m_frame = &frame0;
560 }
561
562 void
mdb_destroy(void)563 mdb_destroy(void)
564 {
565 const mdb_dcmd_t *dcp;
566 mdb_var_t *v;
567 int unload_mode = MDB_MOD_SILENT;
568
569 #ifdef _KMDB
570 unload_mode |= MDB_MOD_DEFER;
571 #endif
572
573 mdb_intr_disable();
574
575 mdb_macalias_destroy();
576
577 /*
578 * Some targets use modules during ->t_destroy, so do it first.
579 */
580 if (mdb.m_target != NULL)
581 (void) mdb_tgt_destroy(mdb.m_target);
582
583 /*
584 * Unload modules _before_ destroying the disassemblers since a
585 * module that installs a disassembler should try to clean up after
586 * itself.
587 */
588 mdb_module_unload_all(unload_mode);
589
590 mdb_nv_rewind(&mdb.m_disasms);
591 while ((v = mdb_nv_advance(&mdb.m_disasms)) != NULL)
592 mdb_dis_destroy(mdb_nv_get_cookie(v));
593
594 mdb_callb_remove_all();
595
596 if (mdb.m_defdisasm != NULL)
597 strfree(mdb.m_defdisasm);
598
599 if (mdb.m_prsym != NULL)
600 mdb_gelf_symtab_destroy(mdb.m_prsym);
601
602 for (dcp = &mdb_dcmd_builtins[0]; dcp->dc_name != NULL; dcp++)
603 (void) mdb_module_remove_dcmd(&mdb.m_rmod, dcp->dc_name);
604
605 mdb_nv_destroy(&mdb.m_nv);
606 mdb_nv_destroy(&mdb.m_walkers);
607 mdb_nv_destroy(&mdb.m_dcmds);
608 mdb_nv_destroy(&mdb.m_modules);
609 mdb_nv_destroy(&mdb.m_disasms);
610
611 mdb_free(mdb.m_ipathstr, MAXPATHLEN);
612 mdb_free(mdb.m_lpathstr, MAXPATHLEN);
613
614 if (mdb.m_ipath != NULL)
615 mdb_path_free(mdb.m_ipath, mdb.m_ipathlen);
616
617 if (mdb.m_lpath != NULL)
618 mdb_path_free(mdb.m_lpath, mdb.m_lpathlen);
619
620 if (mdb.m_in != NULL)
621 mdb_iob_destroy(mdb.m_in);
622
623 mdb_iob_destroy(mdb.m_out);
624 mdb.m_out = NULL;
625 mdb_iob_destroy(mdb.m_err);
626 mdb.m_err = NULL;
627
628 if (mdb.m_log != NULL)
629 mdb_io_rele(mdb.m_log);
630
631 mdb_lex_state_destroy(&frame0);
632 }
633
634 /*
635 * The real main loop of the debugger: create a new execution frame on the
636 * debugger stack, and while we have input available, call into the parser.
637 */
638 int
mdb_run(void)639 mdb_run(void)
640 {
641 volatile int err;
642 mdb_frame_t f;
643
644 mdb_intr_disable();
645 mdb_frame_push(&f);
646
647 /*
648 * This is a fresh mdb context, so ignore any pipe command we may have
649 * inherited from the previous frame.
650 */
651 f.f_pcmd = NULL;
652
653 if ((err = setjmp(f.f_pcb)) != 0) {
654 int pop = (mdb.m_in != NULL &&
655 (mdb_iob_isapipe(mdb.m_in) || mdb_iob_isastr(mdb.m_in)));
656 int fromcmd = (f.f_cp != NULL);
657
658 mdb_dprintf(MDB_DBG_DSTK, "frame <%u> caught event %s\n",
659 f.f_id, mdb_err2str(err));
660
661 /*
662 * If a syntax error or other failure has occurred, pop all
663 * input buffers pushed by commands executed in this frame.
664 */
665 while (mdb_iob_stack_size(&f.f_istk) != 0) {
666 if (mdb.m_in != NULL)
667 mdb_iob_destroy(mdb.m_in);
668 mdb.m_in = mdb_iob_stack_pop(&f.f_istk);
669 yylineno = mdb_iob_lineno(mdb.m_in);
670 }
671
672 /*
673 * Reset standard output and the current frame to a known,
674 * clean state, so we can continue execution.
675 */
676 mdb_iob_margin(mdb.m_out, MDB_IOB_DEFMARGIN);
677 mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
678 mdb_iob_discard(mdb.m_out);
679 mdb_frame_reset(&f);
680
681 /*
682 * If there was an error writing to output, display a warning
683 * message if this is the topmost frame.
684 */
685 if (err == MDB_ERR_OUTPUT && mdb.m_depth == 1 && errno != EPIPE)
686 mdb_warn("write failed");
687
688 /*
689 * If an interrupt or quit signal is reported, we may have been
690 * in the middle of typing or processing the command line:
691 * print a newline and discard everything in the parser's iob.
692 * Note that we do this after m_out has been reset, otherwise
693 * we could trigger a pipe context switch or cause a write
694 * to a broken pipe (in the case of a shell command) when
695 * writing the newline.
696 */
697 if (err == MDB_ERR_SIGINT || err == MDB_ERR_QUIT) {
698 mdb_iob_nl(mdb.m_out);
699 yydiscard();
700 }
701
702 /*
703 * If we quit or abort using the output pager, reset the
704 * line count on standard output back to zero.
705 */
706 if (err == MDB_ERR_PAGER || MDB_ERR_IS_FATAL(err))
707 mdb_iob_clearlines(mdb.m_out);
708
709 /*
710 * If the user requested the debugger quit or abort back to
711 * the top, or if standard input is a pipe or mdb_eval("..."),
712 * then propagate the error up the debugger stack.
713 */
714 if (MDB_ERR_IS_FATAL(err) || pop != 0 ||
715 (err == MDB_ERR_PAGER && mdb.m_fmark != &f) ||
716 (err == MDB_ERR_NOMEM && !fromcmd)) {
717 mdb_frame_pop(&f, err);
718 return (err);
719 }
720
721 /*
722 * If we've returned here from a context where signals were
723 * blocked (e.g. a signal handler), we can now unblock them.
724 */
725 if (err == MDB_ERR_SIGINT)
726 (void) mdb_signal_unblock(SIGINT);
727 } else
728 mdb_intr_enable();
729
730 for (;;) {
731 while (mdb.m_in != NULL && (mdb_iob_getflags(mdb.m_in) &
732 (MDB_IOB_ERR | MDB_IOB_EOF)) == 0) {
733 if (mdb.m_depth == 1 &&
734 mdb_iob_stack_size(&f.f_istk) == 0) {
735 mdb_iob_clearlines(mdb.m_out);
736 mdb_tgt_periodic(mdb.m_target);
737 }
738
739 (void) yyparse();
740 }
741
742 if (mdb.m_in != NULL) {
743 if (mdb_iob_err(mdb.m_in)) {
744 warn("error reading input stream %s\n",
745 mdb_iob_name(mdb.m_in));
746 }
747 mdb_iob_destroy(mdb.m_in);
748 mdb.m_in = NULL;
749 }
750
751 if (mdb_iob_stack_size(&f.f_istk) == 0)
752 break; /* return when we're out of input */
753
754 mdb.m_in = mdb_iob_stack_pop(&f.f_istk);
755 yylineno = mdb_iob_lineno(mdb.m_in);
756 }
757
758 mdb_frame_pop(&f, 0);
759
760 /*
761 * The value of '.' is a per-frame attribute, to preserve it properly
762 * when switching frames. But in the case of calling mdb_run()
763 * explicitly (such as through mdb_eval), we want to propagate the value
764 * of '.' to the parent.
765 */
766 mdb_nv_set_value(mdb.m_dot, f.f_dot);
767
768 return (0);
769 }
770
771 /*
772 * The read-side of the pipe executes this service routine. We simply call
773 * mdb_run to create a new frame on the execution stack and run the MDB parser,
774 * and then propagate any error code back to the previous frame.
775 */
776 static int
runsvc(void)777 runsvc(void)
778 {
779 int err = mdb_run();
780
781 if (err != 0) {
782 mdb_dprintf(MDB_DBG_DSTK, "forwarding error %s from pipeline\n",
783 mdb_err2str(err));
784 longjmp(mdb.m_frame->f_pcb, err);
785 }
786
787 return (err);
788 }
789
790 /*
791 * Read-side pipe service routine: if we longjmp here, just return to the read
792 * routine because now we have more data to consume. Otherwise:
793 * (1) if ctx_data is non-NULL, longjmp to the write-side to produce more data;
794 * (2) if wriob is NULL, there is no writer but this is the first read, so we
795 * can just execute mdb_run() to completion on the current stack;
796 * (3) if (1) and (2) are false, then there is a writer and this is the first
797 * read, so create a co-routine context to execute mdb_run().
798 */
799 /*ARGSUSED*/
800 static void
rdsvc(mdb_iob_t * rdiob,mdb_iob_t * wriob,mdb_iob_ctx_t * ctx)801 rdsvc(mdb_iob_t *rdiob, mdb_iob_t *wriob, mdb_iob_ctx_t *ctx)
802 {
803 if (setjmp(ctx->ctx_rpcb) == 0) {
804 /*
805 * Save the current standard input into the pipe context, and
806 * reset m_in to point to the pipe. We will restore it on
807 * the way back in wrsvc() below.
808 */
809 ctx->ctx_iob = mdb.m_in;
810 mdb.m_in = rdiob;
811
812 ctx->ctx_rptr = mdb.m_frame;
813 if (ctx->ctx_wptr != NULL)
814 mdb_frame_switch(ctx->ctx_wptr);
815
816 if (ctx->ctx_data != NULL)
817 longjmp(ctx->ctx_wpcb, 1);
818 else if (wriob == NULL)
819 (void) runsvc();
820 else if ((ctx->ctx_data = mdb_context_create(runsvc)) != NULL)
821 mdb_context_switch(ctx->ctx_data);
822 else
823 mdb_warn("failed to create pipe context");
824 }
825 }
826
827 /*
828 * Write-side pipe service routine: if we longjmp here, just return to the
829 * write routine because now we have free space in the pipe buffer for writing;
830 * otherwise longjmp to the read-side to consume data and create space for us.
831 */
832 /*ARGSUSED*/
833 static void
wrsvc(mdb_iob_t * rdiob,mdb_iob_t * wriob,mdb_iob_ctx_t * ctx)834 wrsvc(mdb_iob_t *rdiob, mdb_iob_t *wriob, mdb_iob_ctx_t *ctx)
835 {
836 if (setjmp(ctx->ctx_wpcb) == 0) {
837 ctx->ctx_wptr = mdb.m_frame;
838 if (ctx->ctx_rptr != NULL)
839 mdb_frame_switch(ctx->ctx_rptr);
840
841 mdb.m_in = ctx->ctx_iob;
842 longjmp(ctx->ctx_rpcb, 1);
843 }
844 }
845
846 /*
847 * Call the current frame's mdb command. This entry point is used by the
848 * MDB parser to actually execute a command once it has successfully parsed
849 * a line of input. The command is waiting for us in the current frame.
850 * We loop through each command on the list, executing its dcmd with the
851 * appropriate argument. If the command has a successor, we know it had
852 * a | operator after it, and so we need to create a pipe and replace
853 * stdout with the pipe's output buffer.
854 */
855 int
mdb_call(uintmax_t addr,uintmax_t count,uint_t flags)856 mdb_call(uintmax_t addr, uintmax_t count, uint_t flags)
857 {
858 mdb_frame_t *fp = mdb.m_frame;
859 mdb_cmd_t *cp, *ncp;
860 mdb_iob_t *iobs[2];
861 int status, err = 0;
862 jmp_buf pcb;
863
864 if (mdb_iob_isapipe(mdb.m_in))
865 yyerror("syntax error");
866
867 mdb_intr_disable();
868 fp->f_cp = mdb_list_next(&fp->f_cmds);
869
870 if (flags & DCMD_LOOP)
871 flags |= DCMD_LOOPFIRST; /* set LOOPFIRST if this is a loop */
872
873 for (cp = mdb_list_next(&fp->f_cmds); cp; cp = mdb_list_next(cp)) {
874 if (mdb_list_next(cp) != NULL) {
875 mdb_iob_pipe(iobs, rdsvc, wrsvc);
876
877 mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
878 mdb.m_in = iobs[MDB_IOB_RDIOB];
879
880 mdb_iob_stack_push(&fp->f_ostk, mdb.m_out, 0);
881 mdb.m_out = iobs[MDB_IOB_WRIOB];
882
883 ncp = mdb_list_next(cp);
884 mdb_vcb_inherit(cp, ncp);
885
886 bcopy(fp->f_pcb, pcb, sizeof (jmp_buf));
887 ASSERT(fp->f_pcmd == NULL);
888 fp->f_pcmd = ncp;
889
890 mdb_frame_set_pipe(fp);
891
892 if ((err = setjmp(fp->f_pcb)) == 0) {
893 status = mdb_call_idcmd(cp->c_dcmd, addr, count,
894 flags | DCMD_PIPE_OUT, &cp->c_argv,
895 &cp->c_addrv, cp->c_vcbs);
896
897 ASSERT(mdb.m_in == iobs[MDB_IOB_RDIOB]);
898 ASSERT(mdb.m_out == iobs[MDB_IOB_WRIOB]);
899 } else {
900 mdb_dprintf(MDB_DBG_DSTK, "frame <%u> caught "
901 "error %s from pipeline\n", fp->f_id,
902 mdb_err2str(err));
903 }
904
905 if (err != 0 || DCMD_ABORTED(status)) {
906 mdb_iob_setflags(mdb.m_in, MDB_IOB_ERR);
907 mdb_iob_setflags(mdb.m_out, MDB_IOB_ERR);
908 } else {
909 mdb_iob_flush(mdb.m_out);
910 (void) mdb_iob_ctl(mdb.m_out, I_FLUSH,
911 (void *)FLUSHW);
912 }
913
914 mdb_frame_clear_pipe(fp);
915
916 mdb_iob_destroy(mdb.m_out);
917 mdb.m_out = mdb_iob_stack_pop(&fp->f_ostk);
918
919 if (mdb.m_in != NULL)
920 mdb_iob_destroy(mdb.m_in);
921
922 mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
923 yylineno = mdb_iob_lineno(mdb.m_in);
924
925 fp->f_pcmd = NULL;
926 bcopy(pcb, fp->f_pcb, sizeof (jmp_buf));
927
928 if (MDB_ERR_IS_FATAL(err))
929 longjmp(fp->f_pcb, err);
930
931 if (err != 0 || DCMD_ABORTED(status) ||
932 mdb_addrvec_length(&ncp->c_addrv) == 0)
933 break;
934
935 addr = mdb_nv_get_value(mdb.m_dot);
936 count = 1;
937 flags = 0;
938
939 } else {
940 mdb_intr_enable();
941 (void) mdb_call_idcmd(cp->c_dcmd, addr, count, flags,
942 &cp->c_argv, &cp->c_addrv, cp->c_vcbs);
943 mdb_intr_disable();
944 }
945
946 fp->f_cp = mdb_list_next(cp);
947 mdb_cmd_reset(cp);
948 }
949
950 /*
951 * If our last-command list is non-empty, destroy it. Then copy the
952 * current frame's cmd list to the m_lastc list and reset the frame.
953 */
954 while ((cp = mdb_list_next(&mdb.m_lastc)) != NULL) {
955 mdb_list_delete(&mdb.m_lastc, cp);
956 mdb_cmd_destroy(cp);
957 }
958
959 mdb_list_move(&fp->f_cmds, &mdb.m_lastc);
960 mdb_frame_reset(fp);
961 mdb_intr_enable();
962 return (err == 0);
963 }
964
965 uintmax_t
mdb_dot_incr(const char * op)966 mdb_dot_incr(const char *op)
967 {
968 uintmax_t odot, ndot;
969
970 odot = mdb_nv_get_value(mdb.m_dot);
971 ndot = odot + mdb.m_incr;
972
973 if ((odot ^ ndot) & 0x8000000000000000ull)
974 yyerror("'%s' would cause '.' to overflow\n", op);
975
976 return (ndot);
977 }
978
979 uintmax_t
mdb_dot_decr(const char * op)980 mdb_dot_decr(const char *op)
981 {
982 uintmax_t odot, ndot;
983
984 odot = mdb_nv_get_value(mdb.m_dot);
985 ndot = odot - mdb.m_incr;
986
987 if (ndot > odot)
988 yyerror("'%s' would cause '.' to underflow\n", op);
989
990 return (ndot);
991 }
992
993 mdb_iwalker_t *
mdb_walker_lookup(const char * s)994 mdb_walker_lookup(const char *s)
995 {
996 const char *p = strchr(s, '`');
997 mdb_var_t *v;
998
999 if (p != NULL) {
1000 size_t nbytes = MIN((size_t)(p - s), MDB_NV_NAMELEN - 1);
1001 char mname[MDB_NV_NAMELEN];
1002 mdb_module_t *mod;
1003
1004 (void) strncpy(mname, s, nbytes);
1005 mname[nbytes] = '\0';
1006
1007 if ((v = mdb_nv_lookup(&mdb.m_modules, mname)) == NULL) {
1008 (void) set_errno(EMDB_NOMOD);
1009 return (NULL);
1010 }
1011
1012 mod = mdb_nv_get_cookie(v);
1013
1014 if ((v = mdb_nv_lookup(&mod->mod_walkers, ++p)) != NULL)
1015 return (mdb_nv_get_cookie(v));
1016
1017 } else if ((v = mdb_nv_lookup(&mdb.m_walkers, s)) != NULL)
1018 return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
1019
1020 (void) set_errno(EMDB_NOWALK);
1021 return (NULL);
1022 }
1023
1024 mdb_idcmd_t *
mdb_dcmd_lookup(const char * s)1025 mdb_dcmd_lookup(const char *s)
1026 {
1027 const char *p = strchr(s, '`');
1028 mdb_var_t *v;
1029
1030 if (p != NULL) {
1031 size_t nbytes = MIN((size_t)(p - s), MDB_NV_NAMELEN - 1);
1032 char mname[MDB_NV_NAMELEN];
1033 mdb_module_t *mod;
1034
1035 (void) strncpy(mname, s, nbytes);
1036 mname[nbytes] = '\0';
1037
1038 if ((v = mdb_nv_lookup(&mdb.m_modules, mname)) == NULL) {
1039 (void) set_errno(EMDB_NOMOD);
1040 return (NULL);
1041 }
1042
1043 mod = mdb_nv_get_cookie(v);
1044
1045 if ((v = mdb_nv_lookup(&mod->mod_dcmds, ++p)) != NULL)
1046 return (mdb_nv_get_cookie(v));
1047
1048 } else if ((v = mdb_nv_lookup(&mdb.m_dcmds, s)) != NULL)
1049 return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
1050
1051 (void) set_errno(EMDB_NODCMD);
1052 return (NULL);
1053 }
1054
1055 void
mdb_dcmd_usage(const mdb_idcmd_t * idcp,mdb_iob_t * iob)1056 mdb_dcmd_usage(const mdb_idcmd_t *idcp, mdb_iob_t *iob)
1057 {
1058 const char *prefix = "", *usage = "";
1059 char name0 = idcp->idc_name[0];
1060
1061 if (idcp->idc_usage != NULL) {
1062 if (idcp->idc_usage[0] == ':') {
1063 if (name0 != ':' && name0 != '$')
1064 prefix = "address::";
1065 else
1066 prefix = "address";
1067 usage = &idcp->idc_usage[1];
1068
1069 } else if (idcp->idc_usage[0] == '?') {
1070 if (name0 != ':' && name0 != '$')
1071 prefix = "[address]::";
1072 else
1073 prefix = "[address]";
1074 usage = &idcp->idc_usage[1];
1075
1076 } else
1077 usage = idcp->idc_usage;
1078 }
1079
1080 mdb_iob_printf(iob, "Usage: %s%s %s\n", prefix, idcp->idc_name, usage);
1081
1082 if (idcp->idc_help != NULL) {
1083 mdb_iob_printf(iob, "%s: try '::help %s' for more "
1084 "information\n", mdb.m_pname, idcp->idc_name);
1085 }
1086 }
1087
1088 static mdb_idcmd_t *
dcmd_ndef(const mdb_idcmd_t * idcp)1089 dcmd_ndef(const mdb_idcmd_t *idcp)
1090 {
1091 mdb_var_t *v = mdb_nv_get_ndef(idcp->idc_var);
1092
1093 if (v != NULL)
1094 return (mdb_nv_get_cookie(mdb_nv_get_cookie(v)));
1095
1096 return (NULL);
1097 }
1098
1099 static int
dcmd_invoke(mdb_idcmd_t * idcp,uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv,const mdb_vcb_t * vcbs)1100 dcmd_invoke(mdb_idcmd_t *idcp, uintptr_t addr, uint_t flags,
1101 int argc, const mdb_arg_t *argv, const mdb_vcb_t *vcbs)
1102 {
1103 int status;
1104
1105 mdb_dprintf(MDB_DBG_DCMD, "dcmd %s`%s dot = %lr incr = %llr\n",
1106 idcp->idc_modp->mod_name, idcp->idc_name, addr, mdb.m_incr);
1107
1108 if ((status = idcp->idc_funcp(addr, flags, argc, argv)) == DCMD_USAGE) {
1109 mdb_dcmd_usage(idcp, mdb.m_err);
1110 goto done;
1111 }
1112
1113 while (status == DCMD_NEXT && (idcp = dcmd_ndef(idcp)) != NULL)
1114 status = idcp->idc_funcp(addr, flags, argc, argv);
1115
1116 if (status == DCMD_USAGE)
1117 mdb_dcmd_usage(idcp, mdb.m_err);
1118
1119 if (status == DCMD_NEXT)
1120 status = DCMD_OK;
1121 done:
1122 /*
1123 * If standard output is a pipe and there are vcbs active, we need to
1124 * flush standard out and the write-side of the pipe. The reasons for
1125 * this are explained in more detail in mdb_vcb.c.
1126 */
1127 if ((flags & DCMD_PIPE_OUT) && (vcbs != NULL)) {
1128 mdb_iob_flush(mdb.m_out);
1129 (void) mdb_iob_ctl(mdb.m_out, I_FLUSH, (void *)FLUSHW);
1130 }
1131
1132 return (status);
1133 }
1134
1135 /*
1136 * Call an internal dcmd directly: this code is used by module API functions
1137 * that need to execute dcmds, and by mdb_call() above.
1138 */
1139 int
mdb_call_idcmd(mdb_idcmd_t * idcp,uintmax_t addr,uintmax_t count,uint_t flags,mdb_argvec_t * avp,mdb_addrvec_t * adp,mdb_vcb_t * vcbs)1140 mdb_call_idcmd(mdb_idcmd_t *idcp, uintmax_t addr, uintmax_t count,
1141 uint_t flags, mdb_argvec_t *avp, mdb_addrvec_t *adp, mdb_vcb_t *vcbs)
1142 {
1143 int is_exec = (strcmp(idcp->idc_name, "$<") == 0);
1144 mdb_arg_t *argv;
1145 int argc;
1146 uintmax_t i;
1147 int status;
1148
1149 /*
1150 * Update the values of dot and the most recent address and count
1151 * to the values of our input parameters.
1152 */
1153 mdb_nv_set_value(mdb.m_dot, addr);
1154 mdb.m_raddr = addr;
1155 mdb.m_dcount = count;
1156
1157 /*
1158 * Here the adb(1) man page lies: '9' is only set to count
1159 * when the command is $<, not when it's $<<.
1160 */
1161 if (is_exec)
1162 mdb_nv_set_value(mdb.m_rcount, count);
1163
1164 /*
1165 * We can now return if the repeat count is zero.
1166 */
1167 if (count == 0)
1168 return (DCMD_OK);
1169
1170 /*
1171 * To guard against bad dcmds, we avoid passing the actual argv that
1172 * we will use to free argument strings directly to the dcmd. Instead,
1173 * we pass a copy that will be garbage collected automatically.
1174 */
1175 argc = avp->a_nelems;
1176 argv = mdb_alloc(sizeof (mdb_arg_t) * argc, UM_SLEEP | UM_GC);
1177 bcopy(avp->a_data, argv, sizeof (mdb_arg_t) * argc);
1178
1179 if (mdb_addrvec_length(adp) != 0) {
1180 flags |= DCMD_PIPE | DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC;
1181 addr = mdb_addrvec_shift(adp);
1182 mdb_nv_set_value(mdb.m_dot, addr);
1183 mdb_vcb_propagate(vcbs);
1184 count = 1;
1185 }
1186
1187 status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
1188 if (DCMD_ABORTED(status))
1189 goto done;
1190
1191 /*
1192 * If the command is $< and we're not receiving input from a pipe, we
1193 * ignore the repeat count and just return since the macro file is now
1194 * pushed on to the input stack.
1195 */
1196 if (is_exec && mdb_addrvec_length(adp) == 0)
1197 goto done;
1198
1199 /*
1200 * If we're going to loop, we've already executed the dcmd once,
1201 * so clear the LOOPFIRST flag before proceeding.
1202 */
1203 if (flags & DCMD_LOOP)
1204 flags &= ~DCMD_LOOPFIRST;
1205
1206 for (i = 1; i < count; i++) {
1207 addr = mdb_dot_incr(",");
1208 mdb_nv_set_value(mdb.m_dot, addr);
1209 status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
1210 if (DCMD_ABORTED(status))
1211 goto done;
1212 }
1213
1214 while (mdb_addrvec_length(adp) != 0) {
1215 addr = mdb_addrvec_shift(adp);
1216 mdb_nv_set_value(mdb.m_dot, addr);
1217 mdb_vcb_propagate(vcbs);
1218 status = dcmd_invoke(idcp, addr, flags, argc, argv, vcbs);
1219 if (DCMD_ABORTED(status))
1220 goto done;
1221 }
1222 done:
1223 mdb_iob_nlflush(mdb.m_out);
1224 return (status);
1225 }
1226
1227 void
mdb_intr_enable(void)1228 mdb_intr_enable(void)
1229 {
1230 ASSERT(mdb.m_intr >= 1);
1231 if (mdb.m_intr == 1 && mdb.m_pend != 0) {
1232 (void) mdb_signal_block(SIGINT);
1233 mdb.m_intr = mdb.m_pend = 0;
1234 mdb_dprintf(MDB_DBG_DSTK, "delivering pending INT\n");
1235 longjmp(mdb.m_frame->f_pcb, MDB_ERR_SIGINT);
1236 } else
1237 mdb.m_intr--;
1238 }
1239
1240 void
mdb_intr_disable(void)1241 mdb_intr_disable(void)
1242 {
1243 mdb.m_intr++;
1244 ASSERT(mdb.m_intr >= 1);
1245 }
1246
1247 /*
1248 * Create an encoded string representing the internal user-modifiable
1249 * configuration of the debugger and return a pointer to it. The string can be
1250 * used to initialize another instance of the debugger with the same
1251 * configuration as this one.
1252 */
1253 char *
mdb_get_config(void)1254 mdb_get_config(void)
1255 {
1256 size_t r, n = 0;
1257 char *s = NULL;
1258
1259 while ((r = mdb_snprintf(s, n,
1260 "%x;%x;%x;%x;%x;%x;%lx;%x;%x;%s;%s;%s;%s;%s",
1261 mdb.m_tgtflags, mdb.m_flags, mdb.m_debug, mdb.m_radix, mdb.m_nargs,
1262 mdb.m_histlen, (ulong_t)mdb.m_symdist, mdb.m_execmode,
1263 mdb.m_forkmode, mdb.m_root, mdb.m_termtype, mdb.m_ipathstr,
1264 mdb.m_lpathstr, mdb.m_prompt)) > n) {
1265
1266 mdb_free(s, n);
1267 n = r + 1;
1268 s = mdb_alloc(r + 1, UM_SLEEP);
1269 }
1270
1271 return (s);
1272 }
1273
1274 /*
1275 * Decode a configuration string created with mdb_get_config() and reset the
1276 * appropriate parts of the global mdb_t accordingly.
1277 */
1278 void
mdb_set_config(const char * s)1279 mdb_set_config(const char *s)
1280 {
1281 const char *p;
1282 size_t len;
1283
1284 if ((p = strchr(s, ';')) != NULL) {
1285 mdb.m_tgtflags = strntoul(s, (size_t)(p - s), 16);
1286 s = p + 1;
1287 }
1288
1289 if ((p = strchr(s, ';')) != NULL) {
1290 mdb.m_flags = strntoul(s, (size_t)(p - s), 16);
1291 mdb.m_flags &= ~(MDB_FL_LOG | MDB_FL_LATEST);
1292 s = p + 1;
1293 }
1294
1295 if ((p = strchr(s, ';')) != NULL) {
1296 mdb.m_debug = strntoul(s, (size_t)(p - s), 16);
1297 s = p + 1;
1298 }
1299
1300 if ((p = strchr(s, ';')) != NULL) {
1301 mdb.m_radix = (int)strntoul(s, (size_t)(p - s), 16);
1302 if (mdb.m_radix < 2 || mdb.m_radix > 16)
1303 mdb.m_radix = MDB_DEF_RADIX;
1304 s = p + 1;
1305 }
1306
1307 if ((p = strchr(s, ';')) != NULL) {
1308 mdb.m_nargs = (int)strntoul(s, (size_t)(p - s), 16);
1309 mdb.m_nargs = MAX(mdb.m_nargs, 0);
1310 s = p + 1;
1311 }
1312
1313 if ((p = strchr(s, ';')) != NULL) {
1314 mdb.m_histlen = (int)strntoul(s, (size_t)(p - s), 16);
1315 mdb.m_histlen = MAX(mdb.m_histlen, 1);
1316 s = p + 1;
1317 }
1318
1319 if ((p = strchr(s, ';')) != NULL) {
1320 mdb.m_symdist = strntoul(s, (size_t)(p - s), 16);
1321 s = p + 1;
1322 }
1323
1324 if ((p = strchr(s, ';')) != NULL) {
1325 mdb.m_execmode = (uchar_t)strntoul(s, (size_t)(p - s), 16);
1326 if (mdb.m_execmode > MDB_EM_FOLLOW)
1327 mdb.m_execmode = MDB_EM_ASK;
1328 s = p + 1;
1329 }
1330
1331 if ((p = strchr(s, ';')) != NULL) {
1332 mdb.m_forkmode = (uchar_t)strntoul(s, (size_t)(p - s), 16);
1333 if (mdb.m_forkmode > MDB_FM_CHILD)
1334 mdb.m_forkmode = MDB_FM_ASK;
1335 s = p + 1;
1336 }
1337
1338 if ((p = strchr(s, ';')) != NULL) {
1339 mdb.m_root = strndup(s, (size_t)(p - s));
1340 s = p + 1;
1341 }
1342
1343 if ((p = strchr(s, ';')) != NULL) {
1344 mdb.m_termtype = strndup(s, (size_t)(p - s));
1345 s = p + 1;
1346 }
1347
1348 if ((p = strchr(s, ';')) != NULL) {
1349 size_t len = MIN(sizeof (mdb.m_ipathstr) - 1, p - s);
1350 (void) strncpy(mdb.m_ipathstr, s, len);
1351 mdb.m_ipathstr[len] = '\0';
1352 s = p + 1;
1353 }
1354
1355 if ((p = strchr(s, ';')) != NULL) {
1356 size_t len = MIN(sizeof (mdb.m_lpathstr) - 1, p - s);
1357 (void) strncpy(mdb.m_lpathstr, s, len);
1358 mdb.m_lpathstr[len] = '\0';
1359 s = p + 1;
1360 }
1361
1362 p = s + strlen(s);
1363 len = MIN(MDB_PROMPTLEN, (size_t)(p - s));
1364 (void) strncpy(mdb.m_prompt, s, len);
1365 mdb.m_prompt[len] = '\0';
1366 mdb.m_promptlen = len;
1367 }
1368
1369 mdb_module_t *
mdb_get_module(void)1370 mdb_get_module(void)
1371 {
1372 if (mdb.m_lmod)
1373 return (mdb.m_lmod);
1374
1375 if (mdb.m_frame == NULL)
1376 return (NULL);
1377
1378 if (mdb.m_frame->f_wcbs && mdb.m_frame->f_wcbs->w_walker &&
1379 mdb.m_frame->f_wcbs->w_walker->iwlk_modp)
1380 return (mdb.m_frame->f_wcbs->w_walker->iwlk_modp);
1381
1382 if (mdb.m_frame->f_cp && mdb.m_frame->f_cp->c_dcmd)
1383 return (mdb.m_frame->f_cp->c_dcmd->idc_modp);
1384
1385 return (NULL);
1386 }
1387