/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013 by Delphix. All rights reserved. * Copyright 2016 Nexenta Systems, Inc. All rights reserved. * Copyright 2019 Joyent, Inc. * Copyright 2024 Oxide Computer Company * Copyright 2023 RackTop Systems, Inc. * Copyright 2023 OmniOS Community Edition (OmniOSce) Association. */ #include #include #include #include #include #include #include #include #include #include #include #include /* * Private callback structure for implementing mdb_walk_dcmd, below. */ typedef struct { mdb_idcmd_t *dw_dcmd; mdb_argvec_t dw_argv; uint_t dw_flags; } dcmd_walk_arg_t; /* * Global properties which modules are allowed to look at. These are * re-initialized by the target activation callbacks. */ int mdb_prop_postmortem = FALSE; /* Are we examining a dump? */ int mdb_prop_kernel = FALSE; /* Are we examining a kernel? */ int mdb_prop_datamodel = 0; /* Data model (see mdb_target_impl.h) */ static int call_idcmd(mdb_idcmd_t *idcp, uintmax_t addr, uintmax_t count, uint_t flags, mdb_argvec_t *argv); int mdb_snprintfrac(char *buf, int len, uint64_t numerator, uint64_t denom, int frac_digits) { int mul = 1; int whole, frac, i; for (i = frac_digits; i; i--) mul *= 10; whole = numerator / denom; frac = mul * numerator / denom - mul * whole; return (mdb_snprintf(buf, len, "%u.%0*u", whole, frac_digits, frac)); } void mdb_nicenum(uint64_t num, char *buf) { uint64_t n = num; int index = 0; char *u; while (n >= 1024) { n = (n + (1024 / 2)) / 1024; /* Round up or down */ index++; } u = &" \0K\0M\0G\0T\0P\0E\0"[index*2]; if (index == 0) { (void) mdb_snprintf(buf, MDB_NICENUM_BUFLEN, "%llu", (u_longlong_t)n); } else if (n < 10 && (num & (num - 1)) != 0) { (void) mdb_snprintfrac(buf, MDB_NICENUM_BUFLEN, num, 1ULL << 10 * index, 2); (void) strcat(buf, u); } else if (n < 100 && (num & (num - 1)) != 0) { (void) mdb_snprintfrac(buf, MDB_NICENUM_BUFLEN, num, 1ULL << 10 * index, 1); (void) strcat(buf, u); } else { (void) mdb_snprintf(buf, MDB_NICENUM_BUFLEN, "%llu%s", (u_longlong_t)n, u); } } void mdb_nicetime(int64_t delta, char *buf, size_t buflen) { const char *sign = (delta < 0) ? "-" : "+"; char daybuf[32] = { 0 }; char fracbuf[32] = { 0 }; if (delta < 0) delta = -delta; if (delta == 0) { (void) mdb_snprintf(buf, buflen, "0ns"); return; } /* Handle values < 1s */ if (delta < NANOSEC) { static const char f_units[] = "num"; uint_t idx = 0; while (delta >= 1000) { delta /= 1000; idx++; } (void) mdb_snprintf(buf, buflen, "t%s%lld%cs", sign, delta, f_units[idx]); return; } uint64_t days, hours, mins, secs, frac; frac = delta % NANOSEC; delta /= NANOSEC; secs = delta % 60; delta /= 60; mins = delta % 60; delta /= 60; hours = delta % 24; delta /= 24; days = delta; if (days > 0) (void) mdb_snprintf(daybuf, sizeof (daybuf), "%llud ", days); if (frac > 0) (void) mdb_snprintf(fracbuf, sizeof (fracbuf), ".%llu", frac); (void) mdb_snprintf(buf, buflen, "t%s%s%02llu:%02llu:%02llu%s", sign, daybuf, hours, mins, secs, fracbuf); } ssize_t mdb_vread(void *buf, size_t nbytes, uintptr_t addr) { ssize_t rbytes = mdb_tgt_vread(mdb.m_target, buf, nbytes, addr); if (rbytes > 0 && rbytes < nbytes) return (set_errbytes(rbytes, nbytes)); return (rbytes); } ssize_t mdb_vwrite(const void *buf, size_t nbytes, uintptr_t addr) { return (mdb_tgt_vwrite(mdb.m_target, buf, nbytes, addr)); } ssize_t mdb_aread(void *buf, size_t nbytes, uintptr_t addr, void *as) { ssize_t rbytes = mdb_tgt_aread(mdb.m_target, as, buf, nbytes, addr); if (rbytes > 0 && rbytes < nbytes) return (set_errbytes(rbytes, nbytes)); return (rbytes); } ssize_t mdb_awrite(const void *buf, size_t nbytes, uintptr_t addr, void *as) { return (mdb_tgt_awrite(mdb.m_target, as, buf, nbytes, addr)); } ssize_t mdb_fread(void *buf, size_t nbytes, uintptr_t addr) { ssize_t rbytes = mdb_tgt_fread(mdb.m_target, buf, nbytes, addr); if (rbytes > 0 && rbytes < nbytes) return (set_errbytes(rbytes, nbytes)); return (rbytes); } ssize_t mdb_fwrite(const void *buf, size_t nbytes, uintptr_t addr) { return (mdb_tgt_fwrite(mdb.m_target, buf, nbytes, addr)); } ssize_t mdb_pread(void *buf, size_t nbytes, physaddr_t addr) { ssize_t rbytes = mdb_tgt_pread(mdb.m_target, buf, nbytes, addr); if (rbytes > 0 && rbytes < nbytes) return (set_errbytes(rbytes, nbytes)); return (rbytes); } ssize_t mdb_pwrite(const void *buf, size_t nbytes, physaddr_t addr) { return (mdb_tgt_pwrite(mdb.m_target, buf, nbytes, addr)); } ssize_t mdb_readstr(char *buf, size_t nbytes, uintptr_t addr) { return (mdb_tgt_readstr(mdb.m_target, MDB_TGT_AS_VIRT, buf, nbytes, addr)); } ssize_t mdb_writestr(const char *buf, uintptr_t addr) { return (mdb_tgt_writestr(mdb.m_target, MDB_TGT_AS_VIRT, buf, addr)); } ssize_t mdb_readsym(void *buf, size_t nbytes, const char *name) { ssize_t rbytes = mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT, buf, nbytes, MDB_TGT_OBJ_EVERY, name); if (rbytes > 0 && rbytes < nbytes) return (set_errbytes(rbytes, nbytes)); return (rbytes); } ssize_t mdb_writesym(const void *buf, size_t nbytes, const char *name) { return (mdb_tgt_writesym(mdb.m_target, MDB_TGT_AS_VIRT, buf, nbytes, MDB_TGT_OBJ_EVERY, name)); } ssize_t mdb_readvar(void *buf, const char *name) { GElf_Sym sym; if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EVERY, name, &sym, NULL)) return (-1); if (mdb_tgt_vread(mdb.m_target, buf, sym.st_size, (uintptr_t)sym.st_value) == sym.st_size) return ((ssize_t)sym.st_size); return (-1); } ssize_t mdb_writevar(const void *buf, const char *name) { GElf_Sym sym; if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EVERY, name, &sym, NULL)) return (-1); if (mdb_tgt_vwrite(mdb.m_target, buf, sym.st_size, (uintptr_t)sym.st_value) == sym.st_size) return ((ssize_t)sym.st_size); return (-1); } int mdb_lookup_by_name(const char *name, GElf_Sym *sym) { return (mdb_lookup_by_obj(MDB_TGT_OBJ_EVERY, name, sym)); } int mdb_lookup_by_obj(const char *obj, const char *name, GElf_Sym *sym) { return (mdb_tgt_lookup_by_name(mdb.m_target, obj, name, sym, NULL)); } int mdb_lookup_by_addr(uintptr_t addr, uint_t flags, char *buf, size_t nbytes, GElf_Sym *sym) { return (mdb_tgt_lookup_by_addr(mdb.m_target, addr, flags, buf, nbytes, sym, NULL)); } int mdb_getareg(mdb_tid_t tid, const char *rname, mdb_reg_t *rp) { return (mdb_tgt_getareg(mdb.m_target, tid, rname, rp)); } int mdb_thread_name(mdb_tid_t tid, char *buf, size_t bufsize) { return (mdb_tgt_thread_name(mdb.m_target, tid, buf, bufsize)); } static u_longlong_t mdb_strtoull_int(const char *s, int radix) { if (s[0] == '0') { switch (s[1]) { case 'I': case 'i': radix = 2; s += 2; break; case 'O': case 'o': radix = 8; s += 2; break; case 'T': case 't': radix = 10; s += 2; break; case 'X': case 'x': radix = 16; s += 2; break; } } return (mdb_strtonum(s, radix)); } u_longlong_t mdb_strtoullx(const char *s, mdb_strtoull_flags_t flags) { int radix; if ((flags & ~MDB_STRTOULL_F_BASE_C) != 0) { mdb_warn("invalid options specified: 0x%lx" PRIx64 "\n", (uint64_t)flags); return ((uintmax_t)ULLONG_MAX); } if ((flags & MDB_STRTOULL_F_BASE_C) != 0) { radix = 10; } else { radix = mdb.m_radix; } return (mdb_strtoull_int(s, radix)); } u_longlong_t mdb_strtoull(const char *s) { return (mdb_strtoull_int(s, mdb.m_radix)); } size_t mdb_snprintf(char *buf, size_t nbytes, const char *format, ...) { va_list alist; va_start(alist, format); nbytes = mdb_iob_vsnprintf(buf, nbytes, format, alist); va_end(alist); return (nbytes); } void mdb_printf(const char *format, ...) { va_list alist; va_start(alist, format); mdb_iob_vprintf(mdb.m_out, format, alist); va_end(alist); } void mdb_warn(const char *format, ...) { va_list alist; va_start(alist, format); vwarn(format, alist); va_end(alist); } void mdb_flush(void) { mdb_iob_flush(mdb.m_out); } /* * Convert an object of len bytes pointed to by srcraw between * network-order and host-order and store in dstraw. The length len must * be the actual length of the objects pointed to by srcraw and dstraw (or * zero) or the results are undefined. srcraw and dstraw may be the same, * in which case the object is converted in-place. Note that this routine * will convert from host-order to network-order or network-order to * host-order, since the conversion is the same in either case. */ /* ARGSUSED */ void mdb_nhconvert(void *dstraw, const void *srcraw, size_t len) { #ifdef _LITTLE_ENDIAN uint8_t b1, b2; uint8_t *dst, *src; size_t i; dst = (uint8_t *)dstraw; src = (uint8_t *)srcraw; for (i = 0; i < len / 2; i++) { b1 = src[i]; b2 = src[len - i - 1]; dst[i] = b2; dst[len - i - 1] = b1; } #else if (dstraw != srcraw) bcopy(srcraw, dstraw, len); #endif } /* * Bit formatting functions: Note the interesting use of UM_GC here to * allocate a buffer for the caller which will be automatically freed * when the dcmd completes or is forcibly aborted. */ #define NBNB (NBBY / 2) /* number of bits per nibble */ #define SETBIT(buf, j, c) { \ if (((j) + 1) % (NBNB + 1) == 0) \ (buf)[(j)++] = ' '; \ (buf)[(j)++] = (c); \ } const char * mdb_one_bit(int width, int bit, int on) { int i, j = 0; char *buf; buf = mdb_zalloc(width + (width / NBNB) + 2, UM_GC | UM_SLEEP); for (i = --width; i > bit; i--) SETBIT(buf, j, '.'); SETBIT(buf, j, on ? '1' : '0'); for (i = bit - 1; i >= 0; i--) SETBIT(buf, j, '.'); return (buf); } const char * mdb_inval_bits(int width, int start, int stop) { int i, j = 0; char *buf; buf = mdb_zalloc(width + (width / NBNB) + 2, UM_GC | UM_SLEEP); for (i = --width; i > stop; i--) SETBIT(buf, j, '.'); for (i = stop; i >= start; i--) SETBIT(buf, j, 'x'); for (; i >= 0; i--) SETBIT(buf, j, '.'); return (buf); } ulong_t mdb_inc_indent(ulong_t i) { if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT) { ulong_t margin = mdb_iob_getmargin(mdb.m_out); mdb_iob_margin(mdb.m_out, margin + i); return (margin); } mdb_iob_margin(mdb.m_out, i); mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT); return (0); } ulong_t mdb_dec_indent(ulong_t i) { if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT) { ulong_t margin = mdb_iob_getmargin(mdb.m_out); if (margin < i || margin - i == 0) { mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT); mdb_iob_margin(mdb.m_out, MDB_IOB_DEFMARGIN); } else mdb_iob_margin(mdb.m_out, margin - i); return (margin); } return (0); } int mdb_eval(const char *s) { mdb_frame_t *ofp = mdb.m_fmark; mdb_frame_t *fp = mdb.m_frame; int err; if (s == NULL) return (set_errno(EINVAL)); /* * Push m_in down onto the input stack, then set m_in to point to the * i/o buffer for our command string, and reset the frame marker. * The mdb_run() function returns when the new m_in iob reaches EOF. */ mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno); mdb.m_in = mdb_iob_create(mdb_strio_create(s), MDB_IOB_RDONLY); mdb.m_fmark = NULL; err = mdb_run(); mdb.m_fmark = ofp; /* * Now pop the old standard input stream and restore mdb.m_in and * the parser's saved current line number. */ mdb.m_in = mdb_iob_stack_pop(&fp->f_istk); yylineno = mdb_iob_lineno(mdb.m_in); /* * If mdb_run() returned an error, propagate this backward * up the stack of debugger environment frames. */ if (MDB_ERR_IS_FATAL(err)) longjmp(fp->f_pcb, err); if (err == MDB_ERR_PAGER || err == MDB_ERR_SIGINT) return (set_errno(EMDB_CANCEL)); if (err != 0) return (set_errno(EMDB_EVAL)); return (0); } void mdb_set_dot(uintmax_t addr) { mdb_nv_set_value(mdb.m_dot, addr); mdb.m_incr = 0; } uintmax_t mdb_get_dot(void) { return (mdb_nv_get_value(mdb.m_dot)); } static int walk_step(mdb_wcb_t *wcb) { mdb_wcb_t *nwcb = wcb->w_lyr_head; int status; /* * If the control block has no layers, we just invoke the walker's * step function and return status indicating whether to continue * or stop. If the control block has layers, we need to invoke * ourself recursively for the next layer, until eventually we * percolate down to an unlayered walk. */ if (nwcb == NULL) return (wcb->w_walker->iwlk_step(&wcb->w_state)); if ((status = walk_step(nwcb)) != WALK_NEXT) { wcb->w_lyr_head = nwcb->w_lyr_link; nwcb->w_lyr_link = NULL; mdb_wcb_destroy(nwcb); } if (status == WALK_DONE && wcb->w_lyr_head != NULL) return (WALK_NEXT); return (status); } static int walk_common(mdb_wcb_t *wcb) { int status, rval = 0; mdb_frame_t *pfp; /* * Enter the control block in the active list so that mdb can clean * up after it in case we abort out of the current command. */ if ((pfp = mdb_list_prev(mdb.m_frame)) != NULL && pfp->f_pcmd != NULL) mdb_wcb_insert(wcb, pfp); else mdb_wcb_insert(wcb, mdb.m_frame); /* * The per-walk constructor performs private buffer initialization * and locates whatever symbols are necessary. */ if ((status = wcb->w_walker->iwlk_init(&wcb->w_state)) != WALK_NEXT) { if (status != WALK_DONE) rval = set_errno(EMDB_WALKINIT); goto done; } /* * Mark wcb to indicate that walk_init has been called (which means * we can call walk_fini if the walk is aborted at this point). */ wcb->w_inited = TRUE; while (walk_step(wcb) == WALK_NEXT) continue; done: if ((pfp = mdb_list_prev(mdb.m_frame)) != NULL && pfp->f_pcmd != NULL) mdb_wcb_delete(wcb, pfp); else mdb_wcb_delete(wcb, mdb.m_frame); mdb_wcb_destroy(wcb); return (rval); } typedef struct pwalk_step { mdb_walk_cb_t ps_cb; void *ps_private; } pwalk_step_t; static int pwalk_step(uintptr_t addr, const void *data, void *private) { pwalk_step_t *psp = private; int ret; mdb.m_frame->f_cbactive = B_TRUE; ret = psp->ps_cb(addr, data, psp->ps_private); mdb.m_frame->f_cbactive = B_FALSE; return (ret); } int mdb_pwalk(const char *name, mdb_walk_cb_t func, void *private, uintptr_t addr) { mdb_iwalker_t *iwp = mdb_walker_lookup(name); pwalk_step_t p; if (func == NULL) return (set_errno(EINVAL)); p.ps_cb = func; p.ps_private = private; if (iwp != NULL) { int ret; int cbactive = mdb.m_frame->f_cbactive; mdb.m_frame->f_cbactive = B_FALSE; ret = walk_common(mdb_wcb_create(iwp, pwalk_step, &p, addr)); mdb.m_frame->f_cbactive = cbactive; return (ret); } return (-1); /* errno is set for us */ } int mdb_walk(const char *name, mdb_walk_cb_t func, void *data) { return (mdb_pwalk(name, func, data, 0)); } /*ARGSUSED*/ static int walk_dcmd(uintptr_t addr, const void *ignored, dcmd_walk_arg_t *dwp) { int status; mdb.m_frame->f_cbactive = B_TRUE; status = call_idcmd(dwp->dw_dcmd, addr, 1, dwp->dw_flags, &dwp->dw_argv); mdb.m_frame->f_cbactive = B_FALSE; if (status == DCMD_USAGE || status == DCMD_ABORT) return (WALK_ERR); dwp->dw_flags &= ~DCMD_LOOPFIRST; return (WALK_NEXT); } 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) { mdb_argvec_t args; dcmd_walk_arg_t dw; mdb_iwalker_t *iwp; mdb_wcb_t *wcb; int status; if (wname == NULL || dcname == NULL) return (set_errno(EINVAL)); if ((dw.dw_dcmd = mdb_dcmd_lookup(dcname)) == NULL) return (-1); /* errno is set for us */ if ((iwp = mdb_walker_lookup(wname)) == NULL) return (-1); /* errno is set for us */ args.a_data = (mdb_arg_t *)argv; args.a_nelems = args.a_size = argc; mdb_argvec_create(&dw.dw_argv); mdb_argvec_copy(&dw.dw_argv, &args); dw.dw_flags = flags | DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC; wcb = mdb_wcb_create(iwp, (mdb_walk_cb_t)walk_dcmd, &dw, addr); status = walk_common(wcb); mdb_argvec_zero(&dw.dw_argv); mdb_argvec_destroy(&dw.dw_argv); return (status); } int mdb_pwalk_dcmd(const char *wname, const char *dcname, int argc, const mdb_arg_t *argv, uintptr_t addr) { return (i_mdb_pwalk_dcmd(wname, dcname, argc, argv, addr, 0)); } int mdb_fpwalk_dcmd(const char *wname, const char *dcname, int argc, const mdb_arg_t *argv, uintptr_t addr, uint_t flags) { return (i_mdb_pwalk_dcmd(wname, dcname, argc, argv, addr, flags)); } int mdb_walk_dcmd(const char *wname, const char *dcname, int argc, const mdb_arg_t *argv) { return (i_mdb_pwalk_dcmd(wname, dcname, argc, argv, 0, 0)); } /*ARGSUSED*/ static int layered_walk_step(uintptr_t addr, const void *data, mdb_wcb_t *wcb) { /* * Prior to calling the top-level walker's step function, reset its * mdb_walk_state_t walk_addr and walk_layer members to refer to the * target virtual address and data buffer of the underlying object. */ wcb->w_state.walk_addr = addr; wcb->w_state.walk_layer = data; return (wcb->w_walker->iwlk_step(&wcb->w_state)); } int mdb_layered_walk(const char *wname, mdb_walk_state_t *wsp) { mdb_wcb_t *cwcb, *wcb; mdb_iwalker_t *iwp; if (wname == NULL || wsp == NULL) return (set_errno(EINVAL)); if ((iwp = mdb_walker_lookup(wname)) == NULL) return (-1); /* errno is set for us */ if ((cwcb = mdb_wcb_from_state(wsp)) == NULL) return (set_errno(EMDB_BADWCB)); if (cwcb->w_walker == iwp) return (set_errno(EMDB_WALKLOOP)); wcb = mdb_wcb_create(iwp, (mdb_walk_cb_t)layered_walk_step, cwcb, wsp->walk_addr); if (iwp->iwlk_init(&wcb->w_state) != WALK_NEXT) { mdb_wcb_destroy(wcb); return (set_errno(EMDB_WALKINIT)); } wcb->w_inited = TRUE; mdb_dprintf(MDB_DBG_WALK, "added %s`%s as %s`%s layer\n", iwp->iwlk_modp->mod_name, iwp->iwlk_name, cwcb->w_walker->iwlk_modp->mod_name, cwcb->w_walker->iwlk_name); if (cwcb->w_lyr_head != NULL) { for (cwcb = cwcb->w_lyr_head; cwcb->w_lyr_link != NULL; ) cwcb = cwcb->w_lyr_link; cwcb->w_lyr_link = wcb; } else cwcb->w_lyr_head = wcb; return (0); } int mdb_call_dcmd(const char *name, uintptr_t dot, uint_t flags, int argc, const mdb_arg_t *argv) { mdb_idcmd_t *idcp; mdb_argvec_t args; int status; if (name == NULL || argc < 0) return (set_errno(EINVAL)); if ((idcp = mdb_dcmd_lookup(name)) == NULL) return (-1); /* errno is set for us */ args.a_data = (mdb_arg_t *)argv; args.a_nelems = args.a_size = argc; status = call_idcmd(idcp, dot, 1, flags, &args); if (status == DCMD_ERR || status == DCMD_ABORT) return (set_errno(EMDB_DCFAIL)); if (status == DCMD_USAGE) return (set_errno(EMDB_DCUSAGE)); return (0); } /* * When dcmds or walkers call a dcmd that might be in another module, * we need to set mdb.m_frame->f_cp to an mdb_cmd that represents the * dcmd we're currently executing, otherwise mdb_get_module gets the * module of the caller instead of the module for the current dcmd. */ static int call_idcmd(mdb_idcmd_t *idcp, uintmax_t addr, uintmax_t count, uint_t flags, mdb_argvec_t *argv) { mdb_cmd_t *save_cp; mdb_cmd_t cmd; int ret; bzero(&cmd, sizeof (cmd)); cmd.c_dcmd = idcp; cmd.c_argv = *argv; save_cp = mdb.m_frame->f_cp; mdb.m_frame->f_cp = &cmd; ret = mdb_call_idcmd(cmd.c_dcmd, addr, count, flags, &cmd.c_argv, NULL, NULL); mdb.m_frame->f_cp = save_cp; return (ret); } int mdb_add_walker(const mdb_walker_t *wp) { mdb_module_t *mp; if (mdb.m_lmod == NULL) { mdb_cmd_t *cp = mdb.m_frame->f_cp; mp = cp->c_dcmd->idc_modp; } else mp = mdb.m_lmod; return (mdb_module_add_walker(mp, wp, 0)); } int mdb_remove_walker(const char *name) { mdb_module_t *mp; if (mdb.m_lmod == NULL) { mdb_cmd_t *cp = mdb.m_frame->f_cp; mp = cp->c_dcmd->idc_modp; } else mp = mdb.m_lmod; return (mdb_module_remove_walker(mp, name)); } void mdb_get_pipe(mdb_pipe_t *p) { mdb_cmd_t *cp = mdb.m_frame->f_cp; mdb_addrvec_t *adp = &cp->c_addrv; if (p == NULL) { warn("dcmd failure: mdb_get_pipe invoked with NULL pointer\n"); longjmp(mdb.m_frame->f_pcb, MDB_ERR_API); } if (adp->ad_nelems != 0) { ASSERT(adp->ad_ndx != 0); p->pipe_data = &adp->ad_data[adp->ad_ndx - 1]; p->pipe_len = adp->ad_nelems - adp->ad_ndx + 1; adp->ad_ndx = adp->ad_nelems; } else { p->pipe_data = NULL; p->pipe_len = 0; } } void mdb_set_pipe(const mdb_pipe_t *p) { mdb_cmd_t *cp = mdb.m_frame->f_pcmd; if (p == NULL) { warn("dcmd failure: mdb_set_pipe invoked with NULL pointer\n"); longjmp(mdb.m_frame->f_pcb, MDB_ERR_API); } if (cp != NULL) { size_t nbytes = sizeof (uintptr_t) * p->pipe_len; mdb_cmd_reset(cp); cp->c_addrv.ad_data = mdb_alloc(nbytes, UM_SLEEP); bcopy(p->pipe_data, cp->c_addrv.ad_data, nbytes); cp->c_addrv.ad_nelems = p->pipe_len; cp->c_addrv.ad_size = p->pipe_len; } } ssize_t mdb_get_xdata(const char *name, void *buf, size_t nbytes) { return (mdb_tgt_getxdata(mdb.m_target, name, buf, nbytes)); } /* * Private callback structure for implementing mdb_object_iter, below. */ typedef struct { mdb_object_cb_t oi_cb; void *oi_arg; int oi_rval; } object_iter_arg_t; /*ARGSUSED*/ static int mdb_object_cb(void *data, const mdb_map_t *map, const char *fullname) { object_iter_arg_t *arg = data; mdb_object_t obj; if (arg->oi_rval != 0) return (0); bzero(&obj, sizeof (obj)); obj.obj_base = map->map_base; obj.obj_name = strbasename(map->map_name); obj.obj_size = map->map_size; obj.obj_fullname = fullname; arg->oi_rval = arg->oi_cb(&obj, arg->oi_arg); return (0); } int mdb_object_iter(mdb_object_cb_t cb, void *data) { object_iter_arg_t arg; arg.oi_cb = cb; arg.oi_arg = data; arg.oi_rval = 0; if (mdb_tgt_object_iter(mdb.m_target, mdb_object_cb, &arg) != 0) return (-1); return (arg.oi_rval); } /* * Private callback structure for implementing mdb_symbol_iter, below. */ typedef struct { mdb_symbol_cb_t si_cb; void *si_arg; int si_rval; } symbol_iter_arg_t; /*ARGSUSED*/ static int mdb_symbol_cb(void *data, const GElf_Sym *gsym, const char *name, const mdb_syminfo_t *sip, const char *obj) { symbol_iter_arg_t *arg = data; mdb_symbol_t sym; if (arg->si_rval != 0) return (0); bzero(&sym, sizeof (sym)); sym.sym_name = name; sym.sym_object = obj; sym.sym_sym = gsym; sym.sym_table = sip->sym_table; sym.sym_id = sip->sym_id; arg->si_rval = arg->si_cb(&sym, arg->si_arg); return (0); } int mdb_symbol_iter(const char *obj, uint_t which, uint_t type, mdb_symbol_cb_t cb, void *data) { symbol_iter_arg_t arg; arg.si_cb = cb; arg.si_arg = data; arg.si_rval = 0; if (mdb_tgt_symbol_iter(mdb.m_target, obj, which, type, mdb_symbol_cb, &arg) != 0) return (-1); return (arg.si_rval); } /* * Private structure and function for implementing mdb_dumpptr on top * of mdb_dump_internal */ typedef struct dptrdat { mdb_dumpptr_cb_t func; void *arg; } dptrdat_t; static ssize_t mdb_dump_aux_ptr(void *buf, size_t nbyte, uint64_t offset, void *arg) { dptrdat_t *dat = arg; return (dat->func(buf, nbyte, offset, dat->arg)); } /* * Private structure and function for handling callbacks which return * EMDB_PARTIAL */ typedef struct d64dat { mdb_dump64_cb_t func; void *arg; } d64dat_t; static ssize_t mdb_dump_aux_partial(void *buf, size_t nbyte, uint64_t offset, void *arg) { d64dat_t *dat = arg; int result; int count; result = dat->func(buf, nbyte, offset, dat->arg); if (result == -1 && errno == EMDB_PARTIAL) { count = 0; do { result = dat->func((char *)buf + count, 1, offset + count, dat->arg); if (result == 1) count++; } while (count < nbyte && result == 1); if (count) result = count; } return (result); } /* Default callback for mdb_dumpptr() is calling mdb_vread(). */ static ssize_t mdb_dumpptr_cb(void *buf, size_t nbytes, uintptr_t addr, void *arg __unused) { return (mdb_vread(buf, nbytes, addr)); } int mdb_dumpptr(uintptr_t addr, size_t len, uint_t flags, mdb_dumpptr_cb_t fp, void *arg) { dptrdat_t dat; d64dat_t dat64; if (fp == NULL) dat.func = mdb_dumpptr_cb; else dat.func = fp; dat.arg = arg; dat64.func = mdb_dump_aux_ptr; dat64.arg = &dat; return (mdb_dump_internal(addr, len, flags, mdb_dump_aux_partial, &dat64, sizeof (uintptr_t))); } int mdb_dump64(uint64_t addr, uint64_t len, uint_t flags, mdb_dump64_cb_t fp, void *arg) { d64dat_t dat64; dat64.func = fp; dat64.arg = arg; return (mdb_dump_internal(addr, len, flags, mdb_dump_aux_partial, &dat64, sizeof (uint64_t))); } int mdb_get_state(void) { mdb_tgt_status_t ts; (void) mdb_tgt_status(mdb.m_target, &ts); return (ts.st_state); } void * mdb_callback_add(int class, mdb_callback_f fp, void *arg) { mdb_module_t *m; if (class != MDB_CALLBACK_STCHG && class != MDB_CALLBACK_PROMPT) { (void) set_errno(EINVAL); return (NULL); } if (mdb.m_lmod != NULL) m = mdb.m_lmod; else m = mdb.m_frame->f_cp->c_dcmd->idc_modp; return (mdb_callb_add(m, class, fp, arg)); } void mdb_callback_remove(void *hdl) { mdb_callb_remove(hdl); }