17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5346799e8SJonathan W Adams * Common Development and Distribution License (the "License").
6346799e8SJonathan W Adams * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
21c9a6ea2eSBryan Cantrill
227c478bd9Sstevel@tonic-gate /*
23c9a6ea2eSBryan Cantrill * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
24*98144673SJosef 'Jeff' Sipek * Copyright (c) 2013, Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
257c478bd9Sstevel@tonic-gate */
267c478bd9Sstevel@tonic-gate
277c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
287c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf.h>
297c478bd9Sstevel@tonic-gate
307c478bd9Sstevel@tonic-gate #include <sys/types.h>
317c478bd9Sstevel@tonic-gate #include <sys/regset.h>
327c478bd9Sstevel@tonic-gate #include <sys/stack.h>
337c478bd9Sstevel@tonic-gate #include <sys/thread.h>
34e6fc74c6SGreg Price #include <sys/modctl.h>
35c9a6ea2eSBryan Cantrill #include <assert.h>
367c478bd9Sstevel@tonic-gate
377c478bd9Sstevel@tonic-gate #include "findstack.h"
38346799e8SJonathan W Adams #include "thread.h"
39346799e8SJonathan W Adams #include "sobj.h"
40346799e8SJonathan W Adams
41c9a6ea2eSBryan Cantrill int findstack_debug_on = 0;
427c478bd9Sstevel@tonic-gate
437c478bd9Sstevel@tonic-gate /*
447c478bd9Sstevel@tonic-gate * "sp" is a kernel VA.
457c478bd9Sstevel@tonic-gate */
467c478bd9Sstevel@tonic-gate static int
print_stack(uintptr_t sp,uintptr_t pc,uintptr_t addr,int argc,const mdb_arg_t * argv,int free_state)477c478bd9Sstevel@tonic-gate print_stack(uintptr_t sp, uintptr_t pc, uintptr_t addr,
487c478bd9Sstevel@tonic-gate int argc, const mdb_arg_t *argv, int free_state)
497c478bd9Sstevel@tonic-gate {
507c478bd9Sstevel@tonic-gate int showargs = 0, count, err;
517c478bd9Sstevel@tonic-gate
527c478bd9Sstevel@tonic-gate count = mdb_getopts(argc, argv,
537c478bd9Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &showargs, NULL);
547c478bd9Sstevel@tonic-gate argc -= count;
557c478bd9Sstevel@tonic-gate argv += count;
567c478bd9Sstevel@tonic-gate
577c478bd9Sstevel@tonic-gate if (argc > 1 || (argc == 1 && argv->a_type != MDB_TYPE_STRING))
587c478bd9Sstevel@tonic-gate return (DCMD_USAGE);
597c478bd9Sstevel@tonic-gate
607c478bd9Sstevel@tonic-gate mdb_printf("stack pointer for thread %p%s: %p\n",
617c478bd9Sstevel@tonic-gate addr, (free_state ? " (TS_FREE)" : ""), sp);
627c478bd9Sstevel@tonic-gate if (pc != 0)
637c478bd9Sstevel@tonic-gate mdb_printf("[ %0?lr %a() ]\n", sp, pc);
647c478bd9Sstevel@tonic-gate
657c478bd9Sstevel@tonic-gate mdb_inc_indent(2);
667c478bd9Sstevel@tonic-gate mdb_set_dot(sp);
677c478bd9Sstevel@tonic-gate
687c478bd9Sstevel@tonic-gate if (argc == 1)
697c478bd9Sstevel@tonic-gate err = mdb_eval(argv->a_un.a_str);
707c478bd9Sstevel@tonic-gate else if (showargs)
717c478bd9Sstevel@tonic-gate err = mdb_eval("<.$C");
727c478bd9Sstevel@tonic-gate else
737c478bd9Sstevel@tonic-gate err = mdb_eval("<.$C0");
747c478bd9Sstevel@tonic-gate
757c478bd9Sstevel@tonic-gate mdb_dec_indent(2);
767c478bd9Sstevel@tonic-gate
777c478bd9Sstevel@tonic-gate return ((err == -1) ? DCMD_ABORT : DCMD_OK);
787c478bd9Sstevel@tonic-gate }
797c478bd9Sstevel@tonic-gate
80346799e8SJonathan W Adams int
findstack(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)81346799e8SJonathan W Adams findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
82346799e8SJonathan W Adams {
83346799e8SJonathan W Adams findstack_info_t fsi;
84346799e8SJonathan W Adams int retval;
85346799e8SJonathan W Adams
86346799e8SJonathan W Adams if (!(flags & DCMD_ADDRSPEC))
87346799e8SJonathan W Adams return (DCMD_USAGE);
88346799e8SJonathan W Adams
89346799e8SJonathan W Adams bzero(&fsi, sizeof (fsi));
90346799e8SJonathan W Adams
91c9a6ea2eSBryan Cantrill if ((retval = stacks_findstack(addr, &fsi, 1)) != DCMD_OK ||
92346799e8SJonathan W Adams fsi.fsi_failed)
93346799e8SJonathan W Adams return (retval);
94346799e8SJonathan W Adams
95346799e8SJonathan W Adams return (print_stack(fsi.fsi_sp, fsi.fsi_pc, addr,
96346799e8SJonathan W Adams argc, argv, fsi.fsi_tstate == TS_FREE));
97346799e8SJonathan W Adams }
98346799e8SJonathan W Adams
997c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1007c478bd9Sstevel@tonic-gate int
findstack_debug(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * av)1017c478bd9Sstevel@tonic-gate findstack_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *av)
1027c478bd9Sstevel@tonic-gate {
1037c478bd9Sstevel@tonic-gate findstack_debug_on ^= 1;
1047c478bd9Sstevel@tonic-gate
1057c478bd9Sstevel@tonic-gate mdb_printf("findstack: debugging is now %s\n",
1067c478bd9Sstevel@tonic-gate findstack_debug_on ? "on" : "off");
1077c478bd9Sstevel@tonic-gate
1087c478bd9Sstevel@tonic-gate return (DCMD_OK);
1097c478bd9Sstevel@tonic-gate }
1107c478bd9Sstevel@tonic-gate
111346799e8SJonathan W Adams static void
uppercase(char * p)112346799e8SJonathan W Adams uppercase(char *p)
1137c478bd9Sstevel@tonic-gate {
114346799e8SJonathan W Adams for (; *p != '\0'; p++) {
115346799e8SJonathan W Adams if (*p >= 'a' && *p <= 'z')
116346799e8SJonathan W Adams *p += 'A' - 'a';
117346799e8SJonathan W Adams }
118346799e8SJonathan W Adams }
119346799e8SJonathan W Adams
120346799e8SJonathan W Adams static void
sobj_to_text(uintptr_t addr,char * out,size_t out_sz)121346799e8SJonathan W Adams sobj_to_text(uintptr_t addr, char *out, size_t out_sz)
122346799e8SJonathan W Adams {
123346799e8SJonathan W Adams sobj_ops_to_text(addr, out, out_sz);
124346799e8SJonathan W Adams uppercase(out);
125346799e8SJonathan W Adams }
126346799e8SJonathan W Adams
127346799e8SJonathan W Adams #define SOBJ_ALL 1
128c9a6ea2eSBryan Cantrill
129346799e8SJonathan W Adams static int
text_to_sobj(const char * text,uintptr_t * out)130346799e8SJonathan W Adams text_to_sobj(const char *text, uintptr_t *out)
131346799e8SJonathan W Adams {
132346799e8SJonathan W Adams if (strcasecmp(text, "ALL") == 0) {
133346799e8SJonathan W Adams *out = SOBJ_ALL;
134346799e8SJonathan W Adams return (0);
135346799e8SJonathan W Adams }
136c9a6ea2eSBryan Cantrill
137346799e8SJonathan W Adams return (sobj_text_to_ops(text, out));
138346799e8SJonathan W Adams }
139346799e8SJonathan W Adams
140346799e8SJonathan W Adams #define TSTATE_PANIC -2U
141346799e8SJonathan W Adams static int
text_to_tstate(const char * text,uint_t * out)142346799e8SJonathan W Adams text_to_tstate(const char *text, uint_t *out)
143346799e8SJonathan W Adams {
144346799e8SJonathan W Adams if (strcasecmp(text, "panic") == 0)
145346799e8SJonathan W Adams *out = TSTATE_PANIC;
146346799e8SJonathan W Adams else if (thread_text_to_state(text, out) != 0) {
147346799e8SJonathan W Adams mdb_warn("tstate \"%s\" not recognized\n", text);
148346799e8SJonathan W Adams return (-1);
149346799e8SJonathan W Adams }
150346799e8SJonathan W Adams return (0);
151346799e8SJonathan W Adams }
152346799e8SJonathan W Adams
153346799e8SJonathan W Adams static void
tstate_to_text(uint_t tstate,uint_t paniced,char * out,size_t out_sz)154346799e8SJonathan W Adams tstate_to_text(uint_t tstate, uint_t paniced, char *out, size_t out_sz)
155346799e8SJonathan W Adams {
156346799e8SJonathan W Adams if (paniced)
157346799e8SJonathan W Adams mdb_snprintf(out, out_sz, "panic");
158346799e8SJonathan W Adams else
159346799e8SJonathan W Adams thread_state_to_text(tstate, out, out_sz);
160346799e8SJonathan W Adams uppercase(out);
161346799e8SJonathan W Adams }
162346799e8SJonathan W Adams
163346799e8SJonathan W Adams typedef struct stacks_entry {
164346799e8SJonathan W Adams struct stacks_entry *se_next;
165346799e8SJonathan W Adams struct stacks_entry *se_dup; /* dups of this stack */
166346799e8SJonathan W Adams uintptr_t se_thread;
167346799e8SJonathan W Adams uintptr_t se_sp;
168346799e8SJonathan W Adams uintptr_t se_sobj_ops;
169346799e8SJonathan W Adams uint32_t se_tstate;
170346799e8SJonathan W Adams uint32_t se_count; /* # threads w/ this stack */
171346799e8SJonathan W Adams uint8_t se_overflow;
172346799e8SJonathan W Adams uint8_t se_depth;
173346799e8SJonathan W Adams uint8_t se_failed; /* failure reason; FSI_FAIL_* */
174346799e8SJonathan W Adams uint8_t se_panic;
175346799e8SJonathan W Adams uintptr_t se_stack[1];
176346799e8SJonathan W Adams } stacks_entry_t;
177346799e8SJonathan W Adams #define STACKS_ENTRY_SIZE(x) OFFSETOF(stacks_entry_t, se_stack[(x)])
178346799e8SJonathan W Adams
179346799e8SJonathan W Adams #define STACKS_HSIZE 127
180346799e8SJonathan W Adams
181346799e8SJonathan W Adams /* Maximum stack depth reported in stacks */
182346799e8SJonathan W Adams #define STACKS_MAX_DEPTH 254
183346799e8SJonathan W Adams
184346799e8SJonathan W Adams typedef struct stacks_info {
185346799e8SJonathan W Adams size_t si_count; /* total stacks_entry_ts (incl dups) */
186346799e8SJonathan W Adams size_t si_entries; /* # entries in hash table */
187346799e8SJonathan W Adams stacks_entry_t **si_hash; /* hash table */
188346799e8SJonathan W Adams findstack_info_t si_fsi; /* transient callback state */
189346799e8SJonathan W Adams } stacks_info_t;
190346799e8SJonathan W Adams
191346799e8SJonathan W Adams /* global state cached between invocations */
192346799e8SJonathan W Adams #define STACKS_STATE_CLEAN 0
193346799e8SJonathan W Adams #define STACKS_STATE_DIRTY 1
194346799e8SJonathan W Adams #define STACKS_STATE_DONE 2
195346799e8SJonathan W Adams static uint_t stacks_state = STACKS_STATE_CLEAN;
196346799e8SJonathan W Adams static stacks_entry_t **stacks_hash;
197346799e8SJonathan W Adams static stacks_entry_t **stacks_array;
198346799e8SJonathan W Adams static size_t stacks_array_size;
199346799e8SJonathan W Adams
200346799e8SJonathan W Adams size_t
stacks_hash_entry(stacks_entry_t * sep)201346799e8SJonathan W Adams stacks_hash_entry(stacks_entry_t *sep)
202346799e8SJonathan W Adams {
203346799e8SJonathan W Adams size_t depth = sep->se_depth;
204346799e8SJonathan W Adams uintptr_t *stack = sep->se_stack;
205346799e8SJonathan W Adams
206346799e8SJonathan W Adams uint64_t total = depth;
207346799e8SJonathan W Adams
208346799e8SJonathan W Adams while (depth > 0) {
209346799e8SJonathan W Adams total += *stack;
210346799e8SJonathan W Adams stack++; depth--;
211346799e8SJonathan W Adams }
212346799e8SJonathan W Adams
213346799e8SJonathan W Adams return (total % STACKS_HSIZE);
214346799e8SJonathan W Adams }
215346799e8SJonathan W Adams
216346799e8SJonathan W Adams /*
217346799e8SJonathan W Adams * This is used to both compare stacks for equality and to sort the final
218346799e8SJonathan W Adams * list of unique stacks. forsort specifies the latter behavior, which
219346799e8SJonathan W Adams * additionally:
220346799e8SJonathan W Adams * compares se_count, and
221346799e8SJonathan W Adams * sorts the stacks by text function name.
222346799e8SJonathan W Adams *
223346799e8SJonathan W Adams * The equality test is independent of se_count, and doesn't care about
224346799e8SJonathan W Adams * relative ordering, so we don't do the extra work of looking up symbols
225346799e8SJonathan W Adams * for the stack addresses.
226346799e8SJonathan W Adams */
227346799e8SJonathan W Adams int
stacks_entry_comp_impl(stacks_entry_t * l,stacks_entry_t * r,uint_t forsort)228346799e8SJonathan W Adams stacks_entry_comp_impl(stacks_entry_t *l, stacks_entry_t *r,
229346799e8SJonathan W Adams uint_t forsort)
230346799e8SJonathan W Adams {
231346799e8SJonathan W Adams int idx;
232346799e8SJonathan W Adams
233346799e8SJonathan W Adams int depth = MIN(l->se_depth, r->se_depth);
234346799e8SJonathan W Adams
235346799e8SJonathan W Adams /* no matter what, panic stacks come last. */
236346799e8SJonathan W Adams if (l->se_panic > r->se_panic)
237346799e8SJonathan W Adams return (1);
238346799e8SJonathan W Adams if (l->se_panic < r->se_panic)
239346799e8SJonathan W Adams return (-1);
240346799e8SJonathan W Adams
241346799e8SJonathan W Adams if (forsort) {
242346799e8SJonathan W Adams /* put large counts earlier */
243346799e8SJonathan W Adams if (l->se_count > r->se_count)
244346799e8SJonathan W Adams return (-1);
245346799e8SJonathan W Adams if (l->se_count < r->se_count)
246346799e8SJonathan W Adams return (1);
247346799e8SJonathan W Adams }
248346799e8SJonathan W Adams
249346799e8SJonathan W Adams if (l->se_tstate > r->se_tstate)
250346799e8SJonathan W Adams return (1);
251346799e8SJonathan W Adams if (l->se_tstate < r->se_tstate)
252346799e8SJonathan W Adams return (-1);
253346799e8SJonathan W Adams
254346799e8SJonathan W Adams if (l->se_failed > r->se_failed)
255346799e8SJonathan W Adams return (1);
256346799e8SJonathan W Adams if (l->se_failed < r->se_failed)
257346799e8SJonathan W Adams return (-1);
258346799e8SJonathan W Adams
259346799e8SJonathan W Adams for (idx = 0; idx < depth; idx++) {
260346799e8SJonathan W Adams char lbuf[MDB_SYM_NAMLEN];
261346799e8SJonathan W Adams char rbuf[MDB_SYM_NAMLEN];
262346799e8SJonathan W Adams
263346799e8SJonathan W Adams int rval;
264346799e8SJonathan W Adams uintptr_t laddr = l->se_stack[idx];
265346799e8SJonathan W Adams uintptr_t raddr = r->se_stack[idx];
266346799e8SJonathan W Adams
267346799e8SJonathan W Adams if (laddr == raddr)
268346799e8SJonathan W Adams continue;
269346799e8SJonathan W Adams
270346799e8SJonathan W Adams if (forsort &&
271346799e8SJonathan W Adams mdb_lookup_by_addr(laddr, MDB_SYM_FUZZY,
272346799e8SJonathan W Adams lbuf, sizeof (lbuf), NULL) != -1 &&
273346799e8SJonathan W Adams mdb_lookup_by_addr(raddr, MDB_SYM_FUZZY,
274346799e8SJonathan W Adams rbuf, sizeof (rbuf), NULL) != -1 &&
275346799e8SJonathan W Adams (rval = strcmp(lbuf, rbuf)) != 0)
276346799e8SJonathan W Adams return (rval);
277346799e8SJonathan W Adams
278346799e8SJonathan W Adams if (laddr > raddr)
279346799e8SJonathan W Adams return (1);
280346799e8SJonathan W Adams return (-1);
281346799e8SJonathan W Adams }
282346799e8SJonathan W Adams
283346799e8SJonathan W Adams if (l->se_overflow > r->se_overflow)
284346799e8SJonathan W Adams return (-1);
285346799e8SJonathan W Adams if (l->se_overflow < r->se_overflow)
286346799e8SJonathan W Adams return (1);
287346799e8SJonathan W Adams
288346799e8SJonathan W Adams if (l->se_depth > r->se_depth)
289346799e8SJonathan W Adams return (1);
290346799e8SJonathan W Adams if (l->se_depth < r->se_depth)
291346799e8SJonathan W Adams return (-1);
292346799e8SJonathan W Adams
293346799e8SJonathan W Adams if (l->se_sobj_ops > r->se_sobj_ops)
294346799e8SJonathan W Adams return (1);
295346799e8SJonathan W Adams if (l->se_sobj_ops < r->se_sobj_ops)
296346799e8SJonathan W Adams return (-1);
297346799e8SJonathan W Adams
298346799e8SJonathan W Adams return (0);
299346799e8SJonathan W Adams }
300346799e8SJonathan W Adams
301346799e8SJonathan W Adams int
stacks_entry_comp(const void * l_arg,const void * r_arg)302346799e8SJonathan W Adams stacks_entry_comp(const void *l_arg, const void *r_arg)
303346799e8SJonathan W Adams {
304346799e8SJonathan W Adams stacks_entry_t * const *lp = l_arg;
305346799e8SJonathan W Adams stacks_entry_t * const *rp = r_arg;
306346799e8SJonathan W Adams
307346799e8SJonathan W Adams return (stacks_entry_comp_impl(*lp, *rp, 1));
308346799e8SJonathan W Adams }
309346799e8SJonathan W Adams
310346799e8SJonathan W Adams void
stacks_cleanup(int force)311346799e8SJonathan W Adams stacks_cleanup(int force)
312346799e8SJonathan W Adams {
313346799e8SJonathan W Adams int idx = 0;
314346799e8SJonathan W Adams stacks_entry_t *cur, *next;
315346799e8SJonathan W Adams
316346799e8SJonathan W Adams if (stacks_state == STACKS_STATE_CLEAN)
317346799e8SJonathan W Adams return;
318346799e8SJonathan W Adams
319346799e8SJonathan W Adams if (!force && stacks_state == STACKS_STATE_DONE)
320346799e8SJonathan W Adams return;
321346799e8SJonathan W Adams
322346799e8SJonathan W Adams /*
323346799e8SJonathan W Adams * Until the array is sorted and stable, stacks_hash will be non-NULL.
324346799e8SJonathan W Adams * This way, we can get at all of the data, even if qsort() was
325346799e8SJonathan W Adams * interrupted while mucking with the array.
326346799e8SJonathan W Adams */
327346799e8SJonathan W Adams if (stacks_hash != NULL) {
328346799e8SJonathan W Adams for (idx = 0; idx < STACKS_HSIZE; idx++) {
329346799e8SJonathan W Adams while ((cur = stacks_hash[idx]) != NULL) {
330346799e8SJonathan W Adams while ((next = cur->se_dup) != NULL) {
331346799e8SJonathan W Adams cur->se_dup = next->se_dup;
332346799e8SJonathan W Adams mdb_free(next,
333346799e8SJonathan W Adams STACKS_ENTRY_SIZE(next->se_depth));
334346799e8SJonathan W Adams }
335346799e8SJonathan W Adams next = cur->se_next;
336346799e8SJonathan W Adams stacks_hash[idx] = next;
337346799e8SJonathan W Adams mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth));
338346799e8SJonathan W Adams }
339346799e8SJonathan W Adams }
340346799e8SJonathan W Adams if (stacks_array != NULL)
341346799e8SJonathan W Adams mdb_free(stacks_array,
342346799e8SJonathan W Adams stacks_array_size * sizeof (*stacks_array));
343346799e8SJonathan W Adams
344*98144673SJosef 'Jeff' Sipek mdb_free(stacks_hash, STACKS_HSIZE * sizeof (*stacks_hash));
345*98144673SJosef 'Jeff' Sipek
346346799e8SJonathan W Adams } else if (stacks_array != NULL) {
347346799e8SJonathan W Adams for (idx = 0; idx < stacks_array_size; idx++) {
348346799e8SJonathan W Adams if ((cur = stacks_array[idx]) != NULL) {
349346799e8SJonathan W Adams while ((next = cur->se_dup) != NULL) {
350346799e8SJonathan W Adams cur->se_dup = next->se_dup;
351346799e8SJonathan W Adams mdb_free(next,
352346799e8SJonathan W Adams STACKS_ENTRY_SIZE(next->se_depth));
353346799e8SJonathan W Adams }
354346799e8SJonathan W Adams stacks_array[idx] = NULL;
355346799e8SJonathan W Adams mdb_free(cur, STACKS_ENTRY_SIZE(cur->se_depth));
356346799e8SJonathan W Adams }
357346799e8SJonathan W Adams }
358346799e8SJonathan W Adams mdb_free(stacks_array,
359346799e8SJonathan W Adams stacks_array_size * sizeof (*stacks_array));
360346799e8SJonathan W Adams }
361346799e8SJonathan W Adams
362c9a6ea2eSBryan Cantrill stacks_findstack_cleanup();
363c9a6ea2eSBryan Cantrill
364346799e8SJonathan W Adams stacks_array_size = 0;
365346799e8SJonathan W Adams stacks_state = STACKS_STATE_CLEAN;
366*98144673SJosef 'Jeff' Sipek stacks_hash = NULL;
367*98144673SJosef 'Jeff' Sipek stacks_array = NULL;
368346799e8SJonathan W Adams }
369346799e8SJonathan W Adams
370346799e8SJonathan W Adams /*ARGSUSED*/
371346799e8SJonathan W Adams int
stacks_thread_cb(uintptr_t addr,const void * ignored,void * cbarg)372346799e8SJonathan W Adams stacks_thread_cb(uintptr_t addr, const void *ignored, void *cbarg)
373346799e8SJonathan W Adams {
374346799e8SJonathan W Adams stacks_info_t *sip = cbarg;
375346799e8SJonathan W Adams findstack_info_t *fsip = &sip->si_fsi;
376346799e8SJonathan W Adams
377346799e8SJonathan W Adams stacks_entry_t **sepp, *nsep, *sep;
378346799e8SJonathan W Adams int idx;
379346799e8SJonathan W Adams size_t depth;
380346799e8SJonathan W Adams
381c9a6ea2eSBryan Cantrill if (stacks_findstack(addr, fsip, 0) != DCMD_OK &&
382346799e8SJonathan W Adams fsip->fsi_failed == FSI_FAIL_BADTHREAD) {
383346799e8SJonathan W Adams mdb_warn("couldn't read thread at %p\n", addr);
384346799e8SJonathan W Adams return (WALK_NEXT);
385346799e8SJonathan W Adams }
386346799e8SJonathan W Adams
387346799e8SJonathan W Adams sip->si_count++;
388346799e8SJonathan W Adams
389346799e8SJonathan W Adams depth = fsip->fsi_depth;
390346799e8SJonathan W Adams nsep = mdb_zalloc(STACKS_ENTRY_SIZE(depth), UM_SLEEP);
391346799e8SJonathan W Adams nsep->se_thread = addr;
392346799e8SJonathan W Adams nsep->se_sp = fsip->fsi_sp;
393346799e8SJonathan W Adams nsep->se_sobj_ops = fsip->fsi_sobj_ops;
394346799e8SJonathan W Adams nsep->se_tstate = fsip->fsi_tstate;
395346799e8SJonathan W Adams nsep->se_count = 1;
396346799e8SJonathan W Adams nsep->se_overflow = fsip->fsi_overflow;
397346799e8SJonathan W Adams nsep->se_depth = depth;
398346799e8SJonathan W Adams nsep->se_failed = fsip->fsi_failed;
399346799e8SJonathan W Adams nsep->se_panic = fsip->fsi_panic;
400346799e8SJonathan W Adams
401346799e8SJonathan W Adams for (idx = 0; idx < depth; idx++)
402346799e8SJonathan W Adams nsep->se_stack[idx] = fsip->fsi_stack[idx];
403346799e8SJonathan W Adams
404346799e8SJonathan W Adams for (sepp = &sip->si_hash[stacks_hash_entry(nsep)];
405346799e8SJonathan W Adams (sep = *sepp) != NULL;
406346799e8SJonathan W Adams sepp = &sep->se_next) {
407346799e8SJonathan W Adams
408346799e8SJonathan W Adams if (stacks_entry_comp_impl(sep, nsep, 0) != 0)
409346799e8SJonathan W Adams continue;
410346799e8SJonathan W Adams
411346799e8SJonathan W Adams nsep->se_dup = sep->se_dup;
412346799e8SJonathan W Adams sep->se_dup = nsep;
413346799e8SJonathan W Adams sep->se_count++;
414346799e8SJonathan W Adams return (WALK_NEXT);
415346799e8SJonathan W Adams }
416346799e8SJonathan W Adams
417346799e8SJonathan W Adams nsep->se_next = NULL;
418346799e8SJonathan W Adams *sepp = nsep;
419346799e8SJonathan W Adams sip->si_entries++;
420346799e8SJonathan W Adams
421346799e8SJonathan W Adams return (WALK_NEXT);
422346799e8SJonathan W Adams }
423346799e8SJonathan W Adams
424346799e8SJonathan W Adams int
stacks_run_tlist(mdb_pipe_t * tlist,stacks_info_t * si)425e0ad97e3SJonathan Adams stacks_run_tlist(mdb_pipe_t *tlist, stacks_info_t *si)
426e0ad97e3SJonathan Adams {
427e0ad97e3SJonathan Adams size_t idx;
428e0ad97e3SJonathan Adams size_t found = 0;
429e0ad97e3SJonathan Adams int ret;
430e0ad97e3SJonathan Adams
431e0ad97e3SJonathan Adams for (idx = 0; idx < tlist->pipe_len; idx++) {
432e0ad97e3SJonathan Adams uintptr_t addr = tlist->pipe_data[idx];
433e0ad97e3SJonathan Adams
434e0ad97e3SJonathan Adams found++;
435e0ad97e3SJonathan Adams
436c9a6ea2eSBryan Cantrill ret = stacks_thread_cb(addr, NULL, si);
437e0ad97e3SJonathan Adams if (ret == WALK_DONE)
438e0ad97e3SJonathan Adams break;
439e0ad97e3SJonathan Adams if (ret != WALK_NEXT)
440e0ad97e3SJonathan Adams return (-1);
441e0ad97e3SJonathan Adams }
442e0ad97e3SJonathan Adams
443e0ad97e3SJonathan Adams if (found)
444e0ad97e3SJonathan Adams return (0);
445e0ad97e3SJonathan Adams return (-1);
446e0ad97e3SJonathan Adams }
447e0ad97e3SJonathan Adams
448e0ad97e3SJonathan Adams int
stacks_run(int verbose,mdb_pipe_t * tlist)449e0ad97e3SJonathan Adams stacks_run(int verbose, mdb_pipe_t *tlist)
450346799e8SJonathan W Adams {
451346799e8SJonathan W Adams stacks_info_t si;
452346799e8SJonathan W Adams findstack_info_t *fsip = &si.si_fsi;
453346799e8SJonathan W Adams size_t idx;
454346799e8SJonathan W Adams stacks_entry_t **cur;
455346799e8SJonathan W Adams
456346799e8SJonathan W Adams bzero(&si, sizeof (si));
457346799e8SJonathan W Adams
458346799e8SJonathan W Adams stacks_state = STACKS_STATE_DIRTY;
459346799e8SJonathan W Adams
460346799e8SJonathan W Adams stacks_hash = si.si_hash =
461346799e8SJonathan W Adams mdb_zalloc(STACKS_HSIZE * sizeof (*si.si_hash), UM_SLEEP);
462346799e8SJonathan W Adams si.si_entries = 0;
463346799e8SJonathan W Adams si.si_count = 0;
464346799e8SJonathan W Adams
465346799e8SJonathan W Adams fsip->fsi_max_depth = STACKS_MAX_DEPTH;
466346799e8SJonathan W Adams fsip->fsi_stack =
467346799e8SJonathan W Adams mdb_alloc(fsip->fsi_max_depth * sizeof (*fsip->fsi_stack),
468346799e8SJonathan W Adams UM_SLEEP | UM_GC);
469346799e8SJonathan W Adams
470346799e8SJonathan W Adams if (verbose)
471346799e8SJonathan W Adams mdb_warn("stacks: processing kernel threads\n");
472346799e8SJonathan W Adams
473e0ad97e3SJonathan Adams if (tlist != NULL) {
474e0ad97e3SJonathan Adams if (stacks_run_tlist(tlist, &si))
475e0ad97e3SJonathan Adams return (DCMD_ERR);
476e0ad97e3SJonathan Adams } else {
477346799e8SJonathan W Adams if (mdb_walk("thread", stacks_thread_cb, &si) != 0) {
478346799e8SJonathan W Adams mdb_warn("cannot walk \"thread\"");
479346799e8SJonathan W Adams return (DCMD_ERR);
480346799e8SJonathan W Adams }
481e0ad97e3SJonathan Adams }
482346799e8SJonathan W Adams
483346799e8SJonathan W Adams if (verbose)
484346799e8SJonathan W Adams mdb_warn("stacks: %d unique stacks / %d threads\n",
485346799e8SJonathan W Adams si.si_entries, si.si_count);
486346799e8SJonathan W Adams
487346799e8SJonathan W Adams stacks_array_size = si.si_entries;
488346799e8SJonathan W Adams stacks_array =
489346799e8SJonathan W Adams mdb_zalloc(si.si_entries * sizeof (*stacks_array), UM_SLEEP);
490346799e8SJonathan W Adams cur = stacks_array;
491346799e8SJonathan W Adams for (idx = 0; idx < STACKS_HSIZE; idx++) {
492346799e8SJonathan W Adams stacks_entry_t *sep;
493346799e8SJonathan W Adams for (sep = si.si_hash[idx]; sep != NULL; sep = sep->se_next)
494346799e8SJonathan W Adams *(cur++) = sep;
495346799e8SJonathan W Adams }
496346799e8SJonathan W Adams
497346799e8SJonathan W Adams if (cur != stacks_array + si.si_entries) {
498346799e8SJonathan W Adams mdb_warn("stacks: miscounted array size (%d != size: %d)\n",
499346799e8SJonathan W Adams (cur - stacks_array), stacks_array_size);
500346799e8SJonathan W Adams return (DCMD_ERR);
501346799e8SJonathan W Adams }
502346799e8SJonathan W Adams qsort(stacks_array, si.si_entries, sizeof (*stacks_array),
503346799e8SJonathan W Adams stacks_entry_comp);
504346799e8SJonathan W Adams
505346799e8SJonathan W Adams /* Now that we're done, free the hash table */
506346799e8SJonathan W Adams stacks_hash = NULL;
507346799e8SJonathan W Adams mdb_free(si.si_hash, STACKS_HSIZE * sizeof (*si.si_hash));
508346799e8SJonathan W Adams
509e0ad97e3SJonathan Adams if (tlist == NULL)
510346799e8SJonathan W Adams stacks_state = STACKS_STATE_DONE;
511346799e8SJonathan W Adams
512346799e8SJonathan W Adams if (verbose)
513346799e8SJonathan W Adams mdb_warn("stacks: done\n");
514346799e8SJonathan W Adams
515346799e8SJonathan W Adams return (DCMD_OK);
516346799e8SJonathan W Adams }
517346799e8SJonathan W Adams
518346799e8SJonathan W Adams static int
stacks_has_caller(stacks_entry_t * sep,uintptr_t addr)519346799e8SJonathan W Adams stacks_has_caller(stacks_entry_t *sep, uintptr_t addr)
520346799e8SJonathan W Adams {
521346799e8SJonathan W Adams uintptr_t laddr = addr;
522346799e8SJonathan W Adams uintptr_t haddr = addr + 1;
523346799e8SJonathan W Adams int idx;
524346799e8SJonathan W Adams char c[MDB_SYM_NAMLEN];
525346799e8SJonathan W Adams GElf_Sym sym;
526346799e8SJonathan W Adams
527346799e8SJonathan W Adams if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY,
528346799e8SJonathan W Adams c, sizeof (c), &sym) != -1 &&
529346799e8SJonathan W Adams addr == (uintptr_t)sym.st_value) {
530346799e8SJonathan W Adams laddr = (uintptr_t)sym.st_value;
531346799e8SJonathan W Adams haddr = (uintptr_t)sym.st_value + sym.st_size;
532346799e8SJonathan W Adams }
533346799e8SJonathan W Adams
534346799e8SJonathan W Adams for (idx = 0; idx < sep->se_depth; idx++)
535346799e8SJonathan W Adams if (sep->se_stack[idx] >= laddr && sep->se_stack[idx] < haddr)
536346799e8SJonathan W Adams return (1);
537346799e8SJonathan W Adams
538346799e8SJonathan W Adams return (0);
539346799e8SJonathan W Adams }
540346799e8SJonathan W Adams
541e6fc74c6SGreg Price static int
stacks_has_module(stacks_entry_t * sep,stacks_module_t * mp)542c9a6ea2eSBryan Cantrill stacks_has_module(stacks_entry_t *sep, stacks_module_t *mp)
543e6fc74c6SGreg Price {
544e6fc74c6SGreg Price int idx;
545e6fc74c6SGreg Price
546c9a6ea2eSBryan Cantrill for (idx = 0; idx < sep->se_depth; idx++) {
547c9a6ea2eSBryan Cantrill if (sep->se_stack[idx] >= mp->sm_text &&
548c9a6ea2eSBryan Cantrill sep->se_stack[idx] < mp->sm_text + mp->sm_size)
549e6fc74c6SGreg Price return (1);
550c9a6ea2eSBryan Cantrill }
551c9a6ea2eSBryan Cantrill
552e6fc74c6SGreg Price return (0);
553e6fc74c6SGreg Price }
554e6fc74c6SGreg Price
555c9a6ea2eSBryan Cantrill static int
stacks_module_find(const char * name,stacks_module_t * mp)556c9a6ea2eSBryan Cantrill stacks_module_find(const char *name, stacks_module_t *mp)
557c9a6ea2eSBryan Cantrill {
558c9a6ea2eSBryan Cantrill (void) strncpy(mp->sm_name, name, sizeof (mp->sm_name));
559c9a6ea2eSBryan Cantrill
560c9a6ea2eSBryan Cantrill if (stacks_module(mp) != 0)
561c9a6ea2eSBryan Cantrill return (-1);
562c9a6ea2eSBryan Cantrill
563c9a6ea2eSBryan Cantrill if (mp->sm_size == 0) {
564c9a6ea2eSBryan Cantrill mdb_warn("stacks: module \"%s\" is unknown\n", name);
565c9a6ea2eSBryan Cantrill return (-1);
566c9a6ea2eSBryan Cantrill }
567c9a6ea2eSBryan Cantrill
568c9a6ea2eSBryan Cantrill return (0);
569c9a6ea2eSBryan Cantrill }
570e6fc74c6SGreg Price
571346799e8SJonathan W Adams static int
uintptrcomp(const void * lp,const void * rp)572346799e8SJonathan W Adams uintptrcomp(const void *lp, const void *rp)
573346799e8SJonathan W Adams {
574346799e8SJonathan W Adams uintptr_t lhs = *(const uintptr_t *)lp;
575346799e8SJonathan W Adams uintptr_t rhs = *(const uintptr_t *)rp;
576346799e8SJonathan W Adams if (lhs > rhs)
577346799e8SJonathan W Adams return (1);
578346799e8SJonathan W Adams if (lhs < rhs)
579346799e8SJonathan W Adams return (-1);
580346799e8SJonathan W Adams return (0);
581346799e8SJonathan W Adams }
582346799e8SJonathan W Adams
583346799e8SJonathan W Adams /*ARGSUSED*/
584346799e8SJonathan W Adams int
stacks(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)585346799e8SJonathan W Adams stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
586346799e8SJonathan W Adams {
587346799e8SJonathan W Adams size_t idx;
588346799e8SJonathan W Adams
589346799e8SJonathan W Adams char *seen = NULL;
590346799e8SJonathan W Adams
591346799e8SJonathan W Adams const char *caller_str = NULL;
592346799e8SJonathan W Adams const char *excl_caller_str = NULL;
593346799e8SJonathan W Adams uintptr_t caller = 0, excl_caller = 0;
594e6fc74c6SGreg Price const char *module_str = NULL;
595e6fc74c6SGreg Price const char *excl_module_str = NULL;
596c9a6ea2eSBryan Cantrill stacks_module_t module, excl_module;
597346799e8SJonathan W Adams const char *sobj = NULL;
598346799e8SJonathan W Adams const char *excl_sobj = NULL;
599346799e8SJonathan W Adams uintptr_t sobj_ops = 0, excl_sobj_ops = 0;
600346799e8SJonathan W Adams const char *tstate_str = NULL;
601346799e8SJonathan W Adams const char *excl_tstate_str = NULL;
602346799e8SJonathan W Adams uint_t tstate = -1U;
603346799e8SJonathan W Adams uint_t excl_tstate = -1U;
604e0ad97e3SJonathan Adams uint_t printed = 0;
605346799e8SJonathan W Adams
606346799e8SJonathan W Adams uint_t all = 0;
607346799e8SJonathan W Adams uint_t force = 0;
608346799e8SJonathan W Adams uint_t interesting = 0;
609346799e8SJonathan W Adams uint_t verbose = 0;
610346799e8SJonathan W Adams
611346799e8SJonathan W Adams /*
612346799e8SJonathan W Adams * We have a slight behavior difference between having piped
613346799e8SJonathan W Adams * input and 'addr::stacks'. Without a pipe, we assume the
614346799e8SJonathan W Adams * thread pointer given is a representative thread, and so
615346799e8SJonathan W Adams * we include all similar threads in the system in our output.
616346799e8SJonathan W Adams *
617346799e8SJonathan W Adams * With a pipe, we filter down to just the threads in our
618346799e8SJonathan W Adams * input.
619346799e8SJonathan W Adams */
620346799e8SJonathan W Adams uint_t addrspec = (flags & DCMD_ADDRSPEC);
621346799e8SJonathan W Adams uint_t only_matching = addrspec && (flags & DCMD_PIPE);
622346799e8SJonathan W Adams
623346799e8SJonathan W Adams mdb_pipe_t p;
624346799e8SJonathan W Adams
625c9a6ea2eSBryan Cantrill bzero(&module, sizeof (module));
626c9a6ea2eSBryan Cantrill bzero(&excl_module, sizeof (excl_module));
627c9a6ea2eSBryan Cantrill
628346799e8SJonathan W Adams if (mdb_getopts(argc, argv,
629346799e8SJonathan W Adams 'a', MDB_OPT_SETBITS, TRUE, &all,
630346799e8SJonathan W Adams 'f', MDB_OPT_SETBITS, TRUE, &force,
631346799e8SJonathan W Adams 'i', MDB_OPT_SETBITS, TRUE, &interesting,
632346799e8SJonathan W Adams 'v', MDB_OPT_SETBITS, TRUE, &verbose,
633346799e8SJonathan W Adams 'c', MDB_OPT_STR, &caller_str,
634346799e8SJonathan W Adams 'C', MDB_OPT_STR, &excl_caller_str,
635e6fc74c6SGreg Price 'm', MDB_OPT_STR, &module_str,
636e6fc74c6SGreg Price 'M', MDB_OPT_STR, &excl_module_str,
637346799e8SJonathan W Adams 's', MDB_OPT_STR, &sobj,
638346799e8SJonathan W Adams 'S', MDB_OPT_STR, &excl_sobj,
639346799e8SJonathan W Adams 't', MDB_OPT_STR, &tstate_str,
640346799e8SJonathan W Adams 'T', MDB_OPT_STR, &excl_tstate_str,
641346799e8SJonathan W Adams NULL) != argc)
642346799e8SJonathan W Adams return (DCMD_USAGE);
643346799e8SJonathan W Adams
644346799e8SJonathan W Adams if (interesting) {
645346799e8SJonathan W Adams if (sobj != NULL || excl_sobj != NULL ||
646346799e8SJonathan W Adams tstate_str != NULL || excl_tstate_str != NULL) {
647346799e8SJonathan W Adams mdb_warn(
648346799e8SJonathan W Adams "stacks: -i is incompatible with -[sStT]\n");
649346799e8SJonathan W Adams return (DCMD_USAGE);
650346799e8SJonathan W Adams }
651346799e8SJonathan W Adams excl_sobj = "CV";
652346799e8SJonathan W Adams excl_tstate_str = "FREE";
653346799e8SJonathan W Adams }
654346799e8SJonathan W Adams
655346799e8SJonathan W Adams if (caller_str != NULL) {
656346799e8SJonathan W Adams mdb_set_dot(0);
657346799e8SJonathan W Adams if (mdb_eval(caller_str) != 0) {
658346799e8SJonathan W Adams mdb_warn("stacks: evaluation of \"%s\" failed",
659346799e8SJonathan W Adams caller_str);
6607c478bd9Sstevel@tonic-gate return (DCMD_ABORT);
6617c478bd9Sstevel@tonic-gate }
662346799e8SJonathan W Adams caller = mdb_get_dot();
663346799e8SJonathan W Adams }
6647c478bd9Sstevel@tonic-gate
665346799e8SJonathan W Adams if (excl_caller_str != NULL) {
666346799e8SJonathan W Adams mdb_set_dot(0);
667346799e8SJonathan W Adams if (mdb_eval(excl_caller_str) != 0) {
668346799e8SJonathan W Adams mdb_warn("stacks: evaluation of \"%s\" failed",
669346799e8SJonathan W Adams excl_caller_str);
670346799e8SJonathan W Adams return (DCMD_ABORT);
671346799e8SJonathan W Adams }
672346799e8SJonathan W Adams excl_caller = mdb_get_dot();
673346799e8SJonathan W Adams }
674346799e8SJonathan W Adams mdb_set_dot(addr);
675346799e8SJonathan W Adams
676c9a6ea2eSBryan Cantrill if (module_str != NULL && stacks_module_find(module_str, &module) != 0)
677e6fc74c6SGreg Price return (DCMD_ABORT);
678e6fc74c6SGreg Price
679e6fc74c6SGreg Price if (excl_module_str != NULL &&
680c9a6ea2eSBryan Cantrill stacks_module_find(excl_module_str, &excl_module) != 0)
681e6fc74c6SGreg Price return (DCMD_ABORT);
682e6fc74c6SGreg Price
683c9a6ea2eSBryan Cantrill if (sobj != NULL && text_to_sobj(sobj, &sobj_ops) != 0)
684346799e8SJonathan W Adams return (DCMD_USAGE);
685346799e8SJonathan W Adams
686c9a6ea2eSBryan Cantrill if (excl_sobj != NULL && text_to_sobj(excl_sobj, &excl_sobj_ops) != 0)
687346799e8SJonathan W Adams return (DCMD_USAGE);
688346799e8SJonathan W Adams
689346799e8SJonathan W Adams if (sobj_ops != 0 && excl_sobj_ops != 0) {
690346799e8SJonathan W Adams mdb_warn("stacks: only one of -s and -S can be specified\n");
691346799e8SJonathan W Adams return (DCMD_USAGE);
692346799e8SJonathan W Adams }
693346799e8SJonathan W Adams
694c9a6ea2eSBryan Cantrill if (tstate_str != NULL && text_to_tstate(tstate_str, &tstate) != 0)
695346799e8SJonathan W Adams return (DCMD_USAGE);
696e6fc74c6SGreg Price
697e6fc74c6SGreg Price if (excl_tstate_str != NULL &&
698346799e8SJonathan W Adams text_to_tstate(excl_tstate_str, &excl_tstate) != 0)
699346799e8SJonathan W Adams return (DCMD_USAGE);
700346799e8SJonathan W Adams
701346799e8SJonathan W Adams if (tstate != -1U && excl_tstate != -1U) {
702346799e8SJonathan W Adams mdb_warn("stacks: only one of -t and -T can be specified\n");
703346799e8SJonathan W Adams return (DCMD_USAGE);
704346799e8SJonathan W Adams }
705346799e8SJonathan W Adams
706346799e8SJonathan W Adams /*
707346799e8SJonathan W Adams * If there's an address specified, we're going to further filter
708346799e8SJonathan W Adams * to only entries which have an address in the input. To reduce
709346799e8SJonathan W Adams * overhead (and make the sorted output come out right), we
710346799e8SJonathan W Adams * use mdb_get_pipe() to grab the entire pipeline of input, then
711346799e8SJonathan W Adams * use qsort() and bsearch() to speed up the search.
712346799e8SJonathan W Adams */
713346799e8SJonathan W Adams if (addrspec) {
714346799e8SJonathan W Adams mdb_get_pipe(&p);
715346799e8SJonathan W Adams if (p.pipe_data == NULL || p.pipe_len == 0) {
716346799e8SJonathan W Adams p.pipe_data = &addr;
717346799e8SJonathan W Adams p.pipe_len = 1;
718346799e8SJonathan W Adams }
719346799e8SJonathan W Adams qsort(p.pipe_data, p.pipe_len, sizeof (uintptr_t),
720346799e8SJonathan W Adams uintptrcomp);
721346799e8SJonathan W Adams
722346799e8SJonathan W Adams /* remove any duplicates in the data */
723346799e8SJonathan W Adams idx = 0;
724346799e8SJonathan W Adams while (idx < p.pipe_len - 1) {
725346799e8SJonathan W Adams uintptr_t *data = &p.pipe_data[idx];
726346799e8SJonathan W Adams size_t len = p.pipe_len - idx;
727346799e8SJonathan W Adams
728346799e8SJonathan W Adams if (data[0] == data[1]) {
729346799e8SJonathan W Adams memmove(data, data + 1,
730346799e8SJonathan W Adams (len - 1) * sizeof (*data));
731346799e8SJonathan W Adams p.pipe_len--;
732346799e8SJonathan W Adams continue; /* repeat without incrementing idx */
733346799e8SJonathan W Adams }
734346799e8SJonathan W Adams idx++;
735346799e8SJonathan W Adams }
736346799e8SJonathan W Adams
737346799e8SJonathan W Adams seen = mdb_zalloc(p.pipe_len, UM_SLEEP | UM_GC);
738346799e8SJonathan W Adams }
739346799e8SJonathan W Adams
740e0ad97e3SJonathan Adams /*
741e0ad97e3SJonathan Adams * Force a cleanup if we're connected to a live system. Never
742e0ad97e3SJonathan Adams * do a cleanup after the first invocation around the loop.
743e0ad97e3SJonathan Adams */
744e0ad97e3SJonathan Adams force |= (mdb_get_state() == MDB_STATE_RUNNING);
745e0ad97e3SJonathan Adams if (force && (flags & (DCMD_LOOPFIRST|DCMD_LOOP)) == DCMD_LOOP)
746e0ad97e3SJonathan Adams force = 0;
747e0ad97e3SJonathan Adams
748e0ad97e3SJonathan Adams stacks_cleanup(force);
749e0ad97e3SJonathan Adams
750e0ad97e3SJonathan Adams if (stacks_state == STACKS_STATE_CLEAN) {
751e0ad97e3SJonathan Adams int res = stacks_run(verbose, addrspec ? &p : NULL);
752e0ad97e3SJonathan Adams if (res != DCMD_OK)
753e0ad97e3SJonathan Adams return (res);
754e0ad97e3SJonathan Adams }
755e0ad97e3SJonathan Adams
756346799e8SJonathan W Adams for (idx = 0; idx < stacks_array_size; idx++) {
757346799e8SJonathan W Adams stacks_entry_t *sep = stacks_array[idx];
758346799e8SJonathan W Adams stacks_entry_t *cur = sep;
759346799e8SJonathan W Adams int frame;
760346799e8SJonathan W Adams size_t count = sep->se_count;
761346799e8SJonathan W Adams
762346799e8SJonathan W Adams if (addrspec) {
763346799e8SJonathan W Adams stacks_entry_t *head = NULL, *tail = NULL, *sp;
764346799e8SJonathan W Adams size_t foundcount = 0;
765346799e8SJonathan W Adams /*
766346799e8SJonathan W Adams * We use the now-unused hash chain field se_next to
767346799e8SJonathan W Adams * link together the dups which match our list.
768346799e8SJonathan W Adams */
769346799e8SJonathan W Adams for (sp = sep; sp != NULL; sp = sp->se_dup) {
770346799e8SJonathan W Adams uintptr_t *entry = bsearch(&sp->se_thread,
771346799e8SJonathan W Adams p.pipe_data, p.pipe_len, sizeof (uintptr_t),
772346799e8SJonathan W Adams uintptrcomp);
773346799e8SJonathan W Adams if (entry != NULL) {
774346799e8SJonathan W Adams foundcount++;
775346799e8SJonathan W Adams seen[entry - p.pipe_data]++;
776346799e8SJonathan W Adams if (head == NULL)
777346799e8SJonathan W Adams head = sp;
778346799e8SJonathan W Adams else
779346799e8SJonathan W Adams tail->se_next = sp;
780346799e8SJonathan W Adams tail = sp;
781346799e8SJonathan W Adams sp->se_next = NULL;
782346799e8SJonathan W Adams }
783346799e8SJonathan W Adams }
784346799e8SJonathan W Adams if (head == NULL)
785346799e8SJonathan W Adams continue; /* no match, skip entry */
786346799e8SJonathan W Adams
787346799e8SJonathan W Adams if (only_matching) {
788346799e8SJonathan W Adams cur = sep = head;
789346799e8SJonathan W Adams count = foundcount;
790346799e8SJonathan W Adams }
791346799e8SJonathan W Adams }
792346799e8SJonathan W Adams
793346799e8SJonathan W Adams if (caller != 0 && !stacks_has_caller(sep, caller))
794346799e8SJonathan W Adams continue;
795c9a6ea2eSBryan Cantrill
796346799e8SJonathan W Adams if (excl_caller != 0 && stacks_has_caller(sep, excl_caller))
797346799e8SJonathan W Adams continue;
798c9a6ea2eSBryan Cantrill
799c9a6ea2eSBryan Cantrill if (module.sm_size != 0 && !stacks_has_module(sep, &module))
800e6fc74c6SGreg Price continue;
801c9a6ea2eSBryan Cantrill
802c9a6ea2eSBryan Cantrill if (excl_module.sm_size != 0 &&
803c9a6ea2eSBryan Cantrill stacks_has_module(sep, &excl_module))
804e6fc74c6SGreg Price continue;
805346799e8SJonathan W Adams
806346799e8SJonathan W Adams if (tstate != -1U) {
807346799e8SJonathan W Adams if (tstate == TSTATE_PANIC) {
808346799e8SJonathan W Adams if (!sep->se_panic)
809346799e8SJonathan W Adams continue;
810346799e8SJonathan W Adams } else if (sep->se_panic || sep->se_tstate != tstate)
811346799e8SJonathan W Adams continue;
812346799e8SJonathan W Adams }
813346799e8SJonathan W Adams if (excl_tstate != -1U) {
814346799e8SJonathan W Adams if (excl_tstate == TSTATE_PANIC) {
815346799e8SJonathan W Adams if (sep->se_panic)
816346799e8SJonathan W Adams continue;
817346799e8SJonathan W Adams } else if (!sep->se_panic &&
818346799e8SJonathan W Adams sep->se_tstate == excl_tstate)
819346799e8SJonathan W Adams continue;
820346799e8SJonathan W Adams }
821346799e8SJonathan W Adams
822346799e8SJonathan W Adams if (sobj_ops == SOBJ_ALL) {
823346799e8SJonathan W Adams if (sep->se_sobj_ops == 0)
824346799e8SJonathan W Adams continue;
825346799e8SJonathan W Adams } else if (sobj_ops != 0) {
826346799e8SJonathan W Adams if (sobj_ops != sep->se_sobj_ops)
827346799e8SJonathan W Adams continue;
828346799e8SJonathan W Adams }
829346799e8SJonathan W Adams
830346799e8SJonathan W Adams if (!(interesting && sep->se_panic)) {
831346799e8SJonathan W Adams if (excl_sobj_ops == SOBJ_ALL) {
832346799e8SJonathan W Adams if (sep->se_sobj_ops != 0)
833346799e8SJonathan W Adams continue;
834346799e8SJonathan W Adams } else if (excl_sobj_ops != 0) {
835346799e8SJonathan W Adams if (excl_sobj_ops == sep->se_sobj_ops)
836346799e8SJonathan W Adams continue;
837346799e8SJonathan W Adams }
838346799e8SJonathan W Adams }
839346799e8SJonathan W Adams
840346799e8SJonathan W Adams if (flags & DCMD_PIPE_OUT) {
841346799e8SJonathan W Adams while (sep != NULL) {
842346799e8SJonathan W Adams mdb_printf("%lr\n", sep->se_thread);
843346799e8SJonathan W Adams sep = only_matching ?
844346799e8SJonathan W Adams sep->se_next : sep->se_dup;
845346799e8SJonathan W Adams }
846346799e8SJonathan W Adams continue;
847346799e8SJonathan W Adams }
848346799e8SJonathan W Adams
849e0ad97e3SJonathan Adams if (all || !printed) {
850346799e8SJonathan W Adams mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n",
851e0ad97e3SJonathan Adams "THREAD", "STATE", "SOBJ", "COUNT");
852e0ad97e3SJonathan Adams printed = 1;
853346799e8SJonathan W Adams }
854346799e8SJonathan W Adams
855346799e8SJonathan W Adams do {
856346799e8SJonathan W Adams char state[20];
857346799e8SJonathan W Adams char sobj[100];
858346799e8SJonathan W Adams
859346799e8SJonathan W Adams tstate_to_text(cur->se_tstate, cur->se_panic,
860346799e8SJonathan W Adams state, sizeof (state));
861346799e8SJonathan W Adams sobj_to_text(cur->se_sobj_ops,
862346799e8SJonathan W Adams sobj, sizeof (sobj));
863346799e8SJonathan W Adams
864346799e8SJonathan W Adams if (cur == sep)
865c9a6ea2eSBryan Cantrill mdb_printf("%-?p %-8s %-?s %8d\n",
866346799e8SJonathan W Adams cur->se_thread, state, sobj, count);
867346799e8SJonathan W Adams else
868c9a6ea2eSBryan Cantrill mdb_printf("%-?p %-8s %-?s %8s\n",
869346799e8SJonathan W Adams cur->se_thread, state, sobj, "-");
870346799e8SJonathan W Adams
871346799e8SJonathan W Adams cur = only_matching ? cur->se_next : cur->se_dup;
872346799e8SJonathan W Adams } while (all && cur != NULL);
873346799e8SJonathan W Adams
874346799e8SJonathan W Adams if (sep->se_failed != 0) {
875346799e8SJonathan W Adams char *reason;
876346799e8SJonathan W Adams switch (sep->se_failed) {
877346799e8SJonathan W Adams case FSI_FAIL_NOTINMEMORY:
878346799e8SJonathan W Adams reason = "thread not in memory";
879346799e8SJonathan W Adams break;
880346799e8SJonathan W Adams case FSI_FAIL_THREADCORRUPT:
881346799e8SJonathan W Adams reason = "thread structure stack info corrupt";
882346799e8SJonathan W Adams break;
883346799e8SJonathan W Adams case FSI_FAIL_STACKNOTFOUND:
884346799e8SJonathan W Adams reason = "no consistent stack found";
885346799e8SJonathan W Adams break;
886346799e8SJonathan W Adams default:
887346799e8SJonathan W Adams reason = "unknown failure";
888346799e8SJonathan W Adams break;
889346799e8SJonathan W Adams }
890346799e8SJonathan W Adams mdb_printf("%?s <%s>\n", "", reason);
891346799e8SJonathan W Adams }
892346799e8SJonathan W Adams
893346799e8SJonathan W Adams for (frame = 0; frame < sep->se_depth; frame++)
894346799e8SJonathan W Adams mdb_printf("%?s %a\n", "", sep->se_stack[frame]);
895346799e8SJonathan W Adams if (sep->se_overflow)
896346799e8SJonathan W Adams mdb_printf("%?s ... truncated ...\n", "");
897346799e8SJonathan W Adams mdb_printf("\n");
898346799e8SJonathan W Adams }
899346799e8SJonathan W Adams
900346799e8SJonathan W Adams if (flags & DCMD_ADDRSPEC) {
901346799e8SJonathan W Adams for (idx = 0; idx < p.pipe_len; idx++)
902346799e8SJonathan W Adams if (seen[idx] == 0)
903346799e8SJonathan W Adams mdb_warn("stacks: %p not in thread list\n",
904346799e8SJonathan W Adams p.pipe_data[idx]);
905346799e8SJonathan W Adams }
9067c478bd9Sstevel@tonic-gate return (DCMD_OK);
9077c478bd9Sstevel@tonic-gate }
908