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