xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_modapi.c (revision 8a6a72fd51dfee674c6daa8a222f68b1532516ab)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <mdb/mdb_modapi.h>
29 #include <mdb/mdb_module.h>
30 #include <mdb/mdb_string.h>
31 #include <mdb/mdb_debug.h>
32 #include <mdb/mdb_callb.h>
33 #include <mdb/mdb_dump.h>
34 #include <mdb/mdb_err.h>
35 #include <mdb/mdb_io.h>
36 #include <mdb/mdb_lex.h>
37 #include <mdb/mdb_frame.h>
38 #include <mdb/mdb.h>
39 
40 /*
41  * Private callback structure for implementing mdb_walk_dcmd, below.
42  */
43 typedef struct {
44 	mdb_idcmd_t *dw_dcmd;
45 	mdb_argvec_t dw_argv;
46 	uint_t dw_flags;
47 } dcmd_walk_arg_t;
48 
49 /*
50  * Global properties which modules are allowed to look at.  These are
51  * re-initialized by the target activation callbacks.
52  */
53 int mdb_prop_postmortem = FALSE;	/* Are we examining a dump? */
54 int mdb_prop_kernel = FALSE;		/* Are we examining a kernel? */
55 int mdb_prop_datamodel = 0;		/* Data model (see mdb_target_impl.h) */
56 
57 ssize_t
58 mdb_vread(void *buf, size_t nbytes, uintptr_t addr)
59 {
60 	ssize_t rbytes = mdb_tgt_vread(mdb.m_target, buf, nbytes, addr);
61 
62 	if (rbytes > 0 && rbytes < nbytes)
63 		return (set_errbytes(rbytes, nbytes));
64 
65 	return (rbytes);
66 }
67 
68 ssize_t
69 mdb_vwrite(const void *buf, size_t nbytes, uintptr_t addr)
70 {
71 	return (mdb_tgt_vwrite(mdb.m_target, buf, nbytes, addr));
72 }
73 
74 ssize_t
75 mdb_fread(void *buf, size_t nbytes, uintptr_t addr)
76 {
77 	ssize_t rbytes = mdb_tgt_fread(mdb.m_target, buf, nbytes, addr);
78 
79 	if (rbytes > 0 && rbytes < nbytes)
80 		return (set_errbytes(rbytes, nbytes));
81 
82 	return (rbytes);
83 }
84 
85 ssize_t
86 mdb_fwrite(const void *buf, size_t nbytes, uintptr_t addr)
87 {
88 	return (mdb_tgt_fwrite(mdb.m_target, buf, nbytes, addr));
89 }
90 
91 ssize_t
92 mdb_pread(void *buf, size_t nbytes, physaddr_t addr)
93 {
94 	ssize_t rbytes = mdb_tgt_pread(mdb.m_target, buf, nbytes, addr);
95 
96 	if (rbytes > 0 && rbytes < nbytes)
97 		return (set_errbytes(rbytes, nbytes));
98 
99 	return (rbytes);
100 }
101 
102 ssize_t
103 mdb_pwrite(const void *buf, size_t nbytes, physaddr_t addr)
104 {
105 	return (mdb_tgt_pwrite(mdb.m_target, buf, nbytes, addr));
106 }
107 
108 ssize_t
109 mdb_readstr(char *buf, size_t nbytes, uintptr_t addr)
110 {
111 	return (mdb_tgt_readstr(mdb.m_target, MDB_TGT_AS_VIRT,
112 	    buf, nbytes, addr));
113 }
114 
115 ssize_t
116 mdb_writestr(const char *buf, uintptr_t addr)
117 {
118 	return (mdb_tgt_writestr(mdb.m_target, MDB_TGT_AS_VIRT, buf, addr));
119 }
120 
121 ssize_t
122 mdb_readsym(void *buf, size_t nbytes, const char *name)
123 {
124 	ssize_t rbytes = mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT,
125 	    buf, nbytes, MDB_TGT_OBJ_EXEC, name);
126 
127 	if (rbytes > 0 && rbytes < nbytes)
128 		return (set_errbytes(rbytes, nbytes));
129 
130 	return (rbytes);
131 }
132 
133 ssize_t
134 mdb_writesym(const void *buf, size_t nbytes, const char *name)
135 {
136 	return (mdb_tgt_writesym(mdb.m_target, MDB_TGT_AS_VIRT,
137 	    buf, nbytes, MDB_TGT_OBJ_EXEC, name));
138 }
139 
140 ssize_t
141 mdb_readvar(void *buf, const char *name)
142 {
143 	GElf_Sym sym;
144 
145 	if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EXEC,
146 	    name, &sym, NULL))
147 		return (-1);
148 
149 	if (mdb_tgt_vread(mdb.m_target, buf, sym.st_size,
150 	    (uintptr_t)sym.st_value) == sym.st_size)
151 		return ((ssize_t)sym.st_size);
152 
153 	return (-1);
154 }
155 
156 ssize_t
157 mdb_writevar(const void *buf, const char *name)
158 {
159 	GElf_Sym sym;
160 
161 	if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EXEC,
162 	    name, &sym, NULL))
163 		return (-1);
164 
165 	if (mdb_tgt_vwrite(mdb.m_target, buf, sym.st_size,
166 	    (uintptr_t)sym.st_value) == sym.st_size)
167 		return ((ssize_t)sym.st_size);
168 
169 	return (-1);
170 }
171 
172 int
173 mdb_lookup_by_name(const char *name, GElf_Sym *sym)
174 {
175 	return (mdb_lookup_by_obj(MDB_TGT_OBJ_EXEC, name, sym));
176 }
177 
178 int
179 mdb_lookup_by_obj(const char *obj, const char *name, GElf_Sym *sym)
180 {
181 	return (mdb_tgt_lookup_by_name(mdb.m_target, obj, name, sym, NULL));
182 }
183 
184 int
185 mdb_lookup_by_addr(uintptr_t addr, uint_t flags, char *buf,
186 	size_t nbytes, GElf_Sym *sym)
187 {
188 	return (mdb_tgt_lookup_by_addr(mdb.m_target, addr, flags,
189 	    buf, nbytes, sym, NULL));
190 }
191 
192 u_longlong_t
193 mdb_strtoull(const char *s)
194 {
195 	int radix = mdb.m_radix;
196 
197 	if (s[0] == '0') {
198 		switch (s[1]) {
199 		case 'I':
200 		case 'i':
201 			radix = 2;
202 			s += 2;
203 			break;
204 		case 'O':
205 		case 'o':
206 			radix = 8;
207 			s += 2;
208 			break;
209 		case 'T':
210 		case 't':
211 			radix = 10;
212 			s += 2;
213 			break;
214 		case 'X':
215 		case 'x':
216 			radix = 16;
217 			s += 2;
218 			break;
219 		}
220 	}
221 
222 	return (strtonum(s, radix));
223 }
224 
225 size_t
226 mdb_snprintf(char *buf, size_t nbytes, const char *format, ...)
227 {
228 	va_list alist;
229 
230 	va_start(alist, format);
231 	nbytes = mdb_iob_vsnprintf(buf, nbytes, format, alist);
232 	va_end(alist);
233 
234 	return (nbytes);
235 }
236 
237 void
238 mdb_printf(const char *format, ...)
239 {
240 	va_list alist;
241 
242 	va_start(alist, format);
243 	mdb_iob_vprintf(mdb.m_out, format, alist);
244 	va_end(alist);
245 }
246 
247 void
248 mdb_warn(const char *format, ...)
249 {
250 	va_list alist;
251 
252 	va_start(alist, format);
253 	vwarn(format, alist);
254 	va_end(alist);
255 }
256 
257 void
258 mdb_flush(void)
259 {
260 	mdb_iob_flush(mdb.m_out);
261 }
262 
263 /*
264  * Convert an object of len bytes pointed to by srcraw between
265  * network-order and host-order and store in dstraw.  The length len must
266  * be the actual length of the objects pointed to by srcraw and dstraw (or
267  * zero) or the results are undefined.  srcraw and dstraw may be the same,
268  * in which case the object is converted in-place.  Note that this routine
269  * will convert from host-order to network-order or network-order to
270  * host-order, since the conversion is the same in either case.
271  */
272 /* ARGSUSED */
273 void
274 mdb_nhconvert(void *dstraw, const void *srcraw, size_t len)
275 {
276 #ifdef	_LITTLE_ENDIAN
277 	uint8_t	b1, b2;
278 	uint8_t *dst, *src;
279 	size_t i;
280 
281 	dst = (uint8_t *)dstraw;
282 	src = (uint8_t *)srcraw;
283 	for (i = 0; i < len / 2; i++) {
284 		b1 = src[i];
285 		b2 = src[len - i - 1];
286 		dst[i] = b2;
287 		dst[len - i - 1] = b1;
288 	}
289 #else
290 	if (dstraw != srcraw)
291 		bcopy(srcraw, dstraw, len);
292 #endif
293 }
294 
295 
296 /*
297  * Bit formatting functions: Note the interesting use of UM_GC here to
298  * allocate a buffer for the caller which will be automatically freed
299  * when the dcmd completes or is forcibly aborted.
300  */
301 
302 #define	NBNB			(NBBY / 2)	/* number of bits per nibble */
303 #define	SETBIT(buf, j, c) { \
304 	if (((j) + 1) % (NBNB + 1) == 0) \
305 		(buf)[(j)++] = ' '; \
306 	(buf)[(j)++] = (c); \
307 }
308 
309 const char *
310 mdb_one_bit(int width, int bit, int on)
311 {
312 	int i, j = 0;
313 	char *buf;
314 
315 	buf = mdb_zalloc(width + (width / NBNB) + 2, UM_GC | UM_SLEEP);
316 
317 	for (i = --width; i > bit; i--)
318 		SETBIT(buf, j, '.');
319 
320 	SETBIT(buf, j, on ? '1' : '0');
321 
322 	for (i = bit - 1; i >= 0; i--)
323 		SETBIT(buf, j, '.');
324 
325 	return (buf);
326 }
327 
328 const char *
329 mdb_inval_bits(int width, int start, int stop)
330 {
331 	int i, j = 0;
332 	char *buf;
333 
334 	buf = mdb_zalloc(width + (width / NBNB) + 2, UM_GC | UM_SLEEP);
335 
336 	for (i = --width; i > stop; i--)
337 		SETBIT(buf, j, '.');
338 
339 	for (i = stop; i >= start; i--)
340 		SETBIT(buf, j, 'x');
341 
342 	for (; i >= 0; i--)
343 		SETBIT(buf, j, '.');
344 
345 	return (buf);
346 }
347 
348 ulong_t
349 mdb_inc_indent(ulong_t i)
350 {
351 	if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT) {
352 		ulong_t margin = mdb_iob_getmargin(mdb.m_out);
353 		mdb_iob_margin(mdb.m_out, margin + i);
354 		return (margin);
355 	}
356 
357 	mdb_iob_margin(mdb.m_out, i);
358 	mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
359 	return (0);
360 }
361 
362 ulong_t
363 mdb_dec_indent(ulong_t i)
364 {
365 	if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT) {
366 		ulong_t margin = mdb_iob_getmargin(mdb.m_out);
367 
368 		if (margin < i || margin - i == 0) {
369 			mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
370 			mdb_iob_margin(mdb.m_out, MDB_IOB_DEFMARGIN);
371 		} else
372 			mdb_iob_margin(mdb.m_out, margin - i);
373 
374 		return (margin);
375 	}
376 
377 	return (0);
378 }
379 
380 int
381 mdb_eval(const char *s)
382 {
383 	mdb_frame_t *ofp = mdb.m_fmark;
384 	mdb_frame_t *fp = mdb.m_frame;
385 	int err;
386 
387 	if (s == NULL)
388 		return (set_errno(EINVAL));
389 
390 	/*
391 	 * Push m_in down onto the input stack, then set m_in to point to the
392 	 * i/o buffer for our command string, and reset the frame marker.
393 	 * The mdb_run() function returns when the new m_in iob reaches EOF.
394 	 */
395 	mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
396 	mdb.m_in = mdb_iob_create(mdb_strio_create(s), MDB_IOB_RDONLY);
397 
398 	mdb.m_fmark = NULL;
399 	err = mdb_run();
400 	mdb.m_fmark = ofp;
401 
402 	/*
403 	 * Now pop the old standard input stream and restore mdb.m_in and
404 	 * the parser's saved current line number.
405 	 */
406 	mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
407 	yylineno = mdb_iob_lineno(mdb.m_in);
408 
409 	/*
410 	 * If mdb_run() returned an error, propagate this backward
411 	 * up the stack of debugger environment frames.
412 	 */
413 	if (MDB_ERR_IS_FATAL(err))
414 		longjmp(fp->f_pcb, err);
415 
416 	if (err == MDB_ERR_PAGER || err == MDB_ERR_SIGINT)
417 		return (set_errno(EMDB_CANCEL));
418 
419 	if (err != 0)
420 		return (set_errno(EMDB_EVAL));
421 
422 	return (0);
423 }
424 
425 void
426 mdb_set_dot(uintmax_t addr)
427 {
428 	mdb_nv_set_value(mdb.m_dot, addr);
429 	mdb.m_incr = 0;
430 }
431 
432 uintmax_t
433 mdb_get_dot(void)
434 {
435 	return (mdb_nv_get_value(mdb.m_dot));
436 }
437 
438 static int
439 walk_step(mdb_wcb_t *wcb)
440 {
441 	mdb_wcb_t *nwcb = wcb->w_lyr_head;
442 	int status;
443 
444 	/*
445 	 * If the control block has no layers, we just invoke the walker's
446 	 * step function and return status indicating whether to continue
447 	 * or stop.  If the control block has layers, we need to invoke
448 	 * ourself recursively for the next layer, until eventually we
449 	 * percolate down to an unlayered walk.
450 	 */
451 	if (nwcb == NULL)
452 		return (wcb->w_walker->iwlk_step(&wcb->w_state));
453 
454 	if ((status = walk_step(nwcb)) != WALK_NEXT) {
455 		wcb->w_lyr_head = nwcb->w_lyr_link;
456 		nwcb->w_lyr_link = NULL;
457 		mdb_wcb_destroy(nwcb);
458 	}
459 
460 	if (status == WALK_DONE && wcb->w_lyr_head != NULL)
461 		return (WALK_NEXT);
462 
463 	return (status);
464 }
465 
466 static int
467 walk_common(mdb_wcb_t *wcb)
468 {
469 	int status, rval = 0;
470 	mdb_frame_t *pfp;
471 
472 	/*
473 	 * Enter the control block in the active list so that mdb can clean
474 	 * up after it in case we abort out of the current command.
475 	 */
476 	if ((pfp = mdb_list_prev(mdb.m_frame)) != NULL && pfp->f_pcmd != NULL)
477 		mdb_wcb_insert(wcb, pfp);
478 	else
479 		mdb_wcb_insert(wcb, mdb.m_frame);
480 
481 	/*
482 	 * The per-walk constructor performs private buffer initialization
483 	 * and locates whatever symbols are necessary.
484 	 */
485 	if ((status = wcb->w_walker->iwlk_init(&wcb->w_state)) != WALK_NEXT) {
486 		if (status != WALK_DONE)
487 			rval = set_errno(EMDB_WALKINIT);
488 		goto done;
489 	}
490 
491 	/*
492 	 * Mark wcb to indicate that walk_init has been called (which means
493 	 * we can call walk_fini if the walk is aborted at this point).
494 	 */
495 	wcb->w_inited = TRUE;
496 
497 	while (walk_step(wcb) == WALK_NEXT)
498 		continue;
499 done:
500 	if ((pfp = mdb_list_prev(mdb.m_frame)) != NULL && pfp->f_pcmd != NULL)
501 		mdb_wcb_delete(wcb, pfp);
502 	else
503 		mdb_wcb_delete(wcb, mdb.m_frame);
504 
505 	mdb_wcb_destroy(wcb);
506 	return (rval);
507 }
508 
509 int
510 mdb_pwalk(const char *name, mdb_walk_cb_t func, void *data, uintptr_t addr)
511 {
512 	mdb_iwalker_t *iwp = mdb_walker_lookup(name);
513 
514 	if (func == NULL)
515 		return (set_errno(EINVAL));
516 
517 	if (iwp != NULL)
518 		return (walk_common(mdb_wcb_create(iwp, func, data, addr)));
519 
520 	return (-1); /* errno is set for us */
521 }
522 
523 int
524 mdb_walk(const char *name, mdb_walk_cb_t func, void *data)
525 {
526 	return (mdb_pwalk(name, func, data, NULL));
527 }
528 
529 /*ARGSUSED*/
530 static int
531 walk_dcmd(uintptr_t addr, const void *ignored, dcmd_walk_arg_t *dwp)
532 {
533 	int status = mdb_call_idcmd(dwp->dw_dcmd, addr, 1, dwp->dw_flags,
534 	    &dwp->dw_argv, NULL, NULL);
535 
536 	if (status == DCMD_USAGE || status == DCMD_ABORT)
537 		return (WALK_ERR);
538 
539 	dwp->dw_flags &= ~DCMD_LOOPFIRST;
540 	return (WALK_NEXT);
541 }
542 
543 int
544 mdb_pwalk_dcmd(const char *wname, const char *dcname,
545     int argc, const mdb_arg_t *argv, uintptr_t addr)
546 {
547 	mdb_argvec_t args;
548 	dcmd_walk_arg_t dw;
549 	mdb_iwalker_t *iwp;
550 	mdb_wcb_t *wcb;
551 	int status;
552 
553 	if (wname == NULL || dcname == NULL)
554 		return (set_errno(EINVAL));
555 
556 	if ((dw.dw_dcmd = mdb_dcmd_lookup(dcname)) == NULL)
557 		return (-1); /* errno is set for us */
558 
559 	if ((iwp = mdb_walker_lookup(wname)) == NULL)
560 		return (-1); /* errno is set for us */
561 
562 	args.a_data = (mdb_arg_t *)argv;
563 	args.a_nelems = args.a_size = argc;
564 
565 	mdb_argvec_create(&dw.dw_argv);
566 	mdb_argvec_copy(&dw.dw_argv, &args);
567 	dw.dw_flags = DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC;
568 
569 	wcb = mdb_wcb_create(iwp, (mdb_walk_cb_t)walk_dcmd, &dw, addr);
570 	status = walk_common(wcb);
571 
572 	mdb_argvec_zero(&dw.dw_argv);
573 	mdb_argvec_destroy(&dw.dw_argv);
574 
575 	return (status);
576 }
577 
578 int
579 mdb_walk_dcmd(const char *wname, const char *dcname,
580     int argc, const mdb_arg_t *argv)
581 {
582 	return (mdb_pwalk_dcmd(wname, dcname, argc, argv, NULL));
583 }
584 
585 /*ARGSUSED*/
586 static int
587 layered_walk_step(uintptr_t addr, const void *data, mdb_wcb_t *wcb)
588 {
589 	/*
590 	 * Prior to calling the top-level walker's step function, reset its
591 	 * mdb_walk_state_t walk_addr and walk_layer members to refer to the
592 	 * target virtual address and data buffer of the underlying object.
593 	 */
594 	wcb->w_state.walk_addr = addr;
595 	wcb->w_state.walk_layer = data;
596 
597 	return (wcb->w_walker->iwlk_step(&wcb->w_state));
598 }
599 
600 int
601 mdb_layered_walk(const char *wname, mdb_walk_state_t *wsp)
602 {
603 	mdb_wcb_t *cwcb, *wcb;
604 	mdb_iwalker_t *iwp;
605 
606 	if (wname == NULL || wsp == NULL)
607 		return (set_errno(EINVAL));
608 
609 	if ((iwp = mdb_walker_lookup(wname)) == NULL)
610 		return (-1); /* errno is set for us */
611 
612 	if ((cwcb = mdb_wcb_from_state(wsp)) == NULL)
613 		return (set_errno(EMDB_BADWCB));
614 
615 	if (cwcb->w_walker == iwp)
616 		return (set_errno(EMDB_WALKLOOP));
617 
618 	wcb = mdb_wcb_create(iwp, (mdb_walk_cb_t)layered_walk_step,
619 	    cwcb, wsp->walk_addr);
620 
621 	if (iwp->iwlk_init(&wcb->w_state) != WALK_NEXT) {
622 		mdb_wcb_destroy(wcb);
623 		return (set_errno(EMDB_WALKINIT));
624 	}
625 
626 	wcb->w_inited = TRUE;
627 
628 	mdb_dprintf(MDB_DBG_WALK, "added %s`%s as %s`%s layer\n",
629 	    iwp->iwlk_modp->mod_name, iwp->iwlk_name,
630 	    cwcb->w_walker->iwlk_modp->mod_name, cwcb->w_walker->iwlk_name);
631 
632 	if (cwcb->w_lyr_head != NULL) {
633 		for (cwcb = cwcb->w_lyr_head; cwcb->w_lyr_link != NULL; )
634 			cwcb = cwcb->w_lyr_link;
635 		cwcb->w_lyr_link = wcb;
636 	} else
637 		cwcb->w_lyr_head = wcb;
638 
639 	return (0);
640 }
641 
642 int
643 mdb_call_dcmd(const char *name, uintptr_t dot, uint_t flags,
644     int argc, const mdb_arg_t *argv)
645 {
646 	mdb_idcmd_t *idcp;
647 	mdb_argvec_t args;
648 	int status;
649 
650 	if (name == NULL || argc < 0)
651 		return (set_errno(EINVAL));
652 
653 	if ((idcp = mdb_dcmd_lookup(name)) == NULL)
654 		return (-1); /* errno is set for us */
655 
656 	args.a_data = (mdb_arg_t *)argv;
657 	args.a_nelems = args.a_size = argc;
658 	status = mdb_call_idcmd(idcp, dot, 1, flags, &args, NULL, NULL);
659 
660 	if (status == DCMD_ERR || status == DCMD_ABORT)
661 		return (set_errno(EMDB_DCFAIL));
662 
663 	if (status == DCMD_USAGE)
664 		return (set_errno(EMDB_DCUSAGE));
665 
666 	return (0);
667 }
668 
669 int
670 mdb_add_walker(const mdb_walker_t *wp)
671 {
672 	mdb_module_t *mp;
673 
674 	if (mdb.m_lmod == NULL) {
675 		mdb_cmd_t *cp = mdb.m_frame->f_cp;
676 		mp = cp->c_dcmd->idc_modp;
677 	} else
678 		mp = mdb.m_lmod;
679 
680 	return (mdb_module_add_walker(mp, wp, 0));
681 }
682 
683 int
684 mdb_remove_walker(const char *name)
685 {
686 	mdb_module_t *mp;
687 
688 	if (mdb.m_lmod == NULL) {
689 		mdb_cmd_t *cp = mdb.m_frame->f_cp;
690 		mp = cp->c_dcmd->idc_modp;
691 	} else
692 		mp = mdb.m_lmod;
693 
694 	return (mdb_module_remove_walker(mp, name));
695 }
696 
697 void
698 mdb_get_pipe(mdb_pipe_t *p)
699 {
700 	mdb_cmd_t *cp = mdb.m_frame->f_cp;
701 	mdb_addrvec_t *adp = &cp->c_addrv;
702 
703 	if (p == NULL) {
704 		warn("dcmd failure: mdb_get_pipe invoked with NULL pointer\n");
705 		longjmp(mdb.m_frame->f_pcb, MDB_ERR_API);
706 	}
707 
708 	if (adp->ad_nelems != 0) {
709 		ASSERT(adp->ad_ndx != 0);
710 		p->pipe_data = &adp->ad_data[adp->ad_ndx - 1];
711 		p->pipe_len = adp->ad_nelems - adp->ad_ndx + 1;
712 		adp->ad_ndx = adp->ad_nelems;
713 	} else {
714 		p->pipe_data = NULL;
715 		p->pipe_len = 0;
716 	}
717 }
718 
719 void
720 mdb_set_pipe(const mdb_pipe_t *p)
721 {
722 	mdb_cmd_t *cp = mdb.m_frame->f_pcmd;
723 
724 	if (p == NULL) {
725 		warn("dcmd failure: mdb_set_pipe invoked with NULL pointer\n");
726 		longjmp(mdb.m_frame->f_pcb, MDB_ERR_API);
727 	}
728 
729 	if (cp != NULL) {
730 		size_t nbytes = sizeof (uintptr_t) * p->pipe_len;
731 
732 		mdb_cmd_reset(cp);
733 		cp->c_addrv.ad_data = mdb_alloc(nbytes, UM_SLEEP);
734 		bcopy(p->pipe_data, cp->c_addrv.ad_data, nbytes);
735 		cp->c_addrv.ad_nelems = p->pipe_len;
736 		cp->c_addrv.ad_size = p->pipe_len;
737 	}
738 }
739 
740 ssize_t
741 mdb_get_xdata(const char *name, void *buf, size_t nbytes)
742 {
743 	return (mdb_tgt_getxdata(mdb.m_target, name, buf, nbytes));
744 }
745 
746 /*
747  * Private structure and function for implementing mdb_dumpptr on top
748  * of mdb_dump_internal
749  */
750 typedef struct dptrdat {
751 	mdb_dumpptr_cb_t func;
752 	void *arg;
753 } dptrdat_t;
754 
755 static ssize_t
756 mdb_dump_aux_ptr(void *buf, size_t nbyte, uint64_t offset, void *arg)
757 {
758 	dptrdat_t *dat = arg;
759 
760 	return (dat->func(buf, nbyte, offset, dat->arg));
761 }
762 
763 /*
764  * Private structure and function for handling callbacks which return
765  * EMDB_PARTIAL
766  */
767 typedef struct d64dat {
768 	mdb_dump64_cb_t func;
769 	void *arg;
770 } d64dat_t;
771 
772 static ssize_t
773 mdb_dump_aux_partial(void *buf, size_t nbyte, uint64_t offset, void *arg)
774 {
775 	d64dat_t *dat = arg;
776 	int result;
777 	int count;
778 
779 	result = dat->func(buf, nbyte, offset, dat->arg);
780 	if (result == -1 && errno == EMDB_PARTIAL) {
781 		count = 0;
782 		do {
783 			result = dat->func((char *)buf + count, 1,
784 			    offset + count, dat->arg);
785 			if (result == 1)
786 				count++;
787 		} while (count < nbyte && result == 1);
788 		if (count)
789 			result = count;
790 	}
791 
792 	return (result);
793 }
794 
795 int
796 mdb_dumpptr(uintptr_t addr, size_t len, uint_t flags, mdb_dumpptr_cb_t fp,
797     void *arg)
798 {
799 	dptrdat_t dat;
800 	d64dat_t dat64;
801 
802 	dat.func = fp;
803 	dat.arg = arg;
804 	dat64.func = mdb_dump_aux_ptr;
805 	dat64.arg = &dat;
806 	return (mdb_dump_internal(addr, len, flags, mdb_dump_aux_partial,
807 	    &dat64, sizeof (uintptr_t)));
808 }
809 
810 int
811 mdb_dump64(uint64_t addr, uint64_t len, uint_t flags, mdb_dump64_cb_t fp,
812     void *arg)
813 {
814 	d64dat_t dat64;
815 
816 	dat64.func = fp;
817 	dat64.arg = arg;
818 	return (mdb_dump_internal(addr, len, flags, mdb_dump_aux_partial,
819 	    &dat64, sizeof (uint64_t)));
820 }
821 
822 int
823 mdb_get_state(void)
824 {
825 	mdb_tgt_status_t ts;
826 
827 	(void) mdb_tgt_status(mdb.m_target, &ts);
828 
829 	return (ts.st_state);
830 }
831 
832 void *
833 mdb_callback_add(int class, mdb_callback_f fp, void *arg)
834 {
835 	mdb_module_t *m;
836 
837 	if (class != MDB_CALLBACK_STCHG && class != MDB_CALLBACK_PROMPT) {
838 		(void) set_errno(EINVAL);
839 		return (NULL);
840 	}
841 
842 	if (mdb.m_lmod != NULL)
843 		m = mdb.m_lmod;
844 	else
845 		m = mdb.m_frame->f_cp->c_dcmd->idc_modp;
846 
847 	return (mdb_callb_add(m, class, fp, arg));
848 }
849 
850 void
851 mdb_callback_remove(void *hdl)
852 {
853 	mdb_callb_remove(hdl);
854 }
855