diff options
author | tomee <none@none> | 2008-05-26 17:53:26 -0700 |
---|---|---|
committer | tomee <none@none> | 2008-05-26 17:53:26 -0700 |
commit | b5fca8f855054d167d04d3b4de5210c83ed2083c (patch) | |
tree | 6b4e690195757449698c233ae276ae53b52a9c9b /usr/src/cmd/mdb/common/modules | |
parent | 7568150a58e78021968b6c22bc28e9787b33496a (diff) | |
download | illumos-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.files | 3 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/avl.c | 110 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/avl.h | 15 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/combined.c | 165 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/combined.h | 49 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/genunix.c | 13 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/kmem.c | 379 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/kmem.h | 8 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/list.c | 94 | ||||
-rw-r--r-- | usr/src/cmd/mdb/common/modules/genunix/list.h | 21 |
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 } |