10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*1528Sjwadams * Common Development and Distribution License (the "License"). 6*1528Sjwadams * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*1528Sjwadams * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate #include "umem.h" 290Sstevel@tonic-gate 300Sstevel@tonic-gate #include <sys/vmem_impl_user.h> 310Sstevel@tonic-gate #include <umem_impl.h> 320Sstevel@tonic-gate 330Sstevel@tonic-gate #include <alloca.h> 34*1528Sjwadams #include <limits.h> 350Sstevel@tonic-gate 360Sstevel@tonic-gate #include "misc.h" 37*1528Sjwadams #include "leaky.h" 380Sstevel@tonic-gate 390Sstevel@tonic-gate #include "umem_pagesize.h" 400Sstevel@tonic-gate 410Sstevel@tonic-gate #define UM_ALLOCATED 0x1 420Sstevel@tonic-gate #define UM_FREE 0x2 430Sstevel@tonic-gate #define UM_BUFCTL 0x4 440Sstevel@tonic-gate #define UM_HASH 0x8 450Sstevel@tonic-gate 46*1528Sjwadams int umem_ready; 47*1528Sjwadams 48*1528Sjwadams static int umem_stack_depth_warned; 49*1528Sjwadams static uint32_t umem_max_ncpus; 500Sstevel@tonic-gate uint32_t umem_stack_depth; 51*1528Sjwadams 520Sstevel@tonic-gate size_t umem_pagesize; 530Sstevel@tonic-gate 540Sstevel@tonic-gate #define UMEM_READVAR(var) \ 550Sstevel@tonic-gate (umem_readvar(&(var), #var) == -1 && \ 56*1528Sjwadams (mdb_warn("failed to read "#var), 1)) 570Sstevel@tonic-gate 580Sstevel@tonic-gate int 59*1528Sjwadams umem_update_variables(void) 600Sstevel@tonic-gate { 610Sstevel@tonic-gate size_t pagesize; 620Sstevel@tonic-gate 630Sstevel@tonic-gate /* 64*1528Sjwadams * Figure out which type of umem is being used; if it's not there 65*1528Sjwadams * yet, succeed quietly. 660Sstevel@tonic-gate */ 67*1528Sjwadams if (umem_set_standalone() == -1) { 68*1528Sjwadams umem_ready = 0; 69*1528Sjwadams return (0); /* umem not there yet */ 70*1528Sjwadams } 71*1528Sjwadams 72*1528Sjwadams /* 73*1528Sjwadams * Solaris 9 used a different name for umem_max_ncpus. It's 74*1528Sjwadams * cheap backwards compatibility to check for both names. 75*1528Sjwadams */ 76*1528Sjwadams if (umem_readvar(&umem_max_ncpus, "umem_max_ncpus") == -1 && 77*1528Sjwadams umem_readvar(&umem_max_ncpus, "max_ncpus") == -1) { 78*1528Sjwadams mdb_warn("unable to read umem_max_ncpus or max_ncpus"); 79*1528Sjwadams return (-1); 80*1528Sjwadams } 81*1528Sjwadams if (UMEM_READVAR(umem_ready)) 820Sstevel@tonic-gate return (-1); 830Sstevel@tonic-gate if (UMEM_READVAR(umem_stack_depth)) 840Sstevel@tonic-gate return (-1); 850Sstevel@tonic-gate if (UMEM_READVAR(pagesize)) 860Sstevel@tonic-gate return (-1); 870Sstevel@tonic-gate 880Sstevel@tonic-gate if (umem_stack_depth > UMEM_MAX_STACK_DEPTH) { 89*1528Sjwadams if (umem_stack_depth_warned == 0) { 90*1528Sjwadams mdb_warn("umem_stack_depth corrupted (%d > %d)\n", 91*1528Sjwadams umem_stack_depth, UMEM_MAX_STACK_DEPTH); 92*1528Sjwadams umem_stack_depth_warned = 1; 93*1528Sjwadams } 940Sstevel@tonic-gate umem_stack_depth = 0; 950Sstevel@tonic-gate } 96*1528Sjwadams 97*1528Sjwadams umem_pagesize = pagesize; 98*1528Sjwadams 990Sstevel@tonic-gate return (0); 1000Sstevel@tonic-gate } 1010Sstevel@tonic-gate 1020Sstevel@tonic-gate /*ARGSUSED*/ 103*1528Sjwadams static int 1040Sstevel@tonic-gate umem_init_walkers(uintptr_t addr, const umem_cache_t *c, void *ignored) 1050Sstevel@tonic-gate { 1060Sstevel@tonic-gate mdb_walker_t w; 1070Sstevel@tonic-gate char descr[64]; 1080Sstevel@tonic-gate 1090Sstevel@tonic-gate (void) mdb_snprintf(descr, sizeof (descr), 1100Sstevel@tonic-gate "walk the %s cache", c->cache_name); 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate w.walk_name = c->cache_name; 1130Sstevel@tonic-gate w.walk_descr = descr; 1140Sstevel@tonic-gate w.walk_init = umem_walk_init; 1150Sstevel@tonic-gate w.walk_step = umem_walk_step; 1160Sstevel@tonic-gate w.walk_fini = umem_walk_fini; 1170Sstevel@tonic-gate w.walk_init_arg = (void *)addr; 1180Sstevel@tonic-gate 1190Sstevel@tonic-gate if (mdb_add_walker(&w) == -1) 1200Sstevel@tonic-gate mdb_warn("failed to add %s walker", c->cache_name); 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate return (WALK_NEXT); 1230Sstevel@tonic-gate } 1240Sstevel@tonic-gate 125*1528Sjwadams /*ARGSUSED*/ 126*1528Sjwadams static void 127*1528Sjwadams umem_statechange_cb(void *arg) 128*1528Sjwadams { 129*1528Sjwadams static int been_ready = 0; 130*1528Sjwadams 131*1528Sjwadams #ifndef _KMDB 132*1528Sjwadams leaky_cleanup(1); /* state changes invalidate leaky state */ 133*1528Sjwadams #endif 134*1528Sjwadams 135*1528Sjwadams if (umem_update_variables() == -1) 136*1528Sjwadams return; 137*1528Sjwadams 138*1528Sjwadams if (been_ready) 139*1528Sjwadams return; 140*1528Sjwadams 141*1528Sjwadams if (umem_ready != UMEM_READY) 142*1528Sjwadams return; 143*1528Sjwadams 144*1528Sjwadams been_ready = 1; 145*1528Sjwadams (void) mdb_walk("umem_cache", (mdb_walk_cb_t)umem_init_walkers, NULL); 146*1528Sjwadams } 147*1528Sjwadams 148*1528Sjwadams int 149*1528Sjwadams umem_init(void) 150*1528Sjwadams { 151*1528Sjwadams mdb_walker_t w = { 152*1528Sjwadams "umem_cache", "walk list of umem caches", umem_cache_walk_init, 153*1528Sjwadams umem_cache_walk_step, umem_cache_walk_fini 154*1528Sjwadams }; 155*1528Sjwadams 156*1528Sjwadams if (mdb_add_walker(&w) == -1) { 157*1528Sjwadams mdb_warn("failed to add umem_cache walker"); 158*1528Sjwadams return (-1); 159*1528Sjwadams } 160*1528Sjwadams 161*1528Sjwadams if (umem_update_variables() == -1) 162*1528Sjwadams return (-1); 163*1528Sjwadams 164*1528Sjwadams /* install a callback so that our variables are always up-to-date */ 165*1528Sjwadams (void) mdb_callback_add(MDB_CALLBACK_STCHG, umem_statechange_cb, NULL); 166*1528Sjwadams umem_statechange_cb(NULL); 167*1528Sjwadams 168*1528Sjwadams return (0); 169*1528Sjwadams } 170*1528Sjwadams 1710Sstevel@tonic-gate int 1720Sstevel@tonic-gate umem_abort_messages(void) 1730Sstevel@tonic-gate { 1740Sstevel@tonic-gate char *umem_error_buffer; 1750Sstevel@tonic-gate uint_t umem_error_begin; 1760Sstevel@tonic-gate GElf_Sym sym; 1770Sstevel@tonic-gate size_t bufsize; 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate if (UMEM_READVAR(umem_error_begin)) 1800Sstevel@tonic-gate return (DCMD_ERR); 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate if (umem_lookup_by_name("umem_error_buffer", &sym) == -1) { 1830Sstevel@tonic-gate mdb_warn("unable to look up umem_error_buffer"); 1840Sstevel@tonic-gate return (DCMD_ERR); 1850Sstevel@tonic-gate } 1860Sstevel@tonic-gate 1870Sstevel@tonic-gate bufsize = (size_t)sym.st_size; 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate umem_error_buffer = mdb_alloc(bufsize+1, UM_SLEEP | UM_GC); 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate if (mdb_vread(umem_error_buffer, bufsize, (uintptr_t)sym.st_value) 1920Sstevel@tonic-gate != bufsize) { 1930Sstevel@tonic-gate mdb_warn("unable to read umem_error_buffer"); 1940Sstevel@tonic-gate return (DCMD_ERR); 1950Sstevel@tonic-gate } 1960Sstevel@tonic-gate /* put a zero after the end of the buffer to simplify printing */ 1970Sstevel@tonic-gate umem_error_buffer[bufsize] = 0; 1980Sstevel@tonic-gate 1990Sstevel@tonic-gate if ((umem_error_begin % bufsize) == 0) 2000Sstevel@tonic-gate mdb_printf("%s\n", umem_error_buffer); 2010Sstevel@tonic-gate else { 2020Sstevel@tonic-gate umem_error_buffer[(umem_error_begin % bufsize) - 1] = 0; 2030Sstevel@tonic-gate mdb_printf("%s%s\n", 2040Sstevel@tonic-gate &umem_error_buffer[umem_error_begin % bufsize], 2050Sstevel@tonic-gate umem_error_buffer); 2060Sstevel@tonic-gate } 2070Sstevel@tonic-gate 2080Sstevel@tonic-gate return (DCMD_OK); 2090Sstevel@tonic-gate } 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate static void 2120Sstevel@tonic-gate umem_log_status(const char *name, umem_log_header_t *val) 2130Sstevel@tonic-gate { 2140Sstevel@tonic-gate umem_log_header_t my_lh; 2150Sstevel@tonic-gate uintptr_t pos = (uintptr_t)val; 2160Sstevel@tonic-gate size_t size; 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate if (pos == NULL) 2190Sstevel@tonic-gate return; 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate if (mdb_vread(&my_lh, sizeof (umem_log_header_t), pos) == -1) { 2220Sstevel@tonic-gate mdb_warn("\nunable to read umem_%s_log pointer %p", 2230Sstevel@tonic-gate name, pos); 2240Sstevel@tonic-gate return; 2250Sstevel@tonic-gate } 2260Sstevel@tonic-gate 2270Sstevel@tonic-gate size = my_lh.lh_chunksize * my_lh.lh_nchunks; 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate if (size % (1024 * 1024) == 0) 2300Sstevel@tonic-gate mdb_printf("%s=%dm ", name, size / (1024 * 1024)); 2310Sstevel@tonic-gate else if (size % 1024 == 0) 2320Sstevel@tonic-gate mdb_printf("%s=%dk ", name, size / 1024); 2330Sstevel@tonic-gate else 2340Sstevel@tonic-gate mdb_printf("%s=%d ", name, size); 2350Sstevel@tonic-gate } 2360Sstevel@tonic-gate 2370Sstevel@tonic-gate typedef struct umem_debug_flags { 2380Sstevel@tonic-gate const char *udf_name; 2390Sstevel@tonic-gate uint_t udf_flags; 2400Sstevel@tonic-gate uint_t udf_clear; /* if 0, uses udf_flags */ 2410Sstevel@tonic-gate } umem_debug_flags_t; 2420Sstevel@tonic-gate 2430Sstevel@tonic-gate umem_debug_flags_t umem_status_flags[] = { 2440Sstevel@tonic-gate { "random", UMF_RANDOMIZE, UMF_RANDOM }, 2450Sstevel@tonic-gate { "default", UMF_AUDIT | UMF_DEADBEEF | UMF_REDZONE | UMF_CONTENTS }, 2460Sstevel@tonic-gate { "audit", UMF_AUDIT }, 2470Sstevel@tonic-gate { "guards", UMF_DEADBEEF | UMF_REDZONE }, 2480Sstevel@tonic-gate { "nosignal", UMF_CHECKSIGNAL }, 2490Sstevel@tonic-gate { "firewall", UMF_FIREWALL }, 2500Sstevel@tonic-gate { "lite", UMF_LITE }, 2510Sstevel@tonic-gate { NULL } 2520Sstevel@tonic-gate }; 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate /*ARGSUSED*/ 2550Sstevel@tonic-gate int 2560Sstevel@tonic-gate umem_status(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv) 2570Sstevel@tonic-gate { 2580Sstevel@tonic-gate int umem_logging; 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate umem_log_header_t *umem_transaction_log; 2610Sstevel@tonic-gate umem_log_header_t *umem_content_log; 2620Sstevel@tonic-gate umem_log_header_t *umem_failure_log; 2630Sstevel@tonic-gate umem_log_header_t *umem_slab_log; 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate mdb_printf("Status:\t\t%s\n", 2660Sstevel@tonic-gate umem_ready == UMEM_READY_INIT_FAILED ? "initialization failed" : 2670Sstevel@tonic-gate umem_ready == UMEM_READY_STARTUP ? "uninitialized" : 2680Sstevel@tonic-gate umem_ready == UMEM_READY_INITING ? "initialization in process" : 2690Sstevel@tonic-gate umem_ready == UMEM_READY ? "ready and active" : 270*1528Sjwadams umem_ready == 0 ? "not loaded into address space" : 2710Sstevel@tonic-gate "unknown (umem_ready invalid)"); 2720Sstevel@tonic-gate 273*1528Sjwadams if (umem_ready == 0) 274*1528Sjwadams return (DCMD_OK); 275*1528Sjwadams 2760Sstevel@tonic-gate mdb_printf("Concurrency:\t%d\n", umem_max_ncpus); 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate if (UMEM_READVAR(umem_logging)) 2790Sstevel@tonic-gate goto err; 2800Sstevel@tonic-gate if (UMEM_READVAR(umem_transaction_log)) 2810Sstevel@tonic-gate goto err; 2820Sstevel@tonic-gate if (UMEM_READVAR(umem_content_log)) 2830Sstevel@tonic-gate goto err; 2840Sstevel@tonic-gate if (UMEM_READVAR(umem_failure_log)) 2850Sstevel@tonic-gate goto err; 2860Sstevel@tonic-gate if (UMEM_READVAR(umem_slab_log)) 2870Sstevel@tonic-gate goto err; 2880Sstevel@tonic-gate 2890Sstevel@tonic-gate mdb_printf("Logs:\t\t"); 2900Sstevel@tonic-gate umem_log_status("transaction", umem_transaction_log); 2910Sstevel@tonic-gate umem_log_status("content", umem_content_log); 2920Sstevel@tonic-gate umem_log_status("fail", umem_failure_log); 2930Sstevel@tonic-gate umem_log_status("slab", umem_slab_log); 2940Sstevel@tonic-gate if (!umem_logging) 2950Sstevel@tonic-gate mdb_printf("(inactive)"); 2960Sstevel@tonic-gate mdb_printf("\n"); 2970Sstevel@tonic-gate 2980Sstevel@tonic-gate mdb_printf("Message buffer:\n"); 2990Sstevel@tonic-gate return (umem_abort_messages()); 3000Sstevel@tonic-gate 3010Sstevel@tonic-gate err: 3020Sstevel@tonic-gate mdb_printf("Message buffer:\n"); 3030Sstevel@tonic-gate (void) umem_abort_messages(); 3040Sstevel@tonic-gate return (DCMD_ERR); 3050Sstevel@tonic-gate } 3060Sstevel@tonic-gate 3070Sstevel@tonic-gate typedef struct { 3080Sstevel@tonic-gate uintptr_t ucw_first; 3090Sstevel@tonic-gate uintptr_t ucw_current; 3100Sstevel@tonic-gate } umem_cache_walk_t; 3110Sstevel@tonic-gate 3120Sstevel@tonic-gate int 3130Sstevel@tonic-gate umem_cache_walk_init(mdb_walk_state_t *wsp) 3140Sstevel@tonic-gate { 3150Sstevel@tonic-gate umem_cache_walk_t *ucw; 3160Sstevel@tonic-gate umem_cache_t c; 3170Sstevel@tonic-gate uintptr_t cp; 3180Sstevel@tonic-gate GElf_Sym sym; 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate if (umem_lookup_by_name("umem_null_cache", &sym) == -1) { 3210Sstevel@tonic-gate mdb_warn("couldn't find umem_null_cache"); 3220Sstevel@tonic-gate return (WALK_ERR); 3230Sstevel@tonic-gate } 3240Sstevel@tonic-gate 3250Sstevel@tonic-gate cp = (uintptr_t)sym.st_value; 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate if (mdb_vread(&c, sizeof (umem_cache_t), cp) == -1) { 3280Sstevel@tonic-gate mdb_warn("couldn't read cache at %p", cp); 3290Sstevel@tonic-gate return (WALK_ERR); 3300Sstevel@tonic-gate } 3310Sstevel@tonic-gate 3320Sstevel@tonic-gate ucw = mdb_alloc(sizeof (umem_cache_walk_t), UM_SLEEP); 3330Sstevel@tonic-gate 3340Sstevel@tonic-gate ucw->ucw_first = cp; 3350Sstevel@tonic-gate ucw->ucw_current = (uintptr_t)c.cache_next; 3360Sstevel@tonic-gate wsp->walk_data = ucw; 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate return (WALK_NEXT); 3390Sstevel@tonic-gate } 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate int 3420Sstevel@tonic-gate umem_cache_walk_step(mdb_walk_state_t *wsp) 3430Sstevel@tonic-gate { 3440Sstevel@tonic-gate umem_cache_walk_t *ucw = wsp->walk_data; 3450Sstevel@tonic-gate umem_cache_t c; 3460Sstevel@tonic-gate int status; 3470Sstevel@tonic-gate 3480Sstevel@tonic-gate if (mdb_vread(&c, sizeof (umem_cache_t), ucw->ucw_current) == -1) { 3490Sstevel@tonic-gate mdb_warn("couldn't read cache at %p", ucw->ucw_current); 3500Sstevel@tonic-gate return (WALK_DONE); 3510Sstevel@tonic-gate } 3520Sstevel@tonic-gate 3530Sstevel@tonic-gate status = wsp->walk_callback(ucw->ucw_current, &c, wsp->walk_cbdata); 3540Sstevel@tonic-gate 3550Sstevel@tonic-gate if ((ucw->ucw_current = (uintptr_t)c.cache_next) == ucw->ucw_first) 3560Sstevel@tonic-gate return (WALK_DONE); 3570Sstevel@tonic-gate 3580Sstevel@tonic-gate return (status); 3590Sstevel@tonic-gate } 3600Sstevel@tonic-gate 3610Sstevel@tonic-gate void 3620Sstevel@tonic-gate umem_cache_walk_fini(mdb_walk_state_t *wsp) 3630Sstevel@tonic-gate { 3640Sstevel@tonic-gate umem_cache_walk_t *ucw = wsp->walk_data; 3650Sstevel@tonic-gate mdb_free(ucw, sizeof (umem_cache_walk_t)); 3660Sstevel@tonic-gate } 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate typedef struct { 3690Sstevel@tonic-gate umem_cpu_t *ucw_cpus; 3700Sstevel@tonic-gate uint32_t ucw_current; 3710Sstevel@tonic-gate uint32_t ucw_max; 3720Sstevel@tonic-gate } umem_cpu_walk_state_t; 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate int 3750Sstevel@tonic-gate umem_cpu_walk_init(mdb_walk_state_t *wsp) 3760Sstevel@tonic-gate { 3770Sstevel@tonic-gate umem_cpu_t *umem_cpus; 3780Sstevel@tonic-gate 3790Sstevel@tonic-gate umem_cpu_walk_state_t *ucw; 3800Sstevel@tonic-gate 3810Sstevel@tonic-gate if (umem_readvar(&umem_cpus, "umem_cpus") == -1) { 3820Sstevel@tonic-gate mdb_warn("failed to read 'umem_cpus'"); 3830Sstevel@tonic-gate return (WALK_ERR); 3840Sstevel@tonic-gate } 3850Sstevel@tonic-gate 3860Sstevel@tonic-gate ucw = mdb_alloc(sizeof (*ucw), UM_SLEEP); 3870Sstevel@tonic-gate 3880Sstevel@tonic-gate ucw->ucw_cpus = umem_cpus; 3890Sstevel@tonic-gate ucw->ucw_current = 0; 3900Sstevel@tonic-gate ucw->ucw_max = umem_max_ncpus; 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate wsp->walk_data = ucw; 3930Sstevel@tonic-gate return (WALK_NEXT); 3940Sstevel@tonic-gate } 3950Sstevel@tonic-gate 3960Sstevel@tonic-gate int 3970Sstevel@tonic-gate umem_cpu_walk_step(mdb_walk_state_t *wsp) 3980Sstevel@tonic-gate { 3990Sstevel@tonic-gate umem_cpu_t cpu; 4000Sstevel@tonic-gate umem_cpu_walk_state_t *ucw = wsp->walk_data; 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate uintptr_t caddr; 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate if (ucw->ucw_current >= ucw->ucw_max) 4050Sstevel@tonic-gate return (WALK_DONE); 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate caddr = (uintptr_t)&(ucw->ucw_cpus[ucw->ucw_current]); 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate if (mdb_vread(&cpu, sizeof (umem_cpu_t), caddr) == -1) { 4100Sstevel@tonic-gate mdb_warn("failed to read cpu %d", ucw->ucw_current); 4110Sstevel@tonic-gate return (WALK_ERR); 4120Sstevel@tonic-gate } 4130Sstevel@tonic-gate 4140Sstevel@tonic-gate ucw->ucw_current++; 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate return (wsp->walk_callback(caddr, &cpu, wsp->walk_cbdata)); 4170Sstevel@tonic-gate } 4180Sstevel@tonic-gate 4190Sstevel@tonic-gate void 4200Sstevel@tonic-gate umem_cpu_walk_fini(mdb_walk_state_t *wsp) 4210Sstevel@tonic-gate { 4220Sstevel@tonic-gate umem_cpu_walk_state_t *ucw = wsp->walk_data; 4230Sstevel@tonic-gate 4240Sstevel@tonic-gate mdb_free(ucw, sizeof (*ucw)); 4250Sstevel@tonic-gate } 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate int 4280Sstevel@tonic-gate umem_cpu_cache_walk_init(mdb_walk_state_t *wsp) 4290Sstevel@tonic-gate { 4300Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 4310Sstevel@tonic-gate mdb_warn("umem_cpu_cache doesn't support global walks"); 4320Sstevel@tonic-gate return (WALK_ERR); 4330Sstevel@tonic-gate } 4340Sstevel@tonic-gate 4350Sstevel@tonic-gate if (mdb_layered_walk("umem_cpu", wsp) == -1) { 4360Sstevel@tonic-gate mdb_warn("couldn't walk 'umem_cpu'"); 4370Sstevel@tonic-gate return (WALK_ERR); 4380Sstevel@tonic-gate } 4390Sstevel@tonic-gate 4400Sstevel@tonic-gate wsp->walk_data = (void *)wsp->walk_addr; 4410Sstevel@tonic-gate 4420Sstevel@tonic-gate return (WALK_NEXT); 4430Sstevel@tonic-gate } 4440Sstevel@tonic-gate 4450Sstevel@tonic-gate int 4460Sstevel@tonic-gate umem_cpu_cache_walk_step(mdb_walk_state_t *wsp) 4470Sstevel@tonic-gate { 4480Sstevel@tonic-gate uintptr_t caddr = (uintptr_t)wsp->walk_data; 4490Sstevel@tonic-gate const umem_cpu_t *cpu = wsp->walk_layer; 4500Sstevel@tonic-gate umem_cpu_cache_t cc; 4510Sstevel@tonic-gate 4520Sstevel@tonic-gate caddr += cpu->cpu_cache_offset; 4530Sstevel@tonic-gate 4540Sstevel@tonic-gate if (mdb_vread(&cc, sizeof (umem_cpu_cache_t), caddr) == -1) { 4550Sstevel@tonic-gate mdb_warn("couldn't read umem_cpu_cache at %p", caddr); 4560Sstevel@tonic-gate return (WALK_ERR); 4570Sstevel@tonic-gate } 4580Sstevel@tonic-gate 4590Sstevel@tonic-gate return (wsp->walk_callback(caddr, &cc, wsp->walk_cbdata)); 4600Sstevel@tonic-gate } 4610Sstevel@tonic-gate 4620Sstevel@tonic-gate int 4630Sstevel@tonic-gate umem_slab_walk_init(mdb_walk_state_t *wsp) 4640Sstevel@tonic-gate { 4650Sstevel@tonic-gate uintptr_t caddr = wsp->walk_addr; 4660Sstevel@tonic-gate umem_cache_t c; 4670Sstevel@tonic-gate 4680Sstevel@tonic-gate if (caddr == NULL) { 4690Sstevel@tonic-gate mdb_warn("umem_slab doesn't support global walks\n"); 4700Sstevel@tonic-gate return (WALK_ERR); 4710Sstevel@tonic-gate } 4720Sstevel@tonic-gate 4730Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), caddr) == -1) { 4740Sstevel@tonic-gate mdb_warn("couldn't read umem_cache at %p", caddr); 4750Sstevel@tonic-gate return (WALK_ERR); 4760Sstevel@tonic-gate } 4770Sstevel@tonic-gate 4780Sstevel@tonic-gate wsp->walk_data = 4790Sstevel@tonic-gate (void *)(caddr + offsetof(umem_cache_t, cache_nullslab)); 4800Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_next; 4810Sstevel@tonic-gate 4820Sstevel@tonic-gate return (WALK_NEXT); 4830Sstevel@tonic-gate } 4840Sstevel@tonic-gate 4850Sstevel@tonic-gate int 4860Sstevel@tonic-gate umem_slab_walk_partial_init(mdb_walk_state_t *wsp) 4870Sstevel@tonic-gate { 4880Sstevel@tonic-gate uintptr_t caddr = wsp->walk_addr; 4890Sstevel@tonic-gate umem_cache_t c; 4900Sstevel@tonic-gate 4910Sstevel@tonic-gate if (caddr == NULL) { 4920Sstevel@tonic-gate mdb_warn("umem_slab_partial doesn't support global walks\n"); 4930Sstevel@tonic-gate return (WALK_ERR); 4940Sstevel@tonic-gate } 4950Sstevel@tonic-gate 4960Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), caddr) == -1) { 4970Sstevel@tonic-gate mdb_warn("couldn't read umem_cache at %p", caddr); 4980Sstevel@tonic-gate return (WALK_ERR); 4990Sstevel@tonic-gate } 5000Sstevel@tonic-gate 5010Sstevel@tonic-gate wsp->walk_data = 5020Sstevel@tonic-gate (void *)(caddr + offsetof(umem_cache_t, cache_nullslab)); 5030Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)c.cache_freelist; 5040Sstevel@tonic-gate 5050Sstevel@tonic-gate /* 5060Sstevel@tonic-gate * Some consumers (umem_walk_step(), in particular) require at 5070Sstevel@tonic-gate * least one callback if there are any buffers in the cache. So 5080Sstevel@tonic-gate * if there are *no* partial slabs, report the last full slab, if 5090Sstevel@tonic-gate * any. 5100Sstevel@tonic-gate * 5110Sstevel@tonic-gate * Yes, this is ugly, but it's cleaner than the other possibilities. 5120Sstevel@tonic-gate */ 5130Sstevel@tonic-gate if ((uintptr_t)wsp->walk_data == wsp->walk_addr) 5140Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_prev; 5150Sstevel@tonic-gate 5160Sstevel@tonic-gate return (WALK_NEXT); 5170Sstevel@tonic-gate } 5180Sstevel@tonic-gate 5190Sstevel@tonic-gate int 5200Sstevel@tonic-gate umem_slab_walk_step(mdb_walk_state_t *wsp) 5210Sstevel@tonic-gate { 5220Sstevel@tonic-gate umem_slab_t s; 5230Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 5240Sstevel@tonic-gate uintptr_t saddr = (uintptr_t)wsp->walk_data; 5250Sstevel@tonic-gate uintptr_t caddr = saddr - offsetof(umem_cache_t, cache_nullslab); 5260Sstevel@tonic-gate 5270Sstevel@tonic-gate if (addr == saddr) 5280Sstevel@tonic-gate return (WALK_DONE); 5290Sstevel@tonic-gate 5300Sstevel@tonic-gate if (mdb_vread(&s, sizeof (s), addr) == -1) { 5310Sstevel@tonic-gate mdb_warn("failed to read slab at %p", wsp->walk_addr); 5320Sstevel@tonic-gate return (WALK_ERR); 5330Sstevel@tonic-gate } 5340Sstevel@tonic-gate 5350Sstevel@tonic-gate if ((uintptr_t)s.slab_cache != caddr) { 5360Sstevel@tonic-gate mdb_warn("slab %p isn't in cache %p (in cache %p)\n", 5370Sstevel@tonic-gate addr, caddr, s.slab_cache); 5380Sstevel@tonic-gate return (WALK_ERR); 5390Sstevel@tonic-gate } 5400Sstevel@tonic-gate 5410Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)s.slab_next; 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate return (wsp->walk_callback(addr, &s, wsp->walk_cbdata)); 5440Sstevel@tonic-gate } 5450Sstevel@tonic-gate 5460Sstevel@tonic-gate int 5470Sstevel@tonic-gate umem_cache(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv) 5480Sstevel@tonic-gate { 5490Sstevel@tonic-gate umem_cache_t c; 5500Sstevel@tonic-gate 5510Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 5520Sstevel@tonic-gate if (mdb_walk_dcmd("umem_cache", "umem_cache", ac, argv) == -1) { 5530Sstevel@tonic-gate mdb_warn("can't walk umem_cache"); 5540Sstevel@tonic-gate return (DCMD_ERR); 5550Sstevel@tonic-gate } 5560Sstevel@tonic-gate return (DCMD_OK); 5570Sstevel@tonic-gate } 5580Sstevel@tonic-gate 5590Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) 5600Sstevel@tonic-gate mdb_printf("%-?s %-25s %4s %8s %8s %8s\n", "ADDR", "NAME", 5610Sstevel@tonic-gate "FLAG", "CFLAG", "BUFSIZE", "BUFTOTL"); 5620Sstevel@tonic-gate 5630Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), addr) == -1) { 5640Sstevel@tonic-gate mdb_warn("couldn't read umem_cache at %p", addr); 5650Sstevel@tonic-gate return (DCMD_ERR); 5660Sstevel@tonic-gate } 5670Sstevel@tonic-gate 5680Sstevel@tonic-gate mdb_printf("%0?p %-25s %04x %08x %8ld %8lld\n", addr, c.cache_name, 5690Sstevel@tonic-gate c.cache_flags, c.cache_cflags, c.cache_bufsize, c.cache_buftotal); 5700Sstevel@tonic-gate 5710Sstevel@tonic-gate return (DCMD_OK); 5720Sstevel@tonic-gate } 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate static int 5750Sstevel@tonic-gate addrcmp(const void *lhs, const void *rhs) 5760Sstevel@tonic-gate { 5770Sstevel@tonic-gate uintptr_t p1 = *((uintptr_t *)lhs); 5780Sstevel@tonic-gate uintptr_t p2 = *((uintptr_t *)rhs); 5790Sstevel@tonic-gate 5800Sstevel@tonic-gate if (p1 < p2) 5810Sstevel@tonic-gate return (-1); 5820Sstevel@tonic-gate if (p1 > p2) 5830Sstevel@tonic-gate return (1); 5840Sstevel@tonic-gate return (0); 5850Sstevel@tonic-gate } 5860Sstevel@tonic-gate 5870Sstevel@tonic-gate static int 5880Sstevel@tonic-gate bufctlcmp(const umem_bufctl_audit_t **lhs, const umem_bufctl_audit_t **rhs) 5890Sstevel@tonic-gate { 5900Sstevel@tonic-gate const umem_bufctl_audit_t *bcp1 = *lhs; 5910Sstevel@tonic-gate const umem_bufctl_audit_t *bcp2 = *rhs; 5920Sstevel@tonic-gate 5930Sstevel@tonic-gate if (bcp1->bc_timestamp > bcp2->bc_timestamp) 5940Sstevel@tonic-gate return (-1); 5950Sstevel@tonic-gate 5960Sstevel@tonic-gate if (bcp1->bc_timestamp < bcp2->bc_timestamp) 5970Sstevel@tonic-gate return (1); 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate return (0); 6000Sstevel@tonic-gate } 6010Sstevel@tonic-gate 6020Sstevel@tonic-gate typedef struct umem_hash_walk { 6030Sstevel@tonic-gate uintptr_t *umhw_table; 6040Sstevel@tonic-gate size_t umhw_nelems; 6050Sstevel@tonic-gate size_t umhw_pos; 6060Sstevel@tonic-gate umem_bufctl_t umhw_cur; 6070Sstevel@tonic-gate } umem_hash_walk_t; 6080Sstevel@tonic-gate 6090Sstevel@tonic-gate int 6100Sstevel@tonic-gate umem_hash_walk_init(mdb_walk_state_t *wsp) 6110Sstevel@tonic-gate { 6120Sstevel@tonic-gate umem_hash_walk_t *umhw; 6130Sstevel@tonic-gate uintptr_t *hash; 6140Sstevel@tonic-gate umem_cache_t c; 6150Sstevel@tonic-gate uintptr_t haddr, addr = wsp->walk_addr; 6160Sstevel@tonic-gate size_t nelems; 6170Sstevel@tonic-gate size_t hsize; 6180Sstevel@tonic-gate 6190Sstevel@tonic-gate if (addr == NULL) { 6200Sstevel@tonic-gate mdb_warn("umem_hash doesn't support global walks\n"); 6210Sstevel@tonic-gate return (WALK_ERR); 6220Sstevel@tonic-gate } 6230Sstevel@tonic-gate 6240Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), addr) == -1) { 6250Sstevel@tonic-gate mdb_warn("couldn't read cache at addr %p", addr); 6260Sstevel@tonic-gate return (WALK_ERR); 6270Sstevel@tonic-gate } 6280Sstevel@tonic-gate 6290Sstevel@tonic-gate if (!(c.cache_flags & UMF_HASH)) { 6300Sstevel@tonic-gate mdb_warn("cache %p doesn't have a hash table\n", addr); 6310Sstevel@tonic-gate return (WALK_DONE); /* nothing to do */ 6320Sstevel@tonic-gate } 6330Sstevel@tonic-gate 6340Sstevel@tonic-gate umhw = mdb_zalloc(sizeof (umem_hash_walk_t), UM_SLEEP); 6350Sstevel@tonic-gate umhw->umhw_cur.bc_next = NULL; 6360Sstevel@tonic-gate umhw->umhw_pos = 0; 6370Sstevel@tonic-gate 6380Sstevel@tonic-gate umhw->umhw_nelems = nelems = c.cache_hash_mask + 1; 6390Sstevel@tonic-gate hsize = nelems * sizeof (uintptr_t); 6400Sstevel@tonic-gate haddr = (uintptr_t)c.cache_hash_table; 6410Sstevel@tonic-gate 6420Sstevel@tonic-gate umhw->umhw_table = hash = mdb_alloc(hsize, UM_SLEEP); 6430Sstevel@tonic-gate if (mdb_vread(hash, hsize, haddr) == -1) { 6440Sstevel@tonic-gate mdb_warn("failed to read hash table at %p", haddr); 6450Sstevel@tonic-gate mdb_free(hash, hsize); 6460Sstevel@tonic-gate mdb_free(umhw, sizeof (umem_hash_walk_t)); 6470Sstevel@tonic-gate return (WALK_ERR); 6480Sstevel@tonic-gate } 6490Sstevel@tonic-gate 6500Sstevel@tonic-gate wsp->walk_data = umhw; 6510Sstevel@tonic-gate 6520Sstevel@tonic-gate return (WALK_NEXT); 6530Sstevel@tonic-gate } 6540Sstevel@tonic-gate 6550Sstevel@tonic-gate int 6560Sstevel@tonic-gate umem_hash_walk_step(mdb_walk_state_t *wsp) 6570Sstevel@tonic-gate { 6580Sstevel@tonic-gate umem_hash_walk_t *umhw = wsp->walk_data; 6590Sstevel@tonic-gate uintptr_t addr = NULL; 6600Sstevel@tonic-gate 6610Sstevel@tonic-gate if ((addr = (uintptr_t)umhw->umhw_cur.bc_next) == NULL) { 6620Sstevel@tonic-gate while (umhw->umhw_pos < umhw->umhw_nelems) { 6630Sstevel@tonic-gate if ((addr = umhw->umhw_table[umhw->umhw_pos++]) != NULL) 6640Sstevel@tonic-gate break; 6650Sstevel@tonic-gate } 6660Sstevel@tonic-gate } 6670Sstevel@tonic-gate if (addr == NULL) 6680Sstevel@tonic-gate return (WALK_DONE); 6690Sstevel@tonic-gate 6700Sstevel@tonic-gate if (mdb_vread(&umhw->umhw_cur, sizeof (umem_bufctl_t), addr) == -1) { 6710Sstevel@tonic-gate mdb_warn("couldn't read umem_bufctl_t at addr %p", addr); 6720Sstevel@tonic-gate return (WALK_ERR); 6730Sstevel@tonic-gate } 6740Sstevel@tonic-gate 6750Sstevel@tonic-gate return (wsp->walk_callback(addr, &umhw->umhw_cur, wsp->walk_cbdata)); 6760Sstevel@tonic-gate } 6770Sstevel@tonic-gate 6780Sstevel@tonic-gate void 6790Sstevel@tonic-gate umem_hash_walk_fini(mdb_walk_state_t *wsp) 6800Sstevel@tonic-gate { 6810Sstevel@tonic-gate umem_hash_walk_t *umhw = wsp->walk_data; 6820Sstevel@tonic-gate 6830Sstevel@tonic-gate if (umhw == NULL) 6840Sstevel@tonic-gate return; 6850Sstevel@tonic-gate 6860Sstevel@tonic-gate mdb_free(umhw->umhw_table, umhw->umhw_nelems * sizeof (uintptr_t)); 6870Sstevel@tonic-gate mdb_free(umhw, sizeof (umem_hash_walk_t)); 6880Sstevel@tonic-gate } 6890Sstevel@tonic-gate 6900Sstevel@tonic-gate /* 6910Sstevel@tonic-gate * Find the address of the bufctl structure for the address 'buf' in cache 6920Sstevel@tonic-gate * 'cp', which is at address caddr, and place it in *out. 6930Sstevel@tonic-gate */ 6940Sstevel@tonic-gate static int 6950Sstevel@tonic-gate umem_hash_lookup(umem_cache_t *cp, uintptr_t caddr, void *buf, uintptr_t *out) 6960Sstevel@tonic-gate { 6970Sstevel@tonic-gate uintptr_t bucket = (uintptr_t)UMEM_HASH(cp, buf); 6980Sstevel@tonic-gate umem_bufctl_t *bcp; 6990Sstevel@tonic-gate umem_bufctl_t bc; 7000Sstevel@tonic-gate 7010Sstevel@tonic-gate if (mdb_vread(&bcp, sizeof (umem_bufctl_t *), bucket) == -1) { 7020Sstevel@tonic-gate mdb_warn("unable to read hash bucket for %p in cache %p", 7030Sstevel@tonic-gate buf, caddr); 7040Sstevel@tonic-gate return (-1); 7050Sstevel@tonic-gate } 7060Sstevel@tonic-gate 7070Sstevel@tonic-gate while (bcp != NULL) { 7080Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (umem_bufctl_t), 7090Sstevel@tonic-gate (uintptr_t)bcp) == -1) { 7100Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", bcp); 7110Sstevel@tonic-gate return (-1); 7120Sstevel@tonic-gate } 7130Sstevel@tonic-gate if (bc.bc_addr == buf) { 7140Sstevel@tonic-gate *out = (uintptr_t)bcp; 7150Sstevel@tonic-gate return (0); 7160Sstevel@tonic-gate } 7170Sstevel@tonic-gate bcp = bc.bc_next; 7180Sstevel@tonic-gate } 7190Sstevel@tonic-gate 7200Sstevel@tonic-gate mdb_warn("unable to find bufctl for %p in cache %p\n", buf, caddr); 7210Sstevel@tonic-gate return (-1); 7220Sstevel@tonic-gate } 7230Sstevel@tonic-gate 7240Sstevel@tonic-gate int 7250Sstevel@tonic-gate umem_get_magsize(const umem_cache_t *cp) 7260Sstevel@tonic-gate { 7270Sstevel@tonic-gate uintptr_t addr = (uintptr_t)cp->cache_magtype; 7280Sstevel@tonic-gate GElf_Sym mt_sym; 7290Sstevel@tonic-gate umem_magtype_t mt; 7300Sstevel@tonic-gate int res; 7310Sstevel@tonic-gate 7320Sstevel@tonic-gate /* 7330Sstevel@tonic-gate * if cpu 0 has a non-zero magsize, it must be correct. caches 7340Sstevel@tonic-gate * with UMF_NOMAGAZINE have disabled their magazine layers, so 7350Sstevel@tonic-gate * it is okay to return 0 for them. 7360Sstevel@tonic-gate */ 7370Sstevel@tonic-gate if ((res = cp->cache_cpu[0].cc_magsize) != 0 || 7380Sstevel@tonic-gate (cp->cache_flags & UMF_NOMAGAZINE)) 7390Sstevel@tonic-gate return (res); 7400Sstevel@tonic-gate 741*1528Sjwadams if (umem_lookup_by_name("umem_magtype", &mt_sym) == -1) { 7420Sstevel@tonic-gate mdb_warn("unable to read 'umem_magtype'"); 7430Sstevel@tonic-gate } else if (addr < mt_sym.st_value || 7440Sstevel@tonic-gate addr + sizeof (mt) - 1 > mt_sym.st_value + mt_sym.st_size - 1 || 7450Sstevel@tonic-gate ((addr - mt_sym.st_value) % sizeof (mt)) != 0) { 7460Sstevel@tonic-gate mdb_warn("cache '%s' has invalid magtype pointer (%p)\n", 7470Sstevel@tonic-gate cp->cache_name, addr); 7480Sstevel@tonic-gate return (0); 7490Sstevel@tonic-gate } 7500Sstevel@tonic-gate if (mdb_vread(&mt, sizeof (mt), addr) == -1) { 7510Sstevel@tonic-gate mdb_warn("unable to read magtype at %a", addr); 7520Sstevel@tonic-gate return (0); 7530Sstevel@tonic-gate } 7540Sstevel@tonic-gate return (mt.mt_magsize); 7550Sstevel@tonic-gate } 7560Sstevel@tonic-gate 7570Sstevel@tonic-gate /*ARGSUSED*/ 7580Sstevel@tonic-gate static int 7590Sstevel@tonic-gate umem_estimate_slab(uintptr_t addr, const umem_slab_t *sp, size_t *est) 7600Sstevel@tonic-gate { 7610Sstevel@tonic-gate *est -= (sp->slab_chunks - sp->slab_refcnt); 7620Sstevel@tonic-gate 7630Sstevel@tonic-gate return (WALK_NEXT); 7640Sstevel@tonic-gate } 7650Sstevel@tonic-gate 7660Sstevel@tonic-gate /* 7670Sstevel@tonic-gate * Returns an upper bound on the number of allocated buffers in a given 7680Sstevel@tonic-gate * cache. 7690Sstevel@tonic-gate */ 7700Sstevel@tonic-gate size_t 7710Sstevel@tonic-gate umem_estimate_allocated(uintptr_t addr, const umem_cache_t *cp) 7720Sstevel@tonic-gate { 7730Sstevel@tonic-gate int magsize; 7740Sstevel@tonic-gate size_t cache_est; 7750Sstevel@tonic-gate 7760Sstevel@tonic-gate cache_est = cp->cache_buftotal; 7770Sstevel@tonic-gate 7780Sstevel@tonic-gate (void) mdb_pwalk("umem_slab_partial", 7790Sstevel@tonic-gate (mdb_walk_cb_t)umem_estimate_slab, &cache_est, addr); 7800Sstevel@tonic-gate 7810Sstevel@tonic-gate if ((magsize = umem_get_magsize(cp)) != 0) { 7820Sstevel@tonic-gate size_t mag_est = cp->cache_full.ml_total * magsize; 7830Sstevel@tonic-gate 7840Sstevel@tonic-gate if (cache_est >= mag_est) { 7850Sstevel@tonic-gate cache_est -= mag_est; 7860Sstevel@tonic-gate } else { 7870Sstevel@tonic-gate mdb_warn("cache %p's magazine layer holds more buffers " 7880Sstevel@tonic-gate "than the slab layer.\n", addr); 7890Sstevel@tonic-gate } 7900Sstevel@tonic-gate } 7910Sstevel@tonic-gate return (cache_est); 7920Sstevel@tonic-gate } 7930Sstevel@tonic-gate 7940Sstevel@tonic-gate #define READMAG_ROUNDS(rounds) { \ 7950Sstevel@tonic-gate if (mdb_vread(mp, magbsize, (uintptr_t)ump) == -1) { \ 7960Sstevel@tonic-gate mdb_warn("couldn't read magazine at %p", ump); \ 7970Sstevel@tonic-gate goto fail; \ 7980Sstevel@tonic-gate } \ 7990Sstevel@tonic-gate for (i = 0; i < rounds; i++) { \ 8000Sstevel@tonic-gate maglist[magcnt++] = mp->mag_round[i]; \ 8010Sstevel@tonic-gate if (magcnt == magmax) { \ 8020Sstevel@tonic-gate mdb_warn("%d magazines exceeds fudge factor\n", \ 8030Sstevel@tonic-gate magcnt); \ 8040Sstevel@tonic-gate goto fail; \ 8050Sstevel@tonic-gate } \ 8060Sstevel@tonic-gate } \ 8070Sstevel@tonic-gate } 8080Sstevel@tonic-gate 8090Sstevel@tonic-gate int 810*1528Sjwadams umem_read_magazines(umem_cache_t *cp, uintptr_t addr, 8110Sstevel@tonic-gate void ***maglistp, size_t *magcntp, size_t *magmaxp, int alloc_flags) 8120Sstevel@tonic-gate { 8130Sstevel@tonic-gate umem_magazine_t *ump, *mp; 8140Sstevel@tonic-gate void **maglist = NULL; 8150Sstevel@tonic-gate int i, cpu; 8160Sstevel@tonic-gate size_t magsize, magmax, magbsize; 8170Sstevel@tonic-gate size_t magcnt = 0; 8180Sstevel@tonic-gate 8190Sstevel@tonic-gate /* 8200Sstevel@tonic-gate * Read the magtype out of the cache, after verifying the pointer's 8210Sstevel@tonic-gate * correctness. 8220Sstevel@tonic-gate */ 8230Sstevel@tonic-gate magsize = umem_get_magsize(cp); 824*1528Sjwadams if (magsize == 0) { 825*1528Sjwadams *maglistp = NULL; 826*1528Sjwadams *magcntp = 0; 827*1528Sjwadams *magmaxp = 0; 828*1528Sjwadams return (WALK_NEXT); 829*1528Sjwadams } 8300Sstevel@tonic-gate 8310Sstevel@tonic-gate /* 8320Sstevel@tonic-gate * There are several places where we need to go buffer hunting: 8330Sstevel@tonic-gate * the per-CPU loaded magazine, the per-CPU spare full magazine, 8340Sstevel@tonic-gate * and the full magazine list in the depot. 8350Sstevel@tonic-gate * 8360Sstevel@tonic-gate * For an upper bound on the number of buffers in the magazine 8370Sstevel@tonic-gate * layer, we have the number of magazines on the cache_full 8380Sstevel@tonic-gate * list plus at most two magazines per CPU (the loaded and the 8390Sstevel@tonic-gate * spare). Toss in 100 magazines as a fudge factor in case this 8400Sstevel@tonic-gate * is live (the number "100" comes from the same fudge factor in 8410Sstevel@tonic-gate * crash(1M)). 8420Sstevel@tonic-gate */ 843*1528Sjwadams magmax = (cp->cache_full.ml_total + 2 * umem_max_ncpus + 100) * magsize; 8440Sstevel@tonic-gate magbsize = offsetof(umem_magazine_t, mag_round[magsize]); 8450Sstevel@tonic-gate 8460Sstevel@tonic-gate if (magbsize >= PAGESIZE / 2) { 8470Sstevel@tonic-gate mdb_warn("magazine size for cache %p unreasonable (%x)\n", 8480Sstevel@tonic-gate addr, magbsize); 849*1528Sjwadams return (WALK_ERR); 8500Sstevel@tonic-gate } 8510Sstevel@tonic-gate 8520Sstevel@tonic-gate maglist = mdb_alloc(magmax * sizeof (void *), alloc_flags); 8530Sstevel@tonic-gate mp = mdb_alloc(magbsize, alloc_flags); 8540Sstevel@tonic-gate if (mp == NULL || maglist == NULL) 8550Sstevel@tonic-gate goto fail; 8560Sstevel@tonic-gate 8570Sstevel@tonic-gate /* 8580Sstevel@tonic-gate * First up: the magazines in the depot (i.e. on the cache_full list). 8590Sstevel@tonic-gate */ 8600Sstevel@tonic-gate for (ump = cp->cache_full.ml_list; ump != NULL; ) { 8610Sstevel@tonic-gate READMAG_ROUNDS(magsize); 8620Sstevel@tonic-gate ump = mp->mag_next; 8630Sstevel@tonic-gate 8640Sstevel@tonic-gate if (ump == cp->cache_full.ml_list) 8650Sstevel@tonic-gate break; /* cache_full list loop detected */ 8660Sstevel@tonic-gate } 8670Sstevel@tonic-gate 8680Sstevel@tonic-gate dprintf(("cache_full list done\n")); 8690Sstevel@tonic-gate 8700Sstevel@tonic-gate /* 8710Sstevel@tonic-gate * Now whip through the CPUs, snagging the loaded magazines 8720Sstevel@tonic-gate * and full spares. 8730Sstevel@tonic-gate */ 874*1528Sjwadams for (cpu = 0; cpu < umem_max_ncpus; cpu++) { 8750Sstevel@tonic-gate umem_cpu_cache_t *ccp = &cp->cache_cpu[cpu]; 8760Sstevel@tonic-gate 8770Sstevel@tonic-gate dprintf(("reading cpu cache %p\n", 8780Sstevel@tonic-gate (uintptr_t)ccp - (uintptr_t)cp + addr)); 8790Sstevel@tonic-gate 8800Sstevel@tonic-gate if (ccp->cc_rounds > 0 && 8810Sstevel@tonic-gate (ump = ccp->cc_loaded) != NULL) { 8820Sstevel@tonic-gate dprintf(("reading %d loaded rounds\n", ccp->cc_rounds)); 8830Sstevel@tonic-gate READMAG_ROUNDS(ccp->cc_rounds); 8840Sstevel@tonic-gate } 8850Sstevel@tonic-gate 8860Sstevel@tonic-gate if (ccp->cc_prounds > 0 && 8870Sstevel@tonic-gate (ump = ccp->cc_ploaded) != NULL) { 8880Sstevel@tonic-gate dprintf(("reading %d previously loaded rounds\n", 8890Sstevel@tonic-gate ccp->cc_prounds)); 8900Sstevel@tonic-gate READMAG_ROUNDS(ccp->cc_prounds); 8910Sstevel@tonic-gate } 8920Sstevel@tonic-gate } 8930Sstevel@tonic-gate 8940Sstevel@tonic-gate dprintf(("magazine layer: %d buffers\n", magcnt)); 8950Sstevel@tonic-gate 8960Sstevel@tonic-gate if (!(alloc_flags & UM_GC)) 8970Sstevel@tonic-gate mdb_free(mp, magbsize); 8980Sstevel@tonic-gate 8990Sstevel@tonic-gate *maglistp = maglist; 9000Sstevel@tonic-gate *magcntp = magcnt; 9010Sstevel@tonic-gate *magmaxp = magmax; 9020Sstevel@tonic-gate 9030Sstevel@tonic-gate return (WALK_NEXT); 9040Sstevel@tonic-gate 9050Sstevel@tonic-gate fail: 9060Sstevel@tonic-gate if (!(alloc_flags & UM_GC)) { 9070Sstevel@tonic-gate if (mp) 9080Sstevel@tonic-gate mdb_free(mp, magbsize); 9090Sstevel@tonic-gate if (maglist) 9100Sstevel@tonic-gate mdb_free(maglist, magmax * sizeof (void *)); 9110Sstevel@tonic-gate } 9120Sstevel@tonic-gate return (WALK_ERR); 9130Sstevel@tonic-gate } 9140Sstevel@tonic-gate 9150Sstevel@tonic-gate static int 9160Sstevel@tonic-gate umem_walk_callback(mdb_walk_state_t *wsp, uintptr_t buf) 9170Sstevel@tonic-gate { 9180Sstevel@tonic-gate return (wsp->walk_callback(buf, NULL, wsp->walk_cbdata)); 9190Sstevel@tonic-gate } 9200Sstevel@tonic-gate 9210Sstevel@tonic-gate static int 9220Sstevel@tonic-gate bufctl_walk_callback(umem_cache_t *cp, mdb_walk_state_t *wsp, uintptr_t buf) 9230Sstevel@tonic-gate { 9240Sstevel@tonic-gate umem_bufctl_audit_t *b; 9250Sstevel@tonic-gate UMEM_LOCAL_BUFCTL_AUDIT(&b); 9260Sstevel@tonic-gate 9270Sstevel@tonic-gate /* 9280Sstevel@tonic-gate * if UMF_AUDIT is not set, we know that we're looking at a 9290Sstevel@tonic-gate * umem_bufctl_t. 9300Sstevel@tonic-gate */ 9310Sstevel@tonic-gate if (!(cp->cache_flags & UMF_AUDIT) || 9320Sstevel@tonic-gate mdb_vread(b, UMEM_BUFCTL_AUDIT_SIZE, buf) == -1) { 9330Sstevel@tonic-gate (void) memset(b, 0, UMEM_BUFCTL_AUDIT_SIZE); 9340Sstevel@tonic-gate if (mdb_vread(b, sizeof (umem_bufctl_t), buf) == -1) { 9350Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", buf); 9360Sstevel@tonic-gate return (WALK_ERR); 9370Sstevel@tonic-gate } 9380Sstevel@tonic-gate } 9390Sstevel@tonic-gate 9400Sstevel@tonic-gate return (wsp->walk_callback(buf, b, wsp->walk_cbdata)); 9410Sstevel@tonic-gate } 9420Sstevel@tonic-gate 9430Sstevel@tonic-gate typedef struct umem_walk { 9440Sstevel@tonic-gate int umw_type; 9450Sstevel@tonic-gate 9460Sstevel@tonic-gate int umw_addr; /* cache address */ 9470Sstevel@tonic-gate umem_cache_t *umw_cp; 9480Sstevel@tonic-gate size_t umw_csize; 9490Sstevel@tonic-gate 9500Sstevel@tonic-gate /* 9510Sstevel@tonic-gate * magazine layer 9520Sstevel@tonic-gate */ 9530Sstevel@tonic-gate void **umw_maglist; 9540Sstevel@tonic-gate size_t umw_max; 9550Sstevel@tonic-gate size_t umw_count; 9560Sstevel@tonic-gate size_t umw_pos; 9570Sstevel@tonic-gate 9580Sstevel@tonic-gate /* 9590Sstevel@tonic-gate * slab layer 9600Sstevel@tonic-gate */ 9610Sstevel@tonic-gate char *umw_valid; /* to keep track of freed buffers */ 9620Sstevel@tonic-gate char *umw_ubase; /* buffer for slab data */ 9630Sstevel@tonic-gate } umem_walk_t; 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate static int 9660Sstevel@tonic-gate umem_walk_init_common(mdb_walk_state_t *wsp, int type) 9670Sstevel@tonic-gate { 9680Sstevel@tonic-gate umem_walk_t *umw; 969*1528Sjwadams int csize; 9700Sstevel@tonic-gate umem_cache_t *cp; 971*1528Sjwadams size_t vm_quantum; 9720Sstevel@tonic-gate 9730Sstevel@tonic-gate size_t magmax, magcnt; 9740Sstevel@tonic-gate void **maglist = NULL; 9750Sstevel@tonic-gate uint_t chunksize, slabsize; 9760Sstevel@tonic-gate int status = WALK_ERR; 9770Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 9780Sstevel@tonic-gate const char *layered; 9790Sstevel@tonic-gate 9800Sstevel@tonic-gate type &= ~UM_HASH; 9810Sstevel@tonic-gate 9820Sstevel@tonic-gate if (addr == NULL) { 9830Sstevel@tonic-gate mdb_warn("umem walk doesn't support global walks\n"); 9840Sstevel@tonic-gate return (WALK_ERR); 9850Sstevel@tonic-gate } 9860Sstevel@tonic-gate 9870Sstevel@tonic-gate dprintf(("walking %p\n", addr)); 9880Sstevel@tonic-gate 9890Sstevel@tonic-gate /* 990*1528Sjwadams * The number of "cpus" determines how large the cache is. 9910Sstevel@tonic-gate */ 992*1528Sjwadams csize = UMEM_CACHE_SIZE(umem_max_ncpus); 9930Sstevel@tonic-gate cp = mdb_alloc(csize, UM_SLEEP); 9940Sstevel@tonic-gate 9950Sstevel@tonic-gate if (mdb_vread(cp, csize, addr) == -1) { 9960Sstevel@tonic-gate mdb_warn("couldn't read cache at addr %p", addr); 9970Sstevel@tonic-gate goto out2; 9980Sstevel@tonic-gate } 9990Sstevel@tonic-gate 1000*1528Sjwadams /* 1001*1528Sjwadams * It's easy for someone to hand us an invalid cache address. 1002*1528Sjwadams * Unfortunately, it is hard for this walker to survive an 1003*1528Sjwadams * invalid cache cleanly. So we make sure that: 1004*1528Sjwadams * 1005*1528Sjwadams * 1. the vmem arena for the cache is readable, 1006*1528Sjwadams * 2. the vmem arena's quantum is a power of 2, 1007*1528Sjwadams * 3. our slabsize is a multiple of the quantum, and 1008*1528Sjwadams * 4. our chunksize is >0 and less than our slabsize. 1009*1528Sjwadams */ 1010*1528Sjwadams if (mdb_vread(&vm_quantum, sizeof (vm_quantum), 1011*1528Sjwadams (uintptr_t)&cp->cache_arena->vm_quantum) == -1 || 1012*1528Sjwadams vm_quantum == 0 || 1013*1528Sjwadams (vm_quantum & (vm_quantum - 1)) != 0 || 1014*1528Sjwadams cp->cache_slabsize < vm_quantum || 1015*1528Sjwadams P2PHASE(cp->cache_slabsize, vm_quantum) != 0 || 1016*1528Sjwadams cp->cache_chunksize == 0 || 1017*1528Sjwadams cp->cache_chunksize > cp->cache_slabsize) { 1018*1528Sjwadams mdb_warn("%p is not a valid umem_cache_t\n", addr); 1019*1528Sjwadams goto out2; 1020*1528Sjwadams } 1021*1528Sjwadams 10220Sstevel@tonic-gate dprintf(("buf total is %d\n", cp->cache_buftotal)); 10230Sstevel@tonic-gate 10240Sstevel@tonic-gate if (cp->cache_buftotal == 0) { 10250Sstevel@tonic-gate mdb_free(cp, csize); 10260Sstevel@tonic-gate return (WALK_DONE); 10270Sstevel@tonic-gate } 10280Sstevel@tonic-gate 10290Sstevel@tonic-gate /* 10300Sstevel@tonic-gate * If they ask for bufctls, but it's a small-slab cache, 10310Sstevel@tonic-gate * there is nothing to report. 10320Sstevel@tonic-gate */ 10330Sstevel@tonic-gate if ((type & UM_BUFCTL) && !(cp->cache_flags & UMF_HASH)) { 10340Sstevel@tonic-gate dprintf(("bufctl requested, not UMF_HASH (flags: %p)\n", 10350Sstevel@tonic-gate cp->cache_flags)); 10360Sstevel@tonic-gate mdb_free(cp, csize); 10370Sstevel@tonic-gate return (WALK_DONE); 10380Sstevel@tonic-gate } 10390Sstevel@tonic-gate 10400Sstevel@tonic-gate /* 10410Sstevel@tonic-gate * Read in the contents of the magazine layer 10420Sstevel@tonic-gate */ 1043*1528Sjwadams if (umem_read_magazines(cp, addr, &maglist, &magcnt, &magmax, 1044*1528Sjwadams UM_SLEEP) == WALK_ERR) 10450Sstevel@tonic-gate goto out2; 10460Sstevel@tonic-gate 10470Sstevel@tonic-gate /* 10480Sstevel@tonic-gate * We have all of the buffers from the magazines; if we are walking 10490Sstevel@tonic-gate * allocated buffers, sort them so we can bsearch them later. 10500Sstevel@tonic-gate */ 10510Sstevel@tonic-gate if (type & UM_ALLOCATED) 10520Sstevel@tonic-gate qsort(maglist, magcnt, sizeof (void *), addrcmp); 10530Sstevel@tonic-gate 10540Sstevel@tonic-gate wsp->walk_data = umw = mdb_zalloc(sizeof (umem_walk_t), UM_SLEEP); 10550Sstevel@tonic-gate 10560Sstevel@tonic-gate umw->umw_type = type; 10570Sstevel@tonic-gate umw->umw_addr = addr; 10580Sstevel@tonic-gate umw->umw_cp = cp; 10590Sstevel@tonic-gate umw->umw_csize = csize; 10600Sstevel@tonic-gate umw->umw_maglist = maglist; 10610Sstevel@tonic-gate umw->umw_max = magmax; 10620Sstevel@tonic-gate umw->umw_count = magcnt; 10630Sstevel@tonic-gate umw->umw_pos = 0; 10640Sstevel@tonic-gate 10650Sstevel@tonic-gate /* 10660Sstevel@tonic-gate * When walking allocated buffers in a UMF_HASH cache, we walk the 10670Sstevel@tonic-gate * hash table instead of the slab layer. 10680Sstevel@tonic-gate */ 10690Sstevel@tonic-gate if ((cp->cache_flags & UMF_HASH) && (type & UM_ALLOCATED)) { 10700Sstevel@tonic-gate layered = "umem_hash"; 10710Sstevel@tonic-gate 10720Sstevel@tonic-gate umw->umw_type |= UM_HASH; 10730Sstevel@tonic-gate } else { 10740Sstevel@tonic-gate /* 10750Sstevel@tonic-gate * If we are walking freed buffers, we only need the 10760Sstevel@tonic-gate * magazine layer plus the partially allocated slabs. 10770Sstevel@tonic-gate * To walk allocated buffers, we need all of the slabs. 10780Sstevel@tonic-gate */ 10790Sstevel@tonic-gate if (type & UM_ALLOCATED) 10800Sstevel@tonic-gate layered = "umem_slab"; 10810Sstevel@tonic-gate else 10820Sstevel@tonic-gate layered = "umem_slab_partial"; 10830Sstevel@tonic-gate 10840Sstevel@tonic-gate /* 10850Sstevel@tonic-gate * for small-slab caches, we read in the entire slab. For 10860Sstevel@tonic-gate * freed buffers, we can just walk the freelist. For 10870Sstevel@tonic-gate * allocated buffers, we use a 'valid' array to track 10880Sstevel@tonic-gate * the freed buffers. 10890Sstevel@tonic-gate */ 10900Sstevel@tonic-gate if (!(cp->cache_flags & UMF_HASH)) { 10910Sstevel@tonic-gate chunksize = cp->cache_chunksize; 10920Sstevel@tonic-gate slabsize = cp->cache_slabsize; 10930Sstevel@tonic-gate 10940Sstevel@tonic-gate umw->umw_ubase = mdb_alloc(slabsize + 10950Sstevel@tonic-gate sizeof (umem_bufctl_t), UM_SLEEP); 10960Sstevel@tonic-gate 10970Sstevel@tonic-gate if (type & UM_ALLOCATED) 10980Sstevel@tonic-gate umw->umw_valid = 10990Sstevel@tonic-gate mdb_alloc(slabsize / chunksize, UM_SLEEP); 11000Sstevel@tonic-gate } 11010Sstevel@tonic-gate } 11020Sstevel@tonic-gate 11030Sstevel@tonic-gate status = WALK_NEXT; 11040Sstevel@tonic-gate 11050Sstevel@tonic-gate if (mdb_layered_walk(layered, wsp) == -1) { 11060Sstevel@tonic-gate mdb_warn("unable to start layered '%s' walk", layered); 11070Sstevel@tonic-gate status = WALK_ERR; 11080Sstevel@tonic-gate } 11090Sstevel@tonic-gate 11100Sstevel@tonic-gate out1: 11110Sstevel@tonic-gate if (status == WALK_ERR) { 11120Sstevel@tonic-gate if (umw->umw_valid) 11130Sstevel@tonic-gate mdb_free(umw->umw_valid, slabsize / chunksize); 11140Sstevel@tonic-gate 11150Sstevel@tonic-gate if (umw->umw_ubase) 11160Sstevel@tonic-gate mdb_free(umw->umw_ubase, slabsize + 11170Sstevel@tonic-gate sizeof (umem_bufctl_t)); 11180Sstevel@tonic-gate 1119*1528Sjwadams if (umw->umw_maglist) 1120*1528Sjwadams mdb_free(umw->umw_maglist, umw->umw_max * 1121*1528Sjwadams sizeof (uintptr_t)); 1122*1528Sjwadams 11230Sstevel@tonic-gate mdb_free(umw, sizeof (umem_walk_t)); 11240Sstevel@tonic-gate wsp->walk_data = NULL; 11250Sstevel@tonic-gate } 11260Sstevel@tonic-gate 11270Sstevel@tonic-gate out2: 11280Sstevel@tonic-gate if (status == WALK_ERR) 11290Sstevel@tonic-gate mdb_free(cp, csize); 11300Sstevel@tonic-gate 11310Sstevel@tonic-gate return (status); 11320Sstevel@tonic-gate } 11330Sstevel@tonic-gate 11340Sstevel@tonic-gate int 11350Sstevel@tonic-gate umem_walk_step(mdb_walk_state_t *wsp) 11360Sstevel@tonic-gate { 11370Sstevel@tonic-gate umem_walk_t *umw = wsp->walk_data; 11380Sstevel@tonic-gate int type = umw->umw_type; 11390Sstevel@tonic-gate umem_cache_t *cp = umw->umw_cp; 11400Sstevel@tonic-gate 11410Sstevel@tonic-gate void **maglist = umw->umw_maglist; 11420Sstevel@tonic-gate int magcnt = umw->umw_count; 11430Sstevel@tonic-gate 11440Sstevel@tonic-gate uintptr_t chunksize, slabsize; 11450Sstevel@tonic-gate uintptr_t addr; 11460Sstevel@tonic-gate const umem_slab_t *sp; 11470Sstevel@tonic-gate const umem_bufctl_t *bcp; 11480Sstevel@tonic-gate umem_bufctl_t bc; 11490Sstevel@tonic-gate 11500Sstevel@tonic-gate int chunks; 11510Sstevel@tonic-gate char *kbase; 11520Sstevel@tonic-gate void *buf; 11530Sstevel@tonic-gate int i, ret; 11540Sstevel@tonic-gate 11550Sstevel@tonic-gate char *valid, *ubase; 11560Sstevel@tonic-gate 11570Sstevel@tonic-gate /* 11580Sstevel@tonic-gate * first, handle the 'umem_hash' layered walk case 11590Sstevel@tonic-gate */ 11600Sstevel@tonic-gate if (type & UM_HASH) { 11610Sstevel@tonic-gate /* 11620Sstevel@tonic-gate * We have a buffer which has been allocated out of the 11630Sstevel@tonic-gate * global layer. We need to make sure that it's not 11640Sstevel@tonic-gate * actually sitting in a magazine before we report it as 11650Sstevel@tonic-gate * an allocated buffer. 11660Sstevel@tonic-gate */ 11670Sstevel@tonic-gate buf = ((const umem_bufctl_t *)wsp->walk_layer)->bc_addr; 11680Sstevel@tonic-gate 11690Sstevel@tonic-gate if (magcnt > 0 && 11700Sstevel@tonic-gate bsearch(&buf, maglist, magcnt, sizeof (void *), 11710Sstevel@tonic-gate addrcmp) != NULL) 11720Sstevel@tonic-gate return (WALK_NEXT); 11730Sstevel@tonic-gate 11740Sstevel@tonic-gate if (type & UM_BUFCTL) 11750Sstevel@tonic-gate return (bufctl_walk_callback(cp, wsp, wsp->walk_addr)); 11760Sstevel@tonic-gate 11770Sstevel@tonic-gate return (umem_walk_callback(wsp, (uintptr_t)buf)); 11780Sstevel@tonic-gate } 11790Sstevel@tonic-gate 11800Sstevel@tonic-gate ret = WALK_NEXT; 11810Sstevel@tonic-gate 11820Sstevel@tonic-gate addr = umw->umw_addr; 11830Sstevel@tonic-gate 11840Sstevel@tonic-gate /* 11850Sstevel@tonic-gate * If we're walking freed buffers, report everything in the 11860Sstevel@tonic-gate * magazine layer before processing the first slab. 11870Sstevel@tonic-gate */ 11880Sstevel@tonic-gate if ((type & UM_FREE) && magcnt != 0) { 11890Sstevel@tonic-gate umw->umw_count = 0; /* only do this once */ 11900Sstevel@tonic-gate for (i = 0; i < magcnt; i++) { 11910Sstevel@tonic-gate buf = maglist[i]; 11920Sstevel@tonic-gate 11930Sstevel@tonic-gate if (type & UM_BUFCTL) { 11940Sstevel@tonic-gate uintptr_t out; 11950Sstevel@tonic-gate 11960Sstevel@tonic-gate if (cp->cache_flags & UMF_BUFTAG) { 11970Sstevel@tonic-gate umem_buftag_t *btp; 11980Sstevel@tonic-gate umem_buftag_t tag; 11990Sstevel@tonic-gate 12000Sstevel@tonic-gate /* LINTED - alignment */ 12010Sstevel@tonic-gate btp = UMEM_BUFTAG(cp, buf); 12020Sstevel@tonic-gate if (mdb_vread(&tag, sizeof (tag), 12030Sstevel@tonic-gate (uintptr_t)btp) == -1) { 12040Sstevel@tonic-gate mdb_warn("reading buftag for " 12050Sstevel@tonic-gate "%p at %p", buf, btp); 12060Sstevel@tonic-gate continue; 12070Sstevel@tonic-gate } 12080Sstevel@tonic-gate out = (uintptr_t)tag.bt_bufctl; 12090Sstevel@tonic-gate } else { 12100Sstevel@tonic-gate if (umem_hash_lookup(cp, addr, buf, 12110Sstevel@tonic-gate &out) == -1) 12120Sstevel@tonic-gate continue; 12130Sstevel@tonic-gate } 12140Sstevel@tonic-gate ret = bufctl_walk_callback(cp, wsp, out); 12150Sstevel@tonic-gate } else { 12160Sstevel@tonic-gate ret = umem_walk_callback(wsp, (uintptr_t)buf); 12170Sstevel@tonic-gate } 12180Sstevel@tonic-gate 12190Sstevel@tonic-gate if (ret != WALK_NEXT) 12200Sstevel@tonic-gate return (ret); 12210Sstevel@tonic-gate } 12220Sstevel@tonic-gate } 12230Sstevel@tonic-gate 12240Sstevel@tonic-gate /* 12250Sstevel@tonic-gate * Handle the buffers in the current slab 12260Sstevel@tonic-gate */ 12270Sstevel@tonic-gate chunksize = cp->cache_chunksize; 12280Sstevel@tonic-gate slabsize = cp->cache_slabsize; 12290Sstevel@tonic-gate 12300Sstevel@tonic-gate sp = wsp->walk_layer; 12310Sstevel@tonic-gate chunks = sp->slab_chunks; 12320Sstevel@tonic-gate kbase = sp->slab_base; 12330Sstevel@tonic-gate 12340Sstevel@tonic-gate dprintf(("kbase is %p\n", kbase)); 12350Sstevel@tonic-gate 12360Sstevel@tonic-gate if (!(cp->cache_flags & UMF_HASH)) { 12370Sstevel@tonic-gate valid = umw->umw_valid; 12380Sstevel@tonic-gate ubase = umw->umw_ubase; 12390Sstevel@tonic-gate 12400Sstevel@tonic-gate if (mdb_vread(ubase, chunks * chunksize, 12410Sstevel@tonic-gate (uintptr_t)kbase) == -1) { 12420Sstevel@tonic-gate mdb_warn("failed to read slab contents at %p", kbase); 12430Sstevel@tonic-gate return (WALK_ERR); 12440Sstevel@tonic-gate } 12450Sstevel@tonic-gate 12460Sstevel@tonic-gate /* 12470Sstevel@tonic-gate * Set up the valid map as fully allocated -- we'll punch 12480Sstevel@tonic-gate * out the freelist. 12490Sstevel@tonic-gate */ 12500Sstevel@tonic-gate if (type & UM_ALLOCATED) 12510Sstevel@tonic-gate (void) memset(valid, 1, chunks); 12520Sstevel@tonic-gate } else { 12530Sstevel@tonic-gate valid = NULL; 12540Sstevel@tonic-gate ubase = NULL; 12550Sstevel@tonic-gate } 12560Sstevel@tonic-gate 12570Sstevel@tonic-gate /* 12580Sstevel@tonic-gate * walk the slab's freelist 12590Sstevel@tonic-gate */ 12600Sstevel@tonic-gate bcp = sp->slab_head; 12610Sstevel@tonic-gate 12620Sstevel@tonic-gate dprintf(("refcnt is %d; chunks is %d\n", sp->slab_refcnt, chunks)); 12630Sstevel@tonic-gate 12640Sstevel@tonic-gate /* 12650Sstevel@tonic-gate * since we could be in the middle of allocating a buffer, 12660Sstevel@tonic-gate * our refcnt could be one higher than it aught. So we 12670Sstevel@tonic-gate * check one further on the freelist than the count allows. 12680Sstevel@tonic-gate */ 12690Sstevel@tonic-gate for (i = sp->slab_refcnt; i <= chunks; i++) { 12700Sstevel@tonic-gate uint_t ndx; 12710Sstevel@tonic-gate 12720Sstevel@tonic-gate dprintf(("bcp is %p\n", bcp)); 12730Sstevel@tonic-gate 12740Sstevel@tonic-gate if (bcp == NULL) { 12750Sstevel@tonic-gate if (i == chunks) 12760Sstevel@tonic-gate break; 12770Sstevel@tonic-gate mdb_warn( 12780Sstevel@tonic-gate "slab %p in cache %p freelist too short by %d\n", 12790Sstevel@tonic-gate sp, addr, chunks - i); 12800Sstevel@tonic-gate break; 12810Sstevel@tonic-gate } 12820Sstevel@tonic-gate 12830Sstevel@tonic-gate if (cp->cache_flags & UMF_HASH) { 12840Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), (uintptr_t)bcp) == -1) { 12850Sstevel@tonic-gate mdb_warn("failed to read bufctl ptr at %p", 12860Sstevel@tonic-gate bcp); 12870Sstevel@tonic-gate break; 12880Sstevel@tonic-gate } 12890Sstevel@tonic-gate buf = bc.bc_addr; 12900Sstevel@tonic-gate } else { 12910Sstevel@tonic-gate /* 12920Sstevel@tonic-gate * Otherwise the buffer is in the slab which 12930Sstevel@tonic-gate * we've read in; we just need to determine 12940Sstevel@tonic-gate * its offset in the slab to find the 12950Sstevel@tonic-gate * umem_bufctl_t. 12960Sstevel@tonic-gate */ 12970Sstevel@tonic-gate bc = *((umem_bufctl_t *) 12980Sstevel@tonic-gate ((uintptr_t)bcp - (uintptr_t)kbase + 12990Sstevel@tonic-gate (uintptr_t)ubase)); 13000Sstevel@tonic-gate 13010Sstevel@tonic-gate buf = UMEM_BUF(cp, bcp); 13020Sstevel@tonic-gate } 13030Sstevel@tonic-gate 13040Sstevel@tonic-gate ndx = ((uintptr_t)buf - (uintptr_t)kbase) / chunksize; 13050Sstevel@tonic-gate 13060Sstevel@tonic-gate if (ndx > slabsize / cp->cache_bufsize) { 13070Sstevel@tonic-gate /* 13080Sstevel@tonic-gate * This is very wrong; we have managed to find 13090Sstevel@tonic-gate * a buffer in the slab which shouldn't 13100Sstevel@tonic-gate * actually be here. Emit a warning, and 13110Sstevel@tonic-gate * try to continue. 13120Sstevel@tonic-gate */ 13130Sstevel@tonic-gate mdb_warn("buf %p is out of range for " 13140Sstevel@tonic-gate "slab %p, cache %p\n", buf, sp, addr); 13150Sstevel@tonic-gate } else if (type & UM_ALLOCATED) { 13160Sstevel@tonic-gate /* 13170Sstevel@tonic-gate * we have found a buffer on the slab's freelist; 13180Sstevel@tonic-gate * clear its entry 13190Sstevel@tonic-gate */ 13200Sstevel@tonic-gate valid[ndx] = 0; 13210Sstevel@tonic-gate } else { 13220Sstevel@tonic-gate /* 13230Sstevel@tonic-gate * Report this freed buffer 13240Sstevel@tonic-gate */ 13250Sstevel@tonic-gate if (type & UM_BUFCTL) { 13260Sstevel@tonic-gate ret = bufctl_walk_callback(cp, wsp, 13270Sstevel@tonic-gate (uintptr_t)bcp); 13280Sstevel@tonic-gate } else { 13290Sstevel@tonic-gate ret = umem_walk_callback(wsp, (uintptr_t)buf); 13300Sstevel@tonic-gate } 13310Sstevel@tonic-gate if (ret != WALK_NEXT) 13320Sstevel@tonic-gate return (ret); 13330Sstevel@tonic-gate } 13340Sstevel@tonic-gate 13350Sstevel@tonic-gate bcp = bc.bc_next; 13360Sstevel@tonic-gate } 13370Sstevel@tonic-gate 13380Sstevel@tonic-gate if (bcp != NULL) { 13390Sstevel@tonic-gate dprintf(("slab %p in cache %p freelist too long (%p)\n", 13400Sstevel@tonic-gate sp, addr, bcp)); 13410Sstevel@tonic-gate } 13420Sstevel@tonic-gate 13430Sstevel@tonic-gate /* 13440Sstevel@tonic-gate * If we are walking freed buffers, the loop above handled reporting 13450Sstevel@tonic-gate * them. 13460Sstevel@tonic-gate */ 13470Sstevel@tonic-gate if (type & UM_FREE) 13480Sstevel@tonic-gate return (WALK_NEXT); 13490Sstevel@tonic-gate 13500Sstevel@tonic-gate if (type & UM_BUFCTL) { 13510Sstevel@tonic-gate mdb_warn("impossible situation: small-slab UM_BUFCTL walk for " 13520Sstevel@tonic-gate "cache %p\n", addr); 13530Sstevel@tonic-gate return (WALK_ERR); 13540Sstevel@tonic-gate } 13550Sstevel@tonic-gate 13560Sstevel@tonic-gate /* 13570Sstevel@tonic-gate * Report allocated buffers, skipping buffers in the magazine layer. 13580Sstevel@tonic-gate * We only get this far for small-slab caches. 13590Sstevel@tonic-gate */ 13600Sstevel@tonic-gate for (i = 0; ret == WALK_NEXT && i < chunks; i++) { 13610Sstevel@tonic-gate buf = (char *)kbase + i * chunksize; 13620Sstevel@tonic-gate 13630Sstevel@tonic-gate if (!valid[i]) 13640Sstevel@tonic-gate continue; /* on slab freelist */ 13650Sstevel@tonic-gate 13660Sstevel@tonic-gate if (magcnt > 0 && 13670Sstevel@tonic-gate bsearch(&buf, maglist, magcnt, sizeof (void *), 13680Sstevel@tonic-gate addrcmp) != NULL) 13690Sstevel@tonic-gate continue; /* in magazine layer */ 13700Sstevel@tonic-gate 13710Sstevel@tonic-gate ret = umem_walk_callback(wsp, (uintptr_t)buf); 13720Sstevel@tonic-gate } 13730Sstevel@tonic-gate return (ret); 13740Sstevel@tonic-gate } 13750Sstevel@tonic-gate 13760Sstevel@tonic-gate void 13770Sstevel@tonic-gate umem_walk_fini(mdb_walk_state_t *wsp) 13780Sstevel@tonic-gate { 13790Sstevel@tonic-gate umem_walk_t *umw = wsp->walk_data; 13800Sstevel@tonic-gate uintptr_t chunksize; 13810Sstevel@tonic-gate uintptr_t slabsize; 13820Sstevel@tonic-gate 13830Sstevel@tonic-gate if (umw == NULL) 13840Sstevel@tonic-gate return; 13850Sstevel@tonic-gate 13860Sstevel@tonic-gate if (umw->umw_maglist != NULL) 13870Sstevel@tonic-gate mdb_free(umw->umw_maglist, umw->umw_max * sizeof (void *)); 13880Sstevel@tonic-gate 13890Sstevel@tonic-gate chunksize = umw->umw_cp->cache_chunksize; 13900Sstevel@tonic-gate slabsize = umw->umw_cp->cache_slabsize; 13910Sstevel@tonic-gate 13920Sstevel@tonic-gate if (umw->umw_valid != NULL) 13930Sstevel@tonic-gate mdb_free(umw->umw_valid, slabsize / chunksize); 13940Sstevel@tonic-gate if (umw->umw_ubase != NULL) 13950Sstevel@tonic-gate mdb_free(umw->umw_ubase, slabsize + sizeof (umem_bufctl_t)); 13960Sstevel@tonic-gate 13970Sstevel@tonic-gate mdb_free(umw->umw_cp, umw->umw_csize); 13980Sstevel@tonic-gate mdb_free(umw, sizeof (umem_walk_t)); 13990Sstevel@tonic-gate } 14000Sstevel@tonic-gate 14010Sstevel@tonic-gate /*ARGSUSED*/ 14020Sstevel@tonic-gate static int 14030Sstevel@tonic-gate umem_walk_all(uintptr_t addr, const umem_cache_t *c, mdb_walk_state_t *wsp) 14040Sstevel@tonic-gate { 14050Sstevel@tonic-gate /* 14060Sstevel@tonic-gate * Buffers allocated from NOTOUCH caches can also show up as freed 14070Sstevel@tonic-gate * memory in other caches. This can be a little confusing, so we 14080Sstevel@tonic-gate * don't walk NOTOUCH caches when walking all caches (thereby assuring 14090Sstevel@tonic-gate * that "::walk umem" and "::walk freemem" yield disjoint output). 14100Sstevel@tonic-gate */ 14110Sstevel@tonic-gate if (c->cache_cflags & UMC_NOTOUCH) 14120Sstevel@tonic-gate return (WALK_NEXT); 14130Sstevel@tonic-gate 14140Sstevel@tonic-gate if (mdb_pwalk(wsp->walk_data, wsp->walk_callback, 14150Sstevel@tonic-gate wsp->walk_cbdata, addr) == -1) 14160Sstevel@tonic-gate return (WALK_DONE); 14170Sstevel@tonic-gate 14180Sstevel@tonic-gate return (WALK_NEXT); 14190Sstevel@tonic-gate } 14200Sstevel@tonic-gate 14210Sstevel@tonic-gate #define UMEM_WALK_ALL(name, wsp) { \ 14220Sstevel@tonic-gate wsp->walk_data = (name); \ 14230Sstevel@tonic-gate if (mdb_walk("umem_cache", (mdb_walk_cb_t)umem_walk_all, wsp) == -1) \ 14240Sstevel@tonic-gate return (WALK_ERR); \ 14250Sstevel@tonic-gate return (WALK_DONE); \ 14260Sstevel@tonic-gate } 14270Sstevel@tonic-gate 14280Sstevel@tonic-gate int 14290Sstevel@tonic-gate umem_walk_init(mdb_walk_state_t *wsp) 14300Sstevel@tonic-gate { 14310Sstevel@tonic-gate if (wsp->walk_arg != NULL) 14320Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)wsp->walk_arg; 14330Sstevel@tonic-gate 14340Sstevel@tonic-gate if (wsp->walk_addr == NULL) 14350Sstevel@tonic-gate UMEM_WALK_ALL("umem", wsp); 14360Sstevel@tonic-gate return (umem_walk_init_common(wsp, UM_ALLOCATED)); 14370Sstevel@tonic-gate } 14380Sstevel@tonic-gate 14390Sstevel@tonic-gate int 14400Sstevel@tonic-gate bufctl_walk_init(mdb_walk_state_t *wsp) 14410Sstevel@tonic-gate { 14420Sstevel@tonic-gate if (wsp->walk_addr == NULL) 14430Sstevel@tonic-gate UMEM_WALK_ALL("bufctl", wsp); 14440Sstevel@tonic-gate return (umem_walk_init_common(wsp, UM_ALLOCATED | UM_BUFCTL)); 14450Sstevel@tonic-gate } 14460Sstevel@tonic-gate 14470Sstevel@tonic-gate int 14480Sstevel@tonic-gate freemem_walk_init(mdb_walk_state_t *wsp) 14490Sstevel@tonic-gate { 14500Sstevel@tonic-gate if (wsp->walk_addr == NULL) 14510Sstevel@tonic-gate UMEM_WALK_ALL("freemem", wsp); 14520Sstevel@tonic-gate return (umem_walk_init_common(wsp, UM_FREE)); 14530Sstevel@tonic-gate } 14540Sstevel@tonic-gate 14550Sstevel@tonic-gate int 14560Sstevel@tonic-gate freectl_walk_init(mdb_walk_state_t *wsp) 14570Sstevel@tonic-gate { 14580Sstevel@tonic-gate if (wsp->walk_addr == NULL) 14590Sstevel@tonic-gate UMEM_WALK_ALL("freectl", wsp); 14600Sstevel@tonic-gate return (umem_walk_init_common(wsp, UM_FREE | UM_BUFCTL)); 14610Sstevel@tonic-gate } 14620Sstevel@tonic-gate 14630Sstevel@tonic-gate typedef struct bufctl_history_walk { 14640Sstevel@tonic-gate void *bhw_next; 14650Sstevel@tonic-gate umem_cache_t *bhw_cache; 14660Sstevel@tonic-gate umem_slab_t *bhw_slab; 14670Sstevel@tonic-gate hrtime_t bhw_timestamp; 14680Sstevel@tonic-gate } bufctl_history_walk_t; 14690Sstevel@tonic-gate 14700Sstevel@tonic-gate int 14710Sstevel@tonic-gate bufctl_history_walk_init(mdb_walk_state_t *wsp) 14720Sstevel@tonic-gate { 14730Sstevel@tonic-gate bufctl_history_walk_t *bhw; 14740Sstevel@tonic-gate umem_bufctl_audit_t bc; 14750Sstevel@tonic-gate umem_bufctl_audit_t bcn; 14760Sstevel@tonic-gate 14770Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 14780Sstevel@tonic-gate mdb_warn("bufctl_history walk doesn't support global walks\n"); 14790Sstevel@tonic-gate return (WALK_ERR); 14800Sstevel@tonic-gate } 14810Sstevel@tonic-gate 14820Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), wsp->walk_addr) == -1) { 14830Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", wsp->walk_addr); 14840Sstevel@tonic-gate return (WALK_ERR); 14850Sstevel@tonic-gate } 14860Sstevel@tonic-gate 14870Sstevel@tonic-gate bhw = mdb_zalloc(sizeof (*bhw), UM_SLEEP); 14880Sstevel@tonic-gate bhw->bhw_timestamp = 0; 14890Sstevel@tonic-gate bhw->bhw_cache = bc.bc_cache; 14900Sstevel@tonic-gate bhw->bhw_slab = bc.bc_slab; 14910Sstevel@tonic-gate 14920Sstevel@tonic-gate /* 14930Sstevel@tonic-gate * sometimes the first log entry matches the base bufctl; in that 14940Sstevel@tonic-gate * case, skip the base bufctl. 14950Sstevel@tonic-gate */ 14960Sstevel@tonic-gate if (bc.bc_lastlog != NULL && 14970Sstevel@tonic-gate mdb_vread(&bcn, sizeof (bcn), (uintptr_t)bc.bc_lastlog) != -1 && 14980Sstevel@tonic-gate bc.bc_addr == bcn.bc_addr && 14990Sstevel@tonic-gate bc.bc_cache == bcn.bc_cache && 15000Sstevel@tonic-gate bc.bc_slab == bcn.bc_slab && 15010Sstevel@tonic-gate bc.bc_timestamp == bcn.bc_timestamp && 15020Sstevel@tonic-gate bc.bc_thread == bcn.bc_thread) 15030Sstevel@tonic-gate bhw->bhw_next = bc.bc_lastlog; 15040Sstevel@tonic-gate else 15050Sstevel@tonic-gate bhw->bhw_next = (void *)wsp->walk_addr; 15060Sstevel@tonic-gate 15070Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)bc.bc_addr; 15080Sstevel@tonic-gate wsp->walk_data = bhw; 15090Sstevel@tonic-gate 15100Sstevel@tonic-gate return (WALK_NEXT); 15110Sstevel@tonic-gate } 15120Sstevel@tonic-gate 15130Sstevel@tonic-gate int 15140Sstevel@tonic-gate bufctl_history_walk_step(mdb_walk_state_t *wsp) 15150Sstevel@tonic-gate { 15160Sstevel@tonic-gate bufctl_history_walk_t *bhw = wsp->walk_data; 15170Sstevel@tonic-gate uintptr_t addr = (uintptr_t)bhw->bhw_next; 15180Sstevel@tonic-gate uintptr_t baseaddr = wsp->walk_addr; 15190Sstevel@tonic-gate umem_bufctl_audit_t *b; 15200Sstevel@tonic-gate UMEM_LOCAL_BUFCTL_AUDIT(&b); 15210Sstevel@tonic-gate 15220Sstevel@tonic-gate if (addr == NULL) 15230Sstevel@tonic-gate return (WALK_DONE); 15240Sstevel@tonic-gate 15250Sstevel@tonic-gate if (mdb_vread(b, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) { 15260Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", bhw->bhw_next); 15270Sstevel@tonic-gate return (WALK_ERR); 15280Sstevel@tonic-gate } 15290Sstevel@tonic-gate 15300Sstevel@tonic-gate /* 15310Sstevel@tonic-gate * The bufctl is only valid if the address, cache, and slab are 15320Sstevel@tonic-gate * correct. We also check that the timestamp is decreasing, to 15330Sstevel@tonic-gate * prevent infinite loops. 15340Sstevel@tonic-gate */ 15350Sstevel@tonic-gate if ((uintptr_t)b->bc_addr != baseaddr || 15360Sstevel@tonic-gate b->bc_cache != bhw->bhw_cache || 15370Sstevel@tonic-gate b->bc_slab != bhw->bhw_slab || 15380Sstevel@tonic-gate (bhw->bhw_timestamp != 0 && b->bc_timestamp >= bhw->bhw_timestamp)) 15390Sstevel@tonic-gate return (WALK_DONE); 15400Sstevel@tonic-gate 15410Sstevel@tonic-gate bhw->bhw_next = b->bc_lastlog; 15420Sstevel@tonic-gate bhw->bhw_timestamp = b->bc_timestamp; 15430Sstevel@tonic-gate 15440Sstevel@tonic-gate return (wsp->walk_callback(addr, b, wsp->walk_cbdata)); 15450Sstevel@tonic-gate } 15460Sstevel@tonic-gate 15470Sstevel@tonic-gate void 15480Sstevel@tonic-gate bufctl_history_walk_fini(mdb_walk_state_t *wsp) 15490Sstevel@tonic-gate { 15500Sstevel@tonic-gate bufctl_history_walk_t *bhw = wsp->walk_data; 15510Sstevel@tonic-gate 15520Sstevel@tonic-gate mdb_free(bhw, sizeof (*bhw)); 15530Sstevel@tonic-gate } 15540Sstevel@tonic-gate 15550Sstevel@tonic-gate typedef struct umem_log_walk { 15560Sstevel@tonic-gate umem_bufctl_audit_t *ulw_base; 15570Sstevel@tonic-gate umem_bufctl_audit_t **ulw_sorted; 15580Sstevel@tonic-gate umem_log_header_t ulw_lh; 15590Sstevel@tonic-gate size_t ulw_size; 15600Sstevel@tonic-gate size_t ulw_maxndx; 15610Sstevel@tonic-gate size_t ulw_ndx; 15620Sstevel@tonic-gate } umem_log_walk_t; 15630Sstevel@tonic-gate 15640Sstevel@tonic-gate int 15650Sstevel@tonic-gate umem_log_walk_init(mdb_walk_state_t *wsp) 15660Sstevel@tonic-gate { 15670Sstevel@tonic-gate uintptr_t lp = wsp->walk_addr; 15680Sstevel@tonic-gate umem_log_walk_t *ulw; 15690Sstevel@tonic-gate umem_log_header_t *lhp; 15700Sstevel@tonic-gate int maxndx, i, j, k; 15710Sstevel@tonic-gate 15720Sstevel@tonic-gate /* 15730Sstevel@tonic-gate * By default (global walk), walk the umem_transaction_log. Otherwise 15740Sstevel@tonic-gate * read the log whose umem_log_header_t is stored at walk_addr. 15750Sstevel@tonic-gate */ 15760Sstevel@tonic-gate if (lp == NULL && umem_readvar(&lp, "umem_transaction_log") == -1) { 15770Sstevel@tonic-gate mdb_warn("failed to read 'umem_transaction_log'"); 15780Sstevel@tonic-gate return (WALK_ERR); 15790Sstevel@tonic-gate } 15800Sstevel@tonic-gate 15810Sstevel@tonic-gate if (lp == NULL) { 15820Sstevel@tonic-gate mdb_warn("log is disabled\n"); 15830Sstevel@tonic-gate return (WALK_ERR); 15840Sstevel@tonic-gate } 15850Sstevel@tonic-gate 15860Sstevel@tonic-gate ulw = mdb_zalloc(sizeof (umem_log_walk_t), UM_SLEEP); 15870Sstevel@tonic-gate lhp = &ulw->ulw_lh; 15880Sstevel@tonic-gate 15890Sstevel@tonic-gate if (mdb_vread(lhp, sizeof (umem_log_header_t), lp) == -1) { 15900Sstevel@tonic-gate mdb_warn("failed to read log header at %p", lp); 15910Sstevel@tonic-gate mdb_free(ulw, sizeof (umem_log_walk_t)); 15920Sstevel@tonic-gate return (WALK_ERR); 15930Sstevel@tonic-gate } 15940Sstevel@tonic-gate 15950Sstevel@tonic-gate ulw->ulw_size = lhp->lh_chunksize * lhp->lh_nchunks; 15960Sstevel@tonic-gate ulw->ulw_base = mdb_alloc(ulw->ulw_size, UM_SLEEP); 15970Sstevel@tonic-gate maxndx = lhp->lh_chunksize / UMEM_BUFCTL_AUDIT_SIZE - 1; 15980Sstevel@tonic-gate 15990Sstevel@tonic-gate if (mdb_vread(ulw->ulw_base, ulw->ulw_size, 16000Sstevel@tonic-gate (uintptr_t)lhp->lh_base) == -1) { 16010Sstevel@tonic-gate mdb_warn("failed to read log at base %p", lhp->lh_base); 16020Sstevel@tonic-gate mdb_free(ulw->ulw_base, ulw->ulw_size); 16030Sstevel@tonic-gate mdb_free(ulw, sizeof (umem_log_walk_t)); 16040Sstevel@tonic-gate return (WALK_ERR); 16050Sstevel@tonic-gate } 16060Sstevel@tonic-gate 16070Sstevel@tonic-gate ulw->ulw_sorted = mdb_alloc(maxndx * lhp->lh_nchunks * 16080Sstevel@tonic-gate sizeof (umem_bufctl_audit_t *), UM_SLEEP); 16090Sstevel@tonic-gate 16100Sstevel@tonic-gate for (i = 0, k = 0; i < lhp->lh_nchunks; i++) { 16110Sstevel@tonic-gate caddr_t chunk = (caddr_t) 16120Sstevel@tonic-gate ((uintptr_t)ulw->ulw_base + i * lhp->lh_chunksize); 16130Sstevel@tonic-gate 16140Sstevel@tonic-gate for (j = 0; j < maxndx; j++) { 16150Sstevel@tonic-gate /* LINTED align */ 16160Sstevel@tonic-gate ulw->ulw_sorted[k++] = (umem_bufctl_audit_t *)chunk; 16170Sstevel@tonic-gate chunk += UMEM_BUFCTL_AUDIT_SIZE; 16180Sstevel@tonic-gate } 16190Sstevel@tonic-gate } 16200Sstevel@tonic-gate 16210Sstevel@tonic-gate qsort(ulw->ulw_sorted, k, sizeof (umem_bufctl_audit_t *), 16220Sstevel@tonic-gate (int(*)(const void *, const void *))bufctlcmp); 16230Sstevel@tonic-gate 16240Sstevel@tonic-gate ulw->ulw_maxndx = k; 16250Sstevel@tonic-gate wsp->walk_data = ulw; 16260Sstevel@tonic-gate 16270Sstevel@tonic-gate return (WALK_NEXT); 16280Sstevel@tonic-gate } 16290Sstevel@tonic-gate 16300Sstevel@tonic-gate int 16310Sstevel@tonic-gate umem_log_walk_step(mdb_walk_state_t *wsp) 16320Sstevel@tonic-gate { 16330Sstevel@tonic-gate umem_log_walk_t *ulw = wsp->walk_data; 16340Sstevel@tonic-gate umem_bufctl_audit_t *bcp; 16350Sstevel@tonic-gate 16360Sstevel@tonic-gate if (ulw->ulw_ndx == ulw->ulw_maxndx) 16370Sstevel@tonic-gate return (WALK_DONE); 16380Sstevel@tonic-gate 16390Sstevel@tonic-gate bcp = ulw->ulw_sorted[ulw->ulw_ndx++]; 16400Sstevel@tonic-gate 16410Sstevel@tonic-gate return (wsp->walk_callback((uintptr_t)bcp - (uintptr_t)ulw->ulw_base + 16420Sstevel@tonic-gate (uintptr_t)ulw->ulw_lh.lh_base, bcp, wsp->walk_cbdata)); 16430Sstevel@tonic-gate } 16440Sstevel@tonic-gate 16450Sstevel@tonic-gate void 16460Sstevel@tonic-gate umem_log_walk_fini(mdb_walk_state_t *wsp) 16470Sstevel@tonic-gate { 16480Sstevel@tonic-gate umem_log_walk_t *ulw = wsp->walk_data; 16490Sstevel@tonic-gate 16500Sstevel@tonic-gate mdb_free(ulw->ulw_base, ulw->ulw_size); 16510Sstevel@tonic-gate mdb_free(ulw->ulw_sorted, ulw->ulw_maxndx * 16520Sstevel@tonic-gate sizeof (umem_bufctl_audit_t *)); 16530Sstevel@tonic-gate mdb_free(ulw, sizeof (umem_log_walk_t)); 16540Sstevel@tonic-gate } 16550Sstevel@tonic-gate 16560Sstevel@tonic-gate typedef struct allocdby_bufctl { 16570Sstevel@tonic-gate uintptr_t abb_addr; 16580Sstevel@tonic-gate hrtime_t abb_ts; 16590Sstevel@tonic-gate } allocdby_bufctl_t; 16600Sstevel@tonic-gate 16610Sstevel@tonic-gate typedef struct allocdby_walk { 16620Sstevel@tonic-gate const char *abw_walk; 16630Sstevel@tonic-gate uintptr_t abw_thread; 16640Sstevel@tonic-gate size_t abw_nbufs; 16650Sstevel@tonic-gate size_t abw_size; 16660Sstevel@tonic-gate allocdby_bufctl_t *abw_buf; 16670Sstevel@tonic-gate size_t abw_ndx; 16680Sstevel@tonic-gate } allocdby_walk_t; 16690Sstevel@tonic-gate 16700Sstevel@tonic-gate int 16710Sstevel@tonic-gate allocdby_walk_bufctl(uintptr_t addr, const umem_bufctl_audit_t *bcp, 16720Sstevel@tonic-gate allocdby_walk_t *abw) 16730Sstevel@tonic-gate { 16740Sstevel@tonic-gate if ((uintptr_t)bcp->bc_thread != abw->abw_thread) 16750Sstevel@tonic-gate return (WALK_NEXT); 16760Sstevel@tonic-gate 16770Sstevel@tonic-gate if (abw->abw_nbufs == abw->abw_size) { 16780Sstevel@tonic-gate allocdby_bufctl_t *buf; 16790Sstevel@tonic-gate size_t oldsize = sizeof (allocdby_bufctl_t) * abw->abw_size; 16800Sstevel@tonic-gate 16810Sstevel@tonic-gate buf = mdb_zalloc(oldsize << 1, UM_SLEEP); 16820Sstevel@tonic-gate 16830Sstevel@tonic-gate bcopy(abw->abw_buf, buf, oldsize); 16840Sstevel@tonic-gate mdb_free(abw->abw_buf, oldsize); 16850Sstevel@tonic-gate 16860Sstevel@tonic-gate abw->abw_size <<= 1; 16870Sstevel@tonic-gate abw->abw_buf = buf; 16880Sstevel@tonic-gate } 16890Sstevel@tonic-gate 16900Sstevel@tonic-gate abw->abw_buf[abw->abw_nbufs].abb_addr = addr; 16910Sstevel@tonic-gate abw->abw_buf[abw->abw_nbufs].abb_ts = bcp->bc_timestamp; 16920Sstevel@tonic-gate abw->abw_nbufs++; 16930Sstevel@tonic-gate 16940Sstevel@tonic-gate return (WALK_NEXT); 16950Sstevel@tonic-gate } 16960Sstevel@tonic-gate 16970Sstevel@tonic-gate /*ARGSUSED*/ 16980Sstevel@tonic-gate int 16990Sstevel@tonic-gate allocdby_walk_cache(uintptr_t addr, const umem_cache_t *c, allocdby_walk_t *abw) 17000Sstevel@tonic-gate { 17010Sstevel@tonic-gate if (mdb_pwalk(abw->abw_walk, (mdb_walk_cb_t)allocdby_walk_bufctl, 17020Sstevel@tonic-gate abw, addr) == -1) { 17030Sstevel@tonic-gate mdb_warn("couldn't walk bufctl for cache %p", addr); 17040Sstevel@tonic-gate return (WALK_DONE); 17050Sstevel@tonic-gate } 17060Sstevel@tonic-gate 17070Sstevel@tonic-gate return (WALK_NEXT); 17080Sstevel@tonic-gate } 17090Sstevel@tonic-gate 17100Sstevel@tonic-gate static int 17110Sstevel@tonic-gate allocdby_cmp(const allocdby_bufctl_t *lhs, const allocdby_bufctl_t *rhs) 17120Sstevel@tonic-gate { 17130Sstevel@tonic-gate if (lhs->abb_ts < rhs->abb_ts) 17140Sstevel@tonic-gate return (1); 17150Sstevel@tonic-gate if (lhs->abb_ts > rhs->abb_ts) 17160Sstevel@tonic-gate return (-1); 17170Sstevel@tonic-gate return (0); 17180Sstevel@tonic-gate } 17190Sstevel@tonic-gate 17200Sstevel@tonic-gate static int 17210Sstevel@tonic-gate allocdby_walk_init_common(mdb_walk_state_t *wsp, const char *walk) 17220Sstevel@tonic-gate { 17230Sstevel@tonic-gate allocdby_walk_t *abw; 17240Sstevel@tonic-gate 17250Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 17260Sstevel@tonic-gate mdb_warn("allocdby walk doesn't support global walks\n"); 17270Sstevel@tonic-gate return (WALK_ERR); 17280Sstevel@tonic-gate } 17290Sstevel@tonic-gate 17300Sstevel@tonic-gate abw = mdb_zalloc(sizeof (allocdby_walk_t), UM_SLEEP); 17310Sstevel@tonic-gate 17320Sstevel@tonic-gate abw->abw_thread = wsp->walk_addr; 17330Sstevel@tonic-gate abw->abw_walk = walk; 17340Sstevel@tonic-gate abw->abw_size = 128; /* something reasonable */ 17350Sstevel@tonic-gate abw->abw_buf = 17360Sstevel@tonic-gate mdb_zalloc(abw->abw_size * sizeof (allocdby_bufctl_t), UM_SLEEP); 17370Sstevel@tonic-gate 17380Sstevel@tonic-gate wsp->walk_data = abw; 17390Sstevel@tonic-gate 17400Sstevel@tonic-gate if (mdb_walk("umem_cache", 17410Sstevel@tonic-gate (mdb_walk_cb_t)allocdby_walk_cache, abw) == -1) { 17420Sstevel@tonic-gate mdb_warn("couldn't walk umem_cache"); 17430Sstevel@tonic-gate allocdby_walk_fini(wsp); 17440Sstevel@tonic-gate return (WALK_ERR); 17450Sstevel@tonic-gate } 17460Sstevel@tonic-gate 17470Sstevel@tonic-gate qsort(abw->abw_buf, abw->abw_nbufs, sizeof (allocdby_bufctl_t), 17480Sstevel@tonic-gate (int(*)(const void *, const void *))allocdby_cmp); 17490Sstevel@tonic-gate 17500Sstevel@tonic-gate return (WALK_NEXT); 17510Sstevel@tonic-gate } 17520Sstevel@tonic-gate 17530Sstevel@tonic-gate int 17540Sstevel@tonic-gate allocdby_walk_init(mdb_walk_state_t *wsp) 17550Sstevel@tonic-gate { 17560Sstevel@tonic-gate return (allocdby_walk_init_common(wsp, "bufctl")); 17570Sstevel@tonic-gate } 17580Sstevel@tonic-gate 17590Sstevel@tonic-gate int 17600Sstevel@tonic-gate freedby_walk_init(mdb_walk_state_t *wsp) 17610Sstevel@tonic-gate { 17620Sstevel@tonic-gate return (allocdby_walk_init_common(wsp, "freectl")); 17630Sstevel@tonic-gate } 17640Sstevel@tonic-gate 17650Sstevel@tonic-gate int 17660Sstevel@tonic-gate allocdby_walk_step(mdb_walk_state_t *wsp) 17670Sstevel@tonic-gate { 17680Sstevel@tonic-gate allocdby_walk_t *abw = wsp->walk_data; 17690Sstevel@tonic-gate uintptr_t addr; 17700Sstevel@tonic-gate umem_bufctl_audit_t *bcp; 17710Sstevel@tonic-gate UMEM_LOCAL_BUFCTL_AUDIT(&bcp); 17720Sstevel@tonic-gate 17730Sstevel@tonic-gate if (abw->abw_ndx == abw->abw_nbufs) 17740Sstevel@tonic-gate return (WALK_DONE); 17750Sstevel@tonic-gate 17760Sstevel@tonic-gate addr = abw->abw_buf[abw->abw_ndx++].abb_addr; 17770Sstevel@tonic-gate 17780Sstevel@tonic-gate if (mdb_vread(bcp, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) { 17790Sstevel@tonic-gate mdb_warn("couldn't read bufctl at %p", addr); 17800Sstevel@tonic-gate return (WALK_DONE); 17810Sstevel@tonic-gate } 17820Sstevel@tonic-gate 17830Sstevel@tonic-gate return (wsp->walk_callback(addr, bcp, wsp->walk_cbdata)); 17840Sstevel@tonic-gate } 17850Sstevel@tonic-gate 17860Sstevel@tonic-gate void 17870Sstevel@tonic-gate allocdby_walk_fini(mdb_walk_state_t *wsp) 17880Sstevel@tonic-gate { 17890Sstevel@tonic-gate allocdby_walk_t *abw = wsp->walk_data; 17900Sstevel@tonic-gate 17910Sstevel@tonic-gate mdb_free(abw->abw_buf, sizeof (allocdby_bufctl_t) * abw->abw_size); 17920Sstevel@tonic-gate mdb_free(abw, sizeof (allocdby_walk_t)); 17930Sstevel@tonic-gate } 17940Sstevel@tonic-gate 17950Sstevel@tonic-gate /*ARGSUSED*/ 17960Sstevel@tonic-gate int 17970Sstevel@tonic-gate allocdby_walk(uintptr_t addr, const umem_bufctl_audit_t *bcp, void *ignored) 17980Sstevel@tonic-gate { 17990Sstevel@tonic-gate char c[MDB_SYM_NAMLEN]; 18000Sstevel@tonic-gate GElf_Sym sym; 18010Sstevel@tonic-gate int i; 18020Sstevel@tonic-gate 18030Sstevel@tonic-gate mdb_printf("%0?p %12llx ", addr, bcp->bc_timestamp); 18040Sstevel@tonic-gate for (i = 0; i < bcp->bc_depth; i++) { 18050Sstevel@tonic-gate if (mdb_lookup_by_addr(bcp->bc_stack[i], 18060Sstevel@tonic-gate MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1) 18070Sstevel@tonic-gate continue; 18080Sstevel@tonic-gate if (is_umem_sym(c, "umem_")) 18090Sstevel@tonic-gate continue; 18100Sstevel@tonic-gate mdb_printf("%s+0x%lx", 18110Sstevel@tonic-gate c, bcp->bc_stack[i] - (uintptr_t)sym.st_value); 18120Sstevel@tonic-gate break; 18130Sstevel@tonic-gate } 18140Sstevel@tonic-gate mdb_printf("\n"); 18150Sstevel@tonic-gate 18160Sstevel@tonic-gate return (WALK_NEXT); 18170Sstevel@tonic-gate } 18180Sstevel@tonic-gate 18190Sstevel@tonic-gate static int 18200Sstevel@tonic-gate allocdby_common(uintptr_t addr, uint_t flags, const char *w) 18210Sstevel@tonic-gate { 18220Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 18230Sstevel@tonic-gate return (DCMD_USAGE); 18240Sstevel@tonic-gate 18250Sstevel@tonic-gate mdb_printf("%-?s %12s %s\n", "BUFCTL", "TIMESTAMP", "CALLER"); 18260Sstevel@tonic-gate 18270Sstevel@tonic-gate if (mdb_pwalk(w, (mdb_walk_cb_t)allocdby_walk, NULL, addr) == -1) { 18280Sstevel@tonic-gate mdb_warn("can't walk '%s' for %p", w, addr); 18290Sstevel@tonic-gate return (DCMD_ERR); 18300Sstevel@tonic-gate } 18310Sstevel@tonic-gate 18320Sstevel@tonic-gate return (DCMD_OK); 18330Sstevel@tonic-gate } 18340Sstevel@tonic-gate 18350Sstevel@tonic-gate /*ARGSUSED*/ 18360Sstevel@tonic-gate int 18370Sstevel@tonic-gate allocdby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 18380Sstevel@tonic-gate { 18390Sstevel@tonic-gate return (allocdby_common(addr, flags, "allocdby")); 18400Sstevel@tonic-gate } 18410Sstevel@tonic-gate 18420Sstevel@tonic-gate /*ARGSUSED*/ 18430Sstevel@tonic-gate int 18440Sstevel@tonic-gate freedby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 18450Sstevel@tonic-gate { 18460Sstevel@tonic-gate return (allocdby_common(addr, flags, "freedby")); 18470Sstevel@tonic-gate } 18480Sstevel@tonic-gate 18490Sstevel@tonic-gate typedef struct whatis { 18500Sstevel@tonic-gate uintptr_t w_addr; 18510Sstevel@tonic-gate const umem_cache_t *w_cache; 18520Sstevel@tonic-gate const vmem_t *w_vmem; 18530Sstevel@tonic-gate int w_found; 18540Sstevel@tonic-gate uint_t w_verbose; 18550Sstevel@tonic-gate uint_t w_freemem; 18560Sstevel@tonic-gate uint_t w_all; 18570Sstevel@tonic-gate uint_t w_bufctl; 18580Sstevel@tonic-gate } whatis_t; 18590Sstevel@tonic-gate 18600Sstevel@tonic-gate static void 18610Sstevel@tonic-gate whatis_print_umem(uintptr_t addr, uintptr_t baddr, whatis_t *w) 18620Sstevel@tonic-gate { 18630Sstevel@tonic-gate /* LINTED pointer cast may result in improper alignment */ 18640Sstevel@tonic-gate uintptr_t btaddr = (uintptr_t)UMEM_BUFTAG(w->w_cache, addr); 18650Sstevel@tonic-gate intptr_t stat; 18660Sstevel@tonic-gate 18670Sstevel@tonic-gate if (w->w_cache->cache_flags & UMF_REDZONE) { 18680Sstevel@tonic-gate umem_buftag_t bt; 18690Sstevel@tonic-gate 18700Sstevel@tonic-gate if (mdb_vread(&bt, sizeof (bt), btaddr) == -1) 18710Sstevel@tonic-gate goto done; 18720Sstevel@tonic-gate 18730Sstevel@tonic-gate stat = (intptr_t)bt.bt_bufctl ^ bt.bt_bxstat; 18740Sstevel@tonic-gate 18750Sstevel@tonic-gate if (stat != UMEM_BUFTAG_ALLOC && stat != UMEM_BUFTAG_FREE) 18760Sstevel@tonic-gate goto done; 18770Sstevel@tonic-gate 18780Sstevel@tonic-gate /* 18790Sstevel@tonic-gate * provide the bufctl ptr if it has useful information 18800Sstevel@tonic-gate */ 18810Sstevel@tonic-gate if (baddr == 0 && (w->w_cache->cache_flags & UMF_AUDIT)) 18820Sstevel@tonic-gate baddr = (uintptr_t)bt.bt_bufctl; 18830Sstevel@tonic-gate } 18840Sstevel@tonic-gate 18850Sstevel@tonic-gate done: 18860Sstevel@tonic-gate if (baddr == 0) 18870Sstevel@tonic-gate mdb_printf("%p is %p+%p, %s from %s\n", 18880Sstevel@tonic-gate w->w_addr, addr, w->w_addr - addr, 18890Sstevel@tonic-gate w->w_freemem == FALSE ? "allocated" : "freed", 18900Sstevel@tonic-gate w->w_cache->cache_name); 18910Sstevel@tonic-gate else 18920Sstevel@tonic-gate mdb_printf("%p is %p+%p, bufctl %p %s from %s\n", 18930Sstevel@tonic-gate w->w_addr, addr, w->w_addr - addr, baddr, 18940Sstevel@tonic-gate w->w_freemem == FALSE ? "allocated" : "freed", 18950Sstevel@tonic-gate w->w_cache->cache_name); 18960Sstevel@tonic-gate } 18970Sstevel@tonic-gate 18980Sstevel@tonic-gate /*ARGSUSED*/ 18990Sstevel@tonic-gate static int 19000Sstevel@tonic-gate whatis_walk_umem(uintptr_t addr, void *ignored, whatis_t *w) 19010Sstevel@tonic-gate { 19020Sstevel@tonic-gate if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize) 19030Sstevel@tonic-gate return (WALK_NEXT); 19040Sstevel@tonic-gate 19050Sstevel@tonic-gate whatis_print_umem(addr, 0, w); 19060Sstevel@tonic-gate w->w_found++; 19070Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 19080Sstevel@tonic-gate } 19090Sstevel@tonic-gate 19100Sstevel@tonic-gate static int 19110Sstevel@tonic-gate whatis_walk_seg(uintptr_t addr, const vmem_seg_t *vs, whatis_t *w) 19120Sstevel@tonic-gate { 19130Sstevel@tonic-gate if (w->w_addr < vs->vs_start || w->w_addr >= vs->vs_end) 19140Sstevel@tonic-gate return (WALK_NEXT); 19150Sstevel@tonic-gate 19160Sstevel@tonic-gate mdb_printf("%p is %p+%p ", w->w_addr, 19170Sstevel@tonic-gate vs->vs_start, w->w_addr - vs->vs_start); 19180Sstevel@tonic-gate 19190Sstevel@tonic-gate /* 19200Sstevel@tonic-gate * Always provide the vmem_seg pointer if it has a stack trace. 19210Sstevel@tonic-gate */ 19220Sstevel@tonic-gate if (w->w_bufctl == TRUE || 19230Sstevel@tonic-gate (vs->vs_type == VMEM_ALLOC && vs->vs_depth != 0)) { 19240Sstevel@tonic-gate mdb_printf("(vmem_seg %p) ", addr); 19250Sstevel@tonic-gate } 19260Sstevel@tonic-gate 19270Sstevel@tonic-gate mdb_printf("%sfrom %s vmem arena\n", w->w_freemem == TRUE ? 19280Sstevel@tonic-gate "freed " : "", w->w_vmem->vm_name); 19290Sstevel@tonic-gate 19300Sstevel@tonic-gate w->w_found++; 19310Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 19320Sstevel@tonic-gate } 19330Sstevel@tonic-gate 19340Sstevel@tonic-gate static int 19350Sstevel@tonic-gate whatis_walk_vmem(uintptr_t addr, const vmem_t *vmem, whatis_t *w) 19360Sstevel@tonic-gate { 19370Sstevel@tonic-gate const char *nm = vmem->vm_name; 19380Sstevel@tonic-gate w->w_vmem = vmem; 19390Sstevel@tonic-gate w->w_freemem = FALSE; 19400Sstevel@tonic-gate 19410Sstevel@tonic-gate if (w->w_verbose) 19420Sstevel@tonic-gate mdb_printf("Searching vmem arena %s...\n", nm); 19430Sstevel@tonic-gate 19440Sstevel@tonic-gate if (mdb_pwalk("vmem_alloc", 19450Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) { 19460Sstevel@tonic-gate mdb_warn("can't walk vmem seg for %p", addr); 19470Sstevel@tonic-gate return (WALK_NEXT); 19480Sstevel@tonic-gate } 19490Sstevel@tonic-gate 19500Sstevel@tonic-gate if (w->w_found && w->w_all == FALSE) 19510Sstevel@tonic-gate return (WALK_DONE); 19520Sstevel@tonic-gate 19530Sstevel@tonic-gate if (w->w_verbose) 19540Sstevel@tonic-gate mdb_printf("Searching vmem arena %s for free virtual...\n", nm); 19550Sstevel@tonic-gate 19560Sstevel@tonic-gate w->w_freemem = TRUE; 19570Sstevel@tonic-gate 19580Sstevel@tonic-gate if (mdb_pwalk("vmem_free", 19590Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) { 19600Sstevel@tonic-gate mdb_warn("can't walk vmem seg for %p", addr); 19610Sstevel@tonic-gate return (WALK_NEXT); 19620Sstevel@tonic-gate } 19630Sstevel@tonic-gate 19640Sstevel@tonic-gate return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT); 19650Sstevel@tonic-gate } 19660Sstevel@tonic-gate 19670Sstevel@tonic-gate /*ARGSUSED*/ 19680Sstevel@tonic-gate static int 19690Sstevel@tonic-gate whatis_walk_bufctl(uintptr_t baddr, const umem_bufctl_t *bcp, whatis_t *w) 19700Sstevel@tonic-gate { 19710Sstevel@tonic-gate uintptr_t addr; 19720Sstevel@tonic-gate 19730Sstevel@tonic-gate if (bcp == NULL) 19740Sstevel@tonic-gate return (WALK_NEXT); 19750Sstevel@tonic-gate 19760Sstevel@tonic-gate addr = (uintptr_t)bcp->bc_addr; 19770Sstevel@tonic-gate 19780Sstevel@tonic-gate if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize) 19790Sstevel@tonic-gate return (WALK_NEXT); 19800Sstevel@tonic-gate 19810Sstevel@tonic-gate whatis_print_umem(addr, baddr, w); 19820Sstevel@tonic-gate w->w_found++; 19830Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 19840Sstevel@tonic-gate } 19850Sstevel@tonic-gate 19860Sstevel@tonic-gate static int 19870Sstevel@tonic-gate whatis_walk_cache(uintptr_t addr, const umem_cache_t *c, whatis_t *w) 19880Sstevel@tonic-gate { 19890Sstevel@tonic-gate char *walk, *freewalk; 19900Sstevel@tonic-gate mdb_walk_cb_t func; 19910Sstevel@tonic-gate 19920Sstevel@tonic-gate if (w->w_bufctl == FALSE) { 19930Sstevel@tonic-gate walk = "umem"; 19940Sstevel@tonic-gate freewalk = "freemem"; 19950Sstevel@tonic-gate func = (mdb_walk_cb_t)whatis_walk_umem; 19960Sstevel@tonic-gate } else { 19970Sstevel@tonic-gate walk = "bufctl"; 19980Sstevel@tonic-gate freewalk = "freectl"; 19990Sstevel@tonic-gate func = (mdb_walk_cb_t)whatis_walk_bufctl; 20000Sstevel@tonic-gate } 20010Sstevel@tonic-gate 20020Sstevel@tonic-gate if (w->w_verbose) 20030Sstevel@tonic-gate mdb_printf("Searching %s...\n", c->cache_name); 20040Sstevel@tonic-gate 20050Sstevel@tonic-gate w->w_cache = c; 20060Sstevel@tonic-gate w->w_freemem = FALSE; 20070Sstevel@tonic-gate 20080Sstevel@tonic-gate if (mdb_pwalk(walk, func, w, addr) == -1) { 20090Sstevel@tonic-gate mdb_warn("can't find %s walker", walk); 20100Sstevel@tonic-gate return (WALK_DONE); 20110Sstevel@tonic-gate } 20120Sstevel@tonic-gate 20130Sstevel@tonic-gate if (w->w_found && w->w_all == FALSE) 20140Sstevel@tonic-gate return (WALK_DONE); 20150Sstevel@tonic-gate 20160Sstevel@tonic-gate /* 20170Sstevel@tonic-gate * We have searched for allocated memory; now search for freed memory. 20180Sstevel@tonic-gate */ 20190Sstevel@tonic-gate if (w->w_verbose) 20200Sstevel@tonic-gate mdb_printf("Searching %s for free memory...\n", c->cache_name); 20210Sstevel@tonic-gate 20220Sstevel@tonic-gate w->w_freemem = TRUE; 20230Sstevel@tonic-gate 20240Sstevel@tonic-gate if (mdb_pwalk(freewalk, func, w, addr) == -1) { 20250Sstevel@tonic-gate mdb_warn("can't find %s walker", freewalk); 20260Sstevel@tonic-gate return (WALK_DONE); 20270Sstevel@tonic-gate } 20280Sstevel@tonic-gate 20290Sstevel@tonic-gate return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT); 20300Sstevel@tonic-gate } 20310Sstevel@tonic-gate 20320Sstevel@tonic-gate static int 20330Sstevel@tonic-gate whatis_walk_touch(uintptr_t addr, const umem_cache_t *c, whatis_t *w) 20340Sstevel@tonic-gate { 20350Sstevel@tonic-gate if (c->cache_cflags & UMC_NOTOUCH) 20360Sstevel@tonic-gate return (WALK_NEXT); 20370Sstevel@tonic-gate 20380Sstevel@tonic-gate return (whatis_walk_cache(addr, c, w)); 20390Sstevel@tonic-gate } 20400Sstevel@tonic-gate 20410Sstevel@tonic-gate static int 20420Sstevel@tonic-gate whatis_walk_notouch(uintptr_t addr, const umem_cache_t *c, whatis_t *w) 20430Sstevel@tonic-gate { 20440Sstevel@tonic-gate if (!(c->cache_cflags & UMC_NOTOUCH)) 20450Sstevel@tonic-gate return (WALK_NEXT); 20460Sstevel@tonic-gate 20470Sstevel@tonic-gate return (whatis_walk_cache(addr, c, w)); 20480Sstevel@tonic-gate } 20490Sstevel@tonic-gate 20500Sstevel@tonic-gate int 20510Sstevel@tonic-gate whatis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 20520Sstevel@tonic-gate { 20530Sstevel@tonic-gate whatis_t w; 20540Sstevel@tonic-gate 20550Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 20560Sstevel@tonic-gate return (DCMD_USAGE); 20570Sstevel@tonic-gate 20580Sstevel@tonic-gate w.w_verbose = FALSE; 20590Sstevel@tonic-gate w.w_bufctl = FALSE; 20600Sstevel@tonic-gate w.w_all = FALSE; 20610Sstevel@tonic-gate 20620Sstevel@tonic-gate if (mdb_getopts(argc, argv, 20630Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &w.w_verbose, 20640Sstevel@tonic-gate 'a', MDB_OPT_SETBITS, TRUE, &w.w_all, 20650Sstevel@tonic-gate 'b', MDB_OPT_SETBITS, TRUE, &w.w_bufctl, NULL) != argc) 20660Sstevel@tonic-gate return (DCMD_USAGE); 20670Sstevel@tonic-gate 20680Sstevel@tonic-gate w.w_addr = addr; 20690Sstevel@tonic-gate w.w_found = 0; 20700Sstevel@tonic-gate 20710Sstevel@tonic-gate /* 20720Sstevel@tonic-gate * Mappings and threads should eventually be added here. 20730Sstevel@tonic-gate */ 20740Sstevel@tonic-gate if (mdb_walk("umem_cache", 20750Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_touch, &w) == -1) { 20760Sstevel@tonic-gate mdb_warn("couldn't find umem_cache walker"); 20770Sstevel@tonic-gate return (DCMD_ERR); 20780Sstevel@tonic-gate } 20790Sstevel@tonic-gate 20800Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 20810Sstevel@tonic-gate return (DCMD_OK); 20820Sstevel@tonic-gate 20830Sstevel@tonic-gate if (mdb_walk("umem_cache", 20840Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_notouch, &w) == -1) { 20850Sstevel@tonic-gate mdb_warn("couldn't find umem_cache walker"); 20860Sstevel@tonic-gate return (DCMD_ERR); 20870Sstevel@tonic-gate } 20880Sstevel@tonic-gate 20890Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 20900Sstevel@tonic-gate return (DCMD_OK); 20910Sstevel@tonic-gate 20920Sstevel@tonic-gate if (mdb_walk("vmem_postfix", 20930Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_vmem, &w) == -1) { 20940Sstevel@tonic-gate mdb_warn("couldn't find vmem_postfix walker"); 20950Sstevel@tonic-gate return (DCMD_ERR); 20960Sstevel@tonic-gate } 20970Sstevel@tonic-gate 20980Sstevel@tonic-gate if (w.w_found == 0) 20990Sstevel@tonic-gate mdb_printf("%p is unknown\n", addr); 21000Sstevel@tonic-gate 21010Sstevel@tonic-gate return (DCMD_OK); 21020Sstevel@tonic-gate } 21030Sstevel@tonic-gate 21040Sstevel@tonic-gate typedef struct umem_log_cpu { 21050Sstevel@tonic-gate uintptr_t umc_low; 21060Sstevel@tonic-gate uintptr_t umc_high; 21070Sstevel@tonic-gate } umem_log_cpu_t; 21080Sstevel@tonic-gate 21090Sstevel@tonic-gate int 21100Sstevel@tonic-gate umem_log_walk(uintptr_t addr, const umem_bufctl_audit_t *b, umem_log_cpu_t *umc) 21110Sstevel@tonic-gate { 21120Sstevel@tonic-gate int i; 21130Sstevel@tonic-gate 21140Sstevel@tonic-gate for (i = 0; i < umem_max_ncpus; i++) { 21150Sstevel@tonic-gate if (addr >= umc[i].umc_low && addr < umc[i].umc_high) 21160Sstevel@tonic-gate break; 21170Sstevel@tonic-gate } 21180Sstevel@tonic-gate 21190Sstevel@tonic-gate if (i == umem_max_ncpus) 21200Sstevel@tonic-gate mdb_printf(" "); 21210Sstevel@tonic-gate else 21220Sstevel@tonic-gate mdb_printf("%3d", i); 21230Sstevel@tonic-gate 21240Sstevel@tonic-gate mdb_printf(" %0?p %0?p %16llx %0?p\n", addr, b->bc_addr, 21250Sstevel@tonic-gate b->bc_timestamp, b->bc_thread); 21260Sstevel@tonic-gate 21270Sstevel@tonic-gate return (WALK_NEXT); 21280Sstevel@tonic-gate } 21290Sstevel@tonic-gate 21300Sstevel@tonic-gate /*ARGSUSED*/ 21310Sstevel@tonic-gate int 21320Sstevel@tonic-gate umem_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 21330Sstevel@tonic-gate { 21340Sstevel@tonic-gate umem_log_header_t lh; 21350Sstevel@tonic-gate umem_cpu_log_header_t clh; 21360Sstevel@tonic-gate uintptr_t lhp, clhp; 21370Sstevel@tonic-gate umem_log_cpu_t *umc; 21380Sstevel@tonic-gate int i; 21390Sstevel@tonic-gate 21400Sstevel@tonic-gate if (umem_readvar(&lhp, "umem_transaction_log") == -1) { 21410Sstevel@tonic-gate mdb_warn("failed to read 'umem_transaction_log'"); 21420Sstevel@tonic-gate return (DCMD_ERR); 21430Sstevel@tonic-gate } 21440Sstevel@tonic-gate 21450Sstevel@tonic-gate if (lhp == NULL) { 21460Sstevel@tonic-gate mdb_warn("no umem transaction log\n"); 21470Sstevel@tonic-gate return (DCMD_ERR); 21480Sstevel@tonic-gate } 21490Sstevel@tonic-gate 21500Sstevel@tonic-gate if (mdb_vread(&lh, sizeof (umem_log_header_t), lhp) == -1) { 21510Sstevel@tonic-gate mdb_warn("failed to read log header at %p", lhp); 21520Sstevel@tonic-gate return (DCMD_ERR); 21530Sstevel@tonic-gate } 21540Sstevel@tonic-gate 21550Sstevel@tonic-gate clhp = lhp + ((uintptr_t)&lh.lh_cpu[0] - (uintptr_t)&lh); 21560Sstevel@tonic-gate 21570Sstevel@tonic-gate umc = mdb_zalloc(sizeof (umem_log_cpu_t) * umem_max_ncpus, 21580Sstevel@tonic-gate UM_SLEEP | UM_GC); 21590Sstevel@tonic-gate 21600Sstevel@tonic-gate for (i = 0; i < umem_max_ncpus; i++) { 21610Sstevel@tonic-gate if (mdb_vread(&clh, sizeof (clh), clhp) == -1) { 21620Sstevel@tonic-gate mdb_warn("cannot read cpu %d's log header at %p", 21630Sstevel@tonic-gate i, clhp); 21640Sstevel@tonic-gate return (DCMD_ERR); 21650Sstevel@tonic-gate } 21660Sstevel@tonic-gate 21670Sstevel@tonic-gate umc[i].umc_low = clh.clh_chunk * lh.lh_chunksize + 21680Sstevel@tonic-gate (uintptr_t)lh.lh_base; 21690Sstevel@tonic-gate umc[i].umc_high = (uintptr_t)clh.clh_current; 21700Sstevel@tonic-gate 21710Sstevel@tonic-gate clhp += sizeof (umem_cpu_log_header_t); 21720Sstevel@tonic-gate } 21730Sstevel@tonic-gate 21740Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) { 21750Sstevel@tonic-gate mdb_printf("%3s %-?s %-?s %16s %-?s\n", "CPU", "ADDR", 21760Sstevel@tonic-gate "BUFADDR", "TIMESTAMP", "THREAD"); 21770Sstevel@tonic-gate } 21780Sstevel@tonic-gate 21790Sstevel@tonic-gate /* 21800Sstevel@tonic-gate * If we have been passed an address, we'll just print out that 21810Sstevel@tonic-gate * log entry. 21820Sstevel@tonic-gate */ 21830Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 21840Sstevel@tonic-gate umem_bufctl_audit_t *bp; 21850Sstevel@tonic-gate UMEM_LOCAL_BUFCTL_AUDIT(&bp); 21860Sstevel@tonic-gate 21870Sstevel@tonic-gate if (mdb_vread(bp, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) { 21880Sstevel@tonic-gate mdb_warn("failed to read bufctl at %p", addr); 21890Sstevel@tonic-gate return (DCMD_ERR); 21900Sstevel@tonic-gate } 21910Sstevel@tonic-gate 21920Sstevel@tonic-gate (void) umem_log_walk(addr, bp, umc); 21930Sstevel@tonic-gate 21940Sstevel@tonic-gate return (DCMD_OK); 21950Sstevel@tonic-gate } 21960Sstevel@tonic-gate 21970Sstevel@tonic-gate if (mdb_walk("umem_log", (mdb_walk_cb_t)umem_log_walk, umc) == -1) { 21980Sstevel@tonic-gate mdb_warn("can't find umem log walker"); 21990Sstevel@tonic-gate return (DCMD_ERR); 22000Sstevel@tonic-gate } 22010Sstevel@tonic-gate 22020Sstevel@tonic-gate return (DCMD_OK); 22030Sstevel@tonic-gate } 22040Sstevel@tonic-gate 22050Sstevel@tonic-gate typedef struct bufctl_history_cb { 22060Sstevel@tonic-gate int bhc_flags; 22070Sstevel@tonic-gate int bhc_argc; 22080Sstevel@tonic-gate const mdb_arg_t *bhc_argv; 22090Sstevel@tonic-gate int bhc_ret; 22100Sstevel@tonic-gate } bufctl_history_cb_t; 22110Sstevel@tonic-gate 22120Sstevel@tonic-gate /*ARGSUSED*/ 22130Sstevel@tonic-gate static int 22140Sstevel@tonic-gate bufctl_history_callback(uintptr_t addr, const void *ign, void *arg) 22150Sstevel@tonic-gate { 22160Sstevel@tonic-gate bufctl_history_cb_t *bhc = arg; 22170Sstevel@tonic-gate 22180Sstevel@tonic-gate bhc->bhc_ret = 22190Sstevel@tonic-gate bufctl(addr, bhc->bhc_flags, bhc->bhc_argc, bhc->bhc_argv); 22200Sstevel@tonic-gate 22210Sstevel@tonic-gate bhc->bhc_flags &= ~DCMD_LOOPFIRST; 22220Sstevel@tonic-gate 22230Sstevel@tonic-gate return ((bhc->bhc_ret == DCMD_OK)? WALK_NEXT : WALK_DONE); 22240Sstevel@tonic-gate } 22250Sstevel@tonic-gate 22260Sstevel@tonic-gate void 22270Sstevel@tonic-gate bufctl_help(void) 22280Sstevel@tonic-gate { 22290Sstevel@tonic-gate mdb_printf("%s\n", 22300Sstevel@tonic-gate "Display the contents of umem_bufctl_audit_ts, with optional filtering.\n"); 22310Sstevel@tonic-gate mdb_dec_indent(2); 22320Sstevel@tonic-gate mdb_printf("%<b>OPTIONS%</b>\n"); 22330Sstevel@tonic-gate mdb_inc_indent(2); 22340Sstevel@tonic-gate mdb_printf("%s", 22350Sstevel@tonic-gate " -v Display the full content of the bufctl, including its stack trace\n" 22360Sstevel@tonic-gate " -h retrieve the bufctl's transaction history, if available\n" 22370Sstevel@tonic-gate " -a addr\n" 22380Sstevel@tonic-gate " filter out bufctls not involving the buffer at addr\n" 22390Sstevel@tonic-gate " -c caller\n" 22400Sstevel@tonic-gate " filter out bufctls without the function/PC in their stack trace\n" 22410Sstevel@tonic-gate " -e earliest\n" 22420Sstevel@tonic-gate " filter out bufctls timestamped before earliest\n" 22430Sstevel@tonic-gate " -l latest\n" 22440Sstevel@tonic-gate " filter out bufctls timestamped after latest\n" 22450Sstevel@tonic-gate " -t thread\n" 22460Sstevel@tonic-gate " filter out bufctls not involving thread\n"); 22470Sstevel@tonic-gate } 22480Sstevel@tonic-gate 22490Sstevel@tonic-gate int 22500Sstevel@tonic-gate bufctl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 22510Sstevel@tonic-gate { 22520Sstevel@tonic-gate uint_t verbose = FALSE; 22530Sstevel@tonic-gate uint_t history = FALSE; 22540Sstevel@tonic-gate uint_t in_history = FALSE; 22550Sstevel@tonic-gate uintptr_t caller = NULL, thread = NULL; 22560Sstevel@tonic-gate uintptr_t laddr, haddr, baddr = NULL; 22570Sstevel@tonic-gate hrtime_t earliest = 0, latest = 0; 22580Sstevel@tonic-gate int i, depth; 22590Sstevel@tonic-gate char c[MDB_SYM_NAMLEN]; 22600Sstevel@tonic-gate GElf_Sym sym; 22610Sstevel@tonic-gate umem_bufctl_audit_t *bcp; 22620Sstevel@tonic-gate UMEM_LOCAL_BUFCTL_AUDIT(&bcp); 22630Sstevel@tonic-gate 22640Sstevel@tonic-gate if (mdb_getopts(argc, argv, 22650Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &verbose, 22660Sstevel@tonic-gate 'h', MDB_OPT_SETBITS, TRUE, &history, 22670Sstevel@tonic-gate 'H', MDB_OPT_SETBITS, TRUE, &in_history, /* internal */ 22680Sstevel@tonic-gate 'c', MDB_OPT_UINTPTR, &caller, 22690Sstevel@tonic-gate 't', MDB_OPT_UINTPTR, &thread, 22700Sstevel@tonic-gate 'e', MDB_OPT_UINT64, &earliest, 22710Sstevel@tonic-gate 'l', MDB_OPT_UINT64, &latest, 22720Sstevel@tonic-gate 'a', MDB_OPT_UINTPTR, &baddr, NULL) != argc) 22730Sstevel@tonic-gate return (DCMD_USAGE); 22740Sstevel@tonic-gate 22750Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 22760Sstevel@tonic-gate return (DCMD_USAGE); 22770Sstevel@tonic-gate 22780Sstevel@tonic-gate if (in_history && !history) 22790Sstevel@tonic-gate return (DCMD_USAGE); 22800Sstevel@tonic-gate 22810Sstevel@tonic-gate if (history && !in_history) { 22820Sstevel@tonic-gate mdb_arg_t *nargv = mdb_zalloc(sizeof (*nargv) * (argc + 1), 22830Sstevel@tonic-gate UM_SLEEP | UM_GC); 22840Sstevel@tonic-gate bufctl_history_cb_t bhc; 22850Sstevel@tonic-gate 22860Sstevel@tonic-gate nargv[0].a_type = MDB_TYPE_STRING; 22870Sstevel@tonic-gate nargv[0].a_un.a_str = "-H"; /* prevent recursion */ 22880Sstevel@tonic-gate 22890Sstevel@tonic-gate for (i = 0; i < argc; i++) 22900Sstevel@tonic-gate nargv[i + 1] = argv[i]; 22910Sstevel@tonic-gate 22920Sstevel@tonic-gate /* 22930Sstevel@tonic-gate * When in history mode, we treat each element as if it 22940Sstevel@tonic-gate * were in a seperate loop, so that the headers group 22950Sstevel@tonic-gate * bufctls with similar histories. 22960Sstevel@tonic-gate */ 22970Sstevel@tonic-gate bhc.bhc_flags = flags | DCMD_LOOP | DCMD_LOOPFIRST; 22980Sstevel@tonic-gate bhc.bhc_argc = argc + 1; 22990Sstevel@tonic-gate bhc.bhc_argv = nargv; 23000Sstevel@tonic-gate bhc.bhc_ret = DCMD_OK; 23010Sstevel@tonic-gate 23020Sstevel@tonic-gate if (mdb_pwalk("bufctl_history", bufctl_history_callback, &bhc, 23030Sstevel@tonic-gate addr) == -1) { 23040Sstevel@tonic-gate mdb_warn("unable to walk bufctl_history"); 23050Sstevel@tonic-gate return (DCMD_ERR); 23060Sstevel@tonic-gate } 23070Sstevel@tonic-gate 23080Sstevel@tonic-gate if (bhc.bhc_ret == DCMD_OK && !(flags & DCMD_PIPE_OUT)) 23090Sstevel@tonic-gate mdb_printf("\n"); 23100Sstevel@tonic-gate 23110Sstevel@tonic-gate return (bhc.bhc_ret); 23120Sstevel@tonic-gate } 23130Sstevel@tonic-gate 23140Sstevel@tonic-gate if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) { 23150Sstevel@tonic-gate if (verbose) { 23160Sstevel@tonic-gate mdb_printf("%16s %16s %16s %16s\n" 23170Sstevel@tonic-gate "%<u>%16s %16s %16s %16s%</u>\n", 23180Sstevel@tonic-gate "ADDR", "BUFADDR", "TIMESTAMP", "THREAD", 23190Sstevel@tonic-gate "", "CACHE", "LASTLOG", "CONTENTS"); 23200Sstevel@tonic-gate } else { 23210Sstevel@tonic-gate mdb_printf("%<u>%-?s %-?s %-12s %5s %s%</u>\n", 23220Sstevel@tonic-gate "ADDR", "BUFADDR", "TIMESTAMP", "THRD", "CALLER"); 23230Sstevel@tonic-gate } 23240Sstevel@tonic-gate } 23250Sstevel@tonic-gate 23260Sstevel@tonic-gate if (mdb_vread(bcp, UMEM_BUFCTL_AUDIT_SIZE, addr) == -1) { 23270Sstevel@tonic-gate mdb_warn("couldn't read bufctl at %p", addr); 23280Sstevel@tonic-gate return (DCMD_ERR); 23290Sstevel@tonic-gate } 23300Sstevel@tonic-gate 23310Sstevel@tonic-gate /* 23320Sstevel@tonic-gate * Guard against bogus bc_depth in case the bufctl is corrupt or 23330Sstevel@tonic-gate * the address does not really refer to a bufctl. 23340Sstevel@tonic-gate */ 23350Sstevel@tonic-gate depth = MIN(bcp->bc_depth, umem_stack_depth); 23360Sstevel@tonic-gate 23370Sstevel@tonic-gate if (caller != NULL) { 23380Sstevel@tonic-gate laddr = caller; 23390Sstevel@tonic-gate haddr = caller + sizeof (caller); 23400Sstevel@tonic-gate 23410Sstevel@tonic-gate if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c, sizeof (c), 23420Sstevel@tonic-gate &sym) != -1 && caller == (uintptr_t)sym.st_value) { 23430Sstevel@tonic-gate /* 23440Sstevel@tonic-gate * We were provided an exact symbol value; any 23450Sstevel@tonic-gate * address in the function is valid. 23460Sstevel@tonic-gate */ 23470Sstevel@tonic-gate laddr = (uintptr_t)sym.st_value; 23480Sstevel@tonic-gate haddr = (uintptr_t)sym.st_value + sym.st_size; 23490Sstevel@tonic-gate } 23500Sstevel@tonic-gate 23510Sstevel@tonic-gate for (i = 0; i < depth; i++) 23520Sstevel@tonic-gate if (bcp->bc_stack[i] >= laddr && 23530Sstevel@tonic-gate bcp->bc_stack[i] < haddr) 23540Sstevel@tonic-gate break; 23550Sstevel@tonic-gate 23560Sstevel@tonic-gate if (i == depth) 23570Sstevel@tonic-gate return (DCMD_OK); 23580Sstevel@tonic-gate } 23590Sstevel@tonic-gate 23600Sstevel@tonic-gate if (thread != NULL && (uintptr_t)bcp->bc_thread != thread) 23610Sstevel@tonic-gate return (DCMD_OK); 23620Sstevel@tonic-gate 23630Sstevel@tonic-gate if (earliest != 0 && bcp->bc_timestamp < earliest) 23640Sstevel@tonic-gate return (DCMD_OK); 23650Sstevel@tonic-gate 23660Sstevel@tonic-gate if (latest != 0 && bcp->bc_timestamp > latest) 23670Sstevel@tonic-gate return (DCMD_OK); 23680Sstevel@tonic-gate 23690Sstevel@tonic-gate if (baddr != 0 && (uintptr_t)bcp->bc_addr != baddr) 23700Sstevel@tonic-gate return (DCMD_OK); 23710Sstevel@tonic-gate 23720Sstevel@tonic-gate if (flags & DCMD_PIPE_OUT) { 23730Sstevel@tonic-gate mdb_printf("%#r\n", addr); 23740Sstevel@tonic-gate return (DCMD_OK); 23750Sstevel@tonic-gate } 23760Sstevel@tonic-gate 23770Sstevel@tonic-gate if (verbose) { 23780Sstevel@tonic-gate mdb_printf( 23790Sstevel@tonic-gate "%<b>%16p%</b> %16p %16llx %16d\n" 23800Sstevel@tonic-gate "%16s %16p %16p %16p\n", 23810Sstevel@tonic-gate addr, bcp->bc_addr, bcp->bc_timestamp, bcp->bc_thread, 23820Sstevel@tonic-gate "", bcp->bc_cache, bcp->bc_lastlog, bcp->bc_contents); 23830Sstevel@tonic-gate 23840Sstevel@tonic-gate mdb_inc_indent(17); 23850Sstevel@tonic-gate for (i = 0; i < depth; i++) 23860Sstevel@tonic-gate mdb_printf("%a\n", bcp->bc_stack[i]); 23870Sstevel@tonic-gate mdb_dec_indent(17); 23880Sstevel@tonic-gate mdb_printf("\n"); 23890Sstevel@tonic-gate } else { 23900Sstevel@tonic-gate mdb_printf("%0?p %0?p %12llx %5d", addr, bcp->bc_addr, 23910Sstevel@tonic-gate bcp->bc_timestamp, bcp->bc_thread); 23920Sstevel@tonic-gate 23930Sstevel@tonic-gate for (i = 0; i < depth; i++) { 23940Sstevel@tonic-gate if (mdb_lookup_by_addr(bcp->bc_stack[i], 23950Sstevel@tonic-gate MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1) 23960Sstevel@tonic-gate continue; 23970Sstevel@tonic-gate if (is_umem_sym(c, "umem_")) 23980Sstevel@tonic-gate continue; 23990Sstevel@tonic-gate mdb_printf(" %a\n", bcp->bc_stack[i]); 24000Sstevel@tonic-gate break; 24010Sstevel@tonic-gate } 24020Sstevel@tonic-gate 24030Sstevel@tonic-gate if (i >= depth) 24040Sstevel@tonic-gate mdb_printf("\n"); 24050Sstevel@tonic-gate } 24060Sstevel@tonic-gate 24070Sstevel@tonic-gate return (DCMD_OK); 24080Sstevel@tonic-gate } 24090Sstevel@tonic-gate 24100Sstevel@tonic-gate /*ARGSUSED*/ 24110Sstevel@tonic-gate int 24120Sstevel@tonic-gate bufctl_audit(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 24130Sstevel@tonic-gate { 24140Sstevel@tonic-gate mdb_arg_t a; 24150Sstevel@tonic-gate 24160Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 24170Sstevel@tonic-gate return (DCMD_USAGE); 24180Sstevel@tonic-gate 24190Sstevel@tonic-gate if (argc != 0) 24200Sstevel@tonic-gate return (DCMD_USAGE); 24210Sstevel@tonic-gate 24220Sstevel@tonic-gate a.a_type = MDB_TYPE_STRING; 24230Sstevel@tonic-gate a.a_un.a_str = "-v"; 24240Sstevel@tonic-gate 24250Sstevel@tonic-gate return (bufctl(addr, flags, 1, &a)); 24260Sstevel@tonic-gate } 24270Sstevel@tonic-gate 24280Sstevel@tonic-gate typedef struct umem_verify { 24290Sstevel@tonic-gate uint64_t *umv_buf; /* buffer to read cache contents into */ 24300Sstevel@tonic-gate size_t umv_size; /* number of bytes in umv_buf */ 24310Sstevel@tonic-gate int umv_corruption; /* > 0 if corruption found. */ 24320Sstevel@tonic-gate int umv_besilent; /* report actual corruption sites */ 24330Sstevel@tonic-gate struct umem_cache umv_cache; /* the cache we're operating on */ 24340Sstevel@tonic-gate } umem_verify_t; 24350Sstevel@tonic-gate 24360Sstevel@tonic-gate /* 24370Sstevel@tonic-gate * verify_pattern() 24380Sstevel@tonic-gate * verify that buf is filled with the pattern pat. 24390Sstevel@tonic-gate */ 24400Sstevel@tonic-gate static int64_t 24410Sstevel@tonic-gate verify_pattern(uint64_t *buf_arg, size_t size, uint64_t pat) 24420Sstevel@tonic-gate { 24430Sstevel@tonic-gate /*LINTED*/ 24440Sstevel@tonic-gate uint64_t *bufend = (uint64_t *)((char *)buf_arg + size); 24450Sstevel@tonic-gate uint64_t *buf; 24460Sstevel@tonic-gate 24470Sstevel@tonic-gate for (buf = buf_arg; buf < bufend; buf++) 24480Sstevel@tonic-gate if (*buf != pat) 24490Sstevel@tonic-gate return ((uintptr_t)buf - (uintptr_t)buf_arg); 24500Sstevel@tonic-gate return (-1); 24510Sstevel@tonic-gate } 24520Sstevel@tonic-gate 24530Sstevel@tonic-gate /* 24540Sstevel@tonic-gate * verify_buftag() 24550Sstevel@tonic-gate * verify that btp->bt_bxstat == (bcp ^ pat) 24560Sstevel@tonic-gate */ 24570Sstevel@tonic-gate static int 24580Sstevel@tonic-gate verify_buftag(umem_buftag_t *btp, uintptr_t pat) 24590Sstevel@tonic-gate { 24600Sstevel@tonic-gate return (btp->bt_bxstat == ((intptr_t)btp->bt_bufctl ^ pat) ? 0 : -1); 24610Sstevel@tonic-gate } 24620Sstevel@tonic-gate 24630Sstevel@tonic-gate /* 24640Sstevel@tonic-gate * verify_free() 24650Sstevel@tonic-gate * verify the integrity of a free block of memory by checking 24660Sstevel@tonic-gate * that it is filled with 0xdeadbeef and that its buftag is sane. 24670Sstevel@tonic-gate */ 24680Sstevel@tonic-gate /*ARGSUSED1*/ 24690Sstevel@tonic-gate static int 24700Sstevel@tonic-gate verify_free(uintptr_t addr, const void *data, void *private) 24710Sstevel@tonic-gate { 24720Sstevel@tonic-gate umem_verify_t *umv = (umem_verify_t *)private; 24730Sstevel@tonic-gate uint64_t *buf = umv->umv_buf; /* buf to validate */ 24740Sstevel@tonic-gate int64_t corrupt; /* corruption offset */ 24750Sstevel@tonic-gate umem_buftag_t *buftagp; /* ptr to buftag */ 24760Sstevel@tonic-gate umem_cache_t *cp = &umv->umv_cache; 24770Sstevel@tonic-gate int besilent = umv->umv_besilent; 24780Sstevel@tonic-gate 24790Sstevel@tonic-gate /*LINTED*/ 24800Sstevel@tonic-gate buftagp = UMEM_BUFTAG(cp, buf); 24810Sstevel@tonic-gate 24820Sstevel@tonic-gate /* 24830Sstevel@tonic-gate * Read the buffer to check. 24840Sstevel@tonic-gate */ 24850Sstevel@tonic-gate if (mdb_vread(buf, umv->umv_size, addr) == -1) { 24860Sstevel@tonic-gate if (!besilent) 24870Sstevel@tonic-gate mdb_warn("couldn't read %p", addr); 24880Sstevel@tonic-gate return (WALK_NEXT); 24890Sstevel@tonic-gate } 24900Sstevel@tonic-gate 24910Sstevel@tonic-gate if ((corrupt = verify_pattern(buf, cp->cache_verify, 24920Sstevel@tonic-gate UMEM_FREE_PATTERN)) >= 0) { 24930Sstevel@tonic-gate if (!besilent) 24940Sstevel@tonic-gate mdb_printf("buffer %p (free) seems corrupted, at %p\n", 24950Sstevel@tonic-gate addr, (uintptr_t)addr + corrupt); 24960Sstevel@tonic-gate goto corrupt; 24970Sstevel@tonic-gate } 24980Sstevel@tonic-gate 24990Sstevel@tonic-gate if ((cp->cache_flags & UMF_HASH) && 25000Sstevel@tonic-gate buftagp->bt_redzone != UMEM_REDZONE_PATTERN) { 25010Sstevel@tonic-gate if (!besilent) 25020Sstevel@tonic-gate mdb_printf("buffer %p (free) seems to " 25030Sstevel@tonic-gate "have a corrupt redzone pattern\n", addr); 25040Sstevel@tonic-gate goto corrupt; 25050Sstevel@tonic-gate } 25060Sstevel@tonic-gate 25070Sstevel@tonic-gate /* 25080Sstevel@tonic-gate * confirm bufctl pointer integrity. 25090Sstevel@tonic-gate */ 25100Sstevel@tonic-gate if (verify_buftag(buftagp, UMEM_BUFTAG_FREE) == -1) { 25110Sstevel@tonic-gate if (!besilent) 25120Sstevel@tonic-gate mdb_printf("buffer %p (free) has a corrupt " 25130Sstevel@tonic-gate "buftag\n", addr); 25140Sstevel@tonic-gate goto corrupt; 25150Sstevel@tonic-gate } 25160Sstevel@tonic-gate 25170Sstevel@tonic-gate return (WALK_NEXT); 25180Sstevel@tonic-gate corrupt: 25190Sstevel@tonic-gate umv->umv_corruption++; 25200Sstevel@tonic-gate return (WALK_NEXT); 25210Sstevel@tonic-gate } 25220Sstevel@tonic-gate 25230Sstevel@tonic-gate /* 25240Sstevel@tonic-gate * verify_alloc() 25250Sstevel@tonic-gate * Verify that the buftag of an allocated buffer makes sense with respect 25260Sstevel@tonic-gate * to the buffer. 25270Sstevel@tonic-gate */ 25280Sstevel@tonic-gate /*ARGSUSED1*/ 25290Sstevel@tonic-gate static int 25300Sstevel@tonic-gate verify_alloc(uintptr_t addr, const void *data, void *private) 25310Sstevel@tonic-gate { 25320Sstevel@tonic-gate umem_verify_t *umv = (umem_verify_t *)private; 25330Sstevel@tonic-gate umem_cache_t *cp = &umv->umv_cache; 25340Sstevel@tonic-gate uint64_t *buf = umv->umv_buf; /* buf to validate */ 25350Sstevel@tonic-gate /*LINTED*/ 25360Sstevel@tonic-gate umem_buftag_t *buftagp = UMEM_BUFTAG(cp, buf); 25370Sstevel@tonic-gate uint32_t *ip = (uint32_t *)buftagp; 25380Sstevel@tonic-gate uint8_t *bp = (uint8_t *)buf; 25390Sstevel@tonic-gate int looks_ok = 0, size_ok = 1; /* flags for finding corruption */ 25400Sstevel@tonic-gate int besilent = umv->umv_besilent; 25410Sstevel@tonic-gate 25420Sstevel@tonic-gate /* 25430Sstevel@tonic-gate * Read the buffer to check. 25440Sstevel@tonic-gate */ 25450Sstevel@tonic-gate if (mdb_vread(buf, umv->umv_size, addr) == -1) { 25460Sstevel@tonic-gate if (!besilent) 25470Sstevel@tonic-gate mdb_warn("couldn't read %p", addr); 25480Sstevel@tonic-gate return (WALK_NEXT); 25490Sstevel@tonic-gate } 25500Sstevel@tonic-gate 25510Sstevel@tonic-gate /* 25520Sstevel@tonic-gate * There are two cases to handle: 25530Sstevel@tonic-gate * 1. If the buf was alloc'd using umem_cache_alloc, it will have 25540Sstevel@tonic-gate * 0xfeedfacefeedface at the end of it 25550Sstevel@tonic-gate * 2. If the buf was alloc'd using umem_alloc, it will have 25560Sstevel@tonic-gate * 0xbb just past the end of the region in use. At the buftag, 25570Sstevel@tonic-gate * it will have 0xfeedface (or, if the whole buffer is in use, 25580Sstevel@tonic-gate * 0xfeedface & bb000000 or 0xfeedfacf & 000000bb depending on 25590Sstevel@tonic-gate * endianness), followed by 32 bits containing the offset of the 25600Sstevel@tonic-gate * 0xbb byte in the buffer. 25610Sstevel@tonic-gate * 25620Sstevel@tonic-gate * Finally, the two 32-bit words that comprise the second half of the 25630Sstevel@tonic-gate * buftag should xor to UMEM_BUFTAG_ALLOC 25640Sstevel@tonic-gate */ 25650Sstevel@tonic-gate 25660Sstevel@tonic-gate if (buftagp->bt_redzone == UMEM_REDZONE_PATTERN) 25670Sstevel@tonic-gate looks_ok = 1; 25680Sstevel@tonic-gate else if (!UMEM_SIZE_VALID(ip[1])) 25690Sstevel@tonic-gate size_ok = 0; 25700Sstevel@tonic-gate else if (bp[UMEM_SIZE_DECODE(ip[1])] == UMEM_REDZONE_BYTE) 25710Sstevel@tonic-gate looks_ok = 1; 25720Sstevel@tonic-gate else 25730Sstevel@tonic-gate size_ok = 0; 25740Sstevel@tonic-gate 25750Sstevel@tonic-gate if (!size_ok) { 25760Sstevel@tonic-gate if (!besilent) 25770Sstevel@tonic-gate mdb_printf("buffer %p (allocated) has a corrupt " 25780Sstevel@tonic-gate "redzone size encoding\n", addr); 25790Sstevel@tonic-gate goto corrupt; 25800Sstevel@tonic-gate } 25810Sstevel@tonic-gate 25820Sstevel@tonic-gate if (!looks_ok) { 25830Sstevel@tonic-gate if (!besilent) 25840Sstevel@tonic-gate mdb_printf("buffer %p (allocated) has a corrupt " 25850Sstevel@tonic-gate "redzone signature\n", addr); 25860Sstevel@tonic-gate goto corrupt; 25870Sstevel@tonic-gate } 25880Sstevel@tonic-gate 25890Sstevel@tonic-gate if (verify_buftag(buftagp, UMEM_BUFTAG_ALLOC) == -1) { 25900Sstevel@tonic-gate if (!besilent) 25910Sstevel@tonic-gate mdb_printf("buffer %p (allocated) has a " 25920Sstevel@tonic-gate "corrupt buftag\n", addr); 25930Sstevel@tonic-gate goto corrupt; 25940Sstevel@tonic-gate } 25950Sstevel@tonic-gate 25960Sstevel@tonic-gate return (WALK_NEXT); 25970Sstevel@tonic-gate corrupt: 25980Sstevel@tonic-gate umv->umv_corruption++; 25990Sstevel@tonic-gate return (WALK_NEXT); 26000Sstevel@tonic-gate } 26010Sstevel@tonic-gate 26020Sstevel@tonic-gate /*ARGSUSED2*/ 26030Sstevel@tonic-gate int 26040Sstevel@tonic-gate umem_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 26050Sstevel@tonic-gate { 26060Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 26070Sstevel@tonic-gate int check_alloc = 0, check_free = 0; 26080Sstevel@tonic-gate umem_verify_t umv; 26090Sstevel@tonic-gate 26100Sstevel@tonic-gate if (mdb_vread(&umv.umv_cache, sizeof (umv.umv_cache), 26110Sstevel@tonic-gate addr) == -1) { 26120Sstevel@tonic-gate mdb_warn("couldn't read umem_cache %p", addr); 26130Sstevel@tonic-gate return (DCMD_ERR); 26140Sstevel@tonic-gate } 26150Sstevel@tonic-gate 26160Sstevel@tonic-gate umv.umv_size = umv.umv_cache.cache_buftag + 26170Sstevel@tonic-gate sizeof (umem_buftag_t); 26180Sstevel@tonic-gate umv.umv_buf = mdb_alloc(umv.umv_size, UM_SLEEP | UM_GC); 26190Sstevel@tonic-gate umv.umv_corruption = 0; 26200Sstevel@tonic-gate 26210Sstevel@tonic-gate if ((umv.umv_cache.cache_flags & UMF_REDZONE)) { 26220Sstevel@tonic-gate check_alloc = 1; 26230Sstevel@tonic-gate if (umv.umv_cache.cache_flags & UMF_DEADBEEF) 26240Sstevel@tonic-gate check_free = 1; 26250Sstevel@tonic-gate } else { 26260Sstevel@tonic-gate if (!(flags & DCMD_LOOP)) { 26270Sstevel@tonic-gate mdb_warn("cache %p (%s) does not have " 26280Sstevel@tonic-gate "redzone checking enabled\n", addr, 26290Sstevel@tonic-gate umv.umv_cache.cache_name); 26300Sstevel@tonic-gate } 26310Sstevel@tonic-gate return (DCMD_ERR); 26320Sstevel@tonic-gate } 26330Sstevel@tonic-gate 26340Sstevel@tonic-gate if (flags & DCMD_LOOP) { 26350Sstevel@tonic-gate /* 26360Sstevel@tonic-gate * table mode, don't print out every corrupt buffer 26370Sstevel@tonic-gate */ 26380Sstevel@tonic-gate umv.umv_besilent = 1; 26390Sstevel@tonic-gate } else { 26400Sstevel@tonic-gate mdb_printf("Summary for cache '%s'\n", 26410Sstevel@tonic-gate umv.umv_cache.cache_name); 26420Sstevel@tonic-gate mdb_inc_indent(2); 26430Sstevel@tonic-gate umv.umv_besilent = 0; 26440Sstevel@tonic-gate } 26450Sstevel@tonic-gate 26460Sstevel@tonic-gate if (check_alloc) 26470Sstevel@tonic-gate (void) mdb_pwalk("umem", verify_alloc, &umv, addr); 26480Sstevel@tonic-gate if (check_free) 26490Sstevel@tonic-gate (void) mdb_pwalk("freemem", verify_free, &umv, addr); 26500Sstevel@tonic-gate 26510Sstevel@tonic-gate if (flags & DCMD_LOOP) { 26520Sstevel@tonic-gate if (umv.umv_corruption == 0) { 26530Sstevel@tonic-gate mdb_printf("%-*s %?p clean\n", 26540Sstevel@tonic-gate UMEM_CACHE_NAMELEN, 26550Sstevel@tonic-gate umv.umv_cache.cache_name, addr); 26560Sstevel@tonic-gate } else { 26570Sstevel@tonic-gate char *s = ""; /* optional s in "buffer[s]" */ 26580Sstevel@tonic-gate if (umv.umv_corruption > 1) 26590Sstevel@tonic-gate s = "s"; 26600Sstevel@tonic-gate 26610Sstevel@tonic-gate mdb_printf("%-*s %?p %d corrupt buffer%s\n", 26620Sstevel@tonic-gate UMEM_CACHE_NAMELEN, 26630Sstevel@tonic-gate umv.umv_cache.cache_name, addr, 26640Sstevel@tonic-gate umv.umv_corruption, s); 26650Sstevel@tonic-gate } 26660Sstevel@tonic-gate } else { 26670Sstevel@tonic-gate /* 26680Sstevel@tonic-gate * This is the more verbose mode, when the user has 26690Sstevel@tonic-gate * type addr::umem_verify. If the cache was clean, 26700Sstevel@tonic-gate * nothing will have yet been printed. So say something. 26710Sstevel@tonic-gate */ 26720Sstevel@tonic-gate if (umv.umv_corruption == 0) 26730Sstevel@tonic-gate mdb_printf("clean\n"); 26740Sstevel@tonic-gate 26750Sstevel@tonic-gate mdb_dec_indent(2); 26760Sstevel@tonic-gate } 26770Sstevel@tonic-gate } else { 26780Sstevel@tonic-gate /* 26790Sstevel@tonic-gate * If the user didn't specify a cache to verify, we'll walk all 26800Sstevel@tonic-gate * umem_cache's, specifying ourself as a callback for each... 26810Sstevel@tonic-gate * this is the equivalent of '::walk umem_cache .::umem_verify' 26820Sstevel@tonic-gate */ 26830Sstevel@tonic-gate mdb_printf("%<u>%-*s %-?s %-20s%</b>\n", UMEM_CACHE_NAMELEN, 26840Sstevel@tonic-gate "Cache Name", "Addr", "Cache Integrity"); 26850Sstevel@tonic-gate (void) (mdb_walk_dcmd("umem_cache", "umem_verify", 0, NULL)); 26860Sstevel@tonic-gate } 26870Sstevel@tonic-gate 26880Sstevel@tonic-gate return (DCMD_OK); 26890Sstevel@tonic-gate } 26900Sstevel@tonic-gate 26910Sstevel@tonic-gate typedef struct vmem_node { 26920Sstevel@tonic-gate struct vmem_node *vn_next; 26930Sstevel@tonic-gate struct vmem_node *vn_parent; 26940Sstevel@tonic-gate struct vmem_node *vn_sibling; 26950Sstevel@tonic-gate struct vmem_node *vn_children; 26960Sstevel@tonic-gate uintptr_t vn_addr; 26970Sstevel@tonic-gate int vn_marked; 26980Sstevel@tonic-gate vmem_t vn_vmem; 26990Sstevel@tonic-gate } vmem_node_t; 27000Sstevel@tonic-gate 27010Sstevel@tonic-gate typedef struct vmem_walk { 27020Sstevel@tonic-gate vmem_node_t *vw_root; 27030Sstevel@tonic-gate vmem_node_t *vw_current; 27040Sstevel@tonic-gate } vmem_walk_t; 27050Sstevel@tonic-gate 27060Sstevel@tonic-gate int 27070Sstevel@tonic-gate vmem_walk_init(mdb_walk_state_t *wsp) 27080Sstevel@tonic-gate { 27090Sstevel@tonic-gate uintptr_t vaddr, paddr; 27100Sstevel@tonic-gate vmem_node_t *head = NULL, *root = NULL, *current = NULL, *parent, *vp; 27110Sstevel@tonic-gate vmem_walk_t *vw; 27120Sstevel@tonic-gate 27130Sstevel@tonic-gate if (umem_readvar(&vaddr, "vmem_list") == -1) { 27140Sstevel@tonic-gate mdb_warn("couldn't read 'vmem_list'"); 27150Sstevel@tonic-gate return (WALK_ERR); 27160Sstevel@tonic-gate } 27170Sstevel@tonic-gate 27180Sstevel@tonic-gate while (vaddr != NULL) { 27190Sstevel@tonic-gate vp = mdb_zalloc(sizeof (vmem_node_t), UM_SLEEP); 27200Sstevel@tonic-gate vp->vn_addr = vaddr; 27210Sstevel@tonic-gate vp->vn_next = head; 27220Sstevel@tonic-gate head = vp; 27230Sstevel@tonic-gate 27240Sstevel@tonic-gate if (vaddr == wsp->walk_addr) 27250Sstevel@tonic-gate current = vp; 27260Sstevel@tonic-gate 27270Sstevel@tonic-gate if (mdb_vread(&vp->vn_vmem, sizeof (vmem_t), vaddr) == -1) { 27280Sstevel@tonic-gate mdb_warn("couldn't read vmem_t at %p", vaddr); 27290Sstevel@tonic-gate goto err; 27300Sstevel@tonic-gate } 27310Sstevel@tonic-gate 27320Sstevel@tonic-gate vaddr = (uintptr_t)vp->vn_vmem.vm_next; 27330Sstevel@tonic-gate } 27340Sstevel@tonic-gate 27350Sstevel@tonic-gate for (vp = head; vp != NULL; vp = vp->vn_next) { 27360Sstevel@tonic-gate 27370Sstevel@tonic-gate if ((paddr = (uintptr_t)vp->vn_vmem.vm_source) == NULL) { 27380Sstevel@tonic-gate vp->vn_sibling = root; 27390Sstevel@tonic-gate root = vp; 27400Sstevel@tonic-gate continue; 27410Sstevel@tonic-gate } 27420Sstevel@tonic-gate 27430Sstevel@tonic-gate for (parent = head; parent != NULL; parent = parent->vn_next) { 27440Sstevel@tonic-gate if (parent->vn_addr != paddr) 27450Sstevel@tonic-gate continue; 27460Sstevel@tonic-gate vp->vn_sibling = parent->vn_children; 27470Sstevel@tonic-gate parent->vn_children = vp; 27480Sstevel@tonic-gate vp->vn_parent = parent; 27490Sstevel@tonic-gate break; 27500Sstevel@tonic-gate } 27510Sstevel@tonic-gate 27520Sstevel@tonic-gate if (parent == NULL) { 27530Sstevel@tonic-gate mdb_warn("couldn't find %p's parent (%p)\n", 27540Sstevel@tonic-gate vp->vn_addr, paddr); 27550Sstevel@tonic-gate goto err; 27560Sstevel@tonic-gate } 27570Sstevel@tonic-gate } 27580Sstevel@tonic-gate 27590Sstevel@tonic-gate vw = mdb_zalloc(sizeof (vmem_walk_t), UM_SLEEP); 27600Sstevel@tonic-gate vw->vw_root = root; 27610Sstevel@tonic-gate 27620Sstevel@tonic-gate if (current != NULL) 27630Sstevel@tonic-gate vw->vw_current = current; 27640Sstevel@tonic-gate else 27650Sstevel@tonic-gate vw->vw_current = root; 27660Sstevel@tonic-gate 27670Sstevel@tonic-gate wsp->walk_data = vw; 27680Sstevel@tonic-gate return (WALK_NEXT); 27690Sstevel@tonic-gate err: 27700Sstevel@tonic-gate for (vp = head; head != NULL; vp = head) { 27710Sstevel@tonic-gate head = vp->vn_next; 27720Sstevel@tonic-gate mdb_free(vp, sizeof (vmem_node_t)); 27730Sstevel@tonic-gate } 27740Sstevel@tonic-gate 27750Sstevel@tonic-gate return (WALK_ERR); 27760Sstevel@tonic-gate } 27770Sstevel@tonic-gate 27780Sstevel@tonic-gate int 27790Sstevel@tonic-gate vmem_walk_step(mdb_walk_state_t *wsp) 27800Sstevel@tonic-gate { 27810Sstevel@tonic-gate vmem_walk_t *vw = wsp->walk_data; 27820Sstevel@tonic-gate vmem_node_t *vp; 27830Sstevel@tonic-gate int rval; 27840Sstevel@tonic-gate 27850Sstevel@tonic-gate if ((vp = vw->vw_current) == NULL) 27860Sstevel@tonic-gate return (WALK_DONE); 27870Sstevel@tonic-gate 27880Sstevel@tonic-gate rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata); 27890Sstevel@tonic-gate 27900Sstevel@tonic-gate if (vp->vn_children != NULL) { 27910Sstevel@tonic-gate vw->vw_current = vp->vn_children; 27920Sstevel@tonic-gate return (rval); 27930Sstevel@tonic-gate } 27940Sstevel@tonic-gate 27950Sstevel@tonic-gate do { 27960Sstevel@tonic-gate vw->vw_current = vp->vn_sibling; 27970Sstevel@tonic-gate vp = vp->vn_parent; 27980Sstevel@tonic-gate } while (vw->vw_current == NULL && vp != NULL); 27990Sstevel@tonic-gate 28000Sstevel@tonic-gate return (rval); 28010Sstevel@tonic-gate } 28020Sstevel@tonic-gate 28030Sstevel@tonic-gate /* 28040Sstevel@tonic-gate * The "vmem_postfix" walk walks the vmem arenas in post-fix order; all 28050Sstevel@tonic-gate * children are visited before their parent. We perform the postfix walk 28060Sstevel@tonic-gate * iteratively (rather than recursively) to allow mdb to regain control 28070Sstevel@tonic-gate * after each callback. 28080Sstevel@tonic-gate */ 28090Sstevel@tonic-gate int 28100Sstevel@tonic-gate vmem_postfix_walk_step(mdb_walk_state_t *wsp) 28110Sstevel@tonic-gate { 28120Sstevel@tonic-gate vmem_walk_t *vw = wsp->walk_data; 28130Sstevel@tonic-gate vmem_node_t *vp = vw->vw_current; 28140Sstevel@tonic-gate int rval; 28150Sstevel@tonic-gate 28160Sstevel@tonic-gate /* 28170Sstevel@tonic-gate * If this node is marked, then we know that we have already visited 28180Sstevel@tonic-gate * all of its children. If the node has any siblings, they need to 28190Sstevel@tonic-gate * be visited next; otherwise, we need to visit the parent. Note 28200Sstevel@tonic-gate * that vp->vn_marked will only be zero on the first invocation of 28210Sstevel@tonic-gate * the step function. 28220Sstevel@tonic-gate */ 28230Sstevel@tonic-gate if (vp->vn_marked) { 28240Sstevel@tonic-gate if (vp->vn_sibling != NULL) 28250Sstevel@tonic-gate vp = vp->vn_sibling; 28260Sstevel@tonic-gate else if (vp->vn_parent != NULL) 28270Sstevel@tonic-gate vp = vp->vn_parent; 28280Sstevel@tonic-gate else { 28290Sstevel@tonic-gate /* 28300Sstevel@tonic-gate * We have neither a parent, nor a sibling, and we 28310Sstevel@tonic-gate * have already been visited; we're done. 28320Sstevel@tonic-gate */ 28330Sstevel@tonic-gate return (WALK_DONE); 28340Sstevel@tonic-gate } 28350Sstevel@tonic-gate } 28360Sstevel@tonic-gate 28370Sstevel@tonic-gate /* 28380Sstevel@tonic-gate * Before we visit this node, visit its children. 28390Sstevel@tonic-gate */ 28400Sstevel@tonic-gate while (vp->vn_children != NULL && !vp->vn_children->vn_marked) 28410Sstevel@tonic-gate vp = vp->vn_children; 28420Sstevel@tonic-gate 28430Sstevel@tonic-gate vp->vn_marked = 1; 28440Sstevel@tonic-gate vw->vw_current = vp; 28450Sstevel@tonic-gate rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata); 28460Sstevel@tonic-gate 28470Sstevel@tonic-gate return (rval); 28480Sstevel@tonic-gate } 28490Sstevel@tonic-gate 28500Sstevel@tonic-gate void 28510Sstevel@tonic-gate vmem_walk_fini(mdb_walk_state_t *wsp) 28520Sstevel@tonic-gate { 28530Sstevel@tonic-gate vmem_walk_t *vw = wsp->walk_data; 28540Sstevel@tonic-gate vmem_node_t *root = vw->vw_root; 28550Sstevel@tonic-gate int done; 28560Sstevel@tonic-gate 28570Sstevel@tonic-gate if (root == NULL) 28580Sstevel@tonic-gate return; 28590Sstevel@tonic-gate 28600Sstevel@tonic-gate if ((vw->vw_root = root->vn_children) != NULL) 28610Sstevel@tonic-gate vmem_walk_fini(wsp); 28620Sstevel@tonic-gate 28630Sstevel@tonic-gate vw->vw_root = root->vn_sibling; 28640Sstevel@tonic-gate done = (root->vn_sibling == NULL && root->vn_parent == NULL); 28650Sstevel@tonic-gate mdb_free(root, sizeof (vmem_node_t)); 28660Sstevel@tonic-gate 28670Sstevel@tonic-gate if (done) { 28680Sstevel@tonic-gate mdb_free(vw, sizeof (vmem_walk_t)); 28690Sstevel@tonic-gate } else { 28700Sstevel@tonic-gate vmem_walk_fini(wsp); 28710Sstevel@tonic-gate } 28720Sstevel@tonic-gate } 28730Sstevel@tonic-gate 28740Sstevel@tonic-gate typedef struct vmem_seg_walk { 28750Sstevel@tonic-gate uint8_t vsw_type; 28760Sstevel@tonic-gate uintptr_t vsw_start; 28770Sstevel@tonic-gate uintptr_t vsw_current; 28780Sstevel@tonic-gate } vmem_seg_walk_t; 28790Sstevel@tonic-gate 28800Sstevel@tonic-gate /*ARGSUSED*/ 28810Sstevel@tonic-gate int 28820Sstevel@tonic-gate vmem_seg_walk_common_init(mdb_walk_state_t *wsp, uint8_t type, char *name) 28830Sstevel@tonic-gate { 28840Sstevel@tonic-gate vmem_seg_walk_t *vsw; 28850Sstevel@tonic-gate 28860Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 28870Sstevel@tonic-gate mdb_warn("vmem_%s does not support global walks\n", name); 28880Sstevel@tonic-gate return (WALK_ERR); 28890Sstevel@tonic-gate } 28900Sstevel@tonic-gate 28910Sstevel@tonic-gate wsp->walk_data = vsw = mdb_alloc(sizeof (vmem_seg_walk_t), UM_SLEEP); 28920Sstevel@tonic-gate 28930Sstevel@tonic-gate vsw->vsw_type = type; 28940Sstevel@tonic-gate vsw->vsw_start = wsp->walk_addr + OFFSETOF(vmem_t, vm_seg0); 28950Sstevel@tonic-gate vsw->vsw_current = vsw->vsw_start; 28960Sstevel@tonic-gate 28970Sstevel@tonic-gate return (WALK_NEXT); 28980Sstevel@tonic-gate } 28990Sstevel@tonic-gate 29000Sstevel@tonic-gate /* 29010Sstevel@tonic-gate * vmem segments can't have type 0 (this should be added to vmem_impl.h). 29020Sstevel@tonic-gate */ 29030Sstevel@tonic-gate #define VMEM_NONE 0 29040Sstevel@tonic-gate 29050Sstevel@tonic-gate int 29060Sstevel@tonic-gate vmem_alloc_walk_init(mdb_walk_state_t *wsp) 29070Sstevel@tonic-gate { 29080Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_ALLOC, "alloc")); 29090Sstevel@tonic-gate } 29100Sstevel@tonic-gate 29110Sstevel@tonic-gate int 29120Sstevel@tonic-gate vmem_free_walk_init(mdb_walk_state_t *wsp) 29130Sstevel@tonic-gate { 29140Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_FREE, "free")); 29150Sstevel@tonic-gate } 29160Sstevel@tonic-gate 29170Sstevel@tonic-gate int 29180Sstevel@tonic-gate vmem_span_walk_init(mdb_walk_state_t *wsp) 29190Sstevel@tonic-gate { 29200Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_SPAN, "span")); 29210Sstevel@tonic-gate } 29220Sstevel@tonic-gate 29230Sstevel@tonic-gate int 29240Sstevel@tonic-gate vmem_seg_walk_init(mdb_walk_state_t *wsp) 29250Sstevel@tonic-gate { 29260Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_NONE, "seg")); 29270Sstevel@tonic-gate } 29280Sstevel@tonic-gate 29290Sstevel@tonic-gate int 29300Sstevel@tonic-gate vmem_seg_walk_step(mdb_walk_state_t *wsp) 29310Sstevel@tonic-gate { 29320Sstevel@tonic-gate vmem_seg_t seg; 29330Sstevel@tonic-gate vmem_seg_walk_t *vsw = wsp->walk_data; 29340Sstevel@tonic-gate uintptr_t addr = vsw->vsw_current; 29350Sstevel@tonic-gate static size_t seg_size = 0; 29360Sstevel@tonic-gate int rval; 29370Sstevel@tonic-gate 29380Sstevel@tonic-gate if (!seg_size) { 29390Sstevel@tonic-gate if (umem_readvar(&seg_size, "vmem_seg_size") == -1) { 29400Sstevel@tonic-gate mdb_warn("failed to read 'vmem_seg_size'"); 29410Sstevel@tonic-gate seg_size = sizeof (vmem_seg_t); 29420Sstevel@tonic-gate } 29430Sstevel@tonic-gate } 29440Sstevel@tonic-gate 29450Sstevel@tonic-gate if (seg_size < sizeof (seg)) 29460Sstevel@tonic-gate bzero((caddr_t)&seg + seg_size, sizeof (seg) - seg_size); 29470Sstevel@tonic-gate 29480Sstevel@tonic-gate if (mdb_vread(&seg, seg_size, addr) == -1) { 29490Sstevel@tonic-gate mdb_warn("couldn't read vmem_seg at %p", addr); 29500Sstevel@tonic-gate return (WALK_ERR); 29510Sstevel@tonic-gate } 29520Sstevel@tonic-gate 29530Sstevel@tonic-gate vsw->vsw_current = (uintptr_t)seg.vs_anext; 29540Sstevel@tonic-gate if (vsw->vsw_type != VMEM_NONE && seg.vs_type != vsw->vsw_type) { 29550Sstevel@tonic-gate rval = WALK_NEXT; 29560Sstevel@tonic-gate } else { 29570Sstevel@tonic-gate rval = wsp->walk_callback(addr, &seg, wsp->walk_cbdata); 29580Sstevel@tonic-gate } 29590Sstevel@tonic-gate 29600Sstevel@tonic-gate if (vsw->vsw_current == vsw->vsw_start) 29610Sstevel@tonic-gate return (WALK_DONE); 29620Sstevel@tonic-gate 29630Sstevel@tonic-gate return (rval); 29640Sstevel@tonic-gate } 29650Sstevel@tonic-gate 29660Sstevel@tonic-gate void 29670Sstevel@tonic-gate vmem_seg_walk_fini(mdb_walk_state_t *wsp) 29680Sstevel@tonic-gate { 29690Sstevel@tonic-gate vmem_seg_walk_t *vsw = wsp->walk_data; 29700Sstevel@tonic-gate 29710Sstevel@tonic-gate mdb_free(vsw, sizeof (vmem_seg_walk_t)); 29720Sstevel@tonic-gate } 29730Sstevel@tonic-gate 29740Sstevel@tonic-gate #define VMEM_NAMEWIDTH 22 29750Sstevel@tonic-gate 29760Sstevel@tonic-gate int 29770Sstevel@tonic-gate vmem(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 29780Sstevel@tonic-gate { 29790Sstevel@tonic-gate vmem_t v, parent; 29800Sstevel@tonic-gate uintptr_t paddr; 29810Sstevel@tonic-gate int ident = 0; 29820Sstevel@tonic-gate char c[VMEM_NAMEWIDTH]; 29830Sstevel@tonic-gate 29840Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 29850Sstevel@tonic-gate if (mdb_walk_dcmd("vmem", "vmem", argc, argv) == -1) { 29860Sstevel@tonic-gate mdb_warn("can't walk vmem"); 29870Sstevel@tonic-gate return (DCMD_ERR); 29880Sstevel@tonic-gate } 29890Sstevel@tonic-gate return (DCMD_OK); 29900Sstevel@tonic-gate } 29910Sstevel@tonic-gate 29920Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) 29930Sstevel@tonic-gate mdb_printf("%-?s %-*s %10s %12s %9s %5s\n", 29940Sstevel@tonic-gate "ADDR", VMEM_NAMEWIDTH, "NAME", "INUSE", 29950Sstevel@tonic-gate "TOTAL", "SUCCEED", "FAIL"); 29960Sstevel@tonic-gate 29970Sstevel@tonic-gate if (mdb_vread(&v, sizeof (v), addr) == -1) { 29980Sstevel@tonic-gate mdb_warn("couldn't read vmem at %p", addr); 29990Sstevel@tonic-gate return (DCMD_ERR); 30000Sstevel@tonic-gate } 30010Sstevel@tonic-gate 30020Sstevel@tonic-gate for (paddr = (uintptr_t)v.vm_source; paddr != NULL; ident += 2) { 30030Sstevel@tonic-gate if (mdb_vread(&parent, sizeof (parent), paddr) == -1) { 30040Sstevel@tonic-gate mdb_warn("couldn't trace %p's ancestry", addr); 30050Sstevel@tonic-gate ident = 0; 30060Sstevel@tonic-gate break; 30070Sstevel@tonic-gate } 30080Sstevel@tonic-gate paddr = (uintptr_t)parent.vm_source; 30090Sstevel@tonic-gate } 30100Sstevel@tonic-gate 30110Sstevel@tonic-gate (void) mdb_snprintf(c, VMEM_NAMEWIDTH, "%*s%s", ident, "", v.vm_name); 30120Sstevel@tonic-gate 30130Sstevel@tonic-gate mdb_printf("%0?p %-*s %10llu %12llu %9llu %5llu\n", 30140Sstevel@tonic-gate addr, VMEM_NAMEWIDTH, c, 30150Sstevel@tonic-gate v.vm_kstat.vk_mem_inuse, v.vm_kstat.vk_mem_total, 30160Sstevel@tonic-gate v.vm_kstat.vk_alloc, v.vm_kstat.vk_fail); 30170Sstevel@tonic-gate 30180Sstevel@tonic-gate return (DCMD_OK); 30190Sstevel@tonic-gate } 30200Sstevel@tonic-gate 30210Sstevel@tonic-gate void 30220Sstevel@tonic-gate vmem_seg_help(void) 30230Sstevel@tonic-gate { 30240Sstevel@tonic-gate mdb_printf("%s\n", 30250Sstevel@tonic-gate "Display the contents of vmem_seg_ts, with optional filtering.\n" 30260Sstevel@tonic-gate "\n" 30270Sstevel@tonic-gate "A vmem_seg_t represents a range of addresses (or arbitrary numbers),\n" 30280Sstevel@tonic-gate "representing a single chunk of data. Only ALLOC segments have debugging\n" 30290Sstevel@tonic-gate "information.\n"); 30300Sstevel@tonic-gate mdb_dec_indent(2); 30310Sstevel@tonic-gate mdb_printf("%<b>OPTIONS%</b>\n"); 30320Sstevel@tonic-gate mdb_inc_indent(2); 30330Sstevel@tonic-gate mdb_printf("%s", 30340Sstevel@tonic-gate " -v Display the full content of the vmem_seg, including its stack trace\n" 30350Sstevel@tonic-gate " -s report the size of the segment, instead of the end address\n" 30360Sstevel@tonic-gate " -c caller\n" 30370Sstevel@tonic-gate " filter out segments without the function/PC in their stack trace\n" 30380Sstevel@tonic-gate " -e earliest\n" 30390Sstevel@tonic-gate " filter out segments timestamped before earliest\n" 30400Sstevel@tonic-gate " -l latest\n" 30410Sstevel@tonic-gate " filter out segments timestamped after latest\n" 30420Sstevel@tonic-gate " -m minsize\n" 30430Sstevel@tonic-gate " filer out segments smaller than minsize\n" 30440Sstevel@tonic-gate " -M maxsize\n" 30450Sstevel@tonic-gate " filer out segments larger than maxsize\n" 30460Sstevel@tonic-gate " -t thread\n" 30470Sstevel@tonic-gate " filter out segments not involving thread\n" 30480Sstevel@tonic-gate " -T type\n" 30490Sstevel@tonic-gate " filter out segments not of type 'type'\n" 30500Sstevel@tonic-gate " type is one of: ALLOC/FREE/SPAN/ROTOR/WALKER\n"); 30510Sstevel@tonic-gate } 30520Sstevel@tonic-gate 30530Sstevel@tonic-gate 30540Sstevel@tonic-gate /*ARGSUSED*/ 30550Sstevel@tonic-gate int 30560Sstevel@tonic-gate vmem_seg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 30570Sstevel@tonic-gate { 30580Sstevel@tonic-gate vmem_seg_t vs; 30590Sstevel@tonic-gate uintptr_t *stk = vs.vs_stack; 30600Sstevel@tonic-gate uintptr_t sz; 30610Sstevel@tonic-gate uint8_t t; 30620Sstevel@tonic-gate const char *type = NULL; 30630Sstevel@tonic-gate GElf_Sym sym; 30640Sstevel@tonic-gate char c[MDB_SYM_NAMLEN]; 30650Sstevel@tonic-gate int no_debug; 30660Sstevel@tonic-gate int i; 30670Sstevel@tonic-gate int depth; 30680Sstevel@tonic-gate uintptr_t laddr, haddr; 30690Sstevel@tonic-gate 30700Sstevel@tonic-gate uintptr_t caller = NULL, thread = NULL; 30710Sstevel@tonic-gate uintptr_t minsize = 0, maxsize = 0; 30720Sstevel@tonic-gate 30730Sstevel@tonic-gate hrtime_t earliest = 0, latest = 0; 30740Sstevel@tonic-gate 30750Sstevel@tonic-gate uint_t size = 0; 30760Sstevel@tonic-gate uint_t verbose = 0; 30770Sstevel@tonic-gate 30780Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 30790Sstevel@tonic-gate return (DCMD_USAGE); 30800Sstevel@tonic-gate 30810Sstevel@tonic-gate if (mdb_getopts(argc, argv, 30820Sstevel@tonic-gate 'c', MDB_OPT_UINTPTR, &caller, 30830Sstevel@tonic-gate 'e', MDB_OPT_UINT64, &earliest, 30840Sstevel@tonic-gate 'l', MDB_OPT_UINT64, &latest, 30850Sstevel@tonic-gate 's', MDB_OPT_SETBITS, TRUE, &size, 30860Sstevel@tonic-gate 'm', MDB_OPT_UINTPTR, &minsize, 30870Sstevel@tonic-gate 'M', MDB_OPT_UINTPTR, &maxsize, 30880Sstevel@tonic-gate 't', MDB_OPT_UINTPTR, &thread, 30890Sstevel@tonic-gate 'T', MDB_OPT_STR, &type, 30900Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &verbose, 30910Sstevel@tonic-gate NULL) != argc) 30920Sstevel@tonic-gate return (DCMD_USAGE); 30930Sstevel@tonic-gate 30940Sstevel@tonic-gate if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) { 30950Sstevel@tonic-gate if (verbose) { 30960Sstevel@tonic-gate mdb_printf("%16s %4s %16s %16s %16s\n" 30970Sstevel@tonic-gate "%<u>%16s %4s %16s %16s %16s%</u>\n", 30980Sstevel@tonic-gate "ADDR", "TYPE", "START", "END", "SIZE", 30990Sstevel@tonic-gate "", "", "THREAD", "TIMESTAMP", ""); 31000Sstevel@tonic-gate } else { 31010Sstevel@tonic-gate mdb_printf("%?s %4s %?s %?s %s\n", "ADDR", "TYPE", 31020Sstevel@tonic-gate "START", size? "SIZE" : "END", "WHO"); 31030Sstevel@tonic-gate } 31040Sstevel@tonic-gate } 31050Sstevel@tonic-gate 31060Sstevel@tonic-gate if (mdb_vread(&vs, sizeof (vs), addr) == -1) { 31070Sstevel@tonic-gate mdb_warn("couldn't read vmem_seg at %p", addr); 31080Sstevel@tonic-gate return (DCMD_ERR); 31090Sstevel@tonic-gate } 31100Sstevel@tonic-gate 31110Sstevel@tonic-gate if (type != NULL) { 31120Sstevel@tonic-gate if (strcmp(type, "ALLC") == 0 || strcmp(type, "ALLOC") == 0) 31130Sstevel@tonic-gate t = VMEM_ALLOC; 31140Sstevel@tonic-gate else if (strcmp(type, "FREE") == 0) 31150Sstevel@tonic-gate t = VMEM_FREE; 31160Sstevel@tonic-gate else if (strcmp(type, "SPAN") == 0) 31170Sstevel@tonic-gate t = VMEM_SPAN; 31180Sstevel@tonic-gate else if (strcmp(type, "ROTR") == 0 || 31190Sstevel@tonic-gate strcmp(type, "ROTOR") == 0) 31200Sstevel@tonic-gate t = VMEM_ROTOR; 31210Sstevel@tonic-gate else if (strcmp(type, "WLKR") == 0 || 31220Sstevel@tonic-gate strcmp(type, "WALKER") == 0) 31230Sstevel@tonic-gate t = VMEM_WALKER; 31240Sstevel@tonic-gate else { 31250Sstevel@tonic-gate mdb_warn("\"%s\" is not a recognized vmem_seg type\n", 31260Sstevel@tonic-gate type); 31270Sstevel@tonic-gate return (DCMD_ERR); 31280Sstevel@tonic-gate } 31290Sstevel@tonic-gate 31300Sstevel@tonic-gate if (vs.vs_type != t) 31310Sstevel@tonic-gate return (DCMD_OK); 31320Sstevel@tonic-gate } 31330Sstevel@tonic-gate 31340Sstevel@tonic-gate sz = vs.vs_end - vs.vs_start; 31350Sstevel@tonic-gate 31360Sstevel@tonic-gate if (minsize != 0 && sz < minsize) 31370Sstevel@tonic-gate return (DCMD_OK); 31380Sstevel@tonic-gate 31390Sstevel@tonic-gate if (maxsize != 0 && sz > maxsize) 31400Sstevel@tonic-gate return (DCMD_OK); 31410Sstevel@tonic-gate 31420Sstevel@tonic-gate t = vs.vs_type; 31430Sstevel@tonic-gate depth = vs.vs_depth; 31440Sstevel@tonic-gate 31450Sstevel@tonic-gate /* 31460Sstevel@tonic-gate * debug info, when present, is only accurate for VMEM_ALLOC segments 31470Sstevel@tonic-gate */ 31480Sstevel@tonic-gate no_debug = (t != VMEM_ALLOC) || 31490Sstevel@tonic-gate (depth == 0 || depth > VMEM_STACK_DEPTH); 31500Sstevel@tonic-gate 31510Sstevel@tonic-gate if (no_debug) { 31520Sstevel@tonic-gate if (caller != NULL || thread != NULL || earliest != 0 || 31530Sstevel@tonic-gate latest != 0) 31540Sstevel@tonic-gate return (DCMD_OK); /* not enough info */ 31550Sstevel@tonic-gate } else { 31560Sstevel@tonic-gate if (caller != NULL) { 31570Sstevel@tonic-gate laddr = caller; 31580Sstevel@tonic-gate haddr = caller + sizeof (caller); 31590Sstevel@tonic-gate 31600Sstevel@tonic-gate if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c, 31610Sstevel@tonic-gate sizeof (c), &sym) != -1 && 31620Sstevel@tonic-gate caller == (uintptr_t)sym.st_value) { 31630Sstevel@tonic-gate /* 31640Sstevel@tonic-gate * We were provided an exact symbol value; any 31650Sstevel@tonic-gate * address in the function is valid. 31660Sstevel@tonic-gate */ 31670Sstevel@tonic-gate laddr = (uintptr_t)sym.st_value; 31680Sstevel@tonic-gate haddr = (uintptr_t)sym.st_value + sym.st_size; 31690Sstevel@tonic-gate } 31700Sstevel@tonic-gate 31710Sstevel@tonic-gate for (i = 0; i < depth; i++) 31720Sstevel@tonic-gate if (vs.vs_stack[i] >= laddr && 31730Sstevel@tonic-gate vs.vs_stack[i] < haddr) 31740Sstevel@tonic-gate break; 31750Sstevel@tonic-gate 31760Sstevel@tonic-gate if (i == depth) 31770Sstevel@tonic-gate return (DCMD_OK); 31780Sstevel@tonic-gate } 31790Sstevel@tonic-gate 31800Sstevel@tonic-gate if (thread != NULL && (uintptr_t)vs.vs_thread != thread) 31810Sstevel@tonic-gate return (DCMD_OK); 31820Sstevel@tonic-gate 31830Sstevel@tonic-gate if (earliest != 0 && vs.vs_timestamp < earliest) 31840Sstevel@tonic-gate return (DCMD_OK); 31850Sstevel@tonic-gate 31860Sstevel@tonic-gate if (latest != 0 && vs.vs_timestamp > latest) 31870Sstevel@tonic-gate return (DCMD_OK); 31880Sstevel@tonic-gate } 31890Sstevel@tonic-gate 31900Sstevel@tonic-gate type = (t == VMEM_ALLOC ? "ALLC" : 31910Sstevel@tonic-gate t == VMEM_FREE ? "FREE" : 31920Sstevel@tonic-gate t == VMEM_SPAN ? "SPAN" : 31930Sstevel@tonic-gate t == VMEM_ROTOR ? "ROTR" : 31940Sstevel@tonic-gate t == VMEM_WALKER ? "WLKR" : 31950Sstevel@tonic-gate "????"); 31960Sstevel@tonic-gate 31970Sstevel@tonic-gate if (flags & DCMD_PIPE_OUT) { 31980Sstevel@tonic-gate mdb_printf("%#r\n", addr); 31990Sstevel@tonic-gate return (DCMD_OK); 32000Sstevel@tonic-gate } 32010Sstevel@tonic-gate 32020Sstevel@tonic-gate if (verbose) { 32030Sstevel@tonic-gate mdb_printf("%<b>%16p%</b> %4s %16p %16p %16d\n", 32040Sstevel@tonic-gate addr, type, vs.vs_start, vs.vs_end, sz); 32050Sstevel@tonic-gate 32060Sstevel@tonic-gate if (no_debug) 32070Sstevel@tonic-gate return (DCMD_OK); 32080Sstevel@tonic-gate 32090Sstevel@tonic-gate mdb_printf("%16s %4s %16d %16llx\n", 32100Sstevel@tonic-gate "", "", vs.vs_thread, vs.vs_timestamp); 32110Sstevel@tonic-gate 32120Sstevel@tonic-gate mdb_inc_indent(17); 32130Sstevel@tonic-gate for (i = 0; i < depth; i++) { 32140Sstevel@tonic-gate mdb_printf("%a\n", stk[i]); 32150Sstevel@tonic-gate } 32160Sstevel@tonic-gate mdb_dec_indent(17); 32170Sstevel@tonic-gate mdb_printf("\n"); 32180Sstevel@tonic-gate } else { 32190Sstevel@tonic-gate mdb_printf("%0?p %4s %0?p %0?p", addr, type, 32200Sstevel@tonic-gate vs.vs_start, size? sz : vs.vs_end); 32210Sstevel@tonic-gate 32220Sstevel@tonic-gate if (no_debug) { 32230Sstevel@tonic-gate mdb_printf("\n"); 32240Sstevel@tonic-gate return (DCMD_OK); 32250Sstevel@tonic-gate } 32260Sstevel@tonic-gate 32270Sstevel@tonic-gate for (i = 0; i < depth; i++) { 32280Sstevel@tonic-gate if (mdb_lookup_by_addr(stk[i], MDB_SYM_FUZZY, 32290Sstevel@tonic-gate c, sizeof (c), &sym) == -1) 32300Sstevel@tonic-gate continue; 32310Sstevel@tonic-gate if (is_umem_sym(c, "vmem_")) 32320Sstevel@tonic-gate continue; 32330Sstevel@tonic-gate break; 32340Sstevel@tonic-gate } 32350Sstevel@tonic-gate mdb_printf(" %a\n", stk[i]); 32360Sstevel@tonic-gate } 32370Sstevel@tonic-gate return (DCMD_OK); 32380Sstevel@tonic-gate } 32390Sstevel@tonic-gate 32400Sstevel@tonic-gate /*ARGSUSED*/ 32410Sstevel@tonic-gate static int 32420Sstevel@tonic-gate showbc(uintptr_t addr, const umem_bufctl_audit_t *bcp, hrtime_t *newest) 32430Sstevel@tonic-gate { 32440Sstevel@tonic-gate char name[UMEM_CACHE_NAMELEN + 1]; 32450Sstevel@tonic-gate hrtime_t delta; 32460Sstevel@tonic-gate int i, depth; 32470Sstevel@tonic-gate 32480Sstevel@tonic-gate if (bcp->bc_timestamp == 0) 32490Sstevel@tonic-gate return (WALK_DONE); 32500Sstevel@tonic-gate 32510Sstevel@tonic-gate if (*newest == 0) 32520Sstevel@tonic-gate *newest = bcp->bc_timestamp; 32530Sstevel@tonic-gate 32540Sstevel@tonic-gate delta = *newest - bcp->bc_timestamp; 32550Sstevel@tonic-gate depth = MIN(bcp->bc_depth, umem_stack_depth); 32560Sstevel@tonic-gate 32570Sstevel@tonic-gate if (mdb_readstr(name, sizeof (name), (uintptr_t) 32580Sstevel@tonic-gate &bcp->bc_cache->cache_name) <= 0) 32590Sstevel@tonic-gate (void) mdb_snprintf(name, sizeof (name), "%a", bcp->bc_cache); 32600Sstevel@tonic-gate 32610Sstevel@tonic-gate mdb_printf("\nT-%lld.%09lld addr=%p %s\n", 32620Sstevel@tonic-gate delta / NANOSEC, delta % NANOSEC, bcp->bc_addr, name); 32630Sstevel@tonic-gate 32640Sstevel@tonic-gate for (i = 0; i < depth; i++) 32650Sstevel@tonic-gate mdb_printf("\t %a\n", bcp->bc_stack[i]); 32660Sstevel@tonic-gate 32670Sstevel@tonic-gate return (WALK_NEXT); 32680Sstevel@tonic-gate } 32690Sstevel@tonic-gate 32700Sstevel@tonic-gate int 32710Sstevel@tonic-gate umalog(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 32720Sstevel@tonic-gate { 32730Sstevel@tonic-gate const char *logname = "umem_transaction_log"; 32740Sstevel@tonic-gate hrtime_t newest = 0; 32750Sstevel@tonic-gate 32760Sstevel@tonic-gate if ((flags & DCMD_ADDRSPEC) || argc > 1) 32770Sstevel@tonic-gate return (DCMD_USAGE); 32780Sstevel@tonic-gate 32790Sstevel@tonic-gate if (argc > 0) { 32800Sstevel@tonic-gate if (argv->a_type != MDB_TYPE_STRING) 32810Sstevel@tonic-gate return (DCMD_USAGE); 32820Sstevel@tonic-gate if (strcmp(argv->a_un.a_str, "fail") == 0) 32830Sstevel@tonic-gate logname = "umem_failure_log"; 32840Sstevel@tonic-gate else if (strcmp(argv->a_un.a_str, "slab") == 0) 32850Sstevel@tonic-gate logname = "umem_slab_log"; 32860Sstevel@tonic-gate else 32870Sstevel@tonic-gate return (DCMD_USAGE); 32880Sstevel@tonic-gate } 32890Sstevel@tonic-gate 32900Sstevel@tonic-gate if (umem_readvar(&addr, logname) == -1) { 32910Sstevel@tonic-gate mdb_warn("failed to read %s log header pointer"); 32920Sstevel@tonic-gate return (DCMD_ERR); 32930Sstevel@tonic-gate } 32940Sstevel@tonic-gate 32950Sstevel@tonic-gate if (mdb_pwalk("umem_log", (mdb_walk_cb_t)showbc, &newest, addr) == -1) { 32960Sstevel@tonic-gate mdb_warn("failed to walk umem log"); 32970Sstevel@tonic-gate return (DCMD_ERR); 32980Sstevel@tonic-gate } 32990Sstevel@tonic-gate 33000Sstevel@tonic-gate return (DCMD_OK); 33010Sstevel@tonic-gate } 33020Sstevel@tonic-gate 33030Sstevel@tonic-gate /* 33040Sstevel@tonic-gate * As the final lure for die-hard crash(1M) users, we provide ::umausers here. 33050Sstevel@tonic-gate * The first piece is a structure which we use to accumulate umem_cache_t 33060Sstevel@tonic-gate * addresses of interest. The umc_add is used as a callback for the umem_cache 33070Sstevel@tonic-gate * walker; we either add all caches, or ones named explicitly as arguments. 33080Sstevel@tonic-gate */ 33090Sstevel@tonic-gate 33100Sstevel@tonic-gate typedef struct umclist { 33110Sstevel@tonic-gate const char *umc_name; /* Name to match (or NULL) */ 33120Sstevel@tonic-gate uintptr_t *umc_caches; /* List of umem_cache_t addrs */ 33130Sstevel@tonic-gate int umc_nelems; /* Num entries in umc_caches */ 33140Sstevel@tonic-gate int umc_size; /* Size of umc_caches array */ 33150Sstevel@tonic-gate } umclist_t; 33160Sstevel@tonic-gate 33170Sstevel@tonic-gate static int 33180Sstevel@tonic-gate umc_add(uintptr_t addr, const umem_cache_t *cp, umclist_t *umc) 33190Sstevel@tonic-gate { 33200Sstevel@tonic-gate void *p; 33210Sstevel@tonic-gate int s; 33220Sstevel@tonic-gate 33230Sstevel@tonic-gate if (umc->umc_name == NULL || 33240Sstevel@tonic-gate strcmp(cp->cache_name, umc->umc_name) == 0) { 33250Sstevel@tonic-gate /* 33260Sstevel@tonic-gate * If we have a match, grow our array (if necessary), and then 33270Sstevel@tonic-gate * add the virtual address of the matching cache to our list. 33280Sstevel@tonic-gate */ 33290Sstevel@tonic-gate if (umc->umc_nelems >= umc->umc_size) { 33300Sstevel@tonic-gate s = umc->umc_size ? umc->umc_size * 2 : 256; 33310Sstevel@tonic-gate p = mdb_alloc(sizeof (uintptr_t) * s, UM_SLEEP | UM_GC); 33320Sstevel@tonic-gate 33330Sstevel@tonic-gate bcopy(umc->umc_caches, p, 33340Sstevel@tonic-gate sizeof (uintptr_t) * umc->umc_size); 33350Sstevel@tonic-gate 33360Sstevel@tonic-gate umc->umc_caches = p; 33370Sstevel@tonic-gate umc->umc_size = s; 33380Sstevel@tonic-gate } 33390Sstevel@tonic-gate 33400Sstevel@tonic-gate umc->umc_caches[umc->umc_nelems++] = addr; 33410Sstevel@tonic-gate return (umc->umc_name ? WALK_DONE : WALK_NEXT); 33420Sstevel@tonic-gate } 33430Sstevel@tonic-gate 33440Sstevel@tonic-gate return (WALK_NEXT); 33450Sstevel@tonic-gate } 33460Sstevel@tonic-gate 33470Sstevel@tonic-gate /* 33480Sstevel@tonic-gate * The second piece of ::umausers is a hash table of allocations. Each 33490Sstevel@tonic-gate * allocation owner is identified by its stack trace and data_size. We then 33500Sstevel@tonic-gate * track the total bytes of all such allocations, and the number of allocations 33510Sstevel@tonic-gate * to report at the end. Once we have a list of caches, we walk through the 33520Sstevel@tonic-gate * allocated bufctls of each, and update our hash table accordingly. 33530Sstevel@tonic-gate */ 33540Sstevel@tonic-gate 33550Sstevel@tonic-gate typedef struct umowner { 33560Sstevel@tonic-gate struct umowner *umo_head; /* First hash elt in bucket */ 33570Sstevel@tonic-gate struct umowner *umo_next; /* Next hash elt in chain */ 33580Sstevel@tonic-gate size_t umo_signature; /* Hash table signature */ 33590Sstevel@tonic-gate uint_t umo_num; /* Number of allocations */ 33600Sstevel@tonic-gate size_t umo_data_size; /* Size of each allocation */ 33610Sstevel@tonic-gate size_t umo_total_size; /* Total bytes of allocation */ 33620Sstevel@tonic-gate int umo_depth; /* Depth of stack trace */ 33630Sstevel@tonic-gate uintptr_t *umo_stack; /* Stack trace */ 33640Sstevel@tonic-gate } umowner_t; 33650Sstevel@tonic-gate 33660Sstevel@tonic-gate typedef struct umusers { 33670Sstevel@tonic-gate const umem_cache_t *umu_cache; /* Current umem cache */ 33680Sstevel@tonic-gate umowner_t *umu_hash; /* Hash table of owners */ 33690Sstevel@tonic-gate uintptr_t *umu_stacks; /* stacks for owners */ 33700Sstevel@tonic-gate int umu_nelems; /* Number of entries in use */ 33710Sstevel@tonic-gate int umu_size; /* Total number of entries */ 33720Sstevel@tonic-gate } umusers_t; 33730Sstevel@tonic-gate 33740Sstevel@tonic-gate static void 33750Sstevel@tonic-gate umu_add(umusers_t *umu, const umem_bufctl_audit_t *bcp, 33760Sstevel@tonic-gate size_t size, size_t data_size) 33770Sstevel@tonic-gate { 33780Sstevel@tonic-gate int i, depth = MIN(bcp->bc_depth, umem_stack_depth); 33790Sstevel@tonic-gate size_t bucket, signature = data_size; 33800Sstevel@tonic-gate umowner_t *umo, *umoend; 33810Sstevel@tonic-gate 33820Sstevel@tonic-gate /* 33830Sstevel@tonic-gate * If the hash table is full, double its size and rehash everything. 33840Sstevel@tonic-gate */ 33850Sstevel@tonic-gate if (umu->umu_nelems >= umu->umu_size) { 33860Sstevel@tonic-gate int s = umu->umu_size ? umu->umu_size * 2 : 1024; 33870Sstevel@tonic-gate size_t umowner_size = sizeof (umowner_t); 33880Sstevel@tonic-gate size_t trace_size = umem_stack_depth * sizeof (uintptr_t); 33890Sstevel@tonic-gate uintptr_t *new_stacks; 33900Sstevel@tonic-gate 33910Sstevel@tonic-gate umo = mdb_alloc(umowner_size * s, UM_SLEEP | UM_GC); 33920Sstevel@tonic-gate new_stacks = mdb_alloc(trace_size * s, UM_SLEEP | UM_GC); 33930Sstevel@tonic-gate 33940Sstevel@tonic-gate bcopy(umu->umu_hash, umo, umowner_size * umu->umu_size); 33950Sstevel@tonic-gate bcopy(umu->umu_stacks, new_stacks, trace_size * umu->umu_size); 33960Sstevel@tonic-gate umu->umu_hash = umo; 33970Sstevel@tonic-gate umu->umu_stacks = new_stacks; 33980Sstevel@tonic-gate umu->umu_size = s; 33990Sstevel@tonic-gate 34000Sstevel@tonic-gate umoend = umu->umu_hash + umu->umu_size; 34010Sstevel@tonic-gate for (umo = umu->umu_hash; umo < umoend; umo++) { 34020Sstevel@tonic-gate umo->umo_head = NULL; 34030Sstevel@tonic-gate umo->umo_stack = &umu->umu_stacks[ 34040Sstevel@tonic-gate umem_stack_depth * (umo - umu->umu_hash)]; 34050Sstevel@tonic-gate } 34060Sstevel@tonic-gate 34070Sstevel@tonic-gate umoend = umu->umu_hash + umu->umu_nelems; 34080Sstevel@tonic-gate for (umo = umu->umu_hash; umo < umoend; umo++) { 34090Sstevel@tonic-gate bucket = umo->umo_signature & (umu->umu_size - 1); 34100Sstevel@tonic-gate umo->umo_next = umu->umu_hash[bucket].umo_head; 34110Sstevel@tonic-gate umu->umu_hash[bucket].umo_head = umo; 34120Sstevel@tonic-gate } 34130Sstevel@tonic-gate } 34140Sstevel@tonic-gate 34150Sstevel@tonic-gate /* 34160Sstevel@tonic-gate * Finish computing the hash signature from the stack trace, and then 34170Sstevel@tonic-gate * see if the owner is in the hash table. If so, update our stats. 34180Sstevel@tonic-gate */ 34190Sstevel@tonic-gate for (i = 0; i < depth; i++) 34200Sstevel@tonic-gate signature += bcp->bc_stack[i]; 34210Sstevel@tonic-gate 34220Sstevel@tonic-gate bucket = signature & (umu->umu_size - 1); 34230Sstevel@tonic-gate 34240Sstevel@tonic-gate for (umo = umu->umu_hash[bucket].umo_head; umo; umo = umo->umo_next) { 34250Sstevel@tonic-gate if (umo->umo_signature == signature) { 34260Sstevel@tonic-gate size_t difference = 0; 34270Sstevel@tonic-gate 34280Sstevel@tonic-gate difference |= umo->umo_data_size - data_size; 34290Sstevel@tonic-gate difference |= umo->umo_depth - depth; 34300Sstevel@tonic-gate 34310Sstevel@tonic-gate for (i = 0; i < depth; i++) { 34320Sstevel@tonic-gate difference |= umo->umo_stack[i] - 34330Sstevel@tonic-gate bcp->bc_stack[i]; 34340Sstevel@tonic-gate } 34350Sstevel@tonic-gate 34360Sstevel@tonic-gate if (difference == 0) { 34370Sstevel@tonic-gate umo->umo_total_size += size; 34380Sstevel@tonic-gate umo->umo_num++; 34390Sstevel@tonic-gate return; 34400Sstevel@tonic-gate } 34410Sstevel@tonic-gate } 34420Sstevel@tonic-gate } 34430Sstevel@tonic-gate 34440Sstevel@tonic-gate /* 34450Sstevel@tonic-gate * If the owner is not yet hashed, grab the next element and fill it 34460Sstevel@tonic-gate * in based on the allocation information. 34470Sstevel@tonic-gate */ 34480Sstevel@tonic-gate umo = &umu->umu_hash[umu->umu_nelems++]; 34490Sstevel@tonic-gate umo->umo_next = umu->umu_hash[bucket].umo_head; 34500Sstevel@tonic-gate umu->umu_hash[bucket].umo_head = umo; 34510Sstevel@tonic-gate 34520Sstevel@tonic-gate umo->umo_signature = signature; 34530Sstevel@tonic-gate umo->umo_num = 1; 34540Sstevel@tonic-gate umo->umo_data_size = data_size; 34550Sstevel@tonic-gate umo->umo_total_size = size; 34560Sstevel@tonic-gate umo->umo_depth = depth; 34570Sstevel@tonic-gate 34580Sstevel@tonic-gate for (i = 0; i < depth; i++) 34590Sstevel@tonic-gate umo->umo_stack[i] = bcp->bc_stack[i]; 34600Sstevel@tonic-gate } 34610Sstevel@tonic-gate 34620Sstevel@tonic-gate /* 34630Sstevel@tonic-gate * When ::umausers is invoked without the -f flag, we simply update our hash 34640Sstevel@tonic-gate * table with the information from each allocated bufctl. 34650Sstevel@tonic-gate */ 34660Sstevel@tonic-gate /*ARGSUSED*/ 34670Sstevel@tonic-gate static int 34680Sstevel@tonic-gate umause1(uintptr_t addr, const umem_bufctl_audit_t *bcp, umusers_t *umu) 34690Sstevel@tonic-gate { 34700Sstevel@tonic-gate const umem_cache_t *cp = umu->umu_cache; 34710Sstevel@tonic-gate 34720Sstevel@tonic-gate umu_add(umu, bcp, cp->cache_bufsize, cp->cache_bufsize); 34730Sstevel@tonic-gate return (WALK_NEXT); 34740Sstevel@tonic-gate } 34750Sstevel@tonic-gate 34760Sstevel@tonic-gate /* 34770Sstevel@tonic-gate * When ::umausers is invoked with the -f flag, we print out the information 34780Sstevel@tonic-gate * for each bufctl as well as updating the hash table. 34790Sstevel@tonic-gate */ 34800Sstevel@tonic-gate static int 34810Sstevel@tonic-gate umause2(uintptr_t addr, const umem_bufctl_audit_t *bcp, umusers_t *umu) 34820Sstevel@tonic-gate { 34830Sstevel@tonic-gate int i, depth = MIN(bcp->bc_depth, umem_stack_depth); 34840Sstevel@tonic-gate const umem_cache_t *cp = umu->umu_cache; 34850Sstevel@tonic-gate 34860Sstevel@tonic-gate mdb_printf("size %d, addr %p, thread %p, cache %s\n", 34870Sstevel@tonic-gate cp->cache_bufsize, addr, bcp->bc_thread, cp->cache_name); 34880Sstevel@tonic-gate 34890Sstevel@tonic-gate for (i = 0; i < depth; i++) 34900Sstevel@tonic-gate mdb_printf("\t %a\n", bcp->bc_stack[i]); 34910Sstevel@tonic-gate 34920Sstevel@tonic-gate umu_add(umu, bcp, cp->cache_bufsize, cp->cache_bufsize); 34930Sstevel@tonic-gate return (WALK_NEXT); 34940Sstevel@tonic-gate } 34950Sstevel@tonic-gate 34960Sstevel@tonic-gate /* 34970Sstevel@tonic-gate * We sort our results by allocation size before printing them. 34980Sstevel@tonic-gate */ 34990Sstevel@tonic-gate static int 35000Sstevel@tonic-gate umownercmp(const void *lp, const void *rp) 35010Sstevel@tonic-gate { 35020Sstevel@tonic-gate const umowner_t *lhs = lp; 35030Sstevel@tonic-gate const umowner_t *rhs = rp; 35040Sstevel@tonic-gate 35050Sstevel@tonic-gate return (rhs->umo_total_size - lhs->umo_total_size); 35060Sstevel@tonic-gate } 35070Sstevel@tonic-gate 35080Sstevel@tonic-gate /* 35090Sstevel@tonic-gate * The main engine of ::umausers is relatively straightforward: First we 35100Sstevel@tonic-gate * accumulate our list of umem_cache_t addresses into the umclist_t. Next we 35110Sstevel@tonic-gate * iterate over the allocated bufctls of each cache in the list. Finally, 35120Sstevel@tonic-gate * we sort and print our results. 35130Sstevel@tonic-gate */ 35140Sstevel@tonic-gate /*ARGSUSED*/ 35150Sstevel@tonic-gate int 35160Sstevel@tonic-gate umausers(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 35170Sstevel@tonic-gate { 35180Sstevel@tonic-gate int mem_threshold = 8192; /* Minimum # bytes for printing */ 35190Sstevel@tonic-gate int cnt_threshold = 100; /* Minimum # blocks for printing */ 35200Sstevel@tonic-gate int audited_caches = 0; /* Number of UMF_AUDIT caches found */ 35210Sstevel@tonic-gate int do_all_caches = 1; /* Do all caches (no arguments) */ 35220Sstevel@tonic-gate int opt_e = FALSE; /* Include "small" users */ 35230Sstevel@tonic-gate int opt_f = FALSE; /* Print stack traces */ 35240Sstevel@tonic-gate 35250Sstevel@tonic-gate mdb_walk_cb_t callback = (mdb_walk_cb_t)umause1; 35260Sstevel@tonic-gate umowner_t *umo, *umoend; 35270Sstevel@tonic-gate int i, oelems; 35280Sstevel@tonic-gate 35290Sstevel@tonic-gate umclist_t umc; 35300Sstevel@tonic-gate umusers_t umu; 35310Sstevel@tonic-gate 35320Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) 35330Sstevel@tonic-gate return (DCMD_USAGE); 35340Sstevel@tonic-gate 35350Sstevel@tonic-gate bzero(&umc, sizeof (umc)); 35360Sstevel@tonic-gate bzero(&umu, sizeof (umu)); 35370Sstevel@tonic-gate 35380Sstevel@tonic-gate while ((i = mdb_getopts(argc, argv, 35390Sstevel@tonic-gate 'e', MDB_OPT_SETBITS, TRUE, &opt_e, 35400Sstevel@tonic-gate 'f', MDB_OPT_SETBITS, TRUE, &opt_f, NULL)) != argc) { 35410Sstevel@tonic-gate 35420Sstevel@tonic-gate argv += i; /* skip past options we just processed */ 35430Sstevel@tonic-gate argc -= i; /* adjust argc */ 35440Sstevel@tonic-gate 35450Sstevel@tonic-gate if (argv->a_type != MDB_TYPE_STRING || *argv->a_un.a_str == '-') 35460Sstevel@tonic-gate return (DCMD_USAGE); 35470Sstevel@tonic-gate 35480Sstevel@tonic-gate oelems = umc.umc_nelems; 35490Sstevel@tonic-gate umc.umc_name = argv->a_un.a_str; 35500Sstevel@tonic-gate (void) mdb_walk("umem_cache", (mdb_walk_cb_t)umc_add, &umc); 35510Sstevel@tonic-gate 35520Sstevel@tonic-gate if (umc.umc_nelems == oelems) { 35530Sstevel@tonic-gate mdb_warn("unknown umem cache: %s\n", umc.umc_name); 35540Sstevel@tonic-gate return (DCMD_ERR); 35550Sstevel@tonic-gate } 35560Sstevel@tonic-gate 35570Sstevel@tonic-gate do_all_caches = 0; 35580Sstevel@tonic-gate argv++; 35590Sstevel@tonic-gate argc--; 35600Sstevel@tonic-gate } 35610Sstevel@tonic-gate 35620Sstevel@tonic-gate if (opt_e) 35630Sstevel@tonic-gate mem_threshold = cnt_threshold = 0; 35640Sstevel@tonic-gate 35650Sstevel@tonic-gate if (opt_f) 35660Sstevel@tonic-gate callback = (mdb_walk_cb_t)umause2; 35670Sstevel@tonic-gate 35680Sstevel@tonic-gate if (do_all_caches) { 35690Sstevel@tonic-gate umc.umc_name = NULL; /* match all cache names */ 35700Sstevel@tonic-gate (void) mdb_walk("umem_cache", (mdb_walk_cb_t)umc_add, &umc); 35710Sstevel@tonic-gate } 35720Sstevel@tonic-gate 35730Sstevel@tonic-gate for (i = 0; i < umc.umc_nelems; i++) { 35740Sstevel@tonic-gate uintptr_t cp = umc.umc_caches[i]; 35750Sstevel@tonic-gate umem_cache_t c; 35760Sstevel@tonic-gate 35770Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), cp) == -1) { 35780Sstevel@tonic-gate mdb_warn("failed to read cache at %p", cp); 35790Sstevel@tonic-gate continue; 35800Sstevel@tonic-gate } 35810Sstevel@tonic-gate 35820Sstevel@tonic-gate if (!(c.cache_flags & UMF_AUDIT)) { 35830Sstevel@tonic-gate if (!do_all_caches) { 35840Sstevel@tonic-gate mdb_warn("UMF_AUDIT is not enabled for %s\n", 35850Sstevel@tonic-gate c.cache_name); 35860Sstevel@tonic-gate } 35870Sstevel@tonic-gate continue; 35880Sstevel@tonic-gate } 35890Sstevel@tonic-gate 35900Sstevel@tonic-gate umu.umu_cache = &c; 35910Sstevel@tonic-gate (void) mdb_pwalk("bufctl", callback, &umu, cp); 35920Sstevel@tonic-gate audited_caches++; 35930Sstevel@tonic-gate } 35940Sstevel@tonic-gate 35950Sstevel@tonic-gate if (audited_caches == 0 && do_all_caches) { 35960Sstevel@tonic-gate mdb_warn("UMF_AUDIT is not enabled for any caches\n"); 35970Sstevel@tonic-gate return (DCMD_ERR); 35980Sstevel@tonic-gate } 35990Sstevel@tonic-gate 36000Sstevel@tonic-gate qsort(umu.umu_hash, umu.umu_nelems, sizeof (umowner_t), umownercmp); 36010Sstevel@tonic-gate umoend = umu.umu_hash + umu.umu_nelems; 36020Sstevel@tonic-gate 36030Sstevel@tonic-gate for (umo = umu.umu_hash; umo < umoend; umo++) { 36040Sstevel@tonic-gate if (umo->umo_total_size < mem_threshold && 36050Sstevel@tonic-gate umo->umo_num < cnt_threshold) 36060Sstevel@tonic-gate continue; 36070Sstevel@tonic-gate mdb_printf("%lu bytes for %u allocations with data size %lu:\n", 36080Sstevel@tonic-gate umo->umo_total_size, umo->umo_num, umo->umo_data_size); 36090Sstevel@tonic-gate for (i = 0; i < umo->umo_depth; i++) 36100Sstevel@tonic-gate mdb_printf("\t %a\n", umo->umo_stack[i]); 36110Sstevel@tonic-gate } 36120Sstevel@tonic-gate 36130Sstevel@tonic-gate return (DCMD_OK); 36140Sstevel@tonic-gate } 3615*1528Sjwadams 3616*1528Sjwadams struct malloc_data { 3617*1528Sjwadams uint32_t malloc_size; 3618*1528Sjwadams uint32_t malloc_stat; /* == UMEM_MALLOC_ENCODE(state, malloc_size) */ 3619*1528Sjwadams }; 3620*1528Sjwadams 3621*1528Sjwadams #ifdef _LP64 3622*1528Sjwadams #define UMI_MAX_BUCKET (UMEM_MAXBUF - 2*sizeof (struct malloc_data)) 3623*1528Sjwadams #else 3624*1528Sjwadams #define UMI_MAX_BUCKET (UMEM_MAXBUF - sizeof (struct malloc_data)) 3625*1528Sjwadams #endif 3626*1528Sjwadams 3627*1528Sjwadams typedef struct umem_malloc_info { 3628*1528Sjwadams size_t um_total; /* total allocated buffers */ 3629*1528Sjwadams size_t um_malloc; /* malloc buffers */ 3630*1528Sjwadams size_t um_malloc_size; /* sum of malloc buffer sizes */ 3631*1528Sjwadams size_t um_malloc_overhead; /* sum of in-chunk overheads */ 3632*1528Sjwadams 3633*1528Sjwadams umem_cache_t *um_cp; 3634*1528Sjwadams 3635*1528Sjwadams uint_t *um_bucket; 3636*1528Sjwadams } umem_malloc_info_t; 3637*1528Sjwadams 3638*1528Sjwadams static const int * 3639*1528Sjwadams dist_linear(int buckets, int beg, int end) 3640*1528Sjwadams { 3641*1528Sjwadams int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC); 3642*1528Sjwadams int pos; 3643*1528Sjwadams int dist = end - beg + 1; 3644*1528Sjwadams 3645*1528Sjwadams for (pos = 0; pos < buckets; pos++) 3646*1528Sjwadams out[pos] = beg + (pos * dist)/buckets; 3647*1528Sjwadams out[buckets] = end + 1; 3648*1528Sjwadams 3649*1528Sjwadams return (out); 3650*1528Sjwadams } 3651*1528Sjwadams 3652*1528Sjwadams /* 3653*1528Sjwadams * We want the bins to be a constant ratio: 3654*1528Sjwadams * 3655*1528Sjwadams * b_0 = beg; 3656*1528Sjwadams * b_idx = b_{idx-1} * r; 3657*1528Sjwadams * b_buckets = end + 1; 3658*1528Sjwadams * 3659*1528Sjwadams * That is: 3660*1528Sjwadams * 3661*1528Sjwadams * buckets 3662*1528Sjwadams * beg * r = end 3663*1528Sjwadams * 3664*1528Sjwadams * Which reduces to: 3665*1528Sjwadams * 3666*1528Sjwadams * buckets ___________________ 3667*1528Sjwadams * r = -------/ ((end + 1) / beg) 3668*1528Sjwadams * 3669*1528Sjwadams * log ((end + 1) / beg) 3670*1528Sjwadams * log r = --------------------- 3671*1528Sjwadams * buckets 3672*1528Sjwadams * 3673*1528Sjwadams * (log ((end + 1) / beg)) / buckets 3674*1528Sjwadams * r = e 3675*1528Sjwadams */ 3676*1528Sjwadams static const int * 3677*1528Sjwadams dist_geometric(int buckets, int beg, int end, int minbucketsize) 3678*1528Sjwadams { 3679*1528Sjwadams #ifdef _KMDB 3680*1528Sjwadams return (dist_linear(buckets, beg, end)); 3681*1528Sjwadams #else 3682*1528Sjwadams int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC); 3683*1528Sjwadams 3684*1528Sjwadams extern double log(double); 3685*1528Sjwadams extern double exp(double); 3686*1528Sjwadams 3687*1528Sjwadams double r; 3688*1528Sjwadams double b; 3689*1528Sjwadams int idx = 0; 3690*1528Sjwadams int last; 3691*1528Sjwadams int begzero; 3692*1528Sjwadams 3693*1528Sjwadams if (minbucketsize == 0) 3694*1528Sjwadams minbucketsize = 1; 3695*1528Sjwadams 3696*1528Sjwadams if (buckets == 1) { 3697*1528Sjwadams out[0] = beg; 3698*1528Sjwadams out[1] = end + 1; 3699*1528Sjwadams return (out); 3700*1528Sjwadams } 3701*1528Sjwadams 3702*1528Sjwadams begzero = (beg == 0); 3703*1528Sjwadams if (begzero) 3704*1528Sjwadams beg = 1; 3705*1528Sjwadams 3706*1528Sjwadams r = exp(log((double)(end + 1) / beg) / buckets); 3707*1528Sjwadams 3708*1528Sjwadams /* 3709*1528Sjwadams * We've now computed r, using the previously derived formula. We 3710*1528Sjwadams * now need to generate the array of bucket bounds. There are 3711*1528Sjwadams * two major variables: 3712*1528Sjwadams * 3713*1528Sjwadams * b holds b_idx, the current index, as a double. 3714*1528Sjwadams * last holds the integer which goes into out[idx] 3715*1528Sjwadams * 3716*1528Sjwadams * Our job is to transform the smooth function b_idx, defined 3717*1528Sjwadams * above, into integer-sized buckets, with a specified minimum 3718*1528Sjwadams * bucket size. Since b_idx is an exponentially growing function, 3719*1528Sjwadams * any inadequate buckets must be at the beginning. To deal 3720*1528Sjwadams * with this, we make buckets of minimum size until b catches up 3721*1528Sjwadams * with last. 3722*1528Sjwadams * 3723*1528Sjwadams * A final wrinkle is that beg *can* be zero. We compute r and b 3724*1528Sjwadams * as if beg was 1, then start last as 0. This can lead to a bit 3725*1528Sjwadams * of oddness around the 0 bucket, but it's mostly reasonable. 3726*1528Sjwadams */ 3727*1528Sjwadams 3728*1528Sjwadams b = last = beg; 3729*1528Sjwadams if (begzero) 3730*1528Sjwadams last = 0; 3731*1528Sjwadams 3732*1528Sjwadams for (idx = 0; idx < buckets; idx++) { 3733*1528Sjwadams int next; 3734*1528Sjwadams 3735*1528Sjwadams out[idx] = last; 3736*1528Sjwadams 3737*1528Sjwadams b *= r; 3738*1528Sjwadams next = (int)b; 3739*1528Sjwadams 3740*1528Sjwadams if (next > last + minbucketsize - 1) 3741*1528Sjwadams last = next; 3742*1528Sjwadams else 3743*1528Sjwadams last += minbucketsize; 3744*1528Sjwadams } 3745*1528Sjwadams out[buckets] = end + 1; 3746*1528Sjwadams 3747*1528Sjwadams return (out); 3748*1528Sjwadams #endif 3749*1528Sjwadams } 3750*1528Sjwadams 3751*1528Sjwadams #define NCHARS 50 3752*1528Sjwadams static void 3753*1528Sjwadams umem_malloc_print_dist(uint_t *um_bucket, size_t minmalloc, size_t maxmalloc, 3754*1528Sjwadams size_t maxbuckets, size_t minbucketsize, int geometric) 3755*1528Sjwadams { 3756*1528Sjwadams size_t um_malloc; 3757*1528Sjwadams int minb = -1; 3758*1528Sjwadams int maxb = -1; 3759*1528Sjwadams int buckets; 3760*1528Sjwadams int nbucks; 3761*1528Sjwadams int i; 3762*1528Sjwadams int n; 3763*1528Sjwadams int b; 3764*1528Sjwadams const char *dist = " Distribution "; 3765*1528Sjwadams char dashes[NCHARS + 1]; 3766*1528Sjwadams const int *distarray; 3767*1528Sjwadams 3768*1528Sjwadams minb = (int)minmalloc; 3769*1528Sjwadams maxb = (int)maxmalloc; 3770*1528Sjwadams 3771*1528Sjwadams nbucks = buckets = maxb - minb + 1; 3772*1528Sjwadams 3773*1528Sjwadams um_malloc = 0; 3774*1528Sjwadams for (b = minb; b <= maxb; b++) 3775*1528Sjwadams um_malloc += um_bucket[b]; 3776*1528Sjwadams if (um_malloc == 0) 3777*1528Sjwadams um_malloc = 1; /* avoid divide-by-zero */ 3778*1528Sjwadams 3779*1528Sjwadams if (maxbuckets != 0) 3780*1528Sjwadams buckets = MIN(buckets, maxbuckets); 3781*1528Sjwadams 3782*1528Sjwadams if (minbucketsize > 1) { 3783*1528Sjwadams buckets = MIN(buckets, nbucks/minbucketsize); 3784*1528Sjwadams if (buckets == 0) { 3785*1528Sjwadams buckets = 1; 3786*1528Sjwadams minbucketsize = nbucks; 3787*1528Sjwadams } 3788*1528Sjwadams } 3789*1528Sjwadams 3790*1528Sjwadams 3791*1528Sjwadams if (geometric) 3792*1528Sjwadams distarray = dist_geometric(buckets, minb, maxb, minbucketsize); 3793*1528Sjwadams else 3794*1528Sjwadams distarray = dist_linear(buckets, minb, maxb); 3795*1528Sjwadams 3796*1528Sjwadams n = (NCHARS - strlen(dist)) / 2; 3797*1528Sjwadams (void) memset(dashes, '-', n); 3798*1528Sjwadams dashes[n] = 0; 3799*1528Sjwadams 3800*1528Sjwadams mdb_printf("%11s %s%s%s %s\n", 3801*1528Sjwadams "malloc size", dashes, dist, dashes, "count"); 3802*1528Sjwadams 3803*1528Sjwadams for (i = 0; i < buckets; i++) { 3804*1528Sjwadams int bb = distarray[i]; 3805*1528Sjwadams int be = distarray[i+1] - 1; 3806*1528Sjwadams uint64_t amount = 0; 3807*1528Sjwadams 3808*1528Sjwadams int nats; 3809*1528Sjwadams char ats[NCHARS + 1], spaces[NCHARS + 1]; 3810*1528Sjwadams char range[40]; 3811*1528Sjwadams 3812*1528Sjwadams for (b = bb; b <= be; b++) 3813*1528Sjwadams amount += um_bucket[b]; 3814*1528Sjwadams 3815*1528Sjwadams nats = (NCHARS * amount)/um_malloc; 3816*1528Sjwadams (void) memset(ats, '@', nats); 3817*1528Sjwadams ats[nats] = 0; 3818*1528Sjwadams (void) memset(spaces, ' ', NCHARS - nats); 3819*1528Sjwadams spaces[NCHARS - nats] = 0; 3820*1528Sjwadams 3821*1528Sjwadams if (bb == be) 3822*1528Sjwadams mdb_snprintf(range, sizeof (range), "%d", bb); 3823*1528Sjwadams else 3824*1528Sjwadams mdb_snprintf(range, sizeof (range), "%d-%d", bb, be); 3825*1528Sjwadams mdb_printf("%11s |%s%s %lld\n", range, ats, spaces, amount); 3826*1528Sjwadams } 3827*1528Sjwadams mdb_printf("\n"); 3828*1528Sjwadams } 3829*1528Sjwadams #undef NCHARS 3830*1528Sjwadams 3831*1528Sjwadams /* 3832*1528Sjwadams * A malloc()ed buffer looks like: 3833*1528Sjwadams * 3834*1528Sjwadams * <----------- mi.malloc_size ---> 3835*1528Sjwadams * <----------- cp.cache_bufsize ------------------> 3836*1528Sjwadams * <----------- cp.cache_chunksize --------------------------------> 3837*1528Sjwadams * +-------+-----------------------+---------------+---------------+ 3838*1528Sjwadams * |/tag///| mallocsz |/round-off/////|/debug info////| 3839*1528Sjwadams * +-------+---------------------------------------+---------------+ 3840*1528Sjwadams * <-- usable space ------> 3841*1528Sjwadams * 3842*1528Sjwadams * mallocsz is the argument to malloc(3C). 3843*1528Sjwadams * mi.malloc_size is the actual size passed to umem_alloc(), which 3844*1528Sjwadams * is rounded up to the smallest available cache size, which is 3845*1528Sjwadams * cache_bufsize. If there is debugging or alignment overhead in 3846*1528Sjwadams * the cache, that is reflected in a larger cache_chunksize. 3847*1528Sjwadams * 3848*1528Sjwadams * The tag at the beginning of the buffer is either 8-bytes or 16-bytes, 3849*1528Sjwadams * depending upon the ISA's alignment requirements. For 32-bit allocations, 3850*1528Sjwadams * it is always a 8-byte tag. For 64-bit allocations larger than 8 bytes, 3851*1528Sjwadams * the tag has 8 bytes of padding before it. 3852*1528Sjwadams * 3853*1528Sjwadams * 32-byte, 64-byte buffers <= 8 bytes: 3854*1528Sjwadams * +-------+-------+--------- ... 3855*1528Sjwadams * |/size//|/stat//| mallocsz ... 3856*1528Sjwadams * +-------+-------+--------- ... 3857*1528Sjwadams * ^ 3858*1528Sjwadams * pointer returned from malloc(3C) 3859*1528Sjwadams * 3860*1528Sjwadams * 64-byte buffers > 8 bytes: 3861*1528Sjwadams * +---------------+-------+-------+--------- ... 3862*1528Sjwadams * |/padding///////|/size//|/stat//| mallocsz ... 3863*1528Sjwadams * +---------------+-------+-------+--------- ... 3864*1528Sjwadams * ^ 3865*1528Sjwadams * pointer returned from malloc(3C) 3866*1528Sjwadams * 3867*1528Sjwadams * The "size" field is "malloc_size", which is mallocsz + the padding. 3868*1528Sjwadams * The "stat" field is derived from malloc_size, and functions as a 3869*1528Sjwadams * validation that this buffer is actually from malloc(3C). 3870*1528Sjwadams */ 3871*1528Sjwadams /*ARGSUSED*/ 3872*1528Sjwadams static int 3873*1528Sjwadams um_umem_buffer_cb(uintptr_t addr, void *buf, umem_malloc_info_t *ump) 3874*1528Sjwadams { 3875*1528Sjwadams struct malloc_data md; 3876*1528Sjwadams size_t m_addr = addr; 3877*1528Sjwadams size_t overhead = sizeof (md); 3878*1528Sjwadams size_t mallocsz; 3879*1528Sjwadams 3880*1528Sjwadams ump->um_total++; 3881*1528Sjwadams 3882*1528Sjwadams #ifdef _LP64 3883*1528Sjwadams if (ump->um_cp->cache_bufsize > UMEM_SECOND_ALIGN) { 3884*1528Sjwadams m_addr += overhead; 3885*1528Sjwadams overhead += sizeof (md); 3886*1528Sjwadams } 3887*1528Sjwadams #endif 3888*1528Sjwadams 3889*1528Sjwadams if (mdb_vread(&md, sizeof (md), m_addr) == -1) { 3890*1528Sjwadams mdb_warn("unable to read malloc header at %p", m_addr); 3891*1528Sjwadams return (WALK_NEXT); 3892*1528Sjwadams } 3893*1528Sjwadams 3894*1528Sjwadams switch (UMEM_MALLOC_DECODE(md.malloc_stat, md.malloc_size)) { 3895*1528Sjwadams case MALLOC_MAGIC: 3896*1528Sjwadams #ifdef _LP64 3897*1528Sjwadams case MALLOC_SECOND_MAGIC: 3898*1528Sjwadams #endif 3899*1528Sjwadams mallocsz = md.malloc_size - overhead; 3900*1528Sjwadams 3901*1528Sjwadams ump->um_malloc++; 3902*1528Sjwadams ump->um_malloc_size += mallocsz; 3903*1528Sjwadams ump->um_malloc_overhead += overhead; 3904*1528Sjwadams 3905*1528Sjwadams /* include round-off and debug overhead */ 3906*1528Sjwadams ump->um_malloc_overhead += 3907*1528Sjwadams ump->um_cp->cache_chunksize - md.malloc_size; 3908*1528Sjwadams 3909*1528Sjwadams if (ump->um_bucket != NULL && mallocsz <= UMI_MAX_BUCKET) 3910*1528Sjwadams ump->um_bucket[mallocsz]++; 3911*1528Sjwadams 3912*1528Sjwadams break; 3913*1528Sjwadams default: 3914*1528Sjwadams break; 3915*1528Sjwadams } 3916*1528Sjwadams 3917*1528Sjwadams return (WALK_NEXT); 3918*1528Sjwadams } 3919*1528Sjwadams 3920*1528Sjwadams int 3921*1528Sjwadams get_umem_alloc_sizes(int **out, size_t *out_num) 3922*1528Sjwadams { 3923*1528Sjwadams GElf_Sym sym; 3924*1528Sjwadams 3925*1528Sjwadams if (umem_lookup_by_name("umem_alloc_sizes", &sym) == -1) { 3926*1528Sjwadams mdb_warn("unable to look up umem_alloc_sizes"); 3927*1528Sjwadams return (-1); 3928*1528Sjwadams } 3929*1528Sjwadams 3930*1528Sjwadams *out = mdb_alloc(sym.st_size, UM_SLEEP | UM_GC); 3931*1528Sjwadams *out_num = sym.st_size / sizeof (int); 3932*1528Sjwadams 3933*1528Sjwadams if (mdb_vread(*out, sym.st_size, sym.st_value) == -1) { 3934*1528Sjwadams mdb_warn("unable to read umem_alloc_sizes (%p)", sym.st_value); 3935*1528Sjwadams *out = NULL; 3936*1528Sjwadams return (-1); 3937*1528Sjwadams } 3938*1528Sjwadams 3939*1528Sjwadams return (0); 3940*1528Sjwadams } 3941*1528Sjwadams 3942*1528Sjwadams 3943*1528Sjwadams static int 3944*1528Sjwadams um_umem_cache_cb(uintptr_t addr, umem_cache_t *cp, umem_malloc_info_t *ump) 3945*1528Sjwadams { 3946*1528Sjwadams if (strncmp(cp->cache_name, "umem_alloc_", strlen("umem_alloc_")) != 0) 3947*1528Sjwadams return (WALK_NEXT); 3948*1528Sjwadams 3949*1528Sjwadams ump->um_cp = cp; 3950*1528Sjwadams 3951*1528Sjwadams if (mdb_pwalk("umem", (mdb_walk_cb_t)um_umem_buffer_cb, ump, addr) == 3952*1528Sjwadams -1) { 3953*1528Sjwadams mdb_warn("can't walk 'umem' for cache %p", addr); 3954*1528Sjwadams return (WALK_ERR); 3955*1528Sjwadams } 3956*1528Sjwadams 3957*1528Sjwadams return (WALK_NEXT); 3958*1528Sjwadams } 3959*1528Sjwadams 3960*1528Sjwadams void 3961*1528Sjwadams umem_malloc_dist_help(void) 3962*1528Sjwadams { 3963*1528Sjwadams mdb_printf("%s\n", 3964*1528Sjwadams "report distribution of outstanding malloc()s"); 3965*1528Sjwadams mdb_dec_indent(2); 3966*1528Sjwadams mdb_printf("%<b>OPTIONS%</b>\n"); 3967*1528Sjwadams mdb_inc_indent(2); 3968*1528Sjwadams mdb_printf("%s", 3969*1528Sjwadams " -b maxbins\n" 3970*1528Sjwadams " Use at most maxbins bins for the data\n" 3971*1528Sjwadams " -B minbinsize\n" 3972*1528Sjwadams " Make the bins at least minbinsize bytes apart\n" 3973*1528Sjwadams " -d dump the raw data out, without binning\n" 3974*1528Sjwadams " -g use geometric binning instead of linear binning\n"); 3975*1528Sjwadams } 3976*1528Sjwadams 3977*1528Sjwadams /*ARGSUSED*/ 3978*1528Sjwadams int 3979*1528Sjwadams umem_malloc_dist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3980*1528Sjwadams { 3981*1528Sjwadams umem_malloc_info_t mi; 3982*1528Sjwadams uint_t geometric = 0; 3983*1528Sjwadams uint_t dump = 0; 3984*1528Sjwadams size_t maxbuckets = 0; 3985*1528Sjwadams size_t minbucketsize = 0; 3986*1528Sjwadams 3987*1528Sjwadams size_t minalloc = 0; 3988*1528Sjwadams size_t maxalloc = UMI_MAX_BUCKET; 3989*1528Sjwadams 3990*1528Sjwadams if (flags & DCMD_ADDRSPEC) 3991*1528Sjwadams return (DCMD_USAGE); 3992*1528Sjwadams 3993*1528Sjwadams if (mdb_getopts(argc, argv, 3994*1528Sjwadams 'd', MDB_OPT_SETBITS, TRUE, &dump, 3995*1528Sjwadams 'g', MDB_OPT_SETBITS, TRUE, &geometric, 3996*1528Sjwadams 'b', MDB_OPT_UINTPTR, &maxbuckets, 3997*1528Sjwadams 'B', MDB_OPT_UINTPTR, &minbucketsize, 3998*1528Sjwadams 0) != argc) 3999*1528Sjwadams return (DCMD_USAGE); 4000*1528Sjwadams 4001*1528Sjwadams bzero(&mi, sizeof (mi)); 4002*1528Sjwadams mi.um_bucket = mdb_zalloc((UMI_MAX_BUCKET + 1) * sizeof (*mi.um_bucket), 4003*1528Sjwadams UM_SLEEP | UM_GC); 4004*1528Sjwadams 4005*1528Sjwadams if (mdb_walk("umem_cache", (mdb_walk_cb_t)um_umem_cache_cb, 4006*1528Sjwadams &mi) == -1) { 4007*1528Sjwadams mdb_warn("unable to walk 'umem_cache'"); 4008*1528Sjwadams return (DCMD_ERR); 4009*1528Sjwadams } 4010*1528Sjwadams 4011*1528Sjwadams if (dump) { 4012*1528Sjwadams int i; 4013*1528Sjwadams for (i = minalloc; i <= maxalloc; i++) 4014*1528Sjwadams mdb_printf("%d\t%d\n", i, mi.um_bucket[i]); 4015*1528Sjwadams 4016*1528Sjwadams return (DCMD_OK); 4017*1528Sjwadams } 4018*1528Sjwadams 4019*1528Sjwadams umem_malloc_print_dist(mi.um_bucket, minalloc, maxalloc, 4020*1528Sjwadams maxbuckets, minbucketsize, geometric); 4021*1528Sjwadams 4022*1528Sjwadams return (DCMD_OK); 4023*1528Sjwadams } 4024*1528Sjwadams 4025*1528Sjwadams void 4026*1528Sjwadams umem_malloc_info_help(void) 4027*1528Sjwadams { 4028*1528Sjwadams mdb_printf("%s\n", 4029*1528Sjwadams "report information about malloc()s by cache. "); 4030*1528Sjwadams mdb_dec_indent(2); 4031*1528Sjwadams mdb_printf("%<b>OPTIONS%</b>\n"); 4032*1528Sjwadams mdb_inc_indent(2); 4033*1528Sjwadams mdb_printf("%s", 4034*1528Sjwadams " -b maxbins\n" 4035*1528Sjwadams " Use at most maxbins bins for the data\n" 4036*1528Sjwadams " -B minbinsize\n" 4037*1528Sjwadams " Make the bins at least minbinsize bytes apart\n" 4038*1528Sjwadams " -d dump the raw distribution data without binning\n" 4039*1528Sjwadams #ifndef _KMDB 4040*1528Sjwadams " -g use geometric binning instead of linear binning\n" 4041*1528Sjwadams #endif 4042*1528Sjwadams ""); 4043*1528Sjwadams } 4044*1528Sjwadams int 4045*1528Sjwadams umem_malloc_info(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 4046*1528Sjwadams { 4047*1528Sjwadams umem_cache_t c; 4048*1528Sjwadams umem_malloc_info_t mi; 4049*1528Sjwadams 4050*1528Sjwadams int skip = 0; 4051*1528Sjwadams 4052*1528Sjwadams size_t maxmalloc; 4053*1528Sjwadams size_t overhead; 4054*1528Sjwadams size_t allocated; 4055*1528Sjwadams size_t avg_malloc; 4056*1528Sjwadams size_t overhead_pct; /* 1000 * overhead_percent */ 4057*1528Sjwadams 4058*1528Sjwadams uint_t verbose = 0; 4059*1528Sjwadams uint_t dump = 0; 4060*1528Sjwadams uint_t geometric = 0; 4061*1528Sjwadams size_t maxbuckets = 0; 4062*1528Sjwadams size_t minbucketsize = 0; 4063*1528Sjwadams 4064*1528Sjwadams int *alloc_sizes; 4065*1528Sjwadams int idx; 4066*1528Sjwadams size_t num; 4067*1528Sjwadams size_t minmalloc; 4068*1528Sjwadams 4069*1528Sjwadams if (mdb_getopts(argc, argv, 4070*1528Sjwadams 'd', MDB_OPT_SETBITS, TRUE, &dump, 4071*1528Sjwadams 'g', MDB_OPT_SETBITS, TRUE, &geometric, 4072*1528Sjwadams 'b', MDB_OPT_UINTPTR, &maxbuckets, 4073*1528Sjwadams 'B', MDB_OPT_UINTPTR, &minbucketsize, 4074*1528Sjwadams 0) != argc) 4075*1528Sjwadams return (DCMD_USAGE); 4076*1528Sjwadams 4077*1528Sjwadams if (dump || geometric || (maxbuckets != 0) || (minbucketsize != 0)) 4078*1528Sjwadams verbose = 1; 4079*1528Sjwadams 4080*1528Sjwadams if (!(flags & DCMD_ADDRSPEC)) { 4081*1528Sjwadams if (mdb_walk_dcmd("umem_cache", "umem_malloc_info", 4082*1528Sjwadams argc, argv) == -1) { 4083*1528Sjwadams mdb_warn("can't walk umem_cache"); 4084*1528Sjwadams return (DCMD_ERR); 4085*1528Sjwadams } 4086*1528Sjwadams return (DCMD_OK); 4087*1528Sjwadams } 4088*1528Sjwadams 4089*1528Sjwadams if (!mdb_vread(&c, sizeof (c), addr)) { 4090*1528Sjwadams mdb_warn("unable to read cache at %p", addr); 4091*1528Sjwadams return (DCMD_ERR); 4092*1528Sjwadams } 4093*1528Sjwadams 4094*1528Sjwadams if (strncmp(c.cache_name, "umem_alloc_", strlen("umem_alloc_")) != 0) { 4095*1528Sjwadams if (!(flags & DCMD_LOOP)) 4096*1528Sjwadams mdb_warn("umem_malloc_info: cache \"%s\" is not used " 4097*1528Sjwadams "by malloc()\n", c.cache_name); 4098*1528Sjwadams skip = 1; 4099*1528Sjwadams } 4100*1528Sjwadams 4101*1528Sjwadams /* 4102*1528Sjwadams * normally, print the header only the first time. In verbose mode, 4103*1528Sjwadams * print the header on every non-skipped buffer 4104*1528Sjwadams */ 4105*1528Sjwadams if ((!verbose && DCMD_HDRSPEC(flags)) || (verbose && !skip)) 4106*1528Sjwadams mdb_printf("%<ul>%-?s %6s %6s %8s %8s %10s %10s %6s%</ul>\n", 4107*1528Sjwadams "CACHE", "BUFSZ", "MAXMAL", 4108*1528Sjwadams "BUFMALLC", "AVG_MAL", "MALLOCED", "OVERHEAD", "%OVER"); 4109*1528Sjwadams 4110*1528Sjwadams if (skip) 4111*1528Sjwadams return (DCMD_OK); 4112*1528Sjwadams 4113*1528Sjwadams maxmalloc = c.cache_bufsize - sizeof (struct malloc_data); 4114*1528Sjwadams #ifdef _LP64 4115*1528Sjwadams if (c.cache_bufsize > UMEM_SECOND_ALIGN) 4116*1528Sjwadams maxmalloc -= sizeof (struct malloc_data); 4117*1528Sjwadams #endif 4118*1528Sjwadams 4119*1528Sjwadams bzero(&mi, sizeof (mi)); 4120*1528Sjwadams mi.um_cp = &c; 4121*1528Sjwadams if (verbose) 4122*1528Sjwadams mi.um_bucket = 4123*1528Sjwadams mdb_zalloc((UMI_MAX_BUCKET + 1) * sizeof (*mi.um_bucket), 4124*1528Sjwadams UM_SLEEP | UM_GC); 4125*1528Sjwadams 4126*1528Sjwadams if (mdb_pwalk("umem", (mdb_walk_cb_t)um_umem_buffer_cb, &mi, addr) == 4127*1528Sjwadams -1) { 4128*1528Sjwadams mdb_warn("can't walk 'umem'"); 4129*1528Sjwadams return (DCMD_ERR); 4130*1528Sjwadams } 4131*1528Sjwadams 4132*1528Sjwadams overhead = mi.um_malloc_overhead; 4133*1528Sjwadams allocated = mi.um_malloc_size; 4134*1528Sjwadams 4135*1528Sjwadams /* do integer round off for the average */ 4136*1528Sjwadams if (mi.um_malloc != 0) 4137*1528Sjwadams avg_malloc = (allocated + (mi.um_malloc - 1)/2) / mi.um_malloc; 4138*1528Sjwadams else 4139*1528Sjwadams avg_malloc = 0; 4140*1528Sjwadams 4141*1528Sjwadams /* 4142*1528Sjwadams * include per-slab overhead 4143*1528Sjwadams * 4144*1528Sjwadams * Each slab in a given cache is the same size, and has the same 4145*1528Sjwadams * number of chunks in it; we read in the first slab on the 4146*1528Sjwadams * slab list to get the number of chunks for all slabs. To 4147*1528Sjwadams * compute the per-slab overhead, we just subtract the chunk usage 4148*1528Sjwadams * from the slabsize: 4149*1528Sjwadams * 4150*1528Sjwadams * +------------+-------+-------+ ... --+-------+-------+-------+ 4151*1528Sjwadams * |////////////| | | ... | |///////|///////| 4152*1528Sjwadams * |////color///| chunk | chunk | ... | chunk |/color/|/slab//| 4153*1528Sjwadams * |////////////| | | ... | |///////|///////| 4154*1528Sjwadams * +------------+-------+-------+ ... --+-------+-------+-------+ 4155*1528Sjwadams * | \_______chunksize * chunks_____/ | 4156*1528Sjwadams * \__________________________slabsize__________________________/ 4157*1528Sjwadams * 4158*1528Sjwadams * For UMF_HASH caches, there is an additional source of overhead; 4159*1528Sjwadams * the external umem_slab_t and per-chunk bufctl structures. We 4160*1528Sjwadams * include those in our per-slab overhead. 4161*1528Sjwadams * 4162*1528Sjwadams * Once we have a number for the per-slab overhead, we estimate 4163*1528Sjwadams * the actual overhead by treating the malloc()ed buffers as if 4164*1528Sjwadams * they were densely packed: 4165*1528Sjwadams * 4166*1528Sjwadams * additional overhead = (# mallocs) * (per-slab) / (chunks); 4167*1528Sjwadams * 4168*1528Sjwadams * carefully ordering the multiply before the divide, to avoid 4169*1528Sjwadams * round-off error. 4170*1528Sjwadams */ 4171*1528Sjwadams if (mi.um_malloc != 0) { 4172*1528Sjwadams umem_slab_t slab; 4173*1528Sjwadams uintptr_t saddr = (uintptr_t)c.cache_nullslab.slab_next; 4174*1528Sjwadams 4175*1528Sjwadams if (mdb_vread(&slab, sizeof (slab), saddr) == -1) { 4176*1528Sjwadams mdb_warn("unable to read slab at %p\n", saddr); 4177*1528Sjwadams } else { 4178*1528Sjwadams long chunks = slab.slab_chunks; 4179*1528Sjwadams if (chunks != 0 && c.cache_chunksize != 0 && 4180*1528Sjwadams chunks <= c.cache_slabsize / c.cache_chunksize) { 4181*1528Sjwadams uintmax_t perslab = 4182*1528Sjwadams c.cache_slabsize - 4183*1528Sjwadams (c.cache_chunksize * chunks); 4184*1528Sjwadams 4185*1528Sjwadams if (c.cache_flags & UMF_HASH) { 4186*1528Sjwadams perslab += sizeof (umem_slab_t) + 4187*1528Sjwadams chunks * 4188*1528Sjwadams ((c.cache_flags & UMF_AUDIT) ? 4189*1528Sjwadams sizeof (umem_bufctl_audit_t) : 4190*1528Sjwadams sizeof (umem_bufctl_t)); 4191*1528Sjwadams } 4192*1528Sjwadams overhead += 4193*1528Sjwadams (perslab * (uintmax_t)mi.um_malloc)/chunks; 4194*1528Sjwadams } else { 4195*1528Sjwadams mdb_warn("invalid #chunks (%d) in slab %p\n", 4196*1528Sjwadams chunks, saddr); 4197*1528Sjwadams } 4198*1528Sjwadams } 4199*1528Sjwadams } 4200*1528Sjwadams 4201*1528Sjwadams if (allocated != 0) 4202*1528Sjwadams overhead_pct = (1000ULL * overhead) / allocated; 4203*1528Sjwadams else 4204*1528Sjwadams overhead_pct = 0; 4205*1528Sjwadams 4206*1528Sjwadams mdb_printf("%0?p %6ld %6ld %8ld %8ld %10ld %10ld %3ld.%01ld%%\n", 4207*1528Sjwadams addr, c.cache_bufsize, maxmalloc, 4208*1528Sjwadams mi.um_malloc, avg_malloc, allocated, overhead, 4209*1528Sjwadams overhead_pct / 10, overhead_pct % 10); 4210*1528Sjwadams 4211*1528Sjwadams if (!verbose) 4212*1528Sjwadams return (DCMD_OK); 4213*1528Sjwadams 4214*1528Sjwadams if (!dump) 4215*1528Sjwadams mdb_printf("\n"); 4216*1528Sjwadams 4217*1528Sjwadams if (get_umem_alloc_sizes(&alloc_sizes, &num) == -1) 4218*1528Sjwadams return (DCMD_ERR); 4219*1528Sjwadams 4220*1528Sjwadams for (idx = 0; idx < num; idx++) { 4221*1528Sjwadams if (alloc_sizes[idx] == c.cache_bufsize) 4222*1528Sjwadams break; 4223*1528Sjwadams if (alloc_sizes[idx] == 0) { 4224*1528Sjwadams idx = num; /* 0-terminated array */ 4225*1528Sjwadams break; 4226*1528Sjwadams } 4227*1528Sjwadams } 4228*1528Sjwadams if (idx == num) { 4229*1528Sjwadams mdb_warn( 4230*1528Sjwadams "cache %p's size (%d) not in umem_alloc_sizes\n", 4231*1528Sjwadams addr, c.cache_bufsize); 4232*1528Sjwadams return (DCMD_ERR); 4233*1528Sjwadams } 4234*1528Sjwadams 4235*1528Sjwadams minmalloc = (idx == 0)? 0 : alloc_sizes[idx - 1]; 4236*1528Sjwadams if (minmalloc > 0) { 4237*1528Sjwadams #ifdef _LP64 4238*1528Sjwadams if (minmalloc > UMEM_SECOND_ALIGN) 4239*1528Sjwadams minmalloc -= sizeof (struct malloc_data); 4240*1528Sjwadams #endif 4241*1528Sjwadams minmalloc -= sizeof (struct malloc_data); 4242*1528Sjwadams minmalloc += 1; 4243*1528Sjwadams } 4244*1528Sjwadams 4245*1528Sjwadams if (dump) { 4246*1528Sjwadams for (idx = minmalloc; idx <= maxmalloc; idx++) 4247*1528Sjwadams mdb_printf("%d\t%d\n", idx, mi.um_bucket[idx]); 4248*1528Sjwadams mdb_printf("\n"); 4249*1528Sjwadams } else { 4250*1528Sjwadams umem_malloc_print_dist(mi.um_bucket, minmalloc, maxmalloc, 4251*1528Sjwadams maxbuckets, minbucketsize, geometric); 4252*1528Sjwadams } 4253*1528Sjwadams 4254*1528Sjwadams return (DCMD_OK); 4255*1528Sjwadams } 4256