xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_modapi.c (revision c3b397890d6e53844f306787c3e343c8ef35293d)
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 /*
23  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2013 by Delphix. All rights reserved.
25  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
26  * Copyright 2019 Joyent, Inc.
27  * Copyright 2022 Oxide Computer Company
28  * Copyright 2023 RackTop Systems, Inc.
29  */
30 
31 #include <mdb/mdb_modapi.h>
32 #include <mdb/mdb_module.h>
33 #include <mdb/mdb_string.h>
34 #include <mdb/mdb_debug.h>
35 #include <mdb/mdb_callb.h>
36 #include <mdb/mdb_dump.h>
37 #include <mdb/mdb_err.h>
38 #include <mdb/mdb_io.h>
39 #include <mdb/mdb_lex.h>
40 #include <mdb/mdb_frame.h>
41 #include <mdb/mdb.h>
42 #include <inttypes.h>
43 
44 /*
45  * Private callback structure for implementing mdb_walk_dcmd, below.
46  */
47 typedef struct {
48 	mdb_idcmd_t *dw_dcmd;
49 	mdb_argvec_t dw_argv;
50 	uint_t dw_flags;
51 } dcmd_walk_arg_t;
52 
53 /*
54  * Global properties which modules are allowed to look at.  These are
55  * re-initialized by the target activation callbacks.
56  */
57 int mdb_prop_postmortem = FALSE;	/* Are we examining a dump? */
58 int mdb_prop_kernel = FALSE;		/* Are we examining a kernel? */
59 int mdb_prop_datamodel = 0;		/* Data model (see mdb_target_impl.h) */
60 
61 static int
62 call_idcmd(mdb_idcmd_t *idcp, uintmax_t addr, uintmax_t count,
63     uint_t flags, mdb_argvec_t *argv);
64 
65 int
66 mdb_snprintfrac(char *buf, int len,
67     uint64_t numerator, uint64_t denom, int frac_digits)
68 {
69 	int mul = 1;
70 	int whole, frac, i;
71 
72 	for (i = frac_digits; i; i--)
73 		mul *= 10;
74 	whole = numerator / denom;
75 	frac = mul * numerator / denom - mul * whole;
76 	return (mdb_snprintf(buf, len, "%u.%0*u", whole, frac_digits, frac));
77 }
78 
79 void
80 mdb_nicenum(uint64_t num, char *buf)
81 {
82 	uint64_t n = num;
83 	int index = 0;
84 	char *u;
85 
86 	while (n >= 1024) {
87 		n = (n + (1024 / 2)) / 1024; /* Round up or down */
88 		index++;
89 	}
90 
91 	u = &" \0K\0M\0G\0T\0P\0E\0"[index*2];
92 
93 	if (index == 0) {
94 		(void) mdb_snprintf(buf, MDB_NICENUM_BUFLEN, "%llu",
95 		    (u_longlong_t)n);
96 	} else if (n < 10 && (num & (num - 1)) != 0) {
97 		(void) mdb_snprintfrac(buf, MDB_NICENUM_BUFLEN,
98 		    num, 1ULL << 10 * index, 2);
99 		(void) strcat(buf, u);
100 	} else if (n < 100 && (num & (num - 1)) != 0) {
101 		(void) mdb_snprintfrac(buf, MDB_NICENUM_BUFLEN,
102 		    num, 1ULL << 10 * index, 1);
103 		(void) strcat(buf, u);
104 	} else {
105 		(void) mdb_snprintf(buf, MDB_NICENUM_BUFLEN, "%llu%s",
106 		    (u_longlong_t)n, u);
107 	}
108 }
109 
110 void
111 mdb_nicetime(int64_t delta, char *buf, size_t buflen)
112 {
113 	const char	*sign = (delta < 0) ? "-" : "+";
114 	char		daybuf[32] = { 0 };
115 	char		fracbuf[32] = { 0 };
116 
117 	if (delta < 0)
118 		delta = -delta;
119 
120 	if (delta == 0) {
121 		(void) mdb_snprintf(buf, buflen, "0ns");
122 		return;
123 	}
124 
125 	/* Handle values < 1s */
126 	if (delta < NANOSEC) {
127 		static const char f_units[] = "num";
128 
129 		uint_t idx = 0;
130 		while (delta >= 1000) {
131 			delta /= 1000;
132 			idx++;
133 		}
134 
135 		(void) mdb_snprintf(buf, buflen, "t%s%lld%cs",
136 		    sign, delta, f_units[idx]);
137 		return;
138 	}
139 
140 	uint64_t days, hours, mins, secs, frac;
141 
142 	frac = delta % NANOSEC;
143 	delta /= NANOSEC;
144 
145 	secs = delta % 60;
146 	delta /= 60;
147 
148 	mins = delta % 60;
149 	delta /= 60;
150 
151 	hours = delta % 24;
152 	delta /= 24;
153 
154 	days = delta;
155 
156 	if (days > 0)
157 		(void) mdb_snprintf(daybuf, sizeof (daybuf), "%llud ", days);
158 
159 	if (frac > 0)
160 		(void) mdb_snprintf(fracbuf, sizeof (fracbuf), ".%llu", frac);
161 
162 	(void) mdb_snprintf(buf, buflen, "t%s%s%02llu:%02llu:%02llu%s",
163 	    sign, daybuf, hours, mins, secs, fracbuf);
164 }
165 
166 ssize_t
167 mdb_vread(void *buf, size_t nbytes, uintptr_t addr)
168 {
169 	ssize_t rbytes = mdb_tgt_vread(mdb.m_target, buf, nbytes, addr);
170 
171 	if (rbytes > 0 && rbytes < nbytes)
172 		return (set_errbytes(rbytes, nbytes));
173 
174 	return (rbytes);
175 }
176 
177 ssize_t
178 mdb_vwrite(const void *buf, size_t nbytes, uintptr_t addr)
179 {
180 	return (mdb_tgt_vwrite(mdb.m_target, buf, nbytes, addr));
181 }
182 
183 ssize_t
184 mdb_aread(void *buf, size_t nbytes, uintptr_t addr, void *as)
185 {
186 	ssize_t rbytes = mdb_tgt_aread(mdb.m_target, as, buf, nbytes, addr);
187 
188 	if (rbytes > 0 && rbytes < nbytes)
189 		return (set_errbytes(rbytes, nbytes));
190 
191 	return (rbytes);
192 }
193 
194 ssize_t
195 mdb_awrite(const void *buf, size_t nbytes, uintptr_t addr, void *as)
196 {
197 	return (mdb_tgt_awrite(mdb.m_target, as, buf, nbytes, addr));
198 }
199 
200 ssize_t
201 mdb_fread(void *buf, size_t nbytes, uintptr_t addr)
202 {
203 	ssize_t rbytes = mdb_tgt_fread(mdb.m_target, buf, nbytes, addr);
204 
205 	if (rbytes > 0 && rbytes < nbytes)
206 		return (set_errbytes(rbytes, nbytes));
207 
208 	return (rbytes);
209 }
210 
211 ssize_t
212 mdb_fwrite(const void *buf, size_t nbytes, uintptr_t addr)
213 {
214 	return (mdb_tgt_fwrite(mdb.m_target, buf, nbytes, addr));
215 }
216 
217 ssize_t
218 mdb_pread(void *buf, size_t nbytes, physaddr_t addr)
219 {
220 	ssize_t rbytes = mdb_tgt_pread(mdb.m_target, buf, nbytes, addr);
221 
222 	if (rbytes > 0 && rbytes < nbytes)
223 		return (set_errbytes(rbytes, nbytes));
224 
225 	return (rbytes);
226 }
227 
228 ssize_t
229 mdb_pwrite(const void *buf, size_t nbytes, physaddr_t addr)
230 {
231 	return (mdb_tgt_pwrite(mdb.m_target, buf, nbytes, addr));
232 }
233 
234 ssize_t
235 mdb_readstr(char *buf, size_t nbytes, uintptr_t addr)
236 {
237 	return (mdb_tgt_readstr(mdb.m_target, MDB_TGT_AS_VIRT,
238 	    buf, nbytes, addr));
239 }
240 
241 ssize_t
242 mdb_writestr(const char *buf, uintptr_t addr)
243 {
244 	return (mdb_tgt_writestr(mdb.m_target, MDB_TGT_AS_VIRT, buf, addr));
245 }
246 
247 ssize_t
248 mdb_readsym(void *buf, size_t nbytes, const char *name)
249 {
250 	ssize_t rbytes = mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT,
251 	    buf, nbytes, MDB_TGT_OBJ_EVERY, name);
252 
253 	if (rbytes > 0 && rbytes < nbytes)
254 		return (set_errbytes(rbytes, nbytes));
255 
256 	return (rbytes);
257 }
258 
259 ssize_t
260 mdb_writesym(const void *buf, size_t nbytes, const char *name)
261 {
262 	return (mdb_tgt_writesym(mdb.m_target, MDB_TGT_AS_VIRT,
263 	    buf, nbytes, MDB_TGT_OBJ_EVERY, name));
264 }
265 
266 ssize_t
267 mdb_readvar(void *buf, const char *name)
268 {
269 	GElf_Sym sym;
270 
271 	if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EVERY,
272 	    name, &sym, NULL))
273 		return (-1);
274 
275 	if (mdb_tgt_vread(mdb.m_target, buf, sym.st_size,
276 	    (uintptr_t)sym.st_value) == sym.st_size)
277 		return ((ssize_t)sym.st_size);
278 
279 	return (-1);
280 }
281 
282 ssize_t
283 mdb_writevar(const void *buf, const char *name)
284 {
285 	GElf_Sym sym;
286 
287 	if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EVERY,
288 	    name, &sym, NULL))
289 		return (-1);
290 
291 	if (mdb_tgt_vwrite(mdb.m_target, buf, sym.st_size,
292 	    (uintptr_t)sym.st_value) == sym.st_size)
293 		return ((ssize_t)sym.st_size);
294 
295 	return (-1);
296 }
297 
298 int
299 mdb_lookup_by_name(const char *name, GElf_Sym *sym)
300 {
301 	return (mdb_lookup_by_obj(MDB_TGT_OBJ_EVERY, name, sym));
302 }
303 
304 int
305 mdb_lookup_by_obj(const char *obj, const char *name, GElf_Sym *sym)
306 {
307 	return (mdb_tgt_lookup_by_name(mdb.m_target, obj, name, sym, NULL));
308 }
309 
310 int
311 mdb_lookup_by_addr(uintptr_t addr, uint_t flags, char *buf,
312     size_t nbytes, GElf_Sym *sym)
313 {
314 	return (mdb_tgt_lookup_by_addr(mdb.m_target, addr, flags,
315 	    buf, nbytes, sym, NULL));
316 }
317 
318 int
319 mdb_getareg(mdb_tid_t tid, const char *rname, mdb_reg_t *rp)
320 {
321 	return (mdb_tgt_getareg(mdb.m_target, tid, rname, rp));
322 }
323 
324 static u_longlong_t
325 mdb_strtoull_int(const char *s, int radix)
326 {
327 	if (s[0] == '0') {
328 		switch (s[1]) {
329 		case 'I':
330 		case 'i':
331 			radix = 2;
332 			s += 2;
333 			break;
334 		case 'O':
335 		case 'o':
336 			radix = 8;
337 			s += 2;
338 			break;
339 		case 'T':
340 		case 't':
341 			radix = 10;
342 			s += 2;
343 			break;
344 		case 'X':
345 		case 'x':
346 			radix = 16;
347 			s += 2;
348 			break;
349 		}
350 	}
351 
352 	return (mdb_strtonum(s, radix));
353 }
354 
355 u_longlong_t
356 mdb_strtoullx(const char *s, mdb_strtoull_flags_t flags)
357 {
358 	int radix;
359 
360 	if ((flags & ~MDB_STRTOULL_F_BASE_C) != 0) {
361 		mdb_warn("invalid options specified: 0x%lx" PRIx64 "\n",
362 		    (uint64_t)flags);
363 		return ((uintmax_t)ULLONG_MAX);
364 	}
365 
366 	if ((flags & MDB_STRTOULL_F_BASE_C) != 0) {
367 		radix = 10;
368 	} else {
369 		radix = mdb.m_radix;
370 	}
371 
372 	return (mdb_strtoull_int(s, radix));
373 }
374 
375 u_longlong_t
376 mdb_strtoull(const char *s)
377 {
378 	return (mdb_strtoull_int(s, mdb.m_radix));
379 }
380 
381 size_t
382 mdb_snprintf(char *buf, size_t nbytes, const char *format, ...)
383 {
384 	va_list alist;
385 
386 	va_start(alist, format);
387 	nbytes = mdb_iob_vsnprintf(buf, nbytes, format, alist);
388 	va_end(alist);
389 
390 	return (nbytes);
391 }
392 
393 void
394 mdb_printf(const char *format, ...)
395 {
396 	va_list alist;
397 
398 	va_start(alist, format);
399 	mdb_iob_vprintf(mdb.m_out, format, alist);
400 	va_end(alist);
401 }
402 
403 void
404 mdb_warn(const char *format, ...)
405 {
406 	va_list alist;
407 
408 	va_start(alist, format);
409 	vwarn(format, alist);
410 	va_end(alist);
411 }
412 
413 void
414 mdb_flush(void)
415 {
416 	mdb_iob_flush(mdb.m_out);
417 }
418 
419 /*
420  * Convert an object of len bytes pointed to by srcraw between
421  * network-order and host-order and store in dstraw.  The length len must
422  * be the actual length of the objects pointed to by srcraw and dstraw (or
423  * zero) or the results are undefined.  srcraw and dstraw may be the same,
424  * in which case the object is converted in-place.  Note that this routine
425  * will convert from host-order to network-order or network-order to
426  * host-order, since the conversion is the same in either case.
427  */
428 /* ARGSUSED */
429 void
430 mdb_nhconvert(void *dstraw, const void *srcraw, size_t len)
431 {
432 #ifdef	_LITTLE_ENDIAN
433 	uint8_t	b1, b2;
434 	uint8_t *dst, *src;
435 	size_t i;
436 
437 	dst = (uint8_t *)dstraw;
438 	src = (uint8_t *)srcraw;
439 	for (i = 0; i < len / 2; i++) {
440 		b1 = src[i];
441 		b2 = src[len - i - 1];
442 		dst[i] = b2;
443 		dst[len - i - 1] = b1;
444 	}
445 #else
446 	if (dstraw != srcraw)
447 		bcopy(srcraw, dstraw, len);
448 #endif
449 }
450 
451 
452 /*
453  * Bit formatting functions: Note the interesting use of UM_GC here to
454  * allocate a buffer for the caller which will be automatically freed
455  * when the dcmd completes or is forcibly aborted.
456  */
457 
458 #define	NBNB			(NBBY / 2)	/* number of bits per nibble */
459 #define	SETBIT(buf, j, c) { \
460 	if (((j) + 1) % (NBNB + 1) == 0) \
461 		(buf)[(j)++] = ' '; \
462 	(buf)[(j)++] = (c); \
463 }
464 
465 const char *
466 mdb_one_bit(int width, int bit, int on)
467 {
468 	int i, j = 0;
469 	char *buf;
470 
471 	buf = mdb_zalloc(width + (width / NBNB) + 2, UM_GC | UM_SLEEP);
472 
473 	for (i = --width; i > bit; i--)
474 		SETBIT(buf, j, '.');
475 
476 	SETBIT(buf, j, on ? '1' : '0');
477 
478 	for (i = bit - 1; i >= 0; i--)
479 		SETBIT(buf, j, '.');
480 
481 	return (buf);
482 }
483 
484 const char *
485 mdb_inval_bits(int width, int start, int stop)
486 {
487 	int i, j = 0;
488 	char *buf;
489 
490 	buf = mdb_zalloc(width + (width / NBNB) + 2, UM_GC | UM_SLEEP);
491 
492 	for (i = --width; i > stop; i--)
493 		SETBIT(buf, j, '.');
494 
495 	for (i = stop; i >= start; i--)
496 		SETBIT(buf, j, 'x');
497 
498 	for (; i >= 0; i--)
499 		SETBIT(buf, j, '.');
500 
501 	return (buf);
502 }
503 
504 ulong_t
505 mdb_inc_indent(ulong_t i)
506 {
507 	if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT) {
508 		ulong_t margin = mdb_iob_getmargin(mdb.m_out);
509 		mdb_iob_margin(mdb.m_out, margin + i);
510 		return (margin);
511 	}
512 
513 	mdb_iob_margin(mdb.m_out, i);
514 	mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
515 	return (0);
516 }
517 
518 ulong_t
519 mdb_dec_indent(ulong_t i)
520 {
521 	if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT) {
522 		ulong_t margin = mdb_iob_getmargin(mdb.m_out);
523 
524 		if (margin < i || margin - i == 0) {
525 			mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
526 			mdb_iob_margin(mdb.m_out, MDB_IOB_DEFMARGIN);
527 		} else
528 			mdb_iob_margin(mdb.m_out, margin - i);
529 
530 		return (margin);
531 	}
532 
533 	return (0);
534 }
535 
536 int
537 mdb_eval(const char *s)
538 {
539 	mdb_frame_t *ofp = mdb.m_fmark;
540 	mdb_frame_t *fp = mdb.m_frame;
541 	int err;
542 
543 	if (s == NULL)
544 		return (set_errno(EINVAL));
545 
546 	/*
547 	 * Push m_in down onto the input stack, then set m_in to point to the
548 	 * i/o buffer for our command string, and reset the frame marker.
549 	 * The mdb_run() function returns when the new m_in iob reaches EOF.
550 	 */
551 	mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
552 	mdb.m_in = mdb_iob_create(mdb_strio_create(s), MDB_IOB_RDONLY);
553 
554 	mdb.m_fmark = NULL;
555 	err = mdb_run();
556 	mdb.m_fmark = ofp;
557 
558 	/*
559 	 * Now pop the old standard input stream and restore mdb.m_in and
560 	 * the parser's saved current line number.
561 	 */
562 	mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
563 	yylineno = mdb_iob_lineno(mdb.m_in);
564 
565 	/*
566 	 * If mdb_run() returned an error, propagate this backward
567 	 * up the stack of debugger environment frames.
568 	 */
569 	if (MDB_ERR_IS_FATAL(err))
570 		longjmp(fp->f_pcb, err);
571 
572 	if (err == MDB_ERR_PAGER || err == MDB_ERR_SIGINT)
573 		return (set_errno(EMDB_CANCEL));
574 
575 	if (err != 0)
576 		return (set_errno(EMDB_EVAL));
577 
578 	return (0);
579 }
580 
581 void
582 mdb_set_dot(uintmax_t addr)
583 {
584 	mdb_nv_set_value(mdb.m_dot, addr);
585 	mdb.m_incr = 0;
586 }
587 
588 uintmax_t
589 mdb_get_dot(void)
590 {
591 	return (mdb_nv_get_value(mdb.m_dot));
592 }
593 
594 static int
595 walk_step(mdb_wcb_t *wcb)
596 {
597 	mdb_wcb_t *nwcb = wcb->w_lyr_head;
598 	int status;
599 
600 	/*
601 	 * If the control block has no layers, we just invoke the walker's
602 	 * step function and return status indicating whether to continue
603 	 * or stop.  If the control block has layers, we need to invoke
604 	 * ourself recursively for the next layer, until eventually we
605 	 * percolate down to an unlayered walk.
606 	 */
607 	if (nwcb == NULL)
608 		return (wcb->w_walker->iwlk_step(&wcb->w_state));
609 
610 	if ((status = walk_step(nwcb)) != WALK_NEXT) {
611 		wcb->w_lyr_head = nwcb->w_lyr_link;
612 		nwcb->w_lyr_link = NULL;
613 		mdb_wcb_destroy(nwcb);
614 	}
615 
616 	if (status == WALK_DONE && wcb->w_lyr_head != NULL)
617 		return (WALK_NEXT);
618 
619 	return (status);
620 }
621 
622 static int
623 walk_common(mdb_wcb_t *wcb)
624 {
625 	int status, rval = 0;
626 	mdb_frame_t *pfp;
627 
628 	/*
629 	 * Enter the control block in the active list so that mdb can clean
630 	 * up after it in case we abort out of the current command.
631 	 */
632 	if ((pfp = mdb_list_prev(mdb.m_frame)) != NULL && pfp->f_pcmd != NULL)
633 		mdb_wcb_insert(wcb, pfp);
634 	else
635 		mdb_wcb_insert(wcb, mdb.m_frame);
636 
637 	/*
638 	 * The per-walk constructor performs private buffer initialization
639 	 * and locates whatever symbols are necessary.
640 	 */
641 	if ((status = wcb->w_walker->iwlk_init(&wcb->w_state)) != WALK_NEXT) {
642 		if (status != WALK_DONE)
643 			rval = set_errno(EMDB_WALKINIT);
644 		goto done;
645 	}
646 
647 	/*
648 	 * Mark wcb to indicate that walk_init has been called (which means
649 	 * we can call walk_fini if the walk is aborted at this point).
650 	 */
651 	wcb->w_inited = TRUE;
652 
653 	while (walk_step(wcb) == WALK_NEXT)
654 		continue;
655 done:
656 	if ((pfp = mdb_list_prev(mdb.m_frame)) != NULL && pfp->f_pcmd != NULL)
657 		mdb_wcb_delete(wcb, pfp);
658 	else
659 		mdb_wcb_delete(wcb, mdb.m_frame);
660 
661 	mdb_wcb_destroy(wcb);
662 	return (rval);
663 }
664 
665 typedef struct pwalk_step {
666 	mdb_walk_cb_t ps_cb;
667 	void *ps_private;
668 } pwalk_step_t;
669 
670 static int
671 pwalk_step(uintptr_t addr, const void *data, void *private)
672 {
673 	pwalk_step_t *psp = private;
674 	int ret;
675 
676 	mdb.m_frame->f_cbactive = B_TRUE;
677 	ret = psp->ps_cb(addr, data, psp->ps_private);
678 	mdb.m_frame->f_cbactive = B_FALSE;
679 
680 	return (ret);
681 }
682 
683 int
684 mdb_pwalk(const char *name, mdb_walk_cb_t func, void *private, uintptr_t addr)
685 {
686 	mdb_iwalker_t *iwp = mdb_walker_lookup(name);
687 	pwalk_step_t p;
688 
689 	if (func == NULL)
690 		return (set_errno(EINVAL));
691 
692 	p.ps_cb = func;
693 	p.ps_private = private;
694 
695 	if (iwp != NULL) {
696 		int ret;
697 		int cbactive = mdb.m_frame->f_cbactive;
698 		mdb.m_frame->f_cbactive = B_FALSE;
699 		ret = walk_common(mdb_wcb_create(iwp, pwalk_step, &p, addr));
700 		mdb.m_frame->f_cbactive = cbactive;
701 		return (ret);
702 	}
703 
704 	return (-1); /* errno is set for us */
705 }
706 
707 int
708 mdb_walk(const char *name, mdb_walk_cb_t func, void *data)
709 {
710 	return (mdb_pwalk(name, func, data, 0));
711 }
712 
713 /*ARGSUSED*/
714 static int
715 walk_dcmd(uintptr_t addr, const void *ignored, dcmd_walk_arg_t *dwp)
716 {
717 	int status;
718 
719 	mdb.m_frame->f_cbactive = B_TRUE;
720 	status = call_idcmd(dwp->dw_dcmd, addr, 1, dwp->dw_flags,
721 	    &dwp->dw_argv);
722 	mdb.m_frame->f_cbactive = B_FALSE;
723 
724 	if (status == DCMD_USAGE || status == DCMD_ABORT)
725 		return (WALK_ERR);
726 
727 	dwp->dw_flags &= ~DCMD_LOOPFIRST;
728 	return (WALK_NEXT);
729 }
730 
731 int
732 mdb_pwalk_dcmd(const char *wname, const char *dcname,
733     int argc, const mdb_arg_t *argv, uintptr_t addr)
734 {
735 	mdb_argvec_t args;
736 	dcmd_walk_arg_t dw;
737 	mdb_iwalker_t *iwp;
738 	mdb_wcb_t *wcb;
739 	int status;
740 
741 	if (wname == NULL || dcname == NULL)
742 		return (set_errno(EINVAL));
743 
744 	if ((dw.dw_dcmd = mdb_dcmd_lookup(dcname)) == NULL)
745 		return (-1); /* errno is set for us */
746 
747 	if ((iwp = mdb_walker_lookup(wname)) == NULL)
748 		return (-1); /* errno is set for us */
749 
750 	args.a_data = (mdb_arg_t *)argv;
751 	args.a_nelems = args.a_size = argc;
752 
753 	mdb_argvec_create(&dw.dw_argv);
754 	mdb_argvec_copy(&dw.dw_argv, &args);
755 	dw.dw_flags = DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC;
756 
757 	wcb = mdb_wcb_create(iwp, (mdb_walk_cb_t)walk_dcmd, &dw, addr);
758 	status = walk_common(wcb);
759 
760 	mdb_argvec_zero(&dw.dw_argv);
761 	mdb_argvec_destroy(&dw.dw_argv);
762 
763 	return (status);
764 }
765 
766 int
767 mdb_walk_dcmd(const char *wname, const char *dcname,
768     int argc, const mdb_arg_t *argv)
769 {
770 	return (mdb_pwalk_dcmd(wname, dcname, argc, argv, 0));
771 }
772 
773 /*ARGSUSED*/
774 static int
775 layered_walk_step(uintptr_t addr, const void *data, mdb_wcb_t *wcb)
776 {
777 	/*
778 	 * Prior to calling the top-level walker's step function, reset its
779 	 * mdb_walk_state_t walk_addr and walk_layer members to refer to the
780 	 * target virtual address and data buffer of the underlying object.
781 	 */
782 	wcb->w_state.walk_addr = addr;
783 	wcb->w_state.walk_layer = data;
784 
785 	return (wcb->w_walker->iwlk_step(&wcb->w_state));
786 }
787 
788 int
789 mdb_layered_walk(const char *wname, mdb_walk_state_t *wsp)
790 {
791 	mdb_wcb_t *cwcb, *wcb;
792 	mdb_iwalker_t *iwp;
793 
794 	if (wname == NULL || wsp == NULL)
795 		return (set_errno(EINVAL));
796 
797 	if ((iwp = mdb_walker_lookup(wname)) == NULL)
798 		return (-1); /* errno is set for us */
799 
800 	if ((cwcb = mdb_wcb_from_state(wsp)) == NULL)
801 		return (set_errno(EMDB_BADWCB));
802 
803 	if (cwcb->w_walker == iwp)
804 		return (set_errno(EMDB_WALKLOOP));
805 
806 	wcb = mdb_wcb_create(iwp, (mdb_walk_cb_t)layered_walk_step,
807 	    cwcb, wsp->walk_addr);
808 
809 	if (iwp->iwlk_init(&wcb->w_state) != WALK_NEXT) {
810 		mdb_wcb_destroy(wcb);
811 		return (set_errno(EMDB_WALKINIT));
812 	}
813 
814 	wcb->w_inited = TRUE;
815 
816 	mdb_dprintf(MDB_DBG_WALK, "added %s`%s as %s`%s layer\n",
817 	    iwp->iwlk_modp->mod_name, iwp->iwlk_name,
818 	    cwcb->w_walker->iwlk_modp->mod_name, cwcb->w_walker->iwlk_name);
819 
820 	if (cwcb->w_lyr_head != NULL) {
821 		for (cwcb = cwcb->w_lyr_head; cwcb->w_lyr_link != NULL; )
822 			cwcb = cwcb->w_lyr_link;
823 		cwcb->w_lyr_link = wcb;
824 	} else
825 		cwcb->w_lyr_head = wcb;
826 
827 	return (0);
828 }
829 
830 int
831 mdb_call_dcmd(const char *name, uintptr_t dot, uint_t flags,
832     int argc, const mdb_arg_t *argv)
833 {
834 	mdb_idcmd_t *idcp;
835 	mdb_argvec_t args;
836 	int status;
837 
838 	if (name == NULL || argc < 0)
839 		return (set_errno(EINVAL));
840 
841 	if ((idcp = mdb_dcmd_lookup(name)) == NULL)
842 		return (-1); /* errno is set for us */
843 
844 	args.a_data = (mdb_arg_t *)argv;
845 	args.a_nelems = args.a_size = argc;
846 	status = call_idcmd(idcp, dot, 1, flags, &args);
847 
848 	if (status == DCMD_ERR || status == DCMD_ABORT)
849 		return (set_errno(EMDB_DCFAIL));
850 
851 	if (status == DCMD_USAGE)
852 		return (set_errno(EMDB_DCUSAGE));
853 
854 	return (0);
855 }
856 
857 /*
858  * When dcmds or walkers call a dcmd that might be in another module,
859  * we need to set mdb.m_frame->f_cp to an mdb_cmd that represents the
860  * dcmd we're currently executing, otherwise mdb_get_module gets the
861  * module of the caller instead of the module for the current dcmd.
862  */
863 static int
864 call_idcmd(mdb_idcmd_t *idcp, uintmax_t addr, uintmax_t count,
865     uint_t flags, mdb_argvec_t *argv)
866 {
867 	mdb_cmd_t *save_cp;
868 	mdb_cmd_t cmd;
869 	int ret;
870 
871 	bzero(&cmd, sizeof (cmd));
872 	cmd.c_dcmd = idcp;
873 	cmd.c_argv = *argv;
874 
875 	save_cp = mdb.m_frame->f_cp;
876 	mdb.m_frame->f_cp = &cmd;
877 
878 	ret = mdb_call_idcmd(cmd.c_dcmd, addr, count, flags,
879 	    &cmd.c_argv, NULL, NULL);
880 
881 	mdb.m_frame->f_cp = save_cp;
882 
883 	return (ret);
884 }
885 
886 int
887 mdb_add_walker(const mdb_walker_t *wp)
888 {
889 	mdb_module_t *mp;
890 
891 	if (mdb.m_lmod == NULL) {
892 		mdb_cmd_t *cp = mdb.m_frame->f_cp;
893 		mp = cp->c_dcmd->idc_modp;
894 	} else
895 		mp = mdb.m_lmod;
896 
897 	return (mdb_module_add_walker(mp, wp, 0));
898 }
899 
900 int
901 mdb_remove_walker(const char *name)
902 {
903 	mdb_module_t *mp;
904 
905 	if (mdb.m_lmod == NULL) {
906 		mdb_cmd_t *cp = mdb.m_frame->f_cp;
907 		mp = cp->c_dcmd->idc_modp;
908 	} else
909 		mp = mdb.m_lmod;
910 
911 	return (mdb_module_remove_walker(mp, name));
912 }
913 
914 void
915 mdb_get_pipe(mdb_pipe_t *p)
916 {
917 	mdb_cmd_t *cp = mdb.m_frame->f_cp;
918 	mdb_addrvec_t *adp = &cp->c_addrv;
919 
920 	if (p == NULL) {
921 		warn("dcmd failure: mdb_get_pipe invoked with NULL pointer\n");
922 		longjmp(mdb.m_frame->f_pcb, MDB_ERR_API);
923 	}
924 
925 	if (adp->ad_nelems != 0) {
926 		ASSERT(adp->ad_ndx != 0);
927 		p->pipe_data = &adp->ad_data[adp->ad_ndx - 1];
928 		p->pipe_len = adp->ad_nelems - adp->ad_ndx + 1;
929 		adp->ad_ndx = adp->ad_nelems;
930 	} else {
931 		p->pipe_data = NULL;
932 		p->pipe_len = 0;
933 	}
934 }
935 
936 void
937 mdb_set_pipe(const mdb_pipe_t *p)
938 {
939 	mdb_cmd_t *cp = mdb.m_frame->f_pcmd;
940 
941 	if (p == NULL) {
942 		warn("dcmd failure: mdb_set_pipe invoked with NULL pointer\n");
943 		longjmp(mdb.m_frame->f_pcb, MDB_ERR_API);
944 	}
945 
946 	if (cp != NULL) {
947 		size_t nbytes = sizeof (uintptr_t) * p->pipe_len;
948 
949 		mdb_cmd_reset(cp);
950 		cp->c_addrv.ad_data = mdb_alloc(nbytes, UM_SLEEP);
951 		bcopy(p->pipe_data, cp->c_addrv.ad_data, nbytes);
952 		cp->c_addrv.ad_nelems = p->pipe_len;
953 		cp->c_addrv.ad_size = p->pipe_len;
954 	}
955 }
956 
957 ssize_t
958 mdb_get_xdata(const char *name, void *buf, size_t nbytes)
959 {
960 	return (mdb_tgt_getxdata(mdb.m_target, name, buf, nbytes));
961 }
962 
963 /*
964  * Private callback structure for implementing mdb_object_iter, below.
965  */
966 typedef struct {
967 	mdb_object_cb_t oi_cb;
968 	void *oi_arg;
969 	int oi_rval;
970 } object_iter_arg_t;
971 
972 /*ARGSUSED*/
973 static int
974 mdb_object_cb(void *data, const mdb_map_t *map, const char *fullname)
975 {
976 	object_iter_arg_t *arg = data;
977 	mdb_object_t obj;
978 
979 	if (arg->oi_rval != 0)
980 		return (0);
981 
982 	bzero(&obj, sizeof (obj));
983 	obj.obj_base = map->map_base;
984 	obj.obj_name = strbasename(map->map_name);
985 	obj.obj_size = map->map_size;
986 	obj.obj_fullname = fullname;
987 
988 	arg->oi_rval = arg->oi_cb(&obj, arg->oi_arg);
989 
990 	return (0);
991 }
992 
993 int
994 mdb_object_iter(mdb_object_cb_t cb, void *data)
995 {
996 	object_iter_arg_t arg;
997 
998 	arg.oi_cb = cb;
999 	arg.oi_arg = data;
1000 	arg.oi_rval = 0;
1001 
1002 	if (mdb_tgt_object_iter(mdb.m_target, mdb_object_cb, &arg) != 0)
1003 		return (-1);
1004 
1005 	return (arg.oi_rval);
1006 }
1007 
1008 /*
1009  * Private callback structure for implementing mdb_symbol_iter, below.
1010  */
1011 typedef struct {
1012 	mdb_symbol_cb_t si_cb;
1013 	void *si_arg;
1014 	int si_rval;
1015 } symbol_iter_arg_t;
1016 
1017 /*ARGSUSED*/
1018 static int
1019 mdb_symbol_cb(void *data, const GElf_Sym *gsym, const char *name,
1020     const mdb_syminfo_t *sip, const char *obj)
1021 {
1022 	symbol_iter_arg_t *arg = data;
1023 	mdb_symbol_t sym;
1024 
1025 	if (arg->si_rval != 0)
1026 		return (0);
1027 
1028 	bzero(&sym, sizeof (sym));
1029 	sym.sym_name = name;
1030 	sym.sym_object = obj;
1031 	sym.sym_sym = gsym;
1032 	sym.sym_table = sip->sym_table;
1033 	sym.sym_id = sip->sym_id;
1034 
1035 	arg->si_rval = arg->si_cb(&sym, arg->si_arg);
1036 
1037 	return (0);
1038 }
1039 
1040 int
1041 mdb_symbol_iter(const char *obj, uint_t which, uint_t type,
1042     mdb_symbol_cb_t cb, void *data)
1043 {
1044 	symbol_iter_arg_t arg;
1045 
1046 	arg.si_cb = cb;
1047 	arg.si_arg = data;
1048 	arg.si_rval = 0;
1049 
1050 	if (mdb_tgt_symbol_iter(mdb.m_target, obj, which, type,
1051 	    mdb_symbol_cb, &arg) != 0)
1052 		return (-1);
1053 
1054 	return (arg.si_rval);
1055 }
1056 
1057 /*
1058  * Private structure and function for implementing mdb_dumpptr on top
1059  * of mdb_dump_internal
1060  */
1061 typedef struct dptrdat {
1062 	mdb_dumpptr_cb_t func;
1063 	void *arg;
1064 } dptrdat_t;
1065 
1066 static ssize_t
1067 mdb_dump_aux_ptr(void *buf, size_t nbyte, uint64_t offset, void *arg)
1068 {
1069 	dptrdat_t *dat = arg;
1070 
1071 	return (dat->func(buf, nbyte, offset, dat->arg));
1072 }
1073 
1074 /*
1075  * Private structure and function for handling callbacks which return
1076  * EMDB_PARTIAL
1077  */
1078 typedef struct d64dat {
1079 	mdb_dump64_cb_t func;
1080 	void *arg;
1081 } d64dat_t;
1082 
1083 static ssize_t
1084 mdb_dump_aux_partial(void *buf, size_t nbyte, uint64_t offset, void *arg)
1085 {
1086 	d64dat_t *dat = arg;
1087 	int result;
1088 	int count;
1089 
1090 	result = dat->func(buf, nbyte, offset, dat->arg);
1091 	if (result == -1 && errno == EMDB_PARTIAL) {
1092 		count = 0;
1093 		do {
1094 			result = dat->func((char *)buf + count, 1,
1095 			    offset + count, dat->arg);
1096 			if (result == 1)
1097 				count++;
1098 		} while (count < nbyte && result == 1);
1099 		if (count)
1100 			result = count;
1101 	}
1102 
1103 	return (result);
1104 }
1105 
1106 /* Default callback for mdb_dumpptr() is calling mdb_vread(). */
1107 static ssize_t
1108 mdb_dumpptr_cb(void *buf, size_t nbytes, uintptr_t addr, void *arg __unused)
1109 {
1110 	return (mdb_vread(buf, nbytes, addr));
1111 }
1112 
1113 int
1114 mdb_dumpptr(uintptr_t addr, size_t len, uint_t flags, mdb_dumpptr_cb_t fp,
1115     void *arg)
1116 {
1117 	dptrdat_t dat;
1118 	d64dat_t dat64;
1119 
1120 	if (fp == NULL)
1121 		dat.func = mdb_dumpptr_cb;
1122 	else
1123 		dat.func = fp;
1124 	dat.arg = arg;
1125 	dat64.func = mdb_dump_aux_ptr;
1126 	dat64.arg = &dat;
1127 	return (mdb_dump_internal(addr, len, flags, mdb_dump_aux_partial,
1128 	    &dat64, sizeof (uintptr_t)));
1129 }
1130 
1131 int
1132 mdb_dump64(uint64_t addr, uint64_t len, uint_t flags, mdb_dump64_cb_t fp,
1133     void *arg)
1134 {
1135 	d64dat_t dat64;
1136 
1137 	dat64.func = fp;
1138 	dat64.arg = arg;
1139 	return (mdb_dump_internal(addr, len, flags, mdb_dump_aux_partial,
1140 	    &dat64, sizeof (uint64_t)));
1141 }
1142 
1143 int
1144 mdb_get_state(void)
1145 {
1146 	mdb_tgt_status_t ts;
1147 
1148 	(void) mdb_tgt_status(mdb.m_target, &ts);
1149 
1150 	return (ts.st_state);
1151 }
1152 
1153 void *
1154 mdb_callback_add(int class, mdb_callback_f fp, void *arg)
1155 {
1156 	mdb_module_t *m;
1157 
1158 	if (class != MDB_CALLBACK_STCHG && class != MDB_CALLBACK_PROMPT) {
1159 		(void) set_errno(EINVAL);
1160 		return (NULL);
1161 	}
1162 
1163 	if (mdb.m_lmod != NULL)
1164 		m = mdb.m_lmod;
1165 	else
1166 		m = mdb.m_frame->f_cp->c_dcmd->idc_modp;
1167 
1168 	return (mdb_callb_add(m, class, fp, arg));
1169 }
1170 
1171 void
1172 mdb_callback_remove(void *hdl)
1173 {
1174 	mdb_callb_remove(hdl);
1175 }
1176