10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 58721SJonathan.Adams@Sun.COM * Common Development and Distribution License (the "License"). 68721SJonathan.Adams@Sun.COM * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 21*12902SBryan.Cantrill@Sun.COM 220Sstevel@tonic-gate /* 23*12902SBryan.Cantrill@Sun.COM * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #include <mdb/mdb_modapi.h> 270Sstevel@tonic-gate #include <mdb/mdb_ctf.h> 280Sstevel@tonic-gate 290Sstevel@tonic-gate #include <sys/types.h> 300Sstevel@tonic-gate #include <sys/regset.h> 310Sstevel@tonic-gate #include <sys/stack.h> 320Sstevel@tonic-gate #include <sys/thread.h> 338742Sgap@sun.com #include <sys/modctl.h> 34*12902SBryan.Cantrill@Sun.COM #include <assert.h> 350Sstevel@tonic-gate 360Sstevel@tonic-gate #include "findstack.h" 378721SJonathan.Adams@Sun.COM #include "thread.h" 388721SJonathan.Adams@Sun.COM #include "sobj.h" 398721SJonathan.Adams@Sun.COM 40*12902SBryan.Cantrill@Sun.COM int findstack_debug_on = 0; 410Sstevel@tonic-gate 420Sstevel@tonic-gate /* 430Sstevel@tonic-gate * "sp" is a kernel VA. 440Sstevel@tonic-gate */ 450Sstevel@tonic-gate static int 460Sstevel@tonic-gate print_stack(uintptr_t sp, uintptr_t pc, uintptr_t addr, 470Sstevel@tonic-gate int argc, const mdb_arg_t *argv, int free_state) 480Sstevel@tonic-gate { 490Sstevel@tonic-gate int showargs = 0, count, err; 500Sstevel@tonic-gate 510Sstevel@tonic-gate count = mdb_getopts(argc, argv, 520Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &showargs, NULL); 530Sstevel@tonic-gate argc -= count; 540Sstevel@tonic-gate argv += count; 550Sstevel@tonic-gate 560Sstevel@tonic-gate if (argc > 1 || (argc == 1 && argv->a_type != MDB_TYPE_STRING)) 570Sstevel@tonic-gate return (DCMD_USAGE); 580Sstevel@tonic-gate 590Sstevel@tonic-gate mdb_printf("stack pointer for thread %p%s: %p\n", 600Sstevel@tonic-gate addr, (free_state ? " (TS_FREE)" : ""), sp); 610Sstevel@tonic-gate if (pc != 0) 620Sstevel@tonic-gate mdb_printf("[ %0?lr %a() ]\n", sp, pc); 630Sstevel@tonic-gate 640Sstevel@tonic-gate mdb_inc_indent(2); 650Sstevel@tonic-gate mdb_set_dot(sp); 660Sstevel@tonic-gate 670Sstevel@tonic-gate if (argc == 1) 680Sstevel@tonic-gate err = mdb_eval(argv->a_un.a_str); 690Sstevel@tonic-gate else if (showargs) 700Sstevel@tonic-gate err = mdb_eval("<.$C"); 710Sstevel@tonic-gate else 720Sstevel@tonic-gate err = mdb_eval("<.$C0"); 730Sstevel@tonic-gate 740Sstevel@tonic-gate mdb_dec_indent(2); 750Sstevel@tonic-gate 760Sstevel@tonic-gate return ((err == -1) ? DCMD_ABORT : DCMD_OK); 770Sstevel@tonic-gate } 780Sstevel@tonic-gate 798721SJonathan.Adams@Sun.COM int 808721SJonathan.Adams@Sun.COM findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 818721SJonathan.Adams@Sun.COM { 828721SJonathan.Adams@Sun.COM findstack_info_t fsi; 838721SJonathan.Adams@Sun.COM int retval; 848721SJonathan.Adams@Sun.COM 858721SJonathan.Adams@Sun.COM if (!(flags & DCMD_ADDRSPEC)) 868721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 878721SJonathan.Adams@Sun.COM 888721SJonathan.Adams@Sun.COM bzero(&fsi, sizeof (fsi)); 898721SJonathan.Adams@Sun.COM 90*12902SBryan.Cantrill@Sun.COM if ((retval = stacks_findstack(addr, &fsi, 1)) != DCMD_OK || 918721SJonathan.Adams@Sun.COM fsi.fsi_failed) 928721SJonathan.Adams@Sun.COM return (retval); 938721SJonathan.Adams@Sun.COM 948721SJonathan.Adams@Sun.COM return (print_stack(fsi.fsi_sp, fsi.fsi_pc, addr, 958721SJonathan.Adams@Sun.COM argc, argv, fsi.fsi_tstate == TS_FREE)); 968721SJonathan.Adams@Sun.COM } 978721SJonathan.Adams@Sun.COM 980Sstevel@tonic-gate /*ARGSUSED*/ 990Sstevel@tonic-gate int 1000Sstevel@tonic-gate findstack_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *av) 1010Sstevel@tonic-gate { 1020Sstevel@tonic-gate findstack_debug_on ^= 1; 1030Sstevel@tonic-gate 1040Sstevel@tonic-gate mdb_printf("findstack: debugging is now %s\n", 1050Sstevel@tonic-gate findstack_debug_on ? "on" : "off"); 1060Sstevel@tonic-gate 1070Sstevel@tonic-gate return (DCMD_OK); 1080Sstevel@tonic-gate } 1090Sstevel@tonic-gate 1108721SJonathan.Adams@Sun.COM static void 1118721SJonathan.Adams@Sun.COM uppercase(char *p) 1128721SJonathan.Adams@Sun.COM { 1138721SJonathan.Adams@Sun.COM for (; *p != '\0'; p++) { 1148721SJonathan.Adams@Sun.COM if (*p >= 'a' && *p <= 'z') 1158721SJonathan.Adams@Sun.COM *p += 'A' - 'a'; 1168721SJonathan.Adams@Sun.COM } 1178721SJonathan.Adams@Sun.COM } 1188721SJonathan.Adams@Sun.COM 1198721SJonathan.Adams@Sun.COM static void 1208721SJonathan.Adams@Sun.COM sobj_to_text(uintptr_t addr, char *out, size_t out_sz) 1218721SJonathan.Adams@Sun.COM { 1228721SJonathan.Adams@Sun.COM sobj_ops_to_text(addr, out, out_sz); 1238721SJonathan.Adams@Sun.COM uppercase(out); 1248721SJonathan.Adams@Sun.COM } 1258721SJonathan.Adams@Sun.COM 1268721SJonathan.Adams@Sun.COM #define SOBJ_ALL 1 127*12902SBryan.Cantrill@Sun.COM 1288721SJonathan.Adams@Sun.COM static int 1298721SJonathan.Adams@Sun.COM text_to_sobj(const char *text, uintptr_t *out) 1308721SJonathan.Adams@Sun.COM { 1318721SJonathan.Adams@Sun.COM if (strcasecmp(text, "ALL") == 0) { 1328721SJonathan.Adams@Sun.COM *out = SOBJ_ALL; 1338721SJonathan.Adams@Sun.COM return (0); 1348721SJonathan.Adams@Sun.COM } 135*12902SBryan.Cantrill@Sun.COM 1368721SJonathan.Adams@Sun.COM return (sobj_text_to_ops(text, out)); 1378721SJonathan.Adams@Sun.COM } 1388721SJonathan.Adams@Sun.COM 1398721SJonathan.Adams@Sun.COM #define TSTATE_PANIC -2U 1408721SJonathan.Adams@Sun.COM static int 1418721SJonathan.Adams@Sun.COM text_to_tstate(const char *text, uint_t *out) 1428721SJonathan.Adams@Sun.COM { 1438721SJonathan.Adams@Sun.COM if (strcasecmp(text, "panic") == 0) 1448721SJonathan.Adams@Sun.COM *out = TSTATE_PANIC; 1458721SJonathan.Adams@Sun.COM else if (thread_text_to_state(text, out) != 0) { 1468721SJonathan.Adams@Sun.COM mdb_warn("tstate \"%s\" not recognized\n", text); 1478721SJonathan.Adams@Sun.COM return (-1); 1488721SJonathan.Adams@Sun.COM } 1498721SJonathan.Adams@Sun.COM return (0); 1508721SJonathan.Adams@Sun.COM } 1518721SJonathan.Adams@Sun.COM 1528721SJonathan.Adams@Sun.COM static void 1538721SJonathan.Adams@Sun.COM tstate_to_text(uint_t tstate, uint_t paniced, char *out, size_t out_sz) 1548721SJonathan.Adams@Sun.COM { 1558721SJonathan.Adams@Sun.COM if (paniced) 1568721SJonathan.Adams@Sun.COM mdb_snprintf(out, out_sz, "panic"); 1578721SJonathan.Adams@Sun.COM else 1588721SJonathan.Adams@Sun.COM thread_state_to_text(tstate, out, out_sz); 1598721SJonathan.Adams@Sun.COM uppercase(out); 1608721SJonathan.Adams@Sun.COM } 1618721SJonathan.Adams@Sun.COM 1628721SJonathan.Adams@Sun.COM typedef struct stacks_entry { 1638721SJonathan.Adams@Sun.COM struct stacks_entry *se_next; 1648721SJonathan.Adams@Sun.COM struct stacks_entry *se_dup; /* dups of this stack */ 1658721SJonathan.Adams@Sun.COM uintptr_t se_thread; 1668721SJonathan.Adams@Sun.COM uintptr_t se_sp; 1678721SJonathan.Adams@Sun.COM uintptr_t se_sobj_ops; 1688721SJonathan.Adams@Sun.COM uint32_t se_tstate; 1698721SJonathan.Adams@Sun.COM uint32_t se_count; /* # threads w/ this stack */ 1708721SJonathan.Adams@Sun.COM uint8_t se_overflow; 1718721SJonathan.Adams@Sun.COM uint8_t se_depth; 1728721SJonathan.Adams@Sun.COM uint8_t se_failed; /* failure reason; FSI_FAIL_* */ 1738721SJonathan.Adams@Sun.COM uint8_t se_panic; 1748721SJonathan.Adams@Sun.COM uintptr_t se_stack[1]; 1758721SJonathan.Adams@Sun.COM } stacks_entry_t; 1768721SJonathan.Adams@Sun.COM #define STACKS_ENTRY_SIZE(x) OFFSETOF(stacks_entry_t, se_stack[(x)]) 1778721SJonathan.Adams@Sun.COM 1788721SJonathan.Adams@Sun.COM #define STACKS_HSIZE 127 1798721SJonathan.Adams@Sun.COM 1808721SJonathan.Adams@Sun.COM /* Maximum stack depth reported in stacks */ 1818721SJonathan.Adams@Sun.COM #define STACKS_MAX_DEPTH 254 1828721SJonathan.Adams@Sun.COM 1838721SJonathan.Adams@Sun.COM typedef struct stacks_info { 1848721SJonathan.Adams@Sun.COM size_t si_count; /* total stacks_entry_ts (incl dups) */ 1858721SJonathan.Adams@Sun.COM size_t si_entries; /* # entries in hash table */ 1868721SJonathan.Adams@Sun.COM stacks_entry_t **si_hash; /* hash table */ 1878721SJonathan.Adams@Sun.COM findstack_info_t si_fsi; /* transient callback state */ 1888721SJonathan.Adams@Sun.COM } stacks_info_t; 1898721SJonathan.Adams@Sun.COM 1908721SJonathan.Adams@Sun.COM /* global state cached between invocations */ 1918721SJonathan.Adams@Sun.COM #define STACKS_STATE_CLEAN 0 1928721SJonathan.Adams@Sun.COM #define STACKS_STATE_DIRTY 1 1938721SJonathan.Adams@Sun.COM #define STACKS_STATE_DONE 2 1948721SJonathan.Adams@Sun.COM static uint_t stacks_state = STACKS_STATE_CLEAN; 1958721SJonathan.Adams@Sun.COM static stacks_entry_t **stacks_hash; 1968721SJonathan.Adams@Sun.COM static stacks_entry_t **stacks_array; 1978721SJonathan.Adams@Sun.COM static size_t stacks_array_size; 1988721SJonathan.Adams@Sun.COM 1998721SJonathan.Adams@Sun.COM size_t 2008721SJonathan.Adams@Sun.COM stacks_hash_entry(stacks_entry_t *sep) 2018721SJonathan.Adams@Sun.COM { 2028721SJonathan.Adams@Sun.COM size_t depth = sep->se_depth; 2038721SJonathan.Adams@Sun.COM uintptr_t *stack = sep->se_stack; 2048721SJonathan.Adams@Sun.COM 2058721SJonathan.Adams@Sun.COM uint64_t total = depth; 2068721SJonathan.Adams@Sun.COM 2078721SJonathan.Adams@Sun.COM while (depth > 0) { 2088721SJonathan.Adams@Sun.COM total += *stack; 2098721SJonathan.Adams@Sun.COM stack++; depth--; 2108721SJonathan.Adams@Sun.COM } 2118721SJonathan.Adams@Sun.COM 2128721SJonathan.Adams@Sun.COM return (total % STACKS_HSIZE); 2138721SJonathan.Adams@Sun.COM } 2148721SJonathan.Adams@Sun.COM 2158721SJonathan.Adams@Sun.COM /* 2168721SJonathan.Adams@Sun.COM * This is used to both compare stacks for equality and to sort the final 2178721SJonathan.Adams@Sun.COM * list of unique stacks. forsort specifies the latter behavior, which 2188721SJonathan.Adams@Sun.COM * additionally: 2198721SJonathan.Adams@Sun.COM * compares se_count, and 2208721SJonathan.Adams@Sun.COM * sorts the stacks by text function name. 2218721SJonathan.Adams@Sun.COM * 2228721SJonathan.Adams@Sun.COM * The equality test is independent of se_count, and doesn't care about 2238721SJonathan.Adams@Sun.COM * relative ordering, so we don't do the extra work of looking up symbols 2248721SJonathan.Adams@Sun.COM * for the stack addresses. 2258721SJonathan.Adams@Sun.COM */ 2260Sstevel@tonic-gate int 2278721SJonathan.Adams@Sun.COM stacks_entry_comp_impl(stacks_entry_t *l, stacks_entry_t *r, 2288721SJonathan.Adams@Sun.COM uint_t forsort) 2290Sstevel@tonic-gate { 2308721SJonathan.Adams@Sun.COM int idx; 2318721SJonathan.Adams@Sun.COM 2328721SJonathan.Adams@Sun.COM int depth = MIN(l->se_depth, r->se_depth); 2338721SJonathan.Adams@Sun.COM 2348721SJonathan.Adams@Sun.COM /* no matter what, panic stacks come last. */ 2358721SJonathan.Adams@Sun.COM if (l->se_panic > r->se_panic) 2368721SJonathan.Adams@Sun.COM return (1); 2378721SJonathan.Adams@Sun.COM if (l->se_panic < r->se_panic) 2388721SJonathan.Adams@Sun.COM return (-1); 2398721SJonathan.Adams@Sun.COM 2408721SJonathan.Adams@Sun.COM if (forsort) { 2418721SJonathan.Adams@Sun.COM /* put large counts earlier */ 2428721SJonathan.Adams@Sun.COM if (l->se_count > r->se_count) 2438721SJonathan.Adams@Sun.COM return (-1); 2448721SJonathan.Adams@Sun.COM if (l->se_count < r->se_count) 2458721SJonathan.Adams@Sun.COM return (1); 2468721SJonathan.Adams@Sun.COM } 2478721SJonathan.Adams@Sun.COM 2488721SJonathan.Adams@Sun.COM if (l->se_tstate > r->se_tstate) 2498721SJonathan.Adams@Sun.COM return (1); 2508721SJonathan.Adams@Sun.COM if (l->se_tstate < r->se_tstate) 2518721SJonathan.Adams@Sun.COM return (-1); 2528721SJonathan.Adams@Sun.COM 2538721SJonathan.Adams@Sun.COM if (l->se_failed > r->se_failed) 2548721SJonathan.Adams@Sun.COM return (1); 2558721SJonathan.Adams@Sun.COM if (l->se_failed < r->se_failed) 2568721SJonathan.Adams@Sun.COM return (-1); 2578721SJonathan.Adams@Sun.COM 2588721SJonathan.Adams@Sun.COM for (idx = 0; idx < depth; idx++) { 2598721SJonathan.Adams@Sun.COM char lbuf[MDB_SYM_NAMLEN]; 2608721SJonathan.Adams@Sun.COM char rbuf[MDB_SYM_NAMLEN]; 2618721SJonathan.Adams@Sun.COM 2628721SJonathan.Adams@Sun.COM int rval; 2638721SJonathan.Adams@Sun.COM uintptr_t laddr = l->se_stack[idx]; 2648721SJonathan.Adams@Sun.COM uintptr_t raddr = r->se_stack[idx]; 2658721SJonathan.Adams@Sun.COM 2668721SJonathan.Adams@Sun.COM if (laddr == raddr) 2678721SJonathan.Adams@Sun.COM continue; 2688721SJonathan.Adams@Sun.COM 2698721SJonathan.Adams@Sun.COM if (forsort && 2708721SJonathan.Adams@Sun.COM mdb_lookup_by_addr(laddr, MDB_SYM_FUZZY, 2718721SJonathan.Adams@Sun.COM lbuf, sizeof (lbuf), NULL) != -1 && 2728721SJonathan.Adams@Sun.COM mdb_lookup_by_addr(raddr, MDB_SYM_FUZZY, 2738721SJonathan.Adams@Sun.COM rbuf, sizeof (rbuf), NULL) != -1 && 2748721SJonathan.Adams@Sun.COM (rval = strcmp(lbuf, rbuf)) != 0) 2758721SJonathan.Adams@Sun.COM return (rval); 2768721SJonathan.Adams@Sun.COM 2778721SJonathan.Adams@Sun.COM if (laddr > raddr) 2788721SJonathan.Adams@Sun.COM return (1); 2798721SJonathan.Adams@Sun.COM return (-1); 2800Sstevel@tonic-gate } 2818721SJonathan.Adams@Sun.COM 2828721SJonathan.Adams@Sun.COM if (l->se_overflow > r->se_overflow) 2838721SJonathan.Adams@Sun.COM return (-1); 2848721SJonathan.Adams@Sun.COM if (l->se_overflow < r->se_overflow) 2858721SJonathan.Adams@Sun.COM return (1); 2868721SJonathan.Adams@Sun.COM 2878721SJonathan.Adams@Sun.COM if (l->se_depth > r->se_depth) 2888721SJonathan.Adams@Sun.COM return (1); 2898721SJonathan.Adams@Sun.COM if (l->se_depth < r->se_depth) 2908721SJonathan.Adams@Sun.COM return (-1); 2918721SJonathan.Adams@Sun.COM 2928721SJonathan.Adams@Sun.COM if (l->se_sobj_ops > r->se_sobj_ops) 2938721SJonathan.Adams@Sun.COM return (1); 2948721SJonathan.Adams@Sun.COM if (l->se_sobj_ops < r->se_sobj_ops) 2958721SJonathan.Adams@Sun.COM return (-1); 2968721SJonathan.Adams@Sun.COM 2978721SJonathan.Adams@Sun.COM return (0); 2988721SJonathan.Adams@Sun.COM } 2998721SJonathan.Adams@Sun.COM 3008721SJonathan.Adams@Sun.COM int 3018721SJonathan.Adams@Sun.COM stacks_entry_comp(const void *l_arg, const void *r_arg) 3028721SJonathan.Adams@Sun.COM { 3038721SJonathan.Adams@Sun.COM stacks_entry_t * const *lp = l_arg; 3048721SJonathan.Adams@Sun.COM stacks_entry_t * const *rp = r_arg; 3058721SJonathan.Adams@Sun.COM 3068721SJonathan.Adams@Sun.COM return (stacks_entry_comp_impl(*lp, *rp, 1)); 3078721SJonathan.Adams@Sun.COM } 3088721SJonathan.Adams@Sun.COM 3098721SJonathan.Adams@Sun.COM void 3108721SJonathan.Adams@Sun.COM stacks_cleanup(int force) 3118721SJonathan.Adams@Sun.COM { 3128721SJonathan.Adams@Sun.COM int idx = 0; 3138721SJonathan.Adams@Sun.COM stacks_entry_t *cur, *next; 3148721SJonathan.Adams@Sun.COM 3158721SJonathan.Adams@Sun.COM if (stacks_state == STACKS_STATE_CLEAN) 3168721SJonathan.Adams@Sun.COM return; 3178721SJonathan.Adams@Sun.COM 3188721SJonathan.Adams@Sun.COM if (!force && stacks_state == STACKS_STATE_DONE) 3198721SJonathan.Adams@Sun.COM return; 3208721SJonathan.Adams@Sun.COM 3218721SJonathan.Adams@Sun.COM /* 3228721SJonathan.Adams@Sun.COM * Until the array is sorted and stable, stacks_hash will be non-NULL. 3238721SJonathan.Adams@Sun.COM * This way, we can get at all of the data, even if qsort() was 3248721SJonathan.Adams@Sun.COM * interrupted while mucking with the array. 3258721SJonathan.Adams@Sun.COM */ 3268721SJonathan.Adams@Sun.COM if (stacks_hash != NULL) { 3278721SJonathan.Adams@Sun.COM for (idx = 0; idx < STACKS_HSIZE; idx++) { 3288721SJonathan.Adams@Sun.COM while ((cur = stacks_hash[idx]) != NULL) { 3298721SJonathan.Adams@Sun.COM while ((next = cur->se_dup) != NULL) { 3308721SJonathan.Adams@Sun.COM cur->se_dup = next->se_dup; 3318721SJonathan.Adams@Sun.COM mdb_free(next, 3328721SJonathan.Adams@Sun.COM STACKS_ENTRY_SIZE(next->se_depth)); 3338721SJonathan.Adams@Sun.COM } 3348721SJonathan.Adams@Sun.COM next = cur->se_next; 3358721SJonathan.Adams@Sun.COM stacks_hash[idx] = next; 3368721SJonathan.Adams@Sun.COM mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth)); 3378721SJonathan.Adams@Sun.COM } 3388721SJonathan.Adams@Sun.COM } 3398721SJonathan.Adams@Sun.COM if (stacks_array != NULL) 3408721SJonathan.Adams@Sun.COM mdb_free(stacks_array, 3418721SJonathan.Adams@Sun.COM stacks_array_size * sizeof (*stacks_array)); 3428721SJonathan.Adams@Sun.COM 3438721SJonathan.Adams@Sun.COM } else if (stacks_array != NULL) { 3448721SJonathan.Adams@Sun.COM for (idx = 0; idx < stacks_array_size; idx++) { 3458721SJonathan.Adams@Sun.COM if ((cur = stacks_array[idx]) != NULL) { 3468721SJonathan.Adams@Sun.COM while ((next = cur->se_dup) != NULL) { 3478721SJonathan.Adams@Sun.COM cur->se_dup = next->se_dup; 3488721SJonathan.Adams@Sun.COM mdb_free(next, 3498721SJonathan.Adams@Sun.COM STACKS_ENTRY_SIZE(next->se_depth)); 3508721SJonathan.Adams@Sun.COM } 3518721SJonathan.Adams@Sun.COM stacks_array[idx] = NULL; 3528721SJonathan.Adams@Sun.COM mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth)); 3538721SJonathan.Adams@Sun.COM } 3548721SJonathan.Adams@Sun.COM } 3558721SJonathan.Adams@Sun.COM mdb_free(stacks_array, 3568721SJonathan.Adams@Sun.COM stacks_array_size * sizeof (*stacks_array)); 3578721SJonathan.Adams@Sun.COM } 3588721SJonathan.Adams@Sun.COM 359*12902SBryan.Cantrill@Sun.COM stacks_findstack_cleanup(); 360*12902SBryan.Cantrill@Sun.COM 3618721SJonathan.Adams@Sun.COM stacks_array_size = 0; 3628721SJonathan.Adams@Sun.COM stacks_state = STACKS_STATE_CLEAN; 3638721SJonathan.Adams@Sun.COM } 3648721SJonathan.Adams@Sun.COM 3658721SJonathan.Adams@Sun.COM /*ARGSUSED*/ 3668721SJonathan.Adams@Sun.COM int 3678721SJonathan.Adams@Sun.COM stacks_thread_cb(uintptr_t addr, const void *ignored, void *cbarg) 3688721SJonathan.Adams@Sun.COM { 3698721SJonathan.Adams@Sun.COM stacks_info_t *sip = cbarg; 3708721SJonathan.Adams@Sun.COM findstack_info_t *fsip = &sip->si_fsi; 3718721SJonathan.Adams@Sun.COM 3728721SJonathan.Adams@Sun.COM stacks_entry_t **sepp, *nsep, *sep; 3738721SJonathan.Adams@Sun.COM int idx; 3748721SJonathan.Adams@Sun.COM size_t depth; 3758721SJonathan.Adams@Sun.COM 376*12902SBryan.Cantrill@Sun.COM if (stacks_findstack(addr, fsip, 0) != DCMD_OK && 3778721SJonathan.Adams@Sun.COM fsip->fsi_failed == FSI_FAIL_BADTHREAD) { 3788721SJonathan.Adams@Sun.COM mdb_warn("couldn't read thread at %p\n", addr); 3798721SJonathan.Adams@Sun.COM return (WALK_NEXT); 3808721SJonathan.Adams@Sun.COM } 3818721SJonathan.Adams@Sun.COM 3828721SJonathan.Adams@Sun.COM sip->si_count++; 3838721SJonathan.Adams@Sun.COM 3848721SJonathan.Adams@Sun.COM depth = fsip->fsi_depth; 3858721SJonathan.Adams@Sun.COM nsep = mdb_zalloc(STACKS_ENTRY_SIZE(depth), UM_SLEEP); 3868721SJonathan.Adams@Sun.COM nsep->se_thread = addr; 3878721SJonathan.Adams@Sun.COM nsep->se_sp = fsip->fsi_sp; 3888721SJonathan.Adams@Sun.COM nsep->se_sobj_ops = fsip->fsi_sobj_ops; 3898721SJonathan.Adams@Sun.COM nsep->se_tstate = fsip->fsi_tstate; 3908721SJonathan.Adams@Sun.COM nsep->se_count = 1; 3918721SJonathan.Adams@Sun.COM nsep->se_overflow = fsip->fsi_overflow; 3928721SJonathan.Adams@Sun.COM nsep->se_depth = depth; 3938721SJonathan.Adams@Sun.COM nsep->se_failed = fsip->fsi_failed; 3948721SJonathan.Adams@Sun.COM nsep->se_panic = fsip->fsi_panic; 3958721SJonathan.Adams@Sun.COM 3968721SJonathan.Adams@Sun.COM for (idx = 0; idx < depth; idx++) 3978721SJonathan.Adams@Sun.COM nsep->se_stack[idx] = fsip->fsi_stack[idx]; 3988721SJonathan.Adams@Sun.COM 3998721SJonathan.Adams@Sun.COM for (sepp = &sip->si_hash[stacks_hash_entry(nsep)]; 4008721SJonathan.Adams@Sun.COM (sep = *sepp) != NULL; 4018721SJonathan.Adams@Sun.COM sepp = &sep->se_next) { 4028721SJonathan.Adams@Sun.COM 4038721SJonathan.Adams@Sun.COM if (stacks_entry_comp_impl(sep, nsep, 0) != 0) 4048721SJonathan.Adams@Sun.COM continue; 4058721SJonathan.Adams@Sun.COM 4068721SJonathan.Adams@Sun.COM nsep->se_dup = sep->se_dup; 4078721SJonathan.Adams@Sun.COM sep->se_dup = nsep; 4088721SJonathan.Adams@Sun.COM sep->se_count++; 4098721SJonathan.Adams@Sun.COM return (WALK_NEXT); 4108721SJonathan.Adams@Sun.COM } 4118721SJonathan.Adams@Sun.COM 4128721SJonathan.Adams@Sun.COM nsep->se_next = NULL; 4138721SJonathan.Adams@Sun.COM *sepp = nsep; 4148721SJonathan.Adams@Sun.COM sip->si_entries++; 4158721SJonathan.Adams@Sun.COM 4168721SJonathan.Adams@Sun.COM return (WALK_NEXT); 4178721SJonathan.Adams@Sun.COM } 4188721SJonathan.Adams@Sun.COM 4198721SJonathan.Adams@Sun.COM int 42010889SJonathan.Adams@Sun.COM stacks_run_tlist(mdb_pipe_t *tlist, stacks_info_t *si) 42110889SJonathan.Adams@Sun.COM { 42210889SJonathan.Adams@Sun.COM size_t idx; 42310889SJonathan.Adams@Sun.COM size_t found = 0; 42410889SJonathan.Adams@Sun.COM int ret; 42510889SJonathan.Adams@Sun.COM 42610889SJonathan.Adams@Sun.COM for (idx = 0; idx < tlist->pipe_len; idx++) { 42710889SJonathan.Adams@Sun.COM uintptr_t addr = tlist->pipe_data[idx]; 42810889SJonathan.Adams@Sun.COM 42910889SJonathan.Adams@Sun.COM found++; 43010889SJonathan.Adams@Sun.COM 431*12902SBryan.Cantrill@Sun.COM ret = stacks_thread_cb(addr, NULL, si); 43210889SJonathan.Adams@Sun.COM if (ret == WALK_DONE) 43310889SJonathan.Adams@Sun.COM break; 43410889SJonathan.Adams@Sun.COM if (ret != WALK_NEXT) 43510889SJonathan.Adams@Sun.COM return (-1); 43610889SJonathan.Adams@Sun.COM } 43710889SJonathan.Adams@Sun.COM 43810889SJonathan.Adams@Sun.COM if (found) 43910889SJonathan.Adams@Sun.COM return (0); 44010889SJonathan.Adams@Sun.COM return (-1); 44110889SJonathan.Adams@Sun.COM } 44210889SJonathan.Adams@Sun.COM 44310889SJonathan.Adams@Sun.COM int 44410889SJonathan.Adams@Sun.COM stacks_run(int verbose, mdb_pipe_t *tlist) 4458721SJonathan.Adams@Sun.COM { 4468721SJonathan.Adams@Sun.COM stacks_info_t si; 4478721SJonathan.Adams@Sun.COM findstack_info_t *fsip = &si.si_fsi; 4488721SJonathan.Adams@Sun.COM size_t idx; 4498721SJonathan.Adams@Sun.COM stacks_entry_t **cur; 4508721SJonathan.Adams@Sun.COM 4518721SJonathan.Adams@Sun.COM bzero(&si, sizeof (si)); 4528721SJonathan.Adams@Sun.COM 4538721SJonathan.Adams@Sun.COM stacks_state = STACKS_STATE_DIRTY; 4548721SJonathan.Adams@Sun.COM 4558721SJonathan.Adams@Sun.COM stacks_hash = si.si_hash = 4568721SJonathan.Adams@Sun.COM mdb_zalloc(STACKS_HSIZE * sizeof (*si.si_hash), UM_SLEEP); 4578721SJonathan.Adams@Sun.COM si.si_entries = 0; 4588721SJonathan.Adams@Sun.COM si.si_count = 0; 4598721SJonathan.Adams@Sun.COM 4608721SJonathan.Adams@Sun.COM fsip->fsi_max_depth = STACKS_MAX_DEPTH; 4618721SJonathan.Adams@Sun.COM fsip->fsi_stack = 4628721SJonathan.Adams@Sun.COM mdb_alloc(fsip->fsi_max_depth * sizeof (*fsip->fsi_stack), 4638721SJonathan.Adams@Sun.COM UM_SLEEP | UM_GC); 4648721SJonathan.Adams@Sun.COM 4658721SJonathan.Adams@Sun.COM if (verbose) 4668721SJonathan.Adams@Sun.COM mdb_warn("stacks: processing kernel threads\n"); 4678721SJonathan.Adams@Sun.COM 46810889SJonathan.Adams@Sun.COM if (tlist != NULL) { 46910889SJonathan.Adams@Sun.COM if (stacks_run_tlist(tlist, &si)) 47010889SJonathan.Adams@Sun.COM return (DCMD_ERR); 47110889SJonathan.Adams@Sun.COM } else { 47210889SJonathan.Adams@Sun.COM if (mdb_walk("thread", stacks_thread_cb, &si) != 0) { 47310889SJonathan.Adams@Sun.COM mdb_warn("cannot walk \"thread\""); 47410889SJonathan.Adams@Sun.COM return (DCMD_ERR); 47510889SJonathan.Adams@Sun.COM } 4768721SJonathan.Adams@Sun.COM } 4778721SJonathan.Adams@Sun.COM 4788721SJonathan.Adams@Sun.COM if (verbose) 4798721SJonathan.Adams@Sun.COM mdb_warn("stacks: %d unique stacks / %d threads\n", 4808721SJonathan.Adams@Sun.COM si.si_entries, si.si_count); 4818721SJonathan.Adams@Sun.COM 4828721SJonathan.Adams@Sun.COM stacks_array_size = si.si_entries; 4838721SJonathan.Adams@Sun.COM stacks_array = 4848721SJonathan.Adams@Sun.COM mdb_zalloc(si.si_entries * sizeof (*stacks_array), UM_SLEEP); 4858721SJonathan.Adams@Sun.COM cur = stacks_array; 4868721SJonathan.Adams@Sun.COM for (idx = 0; idx < STACKS_HSIZE; idx++) { 4878721SJonathan.Adams@Sun.COM stacks_entry_t *sep; 4888721SJonathan.Adams@Sun.COM for (sep = si.si_hash[idx]; sep != NULL; sep = sep->se_next) 4898721SJonathan.Adams@Sun.COM *(cur++) = sep; 4908721SJonathan.Adams@Sun.COM } 4918721SJonathan.Adams@Sun.COM 4928721SJonathan.Adams@Sun.COM if (cur != stacks_array + si.si_entries) { 4938721SJonathan.Adams@Sun.COM mdb_warn("stacks: miscounted array size (%d != size: %d)\n", 4948721SJonathan.Adams@Sun.COM (cur - stacks_array), stacks_array_size); 4958721SJonathan.Adams@Sun.COM return (DCMD_ERR); 4968721SJonathan.Adams@Sun.COM } 4978721SJonathan.Adams@Sun.COM qsort(stacks_array, si.si_entries, sizeof (*stacks_array), 4988721SJonathan.Adams@Sun.COM stacks_entry_comp); 4998721SJonathan.Adams@Sun.COM 5008721SJonathan.Adams@Sun.COM /* Now that we're done, free the hash table */ 5018721SJonathan.Adams@Sun.COM stacks_hash = NULL; 5028721SJonathan.Adams@Sun.COM mdb_free(si.si_hash, STACKS_HSIZE * sizeof (*si.si_hash)); 5038721SJonathan.Adams@Sun.COM 50410889SJonathan.Adams@Sun.COM if (tlist == NULL) 50510889SJonathan.Adams@Sun.COM stacks_state = STACKS_STATE_DONE; 5068721SJonathan.Adams@Sun.COM 5078721SJonathan.Adams@Sun.COM if (verbose) 5088721SJonathan.Adams@Sun.COM mdb_warn("stacks: done\n"); 5090Sstevel@tonic-gate 5100Sstevel@tonic-gate return (DCMD_OK); 5110Sstevel@tonic-gate } 5128721SJonathan.Adams@Sun.COM 5138721SJonathan.Adams@Sun.COM static int 5148721SJonathan.Adams@Sun.COM stacks_has_caller(stacks_entry_t *sep, uintptr_t addr) 5158721SJonathan.Adams@Sun.COM { 5168721SJonathan.Adams@Sun.COM uintptr_t laddr = addr; 5178721SJonathan.Adams@Sun.COM uintptr_t haddr = addr + 1; 5188721SJonathan.Adams@Sun.COM int idx; 5198721SJonathan.Adams@Sun.COM char c[MDB_SYM_NAMLEN]; 5208721SJonathan.Adams@Sun.COM GElf_Sym sym; 5218721SJonathan.Adams@Sun.COM 5228721SJonathan.Adams@Sun.COM if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY, 5238721SJonathan.Adams@Sun.COM c, sizeof (c), &sym) != -1 && 5248721SJonathan.Adams@Sun.COM addr == (uintptr_t)sym.st_value) { 5258721SJonathan.Adams@Sun.COM laddr = (uintptr_t)sym.st_value; 5268721SJonathan.Adams@Sun.COM haddr = (uintptr_t)sym.st_value + sym.st_size; 5278721SJonathan.Adams@Sun.COM } 5288721SJonathan.Adams@Sun.COM 5298721SJonathan.Adams@Sun.COM for (idx = 0; idx < sep->se_depth; idx++) 5308721SJonathan.Adams@Sun.COM if (sep->se_stack[idx] >= laddr && sep->se_stack[idx] < haddr) 5318721SJonathan.Adams@Sun.COM return (1); 5328721SJonathan.Adams@Sun.COM 5338721SJonathan.Adams@Sun.COM return (0); 5348721SJonathan.Adams@Sun.COM } 5358721SJonathan.Adams@Sun.COM 536*12902SBryan.Cantrill@Sun.COM static int 537*12902SBryan.Cantrill@Sun.COM stacks_has_module(stacks_entry_t *sep, stacks_module_t *mp) 5388742Sgap@sun.com { 539*12902SBryan.Cantrill@Sun.COM int idx; 5408742Sgap@sun.com 541*12902SBryan.Cantrill@Sun.COM for (idx = 0; idx < sep->se_depth; idx++) { 542*12902SBryan.Cantrill@Sun.COM if (sep->se_stack[idx] >= mp->sm_text && 543*12902SBryan.Cantrill@Sun.COM sep->se_stack[idx] < mp->sm_text + mp->sm_size) 544*12902SBryan.Cantrill@Sun.COM return (1); 5458742Sgap@sun.com } 5468742Sgap@sun.com 547*12902SBryan.Cantrill@Sun.COM return (0); 5488742Sgap@sun.com } 5498742Sgap@sun.com 5508742Sgap@sun.com static int 551*12902SBryan.Cantrill@Sun.COM stacks_module_find(const char *name, stacks_module_t *mp) 5528742Sgap@sun.com { 553*12902SBryan.Cantrill@Sun.COM (void) strncpy(mp->sm_name, name, sizeof (mp->sm_name)); 5548742Sgap@sun.com 555*12902SBryan.Cantrill@Sun.COM if (stacks_module(mp) != 0) 556*12902SBryan.Cantrill@Sun.COM return (-1); 5578742Sgap@sun.com 558*12902SBryan.Cantrill@Sun.COM if (mp->sm_size == 0) { 559*12902SBryan.Cantrill@Sun.COM mdb_warn("stacks: module \"%s\" is unknown\n", name); 560*12902SBryan.Cantrill@Sun.COM return (-1); 561*12902SBryan.Cantrill@Sun.COM } 562*12902SBryan.Cantrill@Sun.COM 5638742Sgap@sun.com return (0); 5648742Sgap@sun.com } 5658742Sgap@sun.com 5668721SJonathan.Adams@Sun.COM static int 5678721SJonathan.Adams@Sun.COM uintptrcomp(const void *lp, const void *rp) 5688721SJonathan.Adams@Sun.COM { 5698721SJonathan.Adams@Sun.COM uintptr_t lhs = *(const uintptr_t *)lp; 5708721SJonathan.Adams@Sun.COM uintptr_t rhs = *(const uintptr_t *)rp; 5718721SJonathan.Adams@Sun.COM if (lhs > rhs) 5728721SJonathan.Adams@Sun.COM return (1); 5738721SJonathan.Adams@Sun.COM if (lhs < rhs) 5748721SJonathan.Adams@Sun.COM return (-1); 5758721SJonathan.Adams@Sun.COM return (0); 5768721SJonathan.Adams@Sun.COM } 5778721SJonathan.Adams@Sun.COM 5788721SJonathan.Adams@Sun.COM /*ARGSUSED*/ 5798721SJonathan.Adams@Sun.COM int 5808721SJonathan.Adams@Sun.COM stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 5818721SJonathan.Adams@Sun.COM { 5828721SJonathan.Adams@Sun.COM size_t idx; 5838721SJonathan.Adams@Sun.COM 5848721SJonathan.Adams@Sun.COM char *seen = NULL; 5858721SJonathan.Adams@Sun.COM 5868721SJonathan.Adams@Sun.COM const char *caller_str = NULL; 5878721SJonathan.Adams@Sun.COM const char *excl_caller_str = NULL; 5888721SJonathan.Adams@Sun.COM uintptr_t caller = 0, excl_caller = 0; 5898742Sgap@sun.com const char *module_str = NULL; 5908742Sgap@sun.com const char *excl_module_str = NULL; 591*12902SBryan.Cantrill@Sun.COM stacks_module_t module, excl_module; 5928721SJonathan.Adams@Sun.COM const char *sobj = NULL; 5938721SJonathan.Adams@Sun.COM const char *excl_sobj = NULL; 5948721SJonathan.Adams@Sun.COM uintptr_t sobj_ops = 0, excl_sobj_ops = 0; 5958721SJonathan.Adams@Sun.COM const char *tstate_str = NULL; 5968721SJonathan.Adams@Sun.COM const char *excl_tstate_str = NULL; 5978721SJonathan.Adams@Sun.COM uint_t tstate = -1U; 5988721SJonathan.Adams@Sun.COM uint_t excl_tstate = -1U; 59910889SJonathan.Adams@Sun.COM uint_t printed = 0; 6008721SJonathan.Adams@Sun.COM 6018721SJonathan.Adams@Sun.COM uint_t all = 0; 6028721SJonathan.Adams@Sun.COM uint_t force = 0; 6038721SJonathan.Adams@Sun.COM uint_t interesting = 0; 6048721SJonathan.Adams@Sun.COM uint_t verbose = 0; 6058721SJonathan.Adams@Sun.COM 6068721SJonathan.Adams@Sun.COM /* 6078721SJonathan.Adams@Sun.COM * We have a slight behavior difference between having piped 6088721SJonathan.Adams@Sun.COM * input and 'addr::stacks'. Without a pipe, we assume the 6098721SJonathan.Adams@Sun.COM * thread pointer given is a representative thread, and so 6108721SJonathan.Adams@Sun.COM * we include all similar threads in the system in our output. 6118721SJonathan.Adams@Sun.COM * 6128721SJonathan.Adams@Sun.COM * With a pipe, we filter down to just the threads in our 6138721SJonathan.Adams@Sun.COM * input. 6148721SJonathan.Adams@Sun.COM */ 6158721SJonathan.Adams@Sun.COM uint_t addrspec = (flags & DCMD_ADDRSPEC); 6168721SJonathan.Adams@Sun.COM uint_t only_matching = addrspec && (flags & DCMD_PIPE); 6178721SJonathan.Adams@Sun.COM 6188721SJonathan.Adams@Sun.COM mdb_pipe_t p; 6198721SJonathan.Adams@Sun.COM 620*12902SBryan.Cantrill@Sun.COM bzero(&module, sizeof (module)); 621*12902SBryan.Cantrill@Sun.COM bzero(&excl_module, sizeof (excl_module)); 622*12902SBryan.Cantrill@Sun.COM 6238721SJonathan.Adams@Sun.COM if (mdb_getopts(argc, argv, 6248721SJonathan.Adams@Sun.COM 'a', MDB_OPT_SETBITS, TRUE, &all, 6258721SJonathan.Adams@Sun.COM 'f', MDB_OPT_SETBITS, TRUE, &force, 6268721SJonathan.Adams@Sun.COM 'i', MDB_OPT_SETBITS, TRUE, &interesting, 6278721SJonathan.Adams@Sun.COM 'v', MDB_OPT_SETBITS, TRUE, &verbose, 6288721SJonathan.Adams@Sun.COM 'c', MDB_OPT_STR, &caller_str, 6298721SJonathan.Adams@Sun.COM 'C', MDB_OPT_STR, &excl_caller_str, 6308742Sgap@sun.com 'm', MDB_OPT_STR, &module_str, 6318742Sgap@sun.com 'M', MDB_OPT_STR, &excl_module_str, 6328721SJonathan.Adams@Sun.COM 's', MDB_OPT_STR, &sobj, 6338721SJonathan.Adams@Sun.COM 'S', MDB_OPT_STR, &excl_sobj, 6348721SJonathan.Adams@Sun.COM 't', MDB_OPT_STR, &tstate_str, 6358721SJonathan.Adams@Sun.COM 'T', MDB_OPT_STR, &excl_tstate_str, 6368721SJonathan.Adams@Sun.COM NULL) != argc) 6378721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 6388721SJonathan.Adams@Sun.COM 6398721SJonathan.Adams@Sun.COM if (interesting) { 6408721SJonathan.Adams@Sun.COM if (sobj != NULL || excl_sobj != NULL || 6418721SJonathan.Adams@Sun.COM tstate_str != NULL || excl_tstate_str != NULL) { 6428721SJonathan.Adams@Sun.COM mdb_warn( 6438721SJonathan.Adams@Sun.COM "stacks: -i is incompatible with -[sStT]\n"); 6448721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 6458721SJonathan.Adams@Sun.COM } 6468721SJonathan.Adams@Sun.COM excl_sobj = "CV"; 6478721SJonathan.Adams@Sun.COM excl_tstate_str = "FREE"; 6488721SJonathan.Adams@Sun.COM } 6498721SJonathan.Adams@Sun.COM 6508721SJonathan.Adams@Sun.COM if (caller_str != NULL) { 6518721SJonathan.Adams@Sun.COM mdb_set_dot(0); 6528721SJonathan.Adams@Sun.COM if (mdb_eval(caller_str) != 0) { 6538721SJonathan.Adams@Sun.COM mdb_warn("stacks: evaluation of \"%s\" failed", 6548721SJonathan.Adams@Sun.COM caller_str); 6558721SJonathan.Adams@Sun.COM return (DCMD_ABORT); 6568721SJonathan.Adams@Sun.COM } 6578721SJonathan.Adams@Sun.COM caller = mdb_get_dot(); 6588721SJonathan.Adams@Sun.COM } 6598721SJonathan.Adams@Sun.COM 6608721SJonathan.Adams@Sun.COM if (excl_caller_str != NULL) { 6618721SJonathan.Adams@Sun.COM mdb_set_dot(0); 6628721SJonathan.Adams@Sun.COM if (mdb_eval(excl_caller_str) != 0) { 6638721SJonathan.Adams@Sun.COM mdb_warn("stacks: evaluation of \"%s\" failed", 6648721SJonathan.Adams@Sun.COM excl_caller_str); 6658721SJonathan.Adams@Sun.COM return (DCMD_ABORT); 6668721SJonathan.Adams@Sun.COM } 6678721SJonathan.Adams@Sun.COM excl_caller = mdb_get_dot(); 6688721SJonathan.Adams@Sun.COM } 6698721SJonathan.Adams@Sun.COM mdb_set_dot(addr); 6708721SJonathan.Adams@Sun.COM 671*12902SBryan.Cantrill@Sun.COM if (module_str != NULL && stacks_module_find(module_str, &module) != 0) 6728742Sgap@sun.com return (DCMD_ABORT); 6738742Sgap@sun.com 6748742Sgap@sun.com if (excl_module_str != NULL && 675*12902SBryan.Cantrill@Sun.COM stacks_module_find(excl_module_str, &excl_module) != 0) 6768742Sgap@sun.com return (DCMD_ABORT); 6778742Sgap@sun.com 678*12902SBryan.Cantrill@Sun.COM if (sobj != NULL && text_to_sobj(sobj, &sobj_ops) != 0) 6798721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 6808721SJonathan.Adams@Sun.COM 681*12902SBryan.Cantrill@Sun.COM if (excl_sobj != NULL && text_to_sobj(excl_sobj, &excl_sobj_ops) != 0) 6828721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 6838721SJonathan.Adams@Sun.COM 6848721SJonathan.Adams@Sun.COM if (sobj_ops != 0 && excl_sobj_ops != 0) { 6858721SJonathan.Adams@Sun.COM mdb_warn("stacks: only one of -s and -S can be specified\n"); 6868721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 6878721SJonathan.Adams@Sun.COM } 6888721SJonathan.Adams@Sun.COM 689*12902SBryan.Cantrill@Sun.COM if (tstate_str != NULL && text_to_tstate(tstate_str, &tstate) != 0) 6908721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 6918742Sgap@sun.com 6928742Sgap@sun.com if (excl_tstate_str != NULL && 6938721SJonathan.Adams@Sun.COM text_to_tstate(excl_tstate_str, &excl_tstate) != 0) 6948721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 6958721SJonathan.Adams@Sun.COM 6968721SJonathan.Adams@Sun.COM if (tstate != -1U && excl_tstate != -1U) { 6978721SJonathan.Adams@Sun.COM mdb_warn("stacks: only one of -t and -T can be specified\n"); 6988721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 6998721SJonathan.Adams@Sun.COM } 7008721SJonathan.Adams@Sun.COM 7018721SJonathan.Adams@Sun.COM /* 7028721SJonathan.Adams@Sun.COM * If there's an address specified, we're going to further filter 7038721SJonathan.Adams@Sun.COM * to only entries which have an address in the input. To reduce 7048721SJonathan.Adams@Sun.COM * overhead (and make the sorted output come out right), we 7058721SJonathan.Adams@Sun.COM * use mdb_get_pipe() to grab the entire pipeline of input, then 7068721SJonathan.Adams@Sun.COM * use qsort() and bsearch() to speed up the search. 7078721SJonathan.Adams@Sun.COM */ 7088721SJonathan.Adams@Sun.COM if (addrspec) { 7098721SJonathan.Adams@Sun.COM mdb_get_pipe(&p); 7108721SJonathan.Adams@Sun.COM if (p.pipe_data == NULL || p.pipe_len == 0) { 7118721SJonathan.Adams@Sun.COM p.pipe_data = &addr; 7128721SJonathan.Adams@Sun.COM p.pipe_len = 1; 7138721SJonathan.Adams@Sun.COM } 7148721SJonathan.Adams@Sun.COM qsort(p.pipe_data, p.pipe_len, sizeof (uintptr_t), 7158721SJonathan.Adams@Sun.COM uintptrcomp); 7168721SJonathan.Adams@Sun.COM 7178721SJonathan.Adams@Sun.COM /* remove any duplicates in the data */ 7188721SJonathan.Adams@Sun.COM idx = 0; 7198721SJonathan.Adams@Sun.COM while (idx < p.pipe_len - 1) { 7208721SJonathan.Adams@Sun.COM uintptr_t *data = &p.pipe_data[idx]; 7218721SJonathan.Adams@Sun.COM size_t len = p.pipe_len - idx; 7228721SJonathan.Adams@Sun.COM 7238721SJonathan.Adams@Sun.COM if (data[0] == data[1]) { 7248721SJonathan.Adams@Sun.COM memmove(data, data + 1, 7258721SJonathan.Adams@Sun.COM (len - 1) * sizeof (*data)); 7268721SJonathan.Adams@Sun.COM p.pipe_len--; 7278721SJonathan.Adams@Sun.COM continue; /* repeat without incrementing idx */ 7288721SJonathan.Adams@Sun.COM } 7298721SJonathan.Adams@Sun.COM idx++; 7308721SJonathan.Adams@Sun.COM } 7318721SJonathan.Adams@Sun.COM 7328721SJonathan.Adams@Sun.COM seen = mdb_zalloc(p.pipe_len, UM_SLEEP | UM_GC); 7338721SJonathan.Adams@Sun.COM } 7348721SJonathan.Adams@Sun.COM 73510889SJonathan.Adams@Sun.COM /* 73610889SJonathan.Adams@Sun.COM * Force a cleanup if we're connected to a live system. Never 73710889SJonathan.Adams@Sun.COM * do a cleanup after the first invocation around the loop. 73810889SJonathan.Adams@Sun.COM */ 73910889SJonathan.Adams@Sun.COM force |= (mdb_get_state() == MDB_STATE_RUNNING); 74010889SJonathan.Adams@Sun.COM if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP) 74110889SJonathan.Adams@Sun.COM force = 0; 74210889SJonathan.Adams@Sun.COM 74310889SJonathan.Adams@Sun.COM stacks_cleanup(force); 74410889SJonathan.Adams@Sun.COM 74510889SJonathan.Adams@Sun.COM if (stacks_state == STACKS_STATE_CLEAN) { 74610889SJonathan.Adams@Sun.COM int res = stacks_run(verbose, addrspec ? &p : NULL); 74710889SJonathan.Adams@Sun.COM if (res != DCMD_OK) 74810889SJonathan.Adams@Sun.COM return (res); 74910889SJonathan.Adams@Sun.COM } 75010889SJonathan.Adams@Sun.COM 7518721SJonathan.Adams@Sun.COM for (idx = 0; idx < stacks_array_size; idx++) { 7528721SJonathan.Adams@Sun.COM stacks_entry_t *sep = stacks_array[idx]; 7538721SJonathan.Adams@Sun.COM stacks_entry_t *cur = sep; 7548721SJonathan.Adams@Sun.COM int frame; 7558721SJonathan.Adams@Sun.COM size_t count = sep->se_count; 7568721SJonathan.Adams@Sun.COM 7578721SJonathan.Adams@Sun.COM if (addrspec) { 7588721SJonathan.Adams@Sun.COM stacks_entry_t *head = NULL, *tail = NULL, *sp; 7598721SJonathan.Adams@Sun.COM size_t foundcount = 0; 7608721SJonathan.Adams@Sun.COM /* 7618721SJonathan.Adams@Sun.COM * We use the now-unused hash chain field se_next to 7628721SJonathan.Adams@Sun.COM * link together the dups which match our list. 7638721SJonathan.Adams@Sun.COM */ 7648721SJonathan.Adams@Sun.COM for (sp = sep; sp != NULL; sp = sp->se_dup) { 7658721SJonathan.Adams@Sun.COM uintptr_t *entry = bsearch(&sp->se_thread, 7668721SJonathan.Adams@Sun.COM p.pipe_data, p.pipe_len, sizeof (uintptr_t), 7678721SJonathan.Adams@Sun.COM uintptrcomp); 7688721SJonathan.Adams@Sun.COM if (entry != NULL) { 7698721SJonathan.Adams@Sun.COM foundcount++; 7708721SJonathan.Adams@Sun.COM seen[entry - p.pipe_data]++; 7718721SJonathan.Adams@Sun.COM if (head == NULL) 7728721SJonathan.Adams@Sun.COM head = sp; 7738721SJonathan.Adams@Sun.COM else 7748721SJonathan.Adams@Sun.COM tail->se_next = sp; 7758721SJonathan.Adams@Sun.COM tail = sp; 7768721SJonathan.Adams@Sun.COM sp->se_next = NULL; 7778721SJonathan.Adams@Sun.COM } 7788721SJonathan.Adams@Sun.COM } 7798721SJonathan.Adams@Sun.COM if (head == NULL) 7808721SJonathan.Adams@Sun.COM continue; /* no match, skip entry */ 7818721SJonathan.Adams@Sun.COM 7828721SJonathan.Adams@Sun.COM if (only_matching) { 7838721SJonathan.Adams@Sun.COM cur = sep = head; 7848721SJonathan.Adams@Sun.COM count = foundcount; 7858721SJonathan.Adams@Sun.COM } 7868721SJonathan.Adams@Sun.COM } 7878721SJonathan.Adams@Sun.COM 7888721SJonathan.Adams@Sun.COM if (caller != 0 && !stacks_has_caller(sep, caller)) 7898721SJonathan.Adams@Sun.COM continue; 790*12902SBryan.Cantrill@Sun.COM 7918721SJonathan.Adams@Sun.COM if (excl_caller != 0 && stacks_has_caller(sep, excl_caller)) 7928721SJonathan.Adams@Sun.COM continue; 793*12902SBryan.Cantrill@Sun.COM 794*12902SBryan.Cantrill@Sun.COM if (module.sm_size != 0 && !stacks_has_module(sep, &module)) 7958742Sgap@sun.com continue; 796*12902SBryan.Cantrill@Sun.COM 797*12902SBryan.Cantrill@Sun.COM if (excl_module.sm_size != 0 && 798*12902SBryan.Cantrill@Sun.COM stacks_has_module(sep, &excl_module)) 7998742Sgap@sun.com continue; 8008721SJonathan.Adams@Sun.COM 8018721SJonathan.Adams@Sun.COM if (tstate != -1U) { 8028721SJonathan.Adams@Sun.COM if (tstate == TSTATE_PANIC) { 8038721SJonathan.Adams@Sun.COM if (!sep->se_panic) 8048721SJonathan.Adams@Sun.COM continue; 8058721SJonathan.Adams@Sun.COM } else if (sep->se_panic || sep->se_tstate != tstate) 8068721SJonathan.Adams@Sun.COM continue; 8078721SJonathan.Adams@Sun.COM } 8088721SJonathan.Adams@Sun.COM if (excl_tstate != -1U) { 8098721SJonathan.Adams@Sun.COM if (excl_tstate == TSTATE_PANIC) { 8108721SJonathan.Adams@Sun.COM if (sep->se_panic) 8118721SJonathan.Adams@Sun.COM continue; 8128721SJonathan.Adams@Sun.COM } else if (!sep->se_panic && 8138721SJonathan.Adams@Sun.COM sep->se_tstate == excl_tstate) 8148721SJonathan.Adams@Sun.COM continue; 8158721SJonathan.Adams@Sun.COM } 8168721SJonathan.Adams@Sun.COM 8178721SJonathan.Adams@Sun.COM if (sobj_ops == SOBJ_ALL) { 8188721SJonathan.Adams@Sun.COM if (sep->se_sobj_ops == 0) 8198721SJonathan.Adams@Sun.COM continue; 8208721SJonathan.Adams@Sun.COM } else if (sobj_ops != 0) { 8218721SJonathan.Adams@Sun.COM if (sobj_ops != sep->se_sobj_ops) 8228721SJonathan.Adams@Sun.COM continue; 8238721SJonathan.Adams@Sun.COM } 8248721SJonathan.Adams@Sun.COM 8258721SJonathan.Adams@Sun.COM if (!(interesting && sep->se_panic)) { 8268721SJonathan.Adams@Sun.COM if (excl_sobj_ops == SOBJ_ALL) { 8278721SJonathan.Adams@Sun.COM if (sep->se_sobj_ops != 0) 8288721SJonathan.Adams@Sun.COM continue; 8298721SJonathan.Adams@Sun.COM } else if (excl_sobj_ops != 0) { 8308721SJonathan.Adams@Sun.COM if (excl_sobj_ops == sep->se_sobj_ops) 8318721SJonathan.Adams@Sun.COM continue; 8328721SJonathan.Adams@Sun.COM } 8338721SJonathan.Adams@Sun.COM } 8348721SJonathan.Adams@Sun.COM 8358721SJonathan.Adams@Sun.COM if (flags & DCMD_PIPE_OUT) { 8368721SJonathan.Adams@Sun.COM while (sep != NULL) { 8378721SJonathan.Adams@Sun.COM mdb_printf("%lr\n", sep->se_thread); 8388721SJonathan.Adams@Sun.COM sep = only_matching ? 8398721SJonathan.Adams@Sun.COM sep->se_next : sep->se_dup; 8408721SJonathan.Adams@Sun.COM } 8418721SJonathan.Adams@Sun.COM continue; 8428721SJonathan.Adams@Sun.COM } 8438721SJonathan.Adams@Sun.COM 84410889SJonathan.Adams@Sun.COM if (all || !printed) { 8458721SJonathan.Adams@Sun.COM mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n", 84610889SJonathan.Adams@Sun.COM "THREAD", "STATE", "SOBJ", "COUNT"); 84710889SJonathan.Adams@Sun.COM printed = 1; 8488721SJonathan.Adams@Sun.COM } 8498721SJonathan.Adams@Sun.COM 8508721SJonathan.Adams@Sun.COM do { 8518721SJonathan.Adams@Sun.COM char state[20]; 8528721SJonathan.Adams@Sun.COM char sobj[100]; 8538721SJonathan.Adams@Sun.COM 8548721SJonathan.Adams@Sun.COM tstate_to_text(cur->se_tstate, cur->se_panic, 8558721SJonathan.Adams@Sun.COM state, sizeof (state)); 8568721SJonathan.Adams@Sun.COM sobj_to_text(cur->se_sobj_ops, 8578721SJonathan.Adams@Sun.COM sobj, sizeof (sobj)); 8588721SJonathan.Adams@Sun.COM 8598721SJonathan.Adams@Sun.COM if (cur == sep) 860*12902SBryan.Cantrill@Sun.COM mdb_printf("%-?p %-8s %-?s %8d\n", 8618721SJonathan.Adams@Sun.COM cur->se_thread, state, sobj, count); 8628721SJonathan.Adams@Sun.COM else 863*12902SBryan.Cantrill@Sun.COM mdb_printf("%-?p %-8s %-?s %8s\n", 8648721SJonathan.Adams@Sun.COM cur->se_thread, state, sobj, "-"); 8658721SJonathan.Adams@Sun.COM 8668721SJonathan.Adams@Sun.COM cur = only_matching ? cur->se_next : cur->se_dup; 8678721SJonathan.Adams@Sun.COM } while (all && cur != NULL); 8688721SJonathan.Adams@Sun.COM 8698721SJonathan.Adams@Sun.COM if (sep->se_failed != 0) { 8708721SJonathan.Adams@Sun.COM char *reason; 8718721SJonathan.Adams@Sun.COM switch (sep->se_failed) { 8728721SJonathan.Adams@Sun.COM case FSI_FAIL_NOTINMEMORY: 8738721SJonathan.Adams@Sun.COM reason = "thread not in memory"; 8748721SJonathan.Adams@Sun.COM break; 8758721SJonathan.Adams@Sun.COM case FSI_FAIL_THREADCORRUPT: 8768721SJonathan.Adams@Sun.COM reason = "thread structure stack info corrupt"; 8778721SJonathan.Adams@Sun.COM break; 8788721SJonathan.Adams@Sun.COM case FSI_FAIL_STACKNOTFOUND: 8798721SJonathan.Adams@Sun.COM reason = "no consistent stack found"; 8808721SJonathan.Adams@Sun.COM break; 8818721SJonathan.Adams@Sun.COM default: 8828721SJonathan.Adams@Sun.COM reason = "unknown failure"; 8838721SJonathan.Adams@Sun.COM break; 8848721SJonathan.Adams@Sun.COM } 8858721SJonathan.Adams@Sun.COM mdb_printf("%?s <%s>\n", "", reason); 8868721SJonathan.Adams@Sun.COM } 8878721SJonathan.Adams@Sun.COM 8888721SJonathan.Adams@Sun.COM for (frame = 0; frame < sep->se_depth; frame++) 8898721SJonathan.Adams@Sun.COM mdb_printf("%?s %a\n", "", sep->se_stack[frame]); 8908721SJonathan.Adams@Sun.COM if (sep->se_overflow) 8918721SJonathan.Adams@Sun.COM mdb_printf("%?s ... truncated ...\n", ""); 8928721SJonathan.Adams@Sun.COM mdb_printf("\n"); 8938721SJonathan.Adams@Sun.COM } 8948721SJonathan.Adams@Sun.COM 8958721SJonathan.Adams@Sun.COM if (flags & DCMD_ADDRSPEC) { 8968721SJonathan.Adams@Sun.COM for (idx = 0; idx < p.pipe_len; idx++) 8978721SJonathan.Adams@Sun.COM if (seen[idx] == 0) 8988721SJonathan.Adams@Sun.COM mdb_warn("stacks: %p not in thread list\n", 8998721SJonathan.Adams@Sun.COM p.pipe_data[idx]); 9008721SJonathan.Adams@Sun.COM } 9018721SJonathan.Adams@Sun.COM return (DCMD_OK); 9028721SJonathan.Adams@Sun.COM } 903