Mercurial > illumos > illumos-gate
changeset 13090:fac90c60a1e7
6919045 A walker to find all pages in a segvn segment would be useful
author | Jonathan Adams <Jonathan.Adams@Sun.COM> |
---|---|
date | Wed, 11 Aug 2010 14:15:46 -0700 |
parents | 9ff7ff6709c5 |
children | 9116acef0349 |
files | usr/src/cmd/mdb/common/modules/genunix/genunix.c usr/src/cmd/mdb/common/modules/genunix/memory.c usr/src/cmd/mdb/common/modules/genunix/memory.h |
diffstat | 3 files changed, 443 insertions(+), 71 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c Wed Aug 11 14:15:46 2010 -0700 +++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c Wed Aug 11 14:15:46 2010 -0700 @@ -4409,14 +4409,36 @@ /* 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 },
--- a/usr/src/cmd/mdb/common/modules/genunix/memory.c Wed Aug 11 14:15:46 2010 -0700 +++ b/usr/src/cmd/mdb/common/modules/genunix/memory.c Wed Aug 11 14:15:46 2010 -0700 @@ -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 @@ /*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 @@ 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; + /* + * Use the segvn_pages walker to find all of the in-core pages + * for this mapping. + */ + 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); } - - /* - * We've got an amp for this segment; walk through - * the amp, and determine mappings. - */ - 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: + 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 @@ { 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 @@ } else { mdb_printf(" [ anon ]"); } + } else { + mdb_printf(" [ &%a ]", seg->s_ops); } mdb_printf("\n"); @@ -1086,15 +1086,19 @@ 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 @@ } 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 @@ 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 @@ 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]; + + /* update the indices for next time */ aw->aw_levtwo_ndx++; - if (aw->aw_levtwo_ndx == ANON_CHUNK_SIZE) { aw->aw_levtwo_ndx = 0; - - do { - aw->aw_levone_ndx++; + 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 @@ 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(&, 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. */
--- a/usr/src/cmd/mdb/common/modules/genunix/memory.h Wed Aug 11 14:15:46 2010 -0700 +++ b/usr/src/cmd/mdb/common/modules/genunix/memory.h Wed Aug 11 14:15:46 2010 -0700 @@ -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 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 *);