dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / mdb / common / modules / libc / findstack_subr.c
blob585eef0b187a5dede1de1192e849e7da9ac6b1ea
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
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"
36 #if defined(__i386) || defined(__amd64)
37 struct rwindow {
38 uintptr_t rw_fp;
39 uintptr_t rw_rtn;
41 #endif
43 #ifndef STACK_BIAS
44 #define STACK_BIAS 0
45 #endif
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
60 #define STACKS_SOBJ_MX (uintptr_t)"MX"
61 #define STACKS_SOBJ_CV (uintptr_t)"CV"
63 int
64 thread_text_to_state(const char *state, uint_t *out)
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) {
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.
76 *out = UINT_MAX;
77 } else {
78 return (-1);
81 return (0);
84 void
85 thread_state_to_text(uint_t state, char *out, size_t out_sz)
87 (void) snprintf(out, out_sz, state ? "PARKED" : "UNPARKED");
90 int
91 sobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out)
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);
102 return (0);
105 void
106 sobj_ops_to_text(uintptr_t addr, char *out, size_t sz)
108 (void) snprintf(out, sz, "%s", addr == (uintptr_t)NULL ?
109 "<none>" : (char *)addr);
112 static int
113 stacks_module_callback(mdb_object_t *obj, void *arg)
115 stacks_module_t *smp = arg;
116 boolean_t match = (strcmp(obj->obj_name, smp->sm_name) == 0);
117 char *suffix = ".so";
118 const char *s, *next;
119 size_t len;
121 if (smp->sm_size != 0)
122 return (0);
125 * It doesn't match the name, but -- for convenience -- we want to
126 * allow matches before ".so.[suffix]". An aside: why doesn't
127 * strrstr() exist? (Don't google that. I'm serious, don't do it.
128 * If you do, and you read the thread of "why doesn't strrstr() exist?"
129 * circa 2005 you will see things that you will NEVER be able to unsee!)
131 if (!match && (s = strstr(obj->obj_name, suffix)) != NULL) {
132 while ((next = strstr(s + 1, suffix)) != NULL) {
133 s = next;
134 continue;
137 len = s - obj->obj_name;
139 match = (strncmp(smp->sm_name, obj->obj_name, len) == 0 &&
140 smp->sm_name[len] == '\0');
144 * If we have a library that has the libc directory in the path, we
145 * want to match against anything that would match libc.so.1. (This
146 * is necessary to be able to easily deal with libc implementations
147 * that have alternate hardware capabilities.)
149 if (!match && strstr(obj->obj_fullname, "/libc/") != NULL) {
150 mdb_object_t libc = *obj;
152 libc.obj_name = "libc.so.1";
153 libc.obj_fullname = "";
155 return (stacks_module_callback(&libc, arg));
158 if (match) {
159 smp->sm_text = obj->obj_base;
160 smp->sm_size = obj->obj_size;
163 return (0);
167 stacks_module(stacks_module_t *smp)
169 if (mdb_object_iter(stacks_module_callback, smp) != 0)
170 return (-1);
172 return (0);
175 typedef struct stacks_ulwp {
176 avl_node_t sulwp_node;
177 lwpid_t sulwp_id;
178 uintptr_t sulwp_addr;
179 } stacks_ulwp_t;
181 boolean_t stacks_ulwp_initialized;
182 avl_tree_t stacks_ulwp_byid;
184 /*ARGSUSED*/
186 stacks_ulwp_walk(uintptr_t addr, ulwp_t *ulwp, void *ignored)
188 stacks_ulwp_t *sulwp = mdb_alloc(sizeof (stacks_ulwp_t), UM_SLEEP);
190 sulwp->sulwp_id = ulwp->ul_lwpid;
191 sulwp->sulwp_addr = addr;
193 if (avl_find(&stacks_ulwp_byid, sulwp, NULL) != NULL) {
194 mdb_warn("found multiple LWPs with ID %d!", ulwp->ul_lwpid);
195 return (WALK_ERR);
198 avl_add(&stacks_ulwp_byid, sulwp);
200 return (WALK_NEXT);
203 static int
204 stacks_ulwp_compare(const void *l, const void *r)
206 const stacks_ulwp_t *lhs = l;
207 const stacks_ulwp_t *rhs = r;
209 if (lhs->sulwp_id > rhs->sulwp_id)
210 return (1);
212 if (lhs->sulwp_id < rhs->sulwp_id)
213 return (-1);
215 return (0);
218 /*ARGSUSED*/
220 stacks_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings)
222 mdb_reg_t reg;
223 uintptr_t fp;
224 struct rwindow frame;
225 avl_tree_t *tree = &stacks_ulwp_byid;
226 stacks_ulwp_t *sulwp, cmp;
227 ulwp_t ulwp;
229 fsip->fsi_failed = 0;
230 fsip->fsi_pc = 0;
231 fsip->fsi_sp = 0;
232 fsip->fsi_depth = 0;
233 fsip->fsi_overflow = 0;
235 if (!stacks_ulwp_initialized) {
236 avl_create(tree, stacks_ulwp_compare, sizeof (stacks_ulwp_t),
237 offsetof(stacks_ulwp_t, sulwp_node));
239 if (mdb_walk("ulwp",
240 (mdb_walk_cb_t)stacks_ulwp_walk, NULL) != 0) {
241 mdb_warn("couldn't walk 'ulwp'");
242 return (-1);
245 stacks_ulwp_initialized = B_TRUE;
248 bzero(&cmp, sizeof (cmp));
249 cmp.sulwp_id = (lwpid_t)addr;
251 if ((sulwp = avl_find(tree, &cmp, NULL)) == NULL) {
252 mdb_warn("couldn't find ulwp_t for tid %d\n", cmp.sulwp_id);
253 return (-1);
256 if (mdb_vread(&ulwp, sizeof (ulwp), sulwp->sulwp_addr) == -1) {
257 mdb_warn("couldn't read ulwp_t for tid %d at %p",
258 cmp.sulwp_id, sulwp->sulwp_addr);
259 return (-1);
262 fsip->fsi_tstate = ulwp.ul_sleepq != NULL;
263 fsip->fsi_sobj_ops = (uintptr_t)(ulwp.ul_sleepq == NULL ?
264 (uintptr_t)NULL :
265 (ulwp.ul_qtype == MX ? STACKS_SOBJ_MX : STACKS_SOBJ_CV));
267 if (mdb_getareg(addr, STACKS_REGS_FP, &reg) != 0) {
268 mdb_warn("couldn't read frame pointer for thread 0x%p", addr);
269 return (-1);
272 fsip->fsi_sp = fp = (uintptr_t)reg;
274 #if !defined(__i386)
275 if (mdb_getareg(addr, STACKS_REGS_RC, &reg) != 0) {
276 mdb_warn("couldn't read program counter for thread 0x%p", addr);
277 return (-1);
280 fsip->fsi_pc = (uintptr_t)reg;
281 #endif
283 while (fp != (uintptr_t)NULL) {
284 if (mdb_vread(&frame, sizeof (frame), fp) == -1) {
285 mdb_warn("couldn't read frame for thread 0x%p at %p",
286 addr, fp);
287 return (-1);
290 if (frame.rw_rtn == (uintptr_t)NULL)
291 break;
293 if (fsip->fsi_depth < fsip->fsi_max_depth) {
294 fsip->fsi_stack[fsip->fsi_depth++] = frame.rw_rtn;
295 } else {
296 fsip->fsi_overflow = 1;
297 break;
300 fp = frame.rw_fp + STACK_BIAS;
303 return (0);
306 void
307 stacks_findstack_cleanup()
309 avl_tree_t *tree = &stacks_ulwp_byid;
310 void *cookie = NULL;
311 stacks_ulwp_t *sulwp;
313 if (!stacks_ulwp_initialized)
314 return;
316 while ((sulwp = avl_destroy_nodes(tree, &cookie)) != NULL)
317 mdb_free(sulwp, sizeof (stacks_ulwp_t));
319 bzero(tree, sizeof (*tree));
320 stacks_ulwp_initialized = B_FALSE;
323 void
324 stacks_help(void)
326 mdb_printf(
327 "::stacks processes all of the thread stacks in the process, grouping\n"
328 "together threads which have the same:\n"
329 "\n"
330 " * Thread state,\n"
331 " * Sync object type, and\n"
332 " * PCs in their stack trace.\n"
333 "\n"
334 "The default output (no address or options) is just a dump of the thread\n"
335 "groups in the process. For a view of active threads, use \"::stacks -i\",\n"
336 "which filters out threads sleeping on a CV. More general filtering options\n"
337 "are described below, in the \"FILTERS\" section.\n"
338 "\n"
339 "::stacks can be used in a pipeline. The input to ::stacks is one or more\n"
340 "thread IDs. When output into a pipe, ::stacks prints all of the threads \n"
341 "input, filtered by the given filtering options. This means that multiple\n"
342 "::stacks invocations can be piped together to achieve more complicated\n"
343 "filters. For example, to get threads which have both '__door_return' and\n"
344 "'mutex_lock' in their stack trace, you could do:\n"
345 "\n"
346 " ::stacks -c __door_return | ::stacks -c mutex_lock\n"
347 "\n"
348 "To get the full list of threads in each group, use the '-a' flag:\n"
349 "\n"
350 " ::stacks -a\n"
351 "\n");
352 mdb_dec_indent(2);
353 mdb_printf("%<b>OPTIONS%</b>\n");
354 mdb_inc_indent(2);
355 mdb_printf("%s",
356 " -a Print all of the grouped threads, instead of just a count.\n"
357 " -f Force a re-run of the thread stack gathering.\n"
358 " -v Be verbose about thread stack gathering.\n"
359 "\n");
360 mdb_dec_indent(2);
361 mdb_printf("%<b>FILTERS%</b>\n");
362 mdb_inc_indent(2);
363 mdb_printf("%s",
364 " -i Show active threads; equivalent to '-S CV'.\n"
365 " -c func[+offset]\n"
366 " Only print threads whose stacks contain func/func+offset.\n"
367 " -C func[+offset]\n"
368 " Only print threads whose stacks do not contain func/func+offset.\n"
369 " -m module\n"
370 " Only print threads whose stacks contain functions from module.\n"
371 " -M module\n"
372 " Only print threads whose stacks do not contain functions from\n"
373 " module.\n"
374 " -s {type | ALL}\n"
375 " Only print threads which are on a 'type' synchronization object\n"
376 " (SOBJ).\n"
377 " -S {type | ALL}\n"
378 " Only print threads which are not on a 'type' SOBJ.\n"
379 " -t tstate\n"
380 " Only print threads which are in thread state 'tstate'.\n"
381 " -T tstate\n"
382 " Only print threads which are not in thread state 'tstate'.\n"
383 "\n");