1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <mdb/mdb_modapi.h> 30*0Sstevel@tonic-gate #include <mdb/mdb_ctf.h> 31*0Sstevel@tonic-gate 32*0Sstevel@tonic-gate #include <sys/types.h> 33*0Sstevel@tonic-gate #include <sys/regset.h> 34*0Sstevel@tonic-gate #include <sys/stack.h> 35*0Sstevel@tonic-gate #include <sys/thread.h> 36*0Sstevel@tonic-gate 37*0Sstevel@tonic-gate #include "findstack.h" 38*0Sstevel@tonic-gate 39*0Sstevel@tonic-gate #ifndef STACK_BIAS 40*0Sstevel@tonic-gate #define STACK_BIAS 0 41*0Sstevel@tonic-gate #endif 42*0Sstevel@tonic-gate 43*0Sstevel@tonic-gate #define fs_dprintf(x) \ 44*0Sstevel@tonic-gate if (findstack_debug_on) { \ 45*0Sstevel@tonic-gate mdb_printf("findstack debug: "); \ 46*0Sstevel@tonic-gate /*CSTYLED*/ \ 47*0Sstevel@tonic-gate mdb_printf x ; \ 48*0Sstevel@tonic-gate } 49*0Sstevel@tonic-gate 50*0Sstevel@tonic-gate static int findstack_debug_on = 0; 51*0Sstevel@tonic-gate 52*0Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 53*0Sstevel@tonic-gate struct rwindow { 54*0Sstevel@tonic-gate uintptr_t rw_fp; 55*0Sstevel@tonic-gate uintptr_t rw_pc; 56*0Sstevel@tonic-gate }; 57*0Sstevel@tonic-gate #endif 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gate #define TOO_BIG_FOR_A_STACK (1024 * 1024) 60*0Sstevel@tonic-gate 61*0Sstevel@tonic-gate #define KTOU(p) ((p) - kbase + ubase) 62*0Sstevel@tonic-gate #define UTOK(p) ((p) - ubase + kbase) 63*0Sstevel@tonic-gate 64*0Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 65*0Sstevel@tonic-gate static GElf_Sym thread_exit_sym; 66*0Sstevel@tonic-gate #endif 67*0Sstevel@tonic-gate 68*0Sstevel@tonic-gate #define CRAWL_FOUNDALL (-1) 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gate /* 71*0Sstevel@tonic-gate * Given a stack pointer, try to crawl down it to the bottom. 72*0Sstevel@tonic-gate * "frame" is a VA in MDB's address space. 73*0Sstevel@tonic-gate * 74*0Sstevel@tonic-gate * Returns the number of frames successfully crawled down, or 75*0Sstevel@tonic-gate * CRAWL_FOUNDALL if it got to the bottom of the stack. 76*0Sstevel@tonic-gate */ 77*0Sstevel@tonic-gate static int 78*0Sstevel@tonic-gate crawl(uintptr_t frame, uintptr_t kbase, uintptr_t ktop, uintptr_t ubase, 79*0Sstevel@tonic-gate int kill_fp) 80*0Sstevel@tonic-gate { 81*0Sstevel@tonic-gate int levels = 0; 82*0Sstevel@tonic-gate 83*0Sstevel@tonic-gate fs_dprintf(("<0> frame = %p, kbase = %p, ktop = %p, ubase = %p\n", 84*0Sstevel@tonic-gate frame, kbase, ktop, ubase)); 85*0Sstevel@tonic-gate for (;;) { 86*0Sstevel@tonic-gate uintptr_t fp; 87*0Sstevel@tonic-gate long *fpp = (long *)&((struct rwindow *)frame)->rw_fp; 88*0Sstevel@tonic-gate 89*0Sstevel@tonic-gate fs_dprintf(("<1> fpp = %p, frame = %p\n", fpp, frame)); 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gate if ((frame & (STACK_ALIGN - 1)) != 0) 92*0Sstevel@tonic-gate break; 93*0Sstevel@tonic-gate 94*0Sstevel@tonic-gate fp = ((struct rwindow *)frame)->rw_fp + STACK_BIAS; 95*0Sstevel@tonic-gate fs_dprintf(("<2> fp = %p\n", fp)); 96*0Sstevel@tonic-gate 97*0Sstevel@tonic-gate if (fp == ktop) 98*0Sstevel@tonic-gate return (CRAWL_FOUNDALL); 99*0Sstevel@tonic-gate fs_dprintf(("<3> not at base\n")); 100*0Sstevel@tonic-gate 101*0Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 102*0Sstevel@tonic-gate if (ktop - fp == sizeof (struct rwindow)) { 103*0Sstevel@tonic-gate fs_dprintf(("<4> found base\n")); 104*0Sstevel@tonic-gate return (CRAWL_FOUNDALL); 105*0Sstevel@tonic-gate } 106*0Sstevel@tonic-gate #endif 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate fs_dprintf(("<5> fp = %p, kbase = %p, ktop - size = %p\n", 109*0Sstevel@tonic-gate fp, kbase, ktop - sizeof (struct rwindow))); 110*0Sstevel@tonic-gate 111*0Sstevel@tonic-gate if (fp < kbase || fp >= (ktop - sizeof (struct rwindow))) 112*0Sstevel@tonic-gate break; 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate frame = KTOU(fp); 115*0Sstevel@tonic-gate fs_dprintf(("<6> frame = %p\n", frame)); 116*0Sstevel@tonic-gate 117*0Sstevel@tonic-gate /* 118*0Sstevel@tonic-gate * NULL out the old %fp so we don't go down this stack 119*0Sstevel@tonic-gate * more than once. 120*0Sstevel@tonic-gate */ 121*0Sstevel@tonic-gate if (kill_fp) { 122*0Sstevel@tonic-gate fs_dprintf(("<7> fpp = %p\n", fpp)); 123*0Sstevel@tonic-gate *fpp = NULL; 124*0Sstevel@tonic-gate } 125*0Sstevel@tonic-gate 126*0Sstevel@tonic-gate fs_dprintf(("<8> levels = %d\n", levels)); 127*0Sstevel@tonic-gate levels++; 128*0Sstevel@tonic-gate } 129*0Sstevel@tonic-gate 130*0Sstevel@tonic-gate return (levels); 131*0Sstevel@tonic-gate } 132*0Sstevel@tonic-gate 133*0Sstevel@tonic-gate /* 134*0Sstevel@tonic-gate * "sp" is a kernel VA. 135*0Sstevel@tonic-gate */ 136*0Sstevel@tonic-gate static int 137*0Sstevel@tonic-gate print_stack(uintptr_t sp, uintptr_t pc, uintptr_t addr, 138*0Sstevel@tonic-gate int argc, const mdb_arg_t *argv, int free_state) 139*0Sstevel@tonic-gate { 140*0Sstevel@tonic-gate int showargs = 0, count, err; 141*0Sstevel@tonic-gate 142*0Sstevel@tonic-gate count = mdb_getopts(argc, argv, 143*0Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &showargs, NULL); 144*0Sstevel@tonic-gate argc -= count; 145*0Sstevel@tonic-gate argv += count; 146*0Sstevel@tonic-gate 147*0Sstevel@tonic-gate if (argc > 1 || (argc == 1 && argv->a_type != MDB_TYPE_STRING)) 148*0Sstevel@tonic-gate return (DCMD_USAGE); 149*0Sstevel@tonic-gate 150*0Sstevel@tonic-gate mdb_printf("stack pointer for thread %p%s: %p\n", 151*0Sstevel@tonic-gate addr, (free_state ? " (TS_FREE)" : ""), sp); 152*0Sstevel@tonic-gate if (pc != 0) 153*0Sstevel@tonic-gate mdb_printf("[ %0?lr %a() ]\n", sp, pc); 154*0Sstevel@tonic-gate 155*0Sstevel@tonic-gate mdb_inc_indent(2); 156*0Sstevel@tonic-gate mdb_set_dot(sp); 157*0Sstevel@tonic-gate 158*0Sstevel@tonic-gate if (argc == 1) 159*0Sstevel@tonic-gate err = mdb_eval(argv->a_un.a_str); 160*0Sstevel@tonic-gate else if (showargs) 161*0Sstevel@tonic-gate err = mdb_eval("<.$C"); 162*0Sstevel@tonic-gate else 163*0Sstevel@tonic-gate err = mdb_eval("<.$C0"); 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate mdb_dec_indent(2); 166*0Sstevel@tonic-gate 167*0Sstevel@tonic-gate return ((err == -1) ? DCMD_ABORT : DCMD_OK); 168*0Sstevel@tonic-gate } 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gate /*ARGSUSED*/ 171*0Sstevel@tonic-gate int 172*0Sstevel@tonic-gate findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 173*0Sstevel@tonic-gate { 174*0Sstevel@tonic-gate kthread_t thr; 175*0Sstevel@tonic-gate size_t stksz; 176*0Sstevel@tonic-gate uintptr_t ubase, utop; 177*0Sstevel@tonic-gate uintptr_t kbase, ktop; 178*0Sstevel@tonic-gate uintptr_t win, sp; 179*0Sstevel@tonic-gate int free_state; 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 182*0Sstevel@tonic-gate return (DCMD_USAGE); 183*0Sstevel@tonic-gate 184*0Sstevel@tonic-gate bzero(&thr, sizeof (thr)); 185*0Sstevel@tonic-gate if (mdb_ctf_vread(&thr, "kthread_t", addr, 186*0Sstevel@tonic-gate MDB_CTF_VREAD_IGNORE_ALL) == -1) { 187*0Sstevel@tonic-gate mdb_warn("couldn't read thread at %p\n", addr); 188*0Sstevel@tonic-gate return (DCMD_ERR); 189*0Sstevel@tonic-gate } 190*0Sstevel@tonic-gate 191*0Sstevel@tonic-gate if ((thr.t_schedflag & TS_LOAD) == 0) { 192*0Sstevel@tonic-gate mdb_warn("thread %p isn't in memory\n", addr); 193*0Sstevel@tonic-gate return (DCMD_ERR); 194*0Sstevel@tonic-gate } 195*0Sstevel@tonic-gate 196*0Sstevel@tonic-gate if (thr.t_stk < thr.t_stkbase) { 197*0Sstevel@tonic-gate mdb_warn("stack base or stack top corrupt for thread %p\n", 198*0Sstevel@tonic-gate addr); 199*0Sstevel@tonic-gate return (DCMD_ERR); 200*0Sstevel@tonic-gate } 201*0Sstevel@tonic-gate 202*0Sstevel@tonic-gate free_state = thr.t_state == TS_FREE; 203*0Sstevel@tonic-gate 204*0Sstevel@tonic-gate kbase = (uintptr_t)thr.t_stkbase; 205*0Sstevel@tonic-gate ktop = (uintptr_t)thr.t_stk; 206*0Sstevel@tonic-gate stksz = ktop - kbase; 207*0Sstevel@tonic-gate 208*0Sstevel@tonic-gate #ifdef __amd64 209*0Sstevel@tonic-gate /* 210*0Sstevel@tonic-gate * The stack on amd64 is intentionally misaligned, so ignore the top 211*0Sstevel@tonic-gate * half-frame. See thread_stk_init(). When handling traps, the frame 212*0Sstevel@tonic-gate * is automatically aligned by the hardware, so we only alter ktop if 213*0Sstevel@tonic-gate * needed. 214*0Sstevel@tonic-gate */ 215*0Sstevel@tonic-gate if ((ktop & (STACK_ALIGN - 1)) != 0) 216*0Sstevel@tonic-gate ktop -= STACK_ENTRY_ALIGN; 217*0Sstevel@tonic-gate #endif 218*0Sstevel@tonic-gate 219*0Sstevel@tonic-gate /* 220*0Sstevel@tonic-gate * If the stack size is larger than a meg, assume that it's bogus. 221*0Sstevel@tonic-gate */ 222*0Sstevel@tonic-gate if (stksz > TOO_BIG_FOR_A_STACK) { 223*0Sstevel@tonic-gate mdb_warn("stack size for thread %p is too big to be " 224*0Sstevel@tonic-gate "reasonable\n", addr); 225*0Sstevel@tonic-gate return (DCMD_ERR); 226*0Sstevel@tonic-gate } 227*0Sstevel@tonic-gate 228*0Sstevel@tonic-gate /* 229*0Sstevel@tonic-gate * This could be (and was) a UM_GC allocation. Unfortunately, 230*0Sstevel@tonic-gate * stksz tends to be very large. As currently implemented, dcmds 231*0Sstevel@tonic-gate * invoked as part of pipelines don't have their UM_GC-allocated 232*0Sstevel@tonic-gate * memory freed until the pipeline completes. With stksz in the 233*0Sstevel@tonic-gate * neighborhood of 20k, the popular ::walk thread |::findstack 234*0Sstevel@tonic-gate * pipeline can easily run memory-constrained debuggers (kmdb) out 235*0Sstevel@tonic-gate * of memory. This can be changed back to a gc-able allocation when 236*0Sstevel@tonic-gate * the debugger is changed to free UM_GC memory more promptly. 237*0Sstevel@tonic-gate */ 238*0Sstevel@tonic-gate ubase = (uintptr_t)mdb_alloc(stksz, UM_SLEEP); 239*0Sstevel@tonic-gate utop = ubase + stksz; 240*0Sstevel@tonic-gate if (mdb_vread((caddr_t)ubase, stksz, kbase) != stksz) { 241*0Sstevel@tonic-gate mdb_free((void *)ubase, stksz); 242*0Sstevel@tonic-gate mdb_warn("couldn't read entire stack for thread %p\n", addr); 243*0Sstevel@tonic-gate return (DCMD_ERR); 244*0Sstevel@tonic-gate } 245*0Sstevel@tonic-gate 246*0Sstevel@tonic-gate /* 247*0Sstevel@tonic-gate * Try the saved %sp first, if it looks reasonable. 248*0Sstevel@tonic-gate */ 249*0Sstevel@tonic-gate sp = KTOU((uintptr_t)thr.t_sp + STACK_BIAS); 250*0Sstevel@tonic-gate if (sp >= ubase && sp <= utop) { 251*0Sstevel@tonic-gate if (crawl(sp, kbase, ktop, ubase, 0) == CRAWL_FOUNDALL) { 252*0Sstevel@tonic-gate mdb_free((void *)ubase, stksz); 253*0Sstevel@tonic-gate #if defined(__i386) 254*0Sstevel@tonic-gate return (print_stack((uintptr_t)thr.t_sp, 0, addr, 255*0Sstevel@tonic-gate argc, argv, free_state)); 256*0Sstevel@tonic-gate #else 257*0Sstevel@tonic-gate return (print_stack((uintptr_t)thr.t_sp, thr.t_pc, addr, 258*0Sstevel@tonic-gate argc, argv, free_state)); 259*0Sstevel@tonic-gate #endif 260*0Sstevel@tonic-gate } 261*0Sstevel@tonic-gate } 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate /* 264*0Sstevel@tonic-gate * Now walk through the whole stack, starting at the base, 265*0Sstevel@tonic-gate * trying every possible "window". 266*0Sstevel@tonic-gate */ 267*0Sstevel@tonic-gate for (win = ubase; 268*0Sstevel@tonic-gate win + sizeof (struct rwindow) <= utop; 269*0Sstevel@tonic-gate win += sizeof (struct rwindow *)) { 270*0Sstevel@tonic-gate if (crawl(win, kbase, ktop, ubase, 1) == CRAWL_FOUNDALL) { 271*0Sstevel@tonic-gate mdb_free((void *)ubase, stksz); 272*0Sstevel@tonic-gate return (print_stack(UTOK(win) - STACK_BIAS, 0, addr, 273*0Sstevel@tonic-gate argc, argv, free_state)); 274*0Sstevel@tonic-gate } 275*0Sstevel@tonic-gate } 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate /* 278*0Sstevel@tonic-gate * We didn't conclusively find the stack. So we'll take another lap, 279*0Sstevel@tonic-gate * and print out anything that looks possible. 280*0Sstevel@tonic-gate */ 281*0Sstevel@tonic-gate mdb_printf("Possible stack pointers for thread %p:\n", addr); 282*0Sstevel@tonic-gate (void) mdb_vread((caddr_t)ubase, stksz, kbase); 283*0Sstevel@tonic-gate 284*0Sstevel@tonic-gate for (win = ubase; 285*0Sstevel@tonic-gate win + sizeof (struct rwindow) <= utop; 286*0Sstevel@tonic-gate win += sizeof (struct rwindow *)) { 287*0Sstevel@tonic-gate uintptr_t fp = ((struct rwindow *)win)->rw_fp; 288*0Sstevel@tonic-gate int levels; 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate if ((levels = crawl(win, kbase, ktop, ubase, 1)) > 1) { 291*0Sstevel@tonic-gate mdb_printf(" %p (%d)\n", fp, levels); 292*0Sstevel@tonic-gate } else if (levels == CRAWL_FOUNDALL) { 293*0Sstevel@tonic-gate /* 294*0Sstevel@tonic-gate * If this is a live system, the stack could change 295*0Sstevel@tonic-gate * between the two mdb_vread(ubase, utop, kbase)'s, 296*0Sstevel@tonic-gate * and we could have a fully valid stack here. 297*0Sstevel@tonic-gate */ 298*0Sstevel@tonic-gate mdb_free((void *)ubase, stksz); 299*0Sstevel@tonic-gate return (print_stack(UTOK(win) - STACK_BIAS, 0, addr, 300*0Sstevel@tonic-gate argc, argv, free_state)); 301*0Sstevel@tonic-gate } 302*0Sstevel@tonic-gate } 303*0Sstevel@tonic-gate 304*0Sstevel@tonic-gate mdb_free((void *)ubase, stksz); 305*0Sstevel@tonic-gate return (DCMD_OK); 306*0Sstevel@tonic-gate } 307*0Sstevel@tonic-gate 308*0Sstevel@tonic-gate /*ARGSUSED*/ 309*0Sstevel@tonic-gate int 310*0Sstevel@tonic-gate findstack_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *av) 311*0Sstevel@tonic-gate { 312*0Sstevel@tonic-gate findstack_debug_on ^= 1; 313*0Sstevel@tonic-gate 314*0Sstevel@tonic-gate mdb_printf("findstack: debugging is now %s\n", 315*0Sstevel@tonic-gate findstack_debug_on ? "on" : "off"); 316*0Sstevel@tonic-gate 317*0Sstevel@tonic-gate return (DCMD_OK); 318*0Sstevel@tonic-gate } 319*0Sstevel@tonic-gate 320*0Sstevel@tonic-gate int 321*0Sstevel@tonic-gate findstack_init(void) 322*0Sstevel@tonic-gate { 323*0Sstevel@tonic-gate #if defined(__i386) || defined(__amd64) 324*0Sstevel@tonic-gate if (mdb_lookup_by_name("thread_exit", &thread_exit_sym) == -1) { 325*0Sstevel@tonic-gate mdb_warn("couldn't find 'thread_exit' symbol"); 326*0Sstevel@tonic-gate return (DCMD_ABORT); 327*0Sstevel@tonic-gate } 328*0Sstevel@tonic-gate #endif 329*0Sstevel@tonic-gate 330*0Sstevel@tonic-gate return (DCMD_OK); 331*0Sstevel@tonic-gate } 332