summaryrefslogtreecommitdiff
path: root/usr/src/cmd/mdb/common
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/mdb/common')
-rw-r--r--usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c15
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_kvm.c12
-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/mdb/mdb_param.h14
-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/devinfo.c7
-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/genunix/genunix.c58
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/memory.c474
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/memory.h12
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/vfs.c5
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/zone.c163
-rw-r--r--usr/src/cmd/mdb/common/modules/libc/findstack_subr.c382
-rw-r--r--usr/src/cmd/mdb/common/modules/libc/libc.c76
-rw-r--r--usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c82
-rw-r--r--usr/src/cmd/mdb/common/modules/pmcs/pmcs.c250
-rw-r--r--usr/src/cmd/mdb/common/modules/sctp/sctp.c124
-rw-r--r--usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c139
-rw-r--r--usr/src/cmd/mdb/common/modules/svc.startd/startd.c82
25 files changed, 2350 insertions, 658 deletions
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c
index 93bbece1c8..761b448f4f 100644
--- a/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_kvm.c
@@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 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 <kmdb/kmdb_kvm.h>
@@ -546,6 +545,7 @@ kmt_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
kmt_data_t *kmt = mdb.m_target->t_data;
struct utsname uts;
+ char uuid[37];
kreg_t tt;
if (mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT, &uts, sizeof (uts),
@@ -561,6 +561,17 @@ kmt_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
mdb_printf("operating system: %s %s (%s)\n",
uts.release, uts.version, uts.machine);
+ if (mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT, uuid, sizeof (uuid),
+ "genunix", "dump_osimage_uuid") != sizeof (uuid)) {
+ warn("failed to read 'dump_osimage_uuid' string from kernel\n");
+ (void) strcpy(uuid, "(error)");
+ } else if (*uuid == '\0') {
+ (void) strcpy(uuid, "(not set)");
+ } else if (uuid[36] != '\0') {
+ (void) strcpy(uuid, "(invalid)");
+ }
+ mdb_printf("image uuid: %s\n", uuid);
+
if (kmt->kmt_cpu != NULL) {
mdb_printf("CPU-specific support: %s\n",
kmt_cpu_name(kmt->kmt_cpu));
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
index 5d0da621cd..4b368e357b 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_kvm.c
@@ -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.
*/
/*
@@ -485,9 +484,18 @@ kt_status_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
if (kt->k_dumphdr) {
dumphdr_t *dh = kt->k_dumphdr;
+ mdb_printf("image uuid: %s\n", dh->dump_uuid[0] != '\0' ?
+ dh->dump_uuid : "(not set)");
mdb_printf("panic message: %s\n", dh->dump_panicstring);
kt->k_dump_print_content(dh, kt->k_dumpcontent);
+ } else {
+ char uuid[37];
+
+ if (mdb_readsym(uuid, 37, "dump_osimage_uuid") == 37 &&
+ uuid[36] == '\0') {
+ mdb_printf("image uuid: %s\n", uuid);
+ }
}
return (DCMD_OK);
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/mdb/mdb_param.h b/usr/src/cmd/mdb/common/mdb/mdb_param.h
index 330011ea1b..b5e64d34b0 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_param.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_param.h
@@ -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,15 +19,12 @@
* CDDL HEADER END
*/
/*
- * Copyright 2004 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_PARAM_H
#define _MDB_PARAM_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -112,6 +108,8 @@ extern uintptr_t _mdb_ks_argsbase;
extern unsigned long _mdb_ks_msg_bsize;
extern unsigned long _mdb_ks_defaultstksz;
extern int _mdb_ks_ncpu;
+extern int _mdb_ks_ncpu_log2;
+extern int _mdb_ks_ncpu_p2;
/*
* Now derive all the macros using the global variables defined in
@@ -134,6 +132,8 @@ extern int _mdb_ks_ncpu;
#define MSG_BSIZE _mdb_ks_msg_bsize
#define DEFAULTSTKSZ _mdb_ks_defaultstksz
#define NCPU _mdb_ks_ncpu
+#define NCPU_LOG2 _mdb_ks_ncpu_log2
+#define NCPU_P2 _mdb_ks_ncpu_p2
#define _STRING_H /* Do not re-include <string.h> */
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/devinfo.c b/usr/src/cmd/mdb/common/modules/genunix/devinfo.c
index 5d05dbbf9b..513103a682 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/devinfo.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/devinfo.c
@@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/types.h>
@@ -1108,8 +1107,8 @@ devinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
DEVI_ATTACHED_CHILDREN, DEVI_ATTACHED_CHILDREN},
{ "BRANCH_HELD", DEVI_BRANCH_HELD, DEVI_BRANCH_HELD },
{ "NO_BIND", DEVI_NO_BIND, DEVI_NO_BIND },
- { "DEVI_REGISTERED_DEVID",
- DEVI_REGISTERED_DEVID, DEVI_REGISTERED_DEVID },
+ { "DEVI_CACHED_DEVID",
+ DEVI_CACHED_DEVID, DEVI_CACHED_DEVID },
{ "PHCI_SIGNALS_VHCI",
DEVI_PHCI_SIGNALS_VHCI,
DEVI_PHCI_SIGNALS_VHCI },
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/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
index 968f0284c4..3004495d6b 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
@@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2010 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_param.h>
@@ -3798,17 +3797,28 @@ panicinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
static int
time(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
+ uint_t opt_dec = FALSE;
uint_t opt_lbolt = FALSE;
+ uint_t opt_hex = FALSE;
+ const char *fmt;
+ hrtime_t result;
- if (mdb_getopts(argc, argv, 'l', MDB_OPT_SETBITS, TRUE, &opt_lbolt,
+ if (mdb_getopts(argc, argv,
+ 'd', MDB_OPT_SETBITS, TRUE, &opt_dec,
+ 'l', MDB_OPT_SETBITS, TRUE, &opt_lbolt,
+ 'x', MDB_OPT_SETBITS, TRUE, &opt_hex,
NULL) != argc)
return (DCMD_USAGE);
- if (opt_lbolt)
- mdb_printf("%ld\n", mdb_get_lbolt());
- else
- mdb_printf("%lld\n", mdb_gethrtime());
+ if (opt_dec && opt_hex)
+ return (DCMD_USAGE);
+
+ result = opt_lbolt ? mdb_get_lbolt() : mdb_gethrtime();
+ fmt =
+ opt_hex ? "0x%llx\n" :
+ opt_dec ? "0t%lld\n" : "%#llr\n";
+ mdb_printf(fmt, result);
return (DCMD_OK);
}
@@ -3821,7 +3831,9 @@ time_help(void)
"time if inspecting one; or the running hires time if we're \n"
"looking at a live system.\n\n"
"Switches:\n"
- " -l prints the number of clock ticks since system boot\n");
+ " -d report times in decimal\n"
+ " -l prints the number of clock ticks since system boot\n"
+ " -x report times in hexadecimal\n");
}
static const mdb_dcmd_t dcmds[] = {
@@ -3862,7 +3874,7 @@ static const mdb_dcmd_t dcmds[] = {
"print sysevent subclass list", sysevent_subclass_list},
{ "system", NULL, "print contents of /etc/system file", sysfile },
{ "task", NULL, "display kernel task(s)", task },
- { "time", "[-l]", "display system time", time, time_help },
+ { "time", "[-dlx]", "display system time", time, time_help },
{ "vnode2path", ":[-F]", "vnode address to pathname", vnode2path },
{ "whereopen", ":", "given a vnode, dumps procs which have it open",
whereopen },
@@ -4159,7 +4171,7 @@ static const mdb_dcmd_t dcmds[] = {
pfiles_help },
/* from zone.c */
- { "zone", "?", "display kernel zone(s)", zoneprt },
+ { "zone", "?[-r [-v]]", "display kernel zone(s)", zoneprt },
{ "zsd", ":[-v] [zsd_key]", "display zone-specific-data entries for "
"selected zones", zsd },
@@ -4397,14 +4409,36 @@ static const mdb_walker_t walkers[] = {
/* from memory.c */
{ "allpages", "walk all pages, including free pages",
allpages_walk_init, allpages_walk_step, allpages_walk_fini },
- { "anon", "given an amp, list of anon structures",
- anon_walk_init, anon_walk_step, anon_walk_fini },
+ { "anon", "given an amp, list allocated anon structures",
+ anon_walk_init, anon_walk_step, anon_walk_fini,
+ ANON_WALK_ALLOC },
+ { "anon_all", "given an amp, list contents of all anon slots",
+ anon_walk_init, anon_walk_step, anon_walk_fini,
+ ANON_WALK_ALL },
{ "memlist", "walk specified memlist",
NULL, memlist_walk_step, NULL },
{ "page", "walk all pages, or those from the specified vnode",
page_walk_init, page_walk_step, page_walk_fini },
{ "seg", "given an as, list of segments",
seg_walk_init, avl_walk_step, avl_walk_fini },
+ { "segvn_anon",
+ "given a struct segvn_data, list allocated anon structures",
+ segvn_anon_walk_init, anon_walk_step, anon_walk_fini,
+ ANON_WALK_ALLOC },
+ { "segvn_anon_all",
+ "given a struct segvn_data, list contents of all anon slots",
+ segvn_anon_walk_init, anon_walk_step, anon_walk_fini,
+ ANON_WALK_ALL },
+ { "segvn_pages",
+ "given a struct segvn_data, list resident pages in "
+ "offset order",
+ segvn_pages_walk_init, segvn_pages_walk_step,
+ segvn_pages_walk_fini, SEGVN_PAGES_RESIDENT },
+ { "segvn_pages_all",
+ "for each offset in a struct segvn_data, give page_t pointer "
+ "(if resident), or NULL.",
+ segvn_pages_walk_init, segvn_pages_walk_step,
+ segvn_pages_walk_fini, SEGVN_PAGES_ALL },
{ "swapinfo", "walk swapinfo structures",
swap_walk_init, swap_walk_step, NULL },
diff --git a/usr/src/cmd/mdb/common/modules/genunix/memory.c b/usr/src/cmd/mdb/common/modules/genunix/memory.c
index e9ae1553ea..528040b9e5 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/memory.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/memory.c
@@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <mdb/mdb_param.h>
@@ -45,6 +44,7 @@
#endif
#include "avl.h"
+#include "memory.h"
/*
* Page walker.
@@ -965,13 +965,11 @@ seg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
/*ARGSUSED*/
static int
-pmap_walk_anon(uintptr_t addr, const struct anon *anon, int *nres)
+pmap_walk_count_pages(uintptr_t addr, const void *data, void *out)
{
- uintptr_t pp =
- mdb_page_lookup((uintptr_t)anon->an_vp, (u_offset_t)anon->an_off);
+ pgcnt_t *nres = out;
- if (pp != NULL)
- (*nres)++;
+ (*nres)++;
return (WALK_NEXT);
}
@@ -982,35 +980,34 @@ pmap_walk_seg(uintptr_t addr, const struct seg *seg, uintptr_t segvn)
mdb_printf("%0?p %0?p %7dk", addr, seg->s_base, seg->s_size / 1024);
- if (segvn == (uintptr_t)seg->s_ops) {
+ if (segvn == (uintptr_t)seg->s_ops && seg->s_data != NULL) {
struct segvn_data svn;
- int nres = 0;
+ pgcnt_t nres = 0;
+ svn.vp = NULL;
(void) mdb_vread(&svn, sizeof (svn), (uintptr_t)seg->s_data);
- if (svn.amp == NULL) {
- mdb_printf(" %8s", "");
- goto drive_on;
- }
-
/*
- * We've got an amp for this segment; walk through
- * the amp, and determine mappings.
+ * Use the segvn_pages walker to find all of the in-core pages
+ * for this mapping.
*/
- if (mdb_pwalk("anon", (mdb_walk_cb_t)pmap_walk_anon,
- &nres, (uintptr_t)svn.amp) == -1)
- mdb_warn("failed to walk anon (amp=%p)", svn.amp);
-
- mdb_printf(" %7dk", (nres * PAGESIZE) / 1024);
-drive_on:
+ if (mdb_pwalk("segvn_pages", pmap_walk_count_pages, &nres,
+ (uintptr_t)seg->s_data) == -1) {
+ mdb_warn("failed to walk segvn_pages (s_data=%p)",
+ seg->s_data);
+ }
+ mdb_printf(" %7ldk", (nres * PAGESIZE) / 1024);
if (svn.vp != NULL) {
char buf[29];
mdb_vnode2path((uintptr_t)svn.vp, buf, sizeof (buf));
mdb_printf(" %s", buf);
- } else
+ } else {
mdb_printf(" [ anon ]");
+ }
+ } else {
+ mdb_printf(" %8s [ &%a ]", "?", seg->s_ops);
}
mdb_printf("\n");
@@ -1022,9 +1019,10 @@ pmap_walk_seg_quick(uintptr_t addr, const struct seg *seg, uintptr_t segvn)
{
mdb_printf("%0?p %0?p %7dk", addr, seg->s_base, seg->s_size / 1024);
- if (segvn == (uintptr_t)seg->s_ops) {
+ if (segvn == (uintptr_t)seg->s_ops && seg->s_data != NULL) {
struct segvn_data svn;
+ svn.vp = NULL;
(void) mdb_vread(&svn, sizeof (svn), (uintptr_t)seg->s_data);
if (svn.vp != NULL) {
@@ -1032,6 +1030,8 @@ pmap_walk_seg_quick(uintptr_t addr, const struct seg *seg, uintptr_t segvn)
} else {
mdb_printf(" [ anon ]");
}
+ } else {
+ mdb_printf(" [ &%a ]", seg->s_ops);
}
mdb_printf("\n");
@@ -1086,15 +1086,19 @@ pmap(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
typedef struct anon_walk_data {
uintptr_t *aw_levone;
uintptr_t *aw_levtwo;
- int aw_nlevone;
- int aw_levone_ndx;
- int aw_levtwo_ndx;
+ size_t aw_minslot;
+ size_t aw_maxslot;
+ pgcnt_t aw_nlevone;
+ pgcnt_t aw_levone_ndx;
+ size_t aw_levtwo_ndx;
+ struct anon_map *aw_ampp;
struct anon_map aw_amp;
- struct anon_hdr aw_ahp;
+ struct anon_hdr aw_ahp;
+ int aw_all; /* report all anon pointers, even NULLs */
} anon_walk_data_t;
int
-anon_walk_init(mdb_walk_state_t *wsp)
+anon_walk_init_common(mdb_walk_state_t *wsp, ulong_t minslot, ulong_t maxslot)
{
anon_walk_data_t *aw;
@@ -1104,6 +1108,7 @@ anon_walk_init(mdb_walk_state_t *wsp)
}
aw = mdb_alloc(sizeof (anon_walk_data_t), UM_SLEEP);
+ aw->aw_ampp = (struct anon_map *)wsp->walk_addr;
if (mdb_vread(&aw->aw_amp, sizeof (aw->aw_amp), wsp->walk_addr) == -1) {
mdb_warn("failed to read anon map at %p", wsp->walk_addr);
@@ -1118,39 +1123,33 @@ anon_walk_init(mdb_walk_state_t *wsp)
return (WALK_ERR);
}
+ /* update min and maxslot with the given constraints */
+ maxslot = MIN(maxslot, aw->aw_ahp.size);
+ minslot = MIN(minslot, maxslot);
+
if (aw->aw_ahp.size <= ANON_CHUNK_SIZE ||
(aw->aw_ahp.flags & ANON_ALLOC_FORCE)) {
- aw->aw_nlevone = aw->aw_ahp.size;
+ aw->aw_nlevone = maxslot;
+ aw->aw_levone_ndx = minslot;
aw->aw_levtwo = NULL;
} else {
aw->aw_nlevone =
- (aw->aw_ahp.size + ANON_CHUNK_OFF) >> ANON_CHUNK_SHIFT;
+ (maxslot + ANON_CHUNK_OFF) >> ANON_CHUNK_SHIFT;
+ aw->aw_levone_ndx = 0;
aw->aw_levtwo =
mdb_zalloc(ANON_CHUNK_SIZE * sizeof (uintptr_t), UM_SLEEP);
}
aw->aw_levone =
mdb_alloc(aw->aw_nlevone * sizeof (uintptr_t), UM_SLEEP);
-
- aw->aw_levone_ndx = 0;
- aw->aw_levtwo_ndx = 0;
+ aw->aw_all = (wsp->walk_arg == ANON_WALK_ALL);
mdb_vread(aw->aw_levone, aw->aw_nlevone * sizeof (uintptr_t),
(uintptr_t)aw->aw_ahp.array_chunk);
- if (aw->aw_levtwo != NULL) {
- while (aw->aw_levone[aw->aw_levone_ndx] == NULL) {
- aw->aw_levone_ndx++;
- if (aw->aw_levone_ndx == aw->aw_nlevone) {
- mdb_warn("corrupt anon; couldn't"
- "find ptr to lev two map");
- goto out;
- }
- }
-
- mdb_vread(aw->aw_levtwo, ANON_CHUNK_SIZE * sizeof (uintptr_t),
- aw->aw_levone[aw->aw_levone_ndx]);
- }
+ aw->aw_levtwo_ndx = 0;
+ aw->aw_minslot = minslot;
+ aw->aw_maxslot = maxslot;
out:
wsp->walk_data = aw;
@@ -1160,48 +1159,78 @@ out:
int
anon_walk_step(mdb_walk_state_t *wsp)
{
- int status;
anon_walk_data_t *aw = (anon_walk_data_t *)wsp->walk_data;
struct anon anon;
uintptr_t anonptr;
+ ulong_t slot;
-again:
/*
* Once we've walked through level one, we're done.
*/
- if (aw->aw_levone_ndx == aw->aw_nlevone)
+ if (aw->aw_levone_ndx >= aw->aw_nlevone) {
return (WALK_DONE);
+ }
if (aw->aw_levtwo == NULL) {
anonptr = aw->aw_levone[aw->aw_levone_ndx];
aw->aw_levone_ndx++;
} else {
+ if (aw->aw_levtwo_ndx == 0) {
+ uintptr_t levtwoptr;
+
+ /* The first time through, skip to our first index. */
+ if (aw->aw_levone_ndx == 0) {
+ aw->aw_levone_ndx =
+ aw->aw_minslot / ANON_CHUNK_SIZE;
+ aw->aw_levtwo_ndx =
+ aw->aw_minslot % ANON_CHUNK_SIZE;
+ }
+
+ levtwoptr = (uintptr_t)aw->aw_levone[aw->aw_levone_ndx];
+
+ if (levtwoptr == NULL) {
+ if (!aw->aw_all) {
+ aw->aw_levtwo_ndx = 0;
+ aw->aw_levone_ndx++;
+ return (WALK_NEXT);
+ }
+ bzero(aw->aw_levtwo,
+ ANON_CHUNK_SIZE * sizeof (uintptr_t));
+
+ } else if (mdb_vread(aw->aw_levtwo,
+ ANON_CHUNK_SIZE * sizeof (uintptr_t), levtwoptr) ==
+ -1) {
+ mdb_warn("unable to read anon_map %p's "
+ "second-level map %d at %p",
+ aw->aw_ampp, aw->aw_levone_ndx,
+ levtwoptr);
+ return (WALK_ERR);
+ }
+ }
+ slot = aw->aw_levone_ndx * ANON_CHUNK_SIZE + aw->aw_levtwo_ndx;
anonptr = aw->aw_levtwo[aw->aw_levtwo_ndx];
- aw->aw_levtwo_ndx++;
+ /* update the indices for next time */
+ aw->aw_levtwo_ndx++;
if (aw->aw_levtwo_ndx == ANON_CHUNK_SIZE) {
aw->aw_levtwo_ndx = 0;
+ aw->aw_levone_ndx++;
+ }
- do {
- aw->aw_levone_ndx++;
-
- if (aw->aw_levone_ndx == aw->aw_nlevone)
- return (WALK_DONE);
- } while (aw->aw_levone[aw->aw_levone_ndx] == NULL);
-
- mdb_vread(aw->aw_levtwo, ANON_CHUNK_SIZE *
- sizeof (uintptr_t),
- aw->aw_levone[aw->aw_levone_ndx]);
+ /* make sure the slot # is in the requested range */
+ if (slot >= aw->aw_maxslot) {
+ return (WALK_DONE);
}
}
if (anonptr != NULL) {
mdb_vread(&anon, sizeof (anon), anonptr);
- status = wsp->walk_callback(anonptr, &anon, wsp->walk_cbdata);
- } else
- goto again;
-
- return (status);
+ return (wsp->walk_callback(anonptr, &anon, wsp->walk_cbdata));
+ }
+ if (aw->aw_all) {
+ return (wsp->walk_callback(NULL, NULL, wsp->walk_cbdata));
+ }
+ return (WALK_NEXT);
}
void
@@ -1216,6 +1245,319 @@ anon_walk_fini(mdb_walk_state_t *wsp)
mdb_free(aw, sizeof (anon_walk_data_t));
}
+int
+anon_walk_init(mdb_walk_state_t *wsp)
+{
+ return (anon_walk_init_common(wsp, 0, ULONG_MAX));
+}
+
+int
+segvn_anon_walk_init(mdb_walk_state_t *wsp)
+{
+ const uintptr_t svd_addr = wsp->walk_addr;
+ uintptr_t amp_addr;
+ uintptr_t seg_addr;
+ struct segvn_data svd;
+ struct anon_map amp;
+ struct seg seg;
+
+ if (svd_addr == NULL) {
+ mdb_warn("segvn_anon walk doesn't support global walks\n");
+ return (WALK_ERR);
+ }
+ if (mdb_vread(&svd, sizeof (svd), svd_addr) == -1) {
+ mdb_warn("segvn_anon walk: unable to read segvn_data at %p",
+ svd_addr);
+ return (WALK_ERR);
+ }
+ if (svd.amp == NULL) {
+ mdb_warn("segvn_anon walk: segvn_data at %p has no anon map\n",
+ svd_addr);
+ return (WALK_ERR);
+ }
+ amp_addr = (uintptr_t)svd.amp;
+ if (mdb_vread(&amp, sizeof (amp), amp_addr) == -1) {
+ mdb_warn("segvn_anon walk: unable to read amp %p for "
+ "segvn_data %p", amp_addr, svd_addr);
+ return (WALK_ERR);
+ }
+ seg_addr = (uintptr_t)svd.seg;
+ if (mdb_vread(&seg, sizeof (seg), seg_addr) == -1) {
+ mdb_warn("segvn_anon walk: unable to read seg %p for "
+ "segvn_data %p", seg_addr, svd_addr);
+ return (WALK_ERR);
+ }
+ if ((seg.s_size + (svd.anon_index << PAGESHIFT)) > amp.size) {
+ mdb_warn("anon map %p is too small for segment %p\n",
+ amp_addr, seg_addr);
+ return (WALK_ERR);
+ }
+
+ wsp->walk_addr = amp_addr;
+ return (anon_walk_init_common(wsp,
+ svd.anon_index, svd.anon_index + (seg.s_size >> PAGESHIFT)));
+}
+
+
+typedef struct {
+ u_offset_t svs_offset;
+ uintptr_t svs_page;
+} segvn_sparse_t;
+#define SEGVN_MAX_SPARSE ((128 * 1024) / sizeof (segvn_sparse_t))
+
+typedef struct {
+ uintptr_t svw_svdp;
+ struct segvn_data svw_svd;
+ struct seg svw_seg;
+ size_t svw_walkoff;
+ ulong_t svw_anonskip;
+ segvn_sparse_t *svw_sparse;
+ size_t svw_sparse_idx;
+ size_t svw_sparse_count;
+ size_t svw_sparse_size;
+ uint8_t svw_sparse_overflow;
+ uint8_t svw_all;
+} segvn_walk_data_t;
+
+static int
+segvn_sparse_fill(uintptr_t addr, const void *pp_arg, void *arg)
+{
+ segvn_walk_data_t *const svw = arg;
+ const page_t *const pp = pp_arg;
+ const u_offset_t offset = pp->p_offset;
+ segvn_sparse_t *const cur =
+ &svw->svw_sparse[svw->svw_sparse_count];
+
+ /* See if the page is of interest */
+ if ((u_offset_t)(offset - svw->svw_svd.offset) >= svw->svw_seg.s_size) {
+ return (WALK_NEXT);
+ }
+ /* See if we have space for the new entry, then add it. */
+ if (svw->svw_sparse_count >= svw->svw_sparse_size) {
+ svw->svw_sparse_overflow = 1;
+ return (WALK_DONE);
+ }
+ svw->svw_sparse_count++;
+ cur->svs_offset = offset;
+ cur->svs_page = addr;
+ return (WALK_NEXT);
+}
+
+static int
+segvn_sparse_cmp(const void *lp, const void *rp)
+{
+ const segvn_sparse_t *const l = lp;
+ const segvn_sparse_t *const r = rp;
+
+ if (l->svs_offset < r->svs_offset) {
+ return (-1);
+ }
+ if (l->svs_offset > r->svs_offset) {
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Builds on the "anon_all" walker to walk all resident pages in a segvn_data
+ * structure. For segvn_datas without an anon structure, it just looks up
+ * pages in the vnode. For segvn_datas with an anon structure, NULL slots
+ * pass through to the vnode, and non-null slots are checked for residency.
+ */
+int
+segvn_pages_walk_init(mdb_walk_state_t *wsp)
+{
+ segvn_walk_data_t *svw;
+ struct segvn_data *svd;
+
+ if (wsp->walk_addr == NULL) {
+ mdb_warn("segvn walk doesn't support global walks\n");
+ return (WALK_ERR);
+ }
+
+ svw = mdb_zalloc(sizeof (*svw), UM_SLEEP);
+ svw->svw_svdp = wsp->walk_addr;
+ svw->svw_anonskip = 0;
+ svw->svw_sparse_idx = 0;
+ svw->svw_walkoff = 0;
+ svw->svw_all = (wsp->walk_arg == SEGVN_PAGES_ALL);
+
+ if (mdb_vread(&svw->svw_svd, sizeof (svw->svw_svd), wsp->walk_addr) ==
+ -1) {
+ mdb_warn("failed to read segvn_data at %p", wsp->walk_addr);
+ mdb_free(svw, sizeof (*svw));
+ return (WALK_ERR);
+ }
+
+ svd = &svw->svw_svd;
+ if (mdb_vread(&svw->svw_seg, sizeof (svw->svw_seg),
+ (uintptr_t)svd->seg) == -1) {
+ mdb_warn("failed to read seg at %p (from %p)",
+ svd->seg, &((struct segvn_data *)(wsp->walk_addr))->seg);
+ mdb_free(svw, sizeof (*svw));
+ return (WALK_ERR);
+ }
+
+ if (svd->amp == NULL && svd->vp == NULL) {
+ /* make the walk terminate immediately; no pages */
+ svw->svw_walkoff = svw->svw_seg.s_size;
+
+ } else if (svd->amp == NULL &&
+ (svw->svw_seg.s_size >> PAGESHIFT) >= SEGVN_MAX_SPARSE) {
+ /*
+ * If we don't have an anon pointer, and the segment is large,
+ * we try to load the in-memory pages into a fixed-size array,
+ * which is then sorted and reported directly. This is much
+ * faster than doing a mdb_page_lookup() for each possible
+ * offset.
+ *
+ * If the allocation fails, or there are too many pages
+ * in-core, we fall back to looking up the pages individually.
+ */
+ svw->svw_sparse = mdb_alloc(
+ SEGVN_MAX_SPARSE * sizeof (*svw->svw_sparse), UM_NOSLEEP);
+ if (svw->svw_sparse != NULL) {
+ svw->svw_sparse_size = SEGVN_MAX_SPARSE;
+
+ if (mdb_pwalk("page", segvn_sparse_fill, svw,
+ (uintptr_t)svd->vp) == -1 ||
+ svw->svw_sparse_overflow) {
+ mdb_free(svw->svw_sparse, SEGVN_MAX_SPARSE *
+ sizeof (*svw->svw_sparse));
+ svw->svw_sparse = NULL;
+ } else {
+ qsort(svw->svw_sparse, svw->svw_sparse_count,
+ sizeof (*svw->svw_sparse),
+ segvn_sparse_cmp);
+ }
+ }
+
+ } else if (svd->amp != NULL) {
+ const char *const layer = (!svw->svw_all && svd->vp == NULL) ?
+ "segvn_anon" : "segvn_anon_all";
+ /*
+ * If we're not printing all offsets, and the segvn_data has
+ * no backing VP, we can use the "segvn_anon" walker, which
+ * efficiently skips NULL slots.
+ *
+ * Otherwise, we layer over the "segvn_anon_all" walker
+ * (which reports all anon slots, even NULL ones), so that
+ * segvn_pages_walk_step() knows the precise offset for each
+ * element. It uses that offset information to look up the
+ * backing pages for NULL anon slots.
+ */
+ if (mdb_layered_walk(layer, wsp) == -1) {
+ mdb_warn("segvn_pages: failed to layer \"%s\" "
+ "for segvn_data %p", layer, svw->svw_svdp);
+ mdb_free(svw, sizeof (*svw));
+ return (WALK_ERR);
+ }
+ }
+
+ wsp->walk_data = svw;
+ return (WALK_NEXT);
+}
+
+int
+segvn_pages_walk_step(mdb_walk_state_t *wsp)
+{
+ segvn_walk_data_t *const svw = wsp->walk_data;
+ struct seg *const seg = &svw->svw_seg;
+ struct segvn_data *const svd = &svw->svw_svd;
+ uintptr_t pp;
+ page_t page;
+
+ /* If we've walked off the end of the segment, we're done. */
+ if (svw->svw_walkoff >= seg->s_size) {
+ return (WALK_DONE);
+ }
+
+ /*
+ * If we've got a sparse page array, just send it directly.
+ */
+ if (svw->svw_sparse != NULL) {
+ u_offset_t off;
+
+ if (svw->svw_sparse_idx >= svw->svw_sparse_count) {
+ pp = NULL;
+ if (!svw->svw_all) {
+ return (WALK_DONE);
+ }
+ } else {
+ segvn_sparse_t *const svs =
+ &svw->svw_sparse[svw->svw_sparse_idx];
+ off = svs->svs_offset - svd->offset;
+ if (svw->svw_all && svw->svw_walkoff != off) {
+ pp = NULL;
+ } else {
+ pp = svs->svs_page;
+ svw->svw_sparse_idx++;
+ }
+ }
+
+ } else if (svd->amp == NULL || wsp->walk_addr == NULL) {
+ /*
+ * If there's no anon, or the anon slot is NULL, look up
+ * <vp, offset>.
+ */
+ if (svd->vp != NULL) {
+ pp = mdb_page_lookup((uintptr_t)svd->vp,
+ svd->offset + svw->svw_walkoff);
+ } else {
+ pp = NULL;
+ }
+
+ } else {
+ const struct anon *const anon = wsp->walk_layer;
+
+ /*
+ * We have a "struct anon"; if it's not swapped out,
+ * look up the page.
+ */
+ if (anon->an_vp != NULL || anon->an_off != 0) {
+ pp = mdb_page_lookup((uintptr_t)anon->an_vp,
+ anon->an_off);
+ if (pp == 0 && mdb_get_state() != MDB_STATE_RUNNING) {
+ mdb_warn("walk segvn_pages: segvn_data %p "
+ "offset %ld, anon page <%p, %llx> not "
+ "found.\n", svw->svw_svdp, svw->svw_walkoff,
+ anon->an_vp, anon->an_off);
+ }
+ } else {
+ if (anon->an_pvp == NULL) {
+ mdb_warn("walk segvn_pages: useless struct "
+ "anon at %p\n", wsp->walk_addr);
+ }
+ pp = NULL; /* nothing at this offset */
+ }
+ }
+
+ svw->svw_walkoff += PAGESIZE; /* Update for the next call */
+ if (pp != NULL) {
+ if (mdb_vread(&page, sizeof (page_t), pp) == -1) {
+ mdb_warn("unable to read page_t at %#lx", pp);
+ return (WALK_ERR);
+ }
+ return (wsp->walk_callback(pp, &page, wsp->walk_cbdata));
+ }
+ if (svw->svw_all) {
+ return (wsp->walk_callback(NULL, NULL, wsp->walk_cbdata));
+ }
+ return (WALK_NEXT);
+}
+
+void
+segvn_pages_walk_fini(mdb_walk_state_t *wsp)
+{
+ segvn_walk_data_t *const svw = wsp->walk_data;
+
+ if (svw->svw_sparse != NULL) {
+ mdb_free(svw->svw_sparse, SEGVN_MAX_SPARSE *
+ sizeof (*svw->svw_sparse));
+ }
+ mdb_free(svw, sizeof (*svw));
+}
+
/*
* Grumble, grumble.
*/
diff --git a/usr/src/cmd/mdb/common/modules/genunix/memory.h b/usr/src/cmd/mdb/common/modules/genunix/memory.h
index b24024b3a4..a6f6130ed7 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/memory.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/memory.h
@@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _MEMORY_H
@@ -46,10 +45,19 @@ int page_num2pp(uintptr_t, uint_t, int, const mdb_arg_t *);
int seg_walk_init(mdb_walk_state_t *);
int seg(uintptr_t, uint_t, int, const mdb_arg_t *);
+#define SEGVN_PAGES_RESIDENT (void *)(uintptr_t)0
+#define SEGVN_PAGES_ALL (void *)(uintptr_t)1
+int segvn_pages_walk_init(mdb_walk_state_t *);
+int segvn_pages_walk_step(mdb_walk_state_t *);
+void segvn_pages_walk_fini(mdb_walk_state_t *);
+
int vnode2smap(uintptr_t, uint_t, int, const mdb_arg_t *);
int addr2smap(uintptr_t, uint_t, int, const mdb_arg_t *);
+#define ANON_WALK_ALLOC (void *)(uintptr_t)0
+#define ANON_WALK_ALL (void *)(uintptr_t)1
int anon_walk_init(mdb_walk_state_t *);
+int segvn_anon_walk_init(mdb_walk_state_t *);
int anon_walk_step(mdb_walk_state_t *);
void anon_walk_fini(mdb_walk_state_t *);
int pmap(uintptr_t, uint_t, int, const mdb_arg_t *);
diff --git a/usr/src/cmd/mdb/common/modules/genunix/vfs.c b/usr/src/cmd/mdb/common/modules/genunix/vfs.c
index 8001c41b3c..3ea333ddf8 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/vfs.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/vfs.c
@@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 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 <mdb/mdb_modapi.h>
@@ -723,7 +722,7 @@ sctp_getpeeraddr(sctp_t *sctp, struct sockaddr *addr)
mdb_warn("failed to read sctp primary faddr");
return (-1);
}
- faddr = sctp_primary.faddr;
+ faddr = sctp_primary.sf_faddr;
switch (connp->conn_family) {
case AF_INET:
diff --git a/usr/src/cmd/mdb/common/modules/genunix/zone.c b/usr/src/cmd/mdb/common/modules/genunix/zone.c
index 28912479b3..96f6b598ec 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/zone.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/zone.c
@@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 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 <mdb/mdb_param.h>
@@ -39,6 +38,22 @@
#define ZONE_PATHLEN 40
#endif
+/*
+ * Names corresponding to zone_status_t values in sys/zone.h
+ */
+char *zone_status_names[] = {
+ "uninitialized", /* ZONE_IS_UNINITIALIZED */
+ "initialized", /* ZONE_IS_INITIALIZED */
+ "ready", /* ZONE_IS_READY */
+ "booting", /* ZONE_IS_BOOTING */
+ "running", /* ZONE_IS_RUNNING */
+ "shutting_down", /* ZONE_IS_SHUTTING_DOWN */
+ "empty", /* ZONE_IS_EMPTY */
+ "down", /* ZONE_IS_DOWN */
+ "dying", /* ZONE_IS_DYING */
+ "dead" /* ZONE_IS_DEAD */
+};
+
int
zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
@@ -46,8 +61,10 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
char name[ZONE_NAMELEN];
char path[ZONE_PATHLEN];
int len;
+ uint_t vopt_given;
+ uint_t ropt_given;
- if (argc != 0)
+ if (argc > 2)
return (DCMD_USAGE);
if (!(flags & DCMD_ADDRSPEC)) {
@@ -57,10 +74,38 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
}
return (DCMD_OK);
}
+
+ /*
+ * Get the optional -r (reference counts) and -v (verbose output)
+ * arguments.
+ */
+ vopt_given = FALSE;
+ ropt_given = FALSE;
+ if (argc > 0 && mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE,
+ &vopt_given, 'r', MDB_OPT_SETBITS, TRUE, &ropt_given, NULL) != argc)
+ return (DCMD_USAGE);
+
+ /*
+ * -v can only be specified with -r.
+ */
+ if (vopt_given == TRUE && ropt_given == FALSE)
+ return (DCMD_USAGE);
+
+ /*
+ * Print a table header, if necessary.
+ */
if (DCMD_HDRSPEC(flags)) {
- mdb_printf("%<u>%?s %6s %-20s %-s%</u>\n",
- "ADDR", "ID", "NAME", "PATH");
+ if (ropt_given == FALSE)
+ mdb_printf("%<u>%?s %6s %-13s %-20s %-s%</u>\n",
+ "ADDR", "ID", "STATUS", "NAME", "PATH");
+ else
+ mdb_printf("%<u>%?s %6s %10s %10s %-20s%</u>\n",
+ "ADDR", "ID", "REFS", "CREFS", "NAME");
}
+
+ /*
+ * Read the zone_t structure at the given address and read its name.
+ */
if (mdb_vread(&zn, sizeof (zone_t), addr) == -1) {
mdb_warn("can't read zone_t structure at %p", addr);
return (DCMD_ERR);
@@ -72,14 +117,110 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
} else {
(void) strcpy(name, "??");
}
- len = mdb_readstr(path, ZONE_PATHLEN, (uintptr_t)zn.zone_rootpath);
- if (len > 0) {
- if (len == ZONE_PATHLEN)
- (void) strcpy(&path[len - 4], "...");
+
+ if (ropt_given == FALSE) {
+ char *statusp;
+
+ /*
+ * Default display
+ * Fetch the zone's path and print the results.
+ */
+ len = mdb_readstr(path, ZONE_PATHLEN,
+ (uintptr_t)zn.zone_rootpath);
+ if (len > 0) {
+ if (len == ZONE_PATHLEN)
+ (void) strcpy(&path[len - 4], "...");
+ } else {
+ (void) strcpy(path, "??");
+ }
+ if (zn.zone_status >= ZONE_IS_UNINITIALIZED && zn.zone_status <=
+ ZONE_IS_DEAD)
+ statusp = zone_status_names[zn.zone_status];
+ else
+ statusp = "???";
+ mdb_printf("%0?p %6d %-13s %-20s %s\n", addr, zn.zone_id,
+ statusp, name, path);
} else {
- (void) strcpy(path, "??");
+ /*
+ * Display the zone's reference counts.
+ * Display the zone's subsystem-specific reference counts if
+ * the user specified the '-v' option.
+ */
+ mdb_printf("%0?p %6d %10u %10u %-20s\n", addr, zn.zone_id,
+ zn.zone_ref, zn.zone_cred_ref, name);
+ if (vopt_given == TRUE) {
+ GElf_Sym subsys_names_sym;
+ uintptr_t **zone_ref_subsys_names;
+ uint_t num_subsys;
+ uint_t n;
+
+ /*
+ * Read zone_ref_subsys_names from the kernel image.
+ */
+ if (mdb_lookup_by_name("zone_ref_subsys_names",
+ &subsys_names_sym) != 0) {
+ mdb_warn("can't find zone_ref_subsys_names");
+ return (DCMD_ERR);
+ }
+ if (subsys_names_sym.st_size != ZONE_REF_NUM_SUBSYS *
+ sizeof (char *)) {
+ mdb_warn("number of subsystems in target "
+ "differs from what mdb expects (mismatched"
+ " kernel versions?)");
+ if (subsys_names_sym.st_size <
+ ZONE_REF_NUM_SUBSYS * sizeof (char *))
+ num_subsys = subsys_names_sym.st_size /
+ sizeof (char *);
+ else
+ num_subsys = ZONE_REF_NUM_SUBSYS;
+ } else {
+ num_subsys = ZONE_REF_NUM_SUBSYS;
+ }
+ if ((zone_ref_subsys_names = mdb_alloc(
+ subsys_names_sym.st_size, UM_GC)) == NULL) {
+ mdb_warn("out of memory");
+ return (DCMD_ERR);
+ }
+ if (mdb_readvar(zone_ref_subsys_names,
+ "zone_ref_subsys_names") == -1) {
+ mdb_warn("can't find zone_ref_subsys_names");
+ return (DCMD_ERR);
+ }
+
+ /*
+ * Display each subsystem's reference count if it's
+ * nonzero.
+ */
+ mdb_inc_indent(7);
+ for (n = 0; n < num_subsys; ++n) {
+ char subsys_name[16];
+
+ /*
+ * Skip subsystems lacking outstanding
+ * references.
+ */
+ if (zn.zone_subsys_ref[n] == 0)
+ continue;
+
+ /*
+ * Each subsystem's name must be read from
+ * the target's image.
+ */
+ if (mdb_readstr(subsys_name,
+ sizeof (subsys_name),
+ (uintptr_t)zone_ref_subsys_names[n]) ==
+ -1) {
+ mdb_warn("unable to read subsystem name"
+ " from zone_ref_subsys_names[%u]",
+ n);
+ return (DCMD_ERR);
+ }
+ mdb_printf("%15s: %10u\n", subsys_name,
+ zn.zone_subsys_ref[n]);
+ }
+ mdb_dec_indent(7);
+ }
}
- mdb_printf("%0?p %6d %-20s %s\n", addr, zn.zone_id, name, path);
return (DCMD_OK);
}
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..da8bbc6828 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)
@@ -844,11 +845,12 @@ d_uberdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
prt_addr(uberdata.ulwp_replace_last, 1),
prt_addr(uberdata.atforklist, 0));
- HD("robustlocks robustlist");
- mdb_printf(OFFSTR "%s %s\n",
+ HD("robustlocks robustlist progname");
+ mdb_printf(OFFSTR "%s %s %s\n",
OFFSET(robustlocks),
prt_addr(uberdata.robustlocks, 1),
- prt_addr(uberdata.robustlist, 0));
+ prt_addr(uberdata.robustlist, 1),
+ prt_addr(uberdata.progname, 0));
HD("tdb_bootstrap tdb_sync_addr_hash tdb_'count tdb_'fail");
mdb_printf(OFFSTR "%s %s %-10d %d\n",
@@ -981,10 +983,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 +1066,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/common/modules/mdb_ks/mdb_ks.c b/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c
index ea9e747ba7..74e0396226 100644
--- a/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c
+++ b/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c
@@ -31,7 +31,6 @@
* to zero.
*/
-
#include <mdb/mdb_target.h>
#include <mdb/mdb_param.h>
#include <mdb/mdb_modapi.h>
@@ -88,6 +87,8 @@ uintptr_t _mdb_ks_argsbase;
unsigned long _mdb_ks_msg_bsize;
unsigned long _mdb_ks_defaultstksz;
int _mdb_ks_ncpu;
+int _mdb_ks_ncpu_log2;
+int _mdb_ks_ncpu_p2;
/*
* In-core copy of DNLC information:
@@ -97,10 +98,19 @@ int _mdb_ks_ncpu;
#define MDB_DNLC_NCACHE_SZ(ncp) (sizeof (ncache_t) + (ncp)->namlen)
#define MDB_DNLC_MAX_RETRY 4
-
static ncache_t **dnlc_hash; /* mdbs hash array of dnlc entries */
/*
+ * copy of page_hash-related data
+ */
+static int page_hash_loaded;
+static long mdb_page_hashsz;
+static uint_t mdb_page_hashsz_shift; /* Needed for PAGE_HASH_FUNC */
+static uintptr_t mdb_page_hash; /* base address of page hash */
+#define page_hashsz mdb_page_hashsz
+#define page_hashsz_shift mdb_page_hashsz_shift
+
+/*
* This will be the location of the vnodeops pointer for "autofs_vnodeops"
* The pointer still needs to be read with mdb_vread() to get the location
* of the vnodeops structure for autofs.
@@ -624,32 +634,78 @@ out:
return (cpu);
}
+static int
+page_hash_load(void)
+{
+ if (page_hash_loaded) {
+ return (1);
+ }
+
+ if (mdb_readvar(&mdb_page_hashsz, "page_hashsz") == -1) {
+ mdb_warn("unable to read page_hashsz");
+ return (0);
+ }
+ if (mdb_readvar(&mdb_page_hashsz_shift, "page_hashsz_shift") == -1) {
+ mdb_warn("unable to read page_hashsz_shift");
+ return (0);
+ }
+ if (mdb_readvar(&mdb_page_hash, "page_hash") == -1) {
+ mdb_warn("unable to read page_hash");
+ return (0);
+ }
+
+ page_hash_loaded = 1; /* zeroed on state change */
+ return (1);
+}
+
uintptr_t
mdb_page_lookup(uintptr_t vp, u_offset_t offset)
{
- long page_hashsz, ndx;
- int page_hashsz_shift; /* Needed for PAGE_HASH_FUNC */
- uintptr_t page_hash, pp;
+ size_t ndx;
+ uintptr_t page_hash_entry, pp;
- if (mdb_readvar(&page_hashsz, "page_hashsz") == -1 ||
- mdb_readvar(&page_hashsz_shift, "page_hashsz_shift") == -1 ||
- mdb_readvar(&page_hash, "page_hash") == -1)
+ if (!page_hash_loaded && !page_hash_load()) {
return (NULL);
+ }
ndx = PAGE_HASH_FUNC(vp, offset);
- page_hash += ndx * sizeof (uintptr_t);
+ page_hash_entry = mdb_page_hash + ndx * sizeof (uintptr_t);
- mdb_vread(&pp, sizeof (pp), page_hash);
+ if (mdb_vread(&pp, sizeof (pp), page_hash_entry) < 0) {
+ mdb_warn("unable to read page_hash[%ld] (%p)", ndx,
+ page_hash_entry);
+ return (NULL);
+ }
while (pp != NULL) {
page_t page;
+ long nndx;
- mdb_vread(&page, sizeof (page), pp);
+ if (mdb_vread(&page, sizeof (page), pp) < 0) {
+ mdb_warn("unable to read page_t at %p", pp);
+ return (NULL);
+ }
if ((uintptr_t)page.p_vnode == vp &&
(uint64_t)page.p_offset == offset)
return (pp);
+ /*
+ * Double check that the pages actually hash to the
+ * bucket we're searching. If not, our version of
+ * PAGE_HASH_FUNC() doesn't match the kernel's, and we're
+ * not going to be able to find the page. The most
+ * likely reason for this that mdb_ks doesn't match the
+ * kernel we're running against.
+ */
+ nndx = PAGE_HASH_FUNC(page.p_vnode, page.p_offset);
+ if (page.p_vnode != NULL && nndx != ndx) {
+ mdb_warn("mdb_page_lookup: mdb_ks PAGE_HASH_FUNC() "
+ "mismatch: in bucket %ld, but page %p hashes to "
+ "bucket %ld\n", ndx, pp, nndx);
+ return (NULL);
+ }
+
pp = (uintptr_t)page.p_hash;
}
@@ -1164,6 +1220,10 @@ update_vars(void *arg)
(void) mdb_readvar(&_mdb_ks_msg_bsize, "_msg_bsize");
(void) mdb_readvar(&_mdb_ks_defaultstksz, "_defaultstksz");
(void) mdb_readvar(&_mdb_ks_ncpu, "_ncpu");
+ (void) mdb_readvar(&_mdb_ks_ncpu_log2, "_ncpu_log2");
+ (void) mdb_readvar(&_mdb_ks_ncpu_p2, "_ncpu_p2");
+
+ page_hash_loaded = 0; /* invalidate cached page_hash state */
}
const mdb_modinfo_t *
diff --git a/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c b/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c
index 24785a7445..30ec30cbb6 100644
--- a/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c
+++ b/usr/src/cmd/mdb/common/modules/pmcs/pmcs.c
@@ -1533,9 +1533,6 @@ outbound_iomb_opcode(uint32_t opcode)
case PMCOUT_DEVICE_HANDLE_ARRIVED:
return ("DEVICE_HANDLE_ARRIVED");
break;
- case PMCOUT_SMP_REQUEST_RECEIVED:
- return ("SMP_REQUEST_RECEIVED");
- break;
case PMCOUT_SSP_REQUEST_RECEIVED:
return ("SSP_REQUEST_RECEIVED");
break;
@@ -1611,13 +1608,196 @@ outbound_iomb_opcode(uint32_t opcode)
}
}
+static uint32_t
+get_devid_from_ob_iomb(struct pmcs_hw ss, uint32_t *qentryp, uint16_t opcode)
+{
+ uint32_t devid = PMCS_INVALID_DEVICE_ID;
+
+ switch (opcode) {
+ /*
+ * These are obtained via the HTAG which is in word 1
+ */
+ case PMCOUT_SSP_COMPLETION:
+ case PMCOUT_SMP_COMPLETION:
+ case PMCOUT_DEREGISTER_DEVICE_HANDLE:
+ case PMCOUT_GET_DEVICE_HANDLE:
+ case PMCOUT_SATA_COMPLETION:
+ case PMCOUT_SSP_ABORT:
+ case PMCOUT_SATA_ABORT:
+ case PMCOUT_SMP_ABORT:
+ case PMCOUT_SAS_HW_EVENT_ACK_ACK: {
+ uint32_t htag;
+ pmcwork_t *wp;
+ pmcs_phy_t *phy;
+ uintptr_t _wp, _phy;
+ uint16_t index;
+
+ htag = LE_32(*(qentryp + 1));
+ index = htag & PMCS_TAG_INDEX_MASK;
+
+ wp = mdb_alloc(sizeof (pmcwork_t), UM_SLEEP);
+ _wp = (uintptr_t)ss.work + (sizeof (pmcwork_t) * index);
+
+ if (MDB_RD(wp, sizeof (pmcwork_t), _wp) == -1) {
+ NOREAD(pmcwork_t, _wp);
+ mdb_free(wp, sizeof (pmcwork_t));
+ break;
+ }
+
+ phy = mdb_alloc(sizeof (pmcs_phy_t), UM_SLEEP);
+ if (wp->phy == NULL) {
+ _phy = (uintptr_t)wp->last_phy;
+ } else {
+ _phy = (uintptr_t)wp->phy;
+ }
+
+ /*
+ * If we have a PHY, read it in and get it's handle
+ */
+ if (_phy != NULL) {
+ if (MDB_RD(phy, sizeof (*phy), _phy) == -1) {
+ NOREAD(pmcs_phy_t, phy);
+ } else {
+ devid = phy->device_id;
+ }
+ }
+
+ mdb_free(phy, sizeof (pmcs_phy_t));
+ mdb_free(wp, sizeof (pmcwork_t));
+ break;
+ }
+
+ /*
+ * The device ID is in the outbound IOMB at word 1
+ */
+ case PMCOUT_SSP_REQUEST_RECEIVED:
+ devid = LE_32(*(qentryp + 1)) & PMCS_DEVICE_ID_MASK;
+ break;
+
+ /*
+ * The device ID is in the outbound IOMB at word 2
+ */
+ case PMCOUT_DEVICE_HANDLE_ARRIVED:
+ case PMCOUT_DEVICE_HANDLE_REMOVED:
+ devid = LE_32(*(qentryp + 2)) & PMCS_DEVICE_ID_MASK;
+ break;
+
+ /*
+ * In this (very rare - never seen it) state, the device ID
+ * comes from the HTAG in the inbound IOMB, which would be word
+ * 3 in the outbound IOMB
+ */
+ case PMCOUT_GENERAL_EVENT:
+ /*
+ * The device ID is in the outbound IOMB at word 3
+ */
+ case PMCOUT_DEVICE_REGISTRATION:
+ case PMCOUT_DEVICE_INFO:
+ case PMCOUT_SET_DEVICE_STATE:
+ case PMCOUT_GET_DEVICE_STATE:
+ case PMCOUT_SET_DEVICE_INFO:
+ devid = LE_32(*(qentryp + 3)) & PMCS_DEVICE_ID_MASK;
+ break;
+
+ /*
+ * Device ID is in the outbound IOMB at word 4
+ */
+ case PMCOUT_SATA_EVENT:
+ case PMCOUT_SSP_EVENT:
+ devid = LE_32(*(qentryp + 4)) & PMCS_DEVICE_ID_MASK;
+ break;
+ }
+
+ return (devid);
+}
+
+static boolean_t
+iomb_is_dev_hdl_specific(uint32_t word0, boolean_t inbound)
+{
+ uint16_t opcode = word0 & PMCS_IOMB_OPCODE_MASK;
+
+ if (inbound) {
+ switch (opcode) {
+ case PMCIN_SSP_INI_IO_START:
+ case PMCIN_SSP_INI_TM_START:
+ case PMCIN_SSP_INI_EXT_IO_START:
+ case PMCIN_SSP_TGT_IO_START:
+ case PMCIN_SSP_TGT_RESPONSE_START:
+ case PMCIN_SSP_ABORT:
+ case PMCIN_DEREGISTER_DEVICE_HANDLE:
+ case PMCIN_SMP_REQUEST:
+ case PMCIN_SMP_RESPONSE:
+ case PMCIN_SMP_ABORT:
+ case PMCIN_ASSISTED_DISCOVERY:
+ case PMCIN_SATA_HOST_IO_START:
+ case PMCIN_SATA_ABORT:
+ case PMCIN_GET_DEVICE_INFO:
+ case PMCIN_SET_DEVICE_STATE:
+ case PMCIN_GET_DEVICE_STATE:
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+ }
+
+ switch (opcode) {
+ case PMCOUT_SSP_COMPLETION:
+ case PMCOUT_SMP_COMPLETION:
+ case PMCOUT_DEVICE_REGISTRATION:
+ case PMCOUT_DEREGISTER_DEVICE_HANDLE:
+ case PMCOUT_GET_DEVICE_HANDLE:
+ case PMCOUT_SATA_COMPLETION:
+ case PMCOUT_SATA_EVENT:
+ case PMCOUT_SSP_EVENT:
+ case PMCOUT_DEVICE_HANDLE_ARRIVED:
+ case PMCOUT_SSP_REQUEST_RECEIVED:
+ case PMCOUT_DEVICE_INFO:
+ case PMCOUT_FW_FLASH_UPDATE:
+ case PMCOUT_GENERAL_EVENT:
+ case PMCOUT_SSP_ABORT:
+ case PMCOUT_SATA_ABORT:
+ case PMCOUT_SAS_HW_EVENT_ACK_ACK:
+ case PMCOUT_SMP_ABORT:
+ case PMCOUT_DEVICE_HANDLE_REMOVED:
+ case PMCOUT_SET_DEVICE_STATE:
+ case PMCOUT_GET_DEVICE_STATE:
+ case PMCOUT_SET_DEVICE_INFO:
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
static void
-dump_one_qentry_outbound(uint32_t *qentryp, int idx)
+dump_one_qentry_outbound(struct pmcs_hw ss, uint32_t *qentryp, int idx,
+ uint64_t devid_filter)
{
int qeidx;
uint32_t word0 = LE_32(*qentryp);
uint32_t word1 = LE_32(*(qentryp + 1));
uint8_t iop_event;
+ uint32_t devid;
+
+ /*
+ * Check to see if we're filtering on a device ID
+ */
+ if (devid_filter != PMCS_INVALID_DEVICE_ID) {
+ if (!iomb_is_dev_hdl_specific(word0, B_FALSE)) {
+ return;
+ }
+
+ /*
+ * Go find the device id. It might be in the outbound
+ * IOMB or we may have to go find the work structure and
+ * get it from there.
+ */
+ devid = get_devid_from_ob_iomb(ss, qentryp,
+ word0 & PMCS_IOMB_OPCODE_MASK);
+ if ((devid == PMCS_INVALID_DEVICE_ID) ||
+ (devid_filter != devid)) {
+ return;
+ }
+ }
mdb_printf("Entry #%02d\n", idx);
mdb_inc_indent(2);
@@ -1652,7 +1832,8 @@ dump_one_qentry_outbound(uint32_t *qentryp, int idx)
}
static void
-display_outbound_queues(struct pmcs_hw ss, uint_t verbose)
+display_outbound_queues(struct pmcs_hw ss, uint64_t devid_filter,
+ uint_t verbose)
{
int idx, qidx;
uintptr_t obqp;
@@ -1709,7 +1890,8 @@ display_outbound_queues(struct pmcs_hw ss, uint_t verbose)
last_consumed)));
break;
}
- dump_one_qentry_outbound(qentryp, last_consumed);
+ dump_one_qentry_outbound(ss, qentryp, last_consumed,
+ devid_filter);
mdb_printf("\n");
mdb_dec_indent(2);
continue;
@@ -1722,7 +1904,8 @@ display_outbound_queues(struct pmcs_hw ss, uint_t verbose)
(obqp + (PMCS_QENTRY_SIZE * idx)));
break;
}
- dump_one_qentry_outbound(qentryp, idx);
+ dump_one_qentry_outbound(ss, qentryp, idx,
+ devid_filter);
}
mdb_printf("\n");
@@ -1734,10 +1917,24 @@ display_outbound_queues(struct pmcs_hw ss, uint_t verbose)
}
static void
-dump_one_qentry_inbound(uint32_t *qentryp, int idx)
+dump_one_qentry_inbound(uint32_t *qentryp, int idx, uint64_t devid_filter)
{
int qeidx;
uint32_t word0 = LE_32(*qentryp);
+ uint32_t devid = LE_32(*(qentryp + 2));
+
+ /*
+ * Check to see if we're filtering on a device ID
+ */
+ if (devid_filter != PMCS_INVALID_DEVICE_ID) {
+ if (iomb_is_dev_hdl_specific(word0, B_TRUE)) {
+ if (devid_filter != devid) {
+ return;
+ }
+ } else {
+ return;
+ }
+ }
mdb_printf("Entry #%02d\n", idx);
mdb_inc_indent(2);
@@ -1769,7 +1966,7 @@ dump_one_qentry_inbound(uint32_t *qentryp, int idx)
}
static void
-display_inbound_queues(struct pmcs_hw ss, uint_t verbose)
+display_inbound_queues(struct pmcs_hw ss, uint64_t devid_filter, uint_t verbose)
{
int idx, qidx, iqci, last_consumed;
uintptr_t ibqp;
@@ -1821,7 +2018,8 @@ display_inbound_queues(struct pmcs_hw ss, uint_t verbose)
last_consumed)));
break;
}
- dump_one_qentry_inbound(qentryp, last_consumed);
+ dump_one_qentry_inbound(qentryp, last_consumed,
+ devid_filter);
mdb_printf("\n");
mdb_dec_indent(2);
continue;
@@ -1834,7 +2032,7 @@ display_inbound_queues(struct pmcs_hw ss, uint_t verbose)
(ibqp + (PMCS_QENTRY_SIZE * idx)));
break;
}
- dump_one_qentry_inbound(qentryp, idx);
+ dump_one_qentry_inbound(qentryp, idx, devid_filter);
}
mdb_printf("\n");
@@ -2835,6 +3033,9 @@ pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
uint_t dtc_info = FALSE;
uint_t wserno = FALSE;
uint_t fwlog = FALSE;
+ boolean_t devid_filter = FALSE;
+ uintptr_t pdevid;
+ uint32_t devid;
int rv = DCMD_OK;
void *pmcs_state;
char *state_str;
@@ -2858,6 +3059,7 @@ pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
if (mdb_getopts(argc, argv,
'c', MDB_OPT_SETBITS, TRUE, &compq,
'd', MDB_OPT_SETBITS, TRUE, &dtc_info,
+ 'D', MDB_OPT_UINTPTR_SET, &devid_filter, &pdevid,
'e', MDB_OPT_SETBITS, TRUE, &fwlog,
'h', MDB_OPT_SETBITS, TRUE, &hw_info,
'i', MDB_OPT_SETBITS, TRUE, &ic_info,
@@ -2885,6 +3087,23 @@ pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
iport_info = TRUE;
}
+ /*
+ * The -D option is meaningless without -q and/or -Q, and implies
+ * verbosity.
+ */
+ if (devid_filter) {
+ devid = (uint64_t)pdevid & 0xffffffff;
+ if (!ibq && !obq) {
+ mdb_printf("-D requires either -q or -Q\n");
+ return (DCMD_USAGE);
+ }
+ if (devid > PMCS_DEVICE_ID_MASK) {
+ mdb_printf("Device ID invalid\n");
+ return (DCMD_USAGE);
+ }
+ verbose = TRUE;
+ }
+
if (MDB_RD(&ss, sizeof (ss), addr) == -1) {
NOREAD(pmcs_hw_t, addr);
return (DCMD_ERR);
@@ -2956,10 +3175,10 @@ pmcs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
display_ic(ss, verbose);
if (ibq)
- display_inbound_queues(ss, verbose);
+ display_inbound_queues(ss, devid, verbose);
if (obq)
- display_outbound_queues(ss, verbose);
+ display_outbound_queues(ss, devid, verbose);
if (iport_info)
display_iport(ss, addr, verbose, &pis);
@@ -2984,6 +3203,7 @@ pmcs_help()
mdb_printf("Prints summary information about each pmcs instance.\n"
" -c: Dump the completion queue\n"
" -d: Print per-iport information about device tree children\n"
+ " -D <device ID>: With -q/-Q, filter by device handle\n"
" -e: Display the in-memory firmware event log\n"
" -h: Print more detailed hardware information\n"
" -i: Print interrupt coalescing information\n"
@@ -3023,8 +3243,8 @@ pmcs_tag_help()
}
static const mdb_dcmd_t dcmds[] = {
- { "pmcs", "?[-cdehiImpQqtTuwWv]", "print pmcs information",
- pmcs_dcmd, pmcs_help
+ { "pmcs", "?[-cdehiImpQqtTuwWv] [-D <device ID>]",
+ "print pmcs information", pmcs_dcmd, pmcs_help
},
{ "pmcs_log",
"?[-v] [-p PHY_PATH | -s SAS_ADDRESS | -l TAIL_LINES]",
diff --git a/usr/src/cmd/mdb/common/modules/sctp/sctp.c b/usr/src/cmd/mdb/common/modules/sctp/sctp.c
index b83ccc4b82..5901efcab8 100644
--- a/usr/src/cmd/mdb/common/modules/sctp/sctp.c
+++ b/usr/src/cmd/mdb/common/modules/sctp/sctp.c
@@ -144,30 +144,31 @@ sctp_faddr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_ERR);
}
- statestr = sctp_faddr_state(fa->state);
- mdb_printf("%<u>%p\t%<b>%N%</b>\t%s%</u>\n", addr, &fa->faddr,
+ statestr = sctp_faddr_state(fa->sf_state);
+ mdb_printf("%<u>%p\t%<b>%N%</b>\t%s%</u>\n", addr, &fa->sf_faddr,
statestr);
- mdb_printf("next\t\t%?p\tsaddr\t%N\n", fa->next, &fa->saddr);
- mdb_printf("rto\t\t%?d\tsrtt\t\t%?d\n", fa->rto, fa->srtt);
- mdb_printf("rttvar\t\t%?d\trtt_updates\t%?u\n", fa->rttvar,
- fa->rtt_updates);
- mdb_printf("strikes\t\t%?d\tmax_retr\t%?d\n", fa->strikes,
- fa->max_retr);
- mdb_printf("hb_expiry\t%?ld\thb_interval\t%?u\n", fa->hb_expiry,
- fa->hb_interval);
- mdb_printf("pmss\t\t%?u\tcwnd\t\t%?u\n", fa->sfa_pmss, fa->cwnd);
- mdb_printf("ssthresh\t%?u\tsuna\t\t%?u\n", fa->ssthresh, fa->suna);
- mdb_printf("pba\t\t%?u\tacked\t\t%?u\n", fa->pba, fa->acked);
- mdb_printf("lastactive\t%?ld\thb_secret\t%?#lx\n", fa->lastactive,
- fa->hb_secret);
- mdb_printf("rxt_unacked\t%?u\n", fa->rxt_unacked);
- mdb_printf("timer_mp\t%?p\tixa\t\t%?p\n", fa->timer_mp, fa->ixa);
+ mdb_printf("next\t\t%?p\tsaddr\t%N\n", fa->sf_next, &fa->sf_saddr);
+ mdb_printf("rto\t\t%?d\tsrtt\t\t%?d\n", fa->sf_rto, fa->sf_srtt);
+ mdb_printf("rttvar\t\t%?d\trtt_updates\t%?u\n", fa->sf_rttvar,
+ fa->sf_rtt_updates);
+ mdb_printf("strikes\t\t%?d\tmax_retr\t%?d\n", fa->sf_strikes,
+ fa->sf_max_retr);
+ mdb_printf("hb_expiry\t%?ld\thb_interval\t%?u\n", fa->sf_hb_expiry,
+ fa->sf_hb_interval);
+ mdb_printf("pmss\t\t%?u\tcwnd\t\t%?u\n", fa->sf_pmss, fa->sf_cwnd);
+ mdb_printf("ssthresh\t%?u\tsuna\t\t%?u\n", fa->sf_ssthresh,
+ fa->sf_suna);
+ mdb_printf("pba\t\t%?u\tacked\t\t%?u\n", fa->sf_pba, fa->sf_acked);
+ mdb_printf("lastactive\t%?ld\thb_secret\t%?#lx\n", fa->sf_lastactive,
+ fa->sf_hb_secret);
+ mdb_printf("rxt_unacked\t%?u\n", fa->sf_rxt_unacked);
+ mdb_printf("timer_mp\t%?p\tixa\t\t%?p\n", fa->sf_timer_mp, fa->sf_ixa);
mdb_printf("hb_enabled\t%?d\thb_pending\t%?d\n"
"timer_running\t%?d\tdf\t\t%?d\n"
"pmtu_discovered\t%?d\tisv4\t\t%?d\n"
"retransmissions\t%?u\n",
- fa->hb_enabled, fa->hb_pending, fa->timer_running, fa->df,
- fa->pmtu_discovered, fa->isv4, fa->T3expire);
+ fa->sf_hb_enabled, fa->sf_hb_pending, fa->sf_timer_running,
+ fa->sf_df, fa->sf_pmtu_discovered, fa->sf_isv4, fa->sf_T3expire);
return (DCMD_OK);
}
@@ -453,8 +454,8 @@ sctp_reass_list(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
"\t\tprev: %?p\tcont: %?p\n", addr, srpmp.b_next,
srpmp.b_prev, srpmp.b_cont);
mdb_printf("\t\tssn: %hu\tneeded: %hu\tgot: %hu\ttail: %?p\n"
- "\t\tpartial_delivered: %s\n", srp.ssn, srp.needed,
- srp.got, srp.tail, srp.partial_delivered ? "TRUE" :
+ "\t\tpartial_delivered: %s\n", srp.sr_ssn, srp.sr_needed,
+ srp.sr_got, srp.sr_tail, srp.sr_partial_delivered ? "TRUE" :
"FALSE");
/* display the contents of this ssn's reassemby list */
@@ -671,9 +672,9 @@ print_faddr(uintptr_t ptr, const void *addr, void *cbdata)
sctp_faddr_t *faddr = (sctp_faddr_t *)addr;
int *i = cbdata;
- statestr = sctp_faddr_state(faddr->state);
+ statestr = sctp_faddr_state(faddr->sf_state);
- mdb_printf("\t%d:\t%N\t%?p (%s)\n", (*i)++, &faddr->faddr, ptr,
+ mdb_printf("\t%d:\t%N\t%?p (%s)\n", (*i)++, &faddr->sf_faddr, ptr,
statestr);
return (WALK_NEXT);
}
@@ -732,7 +733,7 @@ sctp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
/* non-verbose faddrs, suitable for pipelines to sctp_faddr */
if (paddr != 0) {
sctp_faddr_t faddr, *fp;
- for (fp = sctp->sctp_faddrs; fp != NULL; fp = faddr.next) {
+ for (fp = sctp->sctp_faddrs; fp != NULL; fp = faddr.sf_next) {
if (mdb_vread(&faddr, sizeof (faddr), (uintptr_t)fp)
== -1) {
mdb_warn("failed to read faddr at %p",
@@ -754,7 +755,7 @@ sctp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
sctp_faddr_t faddr;
if (mdb_vread(&faddr, sizeof (faddr),
(uintptr_t)sctp->sctp_faddrs) != -1)
- mdb_printf("%<u> %N%</u>", &faddr.faddr);
+ mdb_printf("%<u> %N%</u>", &faddr.sf_faddr);
}
mdb_printf("\n");
@@ -829,10 +830,10 @@ sctp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
mdb_printf("%<b>Flow Control%</b>\n");
mdb_printf("tconn_sndbuf\t%?d\n"
"conn_sndlowat\t%?d\tfrwnd\t\t%?u\n"
- "rwnd\t\t%?u\tinitial rwnd\t%?u\n"
+ "rwnd\t\t%?u\tlast advertised rwnd\t%?u\n"
"rxqueued\t%?u\tcwnd_max\t%?u\n", connp->conn_sndbuf,
connp->conn_sndlowat, sctp->sctp_frwnd,
- sctp->sctp_rwnd, sctp->sctp_irwnd, sctp->sctp_rxqueued,
+ sctp->sctp_rwnd, sctp->sctp_arwnd, sctp->sctp_rxqueued,
sctp->sctp_cwnd_max);
}
@@ -1229,7 +1230,7 @@ sctp_walk_faddr_step(mdb_walk_state_t *wsp)
status = wsp->walk_callback(faddr_ptr, &sctp_faddr, wsp->walk_cbdata);
if (status != WALK_NEXT)
return (status);
- if ((faddr_ptr = (uintptr_t)sctp_faddr.next) == NULL) {
+ if ((faddr_ptr = (uintptr_t)sctp_faddr.sf_next) == NULL) {
return (WALK_DONE);
} else {
wsp->walk_addr = faddr_ptr;
@@ -1499,6 +1500,68 @@ sctp_stack_ipif_walk_step(mdb_walk_state_t *wsp)
wsp->walk_cbdata));
}
+/*
+ * Initialization function for the per CPU SCTP stats counter walker of a given
+ * SCTP stack.
+ */
+int
+sctps_sc_walk_init(mdb_walk_state_t *wsp)
+{
+ sctp_stack_t sctps;
+
+ if (wsp->walk_addr == NULL)
+ return (WALK_ERR);
+
+ if (mdb_vread(&sctps, sizeof (sctps), wsp->walk_addr) == -1) {
+ mdb_warn("failed to read sctp_stack_t at %p", wsp->walk_addr);
+ return (WALK_ERR);
+ }
+ if (sctps.sctps_sc_cnt == 0)
+ return (WALK_DONE);
+
+ /*
+ * Store the sctp_stack_t pointer in walk_data. The stepping function
+ * used it to calculate if the end of the counter has reached.
+ */
+ wsp->walk_data = (void *)wsp->walk_addr;
+ wsp->walk_addr = (uintptr_t)sctps.sctps_sc;
+ return (WALK_NEXT);
+}
+
+/*
+ * Stepping function for the per CPU SCTP stats counterwalker.
+ */
+int
+sctps_sc_walk_step(mdb_walk_state_t *wsp)
+{
+ int status;
+ sctp_stack_t sctps;
+ sctp_stats_cpu_t *stats;
+ char *next, *end;
+
+ if (mdb_vread(&sctps, sizeof (sctps), (uintptr_t)wsp->walk_data) ==
+ -1) {
+ mdb_warn("failed to read sctp_stack_t at %p", wsp->walk_addr);
+ return (WALK_ERR);
+ }
+ if (mdb_vread(&stats, sizeof (stats), wsp->walk_addr) == -1) {
+ mdb_warn("failed ot read sctp_stats_cpu_t at %p",
+ wsp->walk_addr);
+ return (WALK_ERR);
+ }
+ status = wsp->walk_callback((uintptr_t)stats, &stats, wsp->walk_cbdata);
+ if (status != WALK_NEXT)
+ return (status);
+
+ next = (char *)wsp->walk_addr + sizeof (sctp_stats_cpu_t *);
+ end = (char *)sctps.sctps_sc + sctps.sctps_sc_cnt *
+ sizeof (sctp_stats_cpu_t *);
+ if (next >= end)
+ return (WALK_DONE);
+ wsp->walk_addr = (uintptr_t)next;
+ return (WALK_NEXT);
+}
+
static void
sctp_help(void)
{
@@ -1522,6 +1585,7 @@ sctp_help(void)
mdb_printf("\t-d\t Local and Peer addresses\n");
mdb_printf("\t-P\t Peer addresses\n");
}
+
static const mdb_dcmd_t dcmds[] = {
{ "sctp", ":[-afhoimrSFHpRCcedP]",
"display sctp control structure", sctp, sctp_help },
@@ -1591,8 +1655,8 @@ static const mdb_walker_t walkers[] = {
sctp_stack_ill_walk_init, sctp_stack_ill_walk_step, NULL },
{ "sctp_stack_walk_ipif", "walk the sctp_g_ipif list for one stack",
sctp_stack_ipif_walk_init, sctp_stack_ipif_walk_step, NULL },
- { "sctp_stacks", "walk all the sctp_stack_t",
- sctp_stacks_walk_init, sctp_stacks_walk_step, NULL },
+ { "sctps_sc", "walk all the per CPU stats counters of a sctp_stack_t",
+ sctps_sc_walk_init, sctps_sc_walk_step, NULL },
{ NULL }
};
diff --git a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c
index 58ebaa2945..ebb843c193 100644
--- a/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c
+++ b/usr/src/cmd/mdb/common/modules/smbsrv/smbsrv.c
@@ -375,6 +375,8 @@ static int smb_node(uintptr_t, uint_t, int, const mdb_arg_t *);
static int smb_node_walk_init(mdb_walk_state_t *);
static int smb_node_walk_step(mdb_walk_state_t *);
static int smb_lock(uintptr_t, uint_t, int, const mdb_arg_t *);
+static int smb_oplock(uintptr_t, uint_t, int, const mdb_arg_t *);
+static int smb_oplock_grant(uintptr_t, uint_t, int, const mdb_arg_t *);
static int smb_ace(uintptr_t, uint_t, int, const mdb_arg_t *);
static int smb_ace_walk_init(mdb_walk_state_t *);
static int smb_ace_walk_step(mdb_walk_state_t *);
@@ -443,8 +445,12 @@ static const mdb_dcmd_t dcmds[] = {
smb_dcmd_odir },
{ "smbofile",
"[-v]",
- "print smb_odir_t information",
+ "print smb_file_t information",
smb_dcmd_ofile },
+ { "smboplock", NULL,
+ "print smb_oplock_t information", smb_oplock },
+ { "smboplockgrant", NULL,
+ "print smb_oplock_grant_t information", smb_oplock_grant },
{ "smbstat", NULL,
"print all smb dispatched requests statistics",
smb_stats },
@@ -799,7 +805,7 @@ static const char *smb_request_state[SMB_REQ_STATE_SENTINEL] =
#define SMB_REQUEST_BANNER \
"%<b>%<u>%-?s %-?s %-14s %-14s %-16s %-32s%</u>%</b>\n"
#define SMB_REQUEST_FORMAT \
- "%-?p %-?p %e %e %-16s %s\n"
+ "%-?p %-?p %-14lld %-14lld %-16s %s\n"
static int
smb_dcmd_request(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
@@ -866,10 +872,12 @@ smb_dcmd_request(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
"FID(file): %u (%p)\n"
"PID: %u\n"
"MID: %u\n\n"
- "waiting time: %1.3e\n"
- "running time: %1.3e",
- sr->first_smb_com, smb_com[sr->first_smb_com],
- sr->smb_com, smb_com[sr->smb_com],
+ "waiting time: %lld\n"
+ "running time: %lld\n",
+ sr->first_smb_com,
+ smb_com[sr->first_smb_com].smb_com,
+ sr->smb_com,
+ smb_com[sr->smb_com].smb_com,
sr->sr_state, state,
sr->smb_tid, sr->tid_tree,
sr->smb_uid, sr->uid_user,
@@ -897,7 +905,7 @@ smb_dcmd_request(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
waiting,
running,
state,
- smb_com[sr->smb_com]);
+ smb_com[sr->smb_com].smb_com);
}
}
return (DCMD_OK);
@@ -1403,13 +1411,14 @@ static int
smb_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
smb_node_t node;
+ int rc;
int verbose = FALSE;
int print_full_path = FALSE;
int stack_trace = FALSE;
vnode_t vnode;
char od_name[MAXNAMELEN];
char path_name[1024];
- uintptr_t list_addr;
+ uintptr_t list_addr, oplock_addr;
if (mdb_getopts(argc, argv,
'v', MDB_OPT_SETBITS, TRUE, &verbose,
@@ -1446,9 +1455,10 @@ smb_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
"%-18s "
"%-6s "
"%-6s "
+ "%-8s "
"%-6s%</u>%</b>\n",
"ADDR", "VP", "NODE-NAME", "OFILES", "LOCKS",
- "REF");
+ "OPLOCK", "REF");
}
}
@@ -1489,11 +1499,25 @@ smb_node(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
}
(void) mdb_dec_indent(SMB_DCMD_INDENT);
}
+ if (node.n_oplock.ol_count == 0) {
+ mdb_printf("Opportunistic Locks: 0\n");
+ } else {
+ oplock_addr =
+ addr + offsetof(smb_node_t, n_oplock);
+ mdb_printf("Opportunistic Lock: %p\n",
+ oplock_addr);
+ rc = mdb_call_dcmd("smboplock", oplock_addr,
+ flags, argc, argv);
+ if (rc != DCMD_OK)
+ return (rc);
+ }
mdb_printf("Reference Count: %u\n\n", node.n_refcnt);
} else {
- mdb_printf("%-?p %-?p %-18s %-6d %-6d %-6d\n",
+ mdb_printf("%-?p %-?p %-18s %-6d %-6d %-8d %-6d ",
addr, node.vp, od_name, node.n_ofile_list.ll_count,
- node.n_lock_list.ll_count, node.n_refcnt);
+ node.n_lock_list.ll_count,
+ node.n_oplock.ol_count, node.n_refcnt);
+
if (print_full_path)
mdb_printf("\t%s\n", path_name);
}
@@ -1713,6 +1737,99 @@ smb_lock(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
}
/*
+ * *****************************************************************************
+ * ************************** smb_oplock_grant_t *******************************
+ * *****************************************************************************
+ */
+/*ARGSUSED*/
+static int
+smb_oplock_grant(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ smb_oplock_grant_t grant;
+ char *level;
+
+ if (!(flags & DCMD_ADDRSPEC))
+ return (DCMD_USAGE);
+
+ /*
+ * If this is the first invocation of the command, print a nice
+ * header line for the output that will follow.
+ */
+ if (DCMD_HDRSPEC(flags)) {
+ mdb_printf("%<u>%-16s %-10s %-16s%</u>\n",
+ "Grants:", "LEVEL", "OFILE");
+ }
+
+ if (mdb_vread(&grant, sizeof (grant), addr) == sizeof (grant)) {
+ switch (grant.og_level) {
+ case SMB_OPLOCK_EXCLUSIVE:
+ level = "EXCLUSIVE";
+ break;
+ case SMB_OPLOCK_BATCH:
+ level = "BATCH";
+ break;
+ case SMB_OPLOCK_LEVEL_II:
+ level = "LEVEL_II";
+ break;
+ default:
+ level = "UNKNOWN";
+ break;
+ }
+
+ mdb_printf("%-16p %-10s %-16p", addr, level, grant.og_ofile);
+ }
+ return (DCMD_OK);
+}
+
+/*
+ * *****************************************************************************
+ * ***************************** smb_oplock_t **********************************
+ * *****************************************************************************
+ */
+/*ARGSUSED*/
+static int
+smb_oplock(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
+{
+ smb_oplock_t oplock;
+ uintptr_t list_addr;
+
+ if (!(flags & DCMD_ADDRSPEC))
+ return (DCMD_USAGE);
+
+ if (mdb_vread(&oplock, sizeof (oplock), addr) != sizeof (oplock)) {
+ mdb_warn("failed to read struct smb_oplock at %p", addr);
+ return (DCMD_ERR);
+ }
+
+ if (oplock.ol_count == 0)
+ return (DCMD_OK);
+
+ (void) mdb_inc_indent(SMB_DCMD_INDENT);
+ switch (oplock.ol_break) {
+ case SMB_OPLOCK_BREAK_TO_NONE:
+ mdb_printf("Break Pending: BREAK_TO_NONE\n");
+ break;
+ case SMB_OPLOCK_BREAK_TO_LEVEL_II:
+ mdb_printf(
+ "Break Pending: BREAK_TO_LEVEL_II\n");
+ break;
+ default:
+ break;
+ }
+
+ list_addr = addr + offsetof(smb_oplock_t, ol_grants);
+
+ if (mdb_pwalk_dcmd("list", "smboplockgrant",
+ argc, argv, list_addr)) {
+ mdb_warn("failed to walk oplock grants");
+ }
+
+ (void) mdb_dec_indent(SMB_DCMD_INDENT);
+
+ return (DCMD_OK);
+}
+
+/*
* ::smbstat
*
* Prints SMB requests statistics.
diff --git a/usr/src/cmd/mdb/common/modules/svc.startd/startd.c b/usr/src/cmd/mdb/common/modules/svc.startd/startd.c
index 1f163db4a0..4fae0df7f1 100644
--- a/usr/src/cmd/mdb/common/modules/svc.startd/startd.c
+++ b/usr/src/cmd/mdb/common/modules/svc.startd/startd.c
@@ -19,12 +19,9 @@
* CDDL HEADER END
*/
/*
- * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <mdb/mdb_modapi.h>
#include <libuutil.h>
@@ -59,6 +56,14 @@ startd_status(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
u_longlong_t lookups;
u_longlong_t dep_inserts, dep_cycle_ns, dep_insert_ns;
size_t graph_num, restarter_num;
+ uint64_t ct_maint;
+ uint64_t ct_hwerr;
+ uint64_t ct_service;
+ uint64_t ct_global;
+ uint64_t ct_noprefs;
+ uint64_t ct_from_uninit;
+ uint64_t ct_bad_state;
+ uint64_t ct_ovr_prefs;
if (mdb_readvar(&lookups, "dictionary_lookups") == -1) {
mdb_warn("failed to read 'dictionary_lookups' value\n");
@@ -109,18 +114,71 @@ startd_status(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_ERR);
}
+ if (mdb_readvar(&ct_maint, "stev_ct_maint") == -1) {
+ mdb_warn("failed to read 'stev_ct_maint'\n");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_readvar(&ct_hwerr, "stev_ct_hwerr") == -1) {
+ mdb_warn("failed to read 'stev_ct_hwerr'\n");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_readvar(&ct_service, "stev_ct_service") == -1) {
+ mdb_warn("failed to read 'stev_ct_service'\n");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_readvar(&ct_global, "stev_ct_global") == -1) {
+ mdb_warn("failed to read 'stev_ct_global'\n");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_readvar(&ct_noprefs, "stev_ct_noprefs") == -1) {
+ mdb_warn("failed to read 'stev_ct_noprefs'\n");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_readvar(&ct_from_uninit, "stev_ct_from_uninit") == -1) {
+ mdb_warn("failed to read 'stev_ct_from_uninit'\n");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_readvar(&ct_bad_state, "stev_ct_bad_state") == -1) {
+ mdb_warn("failed to read 'stev_ct_bad_state'\n");
+ return (DCMD_ERR);
+ }
+
+ if (mdb_readvar(&ct_ovr_prefs, "stev_ct_ovr_prefs") == -1) {
+ mdb_warn("failed to read 'stev_ct_ovr_prefs'\n");
+ return (DCMD_ERR);
+ }
+
mdb_printf(
- " dictionary lookups: %llu\n"
- " average lookup time: %llu us\n"
- "graph dependency insertions: %llu\n"
- " average cycle-check time: %llu us\n"
- " avg dependency insert time: %llu us\n"
- "number of nodes in dgraph: %llu\n"
- "number of nodes in instance_list: %llu\n", lookups,
+ "General stats\n"
+ " dictionary lookups: %llu\n"
+ " average lookup time: %llu us\n"
+ " graph dependency insertions: %llu\n"
+ " average cycle-check time: %llu us\n"
+ " avg dependency insert time: %llu us\n"
+ " number of nodes in dgraph: %llu\n"
+ "number of nodes in instance_list: %llu\n"
+ "\nState Transition Events\n"
+ " maintenance: %llu\n"
+ " hardware error: %llu\n"
+ " service specific pref: %llu\n"
+ " system wide pref: %llu\n"
+ " no prefs, not raised: %llu\n"
+ " from unint, not raised: %llu\n"
+ " bad state, not raised: %llu\n"
+ " override pref, raised: %llu\n", lookups,
lookups ? ns_total / (1000 * lookups) : 0, dep_inserts,
dep_inserts ? dep_cycle_ns / (1000 * dep_inserts) : 0,
dep_inserts ? dep_insert_ns / (1000 * dep_inserts) : 0,
- (u_longlong_t)graph_num, (u_longlong_t)restarter_num);
+ (u_longlong_t)graph_num, (u_longlong_t)restarter_num,
+ ct_maint, ct_hwerr, ct_service, ct_global, ct_noprefs,
+ ct_from_uninit, ct_bad_state, ct_ovr_prefs);
+
return (DCMD_OK);
}