static int dcmd_invoke(mdb_idcmd_t *idcp, uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv, const mdb_vcb_t *vcbs) { int status; mdb_dprintf(MDB_DBG_DCMD, "dcmd %s`%s dot = %lr incr = %llr\n", idcp->idc_modp->mod_name, idcp->idc_name, addr, mdb.m_incr); if ((status = idcp->idc_funcp(addr, flags, argc, argv)) == DCMD_USAGE) { mdb_dcmd_usage(idcp, mdb.m_err); goto done; } while (status == DCMD_NEXT && (idcp = dcmd_ndef(idcp)) != NULL) status = idcp->idc_funcp(addr, flags, argc, argv); if (status == DCMD_USAGE) mdb_dcmd_usage(idcp, mdb.m_err); if (status == DCMD_NEXT) status = DCMD_OK; done: /* * If standard output is a pipe and there are vcbs active, we need to * flush standard out and the write-side of the pipe. The reasons for * this are explained in more detail in mdb_vcb.c. */ if ((flags & DCMD_PIPE_OUT) && (vcbs != NULL)) { mdb_iob_flush(mdb.m_out); (void) mdb_iob_ctl(mdb.m_out, I_FLUSH, (void *)FLUSHW); } return (status); }
/*ARGSUSED*/ void exit(int status) { #ifdef __sparc extern void kmdb_prom_exit_to_mon(void) __NORETURN; kmdb_prom_exit_to_mon(); #else extern void kmdb_dpi_reboot(void) __NORETURN; static int recurse = 0; if (!recurse) { recurse = 1; mdb_iob_printf(mdb.m_out, "Press any key to reboot\n"); mdb_iob_flush(mdb.m_out); mdb_iob_clearlines(mdb.m_out); (void) kmdb_getchar(); } kmdb_dpi_reboot(); #endif }
void vfail(const char *format, va_list alist) { extern const char *volatile _mdb_abort_str; static char buf[256]; static int nfail; if (_mdb_abort_str == NULL) { _mdb_abort_str = buf; /* Do this first so we don't recurse */ (void) mdb_iob_vsnprintf(buf, sizeof (buf), format, alist); nfail = 1; } /* * We'll try to print failure messages twice. Any more than that, * and we're probably hitting an assertion or some other problem in * the printing routines, and will recurse until we run out of stack. */ if (nfail++ < 3) { mdb_iob_printf(mdb.m_err, "%s ABORT: ", mdb.m_pname); mdb_iob_vprintf(mdb.m_err, format, alist); mdb_iob_flush(mdb.m_err); (void) mdb_signal_blockall(); (void) mdb_signal_raise(SIGABRT); (void) mdb_signal_unblock(SIGABRT); } exit(1); }
/* * Called during normal debugger operation and during debugger faults. */ static void kaif_enter_mon(void) { char c; for (;;) { mdb_iob_printf(mdb.m_out, "%s: Do you really want to reboot? (y/n) ", mdb.m_pname); mdb_iob_flush(mdb.m_out); mdb_iob_clearlines(mdb.m_out); c = kmdb_getchar(); if (c == 'n' || c == 'N' || c == CTRL('c')) return; else if (c == 'y' || c == 'Y') { mdb_iob_printf(mdb.m_out, "Rebooting...\n"); kmdb_dpi_reboot(); } } }
void mdb_flush(void) { mdb_iob_flush(mdb.m_out); }
/* * Call the current frame's mdb command. This entry point is used by the * MDB parser to actually execute a command once it has successfully parsed * a line of input. The command is waiting for us in the current frame. * We loop through each command on the list, executing its dcmd with the * appropriate argument. If the command has a successor, we know it had * a | operator after it, and so we need to create a pipe and replace * stdout with the pipe's output buffer. */ int mdb_call(uintmax_t addr, uintmax_t count, uint_t flags) { mdb_frame_t *fp = mdb.m_frame; mdb_cmd_t *cp, *ncp; mdb_iob_t *iobs[2]; int status, err = 0; jmp_buf pcb; if (mdb_iob_isapipe(mdb.m_in)) yyerror("syntax error"); mdb_intr_disable(); fp->f_cp = mdb_list_next(&fp->f_cmds); if (flags & DCMD_LOOP) flags |= DCMD_LOOPFIRST; /* set LOOPFIRST if this is a loop */ for (cp = mdb_list_next(&fp->f_cmds); cp; cp = mdb_list_next(cp)) { if (mdb_list_next(cp) != NULL) { mdb_iob_pipe(iobs, rdsvc, wrsvc); mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno); mdb.m_in = iobs[MDB_IOB_RDIOB]; mdb_iob_stack_push(&fp->f_ostk, mdb.m_out, 0); mdb.m_out = iobs[MDB_IOB_WRIOB]; ncp = mdb_list_next(cp); mdb_vcb_inherit(cp, ncp); bcopy(fp->f_pcb, pcb, sizeof (jmp_buf)); ASSERT(fp->f_pcmd == NULL); fp->f_pcmd = ncp; mdb_frame_set_pipe(fp); if ((err = setjmp(fp->f_pcb)) == 0) { status = mdb_call_idcmd(cp->c_dcmd, addr, count, flags | DCMD_PIPE_OUT, &cp->c_argv, &cp->c_addrv, cp->c_vcbs); ASSERT(mdb.m_in == iobs[MDB_IOB_RDIOB]); ASSERT(mdb.m_out == iobs[MDB_IOB_WRIOB]); } else { mdb_dprintf(MDB_DBG_DSTK, "frame <%u> caught " "error %s from pipeline\n", fp->f_id, mdb_err2str(err)); } if (err != 0 || DCMD_ABORTED(status)) { mdb_iob_setflags(mdb.m_in, MDB_IOB_ERR); mdb_iob_setflags(mdb.m_out, MDB_IOB_ERR); } else { mdb_iob_flush(mdb.m_out); (void) mdb_iob_ctl(mdb.m_out, I_FLUSH, (void *)FLUSHW); } mdb_frame_clear_pipe(fp); mdb_iob_destroy(mdb.m_out); mdb.m_out = mdb_iob_stack_pop(&fp->f_ostk); if (mdb.m_in != NULL) mdb_iob_destroy(mdb.m_in); mdb.m_in = mdb_iob_stack_pop(&fp->f_istk); yylineno = mdb_iob_lineno(mdb.m_in); fp->f_pcmd = NULL; bcopy(pcb, fp->f_pcb, sizeof (jmp_buf)); if (MDB_ERR_IS_FATAL(err)) longjmp(fp->f_pcb, err); if (err != 0 || DCMD_ABORTED(status) || mdb_addrvec_length(&ncp->c_addrv) == 0) break; addr = mdb_nv_get_value(mdb.m_dot); count = 1; flags = 0; } else { mdb_intr_enable(); (void) mdb_call_idcmd(cp->c_dcmd, addr, count, flags, &cp->c_argv, &cp->c_addrv, cp->c_vcbs); mdb_intr_disable(); } fp->f_cp = mdb_list_next(cp); mdb_cmd_reset(cp); } /* * If our last-command list is non-empty, destroy it. Then copy the * current frame's cmd list to the m_lastc list and reset the frame. */ while ((cp = mdb_list_next(&mdb.m_lastc)) != NULL) { mdb_list_delete(&mdb.m_lastc, cp); mdb_cmd_destroy(cp); } mdb_list_move(&fp->f_cmds, &mdb.m_lastc); mdb_frame_reset(fp); mdb_intr_enable(); return (err == 0); }
void kt_activate(mdb_tgt_t *t) { static const mdb_nv_disc_t reg_disc = { NULL, reg_disc_get }; kt_data_t *kt = t->t_data; void *sym; int oflag; mdb_prop_postmortem = (kt->k_dumphdr != NULL); mdb_prop_kernel = TRUE; mdb_prop_datamodel = MDB_TGT_MODEL_NATIVE; if (kt->k_activated == FALSE) { struct utsname u1, u2; /* * If we're examining a crash dump, root is /, and uname(2) * does not match the utsname in the dump, issue a warning. * Note that we are assuming that the modules and macros in * /usr/lib are compiled against the kernel from uname -rv. */ if (mdb_prop_postmortem && strcmp(mdb.m_root, "/") == 0 && uname(&u1) >= 0 && kt_uname(t, &u2) >= 0 && (strcmp(u1.release, u2.release) || strcmp(u1.version, u2.version))) { mdb_warn("warning: dump is from %s %s %s; dcmds and " "macros may not match kernel implementation\n", u2.sysname, u2.release, u2.version); } if (mdb_module_load(KT_MODULE, MDB_MOD_GLOBAL) < 0) { warn("failed to load kernel support module -- " "some modules may not load\n"); } if (mdb_prop_postmortem) { sym = dlsym(RTLD_NEXT, "mdb_dump_print_content"); if (sym != NULL) kt->k_dump_print_content = (void (*)())sym; sym = dlsym(RTLD_NEXT, "mdb_dump_find_curproc"); if (sym != NULL) kt->k_dump_find_curproc = (int (*)())sym; kt->k_dumpcontent = kt_find_dump_contents(kt); } if (t->t_flags & MDB_TGT_F_PRELOAD) { oflag = mdb_iob_getflags(mdb.m_out) & MDB_IOB_PGENABLE; mdb_iob_clrflags(mdb.m_out, oflag); mdb_iob_puts(mdb.m_out, "Preloading module symbols: ["); mdb_iob_flush(mdb.m_out); } if (!(t->t_flags & MDB_TGT_F_NOLOAD)) kt_load_modules(kt, t); if (t->t_flags & MDB_TGT_F_PRELOAD) { mdb_iob_puts(mdb.m_out, " ]\n"); mdb_iob_setflags(mdb.m_out, oflag); } kt->k_activated = TRUE; } (void) mdb_tgt_register_dcmds(t, &kt_dcmds[0], MDB_MOD_FORCE); /* Export some of our registers as named variables */ mdb_tgt_register_regvars(t, kt->k_rds, ®_disc, MDB_NV_RDONLY); mdb_tgt_elf_export(kt->k_file); }
static void kt_load_modules(kt_data_t *kt, mdb_tgt_t *t) { char name[MAXNAMELEN]; uintptr_t addr, head; struct module kmod; struct modctl ctl; Shdr symhdr, strhdr; GElf_Sym sym; kt_module_t *km; if (mdb_tgt_lookup_by_name(t, MDB_TGT_OBJ_EXEC, "modules", &sym, NULL) == -1) { warn("failed to get 'modules' symbol"); return; } if (mdb_tgt_readsym(t, MDB_TGT_AS_VIRT, &ctl, sizeof (ctl), MDB_TGT_OBJ_EXEC, "modules") != sizeof (ctl)) { warn("failed to read 'modules' struct"); return; } addr = head = (uintptr_t)sym.st_value; do { if (addr == NULL) break; /* Avoid spurious NULL pointers in list */ if (mdb_tgt_vread(t, &ctl, sizeof (ctl), addr) == -1) { warn("failed to read modctl at %p", (void *)addr); return; } if (ctl.mod_mp == NULL) continue; /* No associated krtld structure */ if (mdb_tgt_readstr(t, MDB_TGT_AS_VIRT, name, MAXNAMELEN, (uintptr_t)ctl.mod_modname) <= 0) { warn("failed to read module name at %p", (void *)ctl.mod_modname); continue; } mdb_dprintf(MDB_DBG_KMOD, "reading mod %s (%p)\n", name, (void *)addr); if (mdb_nv_lookup(&kt->k_modules, name) != NULL) { warn("skipping duplicate module '%s', id=%d\n", name, ctl.mod_id); continue; } if (mdb_tgt_vread(t, &kmod, sizeof (kmod), (uintptr_t)ctl.mod_mp) == -1) { warn("failed to read module at %p\n", (void *)ctl.mod_mp); continue; } if (kmod.symspace == NULL || kmod.symhdr == NULL || kmod.strhdr == NULL) { /* * If no buffer for the symbols has been allocated, * or the shdrs for .symtab and .strtab are missing, * then we're out of luck. */ continue; } if (mdb_tgt_vread(t, &symhdr, sizeof (Shdr), (uintptr_t)kmod.symhdr) == -1) { warn("failed to read .symtab header for '%s', id=%d", name, ctl.mod_id); continue; } if (mdb_tgt_vread(t, &strhdr, sizeof (Shdr), (uintptr_t)kmod.strhdr) == -1) { warn("failed to read .strtab header for '%s', id=%d", name, ctl.mod_id); continue; } /* * Now get clever: f(*^ing krtld didn't used to bother updating * its own kmod.symsize value. We know that prior to this bug * being fixed, symspace was a contiguous buffer containing * .symtab, .strtab, and the symbol hash table in that order. * So if symsize is zero, recompute it as the size of .symtab * plus the size of .strtab. We don't need to load the hash * table anyway since we re-hash all the symbols internally. */ if (kmod.symsize == 0) kmod.symsize = symhdr.sh_size + strhdr.sh_size; /* * Similar logic can be used to make educated guesses * at the values of kmod.symtbl and kmod.strings. */ if (kmod.symtbl == NULL) kmod.symtbl = kmod.symspace; if (kmod.strings == NULL) kmod.strings = kmod.symspace + symhdr.sh_size; /* * Make sure things seem reasonable before we proceed * to actually read and decipher the symspace. */ if (KT_BAD_BUF(kmod.symtbl, kmod.symspace, kmod.symsize) || KT_BAD_BUF(kmod.strings, kmod.symspace, kmod.symsize)) { warn("skipping module '%s', id=%d (corrupt symspace)\n", name, ctl.mod_id); continue; } km = mdb_zalloc(sizeof (kt_module_t), UM_SLEEP); km->km_name = strdup(name); (void) mdb_nv_insert(&kt->k_modules, km->km_name, NULL, (uintptr_t)km, MDB_NV_EXTNAME); km->km_datasz = kmod.symsize; km->km_symspace_va = (uintptr_t)kmod.symspace; km->km_symtab_va = (uintptr_t)kmod.symtbl; km->km_strtab_va = (uintptr_t)kmod.strings; km->km_symtab_hdr = symhdr; km->km_strtab_hdr = strhdr; km->km_text_va = (uintptr_t)kmod.text; km->km_text_size = kmod.text_size; km->km_data_va = (uintptr_t)kmod.data; km->km_data_size = kmod.data_size; km->km_bss_va = (uintptr_t)kmod.bss; km->km_bss_size = kmod.bss_size; if (kt->k_ctfvalid) { km->km_ctf_va = (uintptr_t)kmod.ctfdata; km->km_ctf_size = kmod.ctfsize; } /* * Add the module to the end of the list of modules in load- * dependency order. This is needed to load the corresponding * debugger modules in the same order for layering purposes. */ mdb_list_append(&kt->k_modlist, km); if (t->t_flags & MDB_TGT_F_PRELOAD) { mdb_iob_printf(mdb.m_out, " %s", name); mdb_iob_flush(mdb.m_out); kt_load_module(kt, t, km); } } while ((addr = (uintptr_t)ctl.mod_next) != head); }
/*ARGSUSED*/ static void flt_handler(int sig, siginfo_t *sip, ucontext_t *ucp, void *data) { static const struct rlimit rl = { (rlim_t)RLIM_INFINITY, (rlim_t)RLIM_INFINITY }; const mdb_idcmd_t *idcp = NULL; if (mdb.m_frame != NULL && mdb.m_frame->f_cp != NULL) idcp = mdb.m_frame->f_cp->c_dcmd; if (sip != NULL) bcopy(sip, &_mdb_abort_info, sizeof (_mdb_abort_info)); if (ucp != NULL) bcopy(ucp, &_mdb_abort_ctx, sizeof (_mdb_abort_ctx)); _mdb_abort_info.si_signo = sig; (void) mdb_signal_sethandler(sig, SIG_DFL, NULL); /* * If there is no current dcmd, or the current dcmd comes from a * builtin module, we don't allow resume and always core dump. */ if (idcp == NULL || idcp->idc_modp == NULL || idcp->idc_modp == &mdb.m_rmod || idcp->idc_modp->mod_hdl == NULL) goto dump; if (mdb.m_term != NULL) { struct frame *fr = (struct frame *) (ucp->uc_mcontext.gregs[STACK_REGISTER] + STACK_BIAS); char signame[SIG2STR_MAX]; int i = 1; char c; if (sig2str(sig, signame) == -1) { mdb_iob_printf(mdb.m_err, "\n*** %s: received signal %d at:\n", mdb.m_pname, sig); } else { mdb_iob_printf(mdb.m_err, "\n*** %s: received signal %s at:\n", mdb.m_pname, signame); } if (ucp->uc_mcontext.gregs[REG_PC] != 0) print_frame(ucp->uc_mcontext.gregs[REG_PC], i++); while (fr != NULL && valid_frame(fr) && fr->fr_savpc != 0) { print_frame(fr->fr_savpc, i++); fr = (struct frame *) ((uintptr_t)fr->fr_savfp + STACK_BIAS); } query: mdb_iob_printf(mdb.m_err, "\n%s: (c)ore dump, (q)uit, " "(r)ecover, or (s)top for debugger [cqrs]? ", mdb.m_pname); mdb_iob_flush(mdb.m_err); for (;;) { if (IOP_READ(mdb.m_term, &c, sizeof (c)) != sizeof (c)) goto dump; switch (c) { case 'c': case 'C': (void) setrlimit(RLIMIT_CORE, &rl); mdb_iob_printf(mdb.m_err, "\n%s: attempting " "to dump core ...\n", mdb.m_pname); goto dump; case 'q': case 'Q': mdb_iob_discard(mdb.m_out); mdb_iob_nl(mdb.m_err); (void) mdb_signal_unblockall(); terminate(1); /*NOTREACHED*/ case 'r': case 'R': mdb_iob_printf(mdb.m_err, "\n%s: unloading " "module '%s' ...\n", mdb.m_pname, idcp->idc_modp->mod_name); (void) mdb_module_unload( idcp->idc_modp->mod_name, 0); (void) mdb_signal_sethandler(sig, flt_handler, NULL); _mdb_abort_rcount++; mdb.m_intr = 0; mdb.m_pend = 0; (void) mdb_signal_unblockall(); longjmp(mdb.m_frame->f_pcb, MDB_ERR_ABORT); /*NOTREACHED*/ case 's': case 'S': mdb_iob_printf(mdb.m_err, "\n%s: " "attempting to stop pid %d ...\n", mdb.m_pname, (int)getpid()); /* * Stop ourself; if this fails or we are * subsequently continued, ask again. */ (void) mdb_signal_raise(SIGSTOP); (void) mdb_signal_unblockall(); goto query; } } } dump: if (SI_FROMUSER(sip)) { (void) mdb_signal_block(sig); (void) mdb_signal_raise(sig); } (void) sigfillset(&ucp->uc_sigmask); (void) sigdelset(&ucp->uc_sigmask, sig); if (_mdb_abort_str == NULL) _mdb_abort_str = "fatal signal received"; ucp->uc_flags |= UC_SIGMASK; (void) setcontext(ucp); }
/*ARGSUSED*/ static void * mdb_umem_handler(size_t nbytes, size_t align, uint_t flags) { #ifdef _KMDB /* * kmdb has a fixed, dedicated VA range in which to play. This range * won't change size while the debugger is running, regardless of how * long we wait. As a result, the only sensible course of action is * to fail the request. If we're here, however, the request was made * with UM_SLEEP. The caller is thus not expecting a NULL back. We'll * have to fail the current dcmd set. */ if (mdb.m_depth > 0) { warn("failed to allocate %lu bytes -- recovering\n", (ulong_t)nbytes); kmdb_print_stack(); longjmp(mdb.m_frame->f_pcb, MDB_ERR_NOMEM); } #else /* * mdb, on the other hand, can afford to wait, as someone may actually * free something. */ if (errno == EAGAIN) { void *ptr = NULL; char buf[64]; (void) mdb_iob_snprintf(buf, sizeof (buf), "[ sleeping for %lu bytes of free memory ... ]", (ulong_t)nbytes); (void) mdb_iob_puts(mdb.m_err, buf); (void) mdb_iob_flush(mdb.m_err); do { (void) poll(NULL, 0, 1000); if (align != 0) ptr = memalign(align, nbytes); else ptr = malloc(nbytes); } while (ptr == NULL && errno == EAGAIN); if (ptr != NULL) return (ptr); (void) memset(buf, '\b', strlen(buf)); (void) mdb_iob_puts(mdb.m_err, buf); (void) mdb_iob_flush(mdb.m_err); (void) memset(buf, ' ', strlen(buf)); (void) mdb_iob_puts(mdb.m_err, buf); (void) mdb_iob_flush(mdb.m_err); (void) memset(buf, '\b', strlen(buf)); (void) mdb_iob_puts(mdb.m_err, buf); (void) mdb_iob_flush(mdb.m_err); } #endif die("failed to allocate %lu bytes -- terminating\n", (ulong_t)nbytes); /*NOTREACHED*/ return (NULL); }