summaryrefslogtreecommitdiff
path: root/usr/src/cmd/mdb/common/modules
diff options
context:
space:
mode:
authortomee <none@none>2008-05-26 17:53:26 -0700
committertomee <none@none>2008-05-26 17:53:26 -0700
commitb5fca8f855054d167d04d3b4de5210c83ed2083c (patch)
tree6b4e690195757449698c233ae276ae53b52a9c9b /usr/src/cmd/mdb/common/modules
parent7568150a58e78021968b6c22bc28e9787b33496a (diff)
downloadillumos-gate-b5fca8f855054d167d04d3b4de5210c83ed2083c.tar.gz
6554564 slab allocator cannot release slabs with lonely buffers
6676406 kmem client constructors and destructors need some cleanup
Diffstat (limited to 'usr/src/cmd/mdb/common/modules')
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/Makefile.files3
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/avl.c110
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/avl.h15
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/combined.c165
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/combined.h49
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/genunix.c13
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/kmem.c379
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/kmem.h8
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/list.c94
-rw-r--r--usr/src/cmd/mdb/common/modules/genunix/list.h21
10 files changed, 660 insertions, 197 deletions
diff --git a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
index 8bf1c1b520..661a69e561 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
+++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#ident "%Z%%M% %I% %E% SMI"
@@ -32,6 +32,7 @@
GENUNIX_SRCS = \
avl.c \
bio.c \
+ combined.c \
contract.c \
cpupart.c \
ctxop.c \
diff --git a/usr/src/cmd/mdb/common/modules/genunix/avl.c b/usr/src/cmd/mdb/common/modules/genunix/avl.c
index b10856cfc3..444af78fa1 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/avl.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/avl.c
@@ -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,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -31,8 +30,12 @@
#include <mdb/mdb_modapi.h>
struct aw_info {
- void *aw_buff; /* buffer to hold the tree's data structure */
+ void *aw_buff; /* buffer to hold tree element */
avl_tree_t aw_tree; /* copy of avl_tree_t being walked */
+ uintptr_t aw_end; /* last node in specified range */
+ const char *aw_elem_name;
+ int (*aw_elem_check)(void *, uintptr_t, void *);
+ void *aw_elem_check_arg;
};
/*
@@ -40,14 +43,15 @@ struct aw_info {
* an AVL node
*/
static uintptr_t
-avl_leftmostchild(uintptr_t addr, void * buff, size_t offset, size_t size)
+avl_leftmostchild(uintptr_t addr, void *buff, size_t offset, size_t size,
+ const char *elem_name)
{
avl_node_t *node = (avl_node_t *)((uintptr_t)buff + offset);
for (;;) {
addr -= offset;
if (mdb_vread(buff, size, addr) == -1) {
- mdb_warn("read of avl_node_t failed: %p", addr);
+ mdb_warn("failed to read %s at %#lx", elem_name, addr);
return ((uintptr_t)-1L);
}
if (node->avl_child[0] == NULL)
@@ -59,14 +63,32 @@ avl_leftmostchild(uintptr_t addr, void * buff, size_t offset, size_t size)
/*
* initialize a forward walk thru an avl tree.
+ *
+ * begin and end optionally specify objects other than the first and last
+ * objects in the tree; either or both may be NULL (defaulting to first and
+ * last).
+ *
+ * avl_name and element_name specify command-specific labels other than
+ * "avl_tree_t" and "tree element" for use in error messages.
+ *
+ * element_check() returns -1, 1, or 0: abort the walk with an error, stop
+ * without an error, or allow the normal callback; arg is an optional user
+ * argument to element_check().
*/
int
-avl_walk_init(mdb_walk_state_t *wsp)
+avl_walk_init_range(mdb_walk_state_t *wsp, uintptr_t begin, uintptr_t end,
+ const char *avl_name, const char *element_name,
+ int (*element_check)(void *, uintptr_t, void *), void *arg)
{
struct aw_info *aw;
avl_tree_t *tree;
uintptr_t addr;
+ if (avl_name == NULL)
+ avl_name = "avl_tree_t";
+ if (element_name == NULL)
+ element_name = "tree element";
+
/*
* allocate the AVL walk data
*/
@@ -77,7 +99,7 @@ avl_walk_init(mdb_walk_state_t *wsp)
*/
tree = &aw->aw_tree;
if (mdb_vread(tree, sizeof (avl_tree_t), wsp->walk_addr) == -1) {
- mdb_warn("read of avl_tree_t failed: %p", wsp->walk_addr);
+ mdb_warn("failed to read %s at %#lx", avl_name, wsp->walk_addr);
goto error;
}
if (tree->avl_size < tree->avl_offset + sizeof (avl_node_t)) {
@@ -91,22 +113,30 @@ avl_walk_init(mdb_walk_state_t *wsp)
* "node" always points at the avl_node_t field inside the struct
*/
aw->aw_buff = mdb_zalloc(tree->avl_size, UM_SLEEP);
+ aw->aw_end = (end == NULL ? NULL : end + tree->avl_offset);
+ aw->aw_elem_name = element_name;
+ aw->aw_elem_check = element_check;
+ aw->aw_elem_check_arg = arg;
/*
* get the first avl_node_t address, use same algorithm
* as avl_start() -- leftmost child in tree from root
*/
- addr = (uintptr_t)tree->avl_root;
- if (addr == NULL) {
- wsp->walk_addr = NULL;
- return (WALK_NEXT);
+ if (begin == NULL) {
+ addr = (uintptr_t)tree->avl_root;
+ if (addr == NULL) {
+ wsp->walk_addr = NULL;
+ return (WALK_NEXT);
+ }
+ addr = avl_leftmostchild(addr, aw->aw_buff, tree->avl_offset,
+ tree->avl_size, aw->aw_elem_name);
+ if (addr == (uintptr_t)-1L)
+ goto error;
+ wsp->walk_addr = addr;
+ } else {
+ wsp->walk_addr = begin + tree->avl_offset;
}
- addr = avl_leftmostchild(addr, aw->aw_buff, tree->avl_offset,
- tree->avl_size);
- if (addr == (uintptr_t)-1L)
- goto error;
- wsp->walk_addr = addr;
return (WALK_NEXT);
error:
@@ -116,6 +146,29 @@ error:
return (WALK_ERR);
}
+int
+avl_walk_init(mdb_walk_state_t *wsp)
+{
+ return (avl_walk_init_range(wsp, NULL, NULL, NULL, NULL, NULL, NULL));
+}
+
+int
+avl_walk_init_named(mdb_walk_state_t *wsp,
+ const char *avl_name, const char *element_name)
+{
+ return (avl_walk_init_range(wsp, NULL, NULL, avl_name, element_name,
+ NULL, NULL));
+}
+
+int
+avl_walk_init_checked(mdb_walk_state_t *wsp,
+ const char *avl_name, const char *element_name,
+ int (*element_check)(void *, uintptr_t, void *), void *arg)
+{
+ return (avl_walk_init_range(wsp, NULL, NULL, avl_name, element_name,
+ element_check, arg));
+}
+
/*
* At each step, visit (callback) the current node, then move to the next
* in the AVL tree. Uses the same algorithm as avl_walk().
@@ -139,6 +192,10 @@ avl_walk_step(mdb_walk_state_t *wsp)
return (WALK_DONE);
aw = (struct aw_info *)wsp->walk_data;
+
+ if (aw->aw_end != NULL && wsp->walk_addr == aw->aw_end)
+ return (WALK_DONE);
+
size = aw->aw_tree.avl_size;
offset = aw->aw_tree.avl_offset;
node = (avl_node_t *)((uintptr_t)aw->aw_buff + offset);
@@ -147,10 +204,19 @@ avl_walk_step(mdb_walk_state_t *wsp)
* must read the current node for the call back to use
*/
if (mdb_vread(aw->aw_buff, size, addr) == -1) {
- mdb_warn("read of avl_node_t failed: %p", addr);
+ mdb_warn("failed to read %s at %#lx", aw->aw_elem_name, addr);
return (WALK_ERR);
}
+ if (aw->aw_elem_check != NULL) {
+ int rc = aw->aw_elem_check(aw->aw_buff, addr,
+ aw->aw_elem_check_arg);
+ if (rc == -1)
+ return (WALK_ERR);
+ else if (rc == 1)
+ return (WALK_DONE);
+ }
+
/*
* do the call back
*/
@@ -169,7 +235,8 @@ avl_walk_step(mdb_walk_state_t *wsp)
*/
addr = (uintptr_t)node->avl_child[1];
if (addr != NULL) {
- addr = avl_leftmostchild(addr, aw->aw_buff, offset, size);
+ addr = avl_leftmostchild(addr, aw->aw_buff, offset, size,
+ aw->aw_elem_name);
if (addr == (uintptr_t)-1L)
return (WALK_ERR);
@@ -187,7 +254,8 @@ avl_walk_step(mdb_walk_state_t *wsp)
if (was_child == 0) /* stop on return from left child */
break;
if (mdb_vread(aw->aw_buff, size, addr) == -1) {
- mdb_warn("read of avl_node_t failed: %p", addr);
+ mdb_warn("failed to read %s at %#lx",
+ aw->aw_elem_name, addr);
return (WALK_ERR);
}
}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/avl.h b/usr/src/cmd/mdb/common/modules/genunix/avl.h
index 1d2e9dcb88..6f3bc202b7 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/avl.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/avl.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,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -38,6 +37,14 @@ extern "C" {
"entries in tree"
extern int avl_walk_init(mdb_walk_state_t *);
+extern int avl_walk_init_named(mdb_walk_state_t *wsp,
+ const char *, const char *);
+extern int avl_walk_init_checked(mdb_walk_state_t *wsp,
+ const char *, const char *,
+ int (*)(void *, uintptr_t, void *), void *);
+extern int avl_walk_init_range(mdb_walk_state_t *wsp, uintptr_t, uintptr_t,
+ const char *, const char *,
+ int (*)(void *, uintptr_t, void *), void *);
extern int avl_walk_step(mdb_walk_state_t *);
extern void avl_walk_fini(mdb_walk_state_t *wsp);
diff --git a/usr/src/cmd/mdb/common/modules/genunix/combined.c b/usr/src/cmd/mdb/common/modules/genunix/combined.c
new file mode 100644
index 0000000000..412761a7bd
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/genunix/combined.c
@@ -0,0 +1,165 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_modapi.h>
+
+typedef struct combined_walk {
+ int (*cw_init)(mdb_walk_state_t *);
+ int (*cw_step)(mdb_walk_state_t *);
+ void (*cw_fini)(mdb_walk_state_t *);
+ struct combined_walk *cw_next;
+ void *cw_data;
+ boolean_t cw_initialized;
+} combined_walk_t;
+
+typedef struct combined_walk_data {
+ uintptr_t cwd_initial_walk_addr; /* to init each walk */
+ combined_walk_t *cwd_current_walk;
+ combined_walk_t *cwd_final_walk; /* tail pointer */
+} combined_walk_data_t;
+
+/*
+ * Initialize a combined walk to
+ * A) present a single concatenated series of elements from different
+ * structures, or
+ * B) select from several possible walks at runtime.
+ * Multiple walks are done in the same order passed to combined_walk_add(). Each
+ * walk is initialized with the same wsp->walk_addr.
+ */
+void
+combined_walk_init(mdb_walk_state_t *wsp)
+{
+ combined_walk_data_t *cwd;
+
+ cwd = mdb_alloc(sizeof (combined_walk_data_t), UM_SLEEP);
+
+ cwd->cwd_initial_walk_addr = wsp->walk_addr;
+ cwd->cwd_current_walk = cwd->cwd_final_walk = NULL;
+ wsp->walk_data = cwd;
+}
+
+static void
+combined_walk_append(combined_walk_data_t *cwd, combined_walk_t *cw)
+{
+ if (cwd->cwd_final_walk == NULL) {
+ cwd->cwd_current_walk = cwd->cwd_final_walk = cw;
+ } else {
+ cwd->cwd_final_walk->cw_next = cw;
+ cwd->cwd_final_walk = cw;
+ }
+}
+
+static combined_walk_t *
+combined_walk_remove_current(combined_walk_data_t *cwd)
+{
+ combined_walk_t *cw = cwd->cwd_current_walk;
+ if (cw == NULL) {
+ return (NULL);
+ }
+ if (cw == cwd->cwd_final_walk) {
+ cwd->cwd_final_walk = cw->cw_next;
+ }
+ cwd->cwd_current_walk = cw->cw_next;
+ cw->cw_next = NULL;
+ return (cw);
+}
+
+void
+combined_walk_add(mdb_walk_state_t *wsp,
+ int (*walk_init)(mdb_walk_state_t *),
+ int (*walk_step)(mdb_walk_state_t *),
+ void (*walk_fini)(mdb_walk_state_t *))
+{
+ combined_walk_data_t *cwd = wsp->walk_data;
+ combined_walk_t *cw;
+
+ cw = mdb_alloc(sizeof (combined_walk_t), UM_SLEEP);
+
+ cw->cw_init = walk_init;
+ cw->cw_step = walk_step;
+ cw->cw_fini = walk_fini;
+ cw->cw_next = NULL;
+ cw->cw_data = NULL;
+ cw->cw_initialized = B_FALSE;
+
+ combined_walk_append(cwd, cw);
+}
+
+int
+combined_walk_step(mdb_walk_state_t *wsp)
+{
+ combined_walk_data_t *cwd = wsp->walk_data;
+ combined_walk_t *cw = cwd->cwd_current_walk;
+ int status;
+
+ if (cw == NULL) {
+ return (WALK_DONE);
+ }
+
+ if (cw->cw_initialized) {
+ wsp->walk_data = cw->cw_data;
+ } else {
+ wsp->walk_addr = cwd->cwd_initial_walk_addr;
+ status = cw->cw_init(wsp);
+ cw->cw_data = wsp->walk_data;
+ cw->cw_initialized = B_TRUE;
+ if (status != WALK_NEXT) {
+ wsp->walk_data = cwd;
+ return (status);
+ }
+ }
+
+ status = cw->cw_step(wsp);
+
+ if (status == WALK_DONE) {
+ (void) combined_walk_remove_current(cwd);
+ cw->cw_fini(wsp);
+ mdb_free(cw, sizeof (combined_walk_t));
+ wsp->walk_data = cwd;
+ return (combined_walk_step(wsp));
+ }
+
+ wsp->walk_data = cwd;
+ return (status);
+}
+
+void
+combined_walk_fini(mdb_walk_state_t *wsp)
+{
+ combined_walk_data_t *cwd = wsp->walk_data;
+ combined_walk_t *cw;
+
+ while ((cw = combined_walk_remove_current(cwd)) != NULL) {
+ if (cw->cw_initialized) {
+ wsp->walk_data = cw->cw_data;
+ cw->cw_fini(wsp);
+ }
+ mdb_free(cw, sizeof (combined_walk_t));
+ }
+
+ mdb_free(cwd, sizeof (combined_walk_data_t));
+}
diff --git a/usr/src/cmd/mdb/common/modules/genunix/combined.h b/usr/src/cmd/mdb/common/modules/genunix/combined.h
new file mode 100644
index 0000000000..f4f48c7b54
--- /dev/null
+++ b/usr/src/cmd/mdb/common/modules/genunix/combined.h
@@ -0,0 +1,49 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _COMBINED_H
+#define _COMBINED_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <mdb/mdb_modapi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void combined_walk_init(mdb_walk_state_t *wsp);
+extern void combined_walk_add(mdb_walk_state_t *wsp,
+ int (*walk_init)(mdb_walk_state_t *),
+ int (*walk_step)(mdb_walk_state_t *),
+ void (*walk_fini)(mdb_walk_state_t *));
+extern int combined_walk_step(mdb_walk_state_t *wsp);
+extern void combined_walk_fini(mdb_walk_state_t *wsp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _COMBINED_H */
diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
index 36d61da041..8fb79f02b6 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c
@@ -69,6 +69,7 @@
#include <sys/port_impl.h>
#include "avl.h"
+#include "combined.h"
#include "contract.h"
#include "cpupart_mdb.h"
#include "devinfo.h"
@@ -3370,9 +3371,10 @@ static const mdb_dcmd_t dcmds[] = {
kmastat },
{ "kmausers", "?[-ef] [cache ...]", "current medium and large users "
"of the kmem allocator", kmausers, kmausers_help },
- { "kmem_cache", "?", "print kernel memory caches", kmem_cache },
- { "kmem_slabs", "?[-v] [-n cache] [-b maxbins] [-B minbinsize]",
- "display slab usage per kmem cache",
+ { "kmem_cache", "?[-n name]",
+ "print kernel memory caches", kmem_cache, kmem_cache_help},
+ { "kmem_slabs", "?[-v] [-n cache] [-N cache] [-b maxbins] "
+ "[-B minbinsize]", "display slab usage per kmem cache",
kmem_slabs, kmem_slabs_help },
{ "kmem_debug", NULL, "toggle kmem dcmd/walk debugging", kmem_debug },
{ "kmem_log", "?[-b]", "dump kmem transaction log", kmem_log },
@@ -3705,10 +3707,11 @@ static const mdb_walker_t walkers[] = {
{ "kmem_log", "walk the kmem transaction log",
kmem_log_walk_init, kmem_log_walk_step, kmem_log_walk_fini },
{ "kmem_slab", "given a kmem cache, walk its slabs",
- kmem_slab_walk_init, kmem_slab_walk_step, NULL },
+ kmem_slab_walk_init, combined_walk_step, combined_walk_fini },
{ "kmem_slab_partial",
"given a kmem cache, walk its partially allocated slabs (min 1)",
- kmem_slab_walk_partial_init, kmem_slab_walk_step, NULL },
+ kmem_slab_walk_partial_init, combined_walk_step,
+ combined_walk_fini },
{ "vmem", "walk vmem structures in pre-fix, depth-first order",
vmem_walk_init, vmem_walk_step, vmem_walk_fini },
{ "vmem_alloc", "given a vmem_t, walk its allocated vmem_segs",
diff --git a/usr/src/cmd/mdb/common/modules/genunix/kmem.c b/usr/src/cmd/mdb/common/modules/genunix/kmem.c
index 47ad09126f..8d4c3b8638 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/kmem.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/kmem.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -39,9 +39,12 @@
#include <sys/sysmacros.h>
#include <vm/page.h>
+#include "avl.h"
+#include "combined.h"
#include "dist.h"
#include "kmem.h"
#include "leaky.h"
+#include "list.h"
#define dprintf(x) if (mdb_debug_level) { \
mdb_printf("kmem debug: "); \
@@ -92,65 +95,19 @@ kmem_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_OK);
}
-typedef struct {
- uintptr_t kcw_first;
- uintptr_t kcw_current;
-} kmem_cache_walk_t;
-
int
kmem_cache_walk_init(mdb_walk_state_t *wsp)
{
- kmem_cache_walk_t *kcw;
- kmem_cache_t c;
- uintptr_t cp;
GElf_Sym sym;
- if (mdb_lookup_by_name("kmem_null_cache", &sym) == -1) {
- mdb_warn("couldn't find kmem_null_cache");
+ if (mdb_lookup_by_name("kmem_caches", &sym) == -1) {
+ mdb_warn("couldn't find kmem_caches");
return (WALK_ERR);
}
- cp = (uintptr_t)sym.st_value;
-
- if (mdb_vread(&c, sizeof (kmem_cache_t), cp) == -1) {
- mdb_warn("couldn't read cache at %p", cp);
- return (WALK_ERR);
- }
+ wsp->walk_addr = (uintptr_t)sym.st_value;
- kcw = mdb_alloc(sizeof (kmem_cache_walk_t), UM_SLEEP);
-
- kcw->kcw_first = cp;
- kcw->kcw_current = (uintptr_t)c.cache_next;
- wsp->walk_data = kcw;
-
- return (WALK_NEXT);
-}
-
-int
-kmem_cache_walk_step(mdb_walk_state_t *wsp)
-{
- kmem_cache_walk_t *kcw = wsp->walk_data;
- kmem_cache_t c;
- int status;
-
- if (mdb_vread(&c, sizeof (kmem_cache_t), kcw->kcw_current) == -1) {
- mdb_warn("couldn't read cache at %p", kcw->kcw_current);
- return (WALK_DONE);
- }
-
- status = wsp->walk_callback(kcw->kcw_current, &c, wsp->walk_cbdata);
-
- if ((kcw->kcw_current = (uintptr_t)c.cache_next) == kcw->kcw_first)
- return (WALK_DONE);
-
- return (status);
-}
-
-void
-kmem_cache_walk_fini(mdb_walk_state_t *wsp)
-{
- kmem_cache_walk_t *kcw = wsp->walk_data;
- mdb_free(kcw, sizeof (kmem_cache_walk_t));
+ return (list_walk_init_named(wsp, "cache list", "cache"));
}
int
@@ -188,29 +145,134 @@ kmem_cpu_cache_walk_step(mdb_walk_state_t *wsp)
return (wsp->walk_callback(caddr, &cc, wsp->walk_cbdata));
}
+static int
+kmem_slab_check(void *p, uintptr_t saddr, void *arg)
+{
+ kmem_slab_t *sp = p;
+ uintptr_t caddr = (uintptr_t)arg;
+ if ((uintptr_t)sp->slab_cache != caddr) {
+ mdb_warn("slab %p isn't in cache %p (in cache %p)\n",
+ saddr, caddr, sp->slab_cache);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+kmem_partial_slab_check(void *p, uintptr_t saddr, void *arg)
+{
+ kmem_slab_t *sp = p;
+
+ int rc = kmem_slab_check(p, saddr, arg);
+ if (rc != 0) {
+ return (rc);
+ }
+
+ if (!KMEM_SLAB_IS_PARTIAL(sp)) {
+ mdb_warn("slab %p is not a partial slab\n", saddr);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+kmem_complete_slab_check(void *p, uintptr_t saddr, void *arg)
+{
+ kmem_slab_t *sp = p;
+
+ int rc = kmem_slab_check(p, saddr, arg);
+ if (rc != 0) {
+ return (rc);
+ }
+
+ if (!KMEM_SLAB_IS_ALL_USED(sp)) {
+ mdb_warn("slab %p is not completely allocated\n", saddr);
+ return (-1);
+ }
+
+ return (0);
+}
+
+typedef struct {
+ uintptr_t kns_cache_addr;
+ int kns_nslabs;
+} kmem_nth_slab_t;
+
+static int
+kmem_nth_slab_check(void *p, uintptr_t saddr, void *arg)
+{
+ kmem_nth_slab_t *chkp = arg;
+
+ int rc = kmem_slab_check(p, saddr, (void *)chkp->kns_cache_addr);
+ if (rc != 0) {
+ return (rc);
+ }
+
+ return (chkp->kns_nslabs-- == 0 ? 1 : 0);
+}
+
+static int
+kmem_complete_slab_walk_init(mdb_walk_state_t *wsp)
+{
+ uintptr_t caddr = wsp->walk_addr;
+
+ wsp->walk_addr = (uintptr_t)(caddr +
+ offsetof(kmem_cache_t, cache_complete_slabs));
+
+ return (list_walk_init_checked(wsp, "slab list", "slab",
+ kmem_complete_slab_check, (void *)caddr));
+}
+
+static int
+kmem_partial_slab_walk_init(mdb_walk_state_t *wsp)
+{
+ uintptr_t caddr = wsp->walk_addr;
+
+ wsp->walk_addr = (uintptr_t)(caddr +
+ offsetof(kmem_cache_t, cache_partial_slabs));
+
+ return (avl_walk_init_checked(wsp, "slab list", "slab",
+ kmem_partial_slab_check, (void *)caddr));
+}
+
int
kmem_slab_walk_init(mdb_walk_state_t *wsp)
{
uintptr_t caddr = wsp->walk_addr;
- kmem_cache_t c;
if (caddr == NULL) {
mdb_warn("kmem_slab doesn't support global walks\n");
return (WALK_ERR);
}
- if (mdb_vread(&c, sizeof (c), caddr) == -1) {
- mdb_warn("couldn't read kmem_cache at %p", caddr);
- return (WALK_ERR);
- }
-
- wsp->walk_data =
- (void *)(caddr + offsetof(kmem_cache_t, cache_nullslab));
- wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_next;
+ combined_walk_init(wsp);
+ combined_walk_add(wsp,
+ kmem_complete_slab_walk_init, list_walk_step, list_walk_fini);
+ combined_walk_add(wsp,
+ kmem_partial_slab_walk_init, avl_walk_step, avl_walk_fini);
return (WALK_NEXT);
}
+static int
+kmem_first_complete_slab_walk_init(mdb_walk_state_t *wsp)
+{
+ uintptr_t caddr = wsp->walk_addr;
+ kmem_nth_slab_t *chk;
+
+ chk = mdb_alloc(sizeof (kmem_nth_slab_t),
+ UM_SLEEP | UM_GC);
+ chk->kns_cache_addr = caddr;
+ chk->kns_nslabs = 1;
+ wsp->walk_addr = (uintptr_t)(caddr +
+ offsetof(kmem_cache_t, cache_complete_slabs));
+
+ return (list_walk_init_checked(wsp, "slab list", "slab",
+ kmem_nth_slab_check, chk));
+}
+
int
kmem_slab_walk_partial_init(mdb_walk_state_t *wsp)
{
@@ -227,55 +289,38 @@ kmem_slab_walk_partial_init(mdb_walk_state_t *wsp)
return (WALK_ERR);
}
- wsp->walk_data =
- (void *)(caddr + offsetof(kmem_cache_t, cache_nullslab));
- wsp->walk_addr = (uintptr_t)c.cache_freelist;
+ combined_walk_init(wsp);
/*
* Some consumers (umem_walk_step(), in particular) require at
* least one callback if there are any buffers in the cache. So
- * if there are *no* partial slabs, report the last full slab, if
+ * if there are *no* partial slabs, report the first full slab, if
* any.
*
* Yes, this is ugly, but it's cleaner than the other possibilities.
*/
- if ((uintptr_t)wsp->walk_data == wsp->walk_addr)
- wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_prev;
-
- return (WALK_NEXT);
-}
-
-int
-kmem_slab_walk_step(mdb_walk_state_t *wsp)
-{
- kmem_slab_t s;
- uintptr_t addr = wsp->walk_addr;
- uintptr_t saddr = (uintptr_t)wsp->walk_data;
- uintptr_t caddr = saddr - offsetof(kmem_cache_t, cache_nullslab);
-
- if (addr == saddr)
- return (WALK_DONE);
-
- if (mdb_vread(&s, sizeof (s), addr) == -1) {
- mdb_warn("failed to read slab at %p", wsp->walk_addr);
- return (WALK_ERR);
- }
-
- if ((uintptr_t)s.slab_cache != caddr) {
- mdb_warn("slab %p isn't in cache %p (in cache %p)\n",
- addr, caddr, s.slab_cache);
- return (WALK_ERR);
+ if (c.cache_partial_slabs.avl_numnodes == 0) {
+ combined_walk_add(wsp, kmem_first_complete_slab_walk_init,
+ list_walk_step, list_walk_fini);
+ } else {
+ combined_walk_add(wsp, kmem_partial_slab_walk_init,
+ avl_walk_step, avl_walk_fini);
}
- wsp->walk_addr = (uintptr_t)s.slab_next;
-
- return (wsp->walk_callback(addr, &s, wsp->walk_cbdata));
+ return (WALK_NEXT);
}
int
kmem_cache(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv)
{
kmem_cache_t c;
+ const char *filter = NULL;
+
+ if (mdb_getopts(ac, argv,
+ 'n', MDB_OPT_STR, &filter,
+ NULL) != ac) {
+ return (DCMD_USAGE);
+ }
if (!(flags & DCMD_ADDRSPEC)) {
if (mdb_walk_dcmd("kmem_cache", "kmem_cache", ac, argv) == -1) {
@@ -294,25 +339,35 @@ kmem_cache(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv)
return (DCMD_ERR);
}
+ if ((filter != NULL) && (strstr(c.cache_name, filter) == NULL))
+ return (DCMD_OK);
+
mdb_printf("%0?p %-25s %04x %06x %8ld %8lld\n", addr, c.cache_name,
c.cache_flags, c.cache_cflags, c.cache_bufsize, c.cache_buftotal);
return (DCMD_OK);
}
-typedef struct kmem_slab_usage {
- int ksu_refcnt; /* count of allocated buffers on slab */
-} kmem_slab_usage_t;
-
-typedef struct kmem_slab_stats {
- int ks_slabs; /* slabs in cache */
- int ks_partial_slabs; /* partially allocated slabs in cache */
- uint64_t ks_unused_buffers; /* total unused buffers in cache */
- int ks_buffers_per_slab; /* buffers per slab */
- int ks_usage_len; /* ks_usage array length */
- kmem_slab_usage_t *ks_usage; /* partial slab usage */
- uint_t *ks_bucket; /* slab usage distribution */
-} kmem_slab_stats_t;
+void
+kmem_cache_help(void)
+{
+ mdb_printf("%s", "Print kernel memory caches.\n\n");
+ mdb_dec_indent(2);
+ mdb_printf("%<b>OPTIONS%</b>\n");
+ mdb_inc_indent(2);
+ mdb_printf("%s",
+" -n name\n"
+" name of kmem cache (or matching partial name)\n"
+"\n"
+"Column\tDescription\n"
+"\n"
+"ADDR\t\taddress of kmem cache\n"
+"NAME\t\tname of kmem cache\n"
+"FLAG\t\tvarious cache state flags\n"
+"CFLAG\t\tcache creation flags\n"
+"BUFSIZE\tobject size in bytes\n"
+"BUFTOTL\tcurrent total buffers in cache (allocated and free)\n");
+}
#define LABEL_WIDTH 11
static void
@@ -388,15 +443,30 @@ kmem_first_partial_slab(uintptr_t addr, const kmem_slab_t *sp,
boolean_t *is_slab)
{
/*
- * The "kmem_partial_slab" walker reports the last full slab if there
+ * The "kmem_partial_slab" walker reports the first full slab if there
* are no partial slabs (for the sake of consumers that require at least
* one callback if there are any buffers in the cache).
*/
- *is_slab = ((sp->slab_refcnt > 0) &&
- (sp->slab_refcnt < sp->slab_chunks));
+ *is_slab = KMEM_SLAB_IS_PARTIAL(sp);
return (WALK_DONE);
}
+typedef struct kmem_slab_usage {
+ int ksu_refcnt; /* count of allocated buffers on slab */
+ boolean_t ksu_nomove; /* slab marked non-reclaimable */
+} kmem_slab_usage_t;
+
+typedef struct kmem_slab_stats {
+ const kmem_cache_t *ks_cp;
+ int ks_slabs; /* slabs in cache */
+ int ks_partial_slabs; /* partially allocated slabs in cache */
+ uint64_t ks_unused_buffers; /* total unused buffers in cache */
+ int ks_max_buffers_per_slab; /* max buffers per slab */
+ int ks_usage_len; /* ks_usage array length */
+ kmem_slab_usage_t *ks_usage; /* partial slab usage */
+ uint_t *ks_bucket; /* slab usage distribution */
+} kmem_slab_stats_t;
+
/*ARGSUSED*/
static int
kmem_slablist_stat(uintptr_t addr, const kmem_slab_t *sp,
@@ -406,12 +476,6 @@ kmem_slablist_stat(uintptr_t addr, const kmem_slab_t *sp,
long unused;
ks->ks_slabs++;
- if (ks->ks_buffers_per_slab == 0) {
- ks->ks_buffers_per_slab = sp->slab_chunks;
- /* +1 to include a zero bucket */
- ks->ks_bucket = mdb_zalloc((ks->ks_buffers_per_slab + 1) *
- sizeof (*ks->ks_bucket), UM_SLEEP | UM_GC);
- }
ks->ks_bucket[sp->slab_refcnt]++;
unused = (sp->slab_chunks - sp->slab_refcnt);
@@ -440,6 +504,7 @@ kmem_slablist_stat(uintptr_t addr, const kmem_slab_t *sp,
ksu = &ks->ks_usage[ks->ks_partial_slabs - 1];
ksu->ksu_refcnt = sp->slab_refcnt;
+ ksu->ksu_nomove = (sp->slab_flags & KMEM_SLAB_NOMOVE);
return (WALK_NEXT);
}
@@ -466,21 +531,23 @@ kmem_slabs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
size_t maxbuckets = 1;
size_t minbucketsize = 0;
const char *filter = NULL;
+ const char *name = NULL;
uint_t opt_v = FALSE;
- boolean_t verbose = B_FALSE;
+ boolean_t buckets = B_FALSE;
boolean_t skip = B_FALSE;
if (mdb_getopts(argc, argv,
'B', MDB_OPT_UINTPTR, &minbucketsize,
'b', MDB_OPT_UINTPTR, &maxbuckets,
'n', MDB_OPT_STR, &filter,
+ 'N', MDB_OPT_STR, &name,
'v', MDB_OPT_SETBITS, TRUE, &opt_v,
NULL) != argc) {
return (DCMD_USAGE);
}
- if (opt_v || (maxbuckets != 1) || (minbucketsize != 0)) {
- verbose = 1;
+ if ((maxbuckets != 1) || (minbucketsize != 0)) {
+ buckets = B_TRUE;
}
if (!(flags & DCMD_ADDRSPEC)) {
@@ -497,13 +564,20 @@ kmem_slabs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_ERR);
}
- if ((filter != NULL) && (strstr(c.cache_name, filter) == NULL)) {
- skip = B_TRUE;
+ if (name == NULL) {
+ skip = ((filter != NULL) &&
+ (strstr(c.cache_name, filter) == NULL));
+ } else if (filter == NULL) {
+ skip = (strcmp(c.cache_name, name) != 0);
+ } else {
+ /* match either -n or -N */
+ skip = ((strcmp(c.cache_name, name) != 0) &&
+ (strstr(c.cache_name, filter) == NULL));
}
- if (!verbose && DCMD_HDRSPEC(flags)) {
+ if (!(opt_v || buckets) && DCMD_HDRSPEC(flags)) {
kmem_slabs_header();
- } else if (verbose && !skip) {
+ } else if ((opt_v || buckets) && !skip) {
if (DCMD_HDRSPEC(flags)) {
kmem_slabs_header();
} else {
@@ -528,6 +602,11 @@ kmem_slabs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
}
bzero(&stats, sizeof (kmem_slab_stats_t));
+ stats.ks_cp = &c;
+ stats.ks_max_buffers_per_slab = c.cache_maxchunks;
+ /* +1 to include a zero bucket */
+ stats.ks_bucket = mdb_zalloc((stats.ks_max_buffers_per_slab + 1) *
+ sizeof (*stats.ks_bucket), UM_SLEEP);
cb = (mdb_walk_cb_t)kmem_slablist_stat;
(void) mdb_pwalk("kmem_slab", cb, &stats, addr);
@@ -550,19 +629,22 @@ kmem_slabs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
stats.ks_slabs, stats.ks_partial_slabs, c.cache_buftotal,
stats.ks_unused_buffers, pct, tenths_pct);
- if (!verbose) {
- return (DCMD_OK);
- }
-
if (maxbuckets == 0) {
- maxbuckets = stats.ks_buffers_per_slab;
+ maxbuckets = stats.ks_max_buffers_per_slab;
}
if (((maxbuckets > 1) || (minbucketsize > 0)) &&
(stats.ks_slabs > 0)) {
mdb_printf("\n");
kmem_slabs_print_dist(stats.ks_bucket,
- stats.ks_buffers_per_slab, maxbuckets, minbucketsize);
+ stats.ks_max_buffers_per_slab, maxbuckets, minbucketsize);
+ }
+
+ mdb_free(stats.ks_bucket, (stats.ks_max_buffers_per_slab + 1) *
+ sizeof (*stats.ks_bucket));
+
+ if (!opt_v) {
+ return (DCMD_OK);
}
if (opt_v && (stats.ks_partial_slabs > 0)) {
@@ -573,11 +655,16 @@ kmem_slabs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
(stats.ks_slabs - stats.ks_partial_slabs),
stats.ks_partial_slabs);
if (stats.ks_partial_slabs > 0) {
- mdb_printf(" (%d):", stats.ks_buffers_per_slab);
+ mdb_printf(" (%d):", stats.ks_max_buffers_per_slab);
}
for (i = 0; i < stats.ks_partial_slabs; i++) {
ksu = &stats.ks_usage[i];
- mdb_printf(" %d", ksu->ksu_refcnt);
+ if (ksu->ksu_nomove) {
+ const char *symbol = "*";
+ mdb_printf(" %d%s", ksu->ksu_refcnt, symbol);
+ } else {
+ mdb_printf(" %d", ksu->ksu_refcnt);
+ }
}
mdb_printf("\n\n");
}
@@ -593,14 +680,16 @@ kmem_slabs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
void
kmem_slabs_help(void)
{
- mdb_printf("%s\n",
-"Display slab usage per kmem cache.\n");
+ mdb_printf("%s",
+"Display slab usage per kmem cache.\n\n");
mdb_dec_indent(2);
mdb_printf("%<b>OPTIONS%</b>\n");
mdb_inc_indent(2);
mdb_printf("%s",
" -n name\n"
" name of kmem cache (or matching partial name)\n"
+" -N name\n"
+" exact name of kmem cache\n"
" -b maxbins\n"
" Print a distribution of allocated buffers per slab using at\n"
" most maxbins bins. The first bin is reserved for completely\n"
@@ -629,9 +718,19 @@ kmem_slabs_help(void)
" list and least-used at the back (as in the example above).\n"
" However, if a slab contains an allocated buffer that will not\n"
" soon be freed, it would be better for that slab to be at the\n"
-" front where it can get used up. Taking a slab off the partial\n"
-" slab list (either with all buffers freed or all buffers\n"
-" allocated) reduces cache fragmentation.\n"
+" front where all of its buffers can be allocated. Taking a slab\n"
+" off the partial slab list (either with all buffers freed or all\n"
+" buffers allocated) reduces cache fragmentation.\n"
+"\n"
+" A slab's allocated buffer count representing a partial slab (9 in\n"
+" the example below) may be marked as follows:\n"
+"\n"
+" 9* An asterisk indicates that kmem has marked the slab non-\n"
+" reclaimable because the kmem client refused to move one of the\n"
+" slab's buffers. Since kmem does not expect to completely free the\n"
+" slab, it moves it to the front of the list in the hope of\n"
+" completely allocating it instead. A slab marked with an asterisk\n"
+" stays marked for as long as it remains on the partial slab list.\n"
"\n"
"Column\t\tDescription\n"
"\n"
@@ -2729,8 +2828,8 @@ bufctl_history_callback(uintptr_t addr, const void *ign, void *arg)
void
bufctl_help(void)
{
- mdb_printf("%s\n",
-"Display the contents of kmem_bufctl_audit_ts, with optional filtering.\n");
+ mdb_printf("%s",
+"Display the contents of kmem_bufctl_audit_ts, with optional filtering.\n\n");
mdb_dec_indent(2);
mdb_printf("%<b>OPTIONS%</b>\n");
mdb_inc_indent(2);
@@ -3509,8 +3608,8 @@ vmem(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
void
vmem_seg_help(void)
{
- mdb_printf("%s\n",
-"Display the contents of vmem_seg_ts, with optional filtering.\n"
+ mdb_printf("%s",
+"Display the contents of vmem_seg_ts, with optional filtering.\n\n"
"\n"
"A vmem_seg_t represents a range of addresses (or arbitrary numbers),\n"
"representing a single chunk of data. Only ALLOC segments have debugging\n"
@@ -4180,7 +4279,7 @@ kmem_init(void)
{
mdb_walker_t w = {
"kmem_cache", "walk list of kmem caches", kmem_cache_walk_init,
- kmem_cache_walk_step, kmem_cache_walk_fini
+ list_walk_step, list_walk_fini
};
/*
diff --git a/usr/src/cmd/mdb/common/modules/genunix/kmem.h b/usr/src/cmd/mdb/common/modules/genunix/kmem.h
index 3d56413655..2d74dad9e3 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/kmem.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/kmem.h
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -35,15 +35,12 @@ extern "C" {
#endif
extern int kmem_cache_walk_init(mdb_walk_state_t *);
-extern int kmem_cache_walk_step(mdb_walk_state_t *);
-extern void kmem_cache_walk_fini(mdb_walk_state_t *);
extern int kmem_cpu_cache_walk_init(mdb_walk_state_t *);
extern int kmem_cpu_cache_walk_step(mdb_walk_state_t *);
extern int kmem_slab_walk_init(mdb_walk_state_t *);
extern int kmem_slab_walk_partial_init(mdb_walk_state_t *);
-extern int kmem_slab_walk_step(mdb_walk_state_t *);
extern int kmem_hash_walk_init(mdb_walk_state_t *wsp);
extern int kmem_hash_walk_step(mdb_walk_state_t *wsp);
@@ -104,11 +101,12 @@ extern int vmem(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int vmem_seg(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int kmalog(uintptr_t, uint_t, int, const mdb_arg_t *);
extern int kmausers(uintptr_t, uint_t, int, const mdb_arg_t *);
+extern void kmem_cache_help(void);
+extern void kmem_slabs_help(void);
extern void whatis_help(void);
extern void bufctl_help(void);
extern void vmem_seg_help(void);
extern void kmausers_help(void);
-extern void kmem_slabs_help(void);
extern int whatthread(uintptr_t, uint_t, int, const mdb_arg_t *);
diff --git a/usr/src/cmd/mdb/common/modules/genunix/list.c b/usr/src/cmd/mdb/common/modules/genunix/list.c
index e73174026d..58e21ebc6f 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/list.c
+++ b/usr/src/cmd/mdb/common/modules/genunix/list.c
@@ -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,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -30,21 +29,47 @@
#include <sys/list.h>
typedef struct list_walk_data {
- uintptr_t lw_start;
- size_t lw_size;
- size_t lw_offset;
- void *lw_obj;
+ uintptr_t lw_head; /* address of list head */
+ size_t lw_size; /* size of list element */
+ size_t lw_offset; /* list element linkage offset */
+ void *lw_obj; /* buffer of lw_size to hold list element */
+ uintptr_t lw_end; /* last node in specified range */
+ const char *lw_elem_name;
+ int (*lw_elem_check)(void *, uintptr_t, void *);
+ void *lw_elem_check_arg;
} list_walk_data_t;
+/*
+ * Initialize a forward walk through a list.
+ *
+ * begin and end optionally specify objects other than the first and last
+ * objects in the list; either or both may be NULL (defaulting to first and
+ * last).
+ *
+ * list_name and element_name specify command-specific labels other than
+ * "list_t" and "list element" for use in error messages.
+ *
+ * element_check() returns -1, 1, or 0: abort the walk with an error, stop
+ * without an error, or allow the normal callback; arg is an optional user
+ * argument to element_check().
+ */
int
-list_walk_init(mdb_walk_state_t *wsp)
+list_walk_init_range(mdb_walk_state_t *wsp, uintptr_t begin, uintptr_t end,
+ const char *list_name, const char *element_name,
+ int (*element_check)(void *, uintptr_t, void *), void *arg)
{
list_walk_data_t *lwd;
list_t list;
+ if (list_name == NULL)
+ list_name = "list_t";
+ if (element_name == NULL)
+ element_name = "list element";
+
lwd = mdb_alloc(sizeof (list_walk_data_t), UM_SLEEP);
if (mdb_vread(&list, sizeof (list_t), wsp->walk_addr) == -1) {
- mdb_warn("failed to read list_t at %#lx", wsp->walk_addr);
+ mdb_warn("failed to read %s at %#lx", list_name,
+ wsp->walk_addr);
mdb_free(lwd, sizeof (list_walk_data_t));
return (WALK_ERR);
}
@@ -52,15 +77,44 @@ list_walk_init(mdb_walk_state_t *wsp)
lwd->lw_size = list.list_size;
lwd->lw_offset = list.list_offset;
lwd->lw_obj = mdb_alloc(list.list_size, UM_SLEEP);
- lwd->lw_start = (uintptr_t)&((list_t *)wsp->walk_addr)->list_head;
+ lwd->lw_head = (uintptr_t)&((list_t *)wsp->walk_addr)->list_head;
+ lwd->lw_end = (end == NULL ? NULL : end + lwd->lw_offset);
+ lwd->lw_elem_name = element_name;
+ lwd->lw_elem_check = element_check;
+ lwd->lw_elem_check_arg = arg;
- wsp->walk_addr = (uintptr_t)list.list_head.list_next;
+ wsp->walk_addr = (begin == NULL
+ ? (uintptr_t)list.list_head.list_next
+ : begin + lwd->lw_offset);
wsp->walk_data = lwd;
return (WALK_NEXT);
}
int
+list_walk_init(mdb_walk_state_t *wsp)
+{
+ return (list_walk_init_range(wsp, NULL, NULL, NULL, NULL, NULL, NULL));
+}
+
+int
+list_walk_init_named(mdb_walk_state_t *wsp,
+ const char *list_name, const char *element_name)
+{
+ return (list_walk_init_range(wsp, NULL, NULL, list_name, element_name,
+ NULL, NULL));
+}
+
+int
+list_walk_init_checked(mdb_walk_state_t *wsp,
+ const char *list_name, const char *element_name,
+ int (*element_check)(void *, uintptr_t, void *), void *arg)
+{
+ return (list_walk_init_range(wsp, NULL, NULL, list_name, element_name,
+ element_check, arg));
+}
+
+int
list_walk_step(mdb_walk_state_t *wsp)
{
list_walk_data_t *lwd = wsp->walk_data;
@@ -68,14 +122,26 @@ list_walk_step(mdb_walk_state_t *wsp)
list_node_t *node;
int status;
- if (wsp->walk_addr == lwd->lw_start)
+ if (wsp->walk_addr == lwd->lw_head)
+ return (WALK_DONE);
+
+ if (lwd->lw_end != NULL && wsp->walk_addr == lwd->lw_end)
return (WALK_DONE);
if (mdb_vread(lwd->lw_obj, lwd->lw_size, addr) == -1) {
- mdb_warn("failed to read list element at %#lx", addr);
+ mdb_warn("failed to read %s at %#lx", lwd->lw_elem_name, addr);
return (WALK_ERR);
}
+ if (lwd->lw_elem_check != NULL) {
+ int rc = lwd->lw_elem_check(lwd->lw_obj, addr,
+ lwd->lw_elem_check_arg);
+ if (rc == -1)
+ return (WALK_ERR);
+ else if (rc == 1)
+ return (WALK_DONE);
+ }
+
status = wsp->walk_callback(addr, lwd->lw_obj, wsp->walk_cbdata);
node = (list_node_t *)((uintptr_t)lwd->lw_obj + lwd->lw_offset);
wsp->walk_addr = (uintptr_t)node->list_next;
diff --git a/usr/src/cmd/mdb/common/modules/genunix/list.h b/usr/src/cmd/mdb/common/modules/genunix/list.h
index 10581cc900..e50ea50806 100644
--- a/usr/src/cmd/mdb/common/modules/genunix/list.h
+++ b/usr/src/cmd/mdb/common/modules/genunix/list.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,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -38,9 +37,17 @@ extern "C" {
#define LIST_WALK_NAME "list"
#define LIST_WALK_DESC "walk a linked list"
-int list_walk_init(mdb_walk_state_t *wsp);
-int list_walk_step(mdb_walk_state_t *wsp);
-void list_walk_fini(mdb_walk_state_t *wsp);
+extern int list_walk_init(mdb_walk_state_t *wsp);
+extern int list_walk_init_named(mdb_walk_state_t *wsp,
+ const char *, const char *);
+extern int list_walk_init_checked(mdb_walk_state_t *wsp,
+ const char *, const char *,
+ int (*)(void *, uintptr_t, void *), void *);
+extern int list_walk_init_range(mdb_walk_state_t *wsp, uintptr_t, uintptr_t,
+ const char *, const char *,
+ int (*)(void *, uintptr_t, void *), void *);
+extern int list_walk_step(mdb_walk_state_t *wsp);
+extern void list_walk_fini(mdb_walk_state_t *wsp);
#ifdef __cplusplus
}