xref: /onnv-gate/usr/src/cmd/mdb/common/modules/libc/findstack_subr.c (revision 12902:3bb859a7330c)
1*12902SBryan.Cantrill@Sun.COM /*
2*12902SBryan.Cantrill@Sun.COM  * CDDL HEADER START
3*12902SBryan.Cantrill@Sun.COM  *
4*12902SBryan.Cantrill@Sun.COM  * The contents of this file are subject to the terms of the
5*12902SBryan.Cantrill@Sun.COM  * Common Development and Distribution License (the "License").
6*12902SBryan.Cantrill@Sun.COM  * You may not use this file except in compliance with the License.
7*12902SBryan.Cantrill@Sun.COM  *
8*12902SBryan.Cantrill@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*12902SBryan.Cantrill@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*12902SBryan.Cantrill@Sun.COM  * See the License for the specific language governing permissions
11*12902SBryan.Cantrill@Sun.COM  * and limitations under the License.
12*12902SBryan.Cantrill@Sun.COM  *
13*12902SBryan.Cantrill@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*12902SBryan.Cantrill@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*12902SBryan.Cantrill@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*12902SBryan.Cantrill@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*12902SBryan.Cantrill@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*12902SBryan.Cantrill@Sun.COM  *
19*12902SBryan.Cantrill@Sun.COM  * CDDL HEADER END
20*12902SBryan.Cantrill@Sun.COM  */
21*12902SBryan.Cantrill@Sun.COM 
22*12902SBryan.Cantrill@Sun.COM /*
23*12902SBryan.Cantrill@Sun.COM  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24*12902SBryan.Cantrill@Sun.COM  */
25*12902SBryan.Cantrill@Sun.COM 
26*12902SBryan.Cantrill@Sun.COM #include <mdb/mdb_modapi.h>
27*12902SBryan.Cantrill@Sun.COM #include <fcntl.h>
28*12902SBryan.Cantrill@Sun.COM #include <stdio.h>
29*12902SBryan.Cantrill@Sun.COM #include <stdlib.h>
30*12902SBryan.Cantrill@Sun.COM #include <sys/avl.h>
31*12902SBryan.Cantrill@Sun.COM #include <sys/lwp.h>
32*12902SBryan.Cantrill@Sun.COM #include <thr_uberdata.h>
33*12902SBryan.Cantrill@Sun.COM #include <stddef.h>
34*12902SBryan.Cantrill@Sun.COM #include "findstack.h"
35*12902SBryan.Cantrill@Sun.COM 
36*12902SBryan.Cantrill@Sun.COM #if defined(__i386) || defined(__amd64)
37*12902SBryan.Cantrill@Sun.COM struct rwindow {
38*12902SBryan.Cantrill@Sun.COM 	uintptr_t rw_fp;
39*12902SBryan.Cantrill@Sun.COM 	uintptr_t rw_rtn;
40*12902SBryan.Cantrill@Sun.COM };
41*12902SBryan.Cantrill@Sun.COM #endif
42*12902SBryan.Cantrill@Sun.COM 
43*12902SBryan.Cantrill@Sun.COM #ifndef STACK_BIAS
44*12902SBryan.Cantrill@Sun.COM #define	STACK_BIAS	0
45*12902SBryan.Cantrill@Sun.COM #endif
46*12902SBryan.Cantrill@Sun.COM 
47*12902SBryan.Cantrill@Sun.COM #ifdef __amd64
48*12902SBryan.Cantrill@Sun.COM #define	STACKS_REGS_FP	"rbp"
49*12902SBryan.Cantrill@Sun.COM #define	STACKS_REGS_RC	"rip"
50*12902SBryan.Cantrill@Sun.COM #else
51*12902SBryan.Cantrill@Sun.COM #ifdef __i386
52*12902SBryan.Cantrill@Sun.COM #define	STACKS_REGS_FP	"ebp"
53*12902SBryan.Cantrill@Sun.COM #define	STACKS_REGS_RC	"eip"
54*12902SBryan.Cantrill@Sun.COM #else
55*12902SBryan.Cantrill@Sun.COM #define	STACKS_REGS_FP	"fp"
56*12902SBryan.Cantrill@Sun.COM #define	STACKS_REGS_RC	"pc"
57*12902SBryan.Cantrill@Sun.COM #endif
58*12902SBryan.Cantrill@Sun.COM #endif
59*12902SBryan.Cantrill@Sun.COM 
60*12902SBryan.Cantrill@Sun.COM #define	STACKS_SOBJ_MX	(uintptr_t)"MX"
61*12902SBryan.Cantrill@Sun.COM #define	STACKS_SOBJ_CV	(uintptr_t)"CV"
62*12902SBryan.Cantrill@Sun.COM 
63*12902SBryan.Cantrill@Sun.COM int
thread_text_to_state(const char * state,uint_t * out)64*12902SBryan.Cantrill@Sun.COM thread_text_to_state(const char *state, uint_t *out)
65*12902SBryan.Cantrill@Sun.COM {
66*12902SBryan.Cantrill@Sun.COM 	if (strcmp(state, "PARKED") == 0) {
67*12902SBryan.Cantrill@Sun.COM 		*out = B_TRUE;
68*12902SBryan.Cantrill@Sun.COM 	} else if (strcmp(state, "UNPARKED") == 0) {
69*12902SBryan.Cantrill@Sun.COM 		*out = B_FALSE;
70*12902SBryan.Cantrill@Sun.COM 	} else if (strcmp(state, "FREE") == 0) {
71*12902SBryan.Cantrill@Sun.COM 		/*
72*12902SBryan.Cantrill@Sun.COM 		 * When run with "-i", ::stacks filters out "FREE" threads.
73*12902SBryan.Cantrill@Sun.COM 		 * We therefore need to recognize "FREE", and set it to a
74*12902SBryan.Cantrill@Sun.COM 		 * value that will never match fsi_tstate.
75*12902SBryan.Cantrill@Sun.COM 		 */
76*12902SBryan.Cantrill@Sun.COM 		*out = UINT_MAX;
77*12902SBryan.Cantrill@Sun.COM 	} else {
78*12902SBryan.Cantrill@Sun.COM 		return (-1);
79*12902SBryan.Cantrill@Sun.COM 	}
80*12902SBryan.Cantrill@Sun.COM 
81*12902SBryan.Cantrill@Sun.COM 	return (0);
82*12902SBryan.Cantrill@Sun.COM }
83*12902SBryan.Cantrill@Sun.COM 
84*12902SBryan.Cantrill@Sun.COM void
thread_state_to_text(uint_t state,char * out,size_t out_sz)85*12902SBryan.Cantrill@Sun.COM thread_state_to_text(uint_t state, char *out, size_t out_sz)
86*12902SBryan.Cantrill@Sun.COM {
87*12902SBryan.Cantrill@Sun.COM 	(void) snprintf(out, out_sz, state ? "PARKED" : "UNPARKED");
88*12902SBryan.Cantrill@Sun.COM }
89*12902SBryan.Cantrill@Sun.COM 
90*12902SBryan.Cantrill@Sun.COM int
sobj_text_to_ops(const char * name,uintptr_t * sobj_ops_out)91*12902SBryan.Cantrill@Sun.COM sobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out)
92*12902SBryan.Cantrill@Sun.COM {
93*12902SBryan.Cantrill@Sun.COM 	if (strcmp(name, "MX") == 0) {
94*12902SBryan.Cantrill@Sun.COM 		*sobj_ops_out = STACKS_SOBJ_MX;
95*12902SBryan.Cantrill@Sun.COM 	} else if (strcmp(name, "CV") == 0) {
96*12902SBryan.Cantrill@Sun.COM 		*sobj_ops_out = STACKS_SOBJ_CV;
97*12902SBryan.Cantrill@Sun.COM 	} else {
98*12902SBryan.Cantrill@Sun.COM 		mdb_warn("sobj \"%s\" not recognized\n", name);
99*12902SBryan.Cantrill@Sun.COM 		return (-1);
100*12902SBryan.Cantrill@Sun.COM 	}
101*12902SBryan.Cantrill@Sun.COM 
102*12902SBryan.Cantrill@Sun.COM 	return (0);
103*12902SBryan.Cantrill@Sun.COM }
104*12902SBryan.Cantrill@Sun.COM 
105*12902SBryan.Cantrill@Sun.COM void
sobj_ops_to_text(uintptr_t addr,char * out,size_t sz)106*12902SBryan.Cantrill@Sun.COM sobj_ops_to_text(uintptr_t addr, char *out, size_t sz)
107*12902SBryan.Cantrill@Sun.COM {
108*12902SBryan.Cantrill@Sun.COM 	(void) snprintf(out, sz, "%s", addr == NULL ? "<none>" : (char *)addr);
109*12902SBryan.Cantrill@Sun.COM }
110*12902SBryan.Cantrill@Sun.COM 
111*12902SBryan.Cantrill@Sun.COM static int
stacks_module_callback(mdb_object_t * obj,void * arg)112*12902SBryan.Cantrill@Sun.COM stacks_module_callback(mdb_object_t *obj, void *arg)
113*12902SBryan.Cantrill@Sun.COM {
114*12902SBryan.Cantrill@Sun.COM 	stacks_module_t *smp = arg;
115*12902SBryan.Cantrill@Sun.COM 	boolean_t match = (strcmp(obj->obj_name, smp->sm_name) == 0);
116*12902SBryan.Cantrill@Sun.COM 	char *suffix = ".so";
117*12902SBryan.Cantrill@Sun.COM 	const char *s, *next;
118*12902SBryan.Cantrill@Sun.COM 	size_t len;
119*12902SBryan.Cantrill@Sun.COM 
120*12902SBryan.Cantrill@Sun.COM 	if (smp->sm_size != 0)
121*12902SBryan.Cantrill@Sun.COM 		return (0);
122*12902SBryan.Cantrill@Sun.COM 
123*12902SBryan.Cantrill@Sun.COM 	/*
124*12902SBryan.Cantrill@Sun.COM 	 * It doesn't match the name, but -- for convenience -- we want to
125*12902SBryan.Cantrill@Sun.COM 	 * allow matches before ".so.[suffix]".  An aside:  why doesn't
126*12902SBryan.Cantrill@Sun.COM 	 * strrstr() exist?  (Don't google that.  I'm serious, don't do it.
127*12902SBryan.Cantrill@Sun.COM 	 * If you do, and you read the thread of "why doesn't strrstr() exist?"
128*12902SBryan.Cantrill@Sun.COM 	 * circa 2005 you will see things that you will NEVER be able to unsee!)
129*12902SBryan.Cantrill@Sun.COM 	 */
130*12902SBryan.Cantrill@Sun.COM 	if (!match && (s = strstr(obj->obj_name, suffix)) != NULL) {
131*12902SBryan.Cantrill@Sun.COM 		while ((next = strstr(s + 1, suffix)) != NULL) {
132*12902SBryan.Cantrill@Sun.COM 			s = next;
133*12902SBryan.Cantrill@Sun.COM 			continue;
134*12902SBryan.Cantrill@Sun.COM 		}
135*12902SBryan.Cantrill@Sun.COM 
136*12902SBryan.Cantrill@Sun.COM 		len = s - obj->obj_name;
137*12902SBryan.Cantrill@Sun.COM 
138*12902SBryan.Cantrill@Sun.COM 		match = (strncmp(smp->sm_name, obj->obj_name, len) == 0 &&
139*12902SBryan.Cantrill@Sun.COM 		    smp->sm_name[len] == '\0');
140*12902SBryan.Cantrill@Sun.COM 	}
141*12902SBryan.Cantrill@Sun.COM 
142*12902SBryan.Cantrill@Sun.COM 	/*
143*12902SBryan.Cantrill@Sun.COM 	 * If we have a library that has the libc directory in the path, we
144*12902SBryan.Cantrill@Sun.COM 	 * want to match against anything that would match libc.so.1.  (This
145*12902SBryan.Cantrill@Sun.COM 	 * is necessary to be able to easily deal with libc implementations
146*12902SBryan.Cantrill@Sun.COM 	 * that have alternate hardware capabilities.)
147*12902SBryan.Cantrill@Sun.COM 	 */
148*12902SBryan.Cantrill@Sun.COM 	if (!match && strstr(obj->obj_fullname, "/libc/") != NULL) {
149*12902SBryan.Cantrill@Sun.COM 		mdb_object_t libc = *obj;
150*12902SBryan.Cantrill@Sun.COM 
151*12902SBryan.Cantrill@Sun.COM 		libc.obj_name = "libc.so.1";
152*12902SBryan.Cantrill@Sun.COM 		libc.obj_fullname = "";
153*12902SBryan.Cantrill@Sun.COM 
154*12902SBryan.Cantrill@Sun.COM 		return (stacks_module_callback(&libc, arg));
155*12902SBryan.Cantrill@Sun.COM 	}
156*12902SBryan.Cantrill@Sun.COM 
157*12902SBryan.Cantrill@Sun.COM 	if (match) {
158*12902SBryan.Cantrill@Sun.COM 		smp->sm_text = obj->obj_base;
159*12902SBryan.Cantrill@Sun.COM 		smp->sm_size = obj->obj_size;
160*12902SBryan.Cantrill@Sun.COM 	}
161*12902SBryan.Cantrill@Sun.COM 
162*12902SBryan.Cantrill@Sun.COM 	return (0);
163*12902SBryan.Cantrill@Sun.COM }
164*12902SBryan.Cantrill@Sun.COM 
165*12902SBryan.Cantrill@Sun.COM int
stacks_module(stacks_module_t * smp)166*12902SBryan.Cantrill@Sun.COM stacks_module(stacks_module_t *smp)
167*12902SBryan.Cantrill@Sun.COM {
168*12902SBryan.Cantrill@Sun.COM 	if (mdb_object_iter(stacks_module_callback, smp) != 0)
169*12902SBryan.Cantrill@Sun.COM 		return (-1);
170*12902SBryan.Cantrill@Sun.COM 
171*12902SBryan.Cantrill@Sun.COM 	return (0);
172*12902SBryan.Cantrill@Sun.COM }
173*12902SBryan.Cantrill@Sun.COM 
174*12902SBryan.Cantrill@Sun.COM typedef struct stacks_ulwp {
175*12902SBryan.Cantrill@Sun.COM 	avl_node_t sulwp_node;
176*12902SBryan.Cantrill@Sun.COM 	lwpid_t sulwp_id;
177*12902SBryan.Cantrill@Sun.COM 	uintptr_t sulwp_addr;
178*12902SBryan.Cantrill@Sun.COM } stacks_ulwp_t;
179*12902SBryan.Cantrill@Sun.COM 
180*12902SBryan.Cantrill@Sun.COM boolean_t stacks_ulwp_initialized;
181*12902SBryan.Cantrill@Sun.COM avl_tree_t stacks_ulwp_byid;
182*12902SBryan.Cantrill@Sun.COM 
183*12902SBryan.Cantrill@Sun.COM /*ARGSUSED*/
184*12902SBryan.Cantrill@Sun.COM int
stacks_ulwp_walk(uintptr_t addr,ulwp_t * ulwp,void * ignored)185*12902SBryan.Cantrill@Sun.COM stacks_ulwp_walk(uintptr_t addr, ulwp_t *ulwp, void *ignored)
186*12902SBryan.Cantrill@Sun.COM {
187*12902SBryan.Cantrill@Sun.COM 	stacks_ulwp_t *sulwp = mdb_alloc(sizeof (stacks_ulwp_t), UM_SLEEP);
188*12902SBryan.Cantrill@Sun.COM 
189*12902SBryan.Cantrill@Sun.COM 	sulwp->sulwp_id = ulwp->ul_lwpid;
190*12902SBryan.Cantrill@Sun.COM 	sulwp->sulwp_addr = addr;
191*12902SBryan.Cantrill@Sun.COM 
192*12902SBryan.Cantrill@Sun.COM 	if (avl_find(&stacks_ulwp_byid, sulwp, NULL) != NULL) {
193*12902SBryan.Cantrill@Sun.COM 		mdb_warn("found multiple LWPs with ID %d!", ulwp->ul_lwpid);
194*12902SBryan.Cantrill@Sun.COM 		return (WALK_ERR);
195*12902SBryan.Cantrill@Sun.COM 	}
196*12902SBryan.Cantrill@Sun.COM 
197*12902SBryan.Cantrill@Sun.COM 	avl_add(&stacks_ulwp_byid, sulwp);
198*12902SBryan.Cantrill@Sun.COM 
199*12902SBryan.Cantrill@Sun.COM 	return (WALK_NEXT);
200*12902SBryan.Cantrill@Sun.COM }
201*12902SBryan.Cantrill@Sun.COM 
202*12902SBryan.Cantrill@Sun.COM static int
stacks_ulwp_compare(const void * l,const void * r)203*12902SBryan.Cantrill@Sun.COM stacks_ulwp_compare(const void *l, const void *r)
204*12902SBryan.Cantrill@Sun.COM {
205*12902SBryan.Cantrill@Sun.COM 	const stacks_ulwp_t *lhs = l;
206*12902SBryan.Cantrill@Sun.COM 	const stacks_ulwp_t *rhs = r;
207*12902SBryan.Cantrill@Sun.COM 
208*12902SBryan.Cantrill@Sun.COM 	if (lhs->sulwp_id > rhs->sulwp_id)
209*12902SBryan.Cantrill@Sun.COM 		return (1);
210*12902SBryan.Cantrill@Sun.COM 
211*12902SBryan.Cantrill@Sun.COM 	if (lhs->sulwp_id < rhs->sulwp_id)
212*12902SBryan.Cantrill@Sun.COM 		return (-1);
213*12902SBryan.Cantrill@Sun.COM 
214*12902SBryan.Cantrill@Sun.COM 	return (0);
215*12902SBryan.Cantrill@Sun.COM }
216*12902SBryan.Cantrill@Sun.COM 
217*12902SBryan.Cantrill@Sun.COM /*ARGSUSED*/
218*12902SBryan.Cantrill@Sun.COM int
stacks_findstack(uintptr_t addr,findstack_info_t * fsip,uint_t print_warnings)219*12902SBryan.Cantrill@Sun.COM stacks_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings)
220*12902SBryan.Cantrill@Sun.COM {
221*12902SBryan.Cantrill@Sun.COM 	mdb_reg_t reg;
222*12902SBryan.Cantrill@Sun.COM 	uintptr_t fp;
223*12902SBryan.Cantrill@Sun.COM 	struct rwindow frame;
224*12902SBryan.Cantrill@Sun.COM 	avl_tree_t *tree = &stacks_ulwp_byid;
225*12902SBryan.Cantrill@Sun.COM 	stacks_ulwp_t *sulwp, cmp;
226*12902SBryan.Cantrill@Sun.COM 	ulwp_t ulwp;
227*12902SBryan.Cantrill@Sun.COM 
228*12902SBryan.Cantrill@Sun.COM 	fsip->fsi_failed = 0;
229*12902SBryan.Cantrill@Sun.COM 	fsip->fsi_pc = 0;
230*12902SBryan.Cantrill@Sun.COM 	fsip->fsi_sp = 0;
231*12902SBryan.Cantrill@Sun.COM 	fsip->fsi_depth = 0;
232*12902SBryan.Cantrill@Sun.COM 	fsip->fsi_overflow = 0;
233*12902SBryan.Cantrill@Sun.COM 
234*12902SBryan.Cantrill@Sun.COM 	if (!stacks_ulwp_initialized) {
235*12902SBryan.Cantrill@Sun.COM 		avl_create(tree, stacks_ulwp_compare, sizeof (stacks_ulwp_t),
236*12902SBryan.Cantrill@Sun.COM 		    offsetof(stacks_ulwp_t, sulwp_node));
237*12902SBryan.Cantrill@Sun.COM 
238*12902SBryan.Cantrill@Sun.COM 		if (mdb_walk("ulwp",
239*12902SBryan.Cantrill@Sun.COM 		    (mdb_walk_cb_t)stacks_ulwp_walk, NULL) != 0) {
240*12902SBryan.Cantrill@Sun.COM 			mdb_warn("couldn't walk 'ulwp'");
241*12902SBryan.Cantrill@Sun.COM 			return (-1);
242*12902SBryan.Cantrill@Sun.COM 		}
243*12902SBryan.Cantrill@Sun.COM 
244*12902SBryan.Cantrill@Sun.COM 		stacks_ulwp_initialized = B_TRUE;
245*12902SBryan.Cantrill@Sun.COM 	}
246*12902SBryan.Cantrill@Sun.COM 
247*12902SBryan.Cantrill@Sun.COM 	bzero(&cmp, sizeof (cmp));
248*12902SBryan.Cantrill@Sun.COM 	cmp.sulwp_id = (lwpid_t)addr;
249*12902SBryan.Cantrill@Sun.COM 
250*12902SBryan.Cantrill@Sun.COM 	if ((sulwp = avl_find(tree, &cmp, NULL)) == NULL) {
251*12902SBryan.Cantrill@Sun.COM 		mdb_warn("couldn't find ulwp_t for tid %d\n", cmp.sulwp_id);
252*12902SBryan.Cantrill@Sun.COM 		return (-1);
253*12902SBryan.Cantrill@Sun.COM 	}
254*12902SBryan.Cantrill@Sun.COM 
255*12902SBryan.Cantrill@Sun.COM 	if (mdb_vread(&ulwp, sizeof (ulwp), sulwp->sulwp_addr) == -1) {
256*12902SBryan.Cantrill@Sun.COM 		mdb_warn("couldn't read ulwp_t for tid %d at %p",
257*12902SBryan.Cantrill@Sun.COM 		    cmp.sulwp_id, sulwp->sulwp_addr);
258*12902SBryan.Cantrill@Sun.COM 		return (-1);
259*12902SBryan.Cantrill@Sun.COM 	}
260*12902SBryan.Cantrill@Sun.COM 
261*12902SBryan.Cantrill@Sun.COM 	fsip->fsi_tstate = ulwp.ul_sleepq != NULL;
262*12902SBryan.Cantrill@Sun.COM 	fsip->fsi_sobj_ops = (uintptr_t)(ulwp.ul_sleepq == NULL ? NULL :
263*12902SBryan.Cantrill@Sun.COM 	    (ulwp.ul_qtype == MX ? STACKS_SOBJ_MX : STACKS_SOBJ_CV));
264*12902SBryan.Cantrill@Sun.COM 
265*12902SBryan.Cantrill@Sun.COM 	if (mdb_getareg(addr, STACKS_REGS_FP, &reg) != 0) {
266*12902SBryan.Cantrill@Sun.COM 		mdb_warn("couldn't read frame pointer for thread 0x%p", addr);
267*12902SBryan.Cantrill@Sun.COM 		return (-1);
268*12902SBryan.Cantrill@Sun.COM 	}
269*12902SBryan.Cantrill@Sun.COM 
270*12902SBryan.Cantrill@Sun.COM 	fsip->fsi_sp = fp = (uintptr_t)reg;
271*12902SBryan.Cantrill@Sun.COM 
272*12902SBryan.Cantrill@Sun.COM #if !defined(__i386)
273*12902SBryan.Cantrill@Sun.COM 	if (mdb_getareg(addr, STACKS_REGS_RC, &reg) != 0) {
274*12902SBryan.Cantrill@Sun.COM 		mdb_warn("couldn't read program counter for thread 0x%p", addr);
275*12902SBryan.Cantrill@Sun.COM 		return (-1);
276*12902SBryan.Cantrill@Sun.COM 	}
277*12902SBryan.Cantrill@Sun.COM 
278*12902SBryan.Cantrill@Sun.COM 	fsip->fsi_pc = (uintptr_t)reg;
279*12902SBryan.Cantrill@Sun.COM #endif
280*12902SBryan.Cantrill@Sun.COM 
281*12902SBryan.Cantrill@Sun.COM 	while (fp != NULL) {
282*12902SBryan.Cantrill@Sun.COM 		if (mdb_vread(&frame, sizeof (frame), fp) == -1) {
283*12902SBryan.Cantrill@Sun.COM 			mdb_warn("couldn't read frame for thread 0x%p at %p",
284*12902SBryan.Cantrill@Sun.COM 			    addr, fp);
285*12902SBryan.Cantrill@Sun.COM 			return (-1);
286*12902SBryan.Cantrill@Sun.COM 		}
287*12902SBryan.Cantrill@Sun.COM 
288*12902SBryan.Cantrill@Sun.COM 		if (frame.rw_rtn == NULL)
289*12902SBryan.Cantrill@Sun.COM 			break;
290*12902SBryan.Cantrill@Sun.COM 
291*12902SBryan.Cantrill@Sun.COM 		if (fsip->fsi_depth < fsip->fsi_max_depth) {
292*12902SBryan.Cantrill@Sun.COM 			fsip->fsi_stack[fsip->fsi_depth++] = frame.rw_rtn;
293*12902SBryan.Cantrill@Sun.COM 		} else {
294*12902SBryan.Cantrill@Sun.COM 			fsip->fsi_overflow = 1;
295*12902SBryan.Cantrill@Sun.COM 			break;
296*12902SBryan.Cantrill@Sun.COM 		}
297*12902SBryan.Cantrill@Sun.COM 
298*12902SBryan.Cantrill@Sun.COM 		fp = frame.rw_fp + STACK_BIAS;
299*12902SBryan.Cantrill@Sun.COM 	}
300*12902SBryan.Cantrill@Sun.COM 
301*12902SBryan.Cantrill@Sun.COM 	return (0);
302*12902SBryan.Cantrill@Sun.COM }
303*12902SBryan.Cantrill@Sun.COM 
304*12902SBryan.Cantrill@Sun.COM void
stacks_findstack_cleanup()305*12902SBryan.Cantrill@Sun.COM stacks_findstack_cleanup()
306*12902SBryan.Cantrill@Sun.COM {
307*12902SBryan.Cantrill@Sun.COM 	avl_tree_t *tree = &stacks_ulwp_byid;
308*12902SBryan.Cantrill@Sun.COM 	void *cookie = NULL;
309*12902SBryan.Cantrill@Sun.COM 	stacks_ulwp_t *sulwp;
310*12902SBryan.Cantrill@Sun.COM 
311*12902SBryan.Cantrill@Sun.COM 	if (!stacks_ulwp_initialized)
312*12902SBryan.Cantrill@Sun.COM 		return;
313*12902SBryan.Cantrill@Sun.COM 
314*12902SBryan.Cantrill@Sun.COM 	while ((sulwp = avl_destroy_nodes(tree, &cookie)) != NULL)
315*12902SBryan.Cantrill@Sun.COM 		mdb_free(sulwp, sizeof (stacks_ulwp_t));
316*12902SBryan.Cantrill@Sun.COM 
317*12902SBryan.Cantrill@Sun.COM 	bzero(tree, sizeof (*tree));
318*12902SBryan.Cantrill@Sun.COM 	stacks_ulwp_initialized = B_FALSE;
319*12902SBryan.Cantrill@Sun.COM }
320*12902SBryan.Cantrill@Sun.COM 
321*12902SBryan.Cantrill@Sun.COM void
stacks_help(void)322*12902SBryan.Cantrill@Sun.COM stacks_help(void)
323*12902SBryan.Cantrill@Sun.COM {
324*12902SBryan.Cantrill@Sun.COM 	mdb_printf(
325*12902SBryan.Cantrill@Sun.COM "::stacks processes all of the thread stacks in the process, grouping\n"
326*12902SBryan.Cantrill@Sun.COM "together threads which have the same:\n"
327*12902SBryan.Cantrill@Sun.COM "\n"
328*12902SBryan.Cantrill@Sun.COM "  * Thread state,\n"
329*12902SBryan.Cantrill@Sun.COM "  * Sync object type, and\n"
330*12902SBryan.Cantrill@Sun.COM "  * PCs in their stack trace.\n"
331*12902SBryan.Cantrill@Sun.COM "\n"
332*12902SBryan.Cantrill@Sun.COM "The default output (no address or options) is just a dump of the thread\n"
333*12902SBryan.Cantrill@Sun.COM "groups in the process.  For a view of active threads, use \"::stacks -i\",\n"
334*12902SBryan.Cantrill@Sun.COM "which filters out threads sleeping on a CV.  More general filtering options\n"
335*12902SBryan.Cantrill@Sun.COM "are described below, in the \"FILTERS\" section.\n"
336*12902SBryan.Cantrill@Sun.COM "\n"
337*12902SBryan.Cantrill@Sun.COM "::stacks can be used in a pipeline.  The input to ::stacks is one or more\n"
338*12902SBryan.Cantrill@Sun.COM "thread IDs.  When output into a pipe, ::stacks prints all of the threads \n"
339*12902SBryan.Cantrill@Sun.COM "input, filtered by the given filtering options.  This means that multiple\n"
340*12902SBryan.Cantrill@Sun.COM "::stacks invocations can be piped together to achieve more complicated\n"
341*12902SBryan.Cantrill@Sun.COM "filters.  For example, to get threads which have both '__door_return' and\n"
342*12902SBryan.Cantrill@Sun.COM "'mutex_lock' in their stack trace, you could do:\n"
343*12902SBryan.Cantrill@Sun.COM "\n"
344*12902SBryan.Cantrill@Sun.COM "  ::stacks -c __door_return | ::stacks -c mutex_lock\n"
345*12902SBryan.Cantrill@Sun.COM "\n"
346*12902SBryan.Cantrill@Sun.COM "To get the full list of threads in each group, use the '-a' flag:\n"
347*12902SBryan.Cantrill@Sun.COM "\n"
348*12902SBryan.Cantrill@Sun.COM "  ::stacks -a\n"
349*12902SBryan.Cantrill@Sun.COM "\n");
350*12902SBryan.Cantrill@Sun.COM 	mdb_dec_indent(2);
351*12902SBryan.Cantrill@Sun.COM 	mdb_printf("%<b>OPTIONS%</b>\n");
352*12902SBryan.Cantrill@Sun.COM 	mdb_inc_indent(2);
353*12902SBryan.Cantrill@Sun.COM 	mdb_printf("%s",
354*12902SBryan.Cantrill@Sun.COM "  -a    Print all of the grouped threads, instead of just a count.\n"
355*12902SBryan.Cantrill@Sun.COM "  -f    Force a re-run of the thread stack gathering.\n"
356*12902SBryan.Cantrill@Sun.COM "  -v    Be verbose about thread stack gathering.\n"
357*12902SBryan.Cantrill@Sun.COM "\n");
358*12902SBryan.Cantrill@Sun.COM 	mdb_dec_indent(2);
359*12902SBryan.Cantrill@Sun.COM 	mdb_printf("%<b>FILTERS%</b>\n");
360*12902SBryan.Cantrill@Sun.COM 	mdb_inc_indent(2);
361*12902SBryan.Cantrill@Sun.COM 	mdb_printf("%s",
362*12902SBryan.Cantrill@Sun.COM "  -i    Show active threads; equivalent to '-S CV'.\n"
363*12902SBryan.Cantrill@Sun.COM "  -c func[+offset]\n"
364*12902SBryan.Cantrill@Sun.COM "        Only print threads whose stacks contain func/func+offset.\n"
365*12902SBryan.Cantrill@Sun.COM "  -C func[+offset]\n"
366*12902SBryan.Cantrill@Sun.COM "        Only print threads whose stacks do not contain func/func+offset.\n"
367*12902SBryan.Cantrill@Sun.COM "  -m module\n"
368*12902SBryan.Cantrill@Sun.COM "        Only print threads whose stacks contain functions from module.\n"
369*12902SBryan.Cantrill@Sun.COM "  -M module\n"
370*12902SBryan.Cantrill@Sun.COM "        Only print threads whose stacks do not contain functions from\n"
371*12902SBryan.Cantrill@Sun.COM "        module.\n"
372*12902SBryan.Cantrill@Sun.COM "  -s {type | ALL}\n"
373*12902SBryan.Cantrill@Sun.COM "        Only print threads which are on a 'type' synchronization object\n"
374*12902SBryan.Cantrill@Sun.COM "        (SOBJ).\n"
375*12902SBryan.Cantrill@Sun.COM "  -S {type | ALL}\n"
376*12902SBryan.Cantrill@Sun.COM "        Only print threads which are not on a 'type' SOBJ.\n"
377*12902SBryan.Cantrill@Sun.COM "  -t tstate\n"
378*12902SBryan.Cantrill@Sun.COM "        Only print threads which are in thread state 'tstate'.\n"
379*12902SBryan.Cantrill@Sun.COM "  -T tstate\n"
380*12902SBryan.Cantrill@Sun.COM "        Only print threads which are not in thread state 'tstate'.\n"
381*12902SBryan.Cantrill@Sun.COM "\n");
382*12902SBryan.Cantrill@Sun.COM }
383