findstack.c revision c9a6ea2e938727c95af7108c5e00eee4c890c7ae
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <mdb/mdb_modapi.h>
#include <assert.h>
#include "findstack.h"
#include "thread.h"
#include "sobj.h"
int findstack_debug_on = 0;
/*
* "sp" is a kernel VA.
*/
static int
{
return (DCMD_USAGE);
mdb_printf("stack pointer for thread %p%s: %p\n",
if (pc != 0)
mdb_inc_indent(2);
if (argc == 1)
else if (showargs)
else
mdb_dec_indent(2);
}
int
{
int retval;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
return (retval);
}
/*ARGSUSED*/
int
{
findstack_debug_on ^= 1;
mdb_printf("findstack: debugging is now %s\n",
return (DCMD_OK);
}
static void
uppercase(char *p)
{
for (; *p != '\0'; p++) {
if (*p >= 'a' && *p <= 'z')
*p += 'A' - 'a';
}
}
static void
{
}
#define SOBJ_ALL 1
static int
{
return (0);
}
}
#define TSTATE_PANIC -2U
static int
{
*out = TSTATE_PANIC;
return (-1);
}
return (0);
}
static void
{
if (paniced)
else
}
typedef struct stacks_entry {
struct stacks_entry *se_next;
#define STACKS_HSIZE 127
/* Maximum stack depth reported in stacks */
#define STACKS_MAX_DEPTH 254
typedef struct stacks_info {
/* global state cached between invocations */
#define STACKS_STATE_CLEAN 0
#define STACKS_STATE_DIRTY 1
#define STACKS_STATE_DONE 2
static stacks_entry_t **stacks_hash;
static stacks_entry_t **stacks_array;
static size_t stacks_array_size;
{
while (depth > 0) {
}
return (total % STACKS_HSIZE);
}
/*
* This is used to both compare stacks for equality and to sort the final
* list of unique stacks. forsort specifies the latter behavior, which
* additionally:
* compares se_count, and
* sorts the stacks by text function name.
*
* The equality test is independent of se_count, and doesn't care about
* relative ordering, so we don't do the extra work of looking up symbols
* for the stack addresses.
*/
int
{
int idx;
/* no matter what, panic stacks come last. */
return (1);
return (-1);
if (forsort) {
/* put large counts earlier */
return (-1);
return (1);
}
return (1);
return (-1);
return (1);
return (-1);
char lbuf[MDB_SYM_NAMLEN];
char rbuf[MDB_SYM_NAMLEN];
int rval;
continue;
if (forsort &&
return (rval);
return (1);
return (-1);
}
if (l->se_overflow > r->se_overflow)
return (-1);
if (l->se_overflow < r->se_overflow)
return (1);
return (1);
return (-1);
if (l->se_sobj_ops > r->se_sobj_ops)
return (1);
if (l->se_sobj_ops < r->se_sobj_ops)
return (-1);
return (0);
}
int
{
}
void
stacks_cleanup(int force)
{
int idx = 0;
if (stacks_state == STACKS_STATE_CLEAN)
return;
return;
/*
* Until the array is sorted and stable, stacks_hash will be non-NULL.
* This way, we can get at all of the data, even if qsort() was
* interrupted while mucking with the array.
*/
if (stacks_hash != NULL) {
}
}
}
if (stacks_array != NULL)
stacks_array_size * sizeof (*stacks_array));
} else if (stacks_array != NULL) {
}
}
}
stacks_array_size * sizeof (*stacks_array));
}
stacks_array_size = 0;
}
/*ARGSUSED*/
int
{
int idx;
return (WALK_NEXT);
}
continue;
return (WALK_NEXT);
}
sip->si_entries++;
return (WALK_NEXT);
}
int
{
int ret;
found++;
break;
return (-1);
}
if (found)
return (0);
return (-1);
}
int
{
si.si_entries = 0;
if (verbose)
mdb_warn("stacks: processing kernel threads\n");
return (DCMD_ERR);
} else {
mdb_warn("cannot walk \"thread\"");
return (DCMD_ERR);
}
}
if (verbose)
mdb_warn("stacks: %d unique stacks / %d threads\n",
cur = stacks_array;
}
mdb_warn("stacks: miscounted array size (%d != size: %d)\n",
return (DCMD_ERR);
}
/* Now that we're done, free the hash table */
stacks_hash = NULL;
if (verbose)
mdb_warn("stacks: done\n");
return (DCMD_OK);
}
static int
{
int idx;
char c[MDB_SYM_NAMLEN];
c, sizeof (c), &sym) != -1 &&
}
return (1);
return (0);
}
static int
{
int idx;
return (1);
}
return (0);
}
static int
{
if (stacks_module(mp) != 0)
return (-1);
return (-1);
}
return (0);
}
static int
{
return (1);
return (-1);
return (0);
}
/*ARGSUSED*/
int
{
const char *caller_str = NULL;
const char *excl_caller_str = NULL;
const char *module_str = NULL;
const char *excl_module_str = NULL;
const char *tstate_str = NULL;
const char *excl_tstate_str = NULL;
uint_t interesting = 0;
/*
* We have a slight behavior difference between having piped
* input and 'addr::stacks'. Without a pipe, we assume the
* thread pointer given is a representative thread, and so
* we include all similar threads in the system in our output.
*
* With a pipe, we filter down to just the threads in our
* input.
*/
mdb_pipe_t p;
return (DCMD_USAGE);
if (interesting) {
"stacks: -i is incompatible with -[sStT]\n");
return (DCMD_USAGE);
}
excl_sobj = "CV";
excl_tstate_str = "FREE";
}
if (caller_str != NULL) {
mdb_set_dot(0);
if (mdb_eval(caller_str) != 0) {
mdb_warn("stacks: evaluation of \"%s\" failed",
return (DCMD_ABORT);
}
caller = mdb_get_dot();
}
if (excl_caller_str != NULL) {
mdb_set_dot(0);
if (mdb_eval(excl_caller_str) != 0) {
mdb_warn("stacks: evaluation of \"%s\" failed",
return (DCMD_ABORT);
}
excl_caller = mdb_get_dot();
}
return (DCMD_ABORT);
if (excl_module_str != NULL &&
return (DCMD_ABORT);
return (DCMD_USAGE);
return (DCMD_USAGE);
if (sobj_ops != 0 && excl_sobj_ops != 0) {
mdb_warn("stacks: only one of -s and -S can be specified\n");
return (DCMD_USAGE);
}
return (DCMD_USAGE);
if (excl_tstate_str != NULL &&
return (DCMD_USAGE);
mdb_warn("stacks: only one of -t and -T can be specified\n");
return (DCMD_USAGE);
}
/*
* If there's an address specified, we're going to further filter
* to only entries which have an address in the input. To reduce
* overhead (and make the sorted output come out right), we
* use mdb_get_pipe() to grab the entire pipeline of input, then
* use qsort() and bsearch() to speed up the search.
*/
if (addrspec) {
mdb_get_pipe(&p);
p.pipe_len = 1;
}
/* remove any duplicates in the data */
idx = 0;
p.pipe_len--;
continue; /* repeat without incrementing idx */
}
idx++;
}
}
/*
* Force a cleanup if we're connected to a live system. Never
* do a cleanup after the first invocation around the loop.
*/
force = 0;
if (stacks_state == STACKS_STATE_CLEAN) {
return (res);
}
int frame;
if (addrspec) {
size_t foundcount = 0;
/*
* We use the now-unused hash chain field se_next to
* link together the dups which match our list.
*/
foundcount++;
else
}
}
continue; /* no match, skip entry */
if (only_matching) {
count = foundcount;
}
}
continue;
continue;
continue;
if (excl_module.sm_size != 0 &&
continue;
if (tstate != -1U) {
if (tstate == TSTATE_PANIC) {
continue;
continue;
}
if (excl_tstate != -1U) {
if (excl_tstate == TSTATE_PANIC) {
continue;
continue;
}
if (sep->se_sobj_ops == 0)
continue;
} else if (sobj_ops != 0) {
continue;
}
if (excl_sobj_ops == SOBJ_ALL) {
if (sep->se_sobj_ops != 0)
continue;
} else if (excl_sobj_ops != 0) {
continue;
}
}
if (flags & DCMD_PIPE_OUT) {
sep = only_matching ?
}
continue;
}
mdb_printf("%<u>%-?s %-8s %-?s %8s%</u>\n",
"THREAD", "STATE", "SOBJ", "COUNT");
printed = 1;
}
do {
char state[20];
char sobj[100];
mdb_printf("%-?p %-8s %-?s %8d\n",
else
mdb_printf("%-?p %-8s %-?s %8s\n",
char *reason;
case FSI_FAIL_NOTINMEMORY:
reason = "thread not in memory";
break;
case FSI_FAIL_THREADCORRUPT:
reason = "thread structure stack info corrupt";
break;
case FSI_FAIL_STACKNOTFOUND:
reason = "no consistent stack found";
break;
default:
reason = "unknown failure";
break;
}
}
if (sep->se_overflow)
mdb_printf("\n");
}
if (flags & DCMD_ADDRSPEC) {
mdb_warn("stacks: %p not in thread list\n",
}
return (DCMD_OK);
}