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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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