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