/* * "sp" is a kernel VA. */ static int print_stack(uintptr_t sp, uintptr_t pc, uintptr_t addr, int argc, const mdb_arg_t *argv, int free_state) { int showargs = 0, count, err; count = mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &showargs, NULL); argc -= count; argv += count; if (argc > 1 || (argc == 1 && argv->a_type != MDB_TYPE_STRING)) return (DCMD_USAGE); mdb_printf("stack pointer for thread %p%s: %p\n", addr, (free_state ? " (TS_FREE)" : ""), sp); if (pc != 0) mdb_printf("[ %0?lr %a() ]\n", sp, pc); mdb_inc_indent(2); mdb_set_dot(sp); if (argc == 1) err = mdb_eval(argv->a_un.a_str); else if (showargs) err = mdb_eval("<.$C"); else err = mdb_eval("<.$C0"); mdb_dec_indent(2); return ((err == -1) ? DCMD_ABORT : DCMD_OK); }
/* ARGSUSED */ static int xhci_mdb_print_device(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int count; xhci_device_t xd; usba_device_t ud; char product[256], mfg[256]; if (!(flags & DCMD_ADDRSPEC)) { return (mdb_eval("::walk xhci`xhci | ::walk xhci`xhci_device | " "::xhci_device")); } if (mdb_vread(&xd, sizeof (xd), addr) != sizeof (xd)) { mdb_warn("failed to read xhci_device_t at 0x%x", addr); return (DCMD_ERR); } if (mdb_vread(&ud, sizeof (ud), (uintptr_t)xd.xd_usbdev) != sizeof (ud)) { mdb_warn("failed to read usba_device_t at %p\n", xd.xd_usbdev); return (DCMD_ERR); } if (ud.usb_mfg_str == NULL || mdb_readstr(mfg, sizeof (mfg), (uintptr_t)ud.usb_mfg_str) <= 0) { (void) strlcpy(mfg, "Unknown Manufacturer", sizeof (mfg)); } if (ud.usb_product_str == NULL || mdb_readstr(product, sizeof (product), (uintptr_t)ud.usb_product_str) <= 0) { (void) strlcpy(product, "Unknown Product", sizeof (product)); } mdb_printf("%<b>%s - %s%</b>\n", mfg, product); count = 0; if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_endpoint_count, &count, addr) == -1) { mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr); return (DCMD_ERR); } mdb_printf("Port %02d | Slot %02d | # Endpoints %02d\n", xd.xd_port, xd.xd_slot, count); mdb_printf("%<u>%-4s %-10s %-10s %-6s %-6s%</u>\n", "EP", "Type", "State", "Head", "Tail"); if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_print_endpoint_summary, &xd, addr) == -1) { mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr); return (DCMD_ERR); } mdb_printf("\n"); return (DCMD_OK); }
/*ARGSUSED*/ int stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { size_t idx; char *seen = NULL; const char *caller_str = NULL; const char *excl_caller_str = NULL; uintptr_t caller = 0, excl_caller = 0; const char *module_str = NULL; const char *excl_module_str = NULL; stacks_module_t module, excl_module; const char *sobj = NULL; const char *excl_sobj = NULL; uintptr_t sobj_ops = 0, excl_sobj_ops = 0; const char *tstate_str = NULL; const char *excl_tstate_str = NULL; uint_t tstate = -1U; uint_t excl_tstate = -1U; uint_t printed = 0; uint_t all = 0; uint_t force = 0; uint_t interesting = 0; uint_t verbose = 0; /* * We have a slight behavior difference between having piped * input and 'addr::stacks'. Without a pipe, we assume the * thread pointer given is a representative thread, and so * we include all similar threads in the system in our output. * * With a pipe, we filter down to just the threads in our * input. */ uint_t addrspec = (flags & DCMD_ADDRSPEC); uint_t only_matching = addrspec && (flags & DCMD_PIPE); mdb_pipe_t p; bzero(&module, sizeof (module)); bzero(&excl_module, sizeof (excl_module)); if (mdb_getopts(argc, argv, 'a', MDB_OPT_SETBITS, TRUE, &all, 'f', MDB_OPT_SETBITS, TRUE, &force, 'i', MDB_OPT_SETBITS, TRUE, &interesting, 'v', MDB_OPT_SETBITS, TRUE, &verbose, 'c', MDB_OPT_STR, &caller_str, 'C', MDB_OPT_STR, &excl_caller_str, 'm', MDB_OPT_STR, &module_str, 'M', MDB_OPT_STR, &excl_module_str, 's', MDB_OPT_STR, &sobj, 'S', MDB_OPT_STR, &excl_sobj, 't', MDB_OPT_STR, &tstate_str, 'T', MDB_OPT_STR, &excl_tstate_str, NULL) != argc) return (DCMD_USAGE); if (interesting) { if (sobj != NULL || excl_sobj != NULL || tstate_str != NULL || excl_tstate_str != NULL) { mdb_warn( "stacks: -i is incompatible with -[sStT]\n"); return (DCMD_USAGE); } excl_sobj = "CV"; excl_tstate_str = "FREE"; } if (caller_str != NULL) { mdb_set_dot(0); if (mdb_eval(caller_str) != 0) { mdb_warn("stacks: evaluation of \"%s\" failed", caller_str); return (DCMD_ABORT); } caller = mdb_get_dot(); } if (excl_caller_str != NULL) { mdb_set_dot(0); if (mdb_eval(excl_caller_str) != 0) { mdb_warn("stacks: evaluation of \"%s\" failed", excl_caller_str); return (DCMD_ABORT); } excl_caller = mdb_get_dot(); } mdb_set_dot(addr); if (module_str != NULL && stacks_module_find(module_str, &module) != 0) return (DCMD_ABORT); if (excl_module_str != NULL && stacks_module_find(excl_module_str, &excl_module) != 0) return (DCMD_ABORT); if (sobj != NULL && text_to_sobj(sobj, &sobj_ops) != 0) return (DCMD_USAGE); if (excl_sobj != NULL && text_to_sobj(excl_sobj, &excl_sobj_ops) != 0) return (DCMD_USAGE); if (sobj_ops != 0 && excl_sobj_ops != 0) { mdb_warn("stacks: only one of -s and -S can be specified\n"); return (DCMD_USAGE); } if (tstate_str != NULL && text_to_tstate(tstate_str, &tstate) != 0) return (DCMD_USAGE); if (excl_tstate_str != NULL && text_to_tstate(excl_tstate_str, &excl_tstate) != 0) return (DCMD_USAGE); if (tstate != -1U && excl_tstate != -1U) { mdb_warn("stacks: only one of -t and -T can be specified\n"); return (DCMD_USAGE); } /* * If there's an address specified, we're going to further filter * to only entries which have an address in the input. To reduce * overhead (and make the sorted output come out right), we * use mdb_get_pipe() to grab the entire pipeline of input, then * use qsort() and bsearch() to speed up the search. */ if (addrspec) { mdb_get_pipe(&p); if (p.pipe_data == NULL || p.pipe_len == 0) { p.pipe_data = &addr; p.pipe_len = 1; } qsort(p.pipe_data, p.pipe_len, sizeof (uintptr_t), uintptrcomp); /* remove any duplicates in the data */ idx = 0; while (idx < p.pipe_len - 1) { uintptr_t *data = &p.pipe_data[idx]; size_t len = p.pipe_len - idx; if (data[0] == data[1]) { memmove(data, data + 1, (len - 1) * sizeof (*data)); p.pipe_len--; continue; /* repeat without incrementing idx */ } idx++; } seen = mdb_zalloc(p.pipe_len, UM_SLEEP | UM_GC); } /* * Force a cleanup if we're connected to a live system. Never * do a cleanup after the first invocation around the loop. */ force |= (mdb_get_state() == MDB_STATE_RUNNING); if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP) force = 0; stacks_cleanup(force); if (stacks_state == STACKS_STATE_CLEAN) { int res = stacks_run(verbose, addrspec ? &p : NULL); if (res != DCMD_OK) return (res); } for (idx = 0; idx < stacks_array_size; idx++) { stacks_entry_t *sep = stacks_array[idx]; stacks_entry_t *cur = sep; int frame; size_t count = sep->se_count; if (addrspec) { stacks_entry_t *head = NULL, *tail = NULL, *sp; size_t foundcount = 0; /* * We use the now-unused hash chain field se_next to * link together the dups which match our list. */ for (sp = sep; sp != NULL; sp = sp->se_dup) { uintptr_t *entry = bsearch(&sp->se_thread, p.pipe_data, p.pipe_len, sizeof (uintptr_t), uintptrcomp); if (entry != NULL) { foundcount++; seen[entry - p.pipe_data]++; if (head == NULL) head = sp; else tail->se_next = sp; tail = sp; sp->se_next = NULL; } } if (head == NULL) continue; /* no match, skip entry */ if (only_matching) { cur = sep = head; count = foundcount; } } if (caller != 0 && !stacks_has_caller(sep, caller)) continue; if (excl_caller != 0 && stacks_has_caller(sep, excl_caller)) continue; if (module.sm_size != 0 && !stacks_has_module(sep, &module)) continue; if (excl_module.sm_size != 0 && stacks_has_module(sep, &excl_module)) continue; if (tstate != -1U) { if (tstate == TSTATE_PANIC) { if (!sep->se_panic) continue; } else if (sep->se_panic || sep->se_tstate != tstate) continue; } if (excl_tstate != -1U) { if (excl_tstate == TSTATE_PANIC) { if (sep->se_panic) continue; } else if (!sep->se_panic && sep->se_tstate == excl_tstate) continue; } if (sobj_ops == SOBJ_ALL) { if (sep->se_sobj_ops == 0) continue; } else if (sobj_ops != 0) { if (sobj_ops != sep->se_sobj_ops) continue; } if (!(interesting && sep->se_panic)) { if (excl_sobj_ops == SOBJ_ALL) { if (sep->se_sobj_ops != 0) continue; } else if (excl_sobj_ops != 0) { if (excl_sobj_ops == sep->se_sobj_ops) continue; } } if (flags & DCMD_PIPE_OUT) { while (sep != NULL) { mdb_printf("%lr\n", sep->se_thread); sep = only_matching ? sep->se_next : sep->se_dup; } continue; } if (all || !printed) { mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n", "THREAD", "STATE", "SOBJ", "COUNT"); printed = 1; } do { char state[20]; char sobj[100]; tstate_to_text(cur->se_tstate, cur->se_panic, state, sizeof (state)); sobj_to_text(cur->se_sobj_ops, sobj, sizeof (sobj)); if (cur == sep) mdb_printf("%-?p %-8s %-?s %8d\n", cur->se_thread, state, sobj, count); else mdb_printf("%-?p %-8s %-?s %8s\n", cur->se_thread, state, sobj, "-"); cur = only_matching ? cur->se_next : cur->se_dup; } while (all && cur != NULL); if (sep->se_failed != 0) { char *reason; switch (sep->se_failed) { case FSI_FAIL_NOTINMEMORY: reason = "thread not in memory"; break; case FSI_FAIL_THREADCORRUPT: reason = "thread structure stack info corrupt"; break; case FSI_FAIL_STACKNOTFOUND: reason = "no consistent stack found"; break; default: reason = "unknown failure"; break; } mdb_printf("%?s <%s>\n", "", reason); } for (frame = 0; frame < sep->se_depth; frame++) mdb_printf("%?s %a\n", "", sep->se_stack[frame]); if (sep->se_overflow) mdb_printf("%?s ... truncated ...\n", ""); mdb_printf("\n"); } if (flags & DCMD_ADDRSPEC) { for (idx = 0; idx < p.pipe_len; idx++) if (seen[idx] == 0) mdb_warn("stacks: %p not in thread list\n", p.pipe_data[idx]); } return (DCMD_OK); }
/*ARGSUSED*/ void cmd_event(mdb_tgt_t *t, int vid, void *s) { if (s != NULL && mdb_eval(s) == -1) mdb_warn("failed to eval [ %d ] command \"%s\"", vid, s); }