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 5*8721SJonathan.Adams@Sun.COM * Common Development and Distribution License (the "License"). 6*8721SJonathan.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 /* 22*8721SJonathan.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> 330Sstevel@tonic-gate 340Sstevel@tonic-gate #include "findstack.h" 35*8721SJonathan.Adams@Sun.COM #include "thread.h" 36*8721SJonathan.Adams@Sun.COM #include "sobj.h" 37*8721SJonathan.Adams@Sun.COM 38*8721SJonathan.Adams@Sun.COM typedef struct findstack_info { 39*8721SJonathan.Adams@Sun.COM uintptr_t *fsi_stack; /* place to record frames */ 40*8721SJonathan.Adams@Sun.COM 41*8721SJonathan.Adams@Sun.COM uintptr_t fsi_sp; /* stack pointer */ 42*8721SJonathan.Adams@Sun.COM uintptr_t fsi_pc; /* pc */ 43*8721SJonathan.Adams@Sun.COM uintptr_t fsi_sobj_ops; /* sobj_ops */ 44*8721SJonathan.Adams@Sun.COM 45*8721SJonathan.Adams@Sun.COM uint_t fsi_tstate; /* t_state */ 46*8721SJonathan.Adams@Sun.COM 47*8721SJonathan.Adams@Sun.COM uchar_t fsi_depth; /* stack depth */ 48*8721SJonathan.Adams@Sun.COM uchar_t fsi_failed; /* search failed */ 49*8721SJonathan.Adams@Sun.COM uchar_t fsi_overflow; /* stack was deeper than max_depth */ 50*8721SJonathan.Adams@Sun.COM uchar_t fsi_panic; /* thread called panic() */ 51*8721SJonathan.Adams@Sun.COM 52*8721SJonathan.Adams@Sun.COM uchar_t fsi_max_depth; /* stack frames available */ 53*8721SJonathan.Adams@Sun.COM } findstack_info_t; 54*8721SJonathan.Adams@Sun.COM #define FSI_FAIL_BADTHREAD 1 55*8721SJonathan.Adams@Sun.COM #define FSI_FAIL_NOTINMEMORY 2 56*8721SJonathan.Adams@Sun.COM #define FSI_FAIL_THREADCORRUPT 3 57*8721SJonathan.Adams@Sun.COM #define FSI_FAIL_STACKNOTFOUND 4 580Sstevel@tonic-gate 590Sstevel@tonic-gate #ifndef STACK_BIAS 600Sstevel@tonic-gate #define STACK_BIAS 0 610Sstevel@tonic-gate #endif 620Sstevel@tonic-gate 630Sstevel@tonic-gate #define fs_dprintf(x) \ 640Sstevel@tonic-gate if (findstack_debug_on) { \ 650Sstevel@tonic-gate mdb_printf("findstack debug: "); \ 660Sstevel@tonic-gate /*CSTYLED*/ \ 670Sstevel@tonic-gate mdb_printf x ; \ 680Sstevel@tonic-gate } 690Sstevel@tonic-gate 700Sstevel@tonic-gate static int findstack_debug_on = 0; 710Sstevel@tonic-gate 720Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 730Sstevel@tonic-gate struct rwindow { 740Sstevel@tonic-gate uintptr_t rw_fp; 75*8721SJonathan.Adams@Sun.COM uintptr_t rw_rtn; 760Sstevel@tonic-gate }; 770Sstevel@tonic-gate #endif 780Sstevel@tonic-gate 790Sstevel@tonic-gate #define TOO_BIG_FOR_A_STACK (1024 * 1024) 800Sstevel@tonic-gate 810Sstevel@tonic-gate #define KTOU(p) ((p) - kbase + ubase) 820Sstevel@tonic-gate #define UTOK(p) ((p) - ubase + kbase) 830Sstevel@tonic-gate 840Sstevel@tonic-gate #define CRAWL_FOUNDALL (-1) 850Sstevel@tonic-gate 860Sstevel@tonic-gate /* 870Sstevel@tonic-gate * Given a stack pointer, try to crawl down it to the bottom. 880Sstevel@tonic-gate * "frame" is a VA in MDB's address space. 890Sstevel@tonic-gate * 900Sstevel@tonic-gate * Returns the number of frames successfully crawled down, or 910Sstevel@tonic-gate * CRAWL_FOUNDALL if it got to the bottom of the stack. 920Sstevel@tonic-gate */ 930Sstevel@tonic-gate static int 940Sstevel@tonic-gate crawl(uintptr_t frame, uintptr_t kbase, uintptr_t ktop, uintptr_t ubase, 95*8721SJonathan.Adams@Sun.COM int kill_fp, findstack_info_t *fsip) 960Sstevel@tonic-gate { 970Sstevel@tonic-gate int levels = 0; 980Sstevel@tonic-gate 99*8721SJonathan.Adams@Sun.COM fsip->fsi_depth = 0; 100*8721SJonathan.Adams@Sun.COM fsip->fsi_overflow = 0; 101*8721SJonathan.Adams@Sun.COM 1020Sstevel@tonic-gate fs_dprintf(("<0> frame = %p, kbase = %p, ktop = %p, ubase = %p\n", 1030Sstevel@tonic-gate frame, kbase, ktop, ubase)); 1040Sstevel@tonic-gate for (;;) { 1050Sstevel@tonic-gate uintptr_t fp; 1060Sstevel@tonic-gate long *fpp = (long *)&((struct rwindow *)frame)->rw_fp; 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate fs_dprintf(("<1> fpp = %p, frame = %p\n", fpp, frame)); 1090Sstevel@tonic-gate 1100Sstevel@tonic-gate if ((frame & (STACK_ALIGN - 1)) != 0) 1110Sstevel@tonic-gate break; 1120Sstevel@tonic-gate 1130Sstevel@tonic-gate fp = ((struct rwindow *)frame)->rw_fp + STACK_BIAS; 114*8721SJonathan.Adams@Sun.COM if (fsip->fsi_depth < fsip->fsi_max_depth) 115*8721SJonathan.Adams@Sun.COM fsip->fsi_stack[fsip->fsi_depth++] = 116*8721SJonathan.Adams@Sun.COM ((struct rwindow *)frame)->rw_rtn; 117*8721SJonathan.Adams@Sun.COM else 118*8721SJonathan.Adams@Sun.COM fsip->fsi_overflow = 1; 119*8721SJonathan.Adams@Sun.COM 1200Sstevel@tonic-gate fs_dprintf(("<2> fp = %p\n", fp)); 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate if (fp == ktop) 1230Sstevel@tonic-gate return (CRAWL_FOUNDALL); 1240Sstevel@tonic-gate fs_dprintf(("<3> not at base\n")); 1250Sstevel@tonic-gate 1260Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 1270Sstevel@tonic-gate if (ktop - fp == sizeof (struct rwindow)) { 1280Sstevel@tonic-gate fs_dprintf(("<4> found base\n")); 1290Sstevel@tonic-gate return (CRAWL_FOUNDALL); 1300Sstevel@tonic-gate } 1310Sstevel@tonic-gate #endif 1320Sstevel@tonic-gate 1330Sstevel@tonic-gate fs_dprintf(("<5> fp = %p, kbase = %p, ktop - size = %p\n", 1340Sstevel@tonic-gate fp, kbase, ktop - sizeof (struct rwindow))); 1350Sstevel@tonic-gate 1360Sstevel@tonic-gate if (fp < kbase || fp >= (ktop - sizeof (struct rwindow))) 1370Sstevel@tonic-gate break; 1380Sstevel@tonic-gate 1390Sstevel@tonic-gate frame = KTOU(fp); 1400Sstevel@tonic-gate fs_dprintf(("<6> frame = %p\n", frame)); 1410Sstevel@tonic-gate 1420Sstevel@tonic-gate /* 1430Sstevel@tonic-gate * NULL out the old %fp so we don't go down this stack 1440Sstevel@tonic-gate * more than once. 1450Sstevel@tonic-gate */ 1460Sstevel@tonic-gate if (kill_fp) { 1470Sstevel@tonic-gate fs_dprintf(("<7> fpp = %p\n", fpp)); 1480Sstevel@tonic-gate *fpp = NULL; 1490Sstevel@tonic-gate } 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate fs_dprintf(("<8> levels = %d\n", levels)); 1520Sstevel@tonic-gate levels++; 1530Sstevel@tonic-gate } 1540Sstevel@tonic-gate 1550Sstevel@tonic-gate return (levels); 1560Sstevel@tonic-gate } 1570Sstevel@tonic-gate 1580Sstevel@tonic-gate /* 1590Sstevel@tonic-gate * "sp" is a kernel VA. 1600Sstevel@tonic-gate */ 1610Sstevel@tonic-gate static int 1620Sstevel@tonic-gate print_stack(uintptr_t sp, uintptr_t pc, uintptr_t addr, 1630Sstevel@tonic-gate int argc, const mdb_arg_t *argv, int free_state) 1640Sstevel@tonic-gate { 1650Sstevel@tonic-gate int showargs = 0, count, err; 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate count = mdb_getopts(argc, argv, 1680Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &showargs, NULL); 1690Sstevel@tonic-gate argc -= count; 1700Sstevel@tonic-gate argv += count; 1710Sstevel@tonic-gate 1720Sstevel@tonic-gate if (argc > 1 || (argc == 1 && argv->a_type != MDB_TYPE_STRING)) 1730Sstevel@tonic-gate return (DCMD_USAGE); 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate mdb_printf("stack pointer for thread %p%s: %p\n", 1760Sstevel@tonic-gate addr, (free_state ? " (TS_FREE)" : ""), sp); 1770Sstevel@tonic-gate if (pc != 0) 1780Sstevel@tonic-gate mdb_printf("[ %0?lr %a() ]\n", sp, pc); 1790Sstevel@tonic-gate 1800Sstevel@tonic-gate mdb_inc_indent(2); 1810Sstevel@tonic-gate mdb_set_dot(sp); 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate if (argc == 1) 1840Sstevel@tonic-gate err = mdb_eval(argv->a_un.a_str); 1850Sstevel@tonic-gate else if (showargs) 1860Sstevel@tonic-gate err = mdb_eval("<.$C"); 1870Sstevel@tonic-gate else 1880Sstevel@tonic-gate err = mdb_eval("<.$C0"); 1890Sstevel@tonic-gate 1900Sstevel@tonic-gate mdb_dec_indent(2); 1910Sstevel@tonic-gate 1920Sstevel@tonic-gate return ((err == -1) ? DCMD_ABORT : DCMD_OK); 1930Sstevel@tonic-gate } 1940Sstevel@tonic-gate 1950Sstevel@tonic-gate /*ARGSUSED*/ 196*8721SJonathan.Adams@Sun.COM static int 197*8721SJonathan.Adams@Sun.COM do_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings) 1980Sstevel@tonic-gate { 1990Sstevel@tonic-gate kthread_t thr; 2000Sstevel@tonic-gate size_t stksz; 2010Sstevel@tonic-gate uintptr_t ubase, utop; 2020Sstevel@tonic-gate uintptr_t kbase, ktop; 2030Sstevel@tonic-gate uintptr_t win, sp; 2040Sstevel@tonic-gate 205*8721SJonathan.Adams@Sun.COM fsip->fsi_failed = 0; 206*8721SJonathan.Adams@Sun.COM fsip->fsi_pc = 0; 207*8721SJonathan.Adams@Sun.COM fsip->fsi_sp = 0; 208*8721SJonathan.Adams@Sun.COM fsip->fsi_depth = 0; 209*8721SJonathan.Adams@Sun.COM fsip->fsi_overflow = 0; 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate bzero(&thr, sizeof (thr)); 2120Sstevel@tonic-gate if (mdb_ctf_vread(&thr, "kthread_t", addr, 2130Sstevel@tonic-gate MDB_CTF_VREAD_IGNORE_ALL) == -1) { 214*8721SJonathan.Adams@Sun.COM if (print_warnings) 215*8721SJonathan.Adams@Sun.COM mdb_warn("couldn't read thread at %p\n", addr); 216*8721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_BADTHREAD; 2170Sstevel@tonic-gate return (DCMD_ERR); 2180Sstevel@tonic-gate } 2190Sstevel@tonic-gate 220*8721SJonathan.Adams@Sun.COM fsip->fsi_sobj_ops = (uintptr_t)thr.t_sobj_ops; 221*8721SJonathan.Adams@Sun.COM fsip->fsi_tstate = thr.t_state; 222*8721SJonathan.Adams@Sun.COM fsip->fsi_panic = !!(thr.t_flag & T_PANIC); 223*8721SJonathan.Adams@Sun.COM 2240Sstevel@tonic-gate if ((thr.t_schedflag & TS_LOAD) == 0) { 225*8721SJonathan.Adams@Sun.COM if (print_warnings) 226*8721SJonathan.Adams@Sun.COM mdb_warn("thread %p isn't in memory\n", addr); 227*8721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_NOTINMEMORY; 2280Sstevel@tonic-gate return (DCMD_ERR); 2290Sstevel@tonic-gate } 2300Sstevel@tonic-gate 2310Sstevel@tonic-gate if (thr.t_stk < thr.t_stkbase) { 232*8721SJonathan.Adams@Sun.COM if (print_warnings) 233*8721SJonathan.Adams@Sun.COM mdb_warn( 234*8721SJonathan.Adams@Sun.COM "stack base or stack top corrupt for thread %p\n", 235*8721SJonathan.Adams@Sun.COM addr); 236*8721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; 2370Sstevel@tonic-gate return (DCMD_ERR); 2380Sstevel@tonic-gate } 2390Sstevel@tonic-gate 2400Sstevel@tonic-gate kbase = (uintptr_t)thr.t_stkbase; 2410Sstevel@tonic-gate ktop = (uintptr_t)thr.t_stk; 2420Sstevel@tonic-gate stksz = ktop - kbase; 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate #ifdef __amd64 2450Sstevel@tonic-gate /* 2460Sstevel@tonic-gate * The stack on amd64 is intentionally misaligned, so ignore the top 2470Sstevel@tonic-gate * half-frame. See thread_stk_init(). When handling traps, the frame 2480Sstevel@tonic-gate * is automatically aligned by the hardware, so we only alter ktop if 2490Sstevel@tonic-gate * needed. 2500Sstevel@tonic-gate */ 2510Sstevel@tonic-gate if ((ktop & (STACK_ALIGN - 1)) != 0) 2520Sstevel@tonic-gate ktop -= STACK_ENTRY_ALIGN; 2530Sstevel@tonic-gate #endif 2540Sstevel@tonic-gate 2550Sstevel@tonic-gate /* 2560Sstevel@tonic-gate * If the stack size is larger than a meg, assume that it's bogus. 2570Sstevel@tonic-gate */ 2580Sstevel@tonic-gate if (stksz > TOO_BIG_FOR_A_STACK) { 259*8721SJonathan.Adams@Sun.COM if (print_warnings) 260*8721SJonathan.Adams@Sun.COM mdb_warn("stack size for thread %p is too big to be " 261*8721SJonathan.Adams@Sun.COM "reasonable\n", addr); 262*8721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; 2630Sstevel@tonic-gate return (DCMD_ERR); 2640Sstevel@tonic-gate } 2650Sstevel@tonic-gate 2660Sstevel@tonic-gate /* 2670Sstevel@tonic-gate * This could be (and was) a UM_GC allocation. Unfortunately, 2680Sstevel@tonic-gate * stksz tends to be very large. As currently implemented, dcmds 2690Sstevel@tonic-gate * invoked as part of pipelines don't have their UM_GC-allocated 2700Sstevel@tonic-gate * memory freed until the pipeline completes. With stksz in the 2710Sstevel@tonic-gate * neighborhood of 20k, the popular ::walk thread |::findstack 2720Sstevel@tonic-gate * pipeline can easily run memory-constrained debuggers (kmdb) out 2730Sstevel@tonic-gate * of memory. This can be changed back to a gc-able allocation when 2740Sstevel@tonic-gate * the debugger is changed to free UM_GC memory more promptly. 2750Sstevel@tonic-gate */ 2760Sstevel@tonic-gate ubase = (uintptr_t)mdb_alloc(stksz, UM_SLEEP); 2770Sstevel@tonic-gate utop = ubase + stksz; 2780Sstevel@tonic-gate if (mdb_vread((caddr_t)ubase, stksz, kbase) != stksz) { 2790Sstevel@tonic-gate mdb_free((void *)ubase, stksz); 280*8721SJonathan.Adams@Sun.COM if (print_warnings) 281*8721SJonathan.Adams@Sun.COM mdb_warn("couldn't read entire stack for thread %p\n", 282*8721SJonathan.Adams@Sun.COM addr); 283*8721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_THREADCORRUPT; 2840Sstevel@tonic-gate return (DCMD_ERR); 2850Sstevel@tonic-gate } 2860Sstevel@tonic-gate 2870Sstevel@tonic-gate /* 2880Sstevel@tonic-gate * Try the saved %sp first, if it looks reasonable. 2890Sstevel@tonic-gate */ 2900Sstevel@tonic-gate sp = KTOU((uintptr_t)thr.t_sp + STACK_BIAS); 2910Sstevel@tonic-gate if (sp >= ubase && sp <= utop) { 292*8721SJonathan.Adams@Sun.COM if (crawl(sp, kbase, ktop, ubase, 0, fsip) == CRAWL_FOUNDALL) { 293*8721SJonathan.Adams@Sun.COM fsip->fsi_sp = (uintptr_t)thr.t_sp; 294*8721SJonathan.Adams@Sun.COM #if !defined(__i386) 295*8721SJonathan.Adams@Sun.COM fsip->fsi_pc = (uintptr_t)thr.t_pc; 2960Sstevel@tonic-gate #endif 297*8721SJonathan.Adams@Sun.COM goto found; 2980Sstevel@tonic-gate } 2990Sstevel@tonic-gate } 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate /* 3020Sstevel@tonic-gate * Now walk through the whole stack, starting at the base, 3030Sstevel@tonic-gate * trying every possible "window". 3040Sstevel@tonic-gate */ 3050Sstevel@tonic-gate for (win = ubase; 3060Sstevel@tonic-gate win + sizeof (struct rwindow) <= utop; 3070Sstevel@tonic-gate win += sizeof (struct rwindow *)) { 308*8721SJonathan.Adams@Sun.COM if (crawl(win, kbase, ktop, ubase, 1, fsip) == CRAWL_FOUNDALL) { 309*8721SJonathan.Adams@Sun.COM fsip->fsi_sp = UTOK(win) - STACK_BIAS; 310*8721SJonathan.Adams@Sun.COM goto found; 3110Sstevel@tonic-gate } 3120Sstevel@tonic-gate } 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate /* 3150Sstevel@tonic-gate * We didn't conclusively find the stack. So we'll take another lap, 3160Sstevel@tonic-gate * and print out anything that looks possible. 3170Sstevel@tonic-gate */ 318*8721SJonathan.Adams@Sun.COM if (print_warnings) 319*8721SJonathan.Adams@Sun.COM mdb_printf("Possible stack pointers for thread %p:\n", addr); 3200Sstevel@tonic-gate (void) mdb_vread((caddr_t)ubase, stksz, kbase); 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate for (win = ubase; 3230Sstevel@tonic-gate win + sizeof (struct rwindow) <= utop; 3240Sstevel@tonic-gate win += sizeof (struct rwindow *)) { 3250Sstevel@tonic-gate uintptr_t fp = ((struct rwindow *)win)->rw_fp; 3260Sstevel@tonic-gate int levels; 3270Sstevel@tonic-gate 328*8721SJonathan.Adams@Sun.COM if ((levels = crawl(win, kbase, ktop, ubase, 1, fsip)) > 1) { 329*8721SJonathan.Adams@Sun.COM if (print_warnings) 330*8721SJonathan.Adams@Sun.COM mdb_printf(" %p (%d)\n", fp, levels); 3310Sstevel@tonic-gate } else if (levels == CRAWL_FOUNDALL) { 3320Sstevel@tonic-gate /* 3330Sstevel@tonic-gate * If this is a live system, the stack could change 3340Sstevel@tonic-gate * between the two mdb_vread(ubase, utop, kbase)'s, 3350Sstevel@tonic-gate * and we could have a fully valid stack here. 3360Sstevel@tonic-gate */ 337*8721SJonathan.Adams@Sun.COM fsip->fsi_sp = UTOK(win) - STACK_BIAS; 338*8721SJonathan.Adams@Sun.COM goto found; 3390Sstevel@tonic-gate } 3400Sstevel@tonic-gate } 3410Sstevel@tonic-gate 342*8721SJonathan.Adams@Sun.COM fsip->fsi_depth = 0; 343*8721SJonathan.Adams@Sun.COM fsip->fsi_overflow = 0; 344*8721SJonathan.Adams@Sun.COM fsip->fsi_failed = FSI_FAIL_STACKNOTFOUND; 345*8721SJonathan.Adams@Sun.COM 346*8721SJonathan.Adams@Sun.COM mdb_free((void *)ubase, stksz); 347*8721SJonathan.Adams@Sun.COM return (DCMD_ERR); 348*8721SJonathan.Adams@Sun.COM found: 3490Sstevel@tonic-gate mdb_free((void *)ubase, stksz); 3500Sstevel@tonic-gate return (DCMD_OK); 3510Sstevel@tonic-gate } 3520Sstevel@tonic-gate 353*8721SJonathan.Adams@Sun.COM int 354*8721SJonathan.Adams@Sun.COM findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 355*8721SJonathan.Adams@Sun.COM { 356*8721SJonathan.Adams@Sun.COM findstack_info_t fsi; 357*8721SJonathan.Adams@Sun.COM int retval; 358*8721SJonathan.Adams@Sun.COM 359*8721SJonathan.Adams@Sun.COM if (!(flags & DCMD_ADDRSPEC)) 360*8721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 361*8721SJonathan.Adams@Sun.COM 362*8721SJonathan.Adams@Sun.COM bzero(&fsi, sizeof (fsi)); 363*8721SJonathan.Adams@Sun.COM 364*8721SJonathan.Adams@Sun.COM if ((retval = do_findstack(addr, &fsi, 1)) != DCMD_OK || 365*8721SJonathan.Adams@Sun.COM fsi.fsi_failed) 366*8721SJonathan.Adams@Sun.COM return (retval); 367*8721SJonathan.Adams@Sun.COM 368*8721SJonathan.Adams@Sun.COM return (print_stack(fsi.fsi_sp, fsi.fsi_pc, addr, 369*8721SJonathan.Adams@Sun.COM argc, argv, fsi.fsi_tstate == TS_FREE)); 370*8721SJonathan.Adams@Sun.COM } 371*8721SJonathan.Adams@Sun.COM 3720Sstevel@tonic-gate /*ARGSUSED*/ 3730Sstevel@tonic-gate int 3740Sstevel@tonic-gate findstack_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *av) 3750Sstevel@tonic-gate { 3760Sstevel@tonic-gate findstack_debug_on ^= 1; 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate mdb_printf("findstack: debugging is now %s\n", 3790Sstevel@tonic-gate findstack_debug_on ? "on" : "off"); 3800Sstevel@tonic-gate 3810Sstevel@tonic-gate return (DCMD_OK); 3820Sstevel@tonic-gate } 3830Sstevel@tonic-gate 384*8721SJonathan.Adams@Sun.COM static void 385*8721SJonathan.Adams@Sun.COM uppercase(char *p) 386*8721SJonathan.Adams@Sun.COM { 387*8721SJonathan.Adams@Sun.COM for (; *p != '\0'; p++) { 388*8721SJonathan.Adams@Sun.COM if (*p >= 'a' && *p <= 'z') 389*8721SJonathan.Adams@Sun.COM *p += 'A' - 'a'; 390*8721SJonathan.Adams@Sun.COM } 391*8721SJonathan.Adams@Sun.COM } 392*8721SJonathan.Adams@Sun.COM 393*8721SJonathan.Adams@Sun.COM static void 394*8721SJonathan.Adams@Sun.COM sobj_to_text(uintptr_t addr, char *out, size_t out_sz) 395*8721SJonathan.Adams@Sun.COM { 396*8721SJonathan.Adams@Sun.COM sobj_ops_to_text(addr, out, out_sz); 397*8721SJonathan.Adams@Sun.COM uppercase(out); 398*8721SJonathan.Adams@Sun.COM } 399*8721SJonathan.Adams@Sun.COM 400*8721SJonathan.Adams@Sun.COM #define SOBJ_ALL 1 401*8721SJonathan.Adams@Sun.COM static int 402*8721SJonathan.Adams@Sun.COM text_to_sobj(const char *text, uintptr_t *out) 403*8721SJonathan.Adams@Sun.COM { 404*8721SJonathan.Adams@Sun.COM if (strcasecmp(text, "ALL") == 0) { 405*8721SJonathan.Adams@Sun.COM *out = SOBJ_ALL; 406*8721SJonathan.Adams@Sun.COM return (0); 407*8721SJonathan.Adams@Sun.COM } 408*8721SJonathan.Adams@Sun.COM return (sobj_text_to_ops(text, out)); 409*8721SJonathan.Adams@Sun.COM } 410*8721SJonathan.Adams@Sun.COM 411*8721SJonathan.Adams@Sun.COM #define TSTATE_PANIC -2U 412*8721SJonathan.Adams@Sun.COM static int 413*8721SJonathan.Adams@Sun.COM text_to_tstate(const char *text, uint_t *out) 414*8721SJonathan.Adams@Sun.COM { 415*8721SJonathan.Adams@Sun.COM if (strcasecmp(text, "panic") == 0) 416*8721SJonathan.Adams@Sun.COM *out = TSTATE_PANIC; 417*8721SJonathan.Adams@Sun.COM else if (thread_text_to_state(text, out) != 0) { 418*8721SJonathan.Adams@Sun.COM mdb_warn("tstate \"%s\" not recognized\n", text); 419*8721SJonathan.Adams@Sun.COM return (-1); 420*8721SJonathan.Adams@Sun.COM } 421*8721SJonathan.Adams@Sun.COM return (0); 422*8721SJonathan.Adams@Sun.COM } 423*8721SJonathan.Adams@Sun.COM 424*8721SJonathan.Adams@Sun.COM static void 425*8721SJonathan.Adams@Sun.COM tstate_to_text(uint_t tstate, uint_t paniced, char *out, size_t out_sz) 426*8721SJonathan.Adams@Sun.COM { 427*8721SJonathan.Adams@Sun.COM if (paniced) 428*8721SJonathan.Adams@Sun.COM mdb_snprintf(out, out_sz, "panic"); 429*8721SJonathan.Adams@Sun.COM else 430*8721SJonathan.Adams@Sun.COM thread_state_to_text(tstate, out, out_sz); 431*8721SJonathan.Adams@Sun.COM uppercase(out); 432*8721SJonathan.Adams@Sun.COM } 433*8721SJonathan.Adams@Sun.COM 434*8721SJonathan.Adams@Sun.COM typedef struct stacks_entry { 435*8721SJonathan.Adams@Sun.COM struct stacks_entry *se_next; 436*8721SJonathan.Adams@Sun.COM struct stacks_entry *se_dup; /* dups of this stack */ 437*8721SJonathan.Adams@Sun.COM uintptr_t se_thread; 438*8721SJonathan.Adams@Sun.COM uintptr_t se_sp; 439*8721SJonathan.Adams@Sun.COM uintptr_t se_sobj_ops; 440*8721SJonathan.Adams@Sun.COM uint32_t se_tstate; 441*8721SJonathan.Adams@Sun.COM uint32_t se_count; /* # threads w/ this stack */ 442*8721SJonathan.Adams@Sun.COM uint8_t se_overflow; 443*8721SJonathan.Adams@Sun.COM uint8_t se_depth; 444*8721SJonathan.Adams@Sun.COM uint8_t se_failed; /* failure reason; FSI_FAIL_* */ 445*8721SJonathan.Adams@Sun.COM uint8_t se_panic; 446*8721SJonathan.Adams@Sun.COM uintptr_t se_stack[1]; 447*8721SJonathan.Adams@Sun.COM } stacks_entry_t; 448*8721SJonathan.Adams@Sun.COM #define STACKS_ENTRY_SIZE(x) OFFSETOF(stacks_entry_t, se_stack[(x)]) 449*8721SJonathan.Adams@Sun.COM 450*8721SJonathan.Adams@Sun.COM #define STACKS_HSIZE 127 451*8721SJonathan.Adams@Sun.COM 452*8721SJonathan.Adams@Sun.COM /* Maximum stack depth reported in stacks */ 453*8721SJonathan.Adams@Sun.COM #define STACKS_MAX_DEPTH 254 454*8721SJonathan.Adams@Sun.COM 455*8721SJonathan.Adams@Sun.COM typedef struct stacks_info { 456*8721SJonathan.Adams@Sun.COM size_t si_count; /* total stacks_entry_ts (incl dups) */ 457*8721SJonathan.Adams@Sun.COM size_t si_entries; /* # entries in hash table */ 458*8721SJonathan.Adams@Sun.COM stacks_entry_t **si_hash; /* hash table */ 459*8721SJonathan.Adams@Sun.COM 460*8721SJonathan.Adams@Sun.COM findstack_info_t si_fsi; /* transient callback state */ 461*8721SJonathan.Adams@Sun.COM } stacks_info_t; 462*8721SJonathan.Adams@Sun.COM 463*8721SJonathan.Adams@Sun.COM 464*8721SJonathan.Adams@Sun.COM /* global state cached between invocations */ 465*8721SJonathan.Adams@Sun.COM #define STACKS_STATE_CLEAN 0 466*8721SJonathan.Adams@Sun.COM #define STACKS_STATE_DIRTY 1 467*8721SJonathan.Adams@Sun.COM #define STACKS_STATE_DONE 2 468*8721SJonathan.Adams@Sun.COM static uint_t stacks_state = STACKS_STATE_CLEAN; 469*8721SJonathan.Adams@Sun.COM static stacks_entry_t **stacks_hash; 470*8721SJonathan.Adams@Sun.COM static stacks_entry_t **stacks_array; 471*8721SJonathan.Adams@Sun.COM static size_t stacks_array_size; 472*8721SJonathan.Adams@Sun.COM 473*8721SJonathan.Adams@Sun.COM size_t 474*8721SJonathan.Adams@Sun.COM stacks_hash_entry(stacks_entry_t *sep) 475*8721SJonathan.Adams@Sun.COM { 476*8721SJonathan.Adams@Sun.COM size_t depth = sep->se_depth; 477*8721SJonathan.Adams@Sun.COM uintptr_t *stack = sep->se_stack; 478*8721SJonathan.Adams@Sun.COM 479*8721SJonathan.Adams@Sun.COM uint64_t total = depth; 480*8721SJonathan.Adams@Sun.COM 481*8721SJonathan.Adams@Sun.COM while (depth > 0) { 482*8721SJonathan.Adams@Sun.COM total += *stack; 483*8721SJonathan.Adams@Sun.COM stack++; depth--; 484*8721SJonathan.Adams@Sun.COM } 485*8721SJonathan.Adams@Sun.COM 486*8721SJonathan.Adams@Sun.COM return (total % STACKS_HSIZE); 487*8721SJonathan.Adams@Sun.COM } 488*8721SJonathan.Adams@Sun.COM 489*8721SJonathan.Adams@Sun.COM /* 490*8721SJonathan.Adams@Sun.COM * This is used to both compare stacks for equality and to sort the final 491*8721SJonathan.Adams@Sun.COM * list of unique stacks. forsort specifies the latter behavior, which 492*8721SJonathan.Adams@Sun.COM * additionally: 493*8721SJonathan.Adams@Sun.COM * compares se_count, and 494*8721SJonathan.Adams@Sun.COM * sorts the stacks by text function name. 495*8721SJonathan.Adams@Sun.COM * 496*8721SJonathan.Adams@Sun.COM * The equality test is independent of se_count, and doesn't care about 497*8721SJonathan.Adams@Sun.COM * relative ordering, so we don't do the extra work of looking up symbols 498*8721SJonathan.Adams@Sun.COM * for the stack addresses. 499*8721SJonathan.Adams@Sun.COM */ 5000Sstevel@tonic-gate int 501*8721SJonathan.Adams@Sun.COM stacks_entry_comp_impl(stacks_entry_t *l, stacks_entry_t *r, 502*8721SJonathan.Adams@Sun.COM uint_t forsort) 5030Sstevel@tonic-gate { 504*8721SJonathan.Adams@Sun.COM int idx; 505*8721SJonathan.Adams@Sun.COM 506*8721SJonathan.Adams@Sun.COM int depth = MIN(l->se_depth, r->se_depth); 507*8721SJonathan.Adams@Sun.COM 508*8721SJonathan.Adams@Sun.COM /* no matter what, panic stacks come last. */ 509*8721SJonathan.Adams@Sun.COM if (l->se_panic > r->se_panic) 510*8721SJonathan.Adams@Sun.COM return (1); 511*8721SJonathan.Adams@Sun.COM if (l->se_panic < r->se_panic) 512*8721SJonathan.Adams@Sun.COM return (-1); 513*8721SJonathan.Adams@Sun.COM 514*8721SJonathan.Adams@Sun.COM if (forsort) { 515*8721SJonathan.Adams@Sun.COM /* put large counts earlier */ 516*8721SJonathan.Adams@Sun.COM if (l->se_count > r->se_count) 517*8721SJonathan.Adams@Sun.COM return (-1); 518*8721SJonathan.Adams@Sun.COM if (l->se_count < r->se_count) 519*8721SJonathan.Adams@Sun.COM return (1); 520*8721SJonathan.Adams@Sun.COM } 521*8721SJonathan.Adams@Sun.COM 522*8721SJonathan.Adams@Sun.COM if (l->se_tstate > r->se_tstate) 523*8721SJonathan.Adams@Sun.COM return (1); 524*8721SJonathan.Adams@Sun.COM if (l->se_tstate < r->se_tstate) 525*8721SJonathan.Adams@Sun.COM return (-1); 526*8721SJonathan.Adams@Sun.COM 527*8721SJonathan.Adams@Sun.COM if (l->se_failed > r->se_failed) 528*8721SJonathan.Adams@Sun.COM return (1); 529*8721SJonathan.Adams@Sun.COM if (l->se_failed < r->se_failed) 530*8721SJonathan.Adams@Sun.COM return (-1); 531*8721SJonathan.Adams@Sun.COM 532*8721SJonathan.Adams@Sun.COM for (idx = 0; idx < depth; idx++) { 533*8721SJonathan.Adams@Sun.COM char lbuf[MDB_SYM_NAMLEN]; 534*8721SJonathan.Adams@Sun.COM char rbuf[MDB_SYM_NAMLEN]; 535*8721SJonathan.Adams@Sun.COM 536*8721SJonathan.Adams@Sun.COM int rval; 537*8721SJonathan.Adams@Sun.COM uintptr_t laddr = l->se_stack[idx]; 538*8721SJonathan.Adams@Sun.COM uintptr_t raddr = r->se_stack[idx]; 539*8721SJonathan.Adams@Sun.COM 540*8721SJonathan.Adams@Sun.COM if (laddr == raddr) 541*8721SJonathan.Adams@Sun.COM continue; 542*8721SJonathan.Adams@Sun.COM 543*8721SJonathan.Adams@Sun.COM if (forsort && 544*8721SJonathan.Adams@Sun.COM mdb_lookup_by_addr(laddr, MDB_SYM_FUZZY, 545*8721SJonathan.Adams@Sun.COM lbuf, sizeof (lbuf), NULL) != -1 && 546*8721SJonathan.Adams@Sun.COM mdb_lookup_by_addr(raddr, MDB_SYM_FUZZY, 547*8721SJonathan.Adams@Sun.COM rbuf, sizeof (rbuf), NULL) != -1 && 548*8721SJonathan.Adams@Sun.COM (rval = strcmp(lbuf, rbuf)) != 0) 549*8721SJonathan.Adams@Sun.COM return (rval); 550*8721SJonathan.Adams@Sun.COM 551*8721SJonathan.Adams@Sun.COM if (laddr > raddr) 552*8721SJonathan.Adams@Sun.COM return (1); 553*8721SJonathan.Adams@Sun.COM return (-1); 5540Sstevel@tonic-gate } 555*8721SJonathan.Adams@Sun.COM 556*8721SJonathan.Adams@Sun.COM if (l->se_overflow > r->se_overflow) 557*8721SJonathan.Adams@Sun.COM return (-1); 558*8721SJonathan.Adams@Sun.COM if (l->se_overflow < r->se_overflow) 559*8721SJonathan.Adams@Sun.COM return (1); 560*8721SJonathan.Adams@Sun.COM 561*8721SJonathan.Adams@Sun.COM if (l->se_depth > r->se_depth) 562*8721SJonathan.Adams@Sun.COM return (1); 563*8721SJonathan.Adams@Sun.COM if (l->se_depth < r->se_depth) 564*8721SJonathan.Adams@Sun.COM return (-1); 565*8721SJonathan.Adams@Sun.COM 566*8721SJonathan.Adams@Sun.COM if (l->se_sobj_ops > r->se_sobj_ops) 567*8721SJonathan.Adams@Sun.COM return (1); 568*8721SJonathan.Adams@Sun.COM if (l->se_sobj_ops < r->se_sobj_ops) 569*8721SJonathan.Adams@Sun.COM return (-1); 570*8721SJonathan.Adams@Sun.COM 571*8721SJonathan.Adams@Sun.COM return (0); 572*8721SJonathan.Adams@Sun.COM } 573*8721SJonathan.Adams@Sun.COM 574*8721SJonathan.Adams@Sun.COM int 575*8721SJonathan.Adams@Sun.COM stacks_entry_comp(const void *l_arg, const void *r_arg) 576*8721SJonathan.Adams@Sun.COM { 577*8721SJonathan.Adams@Sun.COM stacks_entry_t * const *lp = l_arg; 578*8721SJonathan.Adams@Sun.COM stacks_entry_t * const *rp = r_arg; 579*8721SJonathan.Adams@Sun.COM 580*8721SJonathan.Adams@Sun.COM return (stacks_entry_comp_impl(*lp, *rp, 1)); 581*8721SJonathan.Adams@Sun.COM } 582*8721SJonathan.Adams@Sun.COM 583*8721SJonathan.Adams@Sun.COM void 584*8721SJonathan.Adams@Sun.COM stacks_cleanup(int force) 585*8721SJonathan.Adams@Sun.COM { 586*8721SJonathan.Adams@Sun.COM int idx = 0; 587*8721SJonathan.Adams@Sun.COM stacks_entry_t *cur, *next; 588*8721SJonathan.Adams@Sun.COM 589*8721SJonathan.Adams@Sun.COM if (stacks_state == STACKS_STATE_CLEAN) 590*8721SJonathan.Adams@Sun.COM return; 591*8721SJonathan.Adams@Sun.COM 592*8721SJonathan.Adams@Sun.COM if (!force && stacks_state == STACKS_STATE_DONE) 593*8721SJonathan.Adams@Sun.COM return; 594*8721SJonathan.Adams@Sun.COM 595*8721SJonathan.Adams@Sun.COM /* 596*8721SJonathan.Adams@Sun.COM * Until the array is sorted and stable, stacks_hash will be non-NULL. 597*8721SJonathan.Adams@Sun.COM * This way, we can get at all of the data, even if qsort() was 598*8721SJonathan.Adams@Sun.COM * interrupted while mucking with the array. 599*8721SJonathan.Adams@Sun.COM */ 600*8721SJonathan.Adams@Sun.COM if (stacks_hash != NULL) { 601*8721SJonathan.Adams@Sun.COM for (idx = 0; idx < STACKS_HSIZE; idx++) { 602*8721SJonathan.Adams@Sun.COM while ((cur = stacks_hash[idx]) != NULL) { 603*8721SJonathan.Adams@Sun.COM while ((next = cur->se_dup) != NULL) { 604*8721SJonathan.Adams@Sun.COM cur->se_dup = next->se_dup; 605*8721SJonathan.Adams@Sun.COM mdb_free(next, 606*8721SJonathan.Adams@Sun.COM STACKS_ENTRY_SIZE(next->se_depth)); 607*8721SJonathan.Adams@Sun.COM } 608*8721SJonathan.Adams@Sun.COM next = cur->se_next; 609*8721SJonathan.Adams@Sun.COM stacks_hash[idx] = next; 610*8721SJonathan.Adams@Sun.COM mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth)); 611*8721SJonathan.Adams@Sun.COM } 612*8721SJonathan.Adams@Sun.COM } 613*8721SJonathan.Adams@Sun.COM if (stacks_array != NULL) 614*8721SJonathan.Adams@Sun.COM mdb_free(stacks_array, 615*8721SJonathan.Adams@Sun.COM stacks_array_size * sizeof (*stacks_array)); 616*8721SJonathan.Adams@Sun.COM 617*8721SJonathan.Adams@Sun.COM } else if (stacks_array != NULL) { 618*8721SJonathan.Adams@Sun.COM for (idx = 0; idx < stacks_array_size; idx++) { 619*8721SJonathan.Adams@Sun.COM if ((cur = stacks_array[idx]) != NULL) { 620*8721SJonathan.Adams@Sun.COM while ((next = cur->se_dup) != NULL) { 621*8721SJonathan.Adams@Sun.COM cur->se_dup = next->se_dup; 622*8721SJonathan.Adams@Sun.COM mdb_free(next, 623*8721SJonathan.Adams@Sun.COM STACKS_ENTRY_SIZE(next->se_depth)); 624*8721SJonathan.Adams@Sun.COM } 625*8721SJonathan.Adams@Sun.COM stacks_array[idx] = NULL; 626*8721SJonathan.Adams@Sun.COM mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth)); 627*8721SJonathan.Adams@Sun.COM } 628*8721SJonathan.Adams@Sun.COM } 629*8721SJonathan.Adams@Sun.COM mdb_free(stacks_array, 630*8721SJonathan.Adams@Sun.COM stacks_array_size * sizeof (*stacks_array)); 631*8721SJonathan.Adams@Sun.COM } 632*8721SJonathan.Adams@Sun.COM 633*8721SJonathan.Adams@Sun.COM stacks_array_size = 0; 634*8721SJonathan.Adams@Sun.COM stacks_state = STACKS_STATE_CLEAN; 635*8721SJonathan.Adams@Sun.COM } 636*8721SJonathan.Adams@Sun.COM 637*8721SJonathan.Adams@Sun.COM /*ARGSUSED*/ 638*8721SJonathan.Adams@Sun.COM int 639*8721SJonathan.Adams@Sun.COM stacks_thread_cb(uintptr_t addr, const void *ignored, void *cbarg) 640*8721SJonathan.Adams@Sun.COM { 641*8721SJonathan.Adams@Sun.COM stacks_info_t *sip = cbarg; 642*8721SJonathan.Adams@Sun.COM findstack_info_t *fsip = &sip->si_fsi; 643*8721SJonathan.Adams@Sun.COM 644*8721SJonathan.Adams@Sun.COM stacks_entry_t **sepp, *nsep, *sep; 645*8721SJonathan.Adams@Sun.COM int idx; 646*8721SJonathan.Adams@Sun.COM size_t depth; 647*8721SJonathan.Adams@Sun.COM 648*8721SJonathan.Adams@Sun.COM if (do_findstack(addr, fsip, 0) != DCMD_OK && 649*8721SJonathan.Adams@Sun.COM fsip->fsi_failed == FSI_FAIL_BADTHREAD) { 650*8721SJonathan.Adams@Sun.COM mdb_warn("couldn't read thread at %p\n", addr); 651*8721SJonathan.Adams@Sun.COM return (WALK_NEXT); 652*8721SJonathan.Adams@Sun.COM } 653*8721SJonathan.Adams@Sun.COM 654*8721SJonathan.Adams@Sun.COM sip->si_count++; 655*8721SJonathan.Adams@Sun.COM 656*8721SJonathan.Adams@Sun.COM depth = fsip->fsi_depth; 657*8721SJonathan.Adams@Sun.COM nsep = mdb_zalloc(STACKS_ENTRY_SIZE(depth), UM_SLEEP); 658*8721SJonathan.Adams@Sun.COM nsep->se_thread = addr; 659*8721SJonathan.Adams@Sun.COM nsep->se_sp = fsip->fsi_sp; 660*8721SJonathan.Adams@Sun.COM nsep->se_sobj_ops = fsip->fsi_sobj_ops; 661*8721SJonathan.Adams@Sun.COM nsep->se_tstate = fsip->fsi_tstate; 662*8721SJonathan.Adams@Sun.COM nsep->se_count = 1; 663*8721SJonathan.Adams@Sun.COM nsep->se_overflow = fsip->fsi_overflow; 664*8721SJonathan.Adams@Sun.COM nsep->se_depth = depth; 665*8721SJonathan.Adams@Sun.COM nsep->se_failed = fsip->fsi_failed; 666*8721SJonathan.Adams@Sun.COM nsep->se_panic = fsip->fsi_panic; 667*8721SJonathan.Adams@Sun.COM 668*8721SJonathan.Adams@Sun.COM for (idx = 0; idx < depth; idx++) 669*8721SJonathan.Adams@Sun.COM nsep->se_stack[idx] = fsip->fsi_stack[idx]; 670*8721SJonathan.Adams@Sun.COM 671*8721SJonathan.Adams@Sun.COM for (sepp = &sip->si_hash[stacks_hash_entry(nsep)]; 672*8721SJonathan.Adams@Sun.COM (sep = *sepp) != NULL; 673*8721SJonathan.Adams@Sun.COM sepp = &sep->se_next) { 674*8721SJonathan.Adams@Sun.COM 675*8721SJonathan.Adams@Sun.COM if (stacks_entry_comp_impl(sep, nsep, 0) != 0) 676*8721SJonathan.Adams@Sun.COM continue; 677*8721SJonathan.Adams@Sun.COM 678*8721SJonathan.Adams@Sun.COM nsep->se_dup = sep->se_dup; 679*8721SJonathan.Adams@Sun.COM sep->se_dup = nsep; 680*8721SJonathan.Adams@Sun.COM sep->se_count++; 681*8721SJonathan.Adams@Sun.COM return (WALK_NEXT); 682*8721SJonathan.Adams@Sun.COM } 683*8721SJonathan.Adams@Sun.COM 684*8721SJonathan.Adams@Sun.COM nsep->se_next = NULL; 685*8721SJonathan.Adams@Sun.COM *sepp = nsep; 686*8721SJonathan.Adams@Sun.COM sip->si_entries++; 687*8721SJonathan.Adams@Sun.COM 688*8721SJonathan.Adams@Sun.COM return (WALK_NEXT); 689*8721SJonathan.Adams@Sun.COM } 690*8721SJonathan.Adams@Sun.COM 691*8721SJonathan.Adams@Sun.COM int 692*8721SJonathan.Adams@Sun.COM stacks_run(int verbose) 693*8721SJonathan.Adams@Sun.COM { 694*8721SJonathan.Adams@Sun.COM stacks_info_t si; 695*8721SJonathan.Adams@Sun.COM findstack_info_t *fsip = &si.si_fsi; 696*8721SJonathan.Adams@Sun.COM size_t idx; 697*8721SJonathan.Adams@Sun.COM stacks_entry_t **cur; 698*8721SJonathan.Adams@Sun.COM 699*8721SJonathan.Adams@Sun.COM bzero(&si, sizeof (si)); 700*8721SJonathan.Adams@Sun.COM 701*8721SJonathan.Adams@Sun.COM stacks_state = STACKS_STATE_DIRTY; 702*8721SJonathan.Adams@Sun.COM 703*8721SJonathan.Adams@Sun.COM stacks_hash = si.si_hash = 704*8721SJonathan.Adams@Sun.COM mdb_zalloc(STACKS_HSIZE * sizeof (*si.si_hash), UM_SLEEP); 705*8721SJonathan.Adams@Sun.COM si.si_entries = 0; 706*8721SJonathan.Adams@Sun.COM si.si_count = 0; 707*8721SJonathan.Adams@Sun.COM 708*8721SJonathan.Adams@Sun.COM fsip->fsi_max_depth = STACKS_MAX_DEPTH; 709*8721SJonathan.Adams@Sun.COM fsip->fsi_stack = 710*8721SJonathan.Adams@Sun.COM mdb_alloc(fsip->fsi_max_depth * sizeof (*fsip->fsi_stack), 711*8721SJonathan.Adams@Sun.COM UM_SLEEP | UM_GC); 712*8721SJonathan.Adams@Sun.COM 713*8721SJonathan.Adams@Sun.COM if (verbose) 714*8721SJonathan.Adams@Sun.COM mdb_warn("stacks: processing kernel threads\n"); 715*8721SJonathan.Adams@Sun.COM 716*8721SJonathan.Adams@Sun.COM if (mdb_walk("thread", stacks_thread_cb, &si) != 0) { 717*8721SJonathan.Adams@Sun.COM mdb_warn("cannot walk \"thread\""); 718*8721SJonathan.Adams@Sun.COM return (DCMD_ERR); 719*8721SJonathan.Adams@Sun.COM } 720*8721SJonathan.Adams@Sun.COM 721*8721SJonathan.Adams@Sun.COM if (verbose) 722*8721SJonathan.Adams@Sun.COM mdb_warn("stacks: %d unique stacks / %d threads\n", 723*8721SJonathan.Adams@Sun.COM si.si_entries, si.si_count); 724*8721SJonathan.Adams@Sun.COM 725*8721SJonathan.Adams@Sun.COM stacks_array_size = si.si_entries; 726*8721SJonathan.Adams@Sun.COM stacks_array = 727*8721SJonathan.Adams@Sun.COM mdb_zalloc(si.si_entries * sizeof (*stacks_array), UM_SLEEP); 728*8721SJonathan.Adams@Sun.COM cur = stacks_array; 729*8721SJonathan.Adams@Sun.COM for (idx = 0; idx < STACKS_HSIZE; idx++) { 730*8721SJonathan.Adams@Sun.COM stacks_entry_t *sep; 731*8721SJonathan.Adams@Sun.COM for (sep = si.si_hash[idx]; sep != NULL; sep = sep->se_next) 732*8721SJonathan.Adams@Sun.COM *(cur++) = sep; 733*8721SJonathan.Adams@Sun.COM } 734*8721SJonathan.Adams@Sun.COM 735*8721SJonathan.Adams@Sun.COM if (cur != stacks_array + si.si_entries) { 736*8721SJonathan.Adams@Sun.COM mdb_warn("stacks: miscounted array size (%d != size: %d)\n", 737*8721SJonathan.Adams@Sun.COM (cur - stacks_array), stacks_array_size); 738*8721SJonathan.Adams@Sun.COM return (DCMD_ERR); 739*8721SJonathan.Adams@Sun.COM } 740*8721SJonathan.Adams@Sun.COM qsort(stacks_array, si.si_entries, sizeof (*stacks_array), 741*8721SJonathan.Adams@Sun.COM stacks_entry_comp); 742*8721SJonathan.Adams@Sun.COM 743*8721SJonathan.Adams@Sun.COM /* Now that we're done, free the hash table */ 744*8721SJonathan.Adams@Sun.COM stacks_hash = NULL; 745*8721SJonathan.Adams@Sun.COM mdb_free(si.si_hash, STACKS_HSIZE * sizeof (*si.si_hash)); 746*8721SJonathan.Adams@Sun.COM 747*8721SJonathan.Adams@Sun.COM stacks_state = STACKS_STATE_DONE; 748*8721SJonathan.Adams@Sun.COM 749*8721SJonathan.Adams@Sun.COM if (verbose) 750*8721SJonathan.Adams@Sun.COM mdb_warn("stacks: done\n"); 7510Sstevel@tonic-gate 7520Sstevel@tonic-gate return (DCMD_OK); 7530Sstevel@tonic-gate } 754*8721SJonathan.Adams@Sun.COM 755*8721SJonathan.Adams@Sun.COM static int 756*8721SJonathan.Adams@Sun.COM stacks_has_caller(stacks_entry_t *sep, uintptr_t addr) 757*8721SJonathan.Adams@Sun.COM { 758*8721SJonathan.Adams@Sun.COM uintptr_t laddr = addr; 759*8721SJonathan.Adams@Sun.COM uintptr_t haddr = addr + 1; 760*8721SJonathan.Adams@Sun.COM int idx; 761*8721SJonathan.Adams@Sun.COM char c[MDB_SYM_NAMLEN]; 762*8721SJonathan.Adams@Sun.COM GElf_Sym sym; 763*8721SJonathan.Adams@Sun.COM 764*8721SJonathan.Adams@Sun.COM if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY, 765*8721SJonathan.Adams@Sun.COM c, sizeof (c), &sym) != -1 && 766*8721SJonathan.Adams@Sun.COM addr == (uintptr_t)sym.st_value) { 767*8721SJonathan.Adams@Sun.COM laddr = (uintptr_t)sym.st_value; 768*8721SJonathan.Adams@Sun.COM haddr = (uintptr_t)sym.st_value + sym.st_size; 769*8721SJonathan.Adams@Sun.COM } 770*8721SJonathan.Adams@Sun.COM 771*8721SJonathan.Adams@Sun.COM for (idx = 0; idx < sep->se_depth; idx++) 772*8721SJonathan.Adams@Sun.COM if (sep->se_stack[idx] >= laddr && sep->se_stack[idx] < haddr) 773*8721SJonathan.Adams@Sun.COM return (1); 774*8721SJonathan.Adams@Sun.COM 775*8721SJonathan.Adams@Sun.COM return (0); 776*8721SJonathan.Adams@Sun.COM } 777*8721SJonathan.Adams@Sun.COM 778*8721SJonathan.Adams@Sun.COM static int 779*8721SJonathan.Adams@Sun.COM uintptrcomp(const void *lp, const void *rp) 780*8721SJonathan.Adams@Sun.COM { 781*8721SJonathan.Adams@Sun.COM uintptr_t lhs = *(const uintptr_t *)lp; 782*8721SJonathan.Adams@Sun.COM uintptr_t rhs = *(const uintptr_t *)rp; 783*8721SJonathan.Adams@Sun.COM if (lhs > rhs) 784*8721SJonathan.Adams@Sun.COM return (1); 785*8721SJonathan.Adams@Sun.COM if (lhs < rhs) 786*8721SJonathan.Adams@Sun.COM return (-1); 787*8721SJonathan.Adams@Sun.COM return (0); 788*8721SJonathan.Adams@Sun.COM } 789*8721SJonathan.Adams@Sun.COM 790*8721SJonathan.Adams@Sun.COM /*ARGSUSED*/ 791*8721SJonathan.Adams@Sun.COM static void 792*8721SJonathan.Adams@Sun.COM print_sobj_help(int type, const char *name, const char *ops_name, void *ign) 793*8721SJonathan.Adams@Sun.COM { 794*8721SJonathan.Adams@Sun.COM mdb_printf(" %s", name); 795*8721SJonathan.Adams@Sun.COM } 796*8721SJonathan.Adams@Sun.COM 797*8721SJonathan.Adams@Sun.COM /*ARGSUSED*/ 798*8721SJonathan.Adams@Sun.COM static void 799*8721SJonathan.Adams@Sun.COM print_tstate_help(uint_t state, const char *name, void *ignored) 800*8721SJonathan.Adams@Sun.COM { 801*8721SJonathan.Adams@Sun.COM mdb_printf(" %s", name); 802*8721SJonathan.Adams@Sun.COM } 803*8721SJonathan.Adams@Sun.COM 804*8721SJonathan.Adams@Sun.COM void 805*8721SJonathan.Adams@Sun.COM stacks_help(void) 806*8721SJonathan.Adams@Sun.COM { 807*8721SJonathan.Adams@Sun.COM mdb_printf( 808*8721SJonathan.Adams@Sun.COM "::stacks processes all of the thread stacks on the system, grouping\n" 809*8721SJonathan.Adams@Sun.COM "together threads which have the same:\n" 810*8721SJonathan.Adams@Sun.COM "\n" 811*8721SJonathan.Adams@Sun.COM " * Thread state,\n" 812*8721SJonathan.Adams@Sun.COM " * Sync object type, and\n" 813*8721SJonathan.Adams@Sun.COM " * PCs in their stack trace.\n" 814*8721SJonathan.Adams@Sun.COM "\n" 815*8721SJonathan.Adams@Sun.COM "The default output (no address or options) is just a dump of the thread\n" 816*8721SJonathan.Adams@Sun.COM "groups in the system. For a view of active threads, use \"::stacks -i\",\n" 817*8721SJonathan.Adams@Sun.COM "which filters out FREE threads (interrupt threads which are currently\n" 818*8721SJonathan.Adams@Sun.COM "inactive) and threads sleeping on a CV. (Note that those threads may still\n" 819*8721SJonathan.Adams@Sun.COM "be noteworthy; this is just for a first glance.) More general filtering\n" 820*8721SJonathan.Adams@Sun.COM "options are described below, in the \"FILTERS\" section.\n" 821*8721SJonathan.Adams@Sun.COM "\n" 822*8721SJonathan.Adams@Sun.COM "::stacks can be used in a pipeline. The input to ::stacks is one or more\n" 823*8721SJonathan.Adams@Sun.COM "thread pointers. For example, to get a summary of threads in a process,\n" 824*8721SJonathan.Adams@Sun.COM "you can do:\n" 825*8721SJonathan.Adams@Sun.COM "\n" 826*8721SJonathan.Adams@Sun.COM " %<b>procp%</b>::walk thread | ::stacks\n" 827*8721SJonathan.Adams@Sun.COM "\n" 828*8721SJonathan.Adams@Sun.COM "When output into a pipe, ::stacks prints all of the threads input,\n" 829*8721SJonathan.Adams@Sun.COM "filtered by the given filtering options. This means that multiple\n" 830*8721SJonathan.Adams@Sun.COM "::stacks invocations can be piped together to achieve more complicated\n" 831*8721SJonathan.Adams@Sun.COM "filters. For example, to get threads which have both 'fop_read' and\n" 832*8721SJonathan.Adams@Sun.COM "'cv_wait_sig_swap' in their stack trace, you could do:\n" 833*8721SJonathan.Adams@Sun.COM "\n" 834*8721SJonathan.Adams@Sun.COM " ::stacks -c fop_read | ::stacks -c cv_wait_sig_swap_core\n" 835*8721SJonathan.Adams@Sun.COM "\n" 836*8721SJonathan.Adams@Sun.COM "To get the full list of threads in each group, use the '-a' flag:\n" 837*8721SJonathan.Adams@Sun.COM "\n" 838*8721SJonathan.Adams@Sun.COM " ::stacks -a\n" 839*8721SJonathan.Adams@Sun.COM "\n"); 840*8721SJonathan.Adams@Sun.COM mdb_dec_indent(2); 841*8721SJonathan.Adams@Sun.COM mdb_printf("%<b>OPTIONS%</b>\n"); 842*8721SJonathan.Adams@Sun.COM mdb_inc_indent(2); 843*8721SJonathan.Adams@Sun.COM mdb_printf("%s", 844*8721SJonathan.Adams@Sun.COM " -a Print all of the grouped threads, instead of just a count.\n" 845*8721SJonathan.Adams@Sun.COM " -f Force a re-run of the thread stack gathering.\n" 846*8721SJonathan.Adams@Sun.COM " -v Be verbose about thread stack gathering.\n" 847*8721SJonathan.Adams@Sun.COM "\n"); 848*8721SJonathan.Adams@Sun.COM mdb_dec_indent(2); 849*8721SJonathan.Adams@Sun.COM mdb_printf("%<b>FILTERS%</b>\n"); 850*8721SJonathan.Adams@Sun.COM mdb_inc_indent(2); 851*8721SJonathan.Adams@Sun.COM mdb_printf("%s", 852*8721SJonathan.Adams@Sun.COM " -i Show active threads; equivalent to '-S CV -T FREE'.\n" 853*8721SJonathan.Adams@Sun.COM " -c func[+offset]\n" 854*8721SJonathan.Adams@Sun.COM " Only print threads whose stacks contain func/func+offset.\n" 855*8721SJonathan.Adams@Sun.COM " -C func[+offset]\n" 856*8721SJonathan.Adams@Sun.COM " Only print threads whose stacks do not contain func/func+offset.\n" 857*8721SJonathan.Adams@Sun.COM " -s {type | ALL}\n" 858*8721SJonathan.Adams@Sun.COM " Only print threads which are on a 'type' synchronization object\n" 859*8721SJonathan.Adams@Sun.COM " (SOBJ).\n" 860*8721SJonathan.Adams@Sun.COM " -S {type | ALL}\n" 861*8721SJonathan.Adams@Sun.COM " Only print threads which are not on a 'type' SOBJ.\n" 862*8721SJonathan.Adams@Sun.COM " -t tstate\n" 863*8721SJonathan.Adams@Sun.COM " Only print threads which are in thread state 'tstate'.\n" 864*8721SJonathan.Adams@Sun.COM " -T tstate\n" 865*8721SJonathan.Adams@Sun.COM " Only print threads which are not in thread state 'tstate'.\n" 866*8721SJonathan.Adams@Sun.COM "\n"); 867*8721SJonathan.Adams@Sun.COM mdb_printf(" SOBJ types:"); 868*8721SJonathan.Adams@Sun.COM sobj_type_walk(print_sobj_help, NULL); 869*8721SJonathan.Adams@Sun.COM mdb_printf("\n"); 870*8721SJonathan.Adams@Sun.COM mdb_printf("Thread states:"); 871*8721SJonathan.Adams@Sun.COM thread_walk_states(print_tstate_help, NULL); 872*8721SJonathan.Adams@Sun.COM mdb_printf(" panic\n"); 873*8721SJonathan.Adams@Sun.COM } 874*8721SJonathan.Adams@Sun.COM 875*8721SJonathan.Adams@Sun.COM /*ARGSUSED*/ 876*8721SJonathan.Adams@Sun.COM int 877*8721SJonathan.Adams@Sun.COM stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 878*8721SJonathan.Adams@Sun.COM { 879*8721SJonathan.Adams@Sun.COM size_t idx; 880*8721SJonathan.Adams@Sun.COM 881*8721SJonathan.Adams@Sun.COM char *seen = NULL; 882*8721SJonathan.Adams@Sun.COM 883*8721SJonathan.Adams@Sun.COM const char *caller_str = NULL; 884*8721SJonathan.Adams@Sun.COM const char *excl_caller_str = NULL; 885*8721SJonathan.Adams@Sun.COM uintptr_t caller = 0, excl_caller = 0; 886*8721SJonathan.Adams@Sun.COM const char *sobj = NULL; 887*8721SJonathan.Adams@Sun.COM const char *excl_sobj = NULL; 888*8721SJonathan.Adams@Sun.COM uintptr_t sobj_ops = 0, excl_sobj_ops = 0; 889*8721SJonathan.Adams@Sun.COM const char *tstate_str = NULL; 890*8721SJonathan.Adams@Sun.COM const char *excl_tstate_str = NULL; 891*8721SJonathan.Adams@Sun.COM uint_t tstate = -1U; 892*8721SJonathan.Adams@Sun.COM uint_t excl_tstate = -1U; 893*8721SJonathan.Adams@Sun.COM 894*8721SJonathan.Adams@Sun.COM uint_t all = 0; 895*8721SJonathan.Adams@Sun.COM uint_t force = 0; 896*8721SJonathan.Adams@Sun.COM uint_t interesting = 0; 897*8721SJonathan.Adams@Sun.COM uint_t verbose = 0; 898*8721SJonathan.Adams@Sun.COM 899*8721SJonathan.Adams@Sun.COM /* 900*8721SJonathan.Adams@Sun.COM * We have a slight behavior difference between having piped 901*8721SJonathan.Adams@Sun.COM * input and 'addr::stacks'. Without a pipe, we assume the 902*8721SJonathan.Adams@Sun.COM * thread pointer given is a representative thread, and so 903*8721SJonathan.Adams@Sun.COM * we include all similar threads in the system in our output. 904*8721SJonathan.Adams@Sun.COM * 905*8721SJonathan.Adams@Sun.COM * With a pipe, we filter down to just the threads in our 906*8721SJonathan.Adams@Sun.COM * input. 907*8721SJonathan.Adams@Sun.COM */ 908*8721SJonathan.Adams@Sun.COM uint_t addrspec = (flags & DCMD_ADDRSPEC); 909*8721SJonathan.Adams@Sun.COM uint_t only_matching = addrspec && (flags & DCMD_PIPE); 910*8721SJonathan.Adams@Sun.COM 911*8721SJonathan.Adams@Sun.COM mdb_pipe_t p; 912*8721SJonathan.Adams@Sun.COM 913*8721SJonathan.Adams@Sun.COM if (mdb_getopts(argc, argv, 914*8721SJonathan.Adams@Sun.COM 'a', MDB_OPT_SETBITS, TRUE, &all, 915*8721SJonathan.Adams@Sun.COM 'f', MDB_OPT_SETBITS, TRUE, &force, 916*8721SJonathan.Adams@Sun.COM 'i', MDB_OPT_SETBITS, TRUE, &interesting, 917*8721SJonathan.Adams@Sun.COM 'v', MDB_OPT_SETBITS, TRUE, &verbose, 918*8721SJonathan.Adams@Sun.COM 'c', MDB_OPT_STR, &caller_str, 919*8721SJonathan.Adams@Sun.COM 'C', MDB_OPT_STR, &excl_caller_str, 920*8721SJonathan.Adams@Sun.COM 's', MDB_OPT_STR, &sobj, 921*8721SJonathan.Adams@Sun.COM 'S', MDB_OPT_STR, &excl_sobj, 922*8721SJonathan.Adams@Sun.COM 't', MDB_OPT_STR, &tstate_str, 923*8721SJonathan.Adams@Sun.COM 'T', MDB_OPT_STR, &excl_tstate_str, 924*8721SJonathan.Adams@Sun.COM NULL) != argc) 925*8721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 926*8721SJonathan.Adams@Sun.COM 927*8721SJonathan.Adams@Sun.COM if (interesting) { 928*8721SJonathan.Adams@Sun.COM if (sobj != NULL || excl_sobj != NULL || 929*8721SJonathan.Adams@Sun.COM tstate_str != NULL || excl_tstate_str != NULL) { 930*8721SJonathan.Adams@Sun.COM mdb_warn( 931*8721SJonathan.Adams@Sun.COM "stacks: -i is incompatible with -[sStT]\n"); 932*8721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 933*8721SJonathan.Adams@Sun.COM } 934*8721SJonathan.Adams@Sun.COM excl_sobj = "CV"; 935*8721SJonathan.Adams@Sun.COM excl_tstate_str = "FREE"; 936*8721SJonathan.Adams@Sun.COM } 937*8721SJonathan.Adams@Sun.COM 938*8721SJonathan.Adams@Sun.COM if (caller_str != NULL) { 939*8721SJonathan.Adams@Sun.COM mdb_set_dot(0); 940*8721SJonathan.Adams@Sun.COM if (mdb_eval(caller_str) != 0) { 941*8721SJonathan.Adams@Sun.COM mdb_warn("stacks: evaluation of \"%s\" failed", 942*8721SJonathan.Adams@Sun.COM caller_str); 943*8721SJonathan.Adams@Sun.COM return (DCMD_ABORT); 944*8721SJonathan.Adams@Sun.COM } 945*8721SJonathan.Adams@Sun.COM caller = mdb_get_dot(); 946*8721SJonathan.Adams@Sun.COM } 947*8721SJonathan.Adams@Sun.COM 948*8721SJonathan.Adams@Sun.COM if (excl_caller_str != NULL) { 949*8721SJonathan.Adams@Sun.COM mdb_set_dot(0); 950*8721SJonathan.Adams@Sun.COM if (mdb_eval(excl_caller_str) != 0) { 951*8721SJonathan.Adams@Sun.COM mdb_warn("stacks: evaluation of \"%s\" failed", 952*8721SJonathan.Adams@Sun.COM excl_caller_str); 953*8721SJonathan.Adams@Sun.COM return (DCMD_ABORT); 954*8721SJonathan.Adams@Sun.COM } 955*8721SJonathan.Adams@Sun.COM excl_caller = mdb_get_dot(); 956*8721SJonathan.Adams@Sun.COM } 957*8721SJonathan.Adams@Sun.COM mdb_set_dot(addr); 958*8721SJonathan.Adams@Sun.COM 959*8721SJonathan.Adams@Sun.COM if (sobj != NULL && 960*8721SJonathan.Adams@Sun.COM text_to_sobj(sobj, &sobj_ops) != 0) 961*8721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 962*8721SJonathan.Adams@Sun.COM 963*8721SJonathan.Adams@Sun.COM if (excl_sobj != NULL && 964*8721SJonathan.Adams@Sun.COM text_to_sobj(excl_sobj, &excl_sobj_ops) != 0) 965*8721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 966*8721SJonathan.Adams@Sun.COM 967*8721SJonathan.Adams@Sun.COM if (sobj_ops != 0 && excl_sobj_ops != 0) { 968*8721SJonathan.Adams@Sun.COM mdb_warn("stacks: only one of -s and -S can be specified\n"); 969*8721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 970*8721SJonathan.Adams@Sun.COM } 971*8721SJonathan.Adams@Sun.COM 972*8721SJonathan.Adams@Sun.COM if (tstate_str && 973*8721SJonathan.Adams@Sun.COM text_to_tstate(tstate_str, &tstate) != 0) 974*8721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 975*8721SJonathan.Adams@Sun.COM if (excl_tstate_str && 976*8721SJonathan.Adams@Sun.COM text_to_tstate(excl_tstate_str, &excl_tstate) != 0) 977*8721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 978*8721SJonathan.Adams@Sun.COM 979*8721SJonathan.Adams@Sun.COM if (tstate != -1U && excl_tstate != -1U) { 980*8721SJonathan.Adams@Sun.COM mdb_warn("stacks: only one of -t and -T can be specified\n"); 981*8721SJonathan.Adams@Sun.COM return (DCMD_USAGE); 982*8721SJonathan.Adams@Sun.COM } 983*8721SJonathan.Adams@Sun.COM 984*8721SJonathan.Adams@Sun.COM /* 985*8721SJonathan.Adams@Sun.COM * Force a cleanup if we're connected to a live system. Never 986*8721SJonathan.Adams@Sun.COM * do a cleanup after the first invocation around the loop. 987*8721SJonathan.Adams@Sun.COM */ 988*8721SJonathan.Adams@Sun.COM force |= (mdb_get_state() == MDB_STATE_RUNNING); 989*8721SJonathan.Adams@Sun.COM if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP) 990*8721SJonathan.Adams@Sun.COM force = 0; 991*8721SJonathan.Adams@Sun.COM 992*8721SJonathan.Adams@Sun.COM stacks_cleanup(force); 993*8721SJonathan.Adams@Sun.COM 994*8721SJonathan.Adams@Sun.COM if (stacks_state == STACKS_STATE_CLEAN) { 995*8721SJonathan.Adams@Sun.COM int res = stacks_run(verbose); 996*8721SJonathan.Adams@Sun.COM if (res != DCMD_OK) 997*8721SJonathan.Adams@Sun.COM return (res); 998*8721SJonathan.Adams@Sun.COM } 999*8721SJonathan.Adams@Sun.COM 1000*8721SJonathan.Adams@Sun.COM if (!all && DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) { 1001*8721SJonathan.Adams@Sun.COM mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n", 1002*8721SJonathan.Adams@Sun.COM "THREAD", "STATE", "SOBJ", "COUNT"); 1003*8721SJonathan.Adams@Sun.COM } 1004*8721SJonathan.Adams@Sun.COM 1005*8721SJonathan.Adams@Sun.COM /* 1006*8721SJonathan.Adams@Sun.COM * If there's an address specified, we're going to further filter 1007*8721SJonathan.Adams@Sun.COM * to only entries which have an address in the input. To reduce 1008*8721SJonathan.Adams@Sun.COM * overhead (and make the sorted output come out right), we 1009*8721SJonathan.Adams@Sun.COM * use mdb_get_pipe() to grab the entire pipeline of input, then 1010*8721SJonathan.Adams@Sun.COM * use qsort() and bsearch() to speed up the search. 1011*8721SJonathan.Adams@Sun.COM */ 1012*8721SJonathan.Adams@Sun.COM if (addrspec) { 1013*8721SJonathan.Adams@Sun.COM mdb_get_pipe(&p); 1014*8721SJonathan.Adams@Sun.COM if (p.pipe_data == NULL || p.pipe_len == 0) { 1015*8721SJonathan.Adams@Sun.COM p.pipe_data = &addr; 1016*8721SJonathan.Adams@Sun.COM p.pipe_len = 1; 1017*8721SJonathan.Adams@Sun.COM } 1018*8721SJonathan.Adams@Sun.COM qsort(p.pipe_data, p.pipe_len, sizeof (uintptr_t), 1019*8721SJonathan.Adams@Sun.COM uintptrcomp); 1020*8721SJonathan.Adams@Sun.COM 1021*8721SJonathan.Adams@Sun.COM /* remove any duplicates in the data */ 1022*8721SJonathan.Adams@Sun.COM idx = 0; 1023*8721SJonathan.Adams@Sun.COM while (idx < p.pipe_len - 1) { 1024*8721SJonathan.Adams@Sun.COM uintptr_t *data = &p.pipe_data[idx]; 1025*8721SJonathan.Adams@Sun.COM size_t len = p.pipe_len - idx; 1026*8721SJonathan.Adams@Sun.COM 1027*8721SJonathan.Adams@Sun.COM if (data[0] == data[1]) { 1028*8721SJonathan.Adams@Sun.COM memmove(data, data + 1, 1029*8721SJonathan.Adams@Sun.COM (len - 1) * sizeof (*data)); 1030*8721SJonathan.Adams@Sun.COM p.pipe_len--; 1031*8721SJonathan.Adams@Sun.COM continue; /* repeat without incrementing idx */ 1032*8721SJonathan.Adams@Sun.COM } 1033*8721SJonathan.Adams@Sun.COM idx++; 1034*8721SJonathan.Adams@Sun.COM } 1035*8721SJonathan.Adams@Sun.COM 1036*8721SJonathan.Adams@Sun.COM seen = mdb_zalloc(p.pipe_len, UM_SLEEP | UM_GC); 1037*8721SJonathan.Adams@Sun.COM } 1038*8721SJonathan.Adams@Sun.COM 1039*8721SJonathan.Adams@Sun.COM for (idx = 0; idx < stacks_array_size; idx++) { 1040*8721SJonathan.Adams@Sun.COM stacks_entry_t *sep = stacks_array[idx]; 1041*8721SJonathan.Adams@Sun.COM stacks_entry_t *cur = sep; 1042*8721SJonathan.Adams@Sun.COM int frame; 1043*8721SJonathan.Adams@Sun.COM size_t count = sep->se_count; 1044*8721SJonathan.Adams@Sun.COM 1045*8721SJonathan.Adams@Sun.COM if (addrspec) { 1046*8721SJonathan.Adams@Sun.COM stacks_entry_t *head = NULL, *tail = NULL, *sp; 1047*8721SJonathan.Adams@Sun.COM size_t foundcount = 0; 1048*8721SJonathan.Adams@Sun.COM /* 1049*8721SJonathan.Adams@Sun.COM * We use the now-unused hash chain field se_next to 1050*8721SJonathan.Adams@Sun.COM * link together the dups which match our list. 1051*8721SJonathan.Adams@Sun.COM */ 1052*8721SJonathan.Adams@Sun.COM for (sp = sep; sp != NULL; sp = sp->se_dup) { 1053*8721SJonathan.Adams@Sun.COM uintptr_t *entry = bsearch(&sp->se_thread, 1054*8721SJonathan.Adams@Sun.COM p.pipe_data, p.pipe_len, sizeof (uintptr_t), 1055*8721SJonathan.Adams@Sun.COM uintptrcomp); 1056*8721SJonathan.Adams@Sun.COM if (entry != NULL) { 1057*8721SJonathan.Adams@Sun.COM foundcount++; 1058*8721SJonathan.Adams@Sun.COM seen[entry - p.pipe_data]++; 1059*8721SJonathan.Adams@Sun.COM if (head == NULL) 1060*8721SJonathan.Adams@Sun.COM head = sp; 1061*8721SJonathan.Adams@Sun.COM else 1062*8721SJonathan.Adams@Sun.COM tail->se_next = sp; 1063*8721SJonathan.Adams@Sun.COM tail = sp; 1064*8721SJonathan.Adams@Sun.COM sp->se_next = NULL; 1065*8721SJonathan.Adams@Sun.COM } 1066*8721SJonathan.Adams@Sun.COM } 1067*8721SJonathan.Adams@Sun.COM if (head == NULL) 1068*8721SJonathan.Adams@Sun.COM continue; /* no match, skip entry */ 1069*8721SJonathan.Adams@Sun.COM 1070*8721SJonathan.Adams@Sun.COM if (only_matching) { 1071*8721SJonathan.Adams@Sun.COM cur = sep = head; 1072*8721SJonathan.Adams@Sun.COM count = foundcount; 1073*8721SJonathan.Adams@Sun.COM } 1074*8721SJonathan.Adams@Sun.COM } 1075*8721SJonathan.Adams@Sun.COM 1076*8721SJonathan.Adams@Sun.COM if (caller != 0 && !stacks_has_caller(sep, caller)) 1077*8721SJonathan.Adams@Sun.COM continue; 1078*8721SJonathan.Adams@Sun.COM if (excl_caller != 0 && stacks_has_caller(sep, excl_caller)) 1079*8721SJonathan.Adams@Sun.COM continue; 1080*8721SJonathan.Adams@Sun.COM 1081*8721SJonathan.Adams@Sun.COM if (tstate != -1U) { 1082*8721SJonathan.Adams@Sun.COM if (tstate == TSTATE_PANIC) { 1083*8721SJonathan.Adams@Sun.COM if (!sep->se_panic) 1084*8721SJonathan.Adams@Sun.COM continue; 1085*8721SJonathan.Adams@Sun.COM } else if (sep->se_panic || sep->se_tstate != tstate) 1086*8721SJonathan.Adams@Sun.COM continue; 1087*8721SJonathan.Adams@Sun.COM } 1088*8721SJonathan.Adams@Sun.COM if (excl_tstate != -1U) { 1089*8721SJonathan.Adams@Sun.COM if (excl_tstate == TSTATE_PANIC) { 1090*8721SJonathan.Adams@Sun.COM if (sep->se_panic) 1091*8721SJonathan.Adams@Sun.COM continue; 1092*8721SJonathan.Adams@Sun.COM } else if (!sep->se_panic && 1093*8721SJonathan.Adams@Sun.COM sep->se_tstate == excl_tstate) 1094*8721SJonathan.Adams@Sun.COM continue; 1095*8721SJonathan.Adams@Sun.COM } 1096*8721SJonathan.Adams@Sun.COM 1097*8721SJonathan.Adams@Sun.COM if (sobj_ops == SOBJ_ALL) { 1098*8721SJonathan.Adams@Sun.COM if (sep->se_sobj_ops == 0) 1099*8721SJonathan.Adams@Sun.COM continue; 1100*8721SJonathan.Adams@Sun.COM } else if (sobj_ops != 0) { 1101*8721SJonathan.Adams@Sun.COM if (sobj_ops != sep->se_sobj_ops) 1102*8721SJonathan.Adams@Sun.COM continue; 1103*8721SJonathan.Adams@Sun.COM } 1104*8721SJonathan.Adams@Sun.COM 1105*8721SJonathan.Adams@Sun.COM if (!(interesting && sep->se_panic)) { 1106*8721SJonathan.Adams@Sun.COM if (excl_sobj_ops == SOBJ_ALL) { 1107*8721SJonathan.Adams@Sun.COM if (sep->se_sobj_ops != 0) 1108*8721SJonathan.Adams@Sun.COM continue; 1109*8721SJonathan.Adams@Sun.COM } else if (excl_sobj_ops != 0) { 1110*8721SJonathan.Adams@Sun.COM if (excl_sobj_ops == sep->se_sobj_ops) 1111*8721SJonathan.Adams@Sun.COM continue; 1112*8721SJonathan.Adams@Sun.COM } 1113*8721SJonathan.Adams@Sun.COM } 1114*8721SJonathan.Adams@Sun.COM 1115*8721SJonathan.Adams@Sun.COM if (flags & DCMD_PIPE_OUT) { 1116*8721SJonathan.Adams@Sun.COM while (sep != NULL) { 1117*8721SJonathan.Adams@Sun.COM mdb_printf("%lr\n", sep->se_thread); 1118*8721SJonathan.Adams@Sun.COM sep = only_matching ? 1119*8721SJonathan.Adams@Sun.COM sep->se_next : sep->se_dup; 1120*8721SJonathan.Adams@Sun.COM } 1121*8721SJonathan.Adams@Sun.COM continue; 1122*8721SJonathan.Adams@Sun.COM } 1123*8721SJonathan.Adams@Sun.COM 1124*8721SJonathan.Adams@Sun.COM if (all) { 1125*8721SJonathan.Adams@Sun.COM mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n", 1126*8721SJonathan.Adams@Sun.COM "THREAD", "STATE", "SOBJTYPE", "COUNT"); 1127*8721SJonathan.Adams@Sun.COM } 1128*8721SJonathan.Adams@Sun.COM 1129*8721SJonathan.Adams@Sun.COM do { 1130*8721SJonathan.Adams@Sun.COM char state[20]; 1131*8721SJonathan.Adams@Sun.COM char sobj[100]; 1132*8721SJonathan.Adams@Sun.COM 1133*8721SJonathan.Adams@Sun.COM tstate_to_text(cur->se_tstate, cur->se_panic, 1134*8721SJonathan.Adams@Sun.COM state, sizeof (state)); 1135*8721SJonathan.Adams@Sun.COM sobj_to_text(cur->se_sobj_ops, 1136*8721SJonathan.Adams@Sun.COM sobj, sizeof (sobj)); 1137*8721SJonathan.Adams@Sun.COM 1138*8721SJonathan.Adams@Sun.COM if (cur == sep) 1139*8721SJonathan.Adams@Sun.COM mdb_printf("%?p %-8s %-?s %8d\n", 1140*8721SJonathan.Adams@Sun.COM cur->se_thread, state, sobj, count); 1141*8721SJonathan.Adams@Sun.COM else 1142*8721SJonathan.Adams@Sun.COM mdb_printf("%?p %-8s %-?s %8s\n", 1143*8721SJonathan.Adams@Sun.COM cur->se_thread, state, sobj, "-"); 1144*8721SJonathan.Adams@Sun.COM 1145*8721SJonathan.Adams@Sun.COM cur = only_matching ? cur->se_next : cur->se_dup; 1146*8721SJonathan.Adams@Sun.COM } while (all && cur != NULL); 1147*8721SJonathan.Adams@Sun.COM 1148*8721SJonathan.Adams@Sun.COM if (sep->se_failed != 0) { 1149*8721SJonathan.Adams@Sun.COM char *reason; 1150*8721SJonathan.Adams@Sun.COM switch (sep->se_failed) { 1151*8721SJonathan.Adams@Sun.COM case FSI_FAIL_NOTINMEMORY: 1152*8721SJonathan.Adams@Sun.COM reason = "thread not in memory"; 1153*8721SJonathan.Adams@Sun.COM break; 1154*8721SJonathan.Adams@Sun.COM case FSI_FAIL_THREADCORRUPT: 1155*8721SJonathan.Adams@Sun.COM reason = "thread structure stack info corrupt"; 1156*8721SJonathan.Adams@Sun.COM break; 1157*8721SJonathan.Adams@Sun.COM case FSI_FAIL_STACKNOTFOUND: 1158*8721SJonathan.Adams@Sun.COM reason = "no consistent stack found"; 1159*8721SJonathan.Adams@Sun.COM break; 1160*8721SJonathan.Adams@Sun.COM default: 1161*8721SJonathan.Adams@Sun.COM reason = "unknown failure"; 1162*8721SJonathan.Adams@Sun.COM break; 1163*8721SJonathan.Adams@Sun.COM } 1164*8721SJonathan.Adams@Sun.COM mdb_printf("%?s <%s>\n", "", reason); 1165*8721SJonathan.Adams@Sun.COM } 1166*8721SJonathan.Adams@Sun.COM 1167*8721SJonathan.Adams@Sun.COM for (frame = 0; frame < sep->se_depth; frame++) 1168*8721SJonathan.Adams@Sun.COM mdb_printf("%?s %a\n", "", sep->se_stack[frame]); 1169*8721SJonathan.Adams@Sun.COM if (sep->se_overflow) 1170*8721SJonathan.Adams@Sun.COM mdb_printf("%?s ... truncated ...\n", ""); 1171*8721SJonathan.Adams@Sun.COM mdb_printf("\n"); 1172*8721SJonathan.Adams@Sun.COM } 1173*8721SJonathan.Adams@Sun.COM 1174*8721SJonathan.Adams@Sun.COM if (flags & DCMD_ADDRSPEC) { 1175*8721SJonathan.Adams@Sun.COM for (idx = 0; idx < p.pipe_len; idx++) 1176*8721SJonathan.Adams@Sun.COM if (seen[idx] == 0) 1177*8721SJonathan.Adams@Sun.COM mdb_warn("stacks: %p not in thread list\n", 1178*8721SJonathan.Adams@Sun.COM p.pipe_data[idx]); 1179*8721SJonathan.Adams@Sun.COM } 1180*8721SJonathan.Adams@Sun.COM return (DCMD_OK); 1181*8721SJonathan.Adams@Sun.COM } 1182