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