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 */ 210Sstevel@tonic-gate /* 228721SJonathan.Adams@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 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> 33*8742Sgap@sun.com #include <sys/modctl.h> 340Sstevel@tonic-gate 350Sstevel@tonic-gate #include "findstack.h" 368721SJonathan.Adams@Sun.COM #include "thread.h" 378721SJonathan.Adams@Sun.COM #include "sobj.h" 388721SJonathan.Adams@Sun.COM 398721SJonathan.Adams@Sun.COM typedef struct findstack_info { 408721SJonathan.Adams@Sun.COM uintptr_t *fsi_stack; /* place to record frames */ 418721SJonathan.Adams@Sun.COM 428721SJonathan.Adams@Sun.COM uintptr_t fsi_sp; /* stack pointer */ 438721SJonathan.Adams@Sun.COM uintptr_t fsi_pc; /* pc */ 448721SJonathan.Adams@Sun.COM uintptr_t fsi_sobj_ops; /* sobj_ops */ 458721SJonathan.Adams@Sun.COM 468721SJonathan.Adams@Sun.COM uint_t fsi_tstate; /* t_state */ 478721SJonathan.Adams@Sun.COM 488721SJonathan.Adams@Sun.COM uchar_t fsi_depth; /* stack depth */ 498721SJonathan.Adams@Sun.COM uchar_t fsi_failed; /* search failed */ 508721SJonathan.Adams@Sun.COM uchar_t fsi_overflow; /* stack was deeper than max_depth */ 518721SJonathan.Adams@Sun.COM uchar_t fsi_panic; /* thread called panic() */ 528721SJonathan.Adams@Sun.COM 538721SJonathan.Adams@Sun.COM uchar_t fsi_max_depth; /* stack frames available */ 548721SJonathan.Adams@Sun.COM } findstack_info_t; 558721SJonathan.Adams@Sun.COM #define FSI_FAIL_BADTHREAD 1 568721SJonathan.Adams@Sun.COM #define FSI_FAIL_NOTINMEMORY 2 578721SJonathan.Adams@Sun.COM #define FSI_FAIL_THREADCORRUPT 3 588721SJonathan.Adams@Sun.COM #define FSI_FAIL_STACKNOTFOUND 4 590Sstevel@tonic-gate 600Sstevel@tonic-gate #ifndef STACK_BIAS 610Sstevel@tonic-gate #define STACK_BIAS 0 620Sstevel@tonic-gate #endif 630Sstevel@tonic-gate 640Sstevel@tonic-gate #define fs_dprintf(x) \ 650Sstevel@tonic-gate if (findstack_debug_on) { \ 660Sstevel@tonic-gate mdb_printf("findstack debug: "); \ 670Sstevel@tonic-gate /*CSTYLED*/ \ 680Sstevel@tonic-gate mdb_printf x ; \ 690Sstevel@tonic-gate } 700Sstevel@tonic-gate 710Sstevel@tonic-gate static int findstack_debug_on = 0; 720Sstevel@tonic-gate 730Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 740Sstevel@tonic-gate struct rwindow { 750Sstevel@tonic-gate uintptr_t rw_fp; 768721SJonathan.Adams@Sun.COM uintptr_t rw_rtn; 770Sstevel@tonic-gate }; 780Sstevel@tonic-gate #endif 790Sstevel@tonic-gate 800Sstevel@tonic-gate #define TOO_BIG_FOR_A_STACK (1024 * 1024) 810Sstevel@tonic-gate 820Sstevel@tonic-gate #define KTOU(p) ((p) - kbase + ubase) 830Sstevel@tonic-gate #define UTOK(p) ((p) - ubase + kbase) 840Sstevel@tonic-gate 850Sstevel@tonic-gate #define CRAWL_FOUNDALL (-1) 860Sstevel@tonic-gate 870Sstevel@tonic-gate /* 880Sstevel@tonic-gate * Given a stack pointer, try to crawl down it to the bottom. 890Sstevel@tonic-gate * "frame" is a VA in MDB's address space. 900Sstevel@tonic-gate * 910Sstevel@tonic-gate * Returns the number of frames successfully crawled down, or 920Sstevel@tonic-gate * CRAWL_FOUNDALL if it got to the bottom of the stack. 930Sstevel@tonic-gate */ 940Sstevel@tonic-gate static int 950Sstevel@tonic-gate crawl(uintptr_t frame, uintptr_t kbase, uintptr_t ktop, uintptr_t ubase, 968721SJonathan.Adams@Sun.COM int kill_fp, findstack_info_t *fsip) 970Sstevel@tonic-gate { 980Sstevel@tonic-gate int levels = 0; 990Sstevel@tonic-gate 1008721SJonathan.Adams@Sun.COM fsip->fsi_depth = 0; 1018721SJonathan.Adams@Sun.COM fsip->fsi_overflow = 0; 1028721SJonathan.Adams@Sun.COM 1030Sstevel@tonic-gate fs_dprintf(("<0> frame = %p, kbase = %p, ktop = %p, ubase = %p\n", 1040Sstevel@tonic-gate frame, kbase, ktop, ubase)); 1050Sstevel@tonic-gate for (;;) { 1060Sstevel@tonic-gate uintptr_t fp; 1070Sstevel@tonic-gate long *fpp = (long *)&((struct rwindow *)frame)->rw_fp; 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate fs_dprintf(("<1> fpp = %p, frame = %p\n", fpp, frame)); 1100Sstevel@tonic-gate 1110Sstevel@tonic-gate if ((frame & (STACK_ALIGN - 1)) != 0) 1120Sstevel@tonic-gate break; 1130Sstevel@tonic-gate 1140Sstevel@tonic-gate fp = ((struct rwindow *)frame)->rw_fp + STACK_BIAS; 1158721SJonathan.Adams@Sun.COM if (fsip->fsi_depth < fsip->fsi_max_depth) 1168721SJonathan.Adams@Sun.COM fsip->fsi_stack[fsip->fsi_depth++] = 1178721SJonathan.Adams@Sun.COM ((struct rwindow *)frame)->rw_rtn; 1188721SJonathan.Adams@Sun.COM else 1198721SJonathan.Adams@Sun.COM fsip->fsi_overflow = 1; 1208721SJonathan.Adams@Sun.COM 1210Sstevel@tonic-gate fs_dprintf(("<2> fp = %p\n", fp)); 1220Sstevel@tonic-gate 1230Sstevel@tonic-gate if (fp == ktop) 1240Sstevel@tonic-gate return (CRAWL_FOUNDALL); 1250Sstevel@tonic-gate fs_dprintf(("<3> not at base\n")); 1260Sstevel@tonic-gate 1270Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 1280Sstevel@tonic-gate if (ktop - fp == sizeof (struct rwindow)) { 1290Sstevel@tonic-gate fs_dprintf(("<4> found base\n")); 1300Sstevel@tonic-gate return (CRAWL_FOUNDALL); 1310Sstevel@tonic-gate } 1320Sstevel@tonic-gate #endif 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate fs_dprintf(("<5> fp = %p, kbase = %p, ktop - size = %p\n", 1350Sstevel@tonic-gate fp, kbase, ktop - sizeof (struct rwindow))); 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate if (fp < kbase || fp >= (ktop - sizeof (struct rwindow))) 1380Sstevel@tonic-gate break; 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate frame = KTOU(fp); 1410Sstevel@tonic-gate fs_dprintf(("<6> frame = %p\n", frame)); 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate /* 1440Sstevel@tonic-gate * NULL out the old %fp so we don't go down this stack 1450Sstevel@tonic-gate * more than once. 1460Sstevel@tonic-gate */ 1470Sstevel@tonic-gate if (kill_fp) { 1480Sstevel@tonic-gate fs_dprintf(("<7> fpp = %p\n", fpp)); 1490Sstevel@tonic-gate *fpp = NULL; 1500Sstevel@tonic-gate } 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate fs_dprintf(("<8> levels = %d\n", levels)); 1530Sstevel@tonic-gate levels++; 1540Sstevel@tonic-gate } 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate return (levels); 1570Sstevel@tonic-gate } 1580Sstevel@tonic-gate 1590Sstevel@tonic-gate /* 1600Sstevel@tonic-gate * "sp" is a kernel VA. 1610Sstevel@tonic-gate */ 1620Sstevel@tonic-gate static int 1630Sstevel@tonic-gate print_stack(uintptr_t sp, uintptr_t pc, uintptr_t addr, 1640Sstevel@tonic-gate int argc, const mdb_arg_t *argv, int free_state) 1650Sstevel@tonic-gate { 1660Sstevel@tonic-gate int showargs = 0, count, err; 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate count = mdb_getopts(argc, argv, 1690Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &showargs, NULL); 1700Sstevel@tonic-gate argc -= count; 1710Sstevel@tonic-gate argv += count; 1720Sstevel@tonic-gate 1730Sstevel@tonic-gate if (argc > 1 || (argc == 1 && argv->a_type != MDB_TYPE_STRING)) 1740Sstevel@tonic-gate return (DCMD_USAGE); 1750Sstevel@tonic-gate 1760Sstevel@tonic-gate mdb_printf("stack pointer for thread %p%s: %p\n", 1770Sstevel@tonic-gate addr, (free_state ? " (TS_FREE)" : ""), sp); 1780Sstevel@tonic-gate if (pc != 0) 1790Sstevel@tonic-gate mdb_printf("[ %0?lr %a() ]\n", sp, pc); 1800Sstevel@tonic-gate 1810Sstevel@tonic-gate mdb_inc_indent(2); 1820Sstevel@tonic-gate mdb_set_dot(sp); 1830Sstevel@tonic-gate 1840Sstevel@tonic-gate if (argc == 1) 1850Sstevel@tonic-gate err = mdb_eval(argv->a_un.a_str); 1860Sstevel@tonic-gate else if (showargs) 1870Sstevel@tonic-gate err = mdb_eval("<.$C"); 1880Sstevel@tonic-gate else 1890Sstevel@tonic-gate err = mdb_eval("<.$C0"); 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate mdb_dec_indent(2); 1920Sstevel@tonic-gate 1930Sstevel@tonic-gate return ((err == -1) ? DCMD_ABORT : DCMD_OK); 1940Sstevel@tonic-gate } 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate /*ARGSUSED*/ 1978721SJonathan.Adams@Sun.COM static int 1988721SJonathan.Adams@Sun.COM do_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings) 1990Sstevel@tonic-gate { 2000Sstevel@tonic-gate kthread_t thr; 2010Sstevel@tonic-gate size_t stksz; 2020Sstevel@tonic-gate uintptr_t ubase, utop; 2030Sstevel@tonic-gate uintptr_t kbase, ktop; 2040Sstevel@tonic-gate uintptr_t win, sp; 2050Sstevel@tonic-gate 2068721SJonathan.Adams@Sun.COM fsip->fsi_failed = 0; 2078721SJonathan.Adams@Sun.COM fsip->fsi_pc = 0; 2088721SJonathan.Adams@Sun.COM fsip->fsi_sp = 0; 2098721SJonathan.Adams@Sun.COM fsip->fsi_depth = 0; 2108721SJonathan.Adams@Sun.COM fsip->fsi_overflow = 0; 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate bzero(&thr, sizeof (thr)); 2130Sstevel@tonic-gate if (mdb_ctf_vread(&thr, "kthread_t", addr, 2140Sstevel@tonic-gate MDB_CTF_VREAD_IGNORE_ALL) == -1) { 2158721SJonathan.Adams@Sun.COM if (print_warnings) 2168721SJonathan.Adams@Sun.COM mdb_warn("couldn't read thread at %p\n", addr); 2178721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_BADTHREAD; 2180Sstevel@tonic-gate return (DCMD_ERR); 2190Sstevel@tonic-gate } 2200Sstevel@tonic-gate 2218721SJonathan.Adams@Sun.COM fsip->fsi_sobj_ops = (uintptr_t)thr.t_sobj_ops; 2228721SJonathan.Adams@Sun.COM fsip->fsi_tstate = thr.t_state; 2238721SJonathan.Adams@Sun.COM fsip->fsi_panic = !!(thr.t_flag & T_PANIC); 2248721SJonathan.Adams@Sun.COM 2250Sstevel@tonic-gate if ((thr.t_schedflag & TS_LOAD) == 0) { 2268721SJonathan.Adams@Sun.COM if (print_warnings) 2278721SJonathan.Adams@Sun.COM mdb_warn("thread %p isn't in memory\n", addr); 2288721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_NOTINMEMORY; 2290Sstevel@tonic-gate return (DCMD_ERR); 2300Sstevel@tonic-gate } 2310Sstevel@tonic-gate 2320Sstevel@tonic-gate if (thr.t_stk < thr.t_stkbase) { 2338721SJonathan.Adams@Sun.COM if (print_warnings) 2348721SJonathan.Adams@Sun.COM mdb_warn( 2358721SJonathan.Adams@Sun.COM "stack base or stack top corrupt for thread %p\n", 2368721SJonathan.Adams@Sun.COM addr); 2378721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; 2380Sstevel@tonic-gate return (DCMD_ERR); 2390Sstevel@tonic-gate } 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate kbase = (uintptr_t)thr.t_stkbase; 2420Sstevel@tonic-gate ktop = (uintptr_t)thr.t_stk; 2430Sstevel@tonic-gate stksz = ktop - kbase; 2440Sstevel@tonic-gate 2450Sstevel@tonic-gate #ifdef __amd64 2460Sstevel@tonic-gate /* 2470Sstevel@tonic-gate * The stack on amd64 is intentionally misaligned, so ignore the top 2480Sstevel@tonic-gate * half-frame. See thread_stk_init(). When handling traps, the frame 2490Sstevel@tonic-gate * is automatically aligned by the hardware, so we only alter ktop if 2500Sstevel@tonic-gate * needed. 2510Sstevel@tonic-gate */ 2520Sstevel@tonic-gate if ((ktop & (STACK_ALIGN - 1)) != 0) 2530Sstevel@tonic-gate ktop -= STACK_ENTRY_ALIGN; 2540Sstevel@tonic-gate #endif 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate /* 2570Sstevel@tonic-gate * If the stack size is larger than a meg, assume that it's bogus. 2580Sstevel@tonic-gate */ 2590Sstevel@tonic-gate if (stksz > TOO_BIG_FOR_A_STACK) { 2608721SJonathan.Adams@Sun.COM if (print_warnings) 2618721SJonathan.Adams@Sun.COM mdb_warn("stack size for thread %p is too big to be " 2628721SJonathan.Adams@Sun.COM "reasonable\n", addr); 2638721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; 2640Sstevel@tonic-gate return (DCMD_ERR); 2650Sstevel@tonic-gate } 2660Sstevel@tonic-gate 2670Sstevel@tonic-gate /* 2680Sstevel@tonic-gate * This could be (and was) a UM_GC allocation. Unfortunately, 2690Sstevel@tonic-gate * stksz tends to be very large. As currently implemented, dcmds 2700Sstevel@tonic-gate * invoked as part of pipelines don't have their UM_GC-allocated 2710Sstevel@tonic-gate * memory freed until the pipeline completes. With stksz in the 2720Sstevel@tonic-gate * neighborhood of 20k, the popular ::walk thread |::findstack 2730Sstevel@tonic-gate * pipeline can easily run memory-constrained debuggers (kmdb) out 2740Sstevel@tonic-gate * of memory. This can be changed back to a gc-able allocation when 2750Sstevel@tonic-gate * the debugger is changed to free UM_GC memory more promptly. 2760Sstevel@tonic-gate */ 2770Sstevel@tonic-gate ubase = (uintptr_t)mdb_alloc(stksz, UM_SLEEP); 2780Sstevel@tonic-gate utop = ubase + stksz; 2790Sstevel@tonic-gate if (mdb_vread((caddr_t)ubase, stksz, kbase) != stksz) { 2800Sstevel@tonic-gate mdb_free((void *)ubase, stksz); 2818721SJonathan.Adams@Sun.COM if (print_warnings) 2828721SJonathan.Adams@Sun.COM mdb_warn("couldn't read entire stack for thread %p\n", 2838721SJonathan.Adams@Sun.COM addr); 2848721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; 2850Sstevel@tonic-gate return (DCMD_ERR); 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate /* 2890Sstevel@tonic-gate * Try the saved %sp first, if it looks reasonable. 2900Sstevel@tonic-gate */ 2910Sstevel@tonic-gate sp = KTOU((uintptr_t)thr.t_sp + STACK_BIAS); 2920Sstevel@tonic-gate if (sp >= ubase && sp <= utop) { 2938721SJonathan.Adams@Sun.COM if (crawl(sp, kbase, ktop, ubase, 0, fsip) == CRAWL_FOUNDALL) { 2948721SJonathan.Adams@Sun.COM fsip->fsi_sp = (uintptr_t)thr.t_sp; 2958721SJonathan.Adams@Sun.COM #if !defined(__i386) 2968721SJonathan.Adams@Sun.COM fsip->fsi_pc = (uintptr_t)thr.t_pc; 2970Sstevel@tonic-gate #endif 2988721SJonathan.Adams@Sun.COM goto found; 2990Sstevel@tonic-gate } 3000Sstevel@tonic-gate } 3010Sstevel@tonic-gate 3020Sstevel@tonic-gate /* 3030Sstevel@tonic-gate * Now walk through the whole stack, starting at the base, 3040Sstevel@tonic-gate * trying every possible "window". 3050Sstevel@tonic-gate */ 3060Sstevel@tonic-gate for (win = ubase; 3070Sstevel@tonic-gate win + sizeof (struct rwindow) <= utop; 3080Sstevel@tonic-gate win += sizeof (struct rwindow *)) { 3098721SJonathan.Adams@Sun.COM if (crawl(win, kbase, ktop, ubase, 1, fsip) == CRAWL_FOUNDALL) { 3108721SJonathan.Adams@Sun.COM fsip->fsi_sp = UTOK(win) - STACK_BIAS; 3118721SJonathan.Adams@Sun.COM goto found; 3120Sstevel@tonic-gate } 3130Sstevel@tonic-gate } 3140Sstevel@tonic-gate 3150Sstevel@tonic-gate /* 3160Sstevel@tonic-gate * We didn't conclusively find the stack. So we'll take another lap, 3170Sstevel@tonic-gate * and print out anything that looks possible. 3180Sstevel@tonic-gate */ 3198721SJonathan.Adams@Sun.COM if (print_warnings) 3208721SJonathan.Adams@Sun.COM mdb_printf("Possible stack pointers for thread %p:\n", addr); 3210Sstevel@tonic-gate (void) mdb_vread((caddr_t)ubase, stksz, kbase); 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate for (win = ubase; 3240Sstevel@tonic-gate win + sizeof (struct rwindow) <= utop; 3250Sstevel@tonic-gate win += sizeof (struct rwindow *)) { 3260Sstevel@tonic-gate uintptr_t fp = ((struct rwindow *)win)->rw_fp; 3270Sstevel@tonic-gate int levels; 3280Sstevel@tonic-gate 3298721SJonathan.Adams@Sun.COM if ((levels = crawl(win, kbase, ktop, ubase, 1, fsip)) > 1) { 3308721SJonathan.Adams@Sun.COM if (print_warnings) 3318721SJonathan.Adams@Sun.COM mdb_printf(" %p (%d)\n", fp, levels); 3320Sstevel@tonic-gate } else if (levels == CRAWL_FOUNDALL) { 3330Sstevel@tonic-gate /* 3340Sstevel@tonic-gate * If this is a live system, the stack could change 3350Sstevel@tonic-gate * between the two mdb_vread(ubase, utop, kbase)'s, 3360Sstevel@tonic-gate * and we could have a fully valid stack here. 3370Sstevel@tonic-gate */ 3388721SJonathan.Adams@Sun.COM fsip->fsi_sp = UTOK(win) - STACK_BIAS; 3398721SJonathan.Adams@Sun.COM goto found; 3400Sstevel@tonic-gate } 3410Sstevel@tonic-gate } 3420Sstevel@tonic-gate 3438721SJonathan.Adams@Sun.COM fsip->fsi_depth = 0; 3448721SJonathan.Adams@Sun.COM fsip->fsi_overflow = 0; 3458721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_STACKNOTFOUND; 3468721SJonathan.Adams@Sun.COM 3478721SJonathan.Adams@Sun.COM mdb_free((void *)ubase, stksz); 3488721SJonathan.Adams@Sun.COM return (DCMD_ERR); 3498721SJonathan.Adams@Sun.COM found: 3500Sstevel@tonic-gate mdb_free((void *)ubase, stksz); 3510Sstevel@tonic-gate return (DCMD_OK); 3520Sstevel@tonic-gate } 3530Sstevel@tonic-gate 3548721SJonathan.Adams@Sun.COM int 3558721SJonathan.Adams@Sun.COM findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3568721SJonathan.Adams@Sun.COM { 3578721SJonathan.Adams@Sun.COM findstack_info_t fsi; 3588721SJonathan.Adams@Sun.COM int retval; 3598721SJonathan.Adams@Sun.COM 3608721SJonathan.Adams@Sun.COM if (!(flags & DCMD_ADDRSPEC)) 3618721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 3628721SJonathan.Adams@Sun.COM 3638721SJonathan.Adams@Sun.COM bzero(&fsi, sizeof (fsi)); 3648721SJonathan.Adams@Sun.COM 3658721SJonathan.Adams@Sun.COM if ((retval = do_findstack(addr, &fsi, 1)) != DCMD_OK || 3668721SJonathan.Adams@Sun.COM fsi.fsi_failed) 3678721SJonathan.Adams@Sun.COM return (retval); 3688721SJonathan.Adams@Sun.COM 3698721SJonathan.Adams@Sun.COM return (print_stack(fsi.fsi_sp, fsi.fsi_pc, addr, 3708721SJonathan.Adams@Sun.COM argc, argv, fsi.fsi_tstate == TS_FREE)); 3718721SJonathan.Adams@Sun.COM } 3728721SJonathan.Adams@Sun.COM 3730Sstevel@tonic-gate /*ARGSUSED*/ 3740Sstevel@tonic-gate int 3750Sstevel@tonic-gate findstack_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *av) 3760Sstevel@tonic-gate { 3770Sstevel@tonic-gate findstack_debug_on ^= 1; 3780Sstevel@tonic-gate 3790Sstevel@tonic-gate mdb_printf("findstack: debugging is now %s\n", 3800Sstevel@tonic-gate findstack_debug_on ? "on" : "off"); 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate return (DCMD_OK); 3830Sstevel@tonic-gate } 3840Sstevel@tonic-gate 3858721SJonathan.Adams@Sun.COM static void 3868721SJonathan.Adams@Sun.COM uppercase(char *p) 3878721SJonathan.Adams@Sun.COM { 3888721SJonathan.Adams@Sun.COM for (; *p != '\0'; p++) { 3898721SJonathan.Adams@Sun.COM if (*p >= 'a' && *p <= 'z') 3908721SJonathan.Adams@Sun.COM *p += 'A' - 'a'; 3918721SJonathan.Adams@Sun.COM } 3928721SJonathan.Adams@Sun.COM } 3938721SJonathan.Adams@Sun.COM 3948721SJonathan.Adams@Sun.COM static void 3958721SJonathan.Adams@Sun.COM sobj_to_text(uintptr_t addr, char *out, size_t out_sz) 3968721SJonathan.Adams@Sun.COM { 3978721SJonathan.Adams@Sun.COM sobj_ops_to_text(addr, out, out_sz); 3988721SJonathan.Adams@Sun.COM uppercase(out); 3998721SJonathan.Adams@Sun.COM } 4008721SJonathan.Adams@Sun.COM 4018721SJonathan.Adams@Sun.COM #define SOBJ_ALL 1 4028721SJonathan.Adams@Sun.COM static int 4038721SJonathan.Adams@Sun.COM text_to_sobj(const char *text, uintptr_t *out) 4048721SJonathan.Adams@Sun.COM { 4058721SJonathan.Adams@Sun.COM if (strcasecmp(text, "ALL") == 0) { 4068721SJonathan.Adams@Sun.COM *out = SOBJ_ALL; 4078721SJonathan.Adams@Sun.COM return (0); 4088721SJonathan.Adams@Sun.COM } 4098721SJonathan.Adams@Sun.COM return (sobj_text_to_ops(text, out)); 4108721SJonathan.Adams@Sun.COM } 4118721SJonathan.Adams@Sun.COM 4128721SJonathan.Adams@Sun.COM #define TSTATE_PANIC -2U 4138721SJonathan.Adams@Sun.COM static int 4148721SJonathan.Adams@Sun.COM text_to_tstate(const char *text, uint_t *out) 4158721SJonathan.Adams@Sun.COM { 4168721SJonathan.Adams@Sun.COM if (strcasecmp(text, "panic") == 0) 4178721SJonathan.Adams@Sun.COM *out = TSTATE_PANIC; 4188721SJonathan.Adams@Sun.COM else if (thread_text_to_state(text, out) != 0) { 4198721SJonathan.Adams@Sun.COM mdb_warn("tstate \"%s\" not recognized\n", text); 4208721SJonathan.Adams@Sun.COM return (-1); 4218721SJonathan.Adams@Sun.COM } 4228721SJonathan.Adams@Sun.COM return (0); 4238721SJonathan.Adams@Sun.COM } 4248721SJonathan.Adams@Sun.COM 4258721SJonathan.Adams@Sun.COM static void 4268721SJonathan.Adams@Sun.COM tstate_to_text(uint_t tstate, uint_t paniced, char *out, size_t out_sz) 4278721SJonathan.Adams@Sun.COM { 4288721SJonathan.Adams@Sun.COM if (paniced) 4298721SJonathan.Adams@Sun.COM mdb_snprintf(out, out_sz, "panic"); 4308721SJonathan.Adams@Sun.COM else 4318721SJonathan.Adams@Sun.COM thread_state_to_text(tstate, out, out_sz); 4328721SJonathan.Adams@Sun.COM uppercase(out); 4338721SJonathan.Adams@Sun.COM } 4348721SJonathan.Adams@Sun.COM 4358721SJonathan.Adams@Sun.COM typedef struct stacks_entry { 4368721SJonathan.Adams@Sun.COM struct stacks_entry *se_next; 4378721SJonathan.Adams@Sun.COM struct stacks_entry *se_dup; /* dups of this stack */ 4388721SJonathan.Adams@Sun.COM uintptr_t se_thread; 4398721SJonathan.Adams@Sun.COM uintptr_t se_sp; 4408721SJonathan.Adams@Sun.COM uintptr_t se_sobj_ops; 4418721SJonathan.Adams@Sun.COM uint32_t se_tstate; 4428721SJonathan.Adams@Sun.COM uint32_t se_count; /* # threads w/ this stack */ 4438721SJonathan.Adams@Sun.COM uint8_t se_overflow; 4448721SJonathan.Adams@Sun.COM uint8_t se_depth; 4458721SJonathan.Adams@Sun.COM uint8_t se_failed; /* failure reason; FSI_FAIL_* */ 4468721SJonathan.Adams@Sun.COM uint8_t se_panic; 4478721SJonathan.Adams@Sun.COM uintptr_t se_stack[1]; 4488721SJonathan.Adams@Sun.COM } stacks_entry_t; 4498721SJonathan.Adams@Sun.COM #define STACKS_ENTRY_SIZE(x) OFFSETOF(stacks_entry_t, se_stack[(x)]) 4508721SJonathan.Adams@Sun.COM 4518721SJonathan.Adams@Sun.COM #define STACKS_HSIZE 127 4528721SJonathan.Adams@Sun.COM 4538721SJonathan.Adams@Sun.COM /* Maximum stack depth reported in stacks */ 4548721SJonathan.Adams@Sun.COM #define STACKS_MAX_DEPTH 254 4558721SJonathan.Adams@Sun.COM 4568721SJonathan.Adams@Sun.COM typedef struct stacks_info { 4578721SJonathan.Adams@Sun.COM size_t si_count; /* total stacks_entry_ts (incl dups) */ 4588721SJonathan.Adams@Sun.COM size_t si_entries; /* # entries in hash table */ 4598721SJonathan.Adams@Sun.COM stacks_entry_t **si_hash; /* hash table */ 4608721SJonathan.Adams@Sun.COM 4618721SJonathan.Adams@Sun.COM findstack_info_t si_fsi; /* transient callback state */ 4628721SJonathan.Adams@Sun.COM } stacks_info_t; 4638721SJonathan.Adams@Sun.COM 4648721SJonathan.Adams@Sun.COM 4658721SJonathan.Adams@Sun.COM /* global state cached between invocations */ 4668721SJonathan.Adams@Sun.COM #define STACKS_STATE_CLEAN 0 4678721SJonathan.Adams@Sun.COM #define STACKS_STATE_DIRTY 1 4688721SJonathan.Adams@Sun.COM #define STACKS_STATE_DONE 2 4698721SJonathan.Adams@Sun.COM static uint_t stacks_state = STACKS_STATE_CLEAN; 4708721SJonathan.Adams@Sun.COM static stacks_entry_t **stacks_hash; 4718721SJonathan.Adams@Sun.COM static stacks_entry_t **stacks_array; 4728721SJonathan.Adams@Sun.COM static size_t stacks_array_size; 4738721SJonathan.Adams@Sun.COM 4748721SJonathan.Adams@Sun.COM size_t 4758721SJonathan.Adams@Sun.COM stacks_hash_entry(stacks_entry_t *sep) 4768721SJonathan.Adams@Sun.COM { 4778721SJonathan.Adams@Sun.COM size_t depth = sep->se_depth; 4788721SJonathan.Adams@Sun.COM uintptr_t *stack = sep->se_stack; 4798721SJonathan.Adams@Sun.COM 4808721SJonathan.Adams@Sun.COM uint64_t total = depth; 4818721SJonathan.Adams@Sun.COM 4828721SJonathan.Adams@Sun.COM while (depth > 0) { 4838721SJonathan.Adams@Sun.COM total += *stack; 4848721SJonathan.Adams@Sun.COM stack++; depth--; 4858721SJonathan.Adams@Sun.COM } 4868721SJonathan.Adams@Sun.COM 4878721SJonathan.Adams@Sun.COM return (total % STACKS_HSIZE); 4888721SJonathan.Adams@Sun.COM } 4898721SJonathan.Adams@Sun.COM 4908721SJonathan.Adams@Sun.COM /* 4918721SJonathan.Adams@Sun.COM * This is used to both compare stacks for equality and to sort the final 4928721SJonathan.Adams@Sun.COM * list of unique stacks. forsort specifies the latter behavior, which 4938721SJonathan.Adams@Sun.COM * additionally: 4948721SJonathan.Adams@Sun.COM * compares se_count, and 4958721SJonathan.Adams@Sun.COM * sorts the stacks by text function name. 4968721SJonathan.Adams@Sun.COM * 4978721SJonathan.Adams@Sun.COM * The equality test is independent of se_count, and doesn't care about 4988721SJonathan.Adams@Sun.COM * relative ordering, so we don't do the extra work of looking up symbols 4998721SJonathan.Adams@Sun.COM * for the stack addresses. 5008721SJonathan.Adams@Sun.COM */ 5010Sstevel@tonic-gate int 5028721SJonathan.Adams@Sun.COM stacks_entry_comp_impl(stacks_entry_t *l, stacks_entry_t *r, 5038721SJonathan.Adams@Sun.COM uint_t forsort) 5040Sstevel@tonic-gate { 5058721SJonathan.Adams@Sun.COM int idx; 5068721SJonathan.Adams@Sun.COM 5078721SJonathan.Adams@Sun.COM int depth = MIN(l->se_depth, r->se_depth); 5088721SJonathan.Adams@Sun.COM 5098721SJonathan.Adams@Sun.COM /* no matter what, panic stacks come last. */ 5108721SJonathan.Adams@Sun.COM if (l->se_panic > r->se_panic) 5118721SJonathan.Adams@Sun.COM return (1); 5128721SJonathan.Adams@Sun.COM if (l->se_panic < r->se_panic) 5138721SJonathan.Adams@Sun.COM return (-1); 5148721SJonathan.Adams@Sun.COM 5158721SJonathan.Adams@Sun.COM if (forsort) { 5168721SJonathan.Adams@Sun.COM /* put large counts earlier */ 5178721SJonathan.Adams@Sun.COM if (l->se_count > r->se_count) 5188721SJonathan.Adams@Sun.COM return (-1); 5198721SJonathan.Adams@Sun.COM if (l->se_count < r->se_count) 5208721SJonathan.Adams@Sun.COM return (1); 5218721SJonathan.Adams@Sun.COM } 5228721SJonathan.Adams@Sun.COM 5238721SJonathan.Adams@Sun.COM if (l->se_tstate > r->se_tstate) 5248721SJonathan.Adams@Sun.COM return (1); 5258721SJonathan.Adams@Sun.COM if (l->se_tstate < r->se_tstate) 5268721SJonathan.Adams@Sun.COM return (-1); 5278721SJonathan.Adams@Sun.COM 5288721SJonathan.Adams@Sun.COM if (l->se_failed > r->se_failed) 5298721SJonathan.Adams@Sun.COM return (1); 5308721SJonathan.Adams@Sun.COM if (l->se_failed < r->se_failed) 5318721SJonathan.Adams@Sun.COM return (-1); 5328721SJonathan.Adams@Sun.COM 5338721SJonathan.Adams@Sun.COM for (idx = 0; idx < depth; idx++) { 5348721SJonathan.Adams@Sun.COM char lbuf[MDB_SYM_NAMLEN]; 5358721SJonathan.Adams@Sun.COM char rbuf[MDB_SYM_NAMLEN]; 5368721SJonathan.Adams@Sun.COM 5378721SJonathan.Adams@Sun.COM int rval; 5388721SJonathan.Adams@Sun.COM uintptr_t laddr = l->se_stack[idx]; 5398721SJonathan.Adams@Sun.COM uintptr_t raddr = r->se_stack[idx]; 5408721SJonathan.Adams@Sun.COM 5418721SJonathan.Adams@Sun.COM if (laddr == raddr) 5428721SJonathan.Adams@Sun.COM continue; 5438721SJonathan.Adams@Sun.COM 5448721SJonathan.Adams@Sun.COM if (forsort && 5458721SJonathan.Adams@Sun.COM mdb_lookup_by_addr(laddr, MDB_SYM_FUZZY, 5468721SJonathan.Adams@Sun.COM lbuf, sizeof (lbuf), NULL) != -1 && 5478721SJonathan.Adams@Sun.COM mdb_lookup_by_addr(raddr, MDB_SYM_FUZZY, 5488721SJonathan.Adams@Sun.COM rbuf, sizeof (rbuf), NULL) != -1 && 5498721SJonathan.Adams@Sun.COM (rval = strcmp(lbuf, rbuf)) != 0) 5508721SJonathan.Adams@Sun.COM return (rval); 5518721SJonathan.Adams@Sun.COM 5528721SJonathan.Adams@Sun.COM if (laddr > raddr) 5538721SJonathan.Adams@Sun.COM return (1); 5548721SJonathan.Adams@Sun.COM return (-1); 5550Sstevel@tonic-gate } 5568721SJonathan.Adams@Sun.COM 5578721SJonathan.Adams@Sun.COM if (l->se_overflow > r->se_overflow) 5588721SJonathan.Adams@Sun.COM return (-1); 5598721SJonathan.Adams@Sun.COM if (l->se_overflow < r->se_overflow) 5608721SJonathan.Adams@Sun.COM return (1); 5618721SJonathan.Adams@Sun.COM 5628721SJonathan.Adams@Sun.COM if (l->se_depth > r->se_depth) 5638721SJonathan.Adams@Sun.COM return (1); 5648721SJonathan.Adams@Sun.COM if (l->se_depth < r->se_depth) 5658721SJonathan.Adams@Sun.COM return (-1); 5668721SJonathan.Adams@Sun.COM 5678721SJonathan.Adams@Sun.COM if (l->se_sobj_ops > r->se_sobj_ops) 5688721SJonathan.Adams@Sun.COM return (1); 5698721SJonathan.Adams@Sun.COM if (l->se_sobj_ops < r->se_sobj_ops) 5708721SJonathan.Adams@Sun.COM return (-1); 5718721SJonathan.Adams@Sun.COM 5728721SJonathan.Adams@Sun.COM return (0); 5738721SJonathan.Adams@Sun.COM } 5748721SJonathan.Adams@Sun.COM 5758721SJonathan.Adams@Sun.COM int 5768721SJonathan.Adams@Sun.COM stacks_entry_comp(const void *l_arg, const void *r_arg) 5778721SJonathan.Adams@Sun.COM { 5788721SJonathan.Adams@Sun.COM stacks_entry_t * const *lp = l_arg; 5798721SJonathan.Adams@Sun.COM stacks_entry_t * const *rp = r_arg; 5808721SJonathan.Adams@Sun.COM 5818721SJonathan.Adams@Sun.COM return (stacks_entry_comp_impl(*lp, *rp, 1)); 5828721SJonathan.Adams@Sun.COM } 5838721SJonathan.Adams@Sun.COM 5848721SJonathan.Adams@Sun.COM void 5858721SJonathan.Adams@Sun.COM stacks_cleanup(int force) 5868721SJonathan.Adams@Sun.COM { 5878721SJonathan.Adams@Sun.COM int idx = 0; 5888721SJonathan.Adams@Sun.COM stacks_entry_t *cur, *next; 5898721SJonathan.Adams@Sun.COM 5908721SJonathan.Adams@Sun.COM if (stacks_state == STACKS_STATE_CLEAN) 5918721SJonathan.Adams@Sun.COM return; 5928721SJonathan.Adams@Sun.COM 5938721SJonathan.Adams@Sun.COM if (!force && stacks_state == STACKS_STATE_DONE) 5948721SJonathan.Adams@Sun.COM return; 5958721SJonathan.Adams@Sun.COM 5968721SJonathan.Adams@Sun.COM /* 5978721SJonathan.Adams@Sun.COM * Until the array is sorted and stable, stacks_hash will be non-NULL. 5988721SJonathan.Adams@Sun.COM * This way, we can get at all of the data, even if qsort() was 5998721SJonathan.Adams@Sun.COM * interrupted while mucking with the array. 6008721SJonathan.Adams@Sun.COM */ 6018721SJonathan.Adams@Sun.COM if (stacks_hash != NULL) { 6028721SJonathan.Adams@Sun.COM for (idx = 0; idx < STACKS_HSIZE; idx++) { 6038721SJonathan.Adams@Sun.COM while ((cur = stacks_hash[idx]) != NULL) { 6048721SJonathan.Adams@Sun.COM while ((next = cur->se_dup) != NULL) { 6058721SJonathan.Adams@Sun.COM cur->se_dup = next->se_dup; 6068721SJonathan.Adams@Sun.COM mdb_free(next, 6078721SJonathan.Adams@Sun.COM STACKS_ENTRY_SIZE(next->se_depth)); 6088721SJonathan.Adams@Sun.COM } 6098721SJonathan.Adams@Sun.COM next = cur->se_next; 6108721SJonathan.Adams@Sun.COM stacks_hash[idx] = next; 6118721SJonathan.Adams@Sun.COM mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth)); 6128721SJonathan.Adams@Sun.COM } 6138721SJonathan.Adams@Sun.COM } 6148721SJonathan.Adams@Sun.COM if (stacks_array != NULL) 6158721SJonathan.Adams@Sun.COM mdb_free(stacks_array, 6168721SJonathan.Adams@Sun.COM stacks_array_size * sizeof (*stacks_array)); 6178721SJonathan.Adams@Sun.COM 6188721SJonathan.Adams@Sun.COM } else if (stacks_array != NULL) { 6198721SJonathan.Adams@Sun.COM for (idx = 0; idx < stacks_array_size; idx++) { 6208721SJonathan.Adams@Sun.COM if ((cur = stacks_array[idx]) != NULL) { 6218721SJonathan.Adams@Sun.COM while ((next = cur->se_dup) != NULL) { 6228721SJonathan.Adams@Sun.COM cur->se_dup = next->se_dup; 6238721SJonathan.Adams@Sun.COM mdb_free(next, 6248721SJonathan.Adams@Sun.COM STACKS_ENTRY_SIZE(next->se_depth)); 6258721SJonathan.Adams@Sun.COM } 6268721SJonathan.Adams@Sun.COM stacks_array[idx] = NULL; 6278721SJonathan.Adams@Sun.COM mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth)); 6288721SJonathan.Adams@Sun.COM } 6298721SJonathan.Adams@Sun.COM } 6308721SJonathan.Adams@Sun.COM mdb_free(stacks_array, 6318721SJonathan.Adams@Sun.COM stacks_array_size * sizeof (*stacks_array)); 6328721SJonathan.Adams@Sun.COM } 6338721SJonathan.Adams@Sun.COM 6348721SJonathan.Adams@Sun.COM stacks_array_size = 0; 6358721SJonathan.Adams@Sun.COM stacks_state = STACKS_STATE_CLEAN; 6368721SJonathan.Adams@Sun.COM } 6378721SJonathan.Adams@Sun.COM 6388721SJonathan.Adams@Sun.COM /*ARGSUSED*/ 6398721SJonathan.Adams@Sun.COM int 6408721SJonathan.Adams@Sun.COM stacks_thread_cb(uintptr_t addr, const void *ignored, void *cbarg) 6418721SJonathan.Adams@Sun.COM { 6428721SJonathan.Adams@Sun.COM stacks_info_t *sip = cbarg; 6438721SJonathan.Adams@Sun.COM findstack_info_t *fsip = &sip->si_fsi; 6448721SJonathan.Adams@Sun.COM 6458721SJonathan.Adams@Sun.COM stacks_entry_t **sepp, *nsep, *sep; 6468721SJonathan.Adams@Sun.COM int idx; 6478721SJonathan.Adams@Sun.COM size_t depth; 6488721SJonathan.Adams@Sun.COM 6498721SJonathan.Adams@Sun.COM if (do_findstack(addr, fsip, 0) != DCMD_OK && 6508721SJonathan.Adams@Sun.COM fsip->fsi_failed == FSI_FAIL_BADTHREAD) { 6518721SJonathan.Adams@Sun.COM mdb_warn("couldn't read thread at %p\n", addr); 6528721SJonathan.Adams@Sun.COM return (WALK_NEXT); 6538721SJonathan.Adams@Sun.COM } 6548721SJonathan.Adams@Sun.COM 6558721SJonathan.Adams@Sun.COM sip->si_count++; 6568721SJonathan.Adams@Sun.COM 6578721SJonathan.Adams@Sun.COM depth = fsip->fsi_depth; 6588721SJonathan.Adams@Sun.COM nsep = mdb_zalloc(STACKS_ENTRY_SIZE(depth), UM_SLEEP); 6598721SJonathan.Adams@Sun.COM nsep->se_thread = addr; 6608721SJonathan.Adams@Sun.COM nsep->se_sp = fsip->fsi_sp; 6618721SJonathan.Adams@Sun.COM nsep->se_sobj_ops = fsip->fsi_sobj_ops; 6628721SJonathan.Adams@Sun.COM nsep->se_tstate = fsip->fsi_tstate; 6638721SJonathan.Adams@Sun.COM nsep->se_count = 1; 6648721SJonathan.Adams@Sun.COM nsep->se_overflow = fsip->fsi_overflow; 6658721SJonathan.Adams@Sun.COM nsep->se_depth = depth; 6668721SJonathan.Adams@Sun.COM nsep->se_failed = fsip->fsi_failed; 6678721SJonathan.Adams@Sun.COM nsep->se_panic = fsip->fsi_panic; 6688721SJonathan.Adams@Sun.COM 6698721SJonathan.Adams@Sun.COM for (idx = 0; idx < depth; idx++) 6708721SJonathan.Adams@Sun.COM nsep->se_stack[idx] = fsip->fsi_stack[idx]; 6718721SJonathan.Adams@Sun.COM 6728721SJonathan.Adams@Sun.COM for (sepp = &sip->si_hash[stacks_hash_entry(nsep)]; 6738721SJonathan.Adams@Sun.COM (sep = *sepp) != NULL; 6748721SJonathan.Adams@Sun.COM sepp = &sep->se_next) { 6758721SJonathan.Adams@Sun.COM 6768721SJonathan.Adams@Sun.COM if (stacks_entry_comp_impl(sep, nsep, 0) != 0) 6778721SJonathan.Adams@Sun.COM continue; 6788721SJonathan.Adams@Sun.COM 6798721SJonathan.Adams@Sun.COM nsep->se_dup = sep->se_dup; 6808721SJonathan.Adams@Sun.COM sep->se_dup = nsep; 6818721SJonathan.Adams@Sun.COM sep->se_count++; 6828721SJonathan.Adams@Sun.COM return (WALK_NEXT); 6838721SJonathan.Adams@Sun.COM } 6848721SJonathan.Adams@Sun.COM 6858721SJonathan.Adams@Sun.COM nsep->se_next = NULL; 6868721SJonathan.Adams@Sun.COM *sepp = nsep; 6878721SJonathan.Adams@Sun.COM sip->si_entries++; 6888721SJonathan.Adams@Sun.COM 6898721SJonathan.Adams@Sun.COM return (WALK_NEXT); 6908721SJonathan.Adams@Sun.COM } 6918721SJonathan.Adams@Sun.COM 6928721SJonathan.Adams@Sun.COM int 6938721SJonathan.Adams@Sun.COM stacks_run(int verbose) 6948721SJonathan.Adams@Sun.COM { 6958721SJonathan.Adams@Sun.COM stacks_info_t si; 6968721SJonathan.Adams@Sun.COM findstack_info_t *fsip = &si.si_fsi; 6978721SJonathan.Adams@Sun.COM size_t idx; 6988721SJonathan.Adams@Sun.COM stacks_entry_t **cur; 6998721SJonathan.Adams@Sun.COM 7008721SJonathan.Adams@Sun.COM bzero(&si, sizeof (si)); 7018721SJonathan.Adams@Sun.COM 7028721SJonathan.Adams@Sun.COM stacks_state = STACKS_STATE_DIRTY; 7038721SJonathan.Adams@Sun.COM 7048721SJonathan.Adams@Sun.COM stacks_hash = si.si_hash = 7058721SJonathan.Adams@Sun.COM mdb_zalloc(STACKS_HSIZE * sizeof (*si.si_hash), UM_SLEEP); 7068721SJonathan.Adams@Sun.COM si.si_entries = 0; 7078721SJonathan.Adams@Sun.COM si.si_count = 0; 7088721SJonathan.Adams@Sun.COM 7098721SJonathan.Adams@Sun.COM fsip->fsi_max_depth = STACKS_MAX_DEPTH; 7108721SJonathan.Adams@Sun.COM fsip->fsi_stack = 7118721SJonathan.Adams@Sun.COM mdb_alloc(fsip->fsi_max_depth * sizeof (*fsip->fsi_stack), 7128721SJonathan.Adams@Sun.COM UM_SLEEP | UM_GC); 7138721SJonathan.Adams@Sun.COM 7148721SJonathan.Adams@Sun.COM if (verbose) 7158721SJonathan.Adams@Sun.COM mdb_warn("stacks: processing kernel threads\n"); 7168721SJonathan.Adams@Sun.COM 7178721SJonathan.Adams@Sun.COM if (mdb_walk("thread", stacks_thread_cb, &si) != 0) { 7188721SJonathan.Adams@Sun.COM mdb_warn("cannot walk \"thread\""); 7198721SJonathan.Adams@Sun.COM return (DCMD_ERR); 7208721SJonathan.Adams@Sun.COM } 7218721SJonathan.Adams@Sun.COM 7228721SJonathan.Adams@Sun.COM if (verbose) 7238721SJonathan.Adams@Sun.COM mdb_warn("stacks: %d unique stacks / %d threads\n", 7248721SJonathan.Adams@Sun.COM si.si_entries, si.si_count); 7258721SJonathan.Adams@Sun.COM 7268721SJonathan.Adams@Sun.COM stacks_array_size = si.si_entries; 7278721SJonathan.Adams@Sun.COM stacks_array = 7288721SJonathan.Adams@Sun.COM mdb_zalloc(si.si_entries * sizeof (*stacks_array), UM_SLEEP); 7298721SJonathan.Adams@Sun.COM cur = stacks_array; 7308721SJonathan.Adams@Sun.COM for (idx = 0; idx < STACKS_HSIZE; idx++) { 7318721SJonathan.Adams@Sun.COM stacks_entry_t *sep; 7328721SJonathan.Adams@Sun.COM for (sep = si.si_hash[idx]; sep != NULL; sep = sep->se_next) 7338721SJonathan.Adams@Sun.COM *(cur++) = sep; 7348721SJonathan.Adams@Sun.COM } 7358721SJonathan.Adams@Sun.COM 7368721SJonathan.Adams@Sun.COM if (cur != stacks_array + si.si_entries) { 7378721SJonathan.Adams@Sun.COM mdb_warn("stacks: miscounted array size (%d != size: %d)\n", 7388721SJonathan.Adams@Sun.COM (cur - stacks_array), stacks_array_size); 7398721SJonathan.Adams@Sun.COM return (DCMD_ERR); 7408721SJonathan.Adams@Sun.COM } 7418721SJonathan.Adams@Sun.COM qsort(stacks_array, si.si_entries, sizeof (*stacks_array), 7428721SJonathan.Adams@Sun.COM stacks_entry_comp); 7438721SJonathan.Adams@Sun.COM 7448721SJonathan.Adams@Sun.COM /* Now that we're done, free the hash table */ 7458721SJonathan.Adams@Sun.COM stacks_hash = NULL; 7468721SJonathan.Adams@Sun.COM mdb_free(si.si_hash, STACKS_HSIZE * sizeof (*si.si_hash)); 7478721SJonathan.Adams@Sun.COM 7488721SJonathan.Adams@Sun.COM stacks_state = STACKS_STATE_DONE; 7498721SJonathan.Adams@Sun.COM 7508721SJonathan.Adams@Sun.COM if (verbose) 7518721SJonathan.Adams@Sun.COM mdb_warn("stacks: done\n"); 7520Sstevel@tonic-gate 7530Sstevel@tonic-gate return (DCMD_OK); 7540Sstevel@tonic-gate } 7558721SJonathan.Adams@Sun.COM 7568721SJonathan.Adams@Sun.COM static int 7578721SJonathan.Adams@Sun.COM stacks_has_caller(stacks_entry_t *sep, uintptr_t addr) 7588721SJonathan.Adams@Sun.COM { 7598721SJonathan.Adams@Sun.COM uintptr_t laddr = addr; 7608721SJonathan.Adams@Sun.COM uintptr_t haddr = addr + 1; 7618721SJonathan.Adams@Sun.COM int idx; 7628721SJonathan.Adams@Sun.COM char c[MDB_SYM_NAMLEN]; 7638721SJonathan.Adams@Sun.COM GElf_Sym sym; 7648721SJonathan.Adams@Sun.COM 7658721SJonathan.Adams@Sun.COM if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY, 7668721SJonathan.Adams@Sun.COM c, sizeof (c), &sym) != -1 && 7678721SJonathan.Adams@Sun.COM addr == (uintptr_t)sym.st_value) { 7688721SJonathan.Adams@Sun.COM laddr = (uintptr_t)sym.st_value; 7698721SJonathan.Adams@Sun.COM haddr = (uintptr_t)sym.st_value + sym.st_size; 7708721SJonathan.Adams@Sun.COM } 7718721SJonathan.Adams@Sun.COM 7728721SJonathan.Adams@Sun.COM for (idx = 0; idx < sep->se_depth; idx++) 7738721SJonathan.Adams@Sun.COM if (sep->se_stack[idx] >= laddr && sep->se_stack[idx] < haddr) 7748721SJonathan.Adams@Sun.COM return (1); 7758721SJonathan.Adams@Sun.COM 7768721SJonathan.Adams@Sun.COM return (0); 7778721SJonathan.Adams@Sun.COM } 7788721SJonathan.Adams@Sun.COM 779*8742Sgap@sun.com typedef struct find_module_struct { 780*8742Sgap@sun.com struct modctl *mcp; 781*8742Sgap@sun.com const char *name; 782*8742Sgap@sun.com } find_module_struct_t; 783*8742Sgap@sun.com 784*8742Sgap@sun.com int 785*8742Sgap@sun.com find_module_cb(uintptr_t addr, const void *modctl_arg, void *cbarg) 786*8742Sgap@sun.com { 787*8742Sgap@sun.com find_module_struct_t *sp = cbarg; 788*8742Sgap@sun.com char mod_modname[MODMAXNAMELEN + 1]; 789*8742Sgap@sun.com const struct modctl *mp = modctl_arg; 790*8742Sgap@sun.com 791*8742Sgap@sun.com if (!mp->mod_modname) 792*8742Sgap@sun.com return (WALK_NEXT); 793*8742Sgap@sun.com 794*8742Sgap@sun.com if (mdb_readstr(mod_modname, sizeof (mod_modname), 795*8742Sgap@sun.com (uintptr_t)mp->mod_modname) == -1) { 796*8742Sgap@sun.com mdb_warn("failed to read mod_modname in \"modctl\" walk"); 797*8742Sgap@sun.com return (WALK_ERR); 798*8742Sgap@sun.com } 799*8742Sgap@sun.com 800*8742Sgap@sun.com if (strcmp(sp->name, mod_modname)) 801*8742Sgap@sun.com return (WALK_NEXT); 802*8742Sgap@sun.com 803*8742Sgap@sun.com sp->mcp = mdb_alloc(sizeof (*sp->mcp), UM_SLEEP | UM_GC); 804*8742Sgap@sun.com bcopy(mp, sp->mcp, sizeof (*sp->mcp)); 805*8742Sgap@sun.com return (WALK_DONE); 806*8742Sgap@sun.com } 807*8742Sgap@sun.com 808*8742Sgap@sun.com static struct modctl * 809*8742Sgap@sun.com find_module(const char *name) 810*8742Sgap@sun.com { 811*8742Sgap@sun.com find_module_struct_t mptr; 812*8742Sgap@sun.com 813*8742Sgap@sun.com mptr.name = name; 814*8742Sgap@sun.com mptr.mcp = NULL; 815*8742Sgap@sun.com 816*8742Sgap@sun.com if (mdb_walk("modctl", find_module_cb, &mptr) != 0) 817*8742Sgap@sun.com mdb_warn("cannot walk \"modctl\""); 818*8742Sgap@sun.com return (mptr.mcp); 819*8742Sgap@sun.com } 820*8742Sgap@sun.com 821*8742Sgap@sun.com static int 822*8742Sgap@sun.com stacks_has_module(stacks_entry_t *sep, struct modctl *mp) 823*8742Sgap@sun.com { 824*8742Sgap@sun.com int idx; 825*8742Sgap@sun.com 826*8742Sgap@sun.com if (mp == NULL) 827*8742Sgap@sun.com return (0); 828*8742Sgap@sun.com 829*8742Sgap@sun.com for (idx = 0; idx < sep->se_depth; idx++) 830*8742Sgap@sun.com if (sep->se_stack[idx] >= (uintptr_t)mp->mod_text && 831*8742Sgap@sun.com sep->se_stack[idx] < 832*8742Sgap@sun.com ((uintptr_t)mp->mod_text + mp->mod_text_size)) 833*8742Sgap@sun.com return (1); 834*8742Sgap@sun.com return (0); 835*8742Sgap@sun.com } 836*8742Sgap@sun.com 837*8742Sgap@sun.com 8388721SJonathan.Adams@Sun.COM static int 8398721SJonathan.Adams@Sun.COM uintptrcomp(const void *lp, const void *rp) 8408721SJonathan.Adams@Sun.COM { 8418721SJonathan.Adams@Sun.COM uintptr_t lhs = *(const uintptr_t *)lp; 8428721SJonathan.Adams@Sun.COM uintptr_t rhs = *(const uintptr_t *)rp; 8438721SJonathan.Adams@Sun.COM if (lhs > rhs) 8448721SJonathan.Adams@Sun.COM return (1); 8458721SJonathan.Adams@Sun.COM if (lhs < rhs) 8468721SJonathan.Adams@Sun.COM return (-1); 8478721SJonathan.Adams@Sun.COM return (0); 8488721SJonathan.Adams@Sun.COM } 8498721SJonathan.Adams@Sun.COM 8508721SJonathan.Adams@Sun.COM /*ARGSUSED*/ 8518721SJonathan.Adams@Sun.COM static void 8528721SJonathan.Adams@Sun.COM print_sobj_help(int type, const char *name, const char *ops_name, void *ign) 8538721SJonathan.Adams@Sun.COM { 8548721SJonathan.Adams@Sun.COM mdb_printf(" %s", name); 8558721SJonathan.Adams@Sun.COM } 8568721SJonathan.Adams@Sun.COM 8578721SJonathan.Adams@Sun.COM /*ARGSUSED*/ 8588721SJonathan.Adams@Sun.COM static void 8598721SJonathan.Adams@Sun.COM print_tstate_help(uint_t state, const char *name, void *ignored) 8608721SJonathan.Adams@Sun.COM { 8618721SJonathan.Adams@Sun.COM mdb_printf(" %s", name); 8628721SJonathan.Adams@Sun.COM } 8638721SJonathan.Adams@Sun.COM 8648721SJonathan.Adams@Sun.COM void 8658721SJonathan.Adams@Sun.COM stacks_help(void) 8668721SJonathan.Adams@Sun.COM { 8678721SJonathan.Adams@Sun.COM mdb_printf( 8688721SJonathan.Adams@Sun.COM "::stacks processes all of the thread stacks on the system, grouping\n" 8698721SJonathan.Adams@Sun.COM "together threads which have the same:\n" 8708721SJonathan.Adams@Sun.COM "\n" 8718721SJonathan.Adams@Sun.COM " * Thread state,\n" 8728721SJonathan.Adams@Sun.COM " * Sync object type, and\n" 8738721SJonathan.Adams@Sun.COM " * PCs in their stack trace.\n" 8748721SJonathan.Adams@Sun.COM "\n" 8758721SJonathan.Adams@Sun.COM "The default output (no address or options) is just a dump of the thread\n" 8768721SJonathan.Adams@Sun.COM "groups in the system. For a view of active threads, use \"::stacks -i\",\n" 8778721SJonathan.Adams@Sun.COM "which filters out FREE threads (interrupt threads which are currently\n" 8788721SJonathan.Adams@Sun.COM "inactive) and threads sleeping on a CV. (Note that those threads may still\n" 8798721SJonathan.Adams@Sun.COM "be noteworthy; this is just for a first glance.) More general filtering\n" 8808721SJonathan.Adams@Sun.COM "options are described below, in the \"FILTERS\" section.\n" 8818721SJonathan.Adams@Sun.COM "\n" 8828721SJonathan.Adams@Sun.COM "::stacks can be used in a pipeline. The input to ::stacks is one or more\n" 8838721SJonathan.Adams@Sun.COM "thread pointers. For example, to get a summary of threads in a process,\n" 8848721SJonathan.Adams@Sun.COM "you can do:\n" 8858721SJonathan.Adams@Sun.COM "\n" 8868721SJonathan.Adams@Sun.COM " %<b>procp%</b>::walk thread | ::stacks\n" 8878721SJonathan.Adams@Sun.COM "\n" 8888721SJonathan.Adams@Sun.COM "When output into a pipe, ::stacks prints all of the threads input,\n" 8898721SJonathan.Adams@Sun.COM "filtered by the given filtering options. This means that multiple\n" 8908721SJonathan.Adams@Sun.COM "::stacks invocations can be piped together to achieve more complicated\n" 8918721SJonathan.Adams@Sun.COM "filters. For example, to get threads which have both 'fop_read' and\n" 8928721SJonathan.Adams@Sun.COM "'cv_wait_sig_swap' in their stack trace, you could do:\n" 8938721SJonathan.Adams@Sun.COM "\n" 8948721SJonathan.Adams@Sun.COM " ::stacks -c fop_read | ::stacks -c cv_wait_sig_swap_core\n" 8958721SJonathan.Adams@Sun.COM "\n" 8968721SJonathan.Adams@Sun.COM "To get the full list of threads in each group, use the '-a' flag:\n" 8978721SJonathan.Adams@Sun.COM "\n" 8988721SJonathan.Adams@Sun.COM " ::stacks -a\n" 8998721SJonathan.Adams@Sun.COM "\n"); 9008721SJonathan.Adams@Sun.COM mdb_dec_indent(2); 9018721SJonathan.Adams@Sun.COM mdb_printf("%<b>OPTIONS%</b>\n"); 9028721SJonathan.Adams@Sun.COM mdb_inc_indent(2); 9038721SJonathan.Adams@Sun.COM mdb_printf("%s", 9048721SJonathan.Adams@Sun.COM " -a Print all of the grouped threads, instead of just a count.\n" 9058721SJonathan.Adams@Sun.COM " -f Force a re-run of the thread stack gathering.\n" 9068721SJonathan.Adams@Sun.COM " -v Be verbose about thread stack gathering.\n" 9078721SJonathan.Adams@Sun.COM "\n"); 9088721SJonathan.Adams@Sun.COM mdb_dec_indent(2); 9098721SJonathan.Adams@Sun.COM mdb_printf("%<b>FILTERS%</b>\n"); 9108721SJonathan.Adams@Sun.COM mdb_inc_indent(2); 9118721SJonathan.Adams@Sun.COM mdb_printf("%s", 9128721SJonathan.Adams@Sun.COM " -i Show active threads; equivalent to '-S CV -T FREE'.\n" 9138721SJonathan.Adams@Sun.COM " -c func[+offset]\n" 9148721SJonathan.Adams@Sun.COM " Only print threads whose stacks contain func/func+offset.\n" 9158721SJonathan.Adams@Sun.COM " -C func[+offset]\n" 9168721SJonathan.Adams@Sun.COM " Only print threads whose stacks do not contain func/func+offset.\n" 917*8742Sgap@sun.com " -m module\n" 918*8742Sgap@sun.com " Only print threads whose stacks contain functions from module.\n" 919*8742Sgap@sun.com " -M module\n" 920*8742Sgap@sun.com " Only print threads whose stacks do not contain functions from\n" 921*8742Sgap@sun.com " module.\n" 9228721SJonathan.Adams@Sun.COM " -s {type | ALL}\n" 9238721SJonathan.Adams@Sun.COM " Only print threads which are on a 'type' synchronization object\n" 9248721SJonathan.Adams@Sun.COM " (SOBJ).\n" 9258721SJonathan.Adams@Sun.COM " -S {type | ALL}\n" 9268721SJonathan.Adams@Sun.COM " Only print threads which are not on a 'type' SOBJ.\n" 9278721SJonathan.Adams@Sun.COM " -t tstate\n" 9288721SJonathan.Adams@Sun.COM " Only print threads which are in thread state 'tstate'.\n" 9298721SJonathan.Adams@Sun.COM " -T tstate\n" 9308721SJonathan.Adams@Sun.COM " Only print threads which are not in thread state 'tstate'.\n" 9318721SJonathan.Adams@Sun.COM "\n"); 9328721SJonathan.Adams@Sun.COM mdb_printf(" SOBJ types:"); 9338721SJonathan.Adams@Sun.COM sobj_type_walk(print_sobj_help, NULL); 9348721SJonathan.Adams@Sun.COM mdb_printf("\n"); 9358721SJonathan.Adams@Sun.COM mdb_printf("Thread states:"); 9368721SJonathan.Adams@Sun.COM thread_walk_states(print_tstate_help, NULL); 9378721SJonathan.Adams@Sun.COM mdb_printf(" panic\n"); 9388721SJonathan.Adams@Sun.COM } 9398721SJonathan.Adams@Sun.COM 9408721SJonathan.Adams@Sun.COM /*ARGSUSED*/ 9418721SJonathan.Adams@Sun.COM int 9428721SJonathan.Adams@Sun.COM stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 9438721SJonathan.Adams@Sun.COM { 9448721SJonathan.Adams@Sun.COM size_t idx; 9458721SJonathan.Adams@Sun.COM 9468721SJonathan.Adams@Sun.COM char *seen = NULL; 9478721SJonathan.Adams@Sun.COM 9488721SJonathan.Adams@Sun.COM const char *caller_str = NULL; 9498721SJonathan.Adams@Sun.COM const char *excl_caller_str = NULL; 9508721SJonathan.Adams@Sun.COM uintptr_t caller = 0, excl_caller = 0; 951*8742Sgap@sun.com const char *module_str = NULL; 952*8742Sgap@sun.com const char *excl_module_str = NULL; 953*8742Sgap@sun.com struct modctl *module = NULL, *excl_module = NULL; 9548721SJonathan.Adams@Sun.COM const char *sobj = NULL; 9558721SJonathan.Adams@Sun.COM const char *excl_sobj = NULL; 9568721SJonathan.Adams@Sun.COM uintptr_t sobj_ops = 0, excl_sobj_ops = 0; 9578721SJonathan.Adams@Sun.COM const char *tstate_str = NULL; 9588721SJonathan.Adams@Sun.COM const char *excl_tstate_str = NULL; 9598721SJonathan.Adams@Sun.COM uint_t tstate = -1U; 9608721SJonathan.Adams@Sun.COM uint_t excl_tstate = -1U; 9618721SJonathan.Adams@Sun.COM 9628721SJonathan.Adams@Sun.COM uint_t all = 0; 9638721SJonathan.Adams@Sun.COM uint_t force = 0; 9648721SJonathan.Adams@Sun.COM uint_t interesting = 0; 9658721SJonathan.Adams@Sun.COM uint_t verbose = 0; 9668721SJonathan.Adams@Sun.COM 9678721SJonathan.Adams@Sun.COM /* 9688721SJonathan.Adams@Sun.COM * We have a slight behavior difference between having piped 9698721SJonathan.Adams@Sun.COM * input and 'addr::stacks'. Without a pipe, we assume the 9708721SJonathan.Adams@Sun.COM * thread pointer given is a representative thread, and so 9718721SJonathan.Adams@Sun.COM * we include all similar threads in the system in our output. 9728721SJonathan.Adams@Sun.COM * 9738721SJonathan.Adams@Sun.COM * With a pipe, we filter down to just the threads in our 9748721SJonathan.Adams@Sun.COM * input. 9758721SJonathan.Adams@Sun.COM */ 9768721SJonathan.Adams@Sun.COM uint_t addrspec = (flags & DCMD_ADDRSPEC); 9778721SJonathan.Adams@Sun.COM uint_t only_matching = addrspec && (flags & DCMD_PIPE); 9788721SJonathan.Adams@Sun.COM 9798721SJonathan.Adams@Sun.COM mdb_pipe_t p; 9808721SJonathan.Adams@Sun.COM 9818721SJonathan.Adams@Sun.COM if (mdb_getopts(argc, argv, 9828721SJonathan.Adams@Sun.COM 'a', MDB_OPT_SETBITS, TRUE, &all, 9838721SJonathan.Adams@Sun.COM 'f', MDB_OPT_SETBITS, TRUE, &force, 9848721SJonathan.Adams@Sun.COM 'i', MDB_OPT_SETBITS, TRUE, &interesting, 9858721SJonathan.Adams@Sun.COM 'v', MDB_OPT_SETBITS, TRUE, &verbose, 9868721SJonathan.Adams@Sun.COM 'c', MDB_OPT_STR, &caller_str, 9878721SJonathan.Adams@Sun.COM 'C', MDB_OPT_STR, &excl_caller_str, 988*8742Sgap@sun.com 'm', MDB_OPT_STR, &module_str, 989*8742Sgap@sun.com 'M', MDB_OPT_STR, &excl_module_str, 9908721SJonathan.Adams@Sun.COM 's', MDB_OPT_STR, &sobj, 9918721SJonathan.Adams@Sun.COM 'S', MDB_OPT_STR, &excl_sobj, 9928721SJonathan.Adams@Sun.COM 't', MDB_OPT_STR, &tstate_str, 9938721SJonathan.Adams@Sun.COM 'T', MDB_OPT_STR, &excl_tstate_str, 9948721SJonathan.Adams@Sun.COM NULL) != argc) 9958721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 9968721SJonathan.Adams@Sun.COM 9978721SJonathan.Adams@Sun.COM if (interesting) { 9988721SJonathan.Adams@Sun.COM if (sobj != NULL || excl_sobj != NULL || 9998721SJonathan.Adams@Sun.COM tstate_str != NULL || excl_tstate_str != NULL) { 10008721SJonathan.Adams@Sun.COM mdb_warn( 10018721SJonathan.Adams@Sun.COM "stacks: -i is incompatible with -[sStT]\n"); 10028721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 10038721SJonathan.Adams@Sun.COM } 10048721SJonathan.Adams@Sun.COM excl_sobj = "CV"; 10058721SJonathan.Adams@Sun.COM excl_tstate_str = "FREE"; 10068721SJonathan.Adams@Sun.COM } 10078721SJonathan.Adams@Sun.COM 10088721SJonathan.Adams@Sun.COM if (caller_str != NULL) { 10098721SJonathan.Adams@Sun.COM mdb_set_dot(0); 10108721SJonathan.Adams@Sun.COM if (mdb_eval(caller_str) != 0) { 10118721SJonathan.Adams@Sun.COM mdb_warn("stacks: evaluation of \"%s\" failed", 10128721SJonathan.Adams@Sun.COM caller_str); 10138721SJonathan.Adams@Sun.COM return (DCMD_ABORT); 10148721SJonathan.Adams@Sun.COM } 10158721SJonathan.Adams@Sun.COM caller = mdb_get_dot(); 10168721SJonathan.Adams@Sun.COM } 10178721SJonathan.Adams@Sun.COM 10188721SJonathan.Adams@Sun.COM if (excl_caller_str != NULL) { 10198721SJonathan.Adams@Sun.COM mdb_set_dot(0); 10208721SJonathan.Adams@Sun.COM if (mdb_eval(excl_caller_str) != 0) { 10218721SJonathan.Adams@Sun.COM mdb_warn("stacks: evaluation of \"%s\" failed", 10228721SJonathan.Adams@Sun.COM excl_caller_str); 10238721SJonathan.Adams@Sun.COM return (DCMD_ABORT); 10248721SJonathan.Adams@Sun.COM } 10258721SJonathan.Adams@Sun.COM excl_caller = mdb_get_dot(); 10268721SJonathan.Adams@Sun.COM } 10278721SJonathan.Adams@Sun.COM mdb_set_dot(addr); 10288721SJonathan.Adams@Sun.COM 1029*8742Sgap@sun.com if (module_str != NULL && 1030*8742Sgap@sun.com (module = find_module(module_str)) == NULL) { 1031*8742Sgap@sun.com mdb_warn("stacks: module \"%s\" is unknown", module_str); 1032*8742Sgap@sun.com return (DCMD_ABORT); 1033*8742Sgap@sun.com } 1034*8742Sgap@sun.com 1035*8742Sgap@sun.com if (excl_module_str != NULL && 1036*8742Sgap@sun.com (excl_module = find_module(excl_module_str)) == NULL) { 1037*8742Sgap@sun.com mdb_warn("stacks: module \"%s\" is unknown", excl_module_str); 1038*8742Sgap@sun.com return (DCMD_ABORT); 1039*8742Sgap@sun.com } 1040*8742Sgap@sun.com 10418721SJonathan.Adams@Sun.COM if (sobj != NULL && 10428721SJonathan.Adams@Sun.COM text_to_sobj(sobj, &sobj_ops) != 0) 10438721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 10448721SJonathan.Adams@Sun.COM 10458721SJonathan.Adams@Sun.COM if (excl_sobj != NULL && 10468721SJonathan.Adams@Sun.COM text_to_sobj(excl_sobj, &excl_sobj_ops) != 0) 10478721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 10488721SJonathan.Adams@Sun.COM 10498721SJonathan.Adams@Sun.COM if (sobj_ops != 0 && excl_sobj_ops != 0) { 10508721SJonathan.Adams@Sun.COM mdb_warn("stacks: only one of -s and -S can be specified\n"); 10518721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 10528721SJonathan.Adams@Sun.COM } 10538721SJonathan.Adams@Sun.COM 1054*8742Sgap@sun.com if (tstate_str != NULL && 10558721SJonathan.Adams@Sun.COM text_to_tstate(tstate_str, &tstate) != 0) 10568721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 1057*8742Sgap@sun.com 1058*8742Sgap@sun.com if (excl_tstate_str != NULL && 10598721SJonathan.Adams@Sun.COM text_to_tstate(excl_tstate_str, &excl_tstate) != 0) 10608721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 10618721SJonathan.Adams@Sun.COM 10628721SJonathan.Adams@Sun.COM if (tstate != -1U && excl_tstate != -1U) { 10638721SJonathan.Adams@Sun.COM mdb_warn("stacks: only one of -t and -T can be specified\n"); 10648721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 10658721SJonathan.Adams@Sun.COM } 10668721SJonathan.Adams@Sun.COM 10678721SJonathan.Adams@Sun.COM /* 10688721SJonathan.Adams@Sun.COM * Force a cleanup if we're connected to a live system. Never 10698721SJonathan.Adams@Sun.COM * do a cleanup after the first invocation around the loop. 10708721SJonathan.Adams@Sun.COM */ 10718721SJonathan.Adams@Sun.COM force |= (mdb_get_state() == MDB_STATE_RUNNING); 10728721SJonathan.Adams@Sun.COM if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP) 10738721SJonathan.Adams@Sun.COM force = 0; 10748721SJonathan.Adams@Sun.COM 10758721SJonathan.Adams@Sun.COM stacks_cleanup(force); 10768721SJonathan.Adams@Sun.COM 10778721SJonathan.Adams@Sun.COM if (stacks_state == STACKS_STATE_CLEAN) { 10788721SJonathan.Adams@Sun.COM int res = stacks_run(verbose); 10798721SJonathan.Adams@Sun.COM if (res != DCMD_OK) 10808721SJonathan.Adams@Sun.COM return (res); 10818721SJonathan.Adams@Sun.COM } 10828721SJonathan.Adams@Sun.COM 10838721SJonathan.Adams@Sun.COM if (!all && DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) { 10848721SJonathan.Adams@Sun.COM mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n", 10858721SJonathan.Adams@Sun.COM "THREAD", "STATE", "SOBJ", "COUNT"); 10868721SJonathan.Adams@Sun.COM } 10878721SJonathan.Adams@Sun.COM 10888721SJonathan.Adams@Sun.COM /* 10898721SJonathan.Adams@Sun.COM * If there's an address specified, we're going to further filter 10908721SJonathan.Adams@Sun.COM * to only entries which have an address in the input. To reduce 10918721SJonathan.Adams@Sun.COM * overhead (and make the sorted output come out right), we 10928721SJonathan.Adams@Sun.COM * use mdb_get_pipe() to grab the entire pipeline of input, then 10938721SJonathan.Adams@Sun.COM * use qsort() and bsearch() to speed up the search. 10948721SJonathan.Adams@Sun.COM */ 10958721SJonathan.Adams@Sun.COM if (addrspec) { 10968721SJonathan.Adams@Sun.COM mdb_get_pipe(&p); 10978721SJonathan.Adams@Sun.COM if (p.pipe_data == NULL || p.pipe_len == 0) { 10988721SJonathan.Adams@Sun.COM p.pipe_data = &addr; 10998721SJonathan.Adams@Sun.COM p.pipe_len = 1; 11008721SJonathan.Adams@Sun.COM } 11018721SJonathan.Adams@Sun.COM qsort(p.pipe_data, p.pipe_len, sizeof (uintptr_t), 11028721SJonathan.Adams@Sun.COM uintptrcomp); 11038721SJonathan.Adams@Sun.COM 11048721SJonathan.Adams@Sun.COM /* remove any duplicates in the data */ 11058721SJonathan.Adams@Sun.COM idx = 0; 11068721SJonathan.Adams@Sun.COM while (idx < p.pipe_len - 1) { 11078721SJonathan.Adams@Sun.COM uintptr_t *data = &p.pipe_data[idx]; 11088721SJonathan.Adams@Sun.COM size_t len = p.pipe_len - idx; 11098721SJonathan.Adams@Sun.COM 11108721SJonathan.Adams@Sun.COM if (data[0] == data[1]) { 11118721SJonathan.Adams@Sun.COM memmove(data, data + 1, 11128721SJonathan.Adams@Sun.COM (len - 1) * sizeof (*data)); 11138721SJonathan.Adams@Sun.COM p.pipe_len--; 11148721SJonathan.Adams@Sun.COM continue; /* repeat without incrementing idx */ 11158721SJonathan.Adams@Sun.COM } 11168721SJonathan.Adams@Sun.COM idx++; 11178721SJonathan.Adams@Sun.COM } 11188721SJonathan.Adams@Sun.COM 11198721SJonathan.Adams@Sun.COM seen = mdb_zalloc(p.pipe_len, UM_SLEEP | UM_GC); 11208721SJonathan.Adams@Sun.COM } 11218721SJonathan.Adams@Sun.COM 11228721SJonathan.Adams@Sun.COM for (idx = 0; idx < stacks_array_size; idx++) { 11238721SJonathan.Adams@Sun.COM stacks_entry_t *sep = stacks_array[idx]; 11248721SJonathan.Adams@Sun.COM stacks_entry_t *cur = sep; 11258721SJonathan.Adams@Sun.COM int frame; 11268721SJonathan.Adams@Sun.COM size_t count = sep->se_count; 11278721SJonathan.Adams@Sun.COM 11288721SJonathan.Adams@Sun.COM if (addrspec) { 11298721SJonathan.Adams@Sun.COM stacks_entry_t *head = NULL, *tail = NULL, *sp; 11308721SJonathan.Adams@Sun.COM size_t foundcount = 0; 11318721SJonathan.Adams@Sun.COM /* 11328721SJonathan.Adams@Sun.COM * We use the now-unused hash chain field se_next to 11338721SJonathan.Adams@Sun.COM * link together the dups which match our list. 11348721SJonathan.Adams@Sun.COM */ 11358721SJonathan.Adams@Sun.COM for (sp = sep; sp != NULL; sp = sp->se_dup) { 11368721SJonathan.Adams@Sun.COM uintptr_t *entry = bsearch(&sp->se_thread, 11378721SJonathan.Adams@Sun.COM p.pipe_data, p.pipe_len, sizeof (uintptr_t), 11388721SJonathan.Adams@Sun.COM uintptrcomp); 11398721SJonathan.Adams@Sun.COM if (entry != NULL) { 11408721SJonathan.Adams@Sun.COM foundcount++; 11418721SJonathan.Adams@Sun.COM seen[entry - p.pipe_data]++; 11428721SJonathan.Adams@Sun.COM if (head == NULL) 11438721SJonathan.Adams@Sun.COM head = sp; 11448721SJonathan.Adams@Sun.COM else 11458721SJonathan.Adams@Sun.COM tail->se_next = sp; 11468721SJonathan.Adams@Sun.COM tail = sp; 11478721SJonathan.Adams@Sun.COM sp->se_next = NULL; 11488721SJonathan.Adams@Sun.COM } 11498721SJonathan.Adams@Sun.COM } 11508721SJonathan.Adams@Sun.COM if (head == NULL) 11518721SJonathan.Adams@Sun.COM continue; /* no match, skip entry */ 11528721SJonathan.Adams@Sun.COM 11538721SJonathan.Adams@Sun.COM if (only_matching) { 11548721SJonathan.Adams@Sun.COM cur = sep = head; 11558721SJonathan.Adams@Sun.COM count = foundcount; 11568721SJonathan.Adams@Sun.COM } 11578721SJonathan.Adams@Sun.COM } 11588721SJonathan.Adams@Sun.COM 11598721SJonathan.Adams@Sun.COM if (caller != 0 && !stacks_has_caller(sep, caller)) 11608721SJonathan.Adams@Sun.COM continue; 11618721SJonathan.Adams@Sun.COM if (excl_caller != 0 && stacks_has_caller(sep, excl_caller)) 11628721SJonathan.Adams@Sun.COM continue; 1163*8742Sgap@sun.com if (module != 0 && !stacks_has_module(sep, module)) 1164*8742Sgap@sun.com continue; 1165*8742Sgap@sun.com if (excl_module != 0 && stacks_has_module(sep, excl_module)) 1166*8742Sgap@sun.com continue; 11678721SJonathan.Adams@Sun.COM 11688721SJonathan.Adams@Sun.COM if (tstate != -1U) { 11698721SJonathan.Adams@Sun.COM if (tstate == TSTATE_PANIC) { 11708721SJonathan.Adams@Sun.COM if (!sep->se_panic) 11718721SJonathan.Adams@Sun.COM continue; 11728721SJonathan.Adams@Sun.COM } else if (sep->se_panic || sep->se_tstate != tstate) 11738721SJonathan.Adams@Sun.COM continue; 11748721SJonathan.Adams@Sun.COM } 11758721SJonathan.Adams@Sun.COM if (excl_tstate != -1U) { 11768721SJonathan.Adams@Sun.COM if (excl_tstate == TSTATE_PANIC) { 11778721SJonathan.Adams@Sun.COM if (sep->se_panic) 11788721SJonathan.Adams@Sun.COM continue; 11798721SJonathan.Adams@Sun.COM } else if (!sep->se_panic && 11808721SJonathan.Adams@Sun.COM sep->se_tstate == excl_tstate) 11818721SJonathan.Adams@Sun.COM continue; 11828721SJonathan.Adams@Sun.COM } 11838721SJonathan.Adams@Sun.COM 11848721SJonathan.Adams@Sun.COM if (sobj_ops == SOBJ_ALL) { 11858721SJonathan.Adams@Sun.COM if (sep->se_sobj_ops == 0) 11868721SJonathan.Adams@Sun.COM continue; 11878721SJonathan.Adams@Sun.COM } else if (sobj_ops != 0) { 11888721SJonathan.Adams@Sun.COM if (sobj_ops != sep->se_sobj_ops) 11898721SJonathan.Adams@Sun.COM continue; 11908721SJonathan.Adams@Sun.COM } 11918721SJonathan.Adams@Sun.COM 11928721SJonathan.Adams@Sun.COM if (!(interesting && sep->se_panic)) { 11938721SJonathan.Adams@Sun.COM if (excl_sobj_ops == SOBJ_ALL) { 11948721SJonathan.Adams@Sun.COM if (sep->se_sobj_ops != 0) 11958721SJonathan.Adams@Sun.COM continue; 11968721SJonathan.Adams@Sun.COM } else if (excl_sobj_ops != 0) { 11978721SJonathan.Adams@Sun.COM if (excl_sobj_ops == sep->se_sobj_ops) 11988721SJonathan.Adams@Sun.COM continue; 11998721SJonathan.Adams@Sun.COM } 12008721SJonathan.Adams@Sun.COM } 12018721SJonathan.Adams@Sun.COM 12028721SJonathan.Adams@Sun.COM if (flags & DCMD_PIPE_OUT) { 12038721SJonathan.Adams@Sun.COM while (sep != NULL) { 12048721SJonathan.Adams@Sun.COM mdb_printf("%lr\n", sep->se_thread); 12058721SJonathan.Adams@Sun.COM sep = only_matching ? 12068721SJonathan.Adams@Sun.COM sep->se_next : sep->se_dup; 12078721SJonathan.Adams@Sun.COM } 12088721SJonathan.Adams@Sun.COM continue; 12098721SJonathan.Adams@Sun.COM } 12108721SJonathan.Adams@Sun.COM 12118721SJonathan.Adams@Sun.COM if (all) { 12128721SJonathan.Adams@Sun.COM mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n", 12138721SJonathan.Adams@Sun.COM "THREAD", "STATE", "SOBJTYPE", "COUNT"); 12148721SJonathan.Adams@Sun.COM } 12158721SJonathan.Adams@Sun.COM 12168721SJonathan.Adams@Sun.COM do { 12178721SJonathan.Adams@Sun.COM char state[20]; 12188721SJonathan.Adams@Sun.COM char sobj[100]; 12198721SJonathan.Adams@Sun.COM 12208721SJonathan.Adams@Sun.COM tstate_to_text(cur->se_tstate, cur->se_panic, 12218721SJonathan.Adams@Sun.COM state, sizeof (state)); 12228721SJonathan.Adams@Sun.COM sobj_to_text(cur->se_sobj_ops, 12238721SJonathan.Adams@Sun.COM sobj, sizeof (sobj)); 12248721SJonathan.Adams@Sun.COM 12258721SJonathan.Adams@Sun.COM if (cur == sep) 12268721SJonathan.Adams@Sun.COM mdb_printf("%?p %-8s %-?s %8d\n", 12278721SJonathan.Adams@Sun.COM cur->se_thread, state, sobj, count); 12288721SJonathan.Adams@Sun.COM else 12298721SJonathan.Adams@Sun.COM mdb_printf("%?p %-8s %-?s %8s\n", 12308721SJonathan.Adams@Sun.COM cur->se_thread, state, sobj, "-"); 12318721SJonathan.Adams@Sun.COM 12328721SJonathan.Adams@Sun.COM cur = only_matching ? cur->se_next : cur->se_dup; 12338721SJonathan.Adams@Sun.COM } while (all && cur != NULL); 12348721SJonathan.Adams@Sun.COM 12358721SJonathan.Adams@Sun.COM if (sep->se_failed != 0) { 12368721SJonathan.Adams@Sun.COM char *reason; 12378721SJonathan.Adams@Sun.COM switch (sep->se_failed) { 12388721SJonathan.Adams@Sun.COM case FSI_FAIL_NOTINMEMORY: 12398721SJonathan.Adams@Sun.COM reason = "thread not in memory"; 12408721SJonathan.Adams@Sun.COM break; 12418721SJonathan.Adams@Sun.COM case FSI_FAIL_THREADCORRUPT: 12428721SJonathan.Adams@Sun.COM reason = "thread structure stack info corrupt"; 12438721SJonathan.Adams@Sun.COM break; 12448721SJonathan.Adams@Sun.COM case FSI_FAIL_STACKNOTFOUND: 12458721SJonathan.Adams@Sun.COM reason = "no consistent stack found"; 12468721SJonathan.Adams@Sun.COM break; 12478721SJonathan.Adams@Sun.COM default: 12488721SJonathan.Adams@Sun.COM reason = "unknown failure"; 12498721SJonathan.Adams@Sun.COM break; 12508721SJonathan.Adams@Sun.COM } 12518721SJonathan.Adams@Sun.COM mdb_printf("%?s <%s>\n", "", reason); 12528721SJonathan.Adams@Sun.COM } 12538721SJonathan.Adams@Sun.COM 12548721SJonathan.Adams@Sun.COM for (frame = 0; frame < sep->se_depth; frame++) 12558721SJonathan.Adams@Sun.COM mdb_printf("%?s %a\n", "", sep->se_stack[frame]); 12568721SJonathan.Adams@Sun.COM if (sep->se_overflow) 12578721SJonathan.Adams@Sun.COM mdb_printf("%?s ... truncated ...\n", ""); 12588721SJonathan.Adams@Sun.COM mdb_printf("\n"); 12598721SJonathan.Adams@Sun.COM } 12608721SJonathan.Adams@Sun.COM 12618721SJonathan.Adams@Sun.COM if (flags & DCMD_ADDRSPEC) { 12628721SJonathan.Adams@Sun.COM for (idx = 0; idx < p.pipe_len; idx++) 12638721SJonathan.Adams@Sun.COM if (seen[idx] == 0) 12648721SJonathan.Adams@Sun.COM mdb_warn("stacks: %p not in thread list\n", 12658721SJonathan.Adams@Sun.COM p.pipe_data[idx]); 12668721SJonathan.Adams@Sun.COM } 12678721SJonathan.Adams@Sun.COM return (DCMD_OK); 12688721SJonathan.Adams@Sun.COM } 1269