summaryrefslogtreecommitdiff
path: root/usr/src/cmd
diff options
context:
space:
mode:
authorBryan Cantrill <Bryan.Cantrill@Sun.COM>2010-07-23 17:34:02 -0700
committerBryan Cantrill <Bryan.Cantrill@Sun.COM>2010-07-23 17:34:02 -0700
commitc9a6ea2e938727c95af7108c5e00eee4c890c7ae (patch)
treef4b57dbbb90ab09fc7e3863d16e27ee599b35f5d /usr/src/cmd
parent34b5025b33f86cffccbb34831e96a3c0f211436e (diff)
downloadillumos-gate-c9a6ea2e938727c95af7108c5e00eee4c890c7ae.tar.gz
6679140 asymmetric alloc/dealloc activity can induce dynamic variable drops
6679193 dtrace_dynvar walker produces flood of dtrace_dynhash_sink 6935550 would like ::stacks for user-land 6970441 allocation failure can induce crashes in libdtrace 6971885 dtrace_program_strcompile() fails spuriously on UTF-8 input
Diffstat (limited to 'usr/src/cmd')
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_modapi.c57
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_modapi.h21
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_module_load.c25
-rw-r--r--usr/src/cmd/mdb/common/modules/conf/mapfile-extern2
-rw-r--r--usr/src/cmd/mdb/common/modules/dtrace/dtrace.c48
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/Makefile.files4
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/findstack.c489
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/findstack.h53
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c414
-rw-r--r--usr/src/cmd/mdb/common/modules/libc/findstack_subr.c382
-rw-r--r--usr/src/cmd/mdb/common/modules/libc/libc.c69
-rw-r--r--usr/src/cmd/mdb/intel/amd64/libc/Makefile21
-rw-r--r--usr/src/cmd/mdb/intel/ia32/libc/Makefile21
-rw-r--r--usr/src/cmd/mdb/sparc/v7/libc/Makefile21
-rw-r--r--usr/src/cmd/mdb/sparc/v9/libc/Makefile21
15 files changed, 1149 insertions, 499 deletions
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_modapi.c b/usr/src/cmd/mdb/common/mdb/mdb_modapi.c
index 670df6a2e2..509ffeb6ac 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.c
@@ -18,13 +18,11 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_module.h>
#include <mdb/mdb_string.h>
@@ -189,6 +187,12 @@ mdb_lookup_by_addr(uintptr_t addr, uint_t flags, char *buf,
buf, nbytes, sym, NULL));
}
+int
+mdb_getareg(mdb_tid_t tid, const char *rname, mdb_reg_t *rp)
+{
+ return (mdb_tgt_getareg(mdb.m_target, tid, rname, rp));
+}
+
u_longlong_t
mdb_strtoull(const char *s)
{
@@ -744,6 +748,51 @@ mdb_get_xdata(const char *name, void *buf, size_t nbytes)
}
/*
+ * Private callback structure for implementing mdb_object_iter, below.
+ */
+typedef struct {
+ mdb_object_cb_t oi_cb;
+ void *oi_arg;
+ int oi_rval;
+} object_iter_arg_t;
+
+/*ARGSUSED*/
+static int
+mdb_object_cb(void *data, const mdb_map_t *map, const char *fullname)
+{
+ object_iter_arg_t *arg = data;
+ mdb_object_t obj;
+
+ if (arg->oi_rval != 0)
+ return (0);
+
+ bzero(&obj, sizeof (obj));
+ obj.obj_base = map->map_base;
+ obj.obj_name = strbasename(map->map_name);
+ obj.obj_size = map->map_size;
+ obj.obj_fullname = fullname;
+
+ arg->oi_rval = arg->oi_cb(&obj, arg->oi_arg);
+
+ return (0);
+}
+
+int
+mdb_object_iter(mdb_object_cb_t cb, void *data)
+{
+ object_iter_arg_t arg;
+
+ arg.oi_cb = cb;
+ arg.oi_arg = data;
+ arg.oi_rval = 0;
+
+ if (mdb_tgt_object_iter(mdb.m_target, mdb_object_cb, &arg) != 0)
+ return (-1);
+
+ return (arg.oi_rval);
+}
+
+/*
* Private structure and function for implementing mdb_dumpptr on top
* of mdb_dump_internal
*/
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h
index 338c56c55f..aa7e8409b6 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h
@@ -18,16 +18,14 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _MDB_MODAPI_H
#define _MDB_MODAPI_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* MDB Module API
*
@@ -164,6 +162,13 @@ typedef struct mdb_pipe {
size_t pipe_len; /* Array length */
} mdb_pipe_t;
+typedef struct mdb_object {
+ const char *obj_name; /* name of object */
+ const char *obj_fullname; /* full name of object */
+ uintptr_t obj_base; /* base address of object */
+ uintptr_t obj_size; /* in memory size of object in bytes */
+} mdb_object_t;
+
extern int mdb_pwalk(const char *, mdb_walk_cb_t, void *, uintptr_t);
extern int mdb_walk(const char *, mdb_walk_cb_t, void *);
@@ -211,6 +216,11 @@ extern int mdb_lookup_by_name(const char *, GElf_Sym *);
extern int mdb_lookup_by_obj(const char *, const char *, GElf_Sym *);
extern int mdb_lookup_by_addr(uintptr_t, uint_t, char *, size_t, GElf_Sym *);
+typedef uintptr_t mdb_tid_t;
+typedef uint64_t mdb_reg_t;
+
+extern int mdb_getareg(mdb_tid_t, const char *, mdb_reg_t *);
+
#define MDB_OPT_SETBITS 1 /* Set specified flag bits */
#define MDB_OPT_CLRBITS 2 /* Clear specified flag bits */
#define MDB_OPT_STR 3 /* const char * argument */
@@ -272,6 +282,9 @@ extern void mdb_set_pipe(const mdb_pipe_t *);
extern ssize_t mdb_get_xdata(const char *, void *, size_t);
+typedef int (*mdb_object_cb_t)(mdb_object_t *, void *);
+extern int mdb_object_iter(mdb_object_cb_t, void *);
+
#define MDB_STATE_IDLE 0 /* Target is idle (not running yet) */
#define MDB_STATE_RUNNING 1 /* Target is currently executing */
#define MDB_STATE_STOPPED 2 /* Target is stopped */
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_module_load.c b/usr/src/cmd/mdb/common/mdb/mdb_module_load.c
index 83ba970eca..324b43e461 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_module_load.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_module_load.c
@@ -18,9 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/param.h>
@@ -148,10 +148,10 @@ typedef struct mdb_modload_data {
/*ARGSUSED*/
static int
-module_load(void *fp, const mdb_map_t *map, const char *name)
+module_load(void *fp, const mdb_map_t *map, const char *fullname)
{
mdb_modload_data_t *mld = fp;
- name = strbasename(name);
+ const char *name = strbasename(fullname);
if (mdb_module_load(name, mld->mld_mode) == 0 && mdb.m_term != NULL) {
if (mld->mld_first == TRUE) {
@@ -162,6 +162,23 @@ module_load(void *fp, const mdb_map_t *map, const char *name)
mdb_iob_flush(mdb.m_out);
}
+ if (strstr(fullname, "/libc/") != NULL) {
+ /*
+ * A bit of a kludge: because we manage alternately capable
+ * libc instances by mounting the appropriately capable
+ * instance over /lib/libc.so.1, we may find that our object
+ * list does not contain libc.so.1, but rather one of its
+ * hwcap variants. Unfortunately, there is not a way of
+ * getting from this shared object to the object that it is
+ * effectively interposing on -- which means that without
+ * special processing, we will not load any libc.so dmod. So
+ * if we see that we have a shared object coming out of the
+ * "libc" directory, we assume that we have a "libc-like"
+ * object, and explicitly load the "libc.so" dmod.
+ */
+ return (module_load(fp, map, "libc.so.1"));
+ }
+
return (0);
}
diff --git a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern
index 256f4bed86..ff6292e606 100644
--- a/usr/src/cmd/mdb/common/modules/conf/mapfile-extern
+++ b/usr/src/cmd/mdb/common/modules/conf/mapfile-extern
@@ -88,6 +88,7 @@ SYMBOL_SCOPE {
mdb_gelf_destroy { FLAGS = EXTERN };
mdb_gelf_sect_by_name { FLAGS = EXTERN };
mdb_gelf_sect_load { FLAGS = EXTERN };
+ mdb_getareg { FLAGS = EXTERN };
mdb_get_dot { FLAGS = EXTERN };
mdb_get_lbolt { FLAGS = EXTERN };
mdb_get_pipe { FLAGS = EXTERN };
@@ -113,6 +114,7 @@ SYMBOL_SCOPE {
mdb_memio_create { FLAGS = EXTERN };
mdb_name_to_major { FLAGS = EXTERN };
mdb_nhconvert { FLAGS = EXTERN };
+ mdb_object_iter { FLAGS = EXTERN };
mdb_one_bit { FLAGS = EXTERN };
mdb_page2pfn { FLAGS = EXTERN };
mdb_page_lookup { FLAGS = EXTERN };
diff --git a/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c b/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c
index 10eb6c80c9..265322e3f5 100644
--- a/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c
+++ b/usr/src/cmd/mdb/common/modules/dtrace/dtrace.c
@@ -20,8 +20,7 @@
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
@@ -435,10 +434,12 @@ dtracemdb_bufsnap(dtrace_buffer_t *which, dtrace_bufdesc_t *desc)
}
/*
- * This is essentially identical to its cousin in the kernel.
+ * This is essentially identical to its cousin in the kernel -- with the
+ * notable exception that we automatically set DTRACEOPT_GRABANON if this
+ * state is an anonymous enabling.
*/
static dof_hdr_t *
-dtracemdb_dof_create(dtrace_state_t *state)
+dtracemdb_dof_create(dtrace_state_t *state, int isanon)
{
dof_hdr_t *dof;
dof_sec_t *sec;
@@ -490,6 +491,9 @@ dtracemdb_dof_create(dtrace_state_t *state)
opt[i].dofo_value = state->dts_options[i];
}
+ if (isanon)
+ opt[DTRACEOPT_GRABANON].dofo_value = 1;
+
return (dof);
}
@@ -621,6 +625,7 @@ typedef struct dtracemdb_data {
char *dtmd_symstr;
char *dtmd_modstr;
uintptr_t dtmd_addr;
+ int dtmd_isanon;
} dtracemdb_data_t;
static int
@@ -645,7 +650,7 @@ dtracemdb_ioctl(void *varg, int cmd, void *arg)
case DTRACEIOC_DOFGET: {
dof_hdr_t *hdr = arg, *dof;
- dof = dtracemdb_dof_create(state);
+ dof = dtracemdb_dof_create(state, data->dtmd_isanon);
bcopy(dof, hdr, MIN(hdr->dofh_loadsz, dof->dofh_loadsz));
mdb_free(dof, dof->dofh_loadsz);
@@ -974,6 +979,7 @@ dtrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
dtrace_optval_t val;
dtracemdb_data_t md;
int rval = DCMD_ERR;
+ dtrace_anon_t anon;
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
@@ -991,6 +997,15 @@ dtrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_ERR);
}
+ if (state.dts_anon != NULL) {
+ addr = (uintptr_t)state.dts_anon;
+
+ if (mdb_vread(&state, sizeof (state), addr) == -1) {
+ mdb_warn("couldn't read anonymous state at %p", addr);
+ return (DCMD_ERR);
+ }
+ }
+
bzero(&md, sizeof (md));
md.dtmd_state = &state;
@@ -1001,6 +1016,17 @@ dtrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_ERR);
}
+ /*
+ * If this is the anonymous enabling, we need to set a bit indicating
+ * that DTRACEOPT_GRABANON should be set.
+ */
+ if (mdb_readvar(&anon, "dtrace_anon") == -1) {
+ mdb_warn("failed to read 'dtrace_anon'");
+ return (DCMD_ERR);
+ }
+
+ md.dtmd_isanon = ((uintptr_t)anon.dta_state == addr);
+
if (dtrace_go(dtp) != 0) {
mdb_warn("failed to initialize dtrace: %s\n",
dtrace_errmsg(dtp, dtrace_errno(dtp)));
@@ -1750,6 +1776,7 @@ typedef struct dtrace_dynvar_data {
uintptr_t dtdvd_hashsize;
uintptr_t dtdvd_next;
uintptr_t dtdvd_ndx;
+ uintptr_t dtdvd_sink;
} dtrace_dynvar_data_t;
int
@@ -1759,6 +1786,7 @@ dtrace_dynvar_init(mdb_walk_state_t *wsp)
dtrace_dstate_t dstate;
dtrace_dynvar_data_t *data;
size_t hsize;
+ GElf_Sym sym;
if ((addr = wsp->walk_addr) == NULL) {
mdb_warn("dtrace_dynvar walk needs dtrace_dstate_t\n");
@@ -1770,11 +1798,17 @@ dtrace_dynvar_init(mdb_walk_state_t *wsp)
return (WALK_ERR);
}
+ if (mdb_lookup_by_name("dtrace_dynhash_sink", &sym) == -1) {
+ mdb_warn("couldn't find 'dtrace_dynhash_sink'");
+ return (WALK_ERR);
+ }
+
data = mdb_zalloc(sizeof (dtrace_dynvar_data_t), UM_SLEEP);
data->dtdvd_hashsize = dstate.dtds_hashsize;
hsize = dstate.dtds_hashsize * sizeof (dtrace_dynhash_t);
data->dtdvd_hash = mdb_alloc(hsize, UM_SLEEP);
+ data->dtdvd_sink = (uintptr_t)sym.st_value;
if (mdb_vread(data->dtdvd_hash, hsize,
(uintptr_t)dstate.dtds_hash) == -1) {
@@ -1785,6 +1819,8 @@ dtrace_dynvar_init(mdb_walk_state_t *wsp)
return (WALK_ERR);
}
+ data->dtdvd_next = (uintptr_t)data->dtdvd_hash[0].dtdh_chain;
+
wsp->walk_data = data;
return (WALK_NEXT);
}
@@ -1798,7 +1834,7 @@ dtrace_dynvar_step(mdb_walk_state_t *wsp)
uintptr_t addr;
int nkeys;
- while ((addr = data->dtdvd_next) == NULL) {
+ while ((addr = data->dtdvd_next) == data->dtdvd_sink) {
if (data->dtdvd_ndx == data->dtdvd_hashsize)
return (WALK_DONE);
diff --git a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
index c07bd9cb70..cbf2f35b6a 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
+++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
@@ -19,8 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
#
#
@@ -41,6 +40,7 @@ GENUNIX_SRCS = \
devinfo.c \
dist.c \
findstack.c \
+ findstack_subr.c \
fm.c \
genunix.c \
group.c \
diff --git a/usr/src/cmd/mdb/common/modules/genunix/findstack.c b/usr/src/cmd/mdb/common/modules/genunix/findstack.c
index ccb50ae26e..223764eda9 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/findstack.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/findstack.c
@@ -18,9 +18,9 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <mdb/mdb_modapi.h>
@@ -31,130 +31,13 @@
#include <sys/stack.h>
#include <sys/thread.h>
#include <sys/modctl.h>
+#include <assert.h>
#include "findstack.h"
#include "thread.h"
#include "sobj.h"
-typedef struct findstack_info {
- uintptr_t *fsi_stack; /* place to record frames */
-
- uintptr_t fsi_sp; /* stack pointer */
- uintptr_t fsi_pc; /* pc */
- uintptr_t fsi_sobj_ops; /* sobj_ops */
-
- uint_t fsi_tstate; /* t_state */
-
- uchar_t fsi_depth; /* stack depth */
- uchar_t fsi_failed; /* search failed */
- uchar_t fsi_overflow; /* stack was deeper than max_depth */
- uchar_t fsi_panic; /* thread called panic() */
-
- uchar_t fsi_max_depth; /* stack frames available */
-} findstack_info_t;
-#define FSI_FAIL_BADTHREAD 1
-#define FSI_FAIL_NOTINMEMORY 2
-#define FSI_FAIL_THREADCORRUPT 3
-#define FSI_FAIL_STACKNOTFOUND 4
-
-#ifndef STACK_BIAS
-#define STACK_BIAS 0
-#endif
-
-#define fs_dprintf(x) \
- if (findstack_debug_on) { \
- mdb_printf("findstack debug: "); \
- /*CSTYLED*/ \
- mdb_printf x ; \
- }
-
-static int findstack_debug_on = 0;
-
-#if defined(__i386) || defined(__amd64)
-struct rwindow {
- uintptr_t rw_fp;
- uintptr_t rw_rtn;
-};
-#endif
-
-#define TOO_BIG_FOR_A_STACK (1024 * 1024)
-
-#define KTOU(p) ((p) - kbase + ubase)
-#define UTOK(p) ((p) - ubase + kbase)
-
-#define CRAWL_FOUNDALL (-1)
-
-/*
- * Given a stack pointer, try to crawl down it to the bottom.
- * "frame" is a VA in MDB's address space.
- *
- * Returns the number of frames successfully crawled down, or
- * CRAWL_FOUNDALL if it got to the bottom of the stack.
- */
-static int
-crawl(uintptr_t frame, uintptr_t kbase, uintptr_t ktop, uintptr_t ubase,
- int kill_fp, findstack_info_t *fsip)
-{
- int levels = 0;
-
- fsip->fsi_depth = 0;
- fsip->fsi_overflow = 0;
-
- fs_dprintf(("<0> frame = %p, kbase = %p, ktop = %p, ubase = %p\n",
- frame, kbase, ktop, ubase));
- for (;;) {
- uintptr_t fp;
- long *fpp = (long *)&((struct rwindow *)frame)->rw_fp;
-
- fs_dprintf(("<1> fpp = %p, frame = %p\n", fpp, frame));
-
- if ((frame & (STACK_ALIGN - 1)) != 0)
- break;
-
- fp = ((struct rwindow *)frame)->rw_fp + STACK_BIAS;
- if (fsip->fsi_depth < fsip->fsi_max_depth)
- fsip->fsi_stack[fsip->fsi_depth++] =
- ((struct rwindow *)frame)->rw_rtn;
- else
- fsip->fsi_overflow = 1;
-
- fs_dprintf(("<2> fp = %p\n", fp));
-
- if (fp == ktop)
- return (CRAWL_FOUNDALL);
- fs_dprintf(("<3> not at base\n"));
-
-#if defined(__i386) || defined(__amd64)
- if (ktop - fp == sizeof (struct rwindow)) {
- fs_dprintf(("<4> found base\n"));
- return (CRAWL_FOUNDALL);
- }
-#endif
-
- fs_dprintf(("<5> fp = %p, kbase = %p, ktop - size = %p\n",
- fp, kbase, ktop - sizeof (struct rwindow)));
-
- if (fp < kbase || fp >= (ktop - sizeof (struct rwindow)))
- break;
-
- frame = KTOU(fp);
- fs_dprintf(("<6> frame = %p\n", frame));
-
- /*
- * NULL out the old %fp so we don't go down this stack
- * more than once.
- */
- if (kill_fp) {
- fs_dprintf(("<7> fpp = %p\n", fpp));
- *fpp = NULL;
- }
-
- fs_dprintf(("<8> levels = %d\n", levels));
- levels++;
- }
-
- return (levels);
-}
+int findstack_debug_on = 0;
/*
* "sp" is a kernel VA.
@@ -193,164 +76,6 @@ print_stack(uintptr_t sp, uintptr_t pc, uintptr_t addr,
return ((err == -1) ? DCMD_ABORT : DCMD_OK);
}
-/*ARGSUSED*/
-static int
-do_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings)
-{
- kthread_t thr;
- size_t stksz;
- uintptr_t ubase, utop;
- uintptr_t kbase, ktop;
- uintptr_t win, sp;
-
- fsip->fsi_failed = 0;
- fsip->fsi_pc = 0;
- fsip->fsi_sp = 0;
- fsip->fsi_depth = 0;
- fsip->fsi_overflow = 0;
-
- bzero(&thr, sizeof (thr));
- if (mdb_ctf_vread(&thr, "kthread_t", addr,
- MDB_CTF_VREAD_IGNORE_ALL) == -1) {
- if (print_warnings)
- mdb_warn("couldn't read thread at %p\n", addr);
- fsip->fsi_failed = FSI_FAIL_BADTHREAD;
- return (DCMD_ERR);
- }
-
- fsip->fsi_sobj_ops = (uintptr_t)thr.t_sobj_ops;
- fsip->fsi_tstate = thr.t_state;
- fsip->fsi_panic = !!(thr.t_flag & T_PANIC);
-
- if ((thr.t_schedflag & TS_LOAD) == 0) {
- if (print_warnings)
- mdb_warn("thread %p isn't in memory\n", addr);
- fsip->fsi_failed = FSI_FAIL_NOTINMEMORY;
- return (DCMD_ERR);
- }
-
- if (thr.t_stk < thr.t_stkbase) {
- if (print_warnings)
- mdb_warn(
- "stack base or stack top corrupt for thread %p\n",
- addr);
- fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
- return (DCMD_ERR);
- }
-
- kbase = (uintptr_t)thr.t_stkbase;
- ktop = (uintptr_t)thr.t_stk;
- stksz = ktop - kbase;
-
-#ifdef __amd64
- /*
- * The stack on amd64 is intentionally misaligned, so ignore the top
- * half-frame. See thread_stk_init(). When handling traps, the frame
- * is automatically aligned by the hardware, so we only alter ktop if
- * needed.
- */
- if ((ktop & (STACK_ALIGN - 1)) != 0)
- ktop -= STACK_ENTRY_ALIGN;
-#endif
-
- /*
- * If the stack size is larger than a meg, assume that it's bogus.
- */
- if (stksz > TOO_BIG_FOR_A_STACK) {
- if (print_warnings)
- mdb_warn("stack size for thread %p is too big to be "
- "reasonable\n", addr);
- fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
- return (DCMD_ERR);
- }
-
- /*
- * This could be (and was) a UM_GC allocation. Unfortunately,
- * stksz tends to be very large. As currently implemented, dcmds
- * invoked as part of pipelines don't have their UM_GC-allocated
- * memory freed until the pipeline completes. With stksz in the
- * neighborhood of 20k, the popular ::walk thread |::findstack
- * pipeline can easily run memory-constrained debuggers (kmdb) out
- * of memory. This can be changed back to a gc-able allocation when
- * the debugger is changed to free UM_GC memory more promptly.
- */
- ubase = (uintptr_t)mdb_alloc(stksz, UM_SLEEP);
- utop = ubase + stksz;
- if (mdb_vread((caddr_t)ubase, stksz, kbase) != stksz) {
- mdb_free((void *)ubase, stksz);
- if (print_warnings)
- mdb_warn("couldn't read entire stack for thread %p\n",
- addr);
- fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
- return (DCMD_ERR);
- }
-
- /*
- * Try the saved %sp first, if it looks reasonable.
- */
- sp = KTOU((uintptr_t)thr.t_sp + STACK_BIAS);
- if (sp >= ubase && sp <= utop) {
- if (crawl(sp, kbase, ktop, ubase, 0, fsip) == CRAWL_FOUNDALL) {
- fsip->fsi_sp = (uintptr_t)thr.t_sp;
-#if !defined(__i386)
- fsip->fsi_pc = (uintptr_t)thr.t_pc;
-#endif
- goto found;
- }
- }
-
- /*
- * Now walk through the whole stack, starting at the base,
- * trying every possible "window".
- */
- for (win = ubase;
- win + sizeof (struct rwindow) <= utop;
- win += sizeof (struct rwindow *)) {
- if (crawl(win, kbase, ktop, ubase, 1, fsip) == CRAWL_FOUNDALL) {
- fsip->fsi_sp = UTOK(win) - STACK_BIAS;
- goto found;
- }
- }
-
- /*
- * We didn't conclusively find the stack. So we'll take another lap,
- * and print out anything that looks possible.
- */
- if (print_warnings)
- mdb_printf("Possible stack pointers for thread %p:\n", addr);
- (void) mdb_vread((caddr_t)ubase, stksz, kbase);
-
- for (win = ubase;
- win + sizeof (struct rwindow) <= utop;
- win += sizeof (struct rwindow *)) {
- uintptr_t fp = ((struct rwindow *)win)->rw_fp;
- int levels;
-
- if ((levels = crawl(win, kbase, ktop, ubase, 1, fsip)) > 1) {
- if (print_warnings)
- mdb_printf(" %p (%d)\n", fp, levels);
- } else if (levels == CRAWL_FOUNDALL) {
- /*
- * If this is a live system, the stack could change
- * between the two mdb_vread(ubase, utop, kbase)'s,
- * and we could have a fully valid stack here.
- */
- fsip->fsi_sp = UTOK(win) - STACK_BIAS;
- goto found;
- }
- }
-
- fsip->fsi_depth = 0;
- fsip->fsi_overflow = 0;
- fsip->fsi_failed = FSI_FAIL_STACKNOTFOUND;
-
- mdb_free((void *)ubase, stksz);
- return (DCMD_ERR);
-found:
- mdb_free((void *)ubase, stksz);
- return (DCMD_OK);
-}
-
int
findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
@@ -362,7 +87,7 @@ findstack(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
bzero(&fsi, sizeof (fsi));
- if ((retval = do_findstack(addr, &fsi, 1)) != DCMD_OK ||
+ if ((retval = stacks_findstack(addr, &fsi, 1)) != DCMD_OK ||
fsi.fsi_failed)
return (retval);
@@ -399,6 +124,7 @@ sobj_to_text(uintptr_t addr, char *out, size_t out_sz)
}
#define SOBJ_ALL 1
+
static int
text_to_sobj(const char *text, uintptr_t *out)
{
@@ -406,6 +132,7 @@ text_to_sobj(const char *text, uintptr_t *out)
*out = SOBJ_ALL;
return (0);
}
+
return (sobj_text_to_ops(text, out));
}
@@ -457,11 +184,9 @@ typedef struct stacks_info {
size_t si_count; /* total stacks_entry_ts (incl dups) */
size_t si_entries; /* # entries in hash table */
stacks_entry_t **si_hash; /* hash table */
-
findstack_info_t si_fsi; /* transient callback state */
} stacks_info_t;
-
/* global state cached between invocations */
#define STACKS_STATE_CLEAN 0
#define STACKS_STATE_DIRTY 1
@@ -631,6 +356,8 @@ stacks_cleanup(int force)
stacks_array_size * sizeof (*stacks_array));
}
+ stacks_findstack_cleanup();
+
stacks_array_size = 0;
stacks_state = STACKS_STATE_CLEAN;
}
@@ -646,7 +373,7 @@ stacks_thread_cb(uintptr_t addr, const void *ignored, void *cbarg)
int idx;
size_t depth;
- if (do_findstack(addr, fsip, 0) != DCMD_OK &&
+ if (stacks_findstack(addr, fsip, 0) != DCMD_OK &&
fsip->fsi_failed == FSI_FAIL_BADTHREAD) {
mdb_warn("couldn't read thread at %p\n", addr);
return (WALK_NEXT);
@@ -694,19 +421,14 @@ stacks_run_tlist(mdb_pipe_t *tlist, stacks_info_t *si)
{
size_t idx;
size_t found = 0;
- kthread_t kt;
int ret;
for (idx = 0; idx < tlist->pipe_len; idx++) {
uintptr_t addr = tlist->pipe_data[idx];
- if (mdb_vread(&kt, sizeof (kt), addr) == -1) {
- mdb_warn("unable to read kthread_t at %p", addr);
- continue;
- }
found++;
- ret = stacks_thread_cb(addr, &kt, si);
+ ret = stacks_thread_cb(addr, NULL, si);
if (ret == WALK_DONE)
break;
if (ret != WALK_NEXT)
@@ -811,66 +533,36 @@ stacks_has_caller(stacks_entry_t *sep, uintptr_t addr)
return (0);
}
-typedef struct find_module_struct {
- struct modctl *mcp;
- const char *name;
-} find_module_struct_t;
-
-/*ARGSUSED*/
-int
-find_module_cb(uintptr_t addr, const void *modctl_arg, void *cbarg)
+static int
+stacks_has_module(stacks_entry_t *sep, stacks_module_t *mp)
{
- find_module_struct_t *sp = cbarg;
- char mod_modname[MODMAXNAMELEN + 1];
- const struct modctl *mp = modctl_arg;
-
- if (!mp->mod_modname)
- return (WALK_NEXT);
+ int idx;
- if (mdb_readstr(mod_modname, sizeof (mod_modname),
- (uintptr_t)mp->mod_modname) == -1) {
- mdb_warn("failed to read mod_modname in \"modctl\" walk");
- return (WALK_ERR);
+ for (idx = 0; idx < sep->se_depth; idx++) {
+ if (sep->se_stack[idx] >= mp->sm_text &&
+ sep->se_stack[idx] < mp->sm_text + mp->sm_size)
+ return (1);
}
- if (strcmp(sp->name, mod_modname))
- return (WALK_NEXT);
-
- sp->mcp = mdb_alloc(sizeof (*sp->mcp), UM_SLEEP | UM_GC);
- bcopy(mp, sp->mcp, sizeof (*sp->mcp));
- return (WALK_DONE);
-}
-
-static struct modctl *
-find_module(const char *name)
-{
- find_module_struct_t mptr;
-
- mptr.name = name;
- mptr.mcp = NULL;
-
- if (mdb_walk("modctl", find_module_cb, &mptr) != 0)
- mdb_warn("cannot walk \"modctl\"");
- return (mptr.mcp);
+ return (0);
}
static int
-stacks_has_module(stacks_entry_t *sep, struct modctl *mp)
+stacks_module_find(const char *name, stacks_module_t *mp)
{
- int idx;
+ (void) strncpy(mp->sm_name, name, sizeof (mp->sm_name));
- if (mp == NULL)
- return (0);
+ if (stacks_module(mp) != 0)
+ return (-1);
+
+ if (mp->sm_size == 0) {
+ mdb_warn("stacks: module \"%s\" is unknown\n", name);
+ return (-1);
+ }
- for (idx = 0; idx < sep->se_depth; idx++)
- if (sep->se_stack[idx] >= (uintptr_t)mp->mod_text &&
- sep->se_stack[idx] <
- ((uintptr_t)mp->mod_text + mp->mod_text_size))
- return (1);
return (0);
}
-
static int
uintptrcomp(const void *lp, const void *rp)
{
@@ -884,96 +576,6 @@ uintptrcomp(const void *lp, const void *rp)
}
/*ARGSUSED*/
-static void
-print_sobj_help(int type, const char *name, const char *ops_name, void *ign)
-{
- mdb_printf(" %s", name);
-}
-
-/*ARGSUSED*/
-static void
-print_tstate_help(uint_t state, const char *name, void *ignored)
-{
- mdb_printf(" %s", name);
-}
-
-void
-stacks_help(void)
-{
- mdb_printf(
-"::stacks processes all of the thread stacks on the system, grouping\n"
-"together threads which have the same:\n"
-"\n"
-" * Thread state,\n"
-" * Sync object type, and\n"
-" * PCs in their stack trace.\n"
-"\n"
-"The default output (no address or options) is just a dump of the thread\n"
-"groups in the system. For a view of active threads, use \"::stacks -i\",\n"
-"which filters out FREE threads (interrupt threads which are currently\n"
-"inactive) and threads sleeping on a CV. (Note that those threads may still\n"
-"be noteworthy; this is just for a first glance.) More general filtering\n"
-"options are described below, in the \"FILTERS\" section.\n"
-"\n"
-"::stacks can be used in a pipeline. The input to ::stacks is one or more\n"
-"thread pointers. For example, to get a summary of threads in a process,\n"
-"you can do:\n"
-"\n"
-" %<b>procp%</b>::walk thread | ::stacks\n"
-"\n"
-"When output into a pipe, ::stacks prints all of the threads input,\n"
-"filtered by the given filtering options. This means that multiple\n"
-"::stacks invocations can be piped together to achieve more complicated\n"
-"filters. For example, to get threads which have both 'fop_read' and\n"
-"'cv_wait_sig_swap' in their stack trace, you could do:\n"
-"\n"
-" ::stacks -c fop_read | ::stacks -c cv_wait_sig_swap_core\n"
-"\n"
-"To get the full list of threads in each group, use the '-a' flag:\n"
-"\n"
-" ::stacks -a\n"
-"\n");
- mdb_dec_indent(2);
- mdb_printf("%<b>OPTIONS%</b>\n");
- mdb_inc_indent(2);
- mdb_printf("%s",
-" -a Print all of the grouped threads, instead of just a count.\n"
-" -f Force a re-run of the thread stack gathering.\n"
-" -v Be verbose about thread stack gathering.\n"
-"\n");
- mdb_dec_indent(2);
- mdb_printf("%<b>FILTERS%</b>\n");
- mdb_inc_indent(2);
- mdb_printf("%s",
-" -i Show active threads; equivalent to '-S CV -T FREE'.\n"
-" -c func[+offset]\n"
-" Only print threads whose stacks contain func/func+offset.\n"
-" -C func[+offset]\n"
-" Only print threads whose stacks do not contain func/func+offset.\n"
-" -m module\n"
-" Only print threads whose stacks contain functions from module.\n"
-" -M module\n"
-" Only print threads whose stacks do not contain functions from\n"
-" module.\n"
-" -s {type | ALL}\n"
-" Only print threads which are on a 'type' synchronization object\n"
-" (SOBJ).\n"
-" -S {type | ALL}\n"
-" Only print threads which are not on a 'type' SOBJ.\n"
-" -t tstate\n"
-" Only print threads which are in thread state 'tstate'.\n"
-" -T tstate\n"
-" Only print threads which are not in thread state 'tstate'.\n"
-"\n");
- mdb_printf(" SOBJ types:");
- sobj_type_walk(print_sobj_help, NULL);
- mdb_printf("\n");
- mdb_printf("Thread states:");
- thread_walk_states(print_tstate_help, NULL);
- mdb_printf(" panic\n");
-}
-
-/*ARGSUSED*/
int
stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
@@ -986,7 +588,7 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
uintptr_t caller = 0, excl_caller = 0;
const char *module_str = NULL;
const char *excl_module_str = NULL;
- struct modctl *module = NULL, *excl_module = NULL;
+ stacks_module_t module, excl_module;
const char *sobj = NULL;
const char *excl_sobj = NULL;
uintptr_t sobj_ops = 0, excl_sobj_ops = 0;
@@ -1015,6 +617,9 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
mdb_pipe_t p;
+ bzero(&module, sizeof (module));
+ bzero(&excl_module, sizeof (excl_module));
+
if (mdb_getopts(argc, argv,
'a', MDB_OPT_SETBITS, TRUE, &all,
'f', MDB_OPT_SETBITS, TRUE, &force,
@@ -1063,24 +668,17 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
}
mdb_set_dot(addr);
- if (module_str != NULL &&
- (module = find_module(module_str)) == NULL) {
- mdb_warn("stacks: module \"%s\" is unknown", module_str);
+ if (module_str != NULL && stacks_module_find(module_str, &module) != 0)
return (DCMD_ABORT);
- }
if (excl_module_str != NULL &&
- (excl_module = find_module(excl_module_str)) == NULL) {
- mdb_warn("stacks: module \"%s\" is unknown", excl_module_str);
+ stacks_module_find(excl_module_str, &excl_module) != 0)
return (DCMD_ABORT);
- }
- if (sobj != NULL &&
- text_to_sobj(sobj, &sobj_ops) != 0)
+ if (sobj != NULL && text_to_sobj(sobj, &sobj_ops) != 0)
return (DCMD_USAGE);
- if (excl_sobj != NULL &&
- text_to_sobj(excl_sobj, &excl_sobj_ops) != 0)
+ if (excl_sobj != NULL && text_to_sobj(excl_sobj, &excl_sobj_ops) != 0)
return (DCMD_USAGE);
if (sobj_ops != 0 && excl_sobj_ops != 0) {
@@ -1088,8 +686,7 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_USAGE);
}
- if (tstate_str != NULL &&
- text_to_tstate(tstate_str, &tstate) != 0)
+ if (tstate_str != NULL && text_to_tstate(tstate_str, &tstate) != 0)
return (DCMD_USAGE);
if (excl_tstate_str != NULL &&
@@ -1190,11 +787,15 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
if (caller != 0 && !stacks_has_caller(sep, caller))
continue;
+
if (excl_caller != 0 && stacks_has_caller(sep, excl_caller))
continue;
- if (module != 0 && !stacks_has_module(sep, module))
+
+ if (module.sm_size != 0 && !stacks_has_module(sep, &module))
continue;
- if (excl_module != 0 && stacks_has_module(sep, excl_module))
+
+ if (excl_module.sm_size != 0 &&
+ stacks_has_module(sep, &excl_module))
continue;
if (tstate != -1U) {
@@ -1256,10 +857,10 @@ stacks(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
sobj, sizeof (sobj));
if (cur == sep)
- mdb_printf("%?p %-8s %-?s %8d\n",
+ mdb_printf("%-?p %-8s %-?s %8d\n",
cur->se_thread, state, sobj, count);
else
- mdb_printf("%?p %-8s %-?s %8s\n",
+ mdb_printf("%-?p %-8s %-?s %8s\n",
cur->se_thread, state, sobj, "-");
cur = only_matching ? cur->se_next : cur->se_dup;
diff --git a/usr/src/cmd/mdb/common/modules/genunix/findstack.h b/usr/src/cmd/mdb/common/modules/genunix/findstack.h
index 304735c931..d81ae1ab6f 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/findstack.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/findstack.h
@@ -18,27 +18,74 @@
*
* CDDL HEADER END
*/
+
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _MDB_FINDSTACK_H
#define _MDB_FINDSTACK_H
#include <mdb/mdb_modapi.h>
+#include <sys/param.h>
#ifdef __cplusplus
extern "C" {
#endif
+typedef struct findstack_info {
+ uintptr_t *fsi_stack; /* place to record frames */
+ uintptr_t fsi_sp; /* stack pointer */
+ uintptr_t fsi_pc; /* pc */
+ uintptr_t fsi_sobj_ops; /* sobj_ops */
+ uint_t fsi_tstate; /* t_state */
+ uchar_t fsi_depth; /* stack depth */
+ uchar_t fsi_failed; /* search failed */
+ uchar_t fsi_overflow; /* stack was deeper than max_depth */
+ uchar_t fsi_panic; /* thread called panic() */
+ uchar_t fsi_max_depth; /* stack frames available */
+} findstack_info_t;
+
+#define FSI_FAIL_BADTHREAD 1
+#define FSI_FAIL_NOTINMEMORY 2
+#define FSI_FAIL_THREADCORRUPT 3
+#define FSI_FAIL_STACKNOTFOUND 4
+
+typedef struct stacks_module {
+ char sm_name[MAXPATHLEN]; /* name of module */
+ uintptr_t sm_text; /* base address of text in module */
+ size_t sm_size; /* size of text in module */
+} stacks_module_t;
+
extern int findstack(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int findstack_debug(uintptr_t, uint_t, int, const mdb_arg_t *);
+/*
+ * The following routines are implemented in findstack.c, shared across both
+ * genunix and libc.
+ */
extern int stacks(uintptr_t, uint_t, int, const mdb_arg_t *);
-extern void stacks_help(void);
extern void stacks_cleanup(int);
+/*
+ * The following routines are specific to their context (kernel vs. user-land)
+ * and are therefore implemented in findstack_subr.c (of which each of genunix
+ * and libc have their own copy).
+ */
+extern void stacks_help(void);
+extern int stacks_findstack(uintptr_t, findstack_info_t *, uint_t);
+extern void stacks_findstack_cleanup();
+extern int stacks_module(stacks_module_t *);
+
+extern int findstack_debug_on;
+
+#define fs_dprintf(x) \
+ if (findstack_debug_on) { \
+ mdb_printf("findstack debug: "); \
+ /*CSTYLED*/ \
+ mdb_printf x ; \
+ }
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c b/usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c
new file mode 100644
index 0000000000..aa68d0236a
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/genunix/findstack_subr.c
@@ -0,0 +1,414 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <mdb/mdb_modapi.h>
+#include <mdb/mdb_ctf.h>
+
+#include <sys/types.h>
+#include <sys/regset.h>
+#include <sys/stack.h>
+#include <sys/thread.h>
+#include <sys/modctl.h>
+
+#include "findstack.h"
+#include "thread.h"
+#include "sobj.h"
+
+#define TOO_BIG_FOR_A_STACK (1024 * 1024)
+
+#define KTOU(p) ((p) - kbase + ubase)
+#define UTOK(p) ((p) - ubase + kbase)
+
+#define CRAWL_FOUNDALL (-1)
+
+#if defined(__i386) || defined(__amd64)
+struct rwindow {
+ uintptr_t rw_fp;
+ uintptr_t rw_rtn;
+};
+#endif
+
+#ifndef STACK_BIAS
+#define STACK_BIAS 0
+#endif
+
+/*
+ * Given a stack pointer, try to crawl down it to the bottom.
+ * "frame" is a VA in MDB's address space.
+ *
+ * Returns the number of frames successfully crawled down, or
+ * CRAWL_FOUNDALL if it got to the bottom of the stack.
+ */
+static int
+crawl(uintptr_t frame, uintptr_t kbase, uintptr_t ktop, uintptr_t ubase,
+ int kill_fp, findstack_info_t *fsip)
+{
+ int levels = 0;
+
+ fsip->fsi_depth = 0;
+ fsip->fsi_overflow = 0;
+
+ fs_dprintf(("<0> frame = %p, kbase = %p, ktop = %p, ubase = %p\n",
+ frame, kbase, ktop, ubase));
+ for (;;) {
+ uintptr_t fp;
+ long *fpp = (long *)&((struct rwindow *)frame)->rw_fp;
+
+ fs_dprintf(("<1> fpp = %p, frame = %p\n", fpp, frame));
+
+ if ((frame & (STACK_ALIGN - 1)) != 0)
+ break;
+
+ fp = ((struct rwindow *)frame)->rw_fp + STACK_BIAS;
+ if (fsip->fsi_depth < fsip->fsi_max_depth)
+ fsip->fsi_stack[fsip->fsi_depth++] =
+ ((struct rwindow *)frame)->rw_rtn;
+ else
+ fsip->fsi_overflow = 1;
+
+ fs_dprintf(("<2> fp = %p\n", fp));
+
+ if (fp == ktop)
+ return (CRAWL_FOUNDALL);
+ fs_dprintf(("<3> not at base\n"));
+
+#if defined(__i386) || defined(__amd64)
+ if (ktop - fp == sizeof (struct rwindow)) {
+ fs_dprintf(("<4> found base\n"));
+ return (CRAWL_FOUNDALL);
+ }
+#endif
+
+ fs_dprintf(("<5> fp = %p, kbase = %p, ktop - size = %p\n",
+ fp, kbase, ktop - sizeof (struct rwindow)));
+
+ if (fp < kbase || fp >= (ktop - sizeof (struct rwindow)))
+ break;
+
+ frame = KTOU(fp);
+ fs_dprintf(("<6> frame = %p\n", frame));
+
+ /*
+ * NULL out the old %fp so we don't go down this stack
+ * more than once.
+ */
+ if (kill_fp) {
+ fs_dprintf(("<7> fpp = %p\n", fpp));
+ *fpp = NULL;
+ }
+
+ fs_dprintf(("<8> levels = %d\n", levels));
+ levels++;
+ }
+
+ return (levels);
+}
+
+/*ARGSUSED*/
+int
+stacks_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings)
+{
+ kthread_t thr;
+ size_t stksz;
+ uintptr_t ubase, utop;
+ uintptr_t kbase, ktop;
+ uintptr_t win, sp;
+
+ fsip->fsi_failed = 0;
+ fsip->fsi_pc = 0;
+ fsip->fsi_sp = 0;
+ fsip->fsi_depth = 0;
+ fsip->fsi_overflow = 0;
+
+ bzero(&thr, sizeof (thr));
+ if (mdb_ctf_vread(&thr, "kthread_t", addr,
+ MDB_CTF_VREAD_IGNORE_ALL) == -1) {
+ if (print_warnings)
+ mdb_warn("couldn't read thread at %p\n", addr);
+ fsip->fsi_failed = FSI_FAIL_BADTHREAD;
+ return (DCMD_ERR);
+ }
+
+ fsip->fsi_sobj_ops = (uintptr_t)thr.t_sobj_ops;
+ fsip->fsi_tstate = thr.t_state;
+ fsip->fsi_panic = !!(thr.t_flag & T_PANIC);
+
+ if ((thr.t_schedflag & TS_LOAD) == 0) {
+ if (print_warnings)
+ mdb_warn("thread %p isn't in memory\n", addr);
+ fsip->fsi_failed = FSI_FAIL_NOTINMEMORY;
+ return (DCMD_ERR);
+ }
+
+ if (thr.t_stk < thr.t_stkbase) {
+ if (print_warnings)
+ mdb_warn(
+ "stack base or stack top corrupt for thread %p\n",
+ addr);
+ fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
+ return (DCMD_ERR);
+ }
+
+ kbase = (uintptr_t)thr.t_stkbase;
+ ktop = (uintptr_t)thr.t_stk;
+ stksz = ktop - kbase;
+
+#ifdef __amd64
+ /*
+ * The stack on amd64 is intentionally misaligned, so ignore the top
+ * half-frame. See thread_stk_init(). When handling traps, the frame
+ * is automatically aligned by the hardware, so we only alter ktop if
+ * needed.
+ */
+ if ((ktop & (STACK_ALIGN - 1)) != 0)
+ ktop -= STACK_ENTRY_ALIGN;
+#endif
+
+ /*
+ * If the stack size is larger than a meg, assume that it's bogus.
+ */
+ if (stksz > TOO_BIG_FOR_A_STACK) {
+ if (print_warnings)
+ mdb_warn("stack size for thread %p is too big to be "
+ "reasonable\n", addr);
+ fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
+ return (DCMD_ERR);
+ }
+
+ /*
+ * This could be (and was) a UM_GC allocation. Unfortunately,
+ * stksz tends to be very large. As currently implemented, dcmds
+ * invoked as part of pipelines don't have their UM_GC-allocated
+ * memory freed until the pipeline completes. With stksz in the
+ * neighborhood of 20k, the popular ::walk thread |::findstack
+ * pipeline can easily run memory-constrained debuggers (kmdb) out
+ * of memory. This can be changed back to a gc-able allocation when
+ * the debugger is changed to free UM_GC memory more promptly.
+ */
+ ubase = (uintptr_t)mdb_alloc(stksz, UM_SLEEP);
+ utop = ubase + stksz;
+ if (mdb_vread((caddr_t)ubase, stksz, kbase) != stksz) {
+ mdb_free((void *)ubase, stksz);
+ if (print_warnings)
+ mdb_warn("couldn't read entire stack for thread %p\n",
+ addr);
+ fsip->fsi_failed = FSI_FAIL_THREADCORRUPT;
+ return (DCMD_ERR);
+ }
+
+ /*
+ * Try the saved %sp first, if it looks reasonable.
+ */
+ sp = KTOU((uintptr_t)thr.t_sp + STACK_BIAS);
+ if (sp >= ubase && sp <= utop) {
+ if (crawl(sp, kbase, ktop, ubase, 0, fsip) == CRAWL_FOUNDALL) {
+ fsip->fsi_sp = (uintptr_t)thr.t_sp;
+#if !defined(__i386)
+ fsip->fsi_pc = (uintptr_t)thr.t_pc;
+#endif
+ goto found;
+ }
+ }
+
+ /*
+ * Now walk through the whole stack, starting at the base,
+ * trying every possible "window".
+ */
+ for (win = ubase;
+ win + sizeof (struct rwindow) <= utop;
+ win += sizeof (struct rwindow *)) {
+ if (crawl(win, kbase, ktop, ubase, 1, fsip) == CRAWL_FOUNDALL) {
+ fsip->fsi_sp = UTOK(win) - STACK_BIAS;
+ goto found;
+ }
+ }
+
+ /*
+ * We didn't conclusively find the stack. So we'll take another lap,
+ * and print out anything that looks possible.
+ */
+ if (print_warnings)
+ mdb_printf("Possible stack pointers for thread %p:\n", addr);
+ (void) mdb_vread((caddr_t)ubase, stksz, kbase);
+
+ for (win = ubase;
+ win + sizeof (struct rwindow) <= utop;
+ win += sizeof (struct rwindow *)) {
+ uintptr_t fp = ((struct rwindow *)win)->rw_fp;
+ int levels;
+
+ if ((levels = crawl(win, kbase, ktop, ubase, 1, fsip)) > 1) {
+ if (print_warnings)
+ mdb_printf(" %p (%d)\n", fp, levels);
+ } else if (levels == CRAWL_FOUNDALL) {
+ /*
+ * If this is a live system, the stack could change
+ * between the two mdb_vread(ubase, utop, kbase)'s,
+ * and we could have a fully valid stack here.
+ */
+ fsip->fsi_sp = UTOK(win) - STACK_BIAS;
+ goto found;
+ }
+ }
+
+ fsip->fsi_depth = 0;
+ fsip->fsi_overflow = 0;
+ fsip->fsi_failed = FSI_FAIL_STACKNOTFOUND;
+
+ mdb_free((void *)ubase, stksz);
+ return (DCMD_ERR);
+found:
+ mdb_free((void *)ubase, stksz);
+ return (DCMD_OK);
+}
+
+void
+stacks_findstack_cleanup()
+{}
+
+/*ARGSUSED*/
+int
+stacks_module_cb(uintptr_t addr, const modctl_t *mp, stacks_module_t *smp)
+{
+ char mod_modname[MODMAXNAMELEN + 1];
+
+ if (!mp->mod_modname)
+ return (WALK_NEXT);
+
+ if (mdb_readstr(mod_modname, sizeof (mod_modname),
+ (uintptr_t)mp->mod_modname) == -1) {
+ mdb_warn("failed to read mod_modname in \"modctl\" walk");
+ return (WALK_ERR);
+ }
+
+ if (strcmp(smp->sm_name, mod_modname))
+ return (WALK_NEXT);
+
+ smp->sm_text = (uintptr_t)mp->mod_text;
+ smp->sm_size = mp->mod_text_size;
+
+ return (WALK_DONE);
+}
+
+int
+stacks_module(stacks_module_t *smp)
+{
+ if (mdb_walk("modctl", (mdb_walk_cb_t)stacks_module_cb, smp) != 0) {
+ mdb_warn("cannot walk \"modctl\"");
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static void
+print_sobj_help(int type, const char *name, const char *ops_name, void *ign)
+{
+ mdb_printf(" %s", name);
+}
+
+/*ARGSUSED*/
+static void
+print_tstate_help(uint_t state, const char *name, void *ignored)
+{
+ mdb_printf(" %s", name);
+}
+
+void
+stacks_help(void)
+{
+ mdb_printf(
+"::stacks processes all of the thread stacks on the system, grouping\n"
+"together threads which have the same:\n"
+"\n"
+" * Thread state,\n"
+" * Sync object type, and\n"
+" * PCs in their stack trace.\n"
+"\n"
+"The default output (no address or options) is just a dump of the thread\n"
+"groups in the system. For a view of active threads, use \"::stacks -i\",\n"
+"which filters out FREE threads (interrupt threads which are currently\n"
+"inactive) and threads sleeping on a CV. (Note that those threads may still\n"
+"be noteworthy; this is just for a first glance.) More general filtering\n"
+"options are described below, in the \"FILTERS\" section.\n"
+"\n"
+"::stacks can be used in a pipeline. The input to ::stacks is one or more\n"
+"thread pointers. For example, to get a summary of threads in a process,\n"
+"you can do:\n"
+"\n"
+" %<b>procp%</b>::walk thread | ::stacks\n"
+"\n"
+"When output into a pipe, ::stacks prints all of the threads input,\n"
+"filtered by the given filtering options. This means that multiple\n"
+"::stacks invocations can be piped together to achieve more complicated\n"
+"filters. For example, to get threads which have both 'fop_read' and\n"
+"'cv_wait_sig_swap' in their stack trace, you could do:\n"
+"\n"
+" ::stacks -c fop_read | ::stacks -c cv_wait_sig_swap_core\n"
+"\n"
+"To get the full list of threads in each group, use the '-a' flag:\n"
+"\n"
+" ::stacks -a\n"
+"\n");
+ mdb_dec_indent(2);
+ mdb_printf("%<b>OPTIONS%</b>\n");
+ mdb_inc_indent(2);
+ mdb_printf("%s",
+" -a Print all of the grouped threads, instead of just a count.\n"
+" -f Force a re-run of the thread stack gathering.\n"
+" -v Be verbose about thread stack gathering.\n"
+"\n");
+ mdb_dec_indent(2);
+ mdb_printf("%<b>FILTERS%</b>\n");
+ mdb_inc_indent(2);
+ mdb_printf("%s",
+" -i Show active threads; equivalent to '-S CV -T FREE'.\n"
+" -c func[+offset]\n"
+" Only print threads whose stacks contain func/func+offset.\n"
+" -C func[+offset]\n"
+" Only print threads whose stacks do not contain func/func+offset.\n"
+" -m module\n"
+" Only print threads whose stacks contain functions from module.\n"
+" -M module\n"
+" Only print threads whose stacks do not contain functions from\n"
+" module.\n"
+" -s {type | ALL}\n"
+" Only print threads which are on a 'type' synchronization object\n"
+" (SOBJ).\n"
+" -S {type | ALL}\n"
+" Only print threads which are not on a 'type' SOBJ.\n"
+" -t tstate\n"
+" Only print threads which are in thread state 'tstate'.\n"
+" -T tstate\n"
+" Only print threads which are not in thread state 'tstate'.\n"
+"\n");
+ mdb_printf(" SOBJ types:");
+ sobj_type_walk(print_sobj_help, NULL);
+ mdb_printf("\n");
+ mdb_printf("Thread states:");
+ thread_walk_states(print_tstate_help, NULL);
+ mdb_printf(" panic\n");
+}
diff --git a/usr/src/cmd/mdb/common/modules/libc/findstack_subr.c b/usr/src/cmd/mdb/common/modules/libc/findstack_subr.c
new file mode 100644
index 0000000000..01e4729475
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/libc/findstack_subr.c
@@ -0,0 +1,382 @@
+/*
+ * 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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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
+ */
+
+/*
+ * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#include <mdb/mdb_modapi.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/avl.h>
+#include <sys/lwp.h>
+#include <thr_uberdata.h>
+#include <stddef.h>
+#include "findstack.h"
+
+#if defined(__i386) || defined(__amd64)
+struct rwindow {
+ uintptr_t rw_fp;
+ uintptr_t rw_rtn;
+};
+#endif
+
+#ifndef STACK_BIAS
+#define STACK_BIAS 0
+#endif
+
+#ifdef __amd64
+#define STACKS_REGS_FP "rbp"
+#define STACKS_REGS_RC "rip"
+#else
+#ifdef __i386
+#define STACKS_REGS_FP "ebp"
+#define STACKS_REGS_RC "eip"
+#else
+#define STACKS_REGS_FP "fp"
+#define STACKS_REGS_RC "pc"
+#endif
+#endif
+
+#define STACKS_SOBJ_MX (uintptr_t)"MX"
+#define STACKS_SOBJ_CV (uintptr_t)"CV"
+
+int
+thread_text_to_state(const char *state, uint_t *out)
+{
+ if (strcmp(state, "PARKED") == 0) {
+ *out = B_TRUE;
+ } else if (strcmp(state, "UNPARKED") == 0) {
+ *out = B_FALSE;
+ } else if (strcmp(state, "FREE") == 0) {
+ /*
+ * When run with "-i", ::stacks filters out "FREE" threads.
+ * We therefore need to recognize "FREE", and set it to a
+ * value that will never match fsi_tstate.
+ */
+ *out = UINT_MAX;
+ } else {
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+thread_state_to_text(uint_t state, char *out, size_t out_sz)
+{
+ (void) snprintf(out, out_sz, state ? "PARKED" : "UNPARKED");
+}
+
+int
+sobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out)
+{
+ if (strcmp(name, "MX") == 0) {
+ *sobj_ops_out = STACKS_SOBJ_MX;
+ } else if (strcmp(name, "CV") == 0) {
+ *sobj_ops_out = STACKS_SOBJ_CV;
+ } else {
+ mdb_warn("sobj \"%s\" not recognized\n", name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+void
+sobj_ops_to_text(uintptr_t addr, char *out, size_t sz)
+{
+ (void) snprintf(out, sz, "%s", addr == NULL ? "<none>" : (char *)addr);
+}
+
+static int
+stacks_module_callback(mdb_object_t *obj, void *arg)
+{
+ stacks_module_t *smp = arg;
+ boolean_t match = (strcmp(obj->obj_name, smp->sm_name) == 0);
+ char *suffix = ".so";
+ const char *s, *next;
+ size_t len;
+
+ if (smp->sm_size != 0)
+ return (0);
+
+ /*
+ * It doesn't match the name, but -- for convenience -- we want to
+ * allow matches before ".so.[suffix]". An aside: why doesn't
+ * strrstr() exist? (Don't google that. I'm serious, don't do it.
+ * If you do, and you read the thread of "why doesn't strrstr() exist?"
+ * circa 2005 you will see things that you will NEVER be able to unsee!)
+ */
+ if (!match && (s = strstr(obj->obj_name, suffix)) != NULL) {
+ while ((next = strstr(s + 1, suffix)) != NULL) {
+ s = next;
+ continue;
+ }
+
+ len = s - obj->obj_name;
+
+ match = (strncmp(smp->sm_name, obj->obj_name, len) == 0 &&
+ smp->sm_name[len] == '\0');
+ }
+
+ /*
+ * If we have a library that has the libc directory in the path, we
+ * want to match against anything that would match libc.so.1. (This
+ * is necessary to be able to easily deal with libc implementations
+ * that have alternate hardware capabilities.)
+ */
+ if (!match && strstr(obj->obj_fullname, "/libc/") != NULL) {
+ mdb_object_t libc = *obj;
+
+ libc.obj_name = "libc.so.1";
+ libc.obj_fullname = "";
+
+ return (stacks_module_callback(&libc, arg));
+ }
+
+ if (match) {
+ smp->sm_text = obj->obj_base;
+ smp->sm_size = obj->obj_size;
+ }
+
+ return (0);
+}
+
+int
+stacks_module(stacks_module_t *smp)
+{
+ if (mdb_object_iter(stacks_module_callback, smp) != 0)
+ return (-1);
+
+ return (0);
+}
+
+typedef struct stacks_ulwp {
+ avl_node_t sulwp_node;
+ lwpid_t sulwp_id;
+ uintptr_t sulwp_addr;
+} stacks_ulwp_t;
+
+boolean_t stacks_ulwp_initialized;
+avl_tree_t stacks_ulwp_byid;
+
+/*ARGSUSED*/
+int
+stacks_ulwp_walk(uintptr_t addr, ulwp_t *ulwp, void *ignored)
+{
+ stacks_ulwp_t *sulwp = mdb_alloc(sizeof (stacks_ulwp_t), UM_SLEEP);
+
+ sulwp->sulwp_id = ulwp->ul_lwpid;
+ sulwp->sulwp_addr = addr;
+
+ if (avl_find(&stacks_ulwp_byid, sulwp, NULL) != NULL) {
+ mdb_warn("found multiple LWPs with ID %d!", ulwp->ul_lwpid);
+ return (WALK_ERR);
+ }
+
+ avl_add(&stacks_ulwp_byid, sulwp);
+
+ return (WALK_NEXT);
+}
+
+static int
+stacks_ulwp_compare(const void *l, const void *r)
+{
+ const stacks_ulwp_t *lhs = l;
+ const stacks_ulwp_t *rhs = r;
+
+ if (lhs->sulwp_id > rhs->sulwp_id)
+ return (1);
+
+ if (lhs->sulwp_id < rhs->sulwp_id)
+ return (-1);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+int
+stacks_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings)
+{
+ mdb_reg_t reg;
+ uintptr_t fp;
+ struct rwindow frame;
+ avl_tree_t *tree = &stacks_ulwp_byid;
+ stacks_ulwp_t *sulwp, cmp;
+ ulwp_t ulwp;
+
+ fsip->fsi_failed = 0;
+ fsip->fsi_pc = 0;
+ fsip->fsi_sp = 0;
+ fsip->fsi_depth = 0;
+ fsip->fsi_overflow = 0;
+
+ if (!stacks_ulwp_initialized) {
+ avl_create(tree, stacks_ulwp_compare, sizeof (stacks_ulwp_t),
+ offsetof(stacks_ulwp_t, sulwp_node));
+
+ if (mdb_walk("ulwp",
+ (mdb_walk_cb_t)stacks_ulwp_walk, NULL) != 0) {
+ mdb_warn("couldn't walk 'ulwp'");
+ return (-1);
+ }
+
+ stacks_ulwp_initialized = B_TRUE;
+ }
+
+ bzero(&cmp, sizeof (cmp));
+ cmp.sulwp_id = (lwpid_t)addr;
+
+ if ((sulwp = avl_find(tree, &cmp, NULL)) == NULL) {
+ mdb_warn("couldn't find ulwp_t for tid %d\n", cmp.sulwp_id);
+ return (-1);
+ }
+
+ if (mdb_vread(&ulwp, sizeof (ulwp), sulwp->sulwp_addr) == -1) {
+ mdb_warn("couldn't read ulwp_t for tid %d at %p",
+ cmp.sulwp_id, sulwp->sulwp_addr);
+ return (-1);
+ }
+
+ fsip->fsi_tstate = ulwp.ul_sleepq != NULL;
+ fsip->fsi_sobj_ops = (uintptr_t)(ulwp.ul_sleepq == NULL ? NULL :
+ (ulwp.ul_qtype == MX ? STACKS_SOBJ_MX : STACKS_SOBJ_CV));
+
+ if (mdb_getareg(addr, STACKS_REGS_FP, &reg) != 0) {
+ mdb_warn("couldn't read frame pointer for thread 0x%p", addr);
+ return (-1);
+ }
+
+ fsip->fsi_sp = fp = (uintptr_t)reg;
+
+#if !defined(__i386)
+ if (mdb_getareg(addr, STACKS_REGS_RC, &reg) != 0) {
+ mdb_warn("couldn't read program counter for thread 0x%p", addr);
+ return (-1);
+ }
+
+ fsip->fsi_pc = (uintptr_t)reg;
+#endif
+
+ while (fp != NULL) {
+ if (mdb_vread(&frame, sizeof (frame), fp) == -1) {
+ mdb_warn("couldn't read frame for thread 0x%p at %p",
+ addr, fp);
+ return (-1);
+ }
+
+ if (frame.rw_rtn == NULL)
+ break;
+
+ if (fsip->fsi_depth < fsip->fsi_max_depth) {
+ fsip->fsi_stack[fsip->fsi_depth++] = frame.rw_rtn;
+ } else {
+ fsip->fsi_overflow = 1;
+ break;
+ }
+
+ fp = frame.rw_fp + STACK_BIAS;
+ }
+
+ return (0);
+}
+
+void
+stacks_findstack_cleanup()
+{
+ avl_tree_t *tree = &stacks_ulwp_byid;
+ void *cookie = NULL;
+ stacks_ulwp_t *sulwp;
+
+ if (!stacks_ulwp_initialized)
+ return;
+
+ while ((sulwp = avl_destroy_nodes(tree, &cookie)) != NULL)
+ mdb_free(sulwp, sizeof (stacks_ulwp_t));
+
+ bzero(tree, sizeof (*tree));
+ stacks_ulwp_initialized = B_FALSE;
+}
+
+void
+stacks_help(void)
+{
+ mdb_printf(
+"::stacks processes all of the thread stacks in the process, grouping\n"
+"together threads which have the same:\n"
+"\n"
+" * Thread state,\n"
+" * Sync object type, and\n"
+" * PCs in their stack trace.\n"
+"\n"
+"The default output (no address or options) is just a dump of the thread\n"
+"groups in the process. For a view of active threads, use \"::stacks -i\",\n"
+"which filters out threads sleeping on a CV. More general filtering options\n"
+"are described below, in the \"FILTERS\" section.\n"
+"\n"
+"::stacks can be used in a pipeline. The input to ::stacks is one or more\n"
+"thread IDs. When output into a pipe, ::stacks prints all of the threads \n"
+"input, filtered by the given filtering options. This means that multiple\n"
+"::stacks invocations can be piped together to achieve more complicated\n"
+"filters. For example, to get threads which have both '__door_return' and\n"
+"'mutex_lock' in their stack trace, you could do:\n"
+"\n"
+" ::stacks -c __door_return | ::stacks -c mutex_lock\n"
+"\n"
+"To get the full list of threads in each group, use the '-a' flag:\n"
+"\n"
+" ::stacks -a\n"
+"\n");
+ mdb_dec_indent(2);
+ mdb_printf("%<b>OPTIONS%</b>\n");
+ mdb_inc_indent(2);
+ mdb_printf("%s",
+" -a Print all of the grouped threads, instead of just a count.\n"
+" -f Force a re-run of the thread stack gathering.\n"
+" -v Be verbose about thread stack gathering.\n"
+"\n");
+ mdb_dec_indent(2);
+ mdb_printf("%<b>FILTERS%</b>\n");
+ mdb_inc_indent(2);
+ mdb_printf("%s",
+" -i Show active threads; equivalent to '-S CV'.\n"
+" -c func[+offset]\n"
+" Only print threads whose stacks contain func/func+offset.\n"
+" -C func[+offset]\n"
+" Only print threads whose stacks do not contain func/func+offset.\n"
+" -m module\n"
+" Only print threads whose stacks contain functions from module.\n"
+" -M module\n"
+" Only print threads whose stacks do not contain functions from\n"
+" module.\n"
+" -s {type | ALL}\n"
+" Only print threads which are on a 'type' synchronization object\n"
+" (SOBJ).\n"
+" -S {type | ALL}\n"
+" Only print threads which are not on a 'type' SOBJ.\n"
+" -t tstate\n"
+" Only print threads which are in thread state 'tstate'.\n"
+" -T tstate\n"
+" Only print threads which are not in thread state 'tstate'.\n"
+"\n");
+}
diff --git a/usr/src/cmd/mdb/common/modules/libc/libc.c b/usr/src/cmd/mdb/common/modules/libc/libc.c
index 5d0debcedf..1032977d05 100644
--- a/usr/src/cmd/mdb/common/modules/libc/libc.c
+++ b/usr/src/cmd/mdb/common/modules/libc/libc.c
@@ -32,6 +32,7 @@
#include <setjmp.h>
#include <string.h>
#include <thr_uberdata.h>
+#include "findstack.h"
static const char *
stack_flags(const stack_t *sp)
@@ -981,10 +982,76 @@ whatis_run_ulwps(mdb_whatis_t *w, void *arg)
* ==================== threads ==========================
*/
+int
+stacks_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ int rval = stacks(addr, flags, argc, argv);
+
+ /*
+ * For the user-level variant of ::stacks, we don't bother caching
+ * state, as even a very large program is unlikely to compare to the
+ * kernel in terms of number of threads. (And if you find yourself
+ * here in anger, frustrated about how long ::stacks is running on
+ * your galactically complicated zillion-thread program, hopefully
+ * you will find some solace in the irony. Okay, probably not...)
+ */
+ stacks_cleanup(B_TRUE);
+ return (rval);
+}
+
+typedef struct tid2ulwp_walk {
+ lwpid_t t2u_tid;
+ uintptr_t t2u_lwp;
+ boolean_t t2u_found;
+} tid2ulwp_walk_t;
+
+/*ARGSUSED*/
+static int
+tid2ulwp_walk(uintptr_t addr, ulwp_t *ulwp, tid2ulwp_walk_t *t2u)
+{
+ if (ulwp->ul_lwpid == t2u->t2u_tid) {
+ t2u->t2u_lwp = addr;
+ t2u->t2u_found = B_TRUE;
+ return (WALK_DONE);
+ }
+
+ return (WALK_NEXT);
+}
+
+/*ARGSUSED*/
+static int
+tid2ulwp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ tid2ulwp_walk_t t2u;
+
+ if (argc != 0)
+ return (DCMD_USAGE);
+
+ bzero(&t2u, sizeof (t2u));
+ t2u.t2u_tid = (lwpid_t)addr;
+
+ if (mdb_walk("ulwp", (mdb_walk_cb_t)tid2ulwp_walk, &t2u) != 0) {
+ mdb_warn("can't walk 'ulwp'");
+ return (DCMD_ERR);
+ }
+
+ if (!t2u.t2u_found) {
+ mdb_warn("thread ID %d not found", t2u.t2u_tid);
+ return (DCMD_ERR);
+ }
+
+ mdb_printf("%p\n", t2u.t2u_lwp);
+
+ return (DCMD_OK);
+}
+
static const mdb_dcmd_t dcmds[] = {
{ "jmp_buf", ":", "print jmp_buf contents", d_jmp_buf, NULL },
{ "sigjmp_buf", ":", "print sigjmp_buf contents", d_sigjmp_buf, NULL },
{ "siginfo", ":", "print siginfo_t structure", d_siginfo, NULL },
+ { "stacks", "?[-afiv] [-c func] [-C func] [-m module] [-M module] ",
+ "print unique thread stacks", stacks_dcmd, stacks_help },
+ { "tid2ulwp", "?", "convert TID to ulwp_t address", tid2ulwp },
{ "ucontext", ":", "print ucontext_t structure", d_ucontext, NULL },
{ "ulwp", ":", "print ulwp_t structure", d_ulwp, NULL },
{ "uberdata", ":", "print uberdata_t structure", d_uberdata, NULL },
@@ -998,6 +1065,8 @@ static const mdb_walker_t walkers[] = {
oldc_walk_init, oldc_walk_step, oldc_walk_fini, NULL },
{ "ulwps", "walk list of ulwp_t pointers",
ulwp_walk_init, ulwp_walk_step, NULL, NULL },
+ { "ulwp", "walk list of ulwp_t pointers",
+ ulwp_walk_init, ulwp_walk_step, NULL, NULL },
{ NULL }
};
diff --git a/usr/src/cmd/mdb/intel/amd64/libc/Makefile b/usr/src/cmd/mdb/intel/amd64/libc/Makefile
index e529a033f8..f535f0781d 100644
--- a/usr/src/cmd/mdb/intel/amd64/libc/Makefile
+++ b/usr/src/cmd/mdb/intel/amd64/libc/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# 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
# or http://www.opensolaris.org/os/licensing.
@@ -20,21 +19,27 @@
# CDDL HEADER END
#
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
-#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
#
MODULE = libc.so
MDBTGT = proc
-MODSRCS = libc.c
+MODSRCS_DIR = ../../../common/modules/genunix
+
+MODSRCS = \
+ libc.c \
+ findstack.c \
+ findstack_subr.c
include ../../../../Makefile.cmd
include ../../../../Makefile.cmd.64
CPPFLAGS += -I$(SRC)/lib/libc/inc
+CPPFLAGS += -I$(MODSRCS_DIR)
include ../../Makefile.amd64
include ../../../Makefile.module
+
+dmod/$(MODULE) := LDLIBS += -lavl
+
diff --git a/usr/src/cmd/mdb/intel/ia32/libc/Makefile b/usr/src/cmd/mdb/intel/ia32/libc/Makefile
index f1330e5ab2..cc630b6bcf 100644
--- a/usr/src/cmd/mdb/intel/ia32/libc/Makefile
+++ b/usr/src/cmd/mdb/intel/ia32/libc/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# 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
# or http://www.opensolaris.org/os/licensing.
@@ -20,20 +19,26 @@
# CDDL HEADER END
#
#
-# Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
-#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
#
MODULE = libc.so
MDBTGT = proc
-MODSRCS = libc.c
+MODSRCS_DIR = ../../../common/modules/genunix
+
+MODSRCS = \
+ libc.c \
+ findstack.c \
+ findstack_subr.c
include ../../../../Makefile.cmd
CPPFLAGS += -I$(SRC)/lib/libc/inc
+CPPFLAGS += -I$(MODSRCS_DIR)
include ../../Makefile.ia32
include ../../../Makefile.module
+
+dmod/$(MODULE) := LDLIBS += -lavl
+
diff --git a/usr/src/cmd/mdb/sparc/v7/libc/Makefile b/usr/src/cmd/mdb/sparc/v7/libc/Makefile
index 4993d6cf00..7d6318e87f 100644
--- a/usr/src/cmd/mdb/sparc/v7/libc/Makefile
+++ b/usr/src/cmd/mdb/sparc/v7/libc/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# 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
# or http://www.opensolaris.org/os/licensing.
@@ -20,20 +19,26 @@
# CDDL HEADER END
#
#
-# Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
-#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
#
MODULE = libc.so
MDBTGT = proc
-MODSRCS = libc.c
+MODSRCS_DIR = ../../../common/modules/genunix
+
+MODSRCS = \
+ libc.c \
+ findstack.c \
+ findstack_subr.c
include ../../../../Makefile.cmd
CPPFLAGS += -I$(SRC)/lib/libc/inc
+CPPFLAGS += -I$(MODSRCS_DIR)
include ../../Makefile.sparcv7
include ../../../Makefile.module
+
+dmod/$(MODULE) := LDLIBS += -lavl
+
diff --git a/usr/src/cmd/mdb/sparc/v9/libc/Makefile b/usr/src/cmd/mdb/sparc/v9/libc/Makefile
index 9cc4542834..76ae7461f7 100644
--- a/usr/src/cmd/mdb/sparc/v9/libc/Makefile
+++ b/usr/src/cmd/mdb/sparc/v9/libc/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# 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
# or http://www.opensolaris.org/os/licensing.
@@ -20,21 +19,27 @@
# CDDL HEADER END
#
#
-# Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
-#
-# ident "%Z%%M% %I% %E% SMI"
+# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
#
MODULE = libc.so
MDBTGT = proc
-MODSRCS = libc.c
+MODSRCS_DIR = ../../../common/modules/genunix
+
+MODSRCS = \
+ libc.c \
+ findstack.c \
+ findstack_subr.c
include ../../../../Makefile.cmd
CPPFLAGS += -I$(SRC)/lib/libc/inc
+CPPFLAGS += -I$(MODSRCS_DIR)
include ../../../../Makefile.cmd.64
include ../../Makefile.sparcv9
include ../../../Makefile.module
+
+dmod/$(MODULE) := LDLIBS += -lavl
+