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