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