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 51528Sjwadams * Common Development and Distribution License (the "License"). 61528Sjwadams * 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 /* 224688Stomee * Copyright 2007 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 <mdb/mdb_param.h> 290Sstevel@tonic-gate #include <mdb/mdb_modapi.h> 300Sstevel@tonic-gate #include <mdb/mdb_ctf.h> 310Sstevel@tonic-gate #include <sys/cpuvar.h> 320Sstevel@tonic-gate #include <sys/kmem_impl.h> 330Sstevel@tonic-gate #include <sys/vmem_impl.h> 340Sstevel@tonic-gate #include <sys/machelf.h> 350Sstevel@tonic-gate #include <sys/modctl.h> 360Sstevel@tonic-gate #include <sys/kobj.h> 370Sstevel@tonic-gate #include <sys/panic.h> 380Sstevel@tonic-gate #include <sys/stack.h> 390Sstevel@tonic-gate #include <sys/sysmacros.h> 400Sstevel@tonic-gate #include <vm/page.h> 410Sstevel@tonic-gate 42*4798Stomee #include "dist.h" 430Sstevel@tonic-gate #include "kmem.h" 441528Sjwadams #include "leaky.h" 450Sstevel@tonic-gate 460Sstevel@tonic-gate #define dprintf(x) if (mdb_debug_level) { \ 470Sstevel@tonic-gate mdb_printf("kmem debug: "); \ 480Sstevel@tonic-gate /*CSTYLED*/\ 490Sstevel@tonic-gate mdb_printf x ;\ 500Sstevel@tonic-gate } 510Sstevel@tonic-gate 520Sstevel@tonic-gate #define KM_ALLOCATED 0x01 530Sstevel@tonic-gate #define KM_FREE 0x02 540Sstevel@tonic-gate #define KM_BUFCTL 0x04 550Sstevel@tonic-gate #define KM_CONSTRUCTED 0x08 /* only constructed free buffers */ 560Sstevel@tonic-gate #define KM_HASH 0x10 570Sstevel@tonic-gate 580Sstevel@tonic-gate static int mdb_debug_level = 0; 590Sstevel@tonic-gate 600Sstevel@tonic-gate /*ARGSUSED*/ 610Sstevel@tonic-gate static int 620Sstevel@tonic-gate kmem_init_walkers(uintptr_t addr, const kmem_cache_t *c, void *ignored) 630Sstevel@tonic-gate { 640Sstevel@tonic-gate mdb_walker_t w; 650Sstevel@tonic-gate char descr[64]; 660Sstevel@tonic-gate 670Sstevel@tonic-gate (void) mdb_snprintf(descr, sizeof (descr), 680Sstevel@tonic-gate "walk the %s cache", c->cache_name); 690Sstevel@tonic-gate 700Sstevel@tonic-gate w.walk_name = c->cache_name; 710Sstevel@tonic-gate w.walk_descr = descr; 720Sstevel@tonic-gate w.walk_init = kmem_walk_init; 730Sstevel@tonic-gate w.walk_step = kmem_walk_step; 740Sstevel@tonic-gate w.walk_fini = kmem_walk_fini; 750Sstevel@tonic-gate w.walk_init_arg = (void *)addr; 760Sstevel@tonic-gate 770Sstevel@tonic-gate if (mdb_add_walker(&w) == -1) 780Sstevel@tonic-gate mdb_warn("failed to add %s walker", c->cache_name); 790Sstevel@tonic-gate 800Sstevel@tonic-gate return (WALK_NEXT); 810Sstevel@tonic-gate } 820Sstevel@tonic-gate 830Sstevel@tonic-gate /*ARGSUSED*/ 840Sstevel@tonic-gate int 850Sstevel@tonic-gate kmem_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 860Sstevel@tonic-gate { 870Sstevel@tonic-gate mdb_debug_level ^= 1; 880Sstevel@tonic-gate 890Sstevel@tonic-gate mdb_printf("kmem: debugging is now %s\n", 900Sstevel@tonic-gate mdb_debug_level ? "on" : "off"); 910Sstevel@tonic-gate 920Sstevel@tonic-gate return (DCMD_OK); 930Sstevel@tonic-gate } 940Sstevel@tonic-gate 950Sstevel@tonic-gate typedef struct { 960Sstevel@tonic-gate uintptr_t kcw_first; 970Sstevel@tonic-gate uintptr_t kcw_current; 980Sstevel@tonic-gate } kmem_cache_walk_t; 990Sstevel@tonic-gate 1000Sstevel@tonic-gate int 1010Sstevel@tonic-gate kmem_cache_walk_init(mdb_walk_state_t *wsp) 1020Sstevel@tonic-gate { 1030Sstevel@tonic-gate kmem_cache_walk_t *kcw; 1040Sstevel@tonic-gate kmem_cache_t c; 1050Sstevel@tonic-gate uintptr_t cp; 1060Sstevel@tonic-gate GElf_Sym sym; 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate if (mdb_lookup_by_name("kmem_null_cache", &sym) == -1) { 1090Sstevel@tonic-gate mdb_warn("couldn't find kmem_null_cache"); 1100Sstevel@tonic-gate return (WALK_ERR); 1110Sstevel@tonic-gate } 1120Sstevel@tonic-gate 1130Sstevel@tonic-gate cp = (uintptr_t)sym.st_value; 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate if (mdb_vread(&c, sizeof (kmem_cache_t), cp) == -1) { 1160Sstevel@tonic-gate mdb_warn("couldn't read cache at %p", cp); 1170Sstevel@tonic-gate return (WALK_ERR); 1180Sstevel@tonic-gate } 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate kcw = mdb_alloc(sizeof (kmem_cache_walk_t), UM_SLEEP); 1210Sstevel@tonic-gate 1220Sstevel@tonic-gate kcw->kcw_first = cp; 1230Sstevel@tonic-gate kcw->kcw_current = (uintptr_t)c.cache_next; 1240Sstevel@tonic-gate wsp->walk_data = kcw; 1250Sstevel@tonic-gate 1260Sstevel@tonic-gate return (WALK_NEXT); 1270Sstevel@tonic-gate } 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate int 1300Sstevel@tonic-gate kmem_cache_walk_step(mdb_walk_state_t *wsp) 1310Sstevel@tonic-gate { 1320Sstevel@tonic-gate kmem_cache_walk_t *kcw = wsp->walk_data; 1330Sstevel@tonic-gate kmem_cache_t c; 1340Sstevel@tonic-gate int status; 1350Sstevel@tonic-gate 1360Sstevel@tonic-gate if (mdb_vread(&c, sizeof (kmem_cache_t), kcw->kcw_current) == -1) { 1370Sstevel@tonic-gate mdb_warn("couldn't read cache at %p", kcw->kcw_current); 1380Sstevel@tonic-gate return (WALK_DONE); 1390Sstevel@tonic-gate } 1400Sstevel@tonic-gate 1410Sstevel@tonic-gate status = wsp->walk_callback(kcw->kcw_current, &c, wsp->walk_cbdata); 1420Sstevel@tonic-gate 1430Sstevel@tonic-gate if ((kcw->kcw_current = (uintptr_t)c.cache_next) == kcw->kcw_first) 1440Sstevel@tonic-gate return (WALK_DONE); 1450Sstevel@tonic-gate 1460Sstevel@tonic-gate return (status); 1470Sstevel@tonic-gate } 1480Sstevel@tonic-gate 1490Sstevel@tonic-gate void 1500Sstevel@tonic-gate kmem_cache_walk_fini(mdb_walk_state_t *wsp) 1510Sstevel@tonic-gate { 1520Sstevel@tonic-gate kmem_cache_walk_t *kcw = wsp->walk_data; 1530Sstevel@tonic-gate mdb_free(kcw, sizeof (kmem_cache_walk_t)); 1540Sstevel@tonic-gate } 1550Sstevel@tonic-gate 1560Sstevel@tonic-gate int 1570Sstevel@tonic-gate kmem_cpu_cache_walk_init(mdb_walk_state_t *wsp) 1580Sstevel@tonic-gate { 1590Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 1600Sstevel@tonic-gate mdb_warn("kmem_cpu_cache doesn't support global walks"); 1610Sstevel@tonic-gate return (WALK_ERR); 1620Sstevel@tonic-gate } 1630Sstevel@tonic-gate 1640Sstevel@tonic-gate if (mdb_layered_walk("cpu", wsp) == -1) { 1650Sstevel@tonic-gate mdb_warn("couldn't walk 'cpu'"); 1660Sstevel@tonic-gate return (WALK_ERR); 1670Sstevel@tonic-gate } 1680Sstevel@tonic-gate 1690Sstevel@tonic-gate wsp->walk_data = (void *)wsp->walk_addr; 1700Sstevel@tonic-gate 1710Sstevel@tonic-gate return (WALK_NEXT); 1720Sstevel@tonic-gate } 1730Sstevel@tonic-gate 1740Sstevel@tonic-gate int 1750Sstevel@tonic-gate kmem_cpu_cache_walk_step(mdb_walk_state_t *wsp) 1760Sstevel@tonic-gate { 1770Sstevel@tonic-gate uintptr_t caddr = (uintptr_t)wsp->walk_data; 1780Sstevel@tonic-gate const cpu_t *cpu = wsp->walk_layer; 1790Sstevel@tonic-gate kmem_cpu_cache_t cc; 1800Sstevel@tonic-gate 1810Sstevel@tonic-gate caddr += cpu->cpu_cache_offset; 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate if (mdb_vread(&cc, sizeof (kmem_cpu_cache_t), caddr) == -1) { 1840Sstevel@tonic-gate mdb_warn("couldn't read kmem_cpu_cache at %p", caddr); 1850Sstevel@tonic-gate return (WALK_ERR); 1860Sstevel@tonic-gate } 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate return (wsp->walk_callback(caddr, &cc, wsp->walk_cbdata)); 1890Sstevel@tonic-gate } 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate int 1920Sstevel@tonic-gate kmem_slab_walk_init(mdb_walk_state_t *wsp) 1930Sstevel@tonic-gate { 1940Sstevel@tonic-gate uintptr_t caddr = wsp->walk_addr; 1950Sstevel@tonic-gate kmem_cache_t c; 1960Sstevel@tonic-gate 1970Sstevel@tonic-gate if (caddr == NULL) { 1980Sstevel@tonic-gate mdb_warn("kmem_slab doesn't support global walks\n"); 1990Sstevel@tonic-gate return (WALK_ERR); 2000Sstevel@tonic-gate } 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), caddr) == -1) { 2030Sstevel@tonic-gate mdb_warn("couldn't read kmem_cache at %p", caddr); 2040Sstevel@tonic-gate return (WALK_ERR); 2050Sstevel@tonic-gate } 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate wsp->walk_data = 2080Sstevel@tonic-gate (void *)(caddr + offsetof(kmem_cache_t, cache_nullslab)); 2090Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_next; 2100Sstevel@tonic-gate 2110Sstevel@tonic-gate return (WALK_NEXT); 2120Sstevel@tonic-gate } 2130Sstevel@tonic-gate 2140Sstevel@tonic-gate int 2150Sstevel@tonic-gate kmem_slab_walk_partial_init(mdb_walk_state_t *wsp) 2160Sstevel@tonic-gate { 2170Sstevel@tonic-gate uintptr_t caddr = wsp->walk_addr; 2180Sstevel@tonic-gate kmem_cache_t c; 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate if (caddr == NULL) { 2210Sstevel@tonic-gate mdb_warn("kmem_slab_partial doesn't support global walks\n"); 2220Sstevel@tonic-gate return (WALK_ERR); 2230Sstevel@tonic-gate } 2240Sstevel@tonic-gate 2250Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), caddr) == -1) { 2260Sstevel@tonic-gate mdb_warn("couldn't read kmem_cache at %p", caddr); 2270Sstevel@tonic-gate return (WALK_ERR); 2280Sstevel@tonic-gate } 2290Sstevel@tonic-gate 2300Sstevel@tonic-gate wsp->walk_data = 2310Sstevel@tonic-gate (void *)(caddr + offsetof(kmem_cache_t, cache_nullslab)); 2320Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)c.cache_freelist; 2330Sstevel@tonic-gate 2340Sstevel@tonic-gate /* 2350Sstevel@tonic-gate * Some consumers (umem_walk_step(), in particular) require at 2360Sstevel@tonic-gate * least one callback if there are any buffers in the cache. So 2370Sstevel@tonic-gate * if there are *no* partial slabs, report the last full slab, if 2380Sstevel@tonic-gate * any. 2390Sstevel@tonic-gate * 2400Sstevel@tonic-gate * Yes, this is ugly, but it's cleaner than the other possibilities. 2410Sstevel@tonic-gate */ 2420Sstevel@tonic-gate if ((uintptr_t)wsp->walk_data == wsp->walk_addr) 2430Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_prev; 2440Sstevel@tonic-gate 2450Sstevel@tonic-gate return (WALK_NEXT); 2460Sstevel@tonic-gate } 2470Sstevel@tonic-gate 2480Sstevel@tonic-gate int 2490Sstevel@tonic-gate kmem_slab_walk_step(mdb_walk_state_t *wsp) 2500Sstevel@tonic-gate { 2510Sstevel@tonic-gate kmem_slab_t s; 2520Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 2530Sstevel@tonic-gate uintptr_t saddr = (uintptr_t)wsp->walk_data; 2540Sstevel@tonic-gate uintptr_t caddr = saddr - offsetof(kmem_cache_t, cache_nullslab); 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate if (addr == saddr) 2570Sstevel@tonic-gate return (WALK_DONE); 2580Sstevel@tonic-gate 2590Sstevel@tonic-gate if (mdb_vread(&s, sizeof (s), addr) == -1) { 2600Sstevel@tonic-gate mdb_warn("failed to read slab at %p", wsp->walk_addr); 2610Sstevel@tonic-gate return (WALK_ERR); 2620Sstevel@tonic-gate } 2630Sstevel@tonic-gate 2640Sstevel@tonic-gate if ((uintptr_t)s.slab_cache != caddr) { 2650Sstevel@tonic-gate mdb_warn("slab %p isn't in cache %p (in cache %p)\n", 2660Sstevel@tonic-gate addr, caddr, s.slab_cache); 2670Sstevel@tonic-gate return (WALK_ERR); 2680Sstevel@tonic-gate } 2690Sstevel@tonic-gate 2700Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)s.slab_next; 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate return (wsp->walk_callback(addr, &s, wsp->walk_cbdata)); 2730Sstevel@tonic-gate } 2740Sstevel@tonic-gate 2750Sstevel@tonic-gate int 2760Sstevel@tonic-gate kmem_cache(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv) 2770Sstevel@tonic-gate { 2780Sstevel@tonic-gate kmem_cache_t c; 2790Sstevel@tonic-gate 2800Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 2810Sstevel@tonic-gate if (mdb_walk_dcmd("kmem_cache", "kmem_cache", ac, argv) == -1) { 2820Sstevel@tonic-gate mdb_warn("can't walk kmem_cache"); 2830Sstevel@tonic-gate return (DCMD_ERR); 2840Sstevel@tonic-gate } 2850Sstevel@tonic-gate return (DCMD_OK); 2860Sstevel@tonic-gate } 2870Sstevel@tonic-gate 2880Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) 2890Sstevel@tonic-gate mdb_printf("%-?s %-25s %4s %6s %8s %8s\n", "ADDR", "NAME", 2900Sstevel@tonic-gate "FLAG", "CFLAG", "BUFSIZE", "BUFTOTL"); 2910Sstevel@tonic-gate 2920Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), addr) == -1) { 2930Sstevel@tonic-gate mdb_warn("couldn't read kmem_cache at %p", addr); 2940Sstevel@tonic-gate return (DCMD_ERR); 2950Sstevel@tonic-gate } 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate mdb_printf("%0?p %-25s %04x %06x %8ld %8lld\n", addr, c.cache_name, 2980Sstevel@tonic-gate c.cache_flags, c.cache_cflags, c.cache_bufsize, c.cache_buftotal); 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate return (DCMD_OK); 3010Sstevel@tonic-gate } 3020Sstevel@tonic-gate 3034688Stomee typedef struct kmem_slab_usage { 3044688Stomee int ksu_refcnt; /* count of allocated buffers on slab */ 3054688Stomee } kmem_slab_usage_t; 3064688Stomee 3074688Stomee typedef struct kmem_slab_stats { 3084688Stomee int ks_slabs; /* slabs in cache */ 3094688Stomee int ks_partial_slabs; /* partially allocated slabs in cache */ 3104688Stomee uint64_t ks_unused_buffers; /* total unused buffers in cache */ 3114688Stomee int ks_buffers_per_slab; /* buffers per slab */ 3124688Stomee int ks_usage_len; /* ks_usage array length */ 3134688Stomee kmem_slab_usage_t *ks_usage; /* partial slab usage */ 3144688Stomee uint_t *ks_bucket; /* slab usage distribution */ 3154688Stomee } kmem_slab_stats_t; 3164688Stomee 3174688Stomee #define LABEL_WIDTH 11 3184688Stomee static void 3194688Stomee kmem_slabs_print_dist(uint_t *ks_bucket, size_t buffers_per_slab, 3204688Stomee size_t maxbuckets, size_t minbucketsize) 3214688Stomee { 3224688Stomee uint64_t total; 3234688Stomee int buckets; 3244688Stomee int i; 3254688Stomee const int *distarray; 3264688Stomee int complete[2]; 3274688Stomee 3284688Stomee buckets = buffers_per_slab; 3294688Stomee 3304688Stomee total = 0; 3314688Stomee for (i = 0; i <= buffers_per_slab; i++) 3324688Stomee total += ks_bucket[i]; 3334688Stomee 3344688Stomee if (maxbuckets > 1) 3354688Stomee buckets = MIN(buckets, maxbuckets); 3364688Stomee 3374688Stomee if (minbucketsize > 1) { 3384688Stomee /* 3394688Stomee * minbucketsize does not apply to the first bucket reserved 3404688Stomee * for completely allocated slabs 3414688Stomee */ 3424688Stomee buckets = MIN(buckets, 1 + ((buffers_per_slab - 1) / 3434688Stomee minbucketsize)); 3444688Stomee if ((buckets < 2) && (buffers_per_slab > 1)) { 3454688Stomee buckets = 2; 3464688Stomee minbucketsize = (buffers_per_slab - 1); 3474688Stomee } 3484688Stomee } 3494688Stomee 3504688Stomee /* 3514688Stomee * The first printed bucket is reserved for completely allocated slabs. 3524688Stomee * Passing (buckets - 1) excludes that bucket from the generated 3534688Stomee * distribution, since we're handling it as a special case. 3544688Stomee */ 3554688Stomee complete[0] = buffers_per_slab; 3564688Stomee complete[1] = buffers_per_slab + 1; 357*4798Stomee distarray = dist_linear(buckets - 1, 1, buffers_per_slab - 1); 3584688Stomee 3594688Stomee mdb_printf("%*s\n", LABEL_WIDTH, "Allocated"); 360*4798Stomee dist_print_header("Buffers", LABEL_WIDTH, "Slabs"); 361*4798Stomee 362*4798Stomee dist_print_bucket(complete, 0, ks_bucket, total, LABEL_WIDTH); 3634688Stomee /* 3644688Stomee * Print bucket ranges in descending order after the first bucket for 3654688Stomee * completely allocated slabs, so a person can see immediately whether 3664688Stomee * or not there is fragmentation without having to scan possibly 3674688Stomee * multiple screens of output. Starting at (buckets - 2) excludes the 3684688Stomee * extra terminating bucket. 3694688Stomee */ 3704688Stomee for (i = buckets - 2; i >= 0; i--) { 371*4798Stomee dist_print_bucket(distarray, i, ks_bucket, total, LABEL_WIDTH); 3724688Stomee } 3734688Stomee mdb_printf("\n"); 3744688Stomee } 3754688Stomee #undef LABEL_WIDTH 3764688Stomee 3774688Stomee /*ARGSUSED*/ 3784688Stomee static int 3794688Stomee kmem_first_slab(uintptr_t addr, const kmem_slab_t *sp, boolean_t *is_slab) 3804688Stomee { 3814688Stomee *is_slab = B_TRUE; 3824688Stomee return (WALK_DONE); 3834688Stomee } 3844688Stomee 3854688Stomee /*ARGSUSED*/ 3864688Stomee static int 3874688Stomee kmem_first_partial_slab(uintptr_t addr, const kmem_slab_t *sp, 3884688Stomee boolean_t *is_slab) 3894688Stomee { 3904688Stomee /* 3914688Stomee * The "kmem_partial_slab" walker reports the last full slab if there 3924688Stomee * are no partial slabs (for the sake of consumers that require at least 3934688Stomee * one callback if there are any buffers in the cache). 3944688Stomee */ 3954688Stomee *is_slab = ((sp->slab_refcnt > 0) && 3964688Stomee (sp->slab_refcnt < sp->slab_chunks)); 3974688Stomee return (WALK_DONE); 3984688Stomee } 3994688Stomee 4004688Stomee /*ARGSUSED*/ 4014688Stomee static int 4024688Stomee kmem_slablist_stat(uintptr_t addr, const kmem_slab_t *sp, 4034688Stomee kmem_slab_stats_t *ks) 4044688Stomee { 4054688Stomee kmem_slab_usage_t *ksu; 4064688Stomee long unused; 4074688Stomee 4084688Stomee ks->ks_slabs++; 4094688Stomee if (ks->ks_buffers_per_slab == 0) { 4104688Stomee ks->ks_buffers_per_slab = sp->slab_chunks; 4114688Stomee /* +1 to include a zero bucket */ 4124688Stomee ks->ks_bucket = mdb_zalloc((ks->ks_buffers_per_slab + 1) * 4134688Stomee sizeof (*ks->ks_bucket), UM_SLEEP | UM_GC); 4144688Stomee } 4154688Stomee ks->ks_bucket[sp->slab_refcnt]++; 4164688Stomee 4174688Stomee unused = (sp->slab_chunks - sp->slab_refcnt); 4184688Stomee if (unused == 0) { 4194688Stomee return (WALK_NEXT); 4204688Stomee } 4214688Stomee 4224688Stomee ks->ks_partial_slabs++; 4234688Stomee ks->ks_unused_buffers += unused; 4244688Stomee 4254688Stomee if (ks->ks_partial_slabs > ks->ks_usage_len) { 4264688Stomee kmem_slab_usage_t *usage; 4274688Stomee int len = ks->ks_usage_len; 4284688Stomee 4294688Stomee len = (len == 0 ? 16 : len * 2); 4304688Stomee usage = mdb_zalloc(len * sizeof (kmem_slab_usage_t), UM_SLEEP); 4314688Stomee if (ks->ks_usage != NULL) { 4324688Stomee bcopy(ks->ks_usage, usage, 4334688Stomee ks->ks_usage_len * sizeof (kmem_slab_usage_t)); 4344688Stomee mdb_free(ks->ks_usage, 4354688Stomee ks->ks_usage_len * sizeof (kmem_slab_usage_t)); 4364688Stomee } 4374688Stomee ks->ks_usage = usage; 4384688Stomee ks->ks_usage_len = len; 4394688Stomee } 4404688Stomee 4414688Stomee ksu = &ks->ks_usage[ks->ks_partial_slabs - 1]; 4424688Stomee ksu->ksu_refcnt = sp->slab_refcnt; 4434688Stomee return (WALK_NEXT); 4444688Stomee } 4454688Stomee 4464688Stomee static void 4474688Stomee kmem_slabs_header() 4484688Stomee { 4494688Stomee mdb_printf("%-25s %8s %8s %9s %9s %6s\n", 4504688Stomee "", "", "Partial", "", "Unused", ""); 4514688Stomee mdb_printf("%-25s %8s %8s %9s %9s %6s\n", 4524688Stomee "Cache Name", "Slabs", "Slabs", "Buffers", "Buffers", "Waste"); 4534688Stomee mdb_printf("%-25s %8s %8s %9s %9s %6s\n", 4544688Stomee "-------------------------", "--------", "--------", "---------", 4554688Stomee "---------", "------"); 4564688Stomee } 4574688Stomee 4584688Stomee int 4594688Stomee kmem_slabs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 4604688Stomee { 4614688Stomee kmem_cache_t c; 4624688Stomee kmem_slab_stats_t stats; 4634688Stomee mdb_walk_cb_t cb; 4644688Stomee int pct; 4654688Stomee int tenths_pct; 4664688Stomee size_t maxbuckets = 1; 4674688Stomee size_t minbucketsize = 0; 4684688Stomee const char *filter = NULL; 4694688Stomee uint_t opt_v = FALSE; 4704688Stomee boolean_t verbose = B_FALSE; 4714688Stomee boolean_t skip = B_FALSE; 4724688Stomee 4734688Stomee if (mdb_getopts(argc, argv, 4744688Stomee 'B', MDB_OPT_UINTPTR, &minbucketsize, 4754688Stomee 'b', MDB_OPT_UINTPTR, &maxbuckets, 4764688Stomee 'n', MDB_OPT_STR, &filter, 4774688Stomee 'v', MDB_OPT_SETBITS, TRUE, &opt_v, 4784688Stomee NULL) != argc) { 4794688Stomee return (DCMD_USAGE); 4804688Stomee } 4814688Stomee 4824688Stomee if (opt_v || (maxbuckets != 1) || (minbucketsize != 0)) { 4834688Stomee verbose = 1; 4844688Stomee } 4854688Stomee 4864688Stomee if (!(flags & DCMD_ADDRSPEC)) { 4874688Stomee if (mdb_walk_dcmd("kmem_cache", "kmem_slabs", argc, 4884688Stomee argv) == -1) { 4894688Stomee mdb_warn("can't walk kmem_cache"); 4904688Stomee return (DCMD_ERR); 4914688Stomee } 4924688Stomee return (DCMD_OK); 4934688Stomee } 4944688Stomee 4954688Stomee if (mdb_vread(&c, sizeof (c), addr) == -1) { 4964688Stomee mdb_warn("couldn't read kmem_cache at %p", addr); 4974688Stomee return (DCMD_ERR); 4984688Stomee } 4994688Stomee 5004688Stomee if ((filter != NULL) && (strstr(c.cache_name, filter) == NULL)) { 5014688Stomee skip = B_TRUE; 5024688Stomee } 5034688Stomee 5044688Stomee if (!verbose && DCMD_HDRSPEC(flags)) { 5054688Stomee kmem_slabs_header(); 5064688Stomee } else if (verbose && !skip) { 5074688Stomee if (DCMD_HDRSPEC(flags)) { 5084688Stomee kmem_slabs_header(); 5094688Stomee } else { 5104688Stomee boolean_t is_slab = B_FALSE; 5114688Stomee const char *walker_name; 5124688Stomee if (opt_v) { 5134688Stomee cb = (mdb_walk_cb_t)kmem_first_partial_slab; 5144688Stomee walker_name = "kmem_slab_partial"; 5154688Stomee } else { 5164688Stomee cb = (mdb_walk_cb_t)kmem_first_slab; 5174688Stomee walker_name = "kmem_slab"; 5184688Stomee } 5194688Stomee (void) mdb_pwalk(walker_name, cb, &is_slab, addr); 5204688Stomee if (is_slab) { 5214688Stomee kmem_slabs_header(); 5224688Stomee } 5234688Stomee } 5244688Stomee } 5254688Stomee 5264688Stomee if (skip) { 5274688Stomee return (DCMD_OK); 5284688Stomee } 5294688Stomee 5304688Stomee bzero(&stats, sizeof (kmem_slab_stats_t)); 5314688Stomee cb = (mdb_walk_cb_t)kmem_slablist_stat; 5324688Stomee (void) mdb_pwalk("kmem_slab", cb, &stats, addr); 5334688Stomee 5344688Stomee if (c.cache_buftotal == 0) { 5354688Stomee pct = 0; 5364688Stomee tenths_pct = 0; 5374688Stomee } else { 5384688Stomee uint64_t n = stats.ks_unused_buffers * 10000; 5394688Stomee pct = (int)(n / c.cache_buftotal); 5404688Stomee tenths_pct = pct - ((pct / 100) * 100); 5414688Stomee tenths_pct = (tenths_pct + 5) / 10; /* round nearest tenth */ 5424688Stomee if (tenths_pct == 10) { 5434688Stomee pct += 100; 5444688Stomee tenths_pct = 0; 5454688Stomee } 5464688Stomee } 5474688Stomee 5484688Stomee pct /= 100; 5494688Stomee mdb_printf("%-25s %8d %8d %9lld %9lld %3d.%1d%%\n", c.cache_name, 5504688Stomee stats.ks_slabs, stats.ks_partial_slabs, c.cache_buftotal, 5514688Stomee stats.ks_unused_buffers, pct, tenths_pct); 5524688Stomee 5534688Stomee if (!verbose) { 5544688Stomee return (DCMD_OK); 5554688Stomee } 5564688Stomee 5574688Stomee if (maxbuckets == 0) { 5584688Stomee maxbuckets = stats.ks_buffers_per_slab; 5594688Stomee } 5604688Stomee 5614688Stomee if (((maxbuckets > 1) || (minbucketsize > 0)) && 5624688Stomee (stats.ks_slabs > 0)) { 5634688Stomee mdb_printf("\n"); 5644688Stomee kmem_slabs_print_dist(stats.ks_bucket, 5654688Stomee stats.ks_buffers_per_slab, maxbuckets, minbucketsize); 5664688Stomee } 5674688Stomee 5684688Stomee if (opt_v && (stats.ks_partial_slabs > 0)) { 5694688Stomee int i; 5704688Stomee kmem_slab_usage_t *ksu; 5714688Stomee 5724688Stomee mdb_printf(" %d complete, %d partial", 5734688Stomee (stats.ks_slabs - stats.ks_partial_slabs), 5744688Stomee stats.ks_partial_slabs); 5754688Stomee if (stats.ks_partial_slabs > 0) { 5764688Stomee mdb_printf(" (%d):", stats.ks_buffers_per_slab); 5774688Stomee } 5784688Stomee for (i = 0; i < stats.ks_partial_slabs; i++) { 5794688Stomee ksu = &stats.ks_usage[i]; 5804688Stomee mdb_printf(" %d", ksu->ksu_refcnt); 5814688Stomee } 5824688Stomee mdb_printf("\n\n"); 5834688Stomee } 5844688Stomee 5854688Stomee if (stats.ks_usage_len > 0) { 5864688Stomee mdb_free(stats.ks_usage, 5874688Stomee stats.ks_usage_len * sizeof (kmem_slab_usage_t)); 5884688Stomee } 5894688Stomee 5904688Stomee return (DCMD_OK); 5914688Stomee } 5924688Stomee 5934688Stomee void 5944688Stomee kmem_slabs_help(void) 5954688Stomee { 5964688Stomee mdb_printf("%s\n", 5974688Stomee "Display slab usage per kmem cache.\n"); 5984688Stomee mdb_dec_indent(2); 5994688Stomee mdb_printf("%<b>OPTIONS%</b>\n"); 6004688Stomee mdb_inc_indent(2); 6014688Stomee mdb_printf("%s", 6024688Stomee " -n name\n" 6034688Stomee " name of kmem cache (or matching partial name)\n" 6044688Stomee " -b maxbins\n" 6054688Stomee " Print a distribution of allocated buffers per slab using at\n" 6064688Stomee " most maxbins bins. The first bin is reserved for completely\n" 6074688Stomee " allocated slabs. Setting maxbins to zero (-b 0) has the same\n" 6084688Stomee " effect as specifying the maximum allocated buffers per slab\n" 6094688Stomee " or setting minbinsize to 1 (-B 1).\n" 6104688Stomee " -B minbinsize\n" 6114688Stomee " Print a distribution of allocated buffers per slab, making\n" 6124688Stomee " all bins (except the first, reserved for completely allocated\n" 6134688Stomee " slabs) at least minbinsize buffers apart.\n" 6144688Stomee " -v verbose output: List the allocated buffer count of each partial\n" 6154688Stomee " slab on the free list in order from front to back to show how\n" 6164688Stomee " closely the slabs are ordered by usage. For example\n" 6174688Stomee "\n" 6184688Stomee " 10 complete, 3 partial (8): 7 3 1\n" 6194688Stomee "\n" 6204688Stomee " means there are thirteen slabs with eight buffers each, including\n" 6214688Stomee " three partially allocated slabs with less than all eight buffers\n" 6224688Stomee " allocated.\n" 6234688Stomee "\n" 6244688Stomee " Buffer allocations are always from the front of the partial slab\n" 6254688Stomee " list. When a buffer is freed from a completely used slab, that\n" 6264688Stomee " slab is added to the front of the partial slab list. Assuming\n" 6274688Stomee " that all buffers are equally likely to be freed soon, the\n" 6284688Stomee " desired order of partial slabs is most-used at the front of the\n" 6294688Stomee " list and least-used at the back (as in the example above).\n" 6304688Stomee " However, if a slab contains an allocated buffer that will not\n" 6314688Stomee " soon be freed, it would be better for that slab to be at the\n" 6324688Stomee " front where it can get used up. Taking a slab off the partial\n" 6334688Stomee " slab list (either with all buffers freed or all buffers\n" 6344688Stomee " allocated) reduces cache fragmentation.\n" 6354688Stomee "\n" 6364688Stomee "Column\t\tDescription\n" 6374688Stomee "\n" 6384688Stomee "Cache Name\t\tname of kmem cache\n" 6394688Stomee "Slabs\t\t\ttotal slab count\n" 6404688Stomee "Partial Slabs\t\tcount of partially allocated slabs on the free list\n" 6414688Stomee "Buffers\t\ttotal buffer count (Slabs * (buffers per slab))\n" 6424688Stomee "Unused Buffers\tcount of unallocated buffers across all partial slabs\n" 6434688Stomee "Waste\t\t\t(Unused Buffers / Buffers) does not include space\n" 6444688Stomee "\t\t\t for accounting structures (debug mode), slab\n" 6454688Stomee "\t\t\t coloring (incremental small offsets to stagger\n" 6464688Stomee "\t\t\t buffer alignment), or the per-CPU magazine layer\n"); 6474688Stomee } 6484688Stomee 6490Sstevel@tonic-gate static int 6500Sstevel@tonic-gate addrcmp(const void *lhs, const void *rhs) 6510Sstevel@tonic-gate { 6520Sstevel@tonic-gate uintptr_t p1 = *((uintptr_t *)lhs); 6530Sstevel@tonic-gate uintptr_t p2 = *((uintptr_t *)rhs); 6540Sstevel@tonic-gate 6550Sstevel@tonic-gate if (p1 < p2) 6560Sstevel@tonic-gate return (-1); 6570Sstevel@tonic-gate if (p1 > p2) 6580Sstevel@tonic-gate return (1); 6590Sstevel@tonic-gate return (0); 6600Sstevel@tonic-gate } 6610Sstevel@tonic-gate 6620Sstevel@tonic-gate static int 6630Sstevel@tonic-gate bufctlcmp(const kmem_bufctl_audit_t **lhs, const kmem_bufctl_audit_t **rhs) 6640Sstevel@tonic-gate { 6650Sstevel@tonic-gate const kmem_bufctl_audit_t *bcp1 = *lhs; 6660Sstevel@tonic-gate const kmem_bufctl_audit_t *bcp2 = *rhs; 6670Sstevel@tonic-gate 6680Sstevel@tonic-gate if (bcp1->bc_timestamp > bcp2->bc_timestamp) 6690Sstevel@tonic-gate return (-1); 6700Sstevel@tonic-gate 6710Sstevel@tonic-gate if (bcp1->bc_timestamp < bcp2->bc_timestamp) 6720Sstevel@tonic-gate return (1); 6730Sstevel@tonic-gate 6740Sstevel@tonic-gate return (0); 6750Sstevel@tonic-gate } 6760Sstevel@tonic-gate 6770Sstevel@tonic-gate typedef struct kmem_hash_walk { 6780Sstevel@tonic-gate uintptr_t *kmhw_table; 6790Sstevel@tonic-gate size_t kmhw_nelems; 6800Sstevel@tonic-gate size_t kmhw_pos; 6810Sstevel@tonic-gate kmem_bufctl_t kmhw_cur; 6820Sstevel@tonic-gate } kmem_hash_walk_t; 6830Sstevel@tonic-gate 6840Sstevel@tonic-gate int 6850Sstevel@tonic-gate kmem_hash_walk_init(mdb_walk_state_t *wsp) 6860Sstevel@tonic-gate { 6870Sstevel@tonic-gate kmem_hash_walk_t *kmhw; 6880Sstevel@tonic-gate uintptr_t *hash; 6890Sstevel@tonic-gate kmem_cache_t c; 6900Sstevel@tonic-gate uintptr_t haddr, addr = wsp->walk_addr; 6910Sstevel@tonic-gate size_t nelems; 6920Sstevel@tonic-gate size_t hsize; 6930Sstevel@tonic-gate 6940Sstevel@tonic-gate if (addr == NULL) { 6950Sstevel@tonic-gate mdb_warn("kmem_hash doesn't support global walks\n"); 6960Sstevel@tonic-gate return (WALK_ERR); 6970Sstevel@tonic-gate } 6980Sstevel@tonic-gate 6990Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), addr) == -1) { 7000Sstevel@tonic-gate mdb_warn("couldn't read cache at addr %p", addr); 7010Sstevel@tonic-gate return (WALK_ERR); 7020Sstevel@tonic-gate } 7030Sstevel@tonic-gate 7040Sstevel@tonic-gate if (!(c.cache_flags & KMF_HASH)) { 7050Sstevel@tonic-gate mdb_warn("cache %p doesn't have a hash table\n", addr); 7060Sstevel@tonic-gate return (WALK_DONE); /* nothing to do */ 7070Sstevel@tonic-gate } 7080Sstevel@tonic-gate 7090Sstevel@tonic-gate kmhw = mdb_zalloc(sizeof (kmem_hash_walk_t), UM_SLEEP); 7100Sstevel@tonic-gate kmhw->kmhw_cur.bc_next = NULL; 7110Sstevel@tonic-gate kmhw->kmhw_pos = 0; 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate kmhw->kmhw_nelems = nelems = c.cache_hash_mask + 1; 7140Sstevel@tonic-gate hsize = nelems * sizeof (uintptr_t); 7150Sstevel@tonic-gate haddr = (uintptr_t)c.cache_hash_table; 7160Sstevel@tonic-gate 7170Sstevel@tonic-gate kmhw->kmhw_table = hash = mdb_alloc(hsize, UM_SLEEP); 7180Sstevel@tonic-gate if (mdb_vread(hash, hsize, haddr) == -1) { 7190Sstevel@tonic-gate mdb_warn("failed to read hash table at %p", haddr); 7200Sstevel@tonic-gate mdb_free(hash, hsize); 7210Sstevel@tonic-gate mdb_free(kmhw, sizeof (kmem_hash_walk_t)); 7220Sstevel@tonic-gate return (WALK_ERR); 7230Sstevel@tonic-gate } 7240Sstevel@tonic-gate 7250Sstevel@tonic-gate wsp->walk_data = kmhw; 7260Sstevel@tonic-gate 7270Sstevel@tonic-gate return (WALK_NEXT); 7280Sstevel@tonic-gate } 7290Sstevel@tonic-gate 7300Sstevel@tonic-gate int 7310Sstevel@tonic-gate kmem_hash_walk_step(mdb_walk_state_t *wsp) 7320Sstevel@tonic-gate { 7330Sstevel@tonic-gate kmem_hash_walk_t *kmhw = wsp->walk_data; 7340Sstevel@tonic-gate uintptr_t addr = NULL; 7350Sstevel@tonic-gate 7360Sstevel@tonic-gate if ((addr = (uintptr_t)kmhw->kmhw_cur.bc_next) == NULL) { 7370Sstevel@tonic-gate while (kmhw->kmhw_pos < kmhw->kmhw_nelems) { 7380Sstevel@tonic-gate if ((addr = kmhw->kmhw_table[kmhw->kmhw_pos++]) != NULL) 7390Sstevel@tonic-gate break; 7400Sstevel@tonic-gate } 7410Sstevel@tonic-gate } 7420Sstevel@tonic-gate if (addr == NULL) 7430Sstevel@tonic-gate return (WALK_DONE); 7440Sstevel@tonic-gate 7450Sstevel@tonic-gate if (mdb_vread(&kmhw->kmhw_cur, sizeof (kmem_bufctl_t), addr) == -1) { 7460Sstevel@tonic-gate mdb_warn("couldn't read kmem_bufctl_t at addr %p", addr); 7470Sstevel@tonic-gate return (WALK_ERR); 7480Sstevel@tonic-gate } 7490Sstevel@tonic-gate 7500Sstevel@tonic-gate return (wsp->walk_callback(addr, &kmhw->kmhw_cur, wsp->walk_cbdata)); 7510Sstevel@tonic-gate } 7520Sstevel@tonic-gate 7530Sstevel@tonic-gate void 7540Sstevel@tonic-gate kmem_hash_walk_fini(mdb_walk_state_t *wsp) 7550Sstevel@tonic-gate { 7560Sstevel@tonic-gate kmem_hash_walk_t *kmhw = wsp->walk_data; 7570Sstevel@tonic-gate 7580Sstevel@tonic-gate if (kmhw == NULL) 7590Sstevel@tonic-gate return; 7600Sstevel@tonic-gate 7610Sstevel@tonic-gate mdb_free(kmhw->kmhw_table, kmhw->kmhw_nelems * sizeof (uintptr_t)); 7620Sstevel@tonic-gate mdb_free(kmhw, sizeof (kmem_hash_walk_t)); 7630Sstevel@tonic-gate } 7640Sstevel@tonic-gate 7650Sstevel@tonic-gate /* 7660Sstevel@tonic-gate * Find the address of the bufctl structure for the address 'buf' in cache 7670Sstevel@tonic-gate * 'cp', which is at address caddr, and place it in *out. 7680Sstevel@tonic-gate */ 7690Sstevel@tonic-gate static int 7700Sstevel@tonic-gate kmem_hash_lookup(kmem_cache_t *cp, uintptr_t caddr, void *buf, uintptr_t *out) 7710Sstevel@tonic-gate { 7720Sstevel@tonic-gate uintptr_t bucket = (uintptr_t)KMEM_HASH(cp, buf); 7730Sstevel@tonic-gate kmem_bufctl_t *bcp; 7740Sstevel@tonic-gate kmem_bufctl_t bc; 7750Sstevel@tonic-gate 7760Sstevel@tonic-gate if (mdb_vread(&bcp, sizeof (kmem_bufctl_t *), bucket) == -1) { 7770Sstevel@tonic-gate mdb_warn("unable to read hash bucket for %p in cache %p", 7780Sstevel@tonic-gate buf, caddr); 7790Sstevel@tonic-gate return (-1); 7800Sstevel@tonic-gate } 7810Sstevel@tonic-gate 7820Sstevel@tonic-gate while (bcp != NULL) { 7830Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (kmem_bufctl_t), 7840Sstevel@tonic-gate (uintptr_t)bcp) == -1) { 7850Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", bcp); 7860Sstevel@tonic-gate return (-1); 7870Sstevel@tonic-gate } 7880Sstevel@tonic-gate if (bc.bc_addr == buf) { 7890Sstevel@tonic-gate *out = (uintptr_t)bcp; 7900Sstevel@tonic-gate return (0); 7910Sstevel@tonic-gate } 7920Sstevel@tonic-gate bcp = bc.bc_next; 7930Sstevel@tonic-gate } 7940Sstevel@tonic-gate 7950Sstevel@tonic-gate mdb_warn("unable to find bufctl for %p in cache %p\n", buf, caddr); 7960Sstevel@tonic-gate return (-1); 7970Sstevel@tonic-gate } 7980Sstevel@tonic-gate 7990Sstevel@tonic-gate int 8000Sstevel@tonic-gate kmem_get_magsize(const kmem_cache_t *cp) 8010Sstevel@tonic-gate { 8020Sstevel@tonic-gate uintptr_t addr = (uintptr_t)cp->cache_magtype; 8030Sstevel@tonic-gate GElf_Sym mt_sym; 8040Sstevel@tonic-gate kmem_magtype_t mt; 8050Sstevel@tonic-gate int res; 8060Sstevel@tonic-gate 8070Sstevel@tonic-gate /* 8080Sstevel@tonic-gate * if cpu 0 has a non-zero magsize, it must be correct. caches 8090Sstevel@tonic-gate * with KMF_NOMAGAZINE have disabled their magazine layers, so 8100Sstevel@tonic-gate * it is okay to return 0 for them. 8110Sstevel@tonic-gate */ 8120Sstevel@tonic-gate if ((res = cp->cache_cpu[0].cc_magsize) != 0 || 8130Sstevel@tonic-gate (cp->cache_flags & KMF_NOMAGAZINE)) 8140Sstevel@tonic-gate return (res); 8150Sstevel@tonic-gate 8160Sstevel@tonic-gate if (mdb_lookup_by_name("kmem_magtype", &mt_sym) == -1) { 8170Sstevel@tonic-gate mdb_warn("unable to read 'kmem_magtype'"); 8180Sstevel@tonic-gate } else if (addr < mt_sym.st_value || 8190Sstevel@tonic-gate addr + sizeof (mt) - 1 > mt_sym.st_value + mt_sym.st_size - 1 || 8200Sstevel@tonic-gate ((addr - mt_sym.st_value) % sizeof (mt)) != 0) { 8210Sstevel@tonic-gate mdb_warn("cache '%s' has invalid magtype pointer (%p)\n", 8220Sstevel@tonic-gate cp->cache_name, addr); 8230Sstevel@tonic-gate return (0); 8240Sstevel@tonic-gate } 8250Sstevel@tonic-gate if (mdb_vread(&mt, sizeof (mt), addr) == -1) { 8260Sstevel@tonic-gate mdb_warn("unable to read magtype at %a", addr); 8270Sstevel@tonic-gate return (0); 8280Sstevel@tonic-gate } 8290Sstevel@tonic-gate return (mt.mt_magsize); 8300Sstevel@tonic-gate } 8310Sstevel@tonic-gate 8320Sstevel@tonic-gate /*ARGSUSED*/ 8330Sstevel@tonic-gate static int 8340Sstevel@tonic-gate kmem_estimate_slab(uintptr_t addr, const kmem_slab_t *sp, size_t *est) 8350Sstevel@tonic-gate { 8360Sstevel@tonic-gate *est -= (sp->slab_chunks - sp->slab_refcnt); 8370Sstevel@tonic-gate 8380Sstevel@tonic-gate return (WALK_NEXT); 8390Sstevel@tonic-gate } 8400Sstevel@tonic-gate 8410Sstevel@tonic-gate /* 8420Sstevel@tonic-gate * Returns an upper bound on the number of allocated buffers in a given 8430Sstevel@tonic-gate * cache. 8440Sstevel@tonic-gate */ 8450Sstevel@tonic-gate size_t 8460Sstevel@tonic-gate kmem_estimate_allocated(uintptr_t addr, const kmem_cache_t *cp) 8470Sstevel@tonic-gate { 8480Sstevel@tonic-gate int magsize; 8490Sstevel@tonic-gate size_t cache_est; 8500Sstevel@tonic-gate 8510Sstevel@tonic-gate cache_est = cp->cache_buftotal; 8520Sstevel@tonic-gate 8530Sstevel@tonic-gate (void) mdb_pwalk("kmem_slab_partial", 8540Sstevel@tonic-gate (mdb_walk_cb_t)kmem_estimate_slab, &cache_est, addr); 8550Sstevel@tonic-gate 8560Sstevel@tonic-gate if ((magsize = kmem_get_magsize(cp)) != 0) { 8570Sstevel@tonic-gate size_t mag_est = cp->cache_full.ml_total * magsize; 8580Sstevel@tonic-gate 8590Sstevel@tonic-gate if (cache_est >= mag_est) { 8600Sstevel@tonic-gate cache_est -= mag_est; 8610Sstevel@tonic-gate } else { 8620Sstevel@tonic-gate mdb_warn("cache %p's magazine layer holds more buffers " 8630Sstevel@tonic-gate "than the slab layer.\n", addr); 8640Sstevel@tonic-gate } 8650Sstevel@tonic-gate } 8660Sstevel@tonic-gate return (cache_est); 8670Sstevel@tonic-gate } 8680Sstevel@tonic-gate 8690Sstevel@tonic-gate #define READMAG_ROUNDS(rounds) { \ 8700Sstevel@tonic-gate if (mdb_vread(mp, magbsize, (uintptr_t)kmp) == -1) { \ 8710Sstevel@tonic-gate mdb_warn("couldn't read magazine at %p", kmp); \ 8720Sstevel@tonic-gate goto fail; \ 8730Sstevel@tonic-gate } \ 8740Sstevel@tonic-gate for (i = 0; i < rounds; i++) { \ 8750Sstevel@tonic-gate maglist[magcnt++] = mp->mag_round[i]; \ 8760Sstevel@tonic-gate if (magcnt == magmax) { \ 8770Sstevel@tonic-gate mdb_warn("%d magazines exceeds fudge factor\n", \ 8780Sstevel@tonic-gate magcnt); \ 8790Sstevel@tonic-gate goto fail; \ 8800Sstevel@tonic-gate } \ 8810Sstevel@tonic-gate } \ 8820Sstevel@tonic-gate } 8830Sstevel@tonic-gate 8840Sstevel@tonic-gate int 8850Sstevel@tonic-gate kmem_read_magazines(kmem_cache_t *cp, uintptr_t addr, int ncpus, 8860Sstevel@tonic-gate void ***maglistp, size_t *magcntp, size_t *magmaxp, int alloc_flags) 8870Sstevel@tonic-gate { 8880Sstevel@tonic-gate kmem_magazine_t *kmp, *mp; 8890Sstevel@tonic-gate void **maglist = NULL; 8900Sstevel@tonic-gate int i, cpu; 8910Sstevel@tonic-gate size_t magsize, magmax, magbsize; 8920Sstevel@tonic-gate size_t magcnt = 0; 8930Sstevel@tonic-gate 8940Sstevel@tonic-gate /* 8950Sstevel@tonic-gate * Read the magtype out of the cache, after verifying the pointer's 8960Sstevel@tonic-gate * correctness. 8970Sstevel@tonic-gate */ 8980Sstevel@tonic-gate magsize = kmem_get_magsize(cp); 8991528Sjwadams if (magsize == 0) { 9001528Sjwadams *maglistp = NULL; 9011528Sjwadams *magcntp = 0; 9021528Sjwadams *magmaxp = 0; 9031528Sjwadams return (WALK_NEXT); 9041528Sjwadams } 9050Sstevel@tonic-gate 9060Sstevel@tonic-gate /* 9070Sstevel@tonic-gate * There are several places where we need to go buffer hunting: 9080Sstevel@tonic-gate * the per-CPU loaded magazine, the per-CPU spare full magazine, 9090Sstevel@tonic-gate * and the full magazine list in the depot. 9100Sstevel@tonic-gate * 9110Sstevel@tonic-gate * For an upper bound on the number of buffers in the magazine 9120Sstevel@tonic-gate * layer, we have the number of magazines on the cache_full 9130Sstevel@tonic-gate * list plus at most two magazines per CPU (the loaded and the 9140Sstevel@tonic-gate * spare). Toss in 100 magazines as a fudge factor in case this 9150Sstevel@tonic-gate * is live (the number "100" comes from the same fudge factor in 9160Sstevel@tonic-gate * crash(1M)). 9170Sstevel@tonic-gate */ 9180Sstevel@tonic-gate magmax = (cp->cache_full.ml_total + 2 * ncpus + 100) * magsize; 9190Sstevel@tonic-gate magbsize = offsetof(kmem_magazine_t, mag_round[magsize]); 9200Sstevel@tonic-gate 9210Sstevel@tonic-gate if (magbsize >= PAGESIZE / 2) { 9220Sstevel@tonic-gate mdb_warn("magazine size for cache %p unreasonable (%x)\n", 9230Sstevel@tonic-gate addr, magbsize); 9241528Sjwadams return (WALK_ERR); 9250Sstevel@tonic-gate } 9260Sstevel@tonic-gate 9270Sstevel@tonic-gate maglist = mdb_alloc(magmax * sizeof (void *), alloc_flags); 9280Sstevel@tonic-gate mp = mdb_alloc(magbsize, alloc_flags); 9290Sstevel@tonic-gate if (mp == NULL || maglist == NULL) 9300Sstevel@tonic-gate goto fail; 9310Sstevel@tonic-gate 9320Sstevel@tonic-gate /* 9330Sstevel@tonic-gate * First up: the magazines in the depot (i.e. on the cache_full list). 9340Sstevel@tonic-gate */ 9350Sstevel@tonic-gate for (kmp = cp->cache_full.ml_list; kmp != NULL; ) { 9360Sstevel@tonic-gate READMAG_ROUNDS(magsize); 9370Sstevel@tonic-gate kmp = mp->mag_next; 9380Sstevel@tonic-gate 9390Sstevel@tonic-gate if (kmp == cp->cache_full.ml_list) 9400Sstevel@tonic-gate break; /* cache_full list loop detected */ 9410Sstevel@tonic-gate } 9420Sstevel@tonic-gate 9430Sstevel@tonic-gate dprintf(("cache_full list done\n")); 9440Sstevel@tonic-gate 9450Sstevel@tonic-gate /* 9460Sstevel@tonic-gate * Now whip through the CPUs, snagging the loaded magazines 9470Sstevel@tonic-gate * and full spares. 9480Sstevel@tonic-gate */ 9490Sstevel@tonic-gate for (cpu = 0; cpu < ncpus; cpu++) { 9500Sstevel@tonic-gate kmem_cpu_cache_t *ccp = &cp->cache_cpu[cpu]; 9510Sstevel@tonic-gate 9520Sstevel@tonic-gate dprintf(("reading cpu cache %p\n", 9530Sstevel@tonic-gate (uintptr_t)ccp - (uintptr_t)cp + addr)); 9540Sstevel@tonic-gate 9550Sstevel@tonic-gate if (ccp->cc_rounds > 0 && 9560Sstevel@tonic-gate (kmp = ccp->cc_loaded) != NULL) { 9570Sstevel@tonic-gate dprintf(("reading %d loaded rounds\n", ccp->cc_rounds)); 9580Sstevel@tonic-gate READMAG_ROUNDS(ccp->cc_rounds); 9590Sstevel@tonic-gate } 9600Sstevel@tonic-gate 9610Sstevel@tonic-gate if (ccp->cc_prounds > 0 && 9620Sstevel@tonic-gate (kmp = ccp->cc_ploaded) != NULL) { 9630Sstevel@tonic-gate dprintf(("reading %d previously loaded rounds\n", 9640Sstevel@tonic-gate ccp->cc_prounds)); 9650Sstevel@tonic-gate READMAG_ROUNDS(ccp->cc_prounds); 9660Sstevel@tonic-gate } 9670Sstevel@tonic-gate } 9680Sstevel@tonic-gate 9690Sstevel@tonic-gate dprintf(("magazine layer: %d buffers\n", magcnt)); 9700Sstevel@tonic-gate 9710Sstevel@tonic-gate if (!(alloc_flags & UM_GC)) 9720Sstevel@tonic-gate mdb_free(mp, magbsize); 9730Sstevel@tonic-gate 9740Sstevel@tonic-gate *maglistp = maglist; 9750Sstevel@tonic-gate *magcntp = magcnt; 9760Sstevel@tonic-gate *magmaxp = magmax; 9770Sstevel@tonic-gate 9780Sstevel@tonic-gate return (WALK_NEXT); 9790Sstevel@tonic-gate 9800Sstevel@tonic-gate fail: 9810Sstevel@tonic-gate if (!(alloc_flags & UM_GC)) { 9820Sstevel@tonic-gate if (mp) 9830Sstevel@tonic-gate mdb_free(mp, magbsize); 9840Sstevel@tonic-gate if (maglist) 9850Sstevel@tonic-gate mdb_free(maglist, magmax * sizeof (void *)); 9860Sstevel@tonic-gate } 9870Sstevel@tonic-gate return (WALK_ERR); 9880Sstevel@tonic-gate } 9890Sstevel@tonic-gate 9900Sstevel@tonic-gate static int 9910Sstevel@tonic-gate kmem_walk_callback(mdb_walk_state_t *wsp, uintptr_t buf) 9920Sstevel@tonic-gate { 9930Sstevel@tonic-gate return (wsp->walk_callback(buf, NULL, wsp->walk_cbdata)); 9940Sstevel@tonic-gate } 9950Sstevel@tonic-gate 9960Sstevel@tonic-gate static int 9970Sstevel@tonic-gate bufctl_walk_callback(kmem_cache_t *cp, mdb_walk_state_t *wsp, uintptr_t buf) 9980Sstevel@tonic-gate { 9990Sstevel@tonic-gate kmem_bufctl_audit_t b; 10000Sstevel@tonic-gate 10010Sstevel@tonic-gate /* 10020Sstevel@tonic-gate * if KMF_AUDIT is not set, we know that we're looking at a 10030Sstevel@tonic-gate * kmem_bufctl_t. 10040Sstevel@tonic-gate */ 10050Sstevel@tonic-gate if (!(cp->cache_flags & KMF_AUDIT) || 10060Sstevel@tonic-gate mdb_vread(&b, sizeof (kmem_bufctl_audit_t), buf) == -1) { 10070Sstevel@tonic-gate (void) memset(&b, 0, sizeof (b)); 10080Sstevel@tonic-gate if (mdb_vread(&b, sizeof (kmem_bufctl_t), buf) == -1) { 10090Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", buf); 10100Sstevel@tonic-gate return (WALK_ERR); 10110Sstevel@tonic-gate } 10120Sstevel@tonic-gate } 10130Sstevel@tonic-gate 10140Sstevel@tonic-gate return (wsp->walk_callback(buf, &b, wsp->walk_cbdata)); 10150Sstevel@tonic-gate } 10160Sstevel@tonic-gate 10170Sstevel@tonic-gate typedef struct kmem_walk { 10180Sstevel@tonic-gate int kmw_type; 10190Sstevel@tonic-gate 10200Sstevel@tonic-gate int kmw_addr; /* cache address */ 10210Sstevel@tonic-gate kmem_cache_t *kmw_cp; 10220Sstevel@tonic-gate size_t kmw_csize; 10230Sstevel@tonic-gate 10240Sstevel@tonic-gate /* 10250Sstevel@tonic-gate * magazine layer 10260Sstevel@tonic-gate */ 10270Sstevel@tonic-gate void **kmw_maglist; 10280Sstevel@tonic-gate size_t kmw_max; 10290Sstevel@tonic-gate size_t kmw_count; 10300Sstevel@tonic-gate size_t kmw_pos; 10310Sstevel@tonic-gate 10320Sstevel@tonic-gate /* 10330Sstevel@tonic-gate * slab layer 10340Sstevel@tonic-gate */ 10350Sstevel@tonic-gate char *kmw_valid; /* to keep track of freed buffers */ 10360Sstevel@tonic-gate char *kmw_ubase; /* buffer for slab data */ 10370Sstevel@tonic-gate } kmem_walk_t; 10380Sstevel@tonic-gate 10390Sstevel@tonic-gate static int 10400Sstevel@tonic-gate kmem_walk_init_common(mdb_walk_state_t *wsp, int type) 10410Sstevel@tonic-gate { 10420Sstevel@tonic-gate kmem_walk_t *kmw; 10430Sstevel@tonic-gate int ncpus, csize; 10440Sstevel@tonic-gate kmem_cache_t *cp; 10451528Sjwadams size_t vm_quantum; 10460Sstevel@tonic-gate 10470Sstevel@tonic-gate size_t magmax, magcnt; 10480Sstevel@tonic-gate void **maglist = NULL; 10490Sstevel@tonic-gate uint_t chunksize, slabsize; 10500Sstevel@tonic-gate int status = WALK_ERR; 10510Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 10520Sstevel@tonic-gate const char *layered; 10530Sstevel@tonic-gate 10540Sstevel@tonic-gate type &= ~KM_HASH; 10550Sstevel@tonic-gate 10560Sstevel@tonic-gate if (addr == NULL) { 10570Sstevel@tonic-gate mdb_warn("kmem walk doesn't support global walks\n"); 10580Sstevel@tonic-gate return (WALK_ERR); 10590Sstevel@tonic-gate } 10600Sstevel@tonic-gate 10610Sstevel@tonic-gate dprintf(("walking %p\n", addr)); 10620Sstevel@tonic-gate 10630Sstevel@tonic-gate /* 10640Sstevel@tonic-gate * First we need to figure out how many CPUs are configured in the 10650Sstevel@tonic-gate * system to know how much to slurp out. 10660Sstevel@tonic-gate */ 10670Sstevel@tonic-gate mdb_readvar(&ncpus, "max_ncpus"); 10680Sstevel@tonic-gate 10690Sstevel@tonic-gate csize = KMEM_CACHE_SIZE(ncpus); 10700Sstevel@tonic-gate cp = mdb_alloc(csize, UM_SLEEP); 10710Sstevel@tonic-gate 10720Sstevel@tonic-gate if (mdb_vread(cp, csize, addr) == -1) { 10730Sstevel@tonic-gate mdb_warn("couldn't read cache at addr %p", addr); 10740Sstevel@tonic-gate goto out2; 10750Sstevel@tonic-gate } 10760Sstevel@tonic-gate 10771528Sjwadams /* 10781528Sjwadams * It's easy for someone to hand us an invalid cache address. 10791528Sjwadams * Unfortunately, it is hard for this walker to survive an 10801528Sjwadams * invalid cache cleanly. So we make sure that: 10811528Sjwadams * 10821528Sjwadams * 1. the vmem arena for the cache is readable, 10831528Sjwadams * 2. the vmem arena's quantum is a power of 2, 10841528Sjwadams * 3. our slabsize is a multiple of the quantum, and 10851528Sjwadams * 4. our chunksize is >0 and less than our slabsize. 10861528Sjwadams */ 10871528Sjwadams if (mdb_vread(&vm_quantum, sizeof (vm_quantum), 10881528Sjwadams (uintptr_t)&cp->cache_arena->vm_quantum) == -1 || 10891528Sjwadams vm_quantum == 0 || 10901528Sjwadams (vm_quantum & (vm_quantum - 1)) != 0 || 10911528Sjwadams cp->cache_slabsize < vm_quantum || 10921528Sjwadams P2PHASE(cp->cache_slabsize, vm_quantum) != 0 || 10931528Sjwadams cp->cache_chunksize == 0 || 10941528Sjwadams cp->cache_chunksize > cp->cache_slabsize) { 10951528Sjwadams mdb_warn("%p is not a valid kmem_cache_t\n", addr); 10961528Sjwadams goto out2; 10971528Sjwadams } 10981528Sjwadams 10990Sstevel@tonic-gate dprintf(("buf total is %d\n", cp->cache_buftotal)); 11000Sstevel@tonic-gate 11010Sstevel@tonic-gate if (cp->cache_buftotal == 0) { 11020Sstevel@tonic-gate mdb_free(cp, csize); 11030Sstevel@tonic-gate return (WALK_DONE); 11040Sstevel@tonic-gate } 11050Sstevel@tonic-gate 11060Sstevel@tonic-gate /* 11070Sstevel@tonic-gate * If they ask for bufctls, but it's a small-slab cache, 11080Sstevel@tonic-gate * there is nothing to report. 11090Sstevel@tonic-gate */ 11100Sstevel@tonic-gate if ((type & KM_BUFCTL) && !(cp->cache_flags & KMF_HASH)) { 11110Sstevel@tonic-gate dprintf(("bufctl requested, not KMF_HASH (flags: %p)\n", 11120Sstevel@tonic-gate cp->cache_flags)); 11130Sstevel@tonic-gate mdb_free(cp, csize); 11140Sstevel@tonic-gate return (WALK_DONE); 11150Sstevel@tonic-gate } 11160Sstevel@tonic-gate 11170Sstevel@tonic-gate /* 11180Sstevel@tonic-gate * If they want constructed buffers, but there's no constructor or 11190Sstevel@tonic-gate * the cache has DEADBEEF checking enabled, there is nothing to report. 11200Sstevel@tonic-gate */ 11210Sstevel@tonic-gate if ((type & KM_CONSTRUCTED) && (!(type & KM_FREE) || 11220Sstevel@tonic-gate cp->cache_constructor == NULL || 11230Sstevel@tonic-gate (cp->cache_flags & (KMF_DEADBEEF | KMF_LITE)) == KMF_DEADBEEF)) { 11240Sstevel@tonic-gate mdb_free(cp, csize); 11250Sstevel@tonic-gate return (WALK_DONE); 11260Sstevel@tonic-gate } 11270Sstevel@tonic-gate 11280Sstevel@tonic-gate /* 11290Sstevel@tonic-gate * Read in the contents of the magazine layer 11300Sstevel@tonic-gate */ 11310Sstevel@tonic-gate if (kmem_read_magazines(cp, addr, ncpus, &maglist, &magcnt, 11320Sstevel@tonic-gate &magmax, UM_SLEEP) == WALK_ERR) 11330Sstevel@tonic-gate goto out2; 11340Sstevel@tonic-gate 11350Sstevel@tonic-gate /* 11360Sstevel@tonic-gate * We have all of the buffers from the magazines; if we are walking 11370Sstevel@tonic-gate * allocated buffers, sort them so we can bsearch them later. 11380Sstevel@tonic-gate */ 11390Sstevel@tonic-gate if (type & KM_ALLOCATED) 11400Sstevel@tonic-gate qsort(maglist, magcnt, sizeof (void *), addrcmp); 11410Sstevel@tonic-gate 11420Sstevel@tonic-gate wsp->walk_data = kmw = mdb_zalloc(sizeof (kmem_walk_t), UM_SLEEP); 11430Sstevel@tonic-gate 11440Sstevel@tonic-gate kmw->kmw_type = type; 11450Sstevel@tonic-gate kmw->kmw_addr = addr; 11460Sstevel@tonic-gate kmw->kmw_cp = cp; 11470Sstevel@tonic-gate kmw->kmw_csize = csize; 11480Sstevel@tonic-gate kmw->kmw_maglist = maglist; 11490Sstevel@tonic-gate kmw->kmw_max = magmax; 11500Sstevel@tonic-gate kmw->kmw_count = magcnt; 11510Sstevel@tonic-gate kmw->kmw_pos = 0; 11520Sstevel@tonic-gate 11530Sstevel@tonic-gate /* 11540Sstevel@tonic-gate * When walking allocated buffers in a KMF_HASH cache, we walk the 11550Sstevel@tonic-gate * hash table instead of the slab layer. 11560Sstevel@tonic-gate */ 11570Sstevel@tonic-gate if ((cp->cache_flags & KMF_HASH) && (type & KM_ALLOCATED)) { 11580Sstevel@tonic-gate layered = "kmem_hash"; 11590Sstevel@tonic-gate 11600Sstevel@tonic-gate kmw->kmw_type |= KM_HASH; 11610Sstevel@tonic-gate } else { 11620Sstevel@tonic-gate /* 11630Sstevel@tonic-gate * If we are walking freed buffers, we only need the 11640Sstevel@tonic-gate * magazine layer plus the partially allocated slabs. 11650Sstevel@tonic-gate * To walk allocated buffers, we need all of the slabs. 11660Sstevel@tonic-gate */ 11670Sstevel@tonic-gate if (type & KM_ALLOCATED) 11680Sstevel@tonic-gate layered = "kmem_slab"; 11690Sstevel@tonic-gate else 11700Sstevel@tonic-gate layered = "kmem_slab_partial"; 11710Sstevel@tonic-gate 11720Sstevel@tonic-gate /* 11730Sstevel@tonic-gate * for small-slab caches, we read in the entire slab. For 11740Sstevel@tonic-gate * freed buffers, we can just walk the freelist. For 11750Sstevel@tonic-gate * allocated buffers, we use a 'valid' array to track 11760Sstevel@tonic-gate * the freed buffers. 11770Sstevel@tonic-gate */ 11780Sstevel@tonic-gate if (!(cp->cache_flags & KMF_HASH)) { 11790Sstevel@tonic-gate chunksize = cp->cache_chunksize; 11800Sstevel@tonic-gate slabsize = cp->cache_slabsize; 11810Sstevel@tonic-gate 11820Sstevel@tonic-gate kmw->kmw_ubase = mdb_alloc(slabsize + 11830Sstevel@tonic-gate sizeof (kmem_bufctl_t), UM_SLEEP); 11840Sstevel@tonic-gate 11850Sstevel@tonic-gate if (type & KM_ALLOCATED) 11860Sstevel@tonic-gate kmw->kmw_valid = 11870Sstevel@tonic-gate mdb_alloc(slabsize / chunksize, UM_SLEEP); 11880Sstevel@tonic-gate } 11890Sstevel@tonic-gate } 11900Sstevel@tonic-gate 11910Sstevel@tonic-gate status = WALK_NEXT; 11920Sstevel@tonic-gate 11930Sstevel@tonic-gate if (mdb_layered_walk(layered, wsp) == -1) { 11940Sstevel@tonic-gate mdb_warn("unable to start layered '%s' walk", layered); 11950Sstevel@tonic-gate status = WALK_ERR; 11960Sstevel@tonic-gate } 11970Sstevel@tonic-gate 11980Sstevel@tonic-gate out1: 11990Sstevel@tonic-gate if (status == WALK_ERR) { 12000Sstevel@tonic-gate if (kmw->kmw_valid) 12010Sstevel@tonic-gate mdb_free(kmw->kmw_valid, slabsize / chunksize); 12020Sstevel@tonic-gate 12030Sstevel@tonic-gate if (kmw->kmw_ubase) 12040Sstevel@tonic-gate mdb_free(kmw->kmw_ubase, slabsize + 12050Sstevel@tonic-gate sizeof (kmem_bufctl_t)); 12060Sstevel@tonic-gate 12071528Sjwadams if (kmw->kmw_maglist) 12081528Sjwadams mdb_free(kmw->kmw_maglist, 12091528Sjwadams kmw->kmw_max * sizeof (uintptr_t)); 12101528Sjwadams 12110Sstevel@tonic-gate mdb_free(kmw, sizeof (kmem_walk_t)); 12120Sstevel@tonic-gate wsp->walk_data = NULL; 12130Sstevel@tonic-gate } 12140Sstevel@tonic-gate 12150Sstevel@tonic-gate out2: 12160Sstevel@tonic-gate if (status == WALK_ERR) 12170Sstevel@tonic-gate mdb_free(cp, csize); 12180Sstevel@tonic-gate 12190Sstevel@tonic-gate return (status); 12200Sstevel@tonic-gate } 12210Sstevel@tonic-gate 12220Sstevel@tonic-gate int 12230Sstevel@tonic-gate kmem_walk_step(mdb_walk_state_t *wsp) 12240Sstevel@tonic-gate { 12250Sstevel@tonic-gate kmem_walk_t *kmw = wsp->walk_data; 12260Sstevel@tonic-gate int type = kmw->kmw_type; 12270Sstevel@tonic-gate kmem_cache_t *cp = kmw->kmw_cp; 12280Sstevel@tonic-gate 12290Sstevel@tonic-gate void **maglist = kmw->kmw_maglist; 12300Sstevel@tonic-gate int magcnt = kmw->kmw_count; 12310Sstevel@tonic-gate 12320Sstevel@tonic-gate uintptr_t chunksize, slabsize; 12330Sstevel@tonic-gate uintptr_t addr; 12340Sstevel@tonic-gate const kmem_slab_t *sp; 12350Sstevel@tonic-gate const kmem_bufctl_t *bcp; 12360Sstevel@tonic-gate kmem_bufctl_t bc; 12370Sstevel@tonic-gate 12380Sstevel@tonic-gate int chunks; 12390Sstevel@tonic-gate char *kbase; 12400Sstevel@tonic-gate void *buf; 12410Sstevel@tonic-gate int i, ret; 12420Sstevel@tonic-gate 12430Sstevel@tonic-gate char *valid, *ubase; 12440Sstevel@tonic-gate 12450Sstevel@tonic-gate /* 12460Sstevel@tonic-gate * first, handle the 'kmem_hash' layered walk case 12470Sstevel@tonic-gate */ 12480Sstevel@tonic-gate if (type & KM_HASH) { 12490Sstevel@tonic-gate /* 12500Sstevel@tonic-gate * We have a buffer which has been allocated out of the 12510Sstevel@tonic-gate * global layer. We need to make sure that it's not 12520Sstevel@tonic-gate * actually sitting in a magazine before we report it as 12530Sstevel@tonic-gate * an allocated buffer. 12540Sstevel@tonic-gate */ 12550Sstevel@tonic-gate buf = ((const kmem_bufctl_t *)wsp->walk_layer)->bc_addr; 12560Sstevel@tonic-gate 12570Sstevel@tonic-gate if (magcnt > 0 && 12580Sstevel@tonic-gate bsearch(&buf, maglist, magcnt, sizeof (void *), 12590Sstevel@tonic-gate addrcmp) != NULL) 12600Sstevel@tonic-gate return (WALK_NEXT); 12610Sstevel@tonic-gate 12620Sstevel@tonic-gate if (type & KM_BUFCTL) 12630Sstevel@tonic-gate return (bufctl_walk_callback(cp, wsp, wsp->walk_addr)); 12640Sstevel@tonic-gate 12650Sstevel@tonic-gate return (kmem_walk_callback(wsp, (uintptr_t)buf)); 12660Sstevel@tonic-gate } 12670Sstevel@tonic-gate 12680Sstevel@tonic-gate ret = WALK_NEXT; 12690Sstevel@tonic-gate 12700Sstevel@tonic-gate addr = kmw->kmw_addr; 12710Sstevel@tonic-gate 12720Sstevel@tonic-gate /* 12730Sstevel@tonic-gate * If we're walking freed buffers, report everything in the 12740Sstevel@tonic-gate * magazine layer before processing the first slab. 12750Sstevel@tonic-gate */ 12760Sstevel@tonic-gate if ((type & KM_FREE) && magcnt != 0) { 12770Sstevel@tonic-gate kmw->kmw_count = 0; /* only do this once */ 12780Sstevel@tonic-gate for (i = 0; i < magcnt; i++) { 12790Sstevel@tonic-gate buf = maglist[i]; 12800Sstevel@tonic-gate 12810Sstevel@tonic-gate if (type & KM_BUFCTL) { 12820Sstevel@tonic-gate uintptr_t out; 12830Sstevel@tonic-gate 12840Sstevel@tonic-gate if (cp->cache_flags & KMF_BUFTAG) { 12850Sstevel@tonic-gate kmem_buftag_t *btp; 12860Sstevel@tonic-gate kmem_buftag_t tag; 12870Sstevel@tonic-gate 12880Sstevel@tonic-gate /* LINTED - alignment */ 12890Sstevel@tonic-gate btp = KMEM_BUFTAG(cp, buf); 12900Sstevel@tonic-gate if (mdb_vread(&tag, sizeof (tag), 12910Sstevel@tonic-gate (uintptr_t)btp) == -1) { 12920Sstevel@tonic-gate mdb_warn("reading buftag for " 12930Sstevel@tonic-gate "%p at %p", buf, btp); 12940Sstevel@tonic-gate continue; 12950Sstevel@tonic-gate } 12960Sstevel@tonic-gate out = (uintptr_t)tag.bt_bufctl; 12970Sstevel@tonic-gate } else { 12980Sstevel@tonic-gate if (kmem_hash_lookup(cp, addr, buf, 12990Sstevel@tonic-gate &out) == -1) 13000Sstevel@tonic-gate continue; 13010Sstevel@tonic-gate } 13020Sstevel@tonic-gate ret = bufctl_walk_callback(cp, wsp, out); 13030Sstevel@tonic-gate } else { 13040Sstevel@tonic-gate ret = kmem_walk_callback(wsp, (uintptr_t)buf); 13050Sstevel@tonic-gate } 13060Sstevel@tonic-gate 13070Sstevel@tonic-gate if (ret != WALK_NEXT) 13080Sstevel@tonic-gate return (ret); 13090Sstevel@tonic-gate } 13100Sstevel@tonic-gate } 13110Sstevel@tonic-gate 13120Sstevel@tonic-gate /* 13130Sstevel@tonic-gate * If they want constructed buffers, we're finished, since the 13140Sstevel@tonic-gate * magazine layer holds them all. 13150Sstevel@tonic-gate */ 13160Sstevel@tonic-gate if (type & KM_CONSTRUCTED) 13170Sstevel@tonic-gate return (WALK_DONE); 13180Sstevel@tonic-gate 13190Sstevel@tonic-gate /* 13200Sstevel@tonic-gate * Handle the buffers in the current slab 13210Sstevel@tonic-gate */ 13220Sstevel@tonic-gate chunksize = cp->cache_chunksize; 13230Sstevel@tonic-gate slabsize = cp->cache_slabsize; 13240Sstevel@tonic-gate 13250Sstevel@tonic-gate sp = wsp->walk_layer; 13260Sstevel@tonic-gate chunks = sp->slab_chunks; 13270Sstevel@tonic-gate kbase = sp->slab_base; 13280Sstevel@tonic-gate 13290Sstevel@tonic-gate dprintf(("kbase is %p\n", kbase)); 13300Sstevel@tonic-gate 13310Sstevel@tonic-gate if (!(cp->cache_flags & KMF_HASH)) { 13320Sstevel@tonic-gate valid = kmw->kmw_valid; 13330Sstevel@tonic-gate ubase = kmw->kmw_ubase; 13340Sstevel@tonic-gate 13350Sstevel@tonic-gate if (mdb_vread(ubase, chunks * chunksize, 13360Sstevel@tonic-gate (uintptr_t)kbase) == -1) { 13370Sstevel@tonic-gate mdb_warn("failed to read slab contents at %p", kbase); 13380Sstevel@tonic-gate return (WALK_ERR); 13390Sstevel@tonic-gate } 13400Sstevel@tonic-gate 13410Sstevel@tonic-gate /* 13420Sstevel@tonic-gate * Set up the valid map as fully allocated -- we'll punch 13430Sstevel@tonic-gate * out the freelist. 13440Sstevel@tonic-gate */ 13450Sstevel@tonic-gate if (type & KM_ALLOCATED) 13460Sstevel@tonic-gate (void) memset(valid, 1, chunks); 13470Sstevel@tonic-gate } else { 13480Sstevel@tonic-gate valid = NULL; 13490Sstevel@tonic-gate ubase = NULL; 13500Sstevel@tonic-gate } 13510Sstevel@tonic-gate 13520Sstevel@tonic-gate /* 13530Sstevel@tonic-gate * walk the slab's freelist 13540Sstevel@tonic-gate */ 13550Sstevel@tonic-gate bcp = sp->slab_head; 13560Sstevel@tonic-gate 13570Sstevel@tonic-gate dprintf(("refcnt is %d; chunks is %d\n", sp->slab_refcnt, chunks)); 13580Sstevel@tonic-gate 13590Sstevel@tonic-gate /* 13600Sstevel@tonic-gate * since we could be in the middle of allocating a buffer, 13610Sstevel@tonic-gate * our refcnt could be one higher than it aught. So we 13620Sstevel@tonic-gate * check one further on the freelist than the count allows. 13630Sstevel@tonic-gate */ 13640Sstevel@tonic-gate for (i = sp->slab_refcnt; i <= chunks; i++) { 13650Sstevel@tonic-gate uint_t ndx; 13660Sstevel@tonic-gate 13670Sstevel@tonic-gate dprintf(("bcp is %p\n", bcp)); 13680Sstevel@tonic-gate 13690Sstevel@tonic-gate if (bcp == NULL) { 13700Sstevel@tonic-gate if (i == chunks) 13710Sstevel@tonic-gate break; 13720Sstevel@tonic-gate mdb_warn( 13730Sstevel@tonic-gate "slab %p in cache %p freelist too short by %d\n", 13740Sstevel@tonic-gate sp, addr, chunks - i); 13750Sstevel@tonic-gate break; 13760Sstevel@tonic-gate } 13770Sstevel@tonic-gate 13780Sstevel@tonic-gate if (cp->cache_flags & KMF_HASH) { 13790Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), (uintptr_t)bcp) == -1) { 13800Sstevel@tonic-gate mdb_warn("failed to read bufctl ptr at %p", 13810Sstevel@tonic-gate bcp); 13820Sstevel@tonic-gate break; 13830Sstevel@tonic-gate } 13840Sstevel@tonic-gate buf = bc.bc_addr; 13850Sstevel@tonic-gate } else { 13860Sstevel@tonic-gate /* 13870Sstevel@tonic-gate * Otherwise the buffer is in the slab which 13880Sstevel@tonic-gate * we've read in; we just need to determine 13890Sstevel@tonic-gate * its offset in the slab to find the 13900Sstevel@tonic-gate * kmem_bufctl_t. 13910Sstevel@tonic-gate */ 13920Sstevel@tonic-gate bc = *((kmem_bufctl_t *) 13930Sstevel@tonic-gate ((uintptr_t)bcp - (uintptr_t)kbase + 13940Sstevel@tonic-gate (uintptr_t)ubase)); 13950Sstevel@tonic-gate 13960Sstevel@tonic-gate buf = KMEM_BUF(cp, bcp); 13970Sstevel@tonic-gate } 13980Sstevel@tonic-gate 13990Sstevel@tonic-gate ndx = ((uintptr_t)buf - (uintptr_t)kbase) / chunksize; 14000Sstevel@tonic-gate 14010Sstevel@tonic-gate if (ndx > slabsize / cp->cache_bufsize) { 14020Sstevel@tonic-gate /* 14030Sstevel@tonic-gate * This is very wrong; we have managed to find 14040Sstevel@tonic-gate * a buffer in the slab which shouldn't 14050Sstevel@tonic-gate * actually be here. Emit a warning, and 14060Sstevel@tonic-gate * try to continue. 14070Sstevel@tonic-gate */ 14080Sstevel@tonic-gate mdb_warn("buf %p is out of range for " 14090Sstevel@tonic-gate "slab %p, cache %p\n", buf, sp, addr); 14100Sstevel@tonic-gate } else if (type & KM_ALLOCATED) { 14110Sstevel@tonic-gate /* 14120Sstevel@tonic-gate * we have found a buffer on the slab's freelist; 14130Sstevel@tonic-gate * clear its entry 14140Sstevel@tonic-gate */ 14150Sstevel@tonic-gate valid[ndx] = 0; 14160Sstevel@tonic-gate } else { 14170Sstevel@tonic-gate /* 14180Sstevel@tonic-gate * Report this freed buffer 14190Sstevel@tonic-gate */ 14200Sstevel@tonic-gate if (type & KM_BUFCTL) { 14210Sstevel@tonic-gate ret = bufctl_walk_callback(cp, wsp, 14220Sstevel@tonic-gate (uintptr_t)bcp); 14230Sstevel@tonic-gate } else { 14240Sstevel@tonic-gate ret = kmem_walk_callback(wsp, (uintptr_t)buf); 14250Sstevel@tonic-gate } 14260Sstevel@tonic-gate if (ret != WALK_NEXT) 14270Sstevel@tonic-gate return (ret); 14280Sstevel@tonic-gate } 14290Sstevel@tonic-gate 14300Sstevel@tonic-gate bcp = bc.bc_next; 14310Sstevel@tonic-gate } 14320Sstevel@tonic-gate 14330Sstevel@tonic-gate if (bcp != NULL) { 14340Sstevel@tonic-gate dprintf(("slab %p in cache %p freelist too long (%p)\n", 14350Sstevel@tonic-gate sp, addr, bcp)); 14360Sstevel@tonic-gate } 14370Sstevel@tonic-gate 14380Sstevel@tonic-gate /* 14390Sstevel@tonic-gate * If we are walking freed buffers, the loop above handled reporting 14400Sstevel@tonic-gate * them. 14410Sstevel@tonic-gate */ 14420Sstevel@tonic-gate if (type & KM_FREE) 14430Sstevel@tonic-gate return (WALK_NEXT); 14440Sstevel@tonic-gate 14450Sstevel@tonic-gate if (type & KM_BUFCTL) { 14460Sstevel@tonic-gate mdb_warn("impossible situation: small-slab KM_BUFCTL walk for " 14470Sstevel@tonic-gate "cache %p\n", addr); 14480Sstevel@tonic-gate return (WALK_ERR); 14490Sstevel@tonic-gate } 14500Sstevel@tonic-gate 14510Sstevel@tonic-gate /* 14520Sstevel@tonic-gate * Report allocated buffers, skipping buffers in the magazine layer. 14530Sstevel@tonic-gate * We only get this far for small-slab caches. 14540Sstevel@tonic-gate */ 14550Sstevel@tonic-gate for (i = 0; ret == WALK_NEXT && i < chunks; i++) { 14560Sstevel@tonic-gate buf = (char *)kbase + i * chunksize; 14570Sstevel@tonic-gate 14580Sstevel@tonic-gate if (!valid[i]) 14590Sstevel@tonic-gate continue; /* on slab freelist */ 14600Sstevel@tonic-gate 14610Sstevel@tonic-gate if (magcnt > 0 && 14620Sstevel@tonic-gate bsearch(&buf, maglist, magcnt, sizeof (void *), 14630Sstevel@tonic-gate addrcmp) != NULL) 14640Sstevel@tonic-gate continue; /* in magazine layer */ 14650Sstevel@tonic-gate 14660Sstevel@tonic-gate ret = kmem_walk_callback(wsp, (uintptr_t)buf); 14670Sstevel@tonic-gate } 14680Sstevel@tonic-gate return (ret); 14690Sstevel@tonic-gate } 14700Sstevel@tonic-gate 14710Sstevel@tonic-gate void 14720Sstevel@tonic-gate kmem_walk_fini(mdb_walk_state_t *wsp) 14730Sstevel@tonic-gate { 14740Sstevel@tonic-gate kmem_walk_t *kmw = wsp->walk_data; 14750Sstevel@tonic-gate uintptr_t chunksize; 14760Sstevel@tonic-gate uintptr_t slabsize; 14770Sstevel@tonic-gate 14780Sstevel@tonic-gate if (kmw == NULL) 14790Sstevel@tonic-gate return; 14800Sstevel@tonic-gate 14810Sstevel@tonic-gate if (kmw->kmw_maglist != NULL) 14820Sstevel@tonic-gate mdb_free(kmw->kmw_maglist, kmw->kmw_max * sizeof (void *)); 14830Sstevel@tonic-gate 14840Sstevel@tonic-gate chunksize = kmw->kmw_cp->cache_chunksize; 14850Sstevel@tonic-gate slabsize = kmw->kmw_cp->cache_slabsize; 14860Sstevel@tonic-gate 14870Sstevel@tonic-gate if (kmw->kmw_valid != NULL) 14880Sstevel@tonic-gate mdb_free(kmw->kmw_valid, slabsize / chunksize); 14890Sstevel@tonic-gate if (kmw->kmw_ubase != NULL) 14900Sstevel@tonic-gate mdb_free(kmw->kmw_ubase, slabsize + sizeof (kmem_bufctl_t)); 14910Sstevel@tonic-gate 14920Sstevel@tonic-gate mdb_free(kmw->kmw_cp, kmw->kmw_csize); 14930Sstevel@tonic-gate mdb_free(kmw, sizeof (kmem_walk_t)); 14940Sstevel@tonic-gate } 14950Sstevel@tonic-gate 14960Sstevel@tonic-gate /*ARGSUSED*/ 14970Sstevel@tonic-gate static int 14980Sstevel@tonic-gate kmem_walk_all(uintptr_t addr, const kmem_cache_t *c, mdb_walk_state_t *wsp) 14990Sstevel@tonic-gate { 15000Sstevel@tonic-gate /* 15010Sstevel@tonic-gate * Buffers allocated from NOTOUCH caches can also show up as freed 15020Sstevel@tonic-gate * memory in other caches. This can be a little confusing, so we 15030Sstevel@tonic-gate * don't walk NOTOUCH caches when walking all caches (thereby assuring 15040Sstevel@tonic-gate * that "::walk kmem" and "::walk freemem" yield disjoint output). 15050Sstevel@tonic-gate */ 15060Sstevel@tonic-gate if (c->cache_cflags & KMC_NOTOUCH) 15070Sstevel@tonic-gate return (WALK_NEXT); 15080Sstevel@tonic-gate 15090Sstevel@tonic-gate if (mdb_pwalk(wsp->walk_data, wsp->walk_callback, 15100Sstevel@tonic-gate wsp->walk_cbdata, addr) == -1) 15110Sstevel@tonic-gate return (WALK_DONE); 15120Sstevel@tonic-gate 15130Sstevel@tonic-gate return (WALK_NEXT); 15140Sstevel@tonic-gate } 15150Sstevel@tonic-gate 15160Sstevel@tonic-gate #define KMEM_WALK_ALL(name, wsp) { \ 15170Sstevel@tonic-gate wsp->walk_data = (name); \ 15180Sstevel@tonic-gate if (mdb_walk("kmem_cache", (mdb_walk_cb_t)kmem_walk_all, wsp) == -1) \ 15190Sstevel@tonic-gate return (WALK_ERR); \ 15200Sstevel@tonic-gate return (WALK_DONE); \ 15210Sstevel@tonic-gate } 15220Sstevel@tonic-gate 15230Sstevel@tonic-gate int 15240Sstevel@tonic-gate kmem_walk_init(mdb_walk_state_t *wsp) 15250Sstevel@tonic-gate { 15260Sstevel@tonic-gate if (wsp->walk_arg != NULL) 15270Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)wsp->walk_arg; 15280Sstevel@tonic-gate 15290Sstevel@tonic-gate if (wsp->walk_addr == NULL) 15300Sstevel@tonic-gate KMEM_WALK_ALL("kmem", wsp); 15310Sstevel@tonic-gate return (kmem_walk_init_common(wsp, KM_ALLOCATED)); 15320Sstevel@tonic-gate } 15330Sstevel@tonic-gate 15340Sstevel@tonic-gate int 15350Sstevel@tonic-gate bufctl_walk_init(mdb_walk_state_t *wsp) 15360Sstevel@tonic-gate { 15370Sstevel@tonic-gate if (wsp->walk_addr == NULL) 15380Sstevel@tonic-gate KMEM_WALK_ALL("bufctl", wsp); 15390Sstevel@tonic-gate return (kmem_walk_init_common(wsp, KM_ALLOCATED | KM_BUFCTL)); 15400Sstevel@tonic-gate } 15410Sstevel@tonic-gate 15420Sstevel@tonic-gate int 15430Sstevel@tonic-gate freemem_walk_init(mdb_walk_state_t *wsp) 15440Sstevel@tonic-gate { 15450Sstevel@tonic-gate if (wsp->walk_addr == NULL) 15460Sstevel@tonic-gate KMEM_WALK_ALL("freemem", wsp); 15470Sstevel@tonic-gate return (kmem_walk_init_common(wsp, KM_FREE)); 15480Sstevel@tonic-gate } 15490Sstevel@tonic-gate 15500Sstevel@tonic-gate int 15510Sstevel@tonic-gate freemem_constructed_walk_init(mdb_walk_state_t *wsp) 15520Sstevel@tonic-gate { 15530Sstevel@tonic-gate if (wsp->walk_addr == NULL) 15540Sstevel@tonic-gate KMEM_WALK_ALL("freemem_constructed", wsp); 15550Sstevel@tonic-gate return (kmem_walk_init_common(wsp, KM_FREE | KM_CONSTRUCTED)); 15560Sstevel@tonic-gate } 15570Sstevel@tonic-gate 15580Sstevel@tonic-gate int 15590Sstevel@tonic-gate freectl_walk_init(mdb_walk_state_t *wsp) 15600Sstevel@tonic-gate { 15610Sstevel@tonic-gate if (wsp->walk_addr == NULL) 15620Sstevel@tonic-gate KMEM_WALK_ALL("freectl", wsp); 15630Sstevel@tonic-gate return (kmem_walk_init_common(wsp, KM_FREE | KM_BUFCTL)); 15640Sstevel@tonic-gate } 15650Sstevel@tonic-gate 15660Sstevel@tonic-gate int 15670Sstevel@tonic-gate freectl_constructed_walk_init(mdb_walk_state_t *wsp) 15680Sstevel@tonic-gate { 15690Sstevel@tonic-gate if (wsp->walk_addr == NULL) 15700Sstevel@tonic-gate KMEM_WALK_ALL("freectl_constructed", wsp); 15710Sstevel@tonic-gate return (kmem_walk_init_common(wsp, 15720Sstevel@tonic-gate KM_FREE | KM_BUFCTL | KM_CONSTRUCTED)); 15730Sstevel@tonic-gate } 15740Sstevel@tonic-gate 15750Sstevel@tonic-gate typedef struct bufctl_history_walk { 15760Sstevel@tonic-gate void *bhw_next; 15770Sstevel@tonic-gate kmem_cache_t *bhw_cache; 15780Sstevel@tonic-gate kmem_slab_t *bhw_slab; 15790Sstevel@tonic-gate hrtime_t bhw_timestamp; 15800Sstevel@tonic-gate } bufctl_history_walk_t; 15810Sstevel@tonic-gate 15820Sstevel@tonic-gate int 15830Sstevel@tonic-gate bufctl_history_walk_init(mdb_walk_state_t *wsp) 15840Sstevel@tonic-gate { 15850Sstevel@tonic-gate bufctl_history_walk_t *bhw; 15860Sstevel@tonic-gate kmem_bufctl_audit_t bc; 15870Sstevel@tonic-gate kmem_bufctl_audit_t bcn; 15880Sstevel@tonic-gate 15890Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 15900Sstevel@tonic-gate mdb_warn("bufctl_history walk doesn't support global walks\n"); 15910Sstevel@tonic-gate return (WALK_ERR); 15920Sstevel@tonic-gate } 15930Sstevel@tonic-gate 15940Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), wsp->walk_addr) == -1) { 15950Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", wsp->walk_addr); 15960Sstevel@tonic-gate return (WALK_ERR); 15970Sstevel@tonic-gate } 15980Sstevel@tonic-gate 15990Sstevel@tonic-gate bhw = mdb_zalloc(sizeof (*bhw), UM_SLEEP); 16000Sstevel@tonic-gate bhw->bhw_timestamp = 0; 16010Sstevel@tonic-gate bhw->bhw_cache = bc.bc_cache; 16020Sstevel@tonic-gate bhw->bhw_slab = bc.bc_slab; 16030Sstevel@tonic-gate 16040Sstevel@tonic-gate /* 16050Sstevel@tonic-gate * sometimes the first log entry matches the base bufctl; in that 16060Sstevel@tonic-gate * case, skip the base bufctl. 16070Sstevel@tonic-gate */ 16080Sstevel@tonic-gate if (bc.bc_lastlog != NULL && 16090Sstevel@tonic-gate mdb_vread(&bcn, sizeof (bcn), (uintptr_t)bc.bc_lastlog) != -1 && 16100Sstevel@tonic-gate bc.bc_addr == bcn.bc_addr && 16110Sstevel@tonic-gate bc.bc_cache == bcn.bc_cache && 16120Sstevel@tonic-gate bc.bc_slab == bcn.bc_slab && 16130Sstevel@tonic-gate bc.bc_timestamp == bcn.bc_timestamp && 16140Sstevel@tonic-gate bc.bc_thread == bcn.bc_thread) 16150Sstevel@tonic-gate bhw->bhw_next = bc.bc_lastlog; 16160Sstevel@tonic-gate else 16170Sstevel@tonic-gate bhw->bhw_next = (void *)wsp->walk_addr; 16180Sstevel@tonic-gate 16190Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)bc.bc_addr; 16200Sstevel@tonic-gate wsp->walk_data = bhw; 16210Sstevel@tonic-gate 16220Sstevel@tonic-gate return (WALK_NEXT); 16230Sstevel@tonic-gate } 16240Sstevel@tonic-gate 16250Sstevel@tonic-gate int 16260Sstevel@tonic-gate bufctl_history_walk_step(mdb_walk_state_t *wsp) 16270Sstevel@tonic-gate { 16280Sstevel@tonic-gate bufctl_history_walk_t *bhw = wsp->walk_data; 16290Sstevel@tonic-gate uintptr_t addr = (uintptr_t)bhw->bhw_next; 16300Sstevel@tonic-gate uintptr_t baseaddr = wsp->walk_addr; 16310Sstevel@tonic-gate kmem_bufctl_audit_t bc; 16320Sstevel@tonic-gate 16330Sstevel@tonic-gate if (addr == NULL) 16340Sstevel@tonic-gate return (WALK_DONE); 16350Sstevel@tonic-gate 16360Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), addr) == -1) { 16370Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", bhw->bhw_next); 16380Sstevel@tonic-gate return (WALK_ERR); 16390Sstevel@tonic-gate } 16400Sstevel@tonic-gate 16410Sstevel@tonic-gate /* 16420Sstevel@tonic-gate * The bufctl is only valid if the address, cache, and slab are 16430Sstevel@tonic-gate * correct. We also check that the timestamp is decreasing, to 16440Sstevel@tonic-gate * prevent infinite loops. 16450Sstevel@tonic-gate */ 16460Sstevel@tonic-gate if ((uintptr_t)bc.bc_addr != baseaddr || 16470Sstevel@tonic-gate bc.bc_cache != bhw->bhw_cache || 16480Sstevel@tonic-gate bc.bc_slab != bhw->bhw_slab || 16490Sstevel@tonic-gate (bhw->bhw_timestamp != 0 && bc.bc_timestamp >= bhw->bhw_timestamp)) 16500Sstevel@tonic-gate return (WALK_DONE); 16510Sstevel@tonic-gate 16520Sstevel@tonic-gate bhw->bhw_next = bc.bc_lastlog; 16530Sstevel@tonic-gate bhw->bhw_timestamp = bc.bc_timestamp; 16540Sstevel@tonic-gate 16550Sstevel@tonic-gate return (wsp->walk_callback(addr, &bc, wsp->walk_cbdata)); 16560Sstevel@tonic-gate } 16570Sstevel@tonic-gate 16580Sstevel@tonic-gate void 16590Sstevel@tonic-gate bufctl_history_walk_fini(mdb_walk_state_t *wsp) 16600Sstevel@tonic-gate { 16610Sstevel@tonic-gate bufctl_history_walk_t *bhw = wsp->walk_data; 16620Sstevel@tonic-gate 16630Sstevel@tonic-gate mdb_free(bhw, sizeof (*bhw)); 16640Sstevel@tonic-gate } 16650Sstevel@tonic-gate 16660Sstevel@tonic-gate typedef struct kmem_log_walk { 16670Sstevel@tonic-gate kmem_bufctl_audit_t *klw_base; 16680Sstevel@tonic-gate kmem_bufctl_audit_t **klw_sorted; 16690Sstevel@tonic-gate kmem_log_header_t klw_lh; 16700Sstevel@tonic-gate size_t klw_size; 16710Sstevel@tonic-gate size_t klw_maxndx; 16720Sstevel@tonic-gate size_t klw_ndx; 16730Sstevel@tonic-gate } kmem_log_walk_t; 16740Sstevel@tonic-gate 16750Sstevel@tonic-gate int 16760Sstevel@tonic-gate kmem_log_walk_init(mdb_walk_state_t *wsp) 16770Sstevel@tonic-gate { 16780Sstevel@tonic-gate uintptr_t lp = wsp->walk_addr; 16790Sstevel@tonic-gate kmem_log_walk_t *klw; 16800Sstevel@tonic-gate kmem_log_header_t *lhp; 16810Sstevel@tonic-gate int maxndx, i, j, k; 16820Sstevel@tonic-gate 16830Sstevel@tonic-gate /* 16840Sstevel@tonic-gate * By default (global walk), walk the kmem_transaction_log. Otherwise 16850Sstevel@tonic-gate * read the log whose kmem_log_header_t is stored at walk_addr. 16860Sstevel@tonic-gate */ 16870Sstevel@tonic-gate if (lp == NULL && mdb_readvar(&lp, "kmem_transaction_log") == -1) { 16880Sstevel@tonic-gate mdb_warn("failed to read 'kmem_transaction_log'"); 16890Sstevel@tonic-gate return (WALK_ERR); 16900Sstevel@tonic-gate } 16910Sstevel@tonic-gate 16920Sstevel@tonic-gate if (lp == NULL) { 16930Sstevel@tonic-gate mdb_warn("log is disabled\n"); 16940Sstevel@tonic-gate return (WALK_ERR); 16950Sstevel@tonic-gate } 16960Sstevel@tonic-gate 16970Sstevel@tonic-gate klw = mdb_zalloc(sizeof (kmem_log_walk_t), UM_SLEEP); 16980Sstevel@tonic-gate lhp = &klw->klw_lh; 16990Sstevel@tonic-gate 17000Sstevel@tonic-gate if (mdb_vread(lhp, sizeof (kmem_log_header_t), lp) == -1) { 17010Sstevel@tonic-gate mdb_warn("failed to read log header at %p", lp); 17020Sstevel@tonic-gate mdb_free(klw, sizeof (kmem_log_walk_t)); 17030Sstevel@tonic-gate return (WALK_ERR); 17040Sstevel@tonic-gate } 17050Sstevel@tonic-gate 17060Sstevel@tonic-gate klw->klw_size = lhp->lh_chunksize * lhp->lh_nchunks; 17070Sstevel@tonic-gate klw->klw_base = mdb_alloc(klw->klw_size, UM_SLEEP); 17080Sstevel@tonic-gate maxndx = lhp->lh_chunksize / sizeof (kmem_bufctl_audit_t) - 1; 17090Sstevel@tonic-gate 17100Sstevel@tonic-gate if (mdb_vread(klw->klw_base, klw->klw_size, 17110Sstevel@tonic-gate (uintptr_t)lhp->lh_base) == -1) { 17120Sstevel@tonic-gate mdb_warn("failed to read log at base %p", lhp->lh_base); 17130Sstevel@tonic-gate mdb_free(klw->klw_base, klw->klw_size); 17140Sstevel@tonic-gate mdb_free(klw, sizeof (kmem_log_walk_t)); 17150Sstevel@tonic-gate return (WALK_ERR); 17160Sstevel@tonic-gate } 17170Sstevel@tonic-gate 17180Sstevel@tonic-gate klw->klw_sorted = mdb_alloc(maxndx * lhp->lh_nchunks * 17190Sstevel@tonic-gate sizeof (kmem_bufctl_audit_t *), UM_SLEEP); 17200Sstevel@tonic-gate 17210Sstevel@tonic-gate for (i = 0, k = 0; i < lhp->lh_nchunks; i++) { 17220Sstevel@tonic-gate kmem_bufctl_audit_t *chunk = (kmem_bufctl_audit_t *) 17230Sstevel@tonic-gate ((uintptr_t)klw->klw_base + i * lhp->lh_chunksize); 17240Sstevel@tonic-gate 17250Sstevel@tonic-gate for (j = 0; j < maxndx; j++) 17260Sstevel@tonic-gate klw->klw_sorted[k++] = &chunk[j]; 17270Sstevel@tonic-gate } 17280Sstevel@tonic-gate 17290Sstevel@tonic-gate qsort(klw->klw_sorted, k, sizeof (kmem_bufctl_audit_t *), 17300Sstevel@tonic-gate (int(*)(const void *, const void *))bufctlcmp); 17310Sstevel@tonic-gate 17320Sstevel@tonic-gate klw->klw_maxndx = k; 17330Sstevel@tonic-gate wsp->walk_data = klw; 17340Sstevel@tonic-gate 17350Sstevel@tonic-gate return (WALK_NEXT); 17360Sstevel@tonic-gate } 17370Sstevel@tonic-gate 17380Sstevel@tonic-gate int 17390Sstevel@tonic-gate kmem_log_walk_step(mdb_walk_state_t *wsp) 17400Sstevel@tonic-gate { 17410Sstevel@tonic-gate kmem_log_walk_t *klw = wsp->walk_data; 17420Sstevel@tonic-gate kmem_bufctl_audit_t *bcp; 17430Sstevel@tonic-gate 17440Sstevel@tonic-gate if (klw->klw_ndx == klw->klw_maxndx) 17450Sstevel@tonic-gate return (WALK_DONE); 17460Sstevel@tonic-gate 17470Sstevel@tonic-gate bcp = klw->klw_sorted[klw->klw_ndx++]; 17480Sstevel@tonic-gate 17490Sstevel@tonic-gate return (wsp->walk_callback((uintptr_t)bcp - (uintptr_t)klw->klw_base + 17500Sstevel@tonic-gate (uintptr_t)klw->klw_lh.lh_base, bcp, wsp->walk_cbdata)); 17510Sstevel@tonic-gate } 17520Sstevel@tonic-gate 17530Sstevel@tonic-gate void 17540Sstevel@tonic-gate kmem_log_walk_fini(mdb_walk_state_t *wsp) 17550Sstevel@tonic-gate { 17560Sstevel@tonic-gate kmem_log_walk_t *klw = wsp->walk_data; 17570Sstevel@tonic-gate 17580Sstevel@tonic-gate mdb_free(klw->klw_base, klw->klw_size); 17590Sstevel@tonic-gate mdb_free(klw->klw_sorted, klw->klw_maxndx * 17600Sstevel@tonic-gate sizeof (kmem_bufctl_audit_t *)); 17610Sstevel@tonic-gate mdb_free(klw, sizeof (kmem_log_walk_t)); 17620Sstevel@tonic-gate } 17630Sstevel@tonic-gate 17640Sstevel@tonic-gate typedef struct allocdby_bufctl { 17650Sstevel@tonic-gate uintptr_t abb_addr; 17660Sstevel@tonic-gate hrtime_t abb_ts; 17670Sstevel@tonic-gate } allocdby_bufctl_t; 17680Sstevel@tonic-gate 17690Sstevel@tonic-gate typedef struct allocdby_walk { 17700Sstevel@tonic-gate const char *abw_walk; 17710Sstevel@tonic-gate uintptr_t abw_thread; 17720Sstevel@tonic-gate size_t abw_nbufs; 17730Sstevel@tonic-gate size_t abw_size; 17740Sstevel@tonic-gate allocdby_bufctl_t *abw_buf; 17750Sstevel@tonic-gate size_t abw_ndx; 17760Sstevel@tonic-gate } allocdby_walk_t; 17770Sstevel@tonic-gate 17780Sstevel@tonic-gate int 17790Sstevel@tonic-gate allocdby_walk_bufctl(uintptr_t addr, const kmem_bufctl_audit_t *bcp, 17800Sstevel@tonic-gate allocdby_walk_t *abw) 17810Sstevel@tonic-gate { 17820Sstevel@tonic-gate if ((uintptr_t)bcp->bc_thread != abw->abw_thread) 17830Sstevel@tonic-gate return (WALK_NEXT); 17840Sstevel@tonic-gate 17850Sstevel@tonic-gate if (abw->abw_nbufs == abw->abw_size) { 17860Sstevel@tonic-gate allocdby_bufctl_t *buf; 17870Sstevel@tonic-gate size_t oldsize = sizeof (allocdby_bufctl_t) * abw->abw_size; 17880Sstevel@tonic-gate 17890Sstevel@tonic-gate buf = mdb_zalloc(oldsize << 1, UM_SLEEP); 17900Sstevel@tonic-gate 17910Sstevel@tonic-gate bcopy(abw->abw_buf, buf, oldsize); 17920Sstevel@tonic-gate mdb_free(abw->abw_buf, oldsize); 17930Sstevel@tonic-gate 17940Sstevel@tonic-gate abw->abw_size <<= 1; 17950Sstevel@tonic-gate abw->abw_buf = buf; 17960Sstevel@tonic-gate } 17970Sstevel@tonic-gate 17980Sstevel@tonic-gate abw->abw_buf[abw->abw_nbufs].abb_addr = addr; 17990Sstevel@tonic-gate abw->abw_buf[abw->abw_nbufs].abb_ts = bcp->bc_timestamp; 18000Sstevel@tonic-gate abw->abw_nbufs++; 18010Sstevel@tonic-gate 18020Sstevel@tonic-gate return (WALK_NEXT); 18030Sstevel@tonic-gate } 18040Sstevel@tonic-gate 18050Sstevel@tonic-gate /*ARGSUSED*/ 18060Sstevel@tonic-gate int 18070Sstevel@tonic-gate allocdby_walk_cache(uintptr_t addr, const kmem_cache_t *c, allocdby_walk_t *abw) 18080Sstevel@tonic-gate { 18090Sstevel@tonic-gate if (mdb_pwalk(abw->abw_walk, (mdb_walk_cb_t)allocdby_walk_bufctl, 18100Sstevel@tonic-gate abw, addr) == -1) { 18110Sstevel@tonic-gate mdb_warn("couldn't walk bufctl for cache %p", addr); 18120Sstevel@tonic-gate return (WALK_DONE); 18130Sstevel@tonic-gate } 18140Sstevel@tonic-gate 18150Sstevel@tonic-gate return (WALK_NEXT); 18160Sstevel@tonic-gate } 18170Sstevel@tonic-gate 18180Sstevel@tonic-gate static int 18190Sstevel@tonic-gate allocdby_cmp(const allocdby_bufctl_t *lhs, const allocdby_bufctl_t *rhs) 18200Sstevel@tonic-gate { 18210Sstevel@tonic-gate if (lhs->abb_ts < rhs->abb_ts) 18220Sstevel@tonic-gate return (1); 18230Sstevel@tonic-gate if (lhs->abb_ts > rhs->abb_ts) 18240Sstevel@tonic-gate return (-1); 18250Sstevel@tonic-gate return (0); 18260Sstevel@tonic-gate } 18270Sstevel@tonic-gate 18280Sstevel@tonic-gate static int 18290Sstevel@tonic-gate allocdby_walk_init_common(mdb_walk_state_t *wsp, const char *walk) 18300Sstevel@tonic-gate { 18310Sstevel@tonic-gate allocdby_walk_t *abw; 18320Sstevel@tonic-gate 18330Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 18340Sstevel@tonic-gate mdb_warn("allocdby walk doesn't support global walks\n"); 18350Sstevel@tonic-gate return (WALK_ERR); 18360Sstevel@tonic-gate } 18370Sstevel@tonic-gate 18380Sstevel@tonic-gate abw = mdb_zalloc(sizeof (allocdby_walk_t), UM_SLEEP); 18390Sstevel@tonic-gate 18400Sstevel@tonic-gate abw->abw_thread = wsp->walk_addr; 18410Sstevel@tonic-gate abw->abw_walk = walk; 18420Sstevel@tonic-gate abw->abw_size = 128; /* something reasonable */ 18430Sstevel@tonic-gate abw->abw_buf = 18440Sstevel@tonic-gate mdb_zalloc(abw->abw_size * sizeof (allocdby_bufctl_t), UM_SLEEP); 18450Sstevel@tonic-gate 18460Sstevel@tonic-gate wsp->walk_data = abw; 18470Sstevel@tonic-gate 18480Sstevel@tonic-gate if (mdb_walk("kmem_cache", 18490Sstevel@tonic-gate (mdb_walk_cb_t)allocdby_walk_cache, abw) == -1) { 18500Sstevel@tonic-gate mdb_warn("couldn't walk kmem_cache"); 18510Sstevel@tonic-gate allocdby_walk_fini(wsp); 18520Sstevel@tonic-gate return (WALK_ERR); 18530Sstevel@tonic-gate } 18540Sstevel@tonic-gate 18550Sstevel@tonic-gate qsort(abw->abw_buf, abw->abw_nbufs, sizeof (allocdby_bufctl_t), 18560Sstevel@tonic-gate (int(*)(const void *, const void *))allocdby_cmp); 18570Sstevel@tonic-gate 18580Sstevel@tonic-gate return (WALK_NEXT); 18590Sstevel@tonic-gate } 18600Sstevel@tonic-gate 18610Sstevel@tonic-gate int 18620Sstevel@tonic-gate allocdby_walk_init(mdb_walk_state_t *wsp) 18630Sstevel@tonic-gate { 18640Sstevel@tonic-gate return (allocdby_walk_init_common(wsp, "bufctl")); 18650Sstevel@tonic-gate } 18660Sstevel@tonic-gate 18670Sstevel@tonic-gate int 18680Sstevel@tonic-gate freedby_walk_init(mdb_walk_state_t *wsp) 18690Sstevel@tonic-gate { 18700Sstevel@tonic-gate return (allocdby_walk_init_common(wsp, "freectl")); 18710Sstevel@tonic-gate } 18720Sstevel@tonic-gate 18730Sstevel@tonic-gate int 18740Sstevel@tonic-gate allocdby_walk_step(mdb_walk_state_t *wsp) 18750Sstevel@tonic-gate { 18760Sstevel@tonic-gate allocdby_walk_t *abw = wsp->walk_data; 18770Sstevel@tonic-gate kmem_bufctl_audit_t bc; 18780Sstevel@tonic-gate uintptr_t addr; 18790Sstevel@tonic-gate 18800Sstevel@tonic-gate if (abw->abw_ndx == abw->abw_nbufs) 18810Sstevel@tonic-gate return (WALK_DONE); 18820Sstevel@tonic-gate 18830Sstevel@tonic-gate addr = abw->abw_buf[abw->abw_ndx++].abb_addr; 18840Sstevel@tonic-gate 18850Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), addr) == -1) { 18860Sstevel@tonic-gate mdb_warn("couldn't read bufctl at %p", addr); 18870Sstevel@tonic-gate return (WALK_DONE); 18880Sstevel@tonic-gate } 18890Sstevel@tonic-gate 18900Sstevel@tonic-gate return (wsp->walk_callback(addr, &bc, wsp->walk_cbdata)); 18910Sstevel@tonic-gate } 18920Sstevel@tonic-gate 18930Sstevel@tonic-gate void 18940Sstevel@tonic-gate allocdby_walk_fini(mdb_walk_state_t *wsp) 18950Sstevel@tonic-gate { 18960Sstevel@tonic-gate allocdby_walk_t *abw = wsp->walk_data; 18970Sstevel@tonic-gate 18980Sstevel@tonic-gate mdb_free(abw->abw_buf, sizeof (allocdby_bufctl_t) * abw->abw_size); 18990Sstevel@tonic-gate mdb_free(abw, sizeof (allocdby_walk_t)); 19000Sstevel@tonic-gate } 19010Sstevel@tonic-gate 19020Sstevel@tonic-gate /*ARGSUSED*/ 19030Sstevel@tonic-gate int 19040Sstevel@tonic-gate allocdby_walk(uintptr_t addr, const kmem_bufctl_audit_t *bcp, void *ignored) 19050Sstevel@tonic-gate { 19060Sstevel@tonic-gate char c[MDB_SYM_NAMLEN]; 19070Sstevel@tonic-gate GElf_Sym sym; 19080Sstevel@tonic-gate int i; 19090Sstevel@tonic-gate 19100Sstevel@tonic-gate mdb_printf("%0?p %12llx ", addr, bcp->bc_timestamp); 19110Sstevel@tonic-gate for (i = 0; i < bcp->bc_depth; i++) { 19120Sstevel@tonic-gate if (mdb_lookup_by_addr(bcp->bc_stack[i], 19130Sstevel@tonic-gate MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1) 19140Sstevel@tonic-gate continue; 19150Sstevel@tonic-gate if (strncmp(c, "kmem_", 5) == 0) 19160Sstevel@tonic-gate continue; 19170Sstevel@tonic-gate mdb_printf("%s+0x%lx", 19180Sstevel@tonic-gate c, bcp->bc_stack[i] - (uintptr_t)sym.st_value); 19190Sstevel@tonic-gate break; 19200Sstevel@tonic-gate } 19210Sstevel@tonic-gate mdb_printf("\n"); 19220Sstevel@tonic-gate 19230Sstevel@tonic-gate return (WALK_NEXT); 19240Sstevel@tonic-gate } 19250Sstevel@tonic-gate 19260Sstevel@tonic-gate static int 19270Sstevel@tonic-gate allocdby_common(uintptr_t addr, uint_t flags, const char *w) 19280Sstevel@tonic-gate { 19290Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 19300Sstevel@tonic-gate return (DCMD_USAGE); 19310Sstevel@tonic-gate 19320Sstevel@tonic-gate mdb_printf("%-?s %12s %s\n", "BUFCTL", "TIMESTAMP", "CALLER"); 19330Sstevel@tonic-gate 19340Sstevel@tonic-gate if (mdb_pwalk(w, (mdb_walk_cb_t)allocdby_walk, NULL, addr) == -1) { 19350Sstevel@tonic-gate mdb_warn("can't walk '%s' for %p", w, addr); 19360Sstevel@tonic-gate return (DCMD_ERR); 19370Sstevel@tonic-gate } 19380Sstevel@tonic-gate 19390Sstevel@tonic-gate return (DCMD_OK); 19400Sstevel@tonic-gate } 19410Sstevel@tonic-gate 19420Sstevel@tonic-gate /*ARGSUSED*/ 19430Sstevel@tonic-gate int 19440Sstevel@tonic-gate allocdby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 19450Sstevel@tonic-gate { 19460Sstevel@tonic-gate return (allocdby_common(addr, flags, "allocdby")); 19470Sstevel@tonic-gate } 19480Sstevel@tonic-gate 19490Sstevel@tonic-gate /*ARGSUSED*/ 19500Sstevel@tonic-gate int 19510Sstevel@tonic-gate freedby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 19520Sstevel@tonic-gate { 19530Sstevel@tonic-gate return (allocdby_common(addr, flags, "freedby")); 19540Sstevel@tonic-gate } 19550Sstevel@tonic-gate 19560Sstevel@tonic-gate /* 19570Sstevel@tonic-gate * Return a string describing the address in relation to the given thread's 19580Sstevel@tonic-gate * stack. 19590Sstevel@tonic-gate * 19600Sstevel@tonic-gate * - If the thread state is TS_FREE, return " (inactive interrupt thread)". 19610Sstevel@tonic-gate * 19620Sstevel@tonic-gate * - If the address is above the stack pointer, return an empty string 19630Sstevel@tonic-gate * signifying that the address is active. 19640Sstevel@tonic-gate * 19650Sstevel@tonic-gate * - If the address is below the stack pointer, and the thread is not on proc, 19660Sstevel@tonic-gate * return " (below sp)". 19670Sstevel@tonic-gate * 19680Sstevel@tonic-gate * - If the address is below the stack pointer, and the thread is on proc, 19690Sstevel@tonic-gate * return " (possibly below sp)". Depending on context, we may or may not 19700Sstevel@tonic-gate * have an accurate t_sp. 19710Sstevel@tonic-gate */ 19720Sstevel@tonic-gate static const char * 19730Sstevel@tonic-gate stack_active(const kthread_t *t, uintptr_t addr) 19740Sstevel@tonic-gate { 19750Sstevel@tonic-gate uintptr_t panicstk; 19760Sstevel@tonic-gate GElf_Sym sym; 19770Sstevel@tonic-gate 19780Sstevel@tonic-gate if (t->t_state == TS_FREE) 19790Sstevel@tonic-gate return (" (inactive interrupt thread)"); 19800Sstevel@tonic-gate 19810Sstevel@tonic-gate /* 19820Sstevel@tonic-gate * Check to see if we're on the panic stack. If so, ignore t_sp, as it 19830Sstevel@tonic-gate * no longer relates to the thread's real stack. 19840Sstevel@tonic-gate */ 19850Sstevel@tonic-gate if (mdb_lookup_by_name("panic_stack", &sym) == 0) { 19860Sstevel@tonic-gate panicstk = (uintptr_t)sym.st_value; 19870Sstevel@tonic-gate 19880Sstevel@tonic-gate if (t->t_sp >= panicstk && t->t_sp < panicstk + PANICSTKSIZE) 19890Sstevel@tonic-gate return (""); 19900Sstevel@tonic-gate } 19910Sstevel@tonic-gate 19920Sstevel@tonic-gate if (addr >= t->t_sp + STACK_BIAS) 19930Sstevel@tonic-gate return (""); 19940Sstevel@tonic-gate 19950Sstevel@tonic-gate if (t->t_state == TS_ONPROC) 19960Sstevel@tonic-gate return (" (possibly below sp)"); 19970Sstevel@tonic-gate 19980Sstevel@tonic-gate return (" (below sp)"); 19990Sstevel@tonic-gate } 20000Sstevel@tonic-gate 20010Sstevel@tonic-gate typedef struct whatis { 20020Sstevel@tonic-gate uintptr_t w_addr; 20030Sstevel@tonic-gate const kmem_cache_t *w_cache; 20040Sstevel@tonic-gate const vmem_t *w_vmem; 20050Sstevel@tonic-gate size_t w_slab_align; 20060Sstevel@tonic-gate int w_slab_found; 20070Sstevel@tonic-gate int w_found; 20080Sstevel@tonic-gate int w_kmem_lite_count; 20090Sstevel@tonic-gate uint_t w_verbose; 20100Sstevel@tonic-gate uint_t w_freemem; 20110Sstevel@tonic-gate uint_t w_all; 20120Sstevel@tonic-gate uint_t w_bufctl; 20130Sstevel@tonic-gate uint_t w_idspace; 20140Sstevel@tonic-gate } whatis_t; 20150Sstevel@tonic-gate 20160Sstevel@tonic-gate static void 20170Sstevel@tonic-gate whatis_print_kmem(uintptr_t addr, uintptr_t baddr, whatis_t *w) 20180Sstevel@tonic-gate { 20190Sstevel@tonic-gate /* LINTED pointer cast may result in improper alignment */ 20200Sstevel@tonic-gate uintptr_t btaddr = (uintptr_t)KMEM_BUFTAG(w->w_cache, addr); 20210Sstevel@tonic-gate intptr_t stat; 20220Sstevel@tonic-gate int count = 0; 20230Sstevel@tonic-gate int i; 20240Sstevel@tonic-gate pc_t callers[16]; 20250Sstevel@tonic-gate 20260Sstevel@tonic-gate if (w->w_cache->cache_flags & KMF_REDZONE) { 20270Sstevel@tonic-gate kmem_buftag_t bt; 20280Sstevel@tonic-gate 20290Sstevel@tonic-gate if (mdb_vread(&bt, sizeof (bt), btaddr) == -1) 20300Sstevel@tonic-gate goto done; 20310Sstevel@tonic-gate 20320Sstevel@tonic-gate stat = (intptr_t)bt.bt_bufctl ^ bt.bt_bxstat; 20330Sstevel@tonic-gate 20340Sstevel@tonic-gate if (stat != KMEM_BUFTAG_ALLOC && stat != KMEM_BUFTAG_FREE) 20350Sstevel@tonic-gate goto done; 20360Sstevel@tonic-gate 20370Sstevel@tonic-gate /* 20380Sstevel@tonic-gate * provide the bufctl ptr if it has useful information 20390Sstevel@tonic-gate */ 20400Sstevel@tonic-gate if (baddr == 0 && (w->w_cache->cache_flags & KMF_AUDIT)) 20410Sstevel@tonic-gate baddr = (uintptr_t)bt.bt_bufctl; 20420Sstevel@tonic-gate 20430Sstevel@tonic-gate if (w->w_cache->cache_flags & KMF_LITE) { 20440Sstevel@tonic-gate count = w->w_kmem_lite_count; 20450Sstevel@tonic-gate 20460Sstevel@tonic-gate if (count * sizeof (pc_t) > sizeof (callers)) 20470Sstevel@tonic-gate count = 0; 20480Sstevel@tonic-gate 20490Sstevel@tonic-gate if (count > 0 && 20500Sstevel@tonic-gate mdb_vread(callers, count * sizeof (pc_t), 20510Sstevel@tonic-gate btaddr + 20520Sstevel@tonic-gate offsetof(kmem_buftag_lite_t, bt_history)) == -1) 20530Sstevel@tonic-gate count = 0; 20540Sstevel@tonic-gate 20550Sstevel@tonic-gate /* 20560Sstevel@tonic-gate * skip unused callers 20570Sstevel@tonic-gate */ 20580Sstevel@tonic-gate while (count > 0 && callers[count - 1] == 20590Sstevel@tonic-gate (pc_t)KMEM_UNINITIALIZED_PATTERN) 20600Sstevel@tonic-gate count--; 20610Sstevel@tonic-gate } 20620Sstevel@tonic-gate } 20630Sstevel@tonic-gate 20640Sstevel@tonic-gate done: 20650Sstevel@tonic-gate if (baddr == 0) 20660Sstevel@tonic-gate mdb_printf("%p is %p+%p, %s from %s\n", 20670Sstevel@tonic-gate w->w_addr, addr, w->w_addr - addr, 20680Sstevel@tonic-gate w->w_freemem == FALSE ? "allocated" : "freed", 20690Sstevel@tonic-gate w->w_cache->cache_name); 20700Sstevel@tonic-gate else 20710Sstevel@tonic-gate mdb_printf("%p is %p+%p, bufctl %p %s from %s\n", 20720Sstevel@tonic-gate w->w_addr, addr, w->w_addr - addr, baddr, 20730Sstevel@tonic-gate w->w_freemem == FALSE ? "allocated" : "freed", 20740Sstevel@tonic-gate w->w_cache->cache_name); 20750Sstevel@tonic-gate 20760Sstevel@tonic-gate if (count > 0) { 20770Sstevel@tonic-gate mdb_inc_indent(8); 20780Sstevel@tonic-gate mdb_printf("recent caller%s: %a%s", (count != 1)? "s":"", 20790Sstevel@tonic-gate callers[0], (count != 1)? ", ":"\n"); 20800Sstevel@tonic-gate for (i = 1; i < count; i++) 20810Sstevel@tonic-gate mdb_printf("%a%s", callers[i], 20820Sstevel@tonic-gate (i + 1 < count)? ", ":"\n"); 20830Sstevel@tonic-gate mdb_dec_indent(8); 20840Sstevel@tonic-gate } 20850Sstevel@tonic-gate } 20860Sstevel@tonic-gate 20870Sstevel@tonic-gate /*ARGSUSED*/ 20880Sstevel@tonic-gate static int 20890Sstevel@tonic-gate whatis_walk_kmem(uintptr_t addr, void *ignored, whatis_t *w) 20900Sstevel@tonic-gate { 20910Sstevel@tonic-gate if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize) 20920Sstevel@tonic-gate return (WALK_NEXT); 20930Sstevel@tonic-gate 20940Sstevel@tonic-gate whatis_print_kmem(addr, 0, w); 20950Sstevel@tonic-gate w->w_found++; 20960Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 20970Sstevel@tonic-gate } 20980Sstevel@tonic-gate 20990Sstevel@tonic-gate static int 21000Sstevel@tonic-gate whatis_walk_seg(uintptr_t addr, const vmem_seg_t *vs, whatis_t *w) 21010Sstevel@tonic-gate { 21020Sstevel@tonic-gate if (w->w_addr < vs->vs_start || w->w_addr >= vs->vs_end) 21030Sstevel@tonic-gate return (WALK_NEXT); 21040Sstevel@tonic-gate 21050Sstevel@tonic-gate mdb_printf("%p is %p+%p ", w->w_addr, 21060Sstevel@tonic-gate vs->vs_start, w->w_addr - vs->vs_start); 21070Sstevel@tonic-gate 21080Sstevel@tonic-gate /* 21090Sstevel@tonic-gate * Always provide the vmem_seg pointer if it has a stack trace. 21100Sstevel@tonic-gate */ 21110Sstevel@tonic-gate if (w->w_bufctl == TRUE || 21120Sstevel@tonic-gate (vs->vs_type == VMEM_ALLOC && vs->vs_depth != 0)) { 21130Sstevel@tonic-gate mdb_printf("(vmem_seg %p) ", addr); 21140Sstevel@tonic-gate } 21150Sstevel@tonic-gate 21160Sstevel@tonic-gate mdb_printf("%sfrom %s vmem arena\n", w->w_freemem == TRUE ? 21170Sstevel@tonic-gate "freed " : "", w->w_vmem->vm_name); 21180Sstevel@tonic-gate 21190Sstevel@tonic-gate w->w_found++; 21200Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 21210Sstevel@tonic-gate } 21220Sstevel@tonic-gate 21230Sstevel@tonic-gate static int 21240Sstevel@tonic-gate whatis_walk_vmem(uintptr_t addr, const vmem_t *vmem, whatis_t *w) 21250Sstevel@tonic-gate { 21260Sstevel@tonic-gate const char *nm = vmem->vm_name; 21270Sstevel@tonic-gate w->w_vmem = vmem; 21280Sstevel@tonic-gate w->w_freemem = FALSE; 21290Sstevel@tonic-gate 21300Sstevel@tonic-gate if (((vmem->vm_cflags & VMC_IDENTIFIER) != 0) ^ w->w_idspace) 21310Sstevel@tonic-gate return (WALK_NEXT); 21320Sstevel@tonic-gate 21330Sstevel@tonic-gate if (w->w_verbose) 21340Sstevel@tonic-gate mdb_printf("Searching vmem arena %s...\n", nm); 21350Sstevel@tonic-gate 21360Sstevel@tonic-gate if (mdb_pwalk("vmem_alloc", 21370Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) { 21380Sstevel@tonic-gate mdb_warn("can't walk vmem seg for %p", addr); 21390Sstevel@tonic-gate return (WALK_NEXT); 21400Sstevel@tonic-gate } 21410Sstevel@tonic-gate 21420Sstevel@tonic-gate if (w->w_found && w->w_all == FALSE) 21430Sstevel@tonic-gate return (WALK_DONE); 21440Sstevel@tonic-gate 21450Sstevel@tonic-gate if (w->w_verbose) 21460Sstevel@tonic-gate mdb_printf("Searching vmem arena %s for free virtual...\n", nm); 21470Sstevel@tonic-gate 21480Sstevel@tonic-gate w->w_freemem = TRUE; 21490Sstevel@tonic-gate 21500Sstevel@tonic-gate if (mdb_pwalk("vmem_free", 21510Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) { 21520Sstevel@tonic-gate mdb_warn("can't walk vmem seg for %p", addr); 21530Sstevel@tonic-gate return (WALK_NEXT); 21540Sstevel@tonic-gate } 21550Sstevel@tonic-gate 21560Sstevel@tonic-gate return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT); 21570Sstevel@tonic-gate } 21580Sstevel@tonic-gate 21590Sstevel@tonic-gate /*ARGSUSED*/ 21600Sstevel@tonic-gate static int 21610Sstevel@tonic-gate whatis_walk_bufctl(uintptr_t baddr, const kmem_bufctl_t *bcp, whatis_t *w) 21620Sstevel@tonic-gate { 21630Sstevel@tonic-gate uintptr_t addr; 21640Sstevel@tonic-gate 21650Sstevel@tonic-gate if (bcp == NULL) 21660Sstevel@tonic-gate return (WALK_NEXT); 21670Sstevel@tonic-gate 21680Sstevel@tonic-gate addr = (uintptr_t)bcp->bc_addr; 21690Sstevel@tonic-gate 21700Sstevel@tonic-gate if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize) 21710Sstevel@tonic-gate return (WALK_NEXT); 21720Sstevel@tonic-gate 21730Sstevel@tonic-gate whatis_print_kmem(addr, baddr, w); 21740Sstevel@tonic-gate w->w_found++; 21750Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 21760Sstevel@tonic-gate } 21770Sstevel@tonic-gate 21780Sstevel@tonic-gate /*ARGSUSED*/ 21790Sstevel@tonic-gate static int 21800Sstevel@tonic-gate whatis_walk_slab(uintptr_t saddr, const kmem_slab_t *sp, whatis_t *w) 21810Sstevel@tonic-gate { 21820Sstevel@tonic-gate uintptr_t base = P2ALIGN((uintptr_t)sp->slab_base, w->w_slab_align); 21830Sstevel@tonic-gate 21840Sstevel@tonic-gate if ((w->w_addr - base) >= w->w_cache->cache_slabsize) 21850Sstevel@tonic-gate return (WALK_NEXT); 21860Sstevel@tonic-gate 21870Sstevel@tonic-gate w->w_slab_found++; 21880Sstevel@tonic-gate return (WALK_DONE); 21890Sstevel@tonic-gate } 21900Sstevel@tonic-gate 21910Sstevel@tonic-gate static int 21920Sstevel@tonic-gate whatis_walk_cache(uintptr_t addr, const kmem_cache_t *c, whatis_t *w) 21930Sstevel@tonic-gate { 21940Sstevel@tonic-gate char *walk, *freewalk; 21950Sstevel@tonic-gate mdb_walk_cb_t func; 21960Sstevel@tonic-gate vmem_t *vmp = c->cache_arena; 21970Sstevel@tonic-gate 21980Sstevel@tonic-gate if (((c->cache_flags & VMC_IDENTIFIER) != 0) ^ w->w_idspace) 21990Sstevel@tonic-gate return (WALK_NEXT); 22000Sstevel@tonic-gate 22010Sstevel@tonic-gate if (w->w_bufctl == FALSE) { 22020Sstevel@tonic-gate walk = "kmem"; 22030Sstevel@tonic-gate freewalk = "freemem"; 22040Sstevel@tonic-gate func = (mdb_walk_cb_t)whatis_walk_kmem; 22050Sstevel@tonic-gate } else { 22060Sstevel@tonic-gate walk = "bufctl"; 22070Sstevel@tonic-gate freewalk = "freectl"; 22080Sstevel@tonic-gate func = (mdb_walk_cb_t)whatis_walk_bufctl; 22090Sstevel@tonic-gate } 22100Sstevel@tonic-gate 22110Sstevel@tonic-gate w->w_cache = c; 22120Sstevel@tonic-gate 22130Sstevel@tonic-gate if (w->w_verbose) 22140Sstevel@tonic-gate mdb_printf("Searching %s's slabs...\n", c->cache_name); 22150Sstevel@tonic-gate 22160Sstevel@tonic-gate /* 22170Sstevel@tonic-gate * Verify that the address is in one of the cache's slabs. If not, 22180Sstevel@tonic-gate * we can skip the more expensive walkers. (this is purely a 22190Sstevel@tonic-gate * heuristic -- as long as there are no false-negatives, we'll be fine) 22200Sstevel@tonic-gate * 22210Sstevel@tonic-gate * We try to get the cache's arena's quantum, since to accurately 22220Sstevel@tonic-gate * get the base of a slab, you have to align it to the quantum. If 22230Sstevel@tonic-gate * it doesn't look sensible, we fall back to not aligning. 22240Sstevel@tonic-gate */ 22250Sstevel@tonic-gate if (mdb_vread(&w->w_slab_align, sizeof (w->w_slab_align), 22260Sstevel@tonic-gate (uintptr_t)&vmp->vm_quantum) == -1) { 22270Sstevel@tonic-gate mdb_warn("unable to read %p->cache_arena->vm_quantum", c); 22280Sstevel@tonic-gate w->w_slab_align = 1; 22290Sstevel@tonic-gate } 22300Sstevel@tonic-gate 22310Sstevel@tonic-gate if ((c->cache_slabsize < w->w_slab_align) || w->w_slab_align == 0 || 22320Sstevel@tonic-gate (w->w_slab_align & (w->w_slab_align - 1))) { 22330Sstevel@tonic-gate mdb_warn("%p's arena has invalid quantum (0x%p)\n", c, 22340Sstevel@tonic-gate w->w_slab_align); 22350Sstevel@tonic-gate w->w_slab_align = 1; 22360Sstevel@tonic-gate } 22370Sstevel@tonic-gate 22380Sstevel@tonic-gate w->w_slab_found = 0; 22390Sstevel@tonic-gate if (mdb_pwalk("kmem_slab", (mdb_walk_cb_t)whatis_walk_slab, w, 22400Sstevel@tonic-gate addr) == -1) { 22410Sstevel@tonic-gate mdb_warn("can't find kmem_slab walker"); 22420Sstevel@tonic-gate return (WALK_DONE); 22430Sstevel@tonic-gate } 22440Sstevel@tonic-gate if (w->w_slab_found == 0) 22450Sstevel@tonic-gate return (WALK_NEXT); 22460Sstevel@tonic-gate 22470Sstevel@tonic-gate if (c->cache_flags & KMF_LITE) { 22480Sstevel@tonic-gate if (mdb_readvar(&w->w_kmem_lite_count, 22490Sstevel@tonic-gate "kmem_lite_count") == -1 || w->w_kmem_lite_count > 16) 22500Sstevel@tonic-gate w->w_kmem_lite_count = 0; 22510Sstevel@tonic-gate } 22520Sstevel@tonic-gate 22530Sstevel@tonic-gate if (w->w_verbose) 22540Sstevel@tonic-gate mdb_printf("Searching %s...\n", c->cache_name); 22550Sstevel@tonic-gate 22560Sstevel@tonic-gate w->w_freemem = FALSE; 22570Sstevel@tonic-gate 22580Sstevel@tonic-gate if (mdb_pwalk(walk, func, w, addr) == -1) { 22590Sstevel@tonic-gate mdb_warn("can't find %s walker", walk); 22600Sstevel@tonic-gate return (WALK_DONE); 22610Sstevel@tonic-gate } 22620Sstevel@tonic-gate 22630Sstevel@tonic-gate if (w->w_found && w->w_all == FALSE) 22640Sstevel@tonic-gate return (WALK_DONE); 22650Sstevel@tonic-gate 22660Sstevel@tonic-gate /* 22670Sstevel@tonic-gate * We have searched for allocated memory; now search for freed memory. 22680Sstevel@tonic-gate */ 22690Sstevel@tonic-gate if (w->w_verbose) 22700Sstevel@tonic-gate mdb_printf("Searching %s for free memory...\n", c->cache_name); 22710Sstevel@tonic-gate 22720Sstevel@tonic-gate w->w_freemem = TRUE; 22730Sstevel@tonic-gate 22740Sstevel@tonic-gate if (mdb_pwalk(freewalk, func, w, addr) == -1) { 22750Sstevel@tonic-gate mdb_warn("can't find %s walker", freewalk); 22760Sstevel@tonic-gate return (WALK_DONE); 22770Sstevel@tonic-gate } 22780Sstevel@tonic-gate 22790Sstevel@tonic-gate return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT); 22800Sstevel@tonic-gate } 22810Sstevel@tonic-gate 22820Sstevel@tonic-gate static int 22830Sstevel@tonic-gate whatis_walk_touch(uintptr_t addr, const kmem_cache_t *c, whatis_t *w) 22840Sstevel@tonic-gate { 22850Sstevel@tonic-gate if (c->cache_cflags & KMC_NOTOUCH) 22860Sstevel@tonic-gate return (WALK_NEXT); 22870Sstevel@tonic-gate 22880Sstevel@tonic-gate return (whatis_walk_cache(addr, c, w)); 22890Sstevel@tonic-gate } 22900Sstevel@tonic-gate 22910Sstevel@tonic-gate static int 22920Sstevel@tonic-gate whatis_walk_notouch(uintptr_t addr, const kmem_cache_t *c, whatis_t *w) 22930Sstevel@tonic-gate { 22940Sstevel@tonic-gate if (!(c->cache_cflags & KMC_NOTOUCH)) 22950Sstevel@tonic-gate return (WALK_NEXT); 22960Sstevel@tonic-gate 22970Sstevel@tonic-gate return (whatis_walk_cache(addr, c, w)); 22980Sstevel@tonic-gate } 22990Sstevel@tonic-gate 23000Sstevel@tonic-gate static int 23010Sstevel@tonic-gate whatis_walk_thread(uintptr_t addr, const kthread_t *t, whatis_t *w) 23020Sstevel@tonic-gate { 23030Sstevel@tonic-gate /* 23040Sstevel@tonic-gate * Often, one calls ::whatis on an address from a thread structure. 23050Sstevel@tonic-gate * We use this opportunity to short circuit this case... 23060Sstevel@tonic-gate */ 23070Sstevel@tonic-gate if (w->w_addr >= addr && w->w_addr < addr + sizeof (kthread_t)) { 23080Sstevel@tonic-gate mdb_printf("%p is %p+%p, allocated as a thread structure\n", 23090Sstevel@tonic-gate w->w_addr, addr, w->w_addr - addr); 23100Sstevel@tonic-gate w->w_found++; 23110Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 23120Sstevel@tonic-gate } 23130Sstevel@tonic-gate 23140Sstevel@tonic-gate if (w->w_addr < (uintptr_t)t->t_stkbase || 23150Sstevel@tonic-gate w->w_addr > (uintptr_t)t->t_stk) 23160Sstevel@tonic-gate return (WALK_NEXT); 23170Sstevel@tonic-gate 23180Sstevel@tonic-gate if (t->t_stkbase == NULL) 23190Sstevel@tonic-gate return (WALK_NEXT); 23200Sstevel@tonic-gate 23210Sstevel@tonic-gate mdb_printf("%p is in thread %p's stack%s\n", w->w_addr, addr, 23220Sstevel@tonic-gate stack_active(t, w->w_addr)); 23230Sstevel@tonic-gate 23240Sstevel@tonic-gate w->w_found++; 23250Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 23260Sstevel@tonic-gate } 23270Sstevel@tonic-gate 23280Sstevel@tonic-gate static int 23290Sstevel@tonic-gate whatis_walk_modctl(uintptr_t addr, const struct modctl *m, whatis_t *w) 23300Sstevel@tonic-gate { 23310Sstevel@tonic-gate struct module mod; 23320Sstevel@tonic-gate char name[MODMAXNAMELEN], *where; 23330Sstevel@tonic-gate char c[MDB_SYM_NAMLEN]; 23340Sstevel@tonic-gate Shdr shdr; 23350Sstevel@tonic-gate GElf_Sym sym; 23360Sstevel@tonic-gate 23370Sstevel@tonic-gate if (m->mod_mp == NULL) 23380Sstevel@tonic-gate return (WALK_NEXT); 23390Sstevel@tonic-gate 23400Sstevel@tonic-gate if (mdb_vread(&mod, sizeof (mod), (uintptr_t)m->mod_mp) == -1) { 23410Sstevel@tonic-gate mdb_warn("couldn't read modctl %p's module", addr); 23420Sstevel@tonic-gate return (WALK_NEXT); 23430Sstevel@tonic-gate } 23440Sstevel@tonic-gate 23450Sstevel@tonic-gate if (w->w_addr >= (uintptr_t)mod.text && 23460Sstevel@tonic-gate w->w_addr < (uintptr_t)mod.text + mod.text_size) { 23470Sstevel@tonic-gate where = "text segment"; 23480Sstevel@tonic-gate goto found; 23490Sstevel@tonic-gate } 23500Sstevel@tonic-gate 23510Sstevel@tonic-gate if (w->w_addr >= (uintptr_t)mod.data && 23520Sstevel@tonic-gate w->w_addr < (uintptr_t)mod.data + mod.data_size) { 23530Sstevel@tonic-gate where = "data segment"; 23540Sstevel@tonic-gate goto found; 23550Sstevel@tonic-gate } 23560Sstevel@tonic-gate 23570Sstevel@tonic-gate if (w->w_addr >= (uintptr_t)mod.bss && 23580Sstevel@tonic-gate w->w_addr < (uintptr_t)mod.bss + mod.bss_size) { 23590Sstevel@tonic-gate where = "bss"; 23600Sstevel@tonic-gate goto found; 23610Sstevel@tonic-gate } 23620Sstevel@tonic-gate 23630Sstevel@tonic-gate if (mdb_vread(&shdr, sizeof (shdr), (uintptr_t)mod.symhdr) == -1) { 23640Sstevel@tonic-gate mdb_warn("couldn't read symbol header for %p's module", addr); 23650Sstevel@tonic-gate return (WALK_NEXT); 23660Sstevel@tonic-gate } 23670Sstevel@tonic-gate 23680Sstevel@tonic-gate if (w->w_addr >= (uintptr_t)mod.symtbl && w->w_addr < 23690Sstevel@tonic-gate (uintptr_t)mod.symtbl + (uintptr_t)mod.nsyms * shdr.sh_entsize) { 23700Sstevel@tonic-gate where = "symtab"; 23710Sstevel@tonic-gate goto found; 23720Sstevel@tonic-gate } 23730Sstevel@tonic-gate 23740Sstevel@tonic-gate if (w->w_addr >= (uintptr_t)mod.symspace && 23750Sstevel@tonic-gate w->w_addr < (uintptr_t)mod.symspace + (uintptr_t)mod.symsize) { 23760Sstevel@tonic-gate where = "symspace"; 23770Sstevel@tonic-gate goto found; 23780Sstevel@tonic-gate } 23790Sstevel@tonic-gate 23800Sstevel@tonic-gate return (WALK_NEXT); 23810Sstevel@tonic-gate 23820Sstevel@tonic-gate found: 23830Sstevel@tonic-gate if (mdb_readstr(name, sizeof (name), (uintptr_t)m->mod_modname) == -1) 23840Sstevel@tonic-gate (void) mdb_snprintf(name, sizeof (name), "0x%p", addr); 23850Sstevel@tonic-gate 23860Sstevel@tonic-gate mdb_printf("%p is ", w->w_addr); 23870Sstevel@tonic-gate 23880Sstevel@tonic-gate /* 23890Sstevel@tonic-gate * If we found this address in a module, then there's a chance that 23900Sstevel@tonic-gate * it's actually a named symbol. Try the symbol lookup. 23910Sstevel@tonic-gate */ 23920Sstevel@tonic-gate if (mdb_lookup_by_addr(w->w_addr, MDB_SYM_FUZZY, c, sizeof (c), 23930Sstevel@tonic-gate &sym) != -1 && w->w_addr >= (uintptr_t)sym.st_value && 23940Sstevel@tonic-gate w->w_addr < (uintptr_t)sym.st_value + sym.st_size) { 23950Sstevel@tonic-gate mdb_printf("%s+%lx ", c, w->w_addr - (uintptr_t)sym.st_value); 23960Sstevel@tonic-gate } 23970Sstevel@tonic-gate 23980Sstevel@tonic-gate mdb_printf("in %s's %s\n", name, where); 23990Sstevel@tonic-gate 24000Sstevel@tonic-gate w->w_found++; 24010Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 24020Sstevel@tonic-gate } 24030Sstevel@tonic-gate 24040Sstevel@tonic-gate /*ARGSUSED*/ 24050Sstevel@tonic-gate static int 24060Sstevel@tonic-gate whatis_walk_page(uintptr_t addr, const void *ignored, whatis_t *w) 24070Sstevel@tonic-gate { 24080Sstevel@tonic-gate static int machsize = 0; 24090Sstevel@tonic-gate mdb_ctf_id_t id; 24100Sstevel@tonic-gate 24110Sstevel@tonic-gate if (machsize == 0) { 24120Sstevel@tonic-gate if (mdb_ctf_lookup_by_name("unix`page_t", &id) == 0) 24130Sstevel@tonic-gate machsize = mdb_ctf_type_size(id); 24140Sstevel@tonic-gate else { 24150Sstevel@tonic-gate mdb_warn("could not get size of page_t"); 24160Sstevel@tonic-gate machsize = sizeof (page_t); 24170Sstevel@tonic-gate } 24180Sstevel@tonic-gate } 24190Sstevel@tonic-gate 24200Sstevel@tonic-gate if (w->w_addr < addr || w->w_addr >= addr + machsize) 24210Sstevel@tonic-gate return (WALK_NEXT); 24220Sstevel@tonic-gate 24230Sstevel@tonic-gate mdb_printf("%p is %p+%p, allocated as a page structure\n", 24240Sstevel@tonic-gate w->w_addr, addr, w->w_addr - addr); 24250Sstevel@tonic-gate 24260Sstevel@tonic-gate w->w_found++; 24270Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 24280Sstevel@tonic-gate } 24290Sstevel@tonic-gate 24300Sstevel@tonic-gate int 24310Sstevel@tonic-gate whatis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 24320Sstevel@tonic-gate { 24330Sstevel@tonic-gate whatis_t w; 24340Sstevel@tonic-gate 24350Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 24360Sstevel@tonic-gate return (DCMD_USAGE); 24370Sstevel@tonic-gate 24380Sstevel@tonic-gate w.w_verbose = FALSE; 24390Sstevel@tonic-gate w.w_bufctl = FALSE; 24400Sstevel@tonic-gate w.w_all = FALSE; 24410Sstevel@tonic-gate w.w_idspace = FALSE; 24420Sstevel@tonic-gate 24430Sstevel@tonic-gate if (mdb_getopts(argc, argv, 24440Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &w.w_verbose, 24450Sstevel@tonic-gate 'a', MDB_OPT_SETBITS, TRUE, &w.w_all, 24460Sstevel@tonic-gate 'i', MDB_OPT_SETBITS, TRUE, &w.w_idspace, 24470Sstevel@tonic-gate 'b', MDB_OPT_SETBITS, TRUE, &w.w_bufctl, NULL) != argc) 24480Sstevel@tonic-gate return (DCMD_USAGE); 24490Sstevel@tonic-gate 24500Sstevel@tonic-gate w.w_addr = addr; 24510Sstevel@tonic-gate w.w_found = 0; 24520Sstevel@tonic-gate 24530Sstevel@tonic-gate if (w.w_verbose) 24540Sstevel@tonic-gate mdb_printf("Searching modules...\n"); 24550Sstevel@tonic-gate 24560Sstevel@tonic-gate if (!w.w_idspace) { 24570Sstevel@tonic-gate if (mdb_walk("modctl", (mdb_walk_cb_t)whatis_walk_modctl, &w) 24580Sstevel@tonic-gate == -1) { 24590Sstevel@tonic-gate mdb_warn("couldn't find modctl walker"); 24600Sstevel@tonic-gate return (DCMD_ERR); 24610Sstevel@tonic-gate } 24620Sstevel@tonic-gate 24630Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 24640Sstevel@tonic-gate return (DCMD_OK); 24650Sstevel@tonic-gate 24660Sstevel@tonic-gate /* 24670Sstevel@tonic-gate * Now search all thread stacks. Yes, this is a little weak; we 24680Sstevel@tonic-gate * can save a lot of work by first checking to see if the 24690Sstevel@tonic-gate * address is in segkp vs. segkmem. But hey, computers are 24700Sstevel@tonic-gate * fast. 24710Sstevel@tonic-gate */ 24720Sstevel@tonic-gate if (w.w_verbose) 24730Sstevel@tonic-gate mdb_printf("Searching threads...\n"); 24740Sstevel@tonic-gate 24750Sstevel@tonic-gate if (mdb_walk("thread", (mdb_walk_cb_t)whatis_walk_thread, &w) 24760Sstevel@tonic-gate == -1) { 24770Sstevel@tonic-gate mdb_warn("couldn't find thread walker"); 24780Sstevel@tonic-gate return (DCMD_ERR); 24790Sstevel@tonic-gate } 24800Sstevel@tonic-gate 24810Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 24820Sstevel@tonic-gate return (DCMD_OK); 24830Sstevel@tonic-gate 24840Sstevel@tonic-gate if (w.w_verbose) 24850Sstevel@tonic-gate mdb_printf("Searching page structures...\n"); 24860Sstevel@tonic-gate 24870Sstevel@tonic-gate if (mdb_walk("page", (mdb_walk_cb_t)whatis_walk_page, &w) 24880Sstevel@tonic-gate == -1) { 24890Sstevel@tonic-gate mdb_warn("couldn't find page walker"); 24900Sstevel@tonic-gate return (DCMD_ERR); 24910Sstevel@tonic-gate } 24920Sstevel@tonic-gate 24930Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 24940Sstevel@tonic-gate return (DCMD_OK); 24950Sstevel@tonic-gate } 24960Sstevel@tonic-gate 24970Sstevel@tonic-gate if (mdb_walk("kmem_cache", 24980Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_touch, &w) == -1) { 24990Sstevel@tonic-gate mdb_warn("couldn't find kmem_cache walker"); 25000Sstevel@tonic-gate return (DCMD_ERR); 25010Sstevel@tonic-gate } 25020Sstevel@tonic-gate 25030Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 25040Sstevel@tonic-gate return (DCMD_OK); 25050Sstevel@tonic-gate 25060Sstevel@tonic-gate if (mdb_walk("kmem_cache", 25070Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_notouch, &w) == -1) { 25080Sstevel@tonic-gate mdb_warn("couldn't find kmem_cache walker"); 25090Sstevel@tonic-gate return (DCMD_ERR); 25100Sstevel@tonic-gate } 25110Sstevel@tonic-gate 25120Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 25130Sstevel@tonic-gate return (DCMD_OK); 25140Sstevel@tonic-gate 25150Sstevel@tonic-gate if (mdb_walk("vmem_postfix", 25160Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_vmem, &w) == -1) { 25170Sstevel@tonic-gate mdb_warn("couldn't find vmem_postfix walker"); 25180Sstevel@tonic-gate return (DCMD_ERR); 25190Sstevel@tonic-gate } 25200Sstevel@tonic-gate 25210Sstevel@tonic-gate if (w.w_found == 0) 25220Sstevel@tonic-gate mdb_printf("%p is unknown\n", addr); 25230Sstevel@tonic-gate 25240Sstevel@tonic-gate return (DCMD_OK); 25250Sstevel@tonic-gate } 25260Sstevel@tonic-gate 25270Sstevel@tonic-gate void 25280Sstevel@tonic-gate whatis_help(void) 25290Sstevel@tonic-gate { 25300Sstevel@tonic-gate mdb_printf( 25310Sstevel@tonic-gate "Given a virtual address, attempt to determine where it came\n" 25320Sstevel@tonic-gate "from.\n" 25330Sstevel@tonic-gate "\n" 25340Sstevel@tonic-gate "\t-v\tVerbose output; display caches/arenas/etc as they are\n" 25350Sstevel@tonic-gate "\t\tsearched\n" 25360Sstevel@tonic-gate "\t-a\tFind all possible sources. Default behavior is to stop at\n" 25370Sstevel@tonic-gate "\t\tthe first (most specific) source.\n" 25380Sstevel@tonic-gate "\t-i\tSearch only identifier arenas and caches. By default\n" 25390Sstevel@tonic-gate "\t\tthese are ignored.\n" 25400Sstevel@tonic-gate "\t-b\tReport bufctls and vmem_segs for matches in kmem and vmem,\n" 25410Sstevel@tonic-gate "\t\trespectively. Warning: if the buffer exists, but does not\n" 25420Sstevel@tonic-gate "\t\thave a bufctl, it will not be reported.\n"); 25430Sstevel@tonic-gate } 25440Sstevel@tonic-gate 25450Sstevel@tonic-gate typedef struct kmem_log_cpu { 25460Sstevel@tonic-gate uintptr_t kmc_low; 25470Sstevel@tonic-gate uintptr_t kmc_high; 25480Sstevel@tonic-gate } kmem_log_cpu_t; 25490Sstevel@tonic-gate 25500Sstevel@tonic-gate typedef struct kmem_log_data { 25510Sstevel@tonic-gate uintptr_t kmd_addr; 25520Sstevel@tonic-gate kmem_log_cpu_t *kmd_cpu; 25530Sstevel@tonic-gate } kmem_log_data_t; 25540Sstevel@tonic-gate 25550Sstevel@tonic-gate int 25560Sstevel@tonic-gate kmem_log_walk(uintptr_t addr, const kmem_bufctl_audit_t *b, 25570Sstevel@tonic-gate kmem_log_data_t *kmd) 25580Sstevel@tonic-gate { 25590Sstevel@tonic-gate int i; 25600Sstevel@tonic-gate kmem_log_cpu_t *kmc = kmd->kmd_cpu; 25610Sstevel@tonic-gate size_t bufsize; 25620Sstevel@tonic-gate 25630Sstevel@tonic-gate for (i = 0; i < NCPU; i++) { 25640Sstevel@tonic-gate if (addr >= kmc[i].kmc_low && addr < kmc[i].kmc_high) 25650Sstevel@tonic-gate break; 25660Sstevel@tonic-gate } 25670Sstevel@tonic-gate 25680Sstevel@tonic-gate if (kmd->kmd_addr) { 25690Sstevel@tonic-gate if (b->bc_cache == NULL) 25700Sstevel@tonic-gate return (WALK_NEXT); 25710Sstevel@tonic-gate 25720Sstevel@tonic-gate if (mdb_vread(&bufsize, sizeof (bufsize), 25730Sstevel@tonic-gate (uintptr_t)&b->bc_cache->cache_bufsize) == -1) { 25740Sstevel@tonic-gate mdb_warn( 25750Sstevel@tonic-gate "failed to read cache_bufsize for cache at %p", 25760Sstevel@tonic-gate b->bc_cache); 25770Sstevel@tonic-gate return (WALK_ERR); 25780Sstevel@tonic-gate } 25790Sstevel@tonic-gate 25800Sstevel@tonic-gate if (kmd->kmd_addr < (uintptr_t)b->bc_addr || 25810Sstevel@tonic-gate kmd->kmd_addr >= (uintptr_t)b->bc_addr + bufsize) 25820Sstevel@tonic-gate return (WALK_NEXT); 25830Sstevel@tonic-gate } 25840Sstevel@tonic-gate 25850Sstevel@tonic-gate if (i == NCPU) 25860Sstevel@tonic-gate mdb_printf(" "); 25870Sstevel@tonic-gate else 25880Sstevel@tonic-gate mdb_printf("%3d", i); 25890Sstevel@tonic-gate 25900Sstevel@tonic-gate mdb_printf(" %0?p %0?p %16llx %0?p\n", addr, b->bc_addr, 25910Sstevel@tonic-gate b->bc_timestamp, b->bc_thread); 25920Sstevel@tonic-gate 25930Sstevel@tonic-gate return (WALK_NEXT); 25940Sstevel@tonic-gate } 25950Sstevel@tonic-gate 25960Sstevel@tonic-gate /*ARGSUSED*/ 25970Sstevel@tonic-gate int 25980Sstevel@tonic-gate kmem_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 25990Sstevel@tonic-gate { 26000Sstevel@tonic-gate kmem_log_header_t lh; 26010Sstevel@tonic-gate kmem_cpu_log_header_t clh; 26020Sstevel@tonic-gate uintptr_t lhp, clhp; 26030Sstevel@tonic-gate int ncpus; 26040Sstevel@tonic-gate uintptr_t *cpu; 26050Sstevel@tonic-gate GElf_Sym sym; 26060Sstevel@tonic-gate kmem_log_cpu_t *kmc; 26070Sstevel@tonic-gate int i; 26080Sstevel@tonic-gate kmem_log_data_t kmd; 26090Sstevel@tonic-gate uint_t opt_b = FALSE; 26100Sstevel@tonic-gate 26110Sstevel@tonic-gate if (mdb_getopts(argc, argv, 26120Sstevel@tonic-gate 'b', MDB_OPT_SETBITS, TRUE, &opt_b, NULL) != argc) 26130Sstevel@tonic-gate return (DCMD_USAGE); 26140Sstevel@tonic-gate 26150Sstevel@tonic-gate if (mdb_readvar(&lhp, "kmem_transaction_log") == -1) { 26160Sstevel@tonic-gate mdb_warn("failed to read 'kmem_transaction_log'"); 26170Sstevel@tonic-gate return (DCMD_ERR); 26180Sstevel@tonic-gate } 26190Sstevel@tonic-gate 26200Sstevel@tonic-gate if (lhp == NULL) { 26210Sstevel@tonic-gate mdb_warn("no kmem transaction log\n"); 26220Sstevel@tonic-gate return (DCMD_ERR); 26230Sstevel@tonic-gate } 26240Sstevel@tonic-gate 26250Sstevel@tonic-gate mdb_readvar(&ncpus, "ncpus"); 26260Sstevel@tonic-gate 26270Sstevel@tonic-gate if (mdb_vread(&lh, sizeof (kmem_log_header_t), lhp) == -1) { 26280Sstevel@tonic-gate mdb_warn("failed to read log header at %p", lhp); 26290Sstevel@tonic-gate return (DCMD_ERR); 26300Sstevel@tonic-gate } 26310Sstevel@tonic-gate 26320Sstevel@tonic-gate clhp = lhp + ((uintptr_t)&lh.lh_cpu[0] - (uintptr_t)&lh); 26330Sstevel@tonic-gate 26340Sstevel@tonic-gate cpu = mdb_alloc(sizeof (uintptr_t) * NCPU, UM_SLEEP | UM_GC); 26350Sstevel@tonic-gate 26360Sstevel@tonic-gate if (mdb_lookup_by_name("cpu", &sym) == -1) { 26370Sstevel@tonic-gate mdb_warn("couldn't find 'cpu' array"); 26380Sstevel@tonic-gate return (DCMD_ERR); 26390Sstevel@tonic-gate } 26400Sstevel@tonic-gate 26410Sstevel@tonic-gate if (sym.st_size != NCPU * sizeof (uintptr_t)) { 26420Sstevel@tonic-gate mdb_warn("expected 'cpu' to be of size %d; found %d\n", 26430Sstevel@tonic-gate NCPU * sizeof (uintptr_t), sym.st_size); 26440Sstevel@tonic-gate return (DCMD_ERR); 26450Sstevel@tonic-gate } 26460Sstevel@tonic-gate 26470Sstevel@tonic-gate if (mdb_vread(cpu, sym.st_size, (uintptr_t)sym.st_value) == -1) { 26480Sstevel@tonic-gate mdb_warn("failed to read cpu array at %p", sym.st_value); 26490Sstevel@tonic-gate return (DCMD_ERR); 26500Sstevel@tonic-gate } 26510Sstevel@tonic-gate 26520Sstevel@tonic-gate kmc = mdb_zalloc(sizeof (kmem_log_cpu_t) * NCPU, UM_SLEEP | UM_GC); 26530Sstevel@tonic-gate kmd.kmd_addr = NULL; 26540Sstevel@tonic-gate kmd.kmd_cpu = kmc; 26550Sstevel@tonic-gate 26560Sstevel@tonic-gate for (i = 0; i < NCPU; i++) { 26570Sstevel@tonic-gate 26580Sstevel@tonic-gate if (cpu[i] == NULL) 26590Sstevel@tonic-gate continue; 26600Sstevel@tonic-gate 26610Sstevel@tonic-gate if (mdb_vread(&clh, sizeof (clh), clhp) == -1) { 26620Sstevel@tonic-gate mdb_warn("cannot read cpu %d's log header at %p", 26630Sstevel@tonic-gate i, clhp); 26640Sstevel@tonic-gate return (DCMD_ERR); 26650Sstevel@tonic-gate } 26660Sstevel@tonic-gate 26670Sstevel@tonic-gate kmc[i].kmc_low = clh.clh_chunk * lh.lh_chunksize + 26680Sstevel@tonic-gate (uintptr_t)lh.lh_base; 26690Sstevel@tonic-gate kmc[i].kmc_high = (uintptr_t)clh.clh_current; 26700Sstevel@tonic-gate 26710Sstevel@tonic-gate clhp += sizeof (kmem_cpu_log_header_t); 26720Sstevel@tonic-gate } 26730Sstevel@tonic-gate 26740Sstevel@tonic-gate mdb_printf("%3s %-?s %-?s %16s %-?s\n", "CPU", "ADDR", "BUFADDR", 26750Sstevel@tonic-gate "TIMESTAMP", "THREAD"); 26760Sstevel@tonic-gate 26770Sstevel@tonic-gate /* 26780Sstevel@tonic-gate * If we have been passed an address, print out only log entries 26790Sstevel@tonic-gate * corresponding to that address. If opt_b is specified, then interpret 26800Sstevel@tonic-gate * the address as a bufctl. 26810Sstevel@tonic-gate */ 26820Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 26830Sstevel@tonic-gate kmem_bufctl_audit_t b; 26840Sstevel@tonic-gate 26850Sstevel@tonic-gate if (opt_b) { 26860Sstevel@tonic-gate kmd.kmd_addr = addr; 26870Sstevel@tonic-gate } else { 26880Sstevel@tonic-gate if (mdb_vread(&b, 26890Sstevel@tonic-gate sizeof (kmem_bufctl_audit_t), addr) == -1) { 26900Sstevel@tonic-gate mdb_warn("failed to read bufctl at %p", addr); 26910Sstevel@tonic-gate return (DCMD_ERR); 26920Sstevel@tonic-gate } 26930Sstevel@tonic-gate 26940Sstevel@tonic-gate (void) kmem_log_walk(addr, &b, &kmd); 26950Sstevel@tonic-gate 26960Sstevel@tonic-gate return (DCMD_OK); 26970Sstevel@tonic-gate } 26980Sstevel@tonic-gate } 26990Sstevel@tonic-gate 27000Sstevel@tonic-gate if (mdb_walk("kmem_log", (mdb_walk_cb_t)kmem_log_walk, &kmd) == -1) { 27010Sstevel@tonic-gate mdb_warn("can't find kmem log walker"); 27020Sstevel@tonic-gate return (DCMD_ERR); 27030Sstevel@tonic-gate } 27040Sstevel@tonic-gate 27050Sstevel@tonic-gate return (DCMD_OK); 27060Sstevel@tonic-gate } 27070Sstevel@tonic-gate 27080Sstevel@tonic-gate typedef struct bufctl_history_cb { 27090Sstevel@tonic-gate int bhc_flags; 27100Sstevel@tonic-gate int bhc_argc; 27110Sstevel@tonic-gate const mdb_arg_t *bhc_argv; 27120Sstevel@tonic-gate int bhc_ret; 27130Sstevel@tonic-gate } bufctl_history_cb_t; 27140Sstevel@tonic-gate 27150Sstevel@tonic-gate /*ARGSUSED*/ 27160Sstevel@tonic-gate static int 27170Sstevel@tonic-gate bufctl_history_callback(uintptr_t addr, const void *ign, void *arg) 27180Sstevel@tonic-gate { 27190Sstevel@tonic-gate bufctl_history_cb_t *bhc = arg; 27200Sstevel@tonic-gate 27210Sstevel@tonic-gate bhc->bhc_ret = 27220Sstevel@tonic-gate bufctl(addr, bhc->bhc_flags, bhc->bhc_argc, bhc->bhc_argv); 27230Sstevel@tonic-gate 27240Sstevel@tonic-gate bhc->bhc_flags &= ~DCMD_LOOPFIRST; 27250Sstevel@tonic-gate 27260Sstevel@tonic-gate return ((bhc->bhc_ret == DCMD_OK)? WALK_NEXT : WALK_DONE); 27270Sstevel@tonic-gate } 27280Sstevel@tonic-gate 27290Sstevel@tonic-gate void 27300Sstevel@tonic-gate bufctl_help(void) 27310Sstevel@tonic-gate { 27320Sstevel@tonic-gate mdb_printf("%s\n", 27330Sstevel@tonic-gate "Display the contents of kmem_bufctl_audit_ts, with optional filtering.\n"); 27340Sstevel@tonic-gate mdb_dec_indent(2); 27350Sstevel@tonic-gate mdb_printf("%<b>OPTIONS%</b>\n"); 27360Sstevel@tonic-gate mdb_inc_indent(2); 27370Sstevel@tonic-gate mdb_printf("%s", 27380Sstevel@tonic-gate " -v Display the full content of the bufctl, including its stack trace\n" 27390Sstevel@tonic-gate " -h retrieve the bufctl's transaction history, if available\n" 27400Sstevel@tonic-gate " -a addr\n" 27410Sstevel@tonic-gate " filter out bufctls not involving the buffer at addr\n" 27420Sstevel@tonic-gate " -c caller\n" 27430Sstevel@tonic-gate " filter out bufctls without the function/PC in their stack trace\n" 27440Sstevel@tonic-gate " -e earliest\n" 27450Sstevel@tonic-gate " filter out bufctls timestamped before earliest\n" 27460Sstevel@tonic-gate " -l latest\n" 27470Sstevel@tonic-gate " filter out bufctls timestamped after latest\n" 27480Sstevel@tonic-gate " -t thread\n" 27490Sstevel@tonic-gate " filter out bufctls not involving thread\n"); 27500Sstevel@tonic-gate } 27510Sstevel@tonic-gate 27520Sstevel@tonic-gate int 27530Sstevel@tonic-gate bufctl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 27540Sstevel@tonic-gate { 27550Sstevel@tonic-gate kmem_bufctl_audit_t bc; 27560Sstevel@tonic-gate uint_t verbose = FALSE; 27570Sstevel@tonic-gate uint_t history = FALSE; 27580Sstevel@tonic-gate uint_t in_history = FALSE; 27590Sstevel@tonic-gate uintptr_t caller = NULL, thread = NULL; 27600Sstevel@tonic-gate uintptr_t laddr, haddr, baddr = NULL; 27610Sstevel@tonic-gate hrtime_t earliest = 0, latest = 0; 27620Sstevel@tonic-gate int i, depth; 27630Sstevel@tonic-gate char c[MDB_SYM_NAMLEN]; 27640Sstevel@tonic-gate GElf_Sym sym; 27650Sstevel@tonic-gate 27660Sstevel@tonic-gate if (mdb_getopts(argc, argv, 27670Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &verbose, 27680Sstevel@tonic-gate 'h', MDB_OPT_SETBITS, TRUE, &history, 27690Sstevel@tonic-gate 'H', MDB_OPT_SETBITS, TRUE, &in_history, /* internal */ 27700Sstevel@tonic-gate 'c', MDB_OPT_UINTPTR, &caller, 27710Sstevel@tonic-gate 't', MDB_OPT_UINTPTR, &thread, 27720Sstevel@tonic-gate 'e', MDB_OPT_UINT64, &earliest, 27730Sstevel@tonic-gate 'l', MDB_OPT_UINT64, &latest, 27740Sstevel@tonic-gate 'a', MDB_OPT_UINTPTR, &baddr, NULL) != argc) 27750Sstevel@tonic-gate return (DCMD_USAGE); 27760Sstevel@tonic-gate 27770Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 27780Sstevel@tonic-gate return (DCMD_USAGE); 27790Sstevel@tonic-gate 27800Sstevel@tonic-gate if (in_history && !history) 27810Sstevel@tonic-gate return (DCMD_USAGE); 27820Sstevel@tonic-gate 27830Sstevel@tonic-gate if (history && !in_history) { 27840Sstevel@tonic-gate mdb_arg_t *nargv = mdb_zalloc(sizeof (*nargv) * (argc + 1), 27850Sstevel@tonic-gate UM_SLEEP | UM_GC); 27860Sstevel@tonic-gate bufctl_history_cb_t bhc; 27870Sstevel@tonic-gate 27880Sstevel@tonic-gate nargv[0].a_type = MDB_TYPE_STRING; 27890Sstevel@tonic-gate nargv[0].a_un.a_str = "-H"; /* prevent recursion */ 27900Sstevel@tonic-gate 27910Sstevel@tonic-gate for (i = 0; i < argc; i++) 27920Sstevel@tonic-gate nargv[i + 1] = argv[i]; 27930Sstevel@tonic-gate 27940Sstevel@tonic-gate /* 27950Sstevel@tonic-gate * When in history mode, we treat each element as if it 27960Sstevel@tonic-gate * were in a seperate loop, so that the headers group 27970Sstevel@tonic-gate * bufctls with similar histories. 27980Sstevel@tonic-gate */ 27990Sstevel@tonic-gate bhc.bhc_flags = flags | DCMD_LOOP | DCMD_LOOPFIRST; 28000Sstevel@tonic-gate bhc.bhc_argc = argc + 1; 28010Sstevel@tonic-gate bhc.bhc_argv = nargv; 28020Sstevel@tonic-gate bhc.bhc_ret = DCMD_OK; 28030Sstevel@tonic-gate 28040Sstevel@tonic-gate if (mdb_pwalk("bufctl_history", bufctl_history_callback, &bhc, 28050Sstevel@tonic-gate addr) == -1) { 28060Sstevel@tonic-gate mdb_warn("unable to walk bufctl_history"); 28070Sstevel@tonic-gate return (DCMD_ERR); 28080Sstevel@tonic-gate } 28090Sstevel@tonic-gate 28100Sstevel@tonic-gate if (bhc.bhc_ret == DCMD_OK && !(flags & DCMD_PIPE_OUT)) 28110Sstevel@tonic-gate mdb_printf("\n"); 28120Sstevel@tonic-gate 28130Sstevel@tonic-gate return (bhc.bhc_ret); 28140Sstevel@tonic-gate } 28150Sstevel@tonic-gate 28160Sstevel@tonic-gate if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) { 28170Sstevel@tonic-gate if (verbose) { 28180Sstevel@tonic-gate mdb_printf("%16s %16s %16s %16s\n" 28190Sstevel@tonic-gate "%<u>%16s %16s %16s %16s%</u>\n", 28200Sstevel@tonic-gate "ADDR", "BUFADDR", "TIMESTAMP", "THREAD", 28210Sstevel@tonic-gate "", "CACHE", "LASTLOG", "CONTENTS"); 28220Sstevel@tonic-gate } else { 28230Sstevel@tonic-gate mdb_printf("%<u>%-?s %-?s %-12s %-?s %s%</u>\n", 28240Sstevel@tonic-gate "ADDR", "BUFADDR", "TIMESTAMP", "THREAD", "CALLER"); 28250Sstevel@tonic-gate } 28260Sstevel@tonic-gate } 28270Sstevel@tonic-gate 28280Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), addr) == -1) { 28290Sstevel@tonic-gate mdb_warn("couldn't read bufctl at %p", addr); 28300Sstevel@tonic-gate return (DCMD_ERR); 28310Sstevel@tonic-gate } 28320Sstevel@tonic-gate 28330Sstevel@tonic-gate /* 28340Sstevel@tonic-gate * Guard against bogus bc_depth in case the bufctl is corrupt or 28350Sstevel@tonic-gate * the address does not really refer to a bufctl. 28360Sstevel@tonic-gate */ 28370Sstevel@tonic-gate depth = MIN(bc.bc_depth, KMEM_STACK_DEPTH); 28380Sstevel@tonic-gate 28390Sstevel@tonic-gate if (caller != NULL) { 28400Sstevel@tonic-gate laddr = caller; 28410Sstevel@tonic-gate haddr = caller + sizeof (caller); 28420Sstevel@tonic-gate 28430Sstevel@tonic-gate if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c, sizeof (c), 28440Sstevel@tonic-gate &sym) != -1 && caller == (uintptr_t)sym.st_value) { 28450Sstevel@tonic-gate /* 28460Sstevel@tonic-gate * We were provided an exact symbol value; any 28470Sstevel@tonic-gate * address in the function is valid. 28480Sstevel@tonic-gate */ 28490Sstevel@tonic-gate laddr = (uintptr_t)sym.st_value; 28500Sstevel@tonic-gate haddr = (uintptr_t)sym.st_value + sym.st_size; 28510Sstevel@tonic-gate } 28520Sstevel@tonic-gate 28530Sstevel@tonic-gate for (i = 0; i < depth; i++) 28540Sstevel@tonic-gate if (bc.bc_stack[i] >= laddr && bc.bc_stack[i] < haddr) 28550Sstevel@tonic-gate break; 28560Sstevel@tonic-gate 28570Sstevel@tonic-gate if (i == depth) 28580Sstevel@tonic-gate return (DCMD_OK); 28590Sstevel@tonic-gate } 28600Sstevel@tonic-gate 28610Sstevel@tonic-gate if (thread != NULL && (uintptr_t)bc.bc_thread != thread) 28620Sstevel@tonic-gate return (DCMD_OK); 28630Sstevel@tonic-gate 28640Sstevel@tonic-gate if (earliest != 0 && bc.bc_timestamp < earliest) 28650Sstevel@tonic-gate return (DCMD_OK); 28660Sstevel@tonic-gate 28670Sstevel@tonic-gate if (latest != 0 && bc.bc_timestamp > latest) 28680Sstevel@tonic-gate return (DCMD_OK); 28690Sstevel@tonic-gate 28700Sstevel@tonic-gate if (baddr != 0 && (uintptr_t)bc.bc_addr != baddr) 28710Sstevel@tonic-gate return (DCMD_OK); 28720Sstevel@tonic-gate 28730Sstevel@tonic-gate if (flags & DCMD_PIPE_OUT) { 28740Sstevel@tonic-gate mdb_printf("%#lr\n", addr); 28750Sstevel@tonic-gate return (DCMD_OK); 28760Sstevel@tonic-gate } 28770Sstevel@tonic-gate 28780Sstevel@tonic-gate if (verbose) { 28790Sstevel@tonic-gate mdb_printf( 28800Sstevel@tonic-gate "%<b>%16p%</b> %16p %16llx %16p\n" 28810Sstevel@tonic-gate "%16s %16p %16p %16p\n", 28820Sstevel@tonic-gate addr, bc.bc_addr, bc.bc_timestamp, bc.bc_thread, 28830Sstevel@tonic-gate "", bc.bc_cache, bc.bc_lastlog, bc.bc_contents); 28840Sstevel@tonic-gate 28850Sstevel@tonic-gate mdb_inc_indent(17); 28860Sstevel@tonic-gate for (i = 0; i < depth; i++) 28870Sstevel@tonic-gate mdb_printf("%a\n", bc.bc_stack[i]); 28880Sstevel@tonic-gate mdb_dec_indent(17); 28890Sstevel@tonic-gate mdb_printf("\n"); 28900Sstevel@tonic-gate } else { 28910Sstevel@tonic-gate mdb_printf("%0?p %0?p %12llx %0?p", addr, bc.bc_addr, 28920Sstevel@tonic-gate bc.bc_timestamp, bc.bc_thread); 28930Sstevel@tonic-gate 28940Sstevel@tonic-gate for (i = 0; i < depth; i++) { 28950Sstevel@tonic-gate if (mdb_lookup_by_addr(bc.bc_stack[i], 28960Sstevel@tonic-gate MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1) 28970Sstevel@tonic-gate continue; 28980Sstevel@tonic-gate if (strncmp(c, "kmem_", 5) == 0) 28990Sstevel@tonic-gate continue; 29000Sstevel@tonic-gate mdb_printf(" %a\n", bc.bc_stack[i]); 29010Sstevel@tonic-gate break; 29020Sstevel@tonic-gate } 29030Sstevel@tonic-gate 29040Sstevel@tonic-gate if (i >= depth) 29050Sstevel@tonic-gate mdb_printf("\n"); 29060Sstevel@tonic-gate } 29070Sstevel@tonic-gate 29080Sstevel@tonic-gate return (DCMD_OK); 29090Sstevel@tonic-gate } 29100Sstevel@tonic-gate 29110Sstevel@tonic-gate typedef struct kmem_verify { 29120Sstevel@tonic-gate uint64_t *kmv_buf; /* buffer to read cache contents into */ 29130Sstevel@tonic-gate size_t kmv_size; /* number of bytes in kmv_buf */ 29140Sstevel@tonic-gate int kmv_corruption; /* > 0 if corruption found. */ 29150Sstevel@tonic-gate int kmv_besilent; /* report actual corruption sites */ 29160Sstevel@tonic-gate struct kmem_cache kmv_cache; /* the cache we're operating on */ 29170Sstevel@tonic-gate } kmem_verify_t; 29180Sstevel@tonic-gate 29190Sstevel@tonic-gate /* 29200Sstevel@tonic-gate * verify_pattern() 29210Sstevel@tonic-gate * verify that buf is filled with the pattern pat. 29220Sstevel@tonic-gate */ 29230Sstevel@tonic-gate static int64_t 29240Sstevel@tonic-gate verify_pattern(uint64_t *buf_arg, size_t size, uint64_t pat) 29250Sstevel@tonic-gate { 29260Sstevel@tonic-gate /*LINTED*/ 29270Sstevel@tonic-gate uint64_t *bufend = (uint64_t *)((char *)buf_arg + size); 29280Sstevel@tonic-gate uint64_t *buf; 29290Sstevel@tonic-gate 29300Sstevel@tonic-gate for (buf = buf_arg; buf < bufend; buf++) 29310Sstevel@tonic-gate if (*buf != pat) 29320Sstevel@tonic-gate return ((uintptr_t)buf - (uintptr_t)buf_arg); 29330Sstevel@tonic-gate return (-1); 29340Sstevel@tonic-gate } 29350Sstevel@tonic-gate 29360Sstevel@tonic-gate /* 29370Sstevel@tonic-gate * verify_buftag() 29380Sstevel@tonic-gate * verify that btp->bt_bxstat == (bcp ^ pat) 29390Sstevel@tonic-gate */ 29400Sstevel@tonic-gate static int 29410Sstevel@tonic-gate verify_buftag(kmem_buftag_t *btp, uintptr_t pat) 29420Sstevel@tonic-gate { 29430Sstevel@tonic-gate return (btp->bt_bxstat == ((intptr_t)btp->bt_bufctl ^ pat) ? 0 : -1); 29440Sstevel@tonic-gate } 29450Sstevel@tonic-gate 29460Sstevel@tonic-gate /* 29470Sstevel@tonic-gate * verify_free() 29480Sstevel@tonic-gate * verify the integrity of a free block of memory by checking 29490Sstevel@tonic-gate * that it is filled with 0xdeadbeef and that its buftag is sane. 29500Sstevel@tonic-gate */ 29510Sstevel@tonic-gate /*ARGSUSED1*/ 29520Sstevel@tonic-gate static int 29530Sstevel@tonic-gate verify_free(uintptr_t addr, const void *data, void *private) 29540Sstevel@tonic-gate { 29550Sstevel@tonic-gate kmem_verify_t *kmv = (kmem_verify_t *)private; 29560Sstevel@tonic-gate uint64_t *buf = kmv->kmv_buf; /* buf to validate */ 29570Sstevel@tonic-gate int64_t corrupt; /* corruption offset */ 29580Sstevel@tonic-gate kmem_buftag_t *buftagp; /* ptr to buftag */ 29590Sstevel@tonic-gate kmem_cache_t *cp = &kmv->kmv_cache; 29600Sstevel@tonic-gate int besilent = kmv->kmv_besilent; 29610Sstevel@tonic-gate 29620Sstevel@tonic-gate /*LINTED*/ 29630Sstevel@tonic-gate buftagp = KMEM_BUFTAG(cp, buf); 29640Sstevel@tonic-gate 29650Sstevel@tonic-gate /* 29660Sstevel@tonic-gate * Read the buffer to check. 29670Sstevel@tonic-gate */ 29680Sstevel@tonic-gate if (mdb_vread(buf, kmv->kmv_size, addr) == -1) { 29690Sstevel@tonic-gate if (!besilent) 29700Sstevel@tonic-gate mdb_warn("couldn't read %p", addr); 29710Sstevel@tonic-gate return (WALK_NEXT); 29720Sstevel@tonic-gate } 29730Sstevel@tonic-gate 29740Sstevel@tonic-gate if ((corrupt = verify_pattern(buf, cp->cache_verify, 29750Sstevel@tonic-gate KMEM_FREE_PATTERN)) >= 0) { 29760Sstevel@tonic-gate if (!besilent) 29770Sstevel@tonic-gate mdb_printf("buffer %p (free) seems corrupted, at %p\n", 29780Sstevel@tonic-gate addr, (uintptr_t)addr + corrupt); 29790Sstevel@tonic-gate goto corrupt; 29800Sstevel@tonic-gate } 29810Sstevel@tonic-gate /* 29820Sstevel@tonic-gate * When KMF_LITE is set, buftagp->bt_redzone is used to hold 29830Sstevel@tonic-gate * the first bytes of the buffer, hence we cannot check for red 29840Sstevel@tonic-gate * zone corruption. 29850Sstevel@tonic-gate */ 29860Sstevel@tonic-gate if ((cp->cache_flags & (KMF_HASH | KMF_LITE)) == KMF_HASH && 29870Sstevel@tonic-gate buftagp->bt_redzone != KMEM_REDZONE_PATTERN) { 29880Sstevel@tonic-gate if (!besilent) 29890Sstevel@tonic-gate mdb_printf("buffer %p (free) seems to " 29900Sstevel@tonic-gate "have a corrupt redzone pattern\n", addr); 29910Sstevel@tonic-gate goto corrupt; 29920Sstevel@tonic-gate } 29930Sstevel@tonic-gate 29940Sstevel@tonic-gate /* 29950Sstevel@tonic-gate * confirm bufctl pointer integrity. 29960Sstevel@tonic-gate */ 29970Sstevel@tonic-gate if (verify_buftag(buftagp, KMEM_BUFTAG_FREE) == -1) { 29980Sstevel@tonic-gate if (!besilent) 29990Sstevel@tonic-gate mdb_printf("buffer %p (free) has a corrupt " 30000Sstevel@tonic-gate "buftag\n", addr); 30010Sstevel@tonic-gate goto corrupt; 30020Sstevel@tonic-gate } 30030Sstevel@tonic-gate 30040Sstevel@tonic-gate return (WALK_NEXT); 30050Sstevel@tonic-gate corrupt: 30060Sstevel@tonic-gate kmv->kmv_corruption++; 30070Sstevel@tonic-gate return (WALK_NEXT); 30080Sstevel@tonic-gate } 30090Sstevel@tonic-gate 30100Sstevel@tonic-gate /* 30110Sstevel@tonic-gate * verify_alloc() 30120Sstevel@tonic-gate * Verify that the buftag of an allocated buffer makes sense with respect 30130Sstevel@tonic-gate * to the buffer. 30140Sstevel@tonic-gate */ 30150Sstevel@tonic-gate /*ARGSUSED1*/ 30160Sstevel@tonic-gate static int 30170Sstevel@tonic-gate verify_alloc(uintptr_t addr, const void *data, void *private) 30180Sstevel@tonic-gate { 30190Sstevel@tonic-gate kmem_verify_t *kmv = (kmem_verify_t *)private; 30200Sstevel@tonic-gate kmem_cache_t *cp = &kmv->kmv_cache; 30210Sstevel@tonic-gate uint64_t *buf = kmv->kmv_buf; /* buf to validate */ 30220Sstevel@tonic-gate /*LINTED*/ 30230Sstevel@tonic-gate kmem_buftag_t *buftagp = KMEM_BUFTAG(cp, buf); 30240Sstevel@tonic-gate uint32_t *ip = (uint32_t *)buftagp; 30250Sstevel@tonic-gate uint8_t *bp = (uint8_t *)buf; 30260Sstevel@tonic-gate int looks_ok = 0, size_ok = 1; /* flags for finding corruption */ 30270Sstevel@tonic-gate int besilent = kmv->kmv_besilent; 30280Sstevel@tonic-gate 30290Sstevel@tonic-gate /* 30300Sstevel@tonic-gate * Read the buffer to check. 30310Sstevel@tonic-gate */ 30320Sstevel@tonic-gate if (mdb_vread(buf, kmv->kmv_size, addr) == -1) { 30330Sstevel@tonic-gate if (!besilent) 30340Sstevel@tonic-gate mdb_warn("couldn't read %p", addr); 30350Sstevel@tonic-gate return (WALK_NEXT); 30360Sstevel@tonic-gate } 30370Sstevel@tonic-gate 30380Sstevel@tonic-gate /* 30390Sstevel@tonic-gate * There are two cases to handle: 30400Sstevel@tonic-gate * 1. If the buf was alloc'd using kmem_cache_alloc, it will have 30410Sstevel@tonic-gate * 0xfeedfacefeedface at the end of it 30420Sstevel@tonic-gate * 2. If the buf was alloc'd using kmem_alloc, it will have 30430Sstevel@tonic-gate * 0xbb just past the end of the region in use. At the buftag, 30440Sstevel@tonic-gate * it will have 0xfeedface (or, if the whole buffer is in use, 30450Sstevel@tonic-gate * 0xfeedface & bb000000 or 0xfeedfacf & 000000bb depending on 30460Sstevel@tonic-gate * endianness), followed by 32 bits containing the offset of the 30470Sstevel@tonic-gate * 0xbb byte in the buffer. 30480Sstevel@tonic-gate * 30490Sstevel@tonic-gate * Finally, the two 32-bit words that comprise the second half of the 30500Sstevel@tonic-gate * buftag should xor to KMEM_BUFTAG_ALLOC 30510Sstevel@tonic-gate */ 30520Sstevel@tonic-gate 30530Sstevel@tonic-gate if (buftagp->bt_redzone == KMEM_REDZONE_PATTERN) 30540Sstevel@tonic-gate looks_ok = 1; 30550Sstevel@tonic-gate else if (!KMEM_SIZE_VALID(ip[1])) 30560Sstevel@tonic-gate size_ok = 0; 30570Sstevel@tonic-gate else if (bp[KMEM_SIZE_DECODE(ip[1])] == KMEM_REDZONE_BYTE) 30580Sstevel@tonic-gate looks_ok = 1; 30590Sstevel@tonic-gate else 30600Sstevel@tonic-gate size_ok = 0; 30610Sstevel@tonic-gate 30620Sstevel@tonic-gate if (!size_ok) { 30630Sstevel@tonic-gate if (!besilent) 30640Sstevel@tonic-gate mdb_printf("buffer %p (allocated) has a corrupt " 30650Sstevel@tonic-gate "redzone size encoding\n", addr); 30660Sstevel@tonic-gate goto corrupt; 30670Sstevel@tonic-gate } 30680Sstevel@tonic-gate 30690Sstevel@tonic-gate if (!looks_ok) { 30700Sstevel@tonic-gate if (!besilent) 30710Sstevel@tonic-gate mdb_printf("buffer %p (allocated) has a corrupt " 30720Sstevel@tonic-gate "redzone signature\n", addr); 30730Sstevel@tonic-gate goto corrupt; 30740Sstevel@tonic-gate } 30750Sstevel@tonic-gate 30760Sstevel@tonic-gate if (verify_buftag(buftagp, KMEM_BUFTAG_ALLOC) == -1) { 30770Sstevel@tonic-gate if (!besilent) 30780Sstevel@tonic-gate mdb_printf("buffer %p (allocated) has a " 30790Sstevel@tonic-gate "corrupt buftag\n", addr); 30800Sstevel@tonic-gate goto corrupt; 30810Sstevel@tonic-gate } 30820Sstevel@tonic-gate 30830Sstevel@tonic-gate return (WALK_NEXT); 30840Sstevel@tonic-gate corrupt: 30850Sstevel@tonic-gate kmv->kmv_corruption++; 30860Sstevel@tonic-gate return (WALK_NEXT); 30870Sstevel@tonic-gate } 30880Sstevel@tonic-gate 30890Sstevel@tonic-gate /*ARGSUSED2*/ 30900Sstevel@tonic-gate int 30910Sstevel@tonic-gate kmem_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 30920Sstevel@tonic-gate { 30930Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 30940Sstevel@tonic-gate int check_alloc = 0, check_free = 0; 30950Sstevel@tonic-gate kmem_verify_t kmv; 30960Sstevel@tonic-gate 30970Sstevel@tonic-gate if (mdb_vread(&kmv.kmv_cache, sizeof (kmv.kmv_cache), 30980Sstevel@tonic-gate addr) == -1) { 30990Sstevel@tonic-gate mdb_warn("couldn't read kmem_cache %p", addr); 31000Sstevel@tonic-gate return (DCMD_ERR); 31010Sstevel@tonic-gate } 31020Sstevel@tonic-gate 31030Sstevel@tonic-gate kmv.kmv_size = kmv.kmv_cache.cache_buftag + 31040Sstevel@tonic-gate sizeof (kmem_buftag_t); 31050Sstevel@tonic-gate kmv.kmv_buf = mdb_alloc(kmv.kmv_size, UM_SLEEP | UM_GC); 31060Sstevel@tonic-gate kmv.kmv_corruption = 0; 31070Sstevel@tonic-gate 31080Sstevel@tonic-gate if ((kmv.kmv_cache.cache_flags & KMF_REDZONE)) { 31090Sstevel@tonic-gate check_alloc = 1; 31100Sstevel@tonic-gate if (kmv.kmv_cache.cache_flags & KMF_DEADBEEF) 31110Sstevel@tonic-gate check_free = 1; 31120Sstevel@tonic-gate } else { 31130Sstevel@tonic-gate if (!(flags & DCMD_LOOP)) { 31140Sstevel@tonic-gate mdb_warn("cache %p (%s) does not have " 31150Sstevel@tonic-gate "redzone checking enabled\n", addr, 31160Sstevel@tonic-gate kmv.kmv_cache.cache_name); 31170Sstevel@tonic-gate } 31180Sstevel@tonic-gate return (DCMD_ERR); 31190Sstevel@tonic-gate } 31200Sstevel@tonic-gate 31210Sstevel@tonic-gate if (flags & DCMD_LOOP) { 31220Sstevel@tonic-gate /* 31230Sstevel@tonic-gate * table mode, don't print out every corrupt buffer 31240Sstevel@tonic-gate */ 31250Sstevel@tonic-gate kmv.kmv_besilent = 1; 31260Sstevel@tonic-gate } else { 31270Sstevel@tonic-gate mdb_printf("Summary for cache '%s'\n", 31280Sstevel@tonic-gate kmv.kmv_cache.cache_name); 31290Sstevel@tonic-gate mdb_inc_indent(2); 31300Sstevel@tonic-gate kmv.kmv_besilent = 0; 31310Sstevel@tonic-gate } 31320Sstevel@tonic-gate 31330Sstevel@tonic-gate if (check_alloc) 31340Sstevel@tonic-gate (void) mdb_pwalk("kmem", verify_alloc, &kmv, addr); 31350Sstevel@tonic-gate if (check_free) 31360Sstevel@tonic-gate (void) mdb_pwalk("freemem", verify_free, &kmv, addr); 31370Sstevel@tonic-gate 31380Sstevel@tonic-gate if (flags & DCMD_LOOP) { 31390Sstevel@tonic-gate if (kmv.kmv_corruption == 0) { 31400Sstevel@tonic-gate mdb_printf("%-*s %?p clean\n", 31410Sstevel@tonic-gate KMEM_CACHE_NAMELEN, 31420Sstevel@tonic-gate kmv.kmv_cache.cache_name, addr); 31430Sstevel@tonic-gate } else { 31440Sstevel@tonic-gate char *s = ""; /* optional s in "buffer[s]" */ 31450Sstevel@tonic-gate if (kmv.kmv_corruption > 1) 31460Sstevel@tonic-gate s = "s"; 31470Sstevel@tonic-gate 31480Sstevel@tonic-gate mdb_printf("%-*s %?p %d corrupt buffer%s\n", 31490Sstevel@tonic-gate KMEM_CACHE_NAMELEN, 31500Sstevel@tonic-gate kmv.kmv_cache.cache_name, addr, 31510Sstevel@tonic-gate kmv.kmv_corruption, s); 31520Sstevel@tonic-gate } 31530Sstevel@tonic-gate } else { 31540Sstevel@tonic-gate /* 31550Sstevel@tonic-gate * This is the more verbose mode, when the user has 31560Sstevel@tonic-gate * type addr::kmem_verify. If the cache was clean, 31570Sstevel@tonic-gate * nothing will have yet been printed. So say something. 31580Sstevel@tonic-gate */ 31590Sstevel@tonic-gate if (kmv.kmv_corruption == 0) 31600Sstevel@tonic-gate mdb_printf("clean\n"); 31610Sstevel@tonic-gate 31620Sstevel@tonic-gate mdb_dec_indent(2); 31630Sstevel@tonic-gate } 31640Sstevel@tonic-gate } else { 31650Sstevel@tonic-gate /* 31660Sstevel@tonic-gate * If the user didn't specify a cache to verify, we'll walk all 31670Sstevel@tonic-gate * kmem_cache's, specifying ourself as a callback for each... 31680Sstevel@tonic-gate * this is the equivalent of '::walk kmem_cache .::kmem_verify' 31690Sstevel@tonic-gate */ 31700Sstevel@tonic-gate mdb_printf("%<u>%-*s %-?s %-20s%</b>\n", KMEM_CACHE_NAMELEN, 31710Sstevel@tonic-gate "Cache Name", "Addr", "Cache Integrity"); 31720Sstevel@tonic-gate (void) (mdb_walk_dcmd("kmem_cache", "kmem_verify", 0, NULL)); 31730Sstevel@tonic-gate } 31740Sstevel@tonic-gate 31750Sstevel@tonic-gate return (DCMD_OK); 31760Sstevel@tonic-gate } 31770Sstevel@tonic-gate 31780Sstevel@tonic-gate typedef struct vmem_node { 31790Sstevel@tonic-gate struct vmem_node *vn_next; 31800Sstevel@tonic-gate struct vmem_node *vn_parent; 31810Sstevel@tonic-gate struct vmem_node *vn_sibling; 31820Sstevel@tonic-gate struct vmem_node *vn_children; 31830Sstevel@tonic-gate uintptr_t vn_addr; 31840Sstevel@tonic-gate int vn_marked; 31850Sstevel@tonic-gate vmem_t vn_vmem; 31860Sstevel@tonic-gate } vmem_node_t; 31870Sstevel@tonic-gate 31880Sstevel@tonic-gate typedef struct vmem_walk { 31890Sstevel@tonic-gate vmem_node_t *vw_root; 31900Sstevel@tonic-gate vmem_node_t *vw_current; 31910Sstevel@tonic-gate } vmem_walk_t; 31920Sstevel@tonic-gate 31930Sstevel@tonic-gate int 31940Sstevel@tonic-gate vmem_walk_init(mdb_walk_state_t *wsp) 31950Sstevel@tonic-gate { 31960Sstevel@tonic-gate uintptr_t vaddr, paddr; 31970Sstevel@tonic-gate vmem_node_t *head = NULL, *root = NULL, *current = NULL, *parent, *vp; 31980Sstevel@tonic-gate vmem_walk_t *vw; 31990Sstevel@tonic-gate 32000Sstevel@tonic-gate if (mdb_readvar(&vaddr, "vmem_list") == -1) { 32010Sstevel@tonic-gate mdb_warn("couldn't read 'vmem_list'"); 32020Sstevel@tonic-gate return (WALK_ERR); 32030Sstevel@tonic-gate } 32040Sstevel@tonic-gate 32050Sstevel@tonic-gate while (vaddr != NULL) { 32060Sstevel@tonic-gate vp = mdb_zalloc(sizeof (vmem_node_t), UM_SLEEP); 32070Sstevel@tonic-gate vp->vn_addr = vaddr; 32080Sstevel@tonic-gate vp->vn_next = head; 32090Sstevel@tonic-gate head = vp; 32100Sstevel@tonic-gate 32110Sstevel@tonic-gate if (vaddr == wsp->walk_addr) 32120Sstevel@tonic-gate current = vp; 32130Sstevel@tonic-gate 32140Sstevel@tonic-gate if (mdb_vread(&vp->vn_vmem, sizeof (vmem_t), vaddr) == -1) { 32150Sstevel@tonic-gate mdb_warn("couldn't read vmem_t at %p", vaddr); 32160Sstevel@tonic-gate goto err; 32170Sstevel@tonic-gate } 32180Sstevel@tonic-gate 32190Sstevel@tonic-gate vaddr = (uintptr_t)vp->vn_vmem.vm_next; 32200Sstevel@tonic-gate } 32210Sstevel@tonic-gate 32220Sstevel@tonic-gate for (vp = head; vp != NULL; vp = vp->vn_next) { 32230Sstevel@tonic-gate 32240Sstevel@tonic-gate if ((paddr = (uintptr_t)vp->vn_vmem.vm_source) == NULL) { 32250Sstevel@tonic-gate vp->vn_sibling = root; 32260Sstevel@tonic-gate root = vp; 32270Sstevel@tonic-gate continue; 32280Sstevel@tonic-gate } 32290Sstevel@tonic-gate 32300Sstevel@tonic-gate for (parent = head; parent != NULL; parent = parent->vn_next) { 32310Sstevel@tonic-gate if (parent->vn_addr != paddr) 32320Sstevel@tonic-gate continue; 32330Sstevel@tonic-gate vp->vn_sibling = parent->vn_children; 32340Sstevel@tonic-gate parent->vn_children = vp; 32350Sstevel@tonic-gate vp->vn_parent = parent; 32360Sstevel@tonic-gate break; 32370Sstevel@tonic-gate } 32380Sstevel@tonic-gate 32390Sstevel@tonic-gate if (parent == NULL) { 32400Sstevel@tonic-gate mdb_warn("couldn't find %p's parent (%p)\n", 32410Sstevel@tonic-gate vp->vn_addr, paddr); 32420Sstevel@tonic-gate goto err; 32430Sstevel@tonic-gate } 32440Sstevel@tonic-gate } 32450Sstevel@tonic-gate 32460Sstevel@tonic-gate vw = mdb_zalloc(sizeof (vmem_walk_t), UM_SLEEP); 32470Sstevel@tonic-gate vw->vw_root = root; 32480Sstevel@tonic-gate 32490Sstevel@tonic-gate if (current != NULL) 32500Sstevel@tonic-gate vw->vw_current = current; 32510Sstevel@tonic-gate else 32520Sstevel@tonic-gate vw->vw_current = root; 32530Sstevel@tonic-gate 32540Sstevel@tonic-gate wsp->walk_data = vw; 32550Sstevel@tonic-gate return (WALK_NEXT); 32560Sstevel@tonic-gate err: 32570Sstevel@tonic-gate for (vp = head; head != NULL; vp = head) { 32580Sstevel@tonic-gate head = vp->vn_next; 32590Sstevel@tonic-gate mdb_free(vp, sizeof (vmem_node_t)); 32600Sstevel@tonic-gate } 32610Sstevel@tonic-gate 32620Sstevel@tonic-gate return (WALK_ERR); 32630Sstevel@tonic-gate } 32640Sstevel@tonic-gate 32650Sstevel@tonic-gate int 32660Sstevel@tonic-gate vmem_walk_step(mdb_walk_state_t *wsp) 32670Sstevel@tonic-gate { 32680Sstevel@tonic-gate vmem_walk_t *vw = wsp->walk_data; 32690Sstevel@tonic-gate vmem_node_t *vp; 32700Sstevel@tonic-gate int rval; 32710Sstevel@tonic-gate 32720Sstevel@tonic-gate if ((vp = vw->vw_current) == NULL) 32730Sstevel@tonic-gate return (WALK_DONE); 32740Sstevel@tonic-gate 32750Sstevel@tonic-gate rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata); 32760Sstevel@tonic-gate 32770Sstevel@tonic-gate if (vp->vn_children != NULL) { 32780Sstevel@tonic-gate vw->vw_current = vp->vn_children; 32790Sstevel@tonic-gate return (rval); 32800Sstevel@tonic-gate } 32810Sstevel@tonic-gate 32820Sstevel@tonic-gate do { 32830Sstevel@tonic-gate vw->vw_current = vp->vn_sibling; 32840Sstevel@tonic-gate vp = vp->vn_parent; 32850Sstevel@tonic-gate } while (vw->vw_current == NULL && vp != NULL); 32860Sstevel@tonic-gate 32870Sstevel@tonic-gate return (rval); 32880Sstevel@tonic-gate } 32890Sstevel@tonic-gate 32900Sstevel@tonic-gate /* 32910Sstevel@tonic-gate * The "vmem_postfix" walk walks the vmem arenas in post-fix order; all 32920Sstevel@tonic-gate * children are visited before their parent. We perform the postfix walk 32930Sstevel@tonic-gate * iteratively (rather than recursively) to allow mdb to regain control 32940Sstevel@tonic-gate * after each callback. 32950Sstevel@tonic-gate */ 32960Sstevel@tonic-gate int 32970Sstevel@tonic-gate vmem_postfix_walk_step(mdb_walk_state_t *wsp) 32980Sstevel@tonic-gate { 32990Sstevel@tonic-gate vmem_walk_t *vw = wsp->walk_data; 33000Sstevel@tonic-gate vmem_node_t *vp = vw->vw_current; 33010Sstevel@tonic-gate int rval; 33020Sstevel@tonic-gate 33030Sstevel@tonic-gate /* 33040Sstevel@tonic-gate * If this node is marked, then we know that we have already visited 33050Sstevel@tonic-gate * all of its children. If the node has any siblings, they need to 33060Sstevel@tonic-gate * be visited next; otherwise, we need to visit the parent. Note 33070Sstevel@tonic-gate * that vp->vn_marked will only be zero on the first invocation of 33080Sstevel@tonic-gate * the step function. 33090Sstevel@tonic-gate */ 33100Sstevel@tonic-gate if (vp->vn_marked) { 33110Sstevel@tonic-gate if (vp->vn_sibling != NULL) 33120Sstevel@tonic-gate vp = vp->vn_sibling; 33130Sstevel@tonic-gate else if (vp->vn_parent != NULL) 33140Sstevel@tonic-gate vp = vp->vn_parent; 33150Sstevel@tonic-gate else { 33160Sstevel@tonic-gate /* 33170Sstevel@tonic-gate * We have neither a parent, nor a sibling, and we 33180Sstevel@tonic-gate * have already been visited; we're done. 33190Sstevel@tonic-gate */ 33200Sstevel@tonic-gate return (WALK_DONE); 33210Sstevel@tonic-gate } 33220Sstevel@tonic-gate } 33230Sstevel@tonic-gate 33240Sstevel@tonic-gate /* 33250Sstevel@tonic-gate * Before we visit this node, visit its children. 33260Sstevel@tonic-gate */ 33270Sstevel@tonic-gate while (vp->vn_children != NULL && !vp->vn_children->vn_marked) 33280Sstevel@tonic-gate vp = vp->vn_children; 33290Sstevel@tonic-gate 33300Sstevel@tonic-gate vp->vn_marked = 1; 33310Sstevel@tonic-gate vw->vw_current = vp; 33320Sstevel@tonic-gate rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata); 33330Sstevel@tonic-gate 33340Sstevel@tonic-gate return (rval); 33350Sstevel@tonic-gate } 33360Sstevel@tonic-gate 33370Sstevel@tonic-gate void 33380Sstevel@tonic-gate vmem_walk_fini(mdb_walk_state_t *wsp) 33390Sstevel@tonic-gate { 33400Sstevel@tonic-gate vmem_walk_t *vw = wsp->walk_data; 33410Sstevel@tonic-gate vmem_node_t *root = vw->vw_root; 33420Sstevel@tonic-gate int done; 33430Sstevel@tonic-gate 33440Sstevel@tonic-gate if (root == NULL) 33450Sstevel@tonic-gate return; 33460Sstevel@tonic-gate 33470Sstevel@tonic-gate if ((vw->vw_root = root->vn_children) != NULL) 33480Sstevel@tonic-gate vmem_walk_fini(wsp); 33490Sstevel@tonic-gate 33500Sstevel@tonic-gate vw->vw_root = root->vn_sibling; 33510Sstevel@tonic-gate done = (root->vn_sibling == NULL && root->vn_parent == NULL); 33520Sstevel@tonic-gate mdb_free(root, sizeof (vmem_node_t)); 33530Sstevel@tonic-gate 33540Sstevel@tonic-gate if (done) { 33550Sstevel@tonic-gate mdb_free(vw, sizeof (vmem_walk_t)); 33560Sstevel@tonic-gate } else { 33570Sstevel@tonic-gate vmem_walk_fini(wsp); 33580Sstevel@tonic-gate } 33590Sstevel@tonic-gate } 33600Sstevel@tonic-gate 33610Sstevel@tonic-gate typedef struct vmem_seg_walk { 33620Sstevel@tonic-gate uint8_t vsw_type; 33630Sstevel@tonic-gate uintptr_t vsw_start; 33640Sstevel@tonic-gate uintptr_t vsw_current; 33650Sstevel@tonic-gate } vmem_seg_walk_t; 33660Sstevel@tonic-gate 33670Sstevel@tonic-gate /*ARGSUSED*/ 33680Sstevel@tonic-gate int 33690Sstevel@tonic-gate vmem_seg_walk_common_init(mdb_walk_state_t *wsp, uint8_t type, char *name) 33700Sstevel@tonic-gate { 33710Sstevel@tonic-gate vmem_seg_walk_t *vsw; 33720Sstevel@tonic-gate 33730Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 33740Sstevel@tonic-gate mdb_warn("vmem_%s does not support global walks\n", name); 33750Sstevel@tonic-gate return (WALK_ERR); 33760Sstevel@tonic-gate } 33770Sstevel@tonic-gate 33780Sstevel@tonic-gate wsp->walk_data = vsw = mdb_alloc(sizeof (vmem_seg_walk_t), UM_SLEEP); 33790Sstevel@tonic-gate 33800Sstevel@tonic-gate vsw->vsw_type = type; 33810Sstevel@tonic-gate vsw->vsw_start = wsp->walk_addr + offsetof(vmem_t, vm_seg0); 33820Sstevel@tonic-gate vsw->vsw_current = vsw->vsw_start; 33830Sstevel@tonic-gate 33840Sstevel@tonic-gate return (WALK_NEXT); 33850Sstevel@tonic-gate } 33860Sstevel@tonic-gate 33870Sstevel@tonic-gate /* 33880Sstevel@tonic-gate * vmem segments can't have type 0 (this should be added to vmem_impl.h). 33890Sstevel@tonic-gate */ 33900Sstevel@tonic-gate #define VMEM_NONE 0 33910Sstevel@tonic-gate 33920Sstevel@tonic-gate int 33930Sstevel@tonic-gate vmem_alloc_walk_init(mdb_walk_state_t *wsp) 33940Sstevel@tonic-gate { 33950Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_ALLOC, "alloc")); 33960Sstevel@tonic-gate } 33970Sstevel@tonic-gate 33980Sstevel@tonic-gate int 33990Sstevel@tonic-gate vmem_free_walk_init(mdb_walk_state_t *wsp) 34000Sstevel@tonic-gate { 34010Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_FREE, "free")); 34020Sstevel@tonic-gate } 34030Sstevel@tonic-gate 34040Sstevel@tonic-gate int 34050Sstevel@tonic-gate vmem_span_walk_init(mdb_walk_state_t *wsp) 34060Sstevel@tonic-gate { 34070Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_SPAN, "span")); 34080Sstevel@tonic-gate } 34090Sstevel@tonic-gate 34100Sstevel@tonic-gate int 34110Sstevel@tonic-gate vmem_seg_walk_init(mdb_walk_state_t *wsp) 34120Sstevel@tonic-gate { 34130Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_NONE, "seg")); 34140Sstevel@tonic-gate } 34150Sstevel@tonic-gate 34160Sstevel@tonic-gate int 34170Sstevel@tonic-gate vmem_seg_walk_step(mdb_walk_state_t *wsp) 34180Sstevel@tonic-gate { 34190Sstevel@tonic-gate vmem_seg_t seg; 34200Sstevel@tonic-gate vmem_seg_walk_t *vsw = wsp->walk_data; 34210Sstevel@tonic-gate uintptr_t addr = vsw->vsw_current; 34220Sstevel@tonic-gate static size_t seg_size = 0; 34230Sstevel@tonic-gate int rval; 34240Sstevel@tonic-gate 34250Sstevel@tonic-gate if (!seg_size) { 34260Sstevel@tonic-gate if (mdb_readvar(&seg_size, "vmem_seg_size") == -1) { 34270Sstevel@tonic-gate mdb_warn("failed to read 'vmem_seg_size'"); 34280Sstevel@tonic-gate seg_size = sizeof (vmem_seg_t); 34290Sstevel@tonic-gate } 34300Sstevel@tonic-gate } 34310Sstevel@tonic-gate 34320Sstevel@tonic-gate if (seg_size < sizeof (seg)) 34330Sstevel@tonic-gate bzero((caddr_t)&seg + seg_size, sizeof (seg) - seg_size); 34340Sstevel@tonic-gate 34350Sstevel@tonic-gate if (mdb_vread(&seg, seg_size, addr) == -1) { 34360Sstevel@tonic-gate mdb_warn("couldn't read vmem_seg at %p", addr); 34370Sstevel@tonic-gate return (WALK_ERR); 34380Sstevel@tonic-gate } 34390Sstevel@tonic-gate 34400Sstevel@tonic-gate vsw->vsw_current = (uintptr_t)seg.vs_anext; 34410Sstevel@tonic-gate if (vsw->vsw_type != VMEM_NONE && seg.vs_type != vsw->vsw_type) { 34420Sstevel@tonic-gate rval = WALK_NEXT; 34430Sstevel@tonic-gate } else { 34440Sstevel@tonic-gate rval = wsp->walk_callback(addr, &seg, wsp->walk_cbdata); 34450Sstevel@tonic-gate } 34460Sstevel@tonic-gate 34470Sstevel@tonic-gate if (vsw->vsw_current == vsw->vsw_start) 34480Sstevel@tonic-gate return (WALK_DONE); 34490Sstevel@tonic-gate 34500Sstevel@tonic-gate return (rval); 34510Sstevel@tonic-gate } 34520Sstevel@tonic-gate 34530Sstevel@tonic-gate void 34540Sstevel@tonic-gate vmem_seg_walk_fini(mdb_walk_state_t *wsp) 34550Sstevel@tonic-gate { 34560Sstevel@tonic-gate vmem_seg_walk_t *vsw = wsp->walk_data; 34570Sstevel@tonic-gate 34580Sstevel@tonic-gate mdb_free(vsw, sizeof (vmem_seg_walk_t)); 34590Sstevel@tonic-gate } 34600Sstevel@tonic-gate 34610Sstevel@tonic-gate #define VMEM_NAMEWIDTH 22 34620Sstevel@tonic-gate 34630Sstevel@tonic-gate int 34640Sstevel@tonic-gate vmem(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 34650Sstevel@tonic-gate { 34660Sstevel@tonic-gate vmem_t v, parent; 34670Sstevel@tonic-gate vmem_kstat_t *vkp = &v.vm_kstat; 34680Sstevel@tonic-gate uintptr_t paddr; 34690Sstevel@tonic-gate int ident = 0; 34700Sstevel@tonic-gate char c[VMEM_NAMEWIDTH]; 34710Sstevel@tonic-gate 34720Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 34730Sstevel@tonic-gate if (mdb_walk_dcmd("vmem", "vmem", argc, argv) == -1) { 34740Sstevel@tonic-gate mdb_warn("can't walk vmem"); 34750Sstevel@tonic-gate return (DCMD_ERR); 34760Sstevel@tonic-gate } 34770Sstevel@tonic-gate return (DCMD_OK); 34780Sstevel@tonic-gate } 34790Sstevel@tonic-gate 34800Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) 34810Sstevel@tonic-gate mdb_printf("%-?s %-*s %10s %12s %9s %5s\n", 34820Sstevel@tonic-gate "ADDR", VMEM_NAMEWIDTH, "NAME", "INUSE", 34830Sstevel@tonic-gate "TOTAL", "SUCCEED", "FAIL"); 34840Sstevel@tonic-gate 34850Sstevel@tonic-gate if (mdb_vread(&v, sizeof (v), addr) == -1) { 34860Sstevel@tonic-gate mdb_warn("couldn't read vmem at %p", addr); 34870Sstevel@tonic-gate return (DCMD_ERR); 34880Sstevel@tonic-gate } 34890Sstevel@tonic-gate 34900Sstevel@tonic-gate for (paddr = (uintptr_t)v.vm_source; paddr != NULL; ident += 2) { 34910Sstevel@tonic-gate if (mdb_vread(&parent, sizeof (parent), paddr) == -1) { 34920Sstevel@tonic-gate mdb_warn("couldn't trace %p's ancestry", addr); 34930Sstevel@tonic-gate ident = 0; 34940Sstevel@tonic-gate break; 34950Sstevel@tonic-gate } 34960Sstevel@tonic-gate paddr = (uintptr_t)parent.vm_source; 34970Sstevel@tonic-gate } 34980Sstevel@tonic-gate 34990Sstevel@tonic-gate (void) mdb_snprintf(c, VMEM_NAMEWIDTH, "%*s%s", ident, "", v.vm_name); 35000Sstevel@tonic-gate 35010Sstevel@tonic-gate mdb_printf("%0?p %-*s %10llu %12llu %9llu %5llu\n", 35020Sstevel@tonic-gate addr, VMEM_NAMEWIDTH, c, 35030Sstevel@tonic-gate vkp->vk_mem_inuse.value.ui64, vkp->vk_mem_total.value.ui64, 35040Sstevel@tonic-gate vkp->vk_alloc.value.ui64, vkp->vk_fail.value.ui64); 35050Sstevel@tonic-gate 35060Sstevel@tonic-gate return (DCMD_OK); 35070Sstevel@tonic-gate } 35080Sstevel@tonic-gate 35090Sstevel@tonic-gate void 35100Sstevel@tonic-gate vmem_seg_help(void) 35110Sstevel@tonic-gate { 35120Sstevel@tonic-gate mdb_printf("%s\n", 35130Sstevel@tonic-gate "Display the contents of vmem_seg_ts, with optional filtering.\n" 35140Sstevel@tonic-gate "\n" 35150Sstevel@tonic-gate "A vmem_seg_t represents a range of addresses (or arbitrary numbers),\n" 35160Sstevel@tonic-gate "representing a single chunk of data. Only ALLOC segments have debugging\n" 35170Sstevel@tonic-gate "information.\n"); 35180Sstevel@tonic-gate mdb_dec_indent(2); 35190Sstevel@tonic-gate mdb_printf("%<b>OPTIONS%</b>\n"); 35200Sstevel@tonic-gate mdb_inc_indent(2); 35210Sstevel@tonic-gate mdb_printf("%s", 35220Sstevel@tonic-gate " -v Display the full content of the vmem_seg, including its stack trace\n" 35230Sstevel@tonic-gate " -s report the size of the segment, instead of the end address\n" 35240Sstevel@tonic-gate " -c caller\n" 35250Sstevel@tonic-gate " filter out segments without the function/PC in their stack trace\n" 35260Sstevel@tonic-gate " -e earliest\n" 35270Sstevel@tonic-gate " filter out segments timestamped before earliest\n" 35280Sstevel@tonic-gate " -l latest\n" 35290Sstevel@tonic-gate " filter out segments timestamped after latest\n" 35300Sstevel@tonic-gate " -m minsize\n" 35310Sstevel@tonic-gate " filer out segments smaller than minsize\n" 35320Sstevel@tonic-gate " -M maxsize\n" 35330Sstevel@tonic-gate " filer out segments larger than maxsize\n" 35340Sstevel@tonic-gate " -t thread\n" 35350Sstevel@tonic-gate " filter out segments not involving thread\n" 35360Sstevel@tonic-gate " -T type\n" 35370Sstevel@tonic-gate " filter out segments not of type 'type'\n" 35380Sstevel@tonic-gate " type is one of: ALLOC/FREE/SPAN/ROTOR/WALKER\n"); 35390Sstevel@tonic-gate } 35400Sstevel@tonic-gate 35410Sstevel@tonic-gate /*ARGSUSED*/ 35420Sstevel@tonic-gate int 35430Sstevel@tonic-gate vmem_seg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 35440Sstevel@tonic-gate { 35450Sstevel@tonic-gate vmem_seg_t vs; 35460Sstevel@tonic-gate pc_t *stk = vs.vs_stack; 35470Sstevel@tonic-gate uintptr_t sz; 35480Sstevel@tonic-gate uint8_t t; 35490Sstevel@tonic-gate const char *type = NULL; 35500Sstevel@tonic-gate GElf_Sym sym; 35510Sstevel@tonic-gate char c[MDB_SYM_NAMLEN]; 35520Sstevel@tonic-gate int no_debug; 35530Sstevel@tonic-gate int i; 35540Sstevel@tonic-gate int depth; 35550Sstevel@tonic-gate uintptr_t laddr, haddr; 35560Sstevel@tonic-gate 35570Sstevel@tonic-gate uintptr_t caller = NULL, thread = NULL; 35580Sstevel@tonic-gate uintptr_t minsize = 0, maxsize = 0; 35590Sstevel@tonic-gate 35600Sstevel@tonic-gate hrtime_t earliest = 0, latest = 0; 35610Sstevel@tonic-gate 35620Sstevel@tonic-gate uint_t size = 0; 35630Sstevel@tonic-gate uint_t verbose = 0; 35640Sstevel@tonic-gate 35650Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 35660Sstevel@tonic-gate return (DCMD_USAGE); 35670Sstevel@tonic-gate 35680Sstevel@tonic-gate if (mdb_getopts(argc, argv, 35690Sstevel@tonic-gate 'c', MDB_OPT_UINTPTR, &caller, 35700Sstevel@tonic-gate 'e', MDB_OPT_UINT64, &earliest, 35710Sstevel@tonic-gate 'l', MDB_OPT_UINT64, &latest, 35720Sstevel@tonic-gate 's', MDB_OPT_SETBITS, TRUE, &size, 35730Sstevel@tonic-gate 'm', MDB_OPT_UINTPTR, &minsize, 35740Sstevel@tonic-gate 'M', MDB_OPT_UINTPTR, &maxsize, 35750Sstevel@tonic-gate 't', MDB_OPT_UINTPTR, &thread, 35760Sstevel@tonic-gate 'T', MDB_OPT_STR, &type, 35770Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &verbose, 35780Sstevel@tonic-gate NULL) != argc) 35790Sstevel@tonic-gate return (DCMD_USAGE); 35800Sstevel@tonic-gate 35810Sstevel@tonic-gate if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) { 35820Sstevel@tonic-gate if (verbose) { 35830Sstevel@tonic-gate mdb_printf("%16s %4s %16s %16s %16s\n" 35840Sstevel@tonic-gate "%<u>%16s %4s %16s %16s %16s%</u>\n", 35850Sstevel@tonic-gate "ADDR", "TYPE", "START", "END", "SIZE", 35860Sstevel@tonic-gate "", "", "THREAD", "TIMESTAMP", ""); 35870Sstevel@tonic-gate } else { 35880Sstevel@tonic-gate mdb_printf("%?s %4s %?s %?s %s\n", "ADDR", "TYPE", 35890Sstevel@tonic-gate "START", size? "SIZE" : "END", "WHO"); 35900Sstevel@tonic-gate } 35910Sstevel@tonic-gate } 35920Sstevel@tonic-gate 35930Sstevel@tonic-gate if (mdb_vread(&vs, sizeof (vs), addr) == -1) { 35940Sstevel@tonic-gate mdb_warn("couldn't read vmem_seg at %p", addr); 35950Sstevel@tonic-gate return (DCMD_ERR); 35960Sstevel@tonic-gate } 35970Sstevel@tonic-gate 35980Sstevel@tonic-gate if (type != NULL) { 35990Sstevel@tonic-gate if (strcmp(type, "ALLC") == 0 || strcmp(type, "ALLOC") == 0) 36000Sstevel@tonic-gate t = VMEM_ALLOC; 36010Sstevel@tonic-gate else if (strcmp(type, "FREE") == 0) 36020Sstevel@tonic-gate t = VMEM_FREE; 36030Sstevel@tonic-gate else if (strcmp(type, "SPAN") == 0) 36040Sstevel@tonic-gate t = VMEM_SPAN; 36050Sstevel@tonic-gate else if (strcmp(type, "ROTR") == 0 || 36060Sstevel@tonic-gate strcmp(type, "ROTOR") == 0) 36070Sstevel@tonic-gate t = VMEM_ROTOR; 36080Sstevel@tonic-gate else if (strcmp(type, "WLKR") == 0 || 36090Sstevel@tonic-gate strcmp(type, "WALKER") == 0) 36100Sstevel@tonic-gate t = VMEM_WALKER; 36110Sstevel@tonic-gate else { 36120Sstevel@tonic-gate mdb_warn("\"%s\" is not a recognized vmem_seg type\n", 36130Sstevel@tonic-gate type); 36140Sstevel@tonic-gate return (DCMD_ERR); 36150Sstevel@tonic-gate } 36160Sstevel@tonic-gate 36170Sstevel@tonic-gate if (vs.vs_type != t) 36180Sstevel@tonic-gate return (DCMD_OK); 36190Sstevel@tonic-gate } 36200Sstevel@tonic-gate 36210Sstevel@tonic-gate sz = vs.vs_end - vs.vs_start; 36220Sstevel@tonic-gate 36230Sstevel@tonic-gate if (minsize != 0 && sz < minsize) 36240Sstevel@tonic-gate return (DCMD_OK); 36250Sstevel@tonic-gate 36260Sstevel@tonic-gate if (maxsize != 0 && sz > maxsize) 36270Sstevel@tonic-gate return (DCMD_OK); 36280Sstevel@tonic-gate 36290Sstevel@tonic-gate t = vs.vs_type; 36300Sstevel@tonic-gate depth = vs.vs_depth; 36310Sstevel@tonic-gate 36320Sstevel@tonic-gate /* 36330Sstevel@tonic-gate * debug info, when present, is only accurate for VMEM_ALLOC segments 36340Sstevel@tonic-gate */ 36350Sstevel@tonic-gate no_debug = (t != VMEM_ALLOC) || 36360Sstevel@tonic-gate (depth == 0 || depth > VMEM_STACK_DEPTH); 36370Sstevel@tonic-gate 36380Sstevel@tonic-gate if (no_debug) { 36390Sstevel@tonic-gate if (caller != NULL || thread != NULL || earliest != 0 || 36400Sstevel@tonic-gate latest != 0) 36410Sstevel@tonic-gate return (DCMD_OK); /* not enough info */ 36420Sstevel@tonic-gate } else { 36430Sstevel@tonic-gate if (caller != NULL) { 36440Sstevel@tonic-gate laddr = caller; 36450Sstevel@tonic-gate haddr = caller + sizeof (caller); 36460Sstevel@tonic-gate 36470Sstevel@tonic-gate if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c, 36480Sstevel@tonic-gate sizeof (c), &sym) != -1 && 36490Sstevel@tonic-gate caller == (uintptr_t)sym.st_value) { 36500Sstevel@tonic-gate /* 36510Sstevel@tonic-gate * We were provided an exact symbol value; any 36520Sstevel@tonic-gate * address in the function is valid. 36530Sstevel@tonic-gate */ 36540Sstevel@tonic-gate laddr = (uintptr_t)sym.st_value; 36550Sstevel@tonic-gate haddr = (uintptr_t)sym.st_value + sym.st_size; 36560Sstevel@tonic-gate } 36570Sstevel@tonic-gate 36580Sstevel@tonic-gate for (i = 0; i < depth; i++) 36590Sstevel@tonic-gate if (vs.vs_stack[i] >= laddr && 36600Sstevel@tonic-gate vs.vs_stack[i] < haddr) 36610Sstevel@tonic-gate break; 36620Sstevel@tonic-gate 36630Sstevel@tonic-gate if (i == depth) 36640Sstevel@tonic-gate return (DCMD_OK); 36650Sstevel@tonic-gate } 36660Sstevel@tonic-gate 36670Sstevel@tonic-gate if (thread != NULL && (uintptr_t)vs.vs_thread != thread) 36680Sstevel@tonic-gate return (DCMD_OK); 36690Sstevel@tonic-gate 36700Sstevel@tonic-gate if (earliest != 0 && vs.vs_timestamp < earliest) 36710Sstevel@tonic-gate return (DCMD_OK); 36720Sstevel@tonic-gate 36730Sstevel@tonic-gate if (latest != 0 && vs.vs_timestamp > latest) 36740Sstevel@tonic-gate return (DCMD_OK); 36750Sstevel@tonic-gate } 36760Sstevel@tonic-gate 36770Sstevel@tonic-gate type = (t == VMEM_ALLOC ? "ALLC" : 36780Sstevel@tonic-gate t == VMEM_FREE ? "FREE" : 36790Sstevel@tonic-gate t == VMEM_SPAN ? "SPAN" : 36800Sstevel@tonic-gate t == VMEM_ROTOR ? "ROTR" : 36810Sstevel@tonic-gate t == VMEM_WALKER ? "WLKR" : 36820Sstevel@tonic-gate "????"); 36830Sstevel@tonic-gate 36840Sstevel@tonic-gate if (flags & DCMD_PIPE_OUT) { 36850Sstevel@tonic-gate mdb_printf("%#lr\n", addr); 36860Sstevel@tonic-gate return (DCMD_OK); 36870Sstevel@tonic-gate } 36880Sstevel@tonic-gate 36890Sstevel@tonic-gate if (verbose) { 36900Sstevel@tonic-gate mdb_printf("%<b>%16p%</b> %4s %16p %16p %16d\n", 36910Sstevel@tonic-gate addr, type, vs.vs_start, vs.vs_end, sz); 36920Sstevel@tonic-gate 36930Sstevel@tonic-gate if (no_debug) 36940Sstevel@tonic-gate return (DCMD_OK); 36950Sstevel@tonic-gate 36960Sstevel@tonic-gate mdb_printf("%16s %4s %16p %16llx\n", 36970Sstevel@tonic-gate "", "", vs.vs_thread, vs.vs_timestamp); 36980Sstevel@tonic-gate 36990Sstevel@tonic-gate mdb_inc_indent(17); 37000Sstevel@tonic-gate for (i = 0; i < depth; i++) { 37010Sstevel@tonic-gate mdb_printf("%a\n", stk[i]); 37020Sstevel@tonic-gate } 37030Sstevel@tonic-gate mdb_dec_indent(17); 37040Sstevel@tonic-gate mdb_printf("\n"); 37050Sstevel@tonic-gate } else { 37060Sstevel@tonic-gate mdb_printf("%0?p %4s %0?p %0?p", addr, type, 37070Sstevel@tonic-gate vs.vs_start, size? sz : vs.vs_end); 37080Sstevel@tonic-gate 37090Sstevel@tonic-gate if (no_debug) { 37100Sstevel@tonic-gate mdb_printf("\n"); 37110Sstevel@tonic-gate return (DCMD_OK); 37120Sstevel@tonic-gate } 37130Sstevel@tonic-gate 37140Sstevel@tonic-gate for (i = 0; i < depth; i++) { 37150Sstevel@tonic-gate if (mdb_lookup_by_addr(stk[i], MDB_SYM_FUZZY, 37160Sstevel@tonic-gate c, sizeof (c), &sym) == -1) 37170Sstevel@tonic-gate continue; 37180Sstevel@tonic-gate if (strncmp(c, "vmem_", 5) == 0) 37190Sstevel@tonic-gate continue; 37200Sstevel@tonic-gate break; 37210Sstevel@tonic-gate } 37220Sstevel@tonic-gate mdb_printf(" %a\n", stk[i]); 37230Sstevel@tonic-gate } 37240Sstevel@tonic-gate return (DCMD_OK); 37250Sstevel@tonic-gate } 37260Sstevel@tonic-gate 37270Sstevel@tonic-gate typedef struct kmalog_data { 37280Sstevel@tonic-gate uintptr_t kma_addr; 37290Sstevel@tonic-gate hrtime_t kma_newest; 37300Sstevel@tonic-gate } kmalog_data_t; 37310Sstevel@tonic-gate 37320Sstevel@tonic-gate /*ARGSUSED*/ 37330Sstevel@tonic-gate static int 37340Sstevel@tonic-gate showbc(uintptr_t addr, const kmem_bufctl_audit_t *bcp, kmalog_data_t *kma) 37350Sstevel@tonic-gate { 37360Sstevel@tonic-gate char name[KMEM_CACHE_NAMELEN + 1]; 37370Sstevel@tonic-gate hrtime_t delta; 37380Sstevel@tonic-gate int i, depth; 37390Sstevel@tonic-gate size_t bufsize; 37400Sstevel@tonic-gate 37410Sstevel@tonic-gate if (bcp->bc_timestamp == 0) 37420Sstevel@tonic-gate return (WALK_DONE); 37430Sstevel@tonic-gate 37440Sstevel@tonic-gate if (kma->kma_newest == 0) 37450Sstevel@tonic-gate kma->kma_newest = bcp->bc_timestamp; 37460Sstevel@tonic-gate 37470Sstevel@tonic-gate if (kma->kma_addr) { 37480Sstevel@tonic-gate if (mdb_vread(&bufsize, sizeof (bufsize), 37490Sstevel@tonic-gate (uintptr_t)&bcp->bc_cache->cache_bufsize) == -1) { 37500Sstevel@tonic-gate mdb_warn( 37510Sstevel@tonic-gate "failed to read cache_bufsize for cache at %p", 37520Sstevel@tonic-gate bcp->bc_cache); 37530Sstevel@tonic-gate return (WALK_ERR); 37540Sstevel@tonic-gate } 37550Sstevel@tonic-gate 37560Sstevel@tonic-gate if (kma->kma_addr < (uintptr_t)bcp->bc_addr || 37570Sstevel@tonic-gate kma->kma_addr >= (uintptr_t)bcp->bc_addr + bufsize) 37580Sstevel@tonic-gate return (WALK_NEXT); 37590Sstevel@tonic-gate } 37600Sstevel@tonic-gate 37610Sstevel@tonic-gate delta = kma->kma_newest - bcp->bc_timestamp; 37620Sstevel@tonic-gate depth = MIN(bcp->bc_depth, KMEM_STACK_DEPTH); 37630Sstevel@tonic-gate 37640Sstevel@tonic-gate if (mdb_readstr(name, sizeof (name), (uintptr_t) 37650Sstevel@tonic-gate &bcp->bc_cache->cache_name) <= 0) 37660Sstevel@tonic-gate (void) mdb_snprintf(name, sizeof (name), "%a", bcp->bc_cache); 37670Sstevel@tonic-gate 37680Sstevel@tonic-gate mdb_printf("\nT-%lld.%09lld addr=%p %s\n", 37690Sstevel@tonic-gate delta / NANOSEC, delta % NANOSEC, bcp->bc_addr, name); 37700Sstevel@tonic-gate 37710Sstevel@tonic-gate for (i = 0; i < depth; i++) 37720Sstevel@tonic-gate mdb_printf("\t %a\n", bcp->bc_stack[i]); 37730Sstevel@tonic-gate 37740Sstevel@tonic-gate return (WALK_NEXT); 37750Sstevel@tonic-gate } 37760Sstevel@tonic-gate 37770Sstevel@tonic-gate int 37780Sstevel@tonic-gate kmalog(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 37790Sstevel@tonic-gate { 37800Sstevel@tonic-gate const char *logname = "kmem_transaction_log"; 37810Sstevel@tonic-gate kmalog_data_t kma; 37820Sstevel@tonic-gate 37830Sstevel@tonic-gate if (argc > 1) 37840Sstevel@tonic-gate return (DCMD_USAGE); 37850Sstevel@tonic-gate 37860Sstevel@tonic-gate kma.kma_newest = 0; 37870Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) 37880Sstevel@tonic-gate kma.kma_addr = addr; 37890Sstevel@tonic-gate else 37900Sstevel@tonic-gate kma.kma_addr = NULL; 37910Sstevel@tonic-gate 37920Sstevel@tonic-gate if (argc > 0) { 37930Sstevel@tonic-gate if (argv->a_type != MDB_TYPE_STRING) 37940Sstevel@tonic-gate return (DCMD_USAGE); 37950Sstevel@tonic-gate if (strcmp(argv->a_un.a_str, "fail") == 0) 37960Sstevel@tonic-gate logname = "kmem_failure_log"; 37970Sstevel@tonic-gate else if (strcmp(argv->a_un.a_str, "slab") == 0) 37980Sstevel@tonic-gate logname = "kmem_slab_log"; 37990Sstevel@tonic-gate else 38000Sstevel@tonic-gate return (DCMD_USAGE); 38010Sstevel@tonic-gate } 38020Sstevel@tonic-gate 38030Sstevel@tonic-gate if (mdb_readvar(&addr, logname) == -1) { 38040Sstevel@tonic-gate mdb_warn("failed to read %s log header pointer"); 38050Sstevel@tonic-gate return (DCMD_ERR); 38060Sstevel@tonic-gate } 38070Sstevel@tonic-gate 38080Sstevel@tonic-gate if (mdb_pwalk("kmem_log", (mdb_walk_cb_t)showbc, &kma, addr) == -1) { 38090Sstevel@tonic-gate mdb_warn("failed to walk kmem log"); 38100Sstevel@tonic-gate return (DCMD_ERR); 38110Sstevel@tonic-gate } 38120Sstevel@tonic-gate 38130Sstevel@tonic-gate return (DCMD_OK); 38140Sstevel@tonic-gate } 38150Sstevel@tonic-gate 38160Sstevel@tonic-gate /* 38170Sstevel@tonic-gate * As the final lure for die-hard crash(1M) users, we provide ::kmausers here. 38180Sstevel@tonic-gate * The first piece is a structure which we use to accumulate kmem_cache_t 38190Sstevel@tonic-gate * addresses of interest. The kmc_add is used as a callback for the kmem_cache 38200Sstevel@tonic-gate * walker; we either add all caches, or ones named explicitly as arguments. 38210Sstevel@tonic-gate */ 38220Sstevel@tonic-gate 38230Sstevel@tonic-gate typedef struct kmclist { 38240Sstevel@tonic-gate const char *kmc_name; /* Name to match (or NULL) */ 38250Sstevel@tonic-gate uintptr_t *kmc_caches; /* List of kmem_cache_t addrs */ 38260Sstevel@tonic-gate int kmc_nelems; /* Num entries in kmc_caches */ 38270Sstevel@tonic-gate int kmc_size; /* Size of kmc_caches array */ 38280Sstevel@tonic-gate } kmclist_t; 38290Sstevel@tonic-gate 38300Sstevel@tonic-gate static int 38310Sstevel@tonic-gate kmc_add(uintptr_t addr, const kmem_cache_t *cp, kmclist_t *kmc) 38320Sstevel@tonic-gate { 38330Sstevel@tonic-gate void *p; 38340Sstevel@tonic-gate int s; 38350Sstevel@tonic-gate 38360Sstevel@tonic-gate if (kmc->kmc_name == NULL || 38370Sstevel@tonic-gate strcmp(cp->cache_name, kmc->kmc_name) == 0) { 38380Sstevel@tonic-gate /* 38390Sstevel@tonic-gate * If we have a match, grow our array (if necessary), and then 38400Sstevel@tonic-gate * add the virtual address of the matching cache to our list. 38410Sstevel@tonic-gate */ 38420Sstevel@tonic-gate if (kmc->kmc_nelems >= kmc->kmc_size) { 38430Sstevel@tonic-gate s = kmc->kmc_size ? kmc->kmc_size * 2 : 256; 38440Sstevel@tonic-gate p = mdb_alloc(sizeof (uintptr_t) * s, UM_SLEEP | UM_GC); 38450Sstevel@tonic-gate 38460Sstevel@tonic-gate bcopy(kmc->kmc_caches, p, 38470Sstevel@tonic-gate sizeof (uintptr_t) * kmc->kmc_size); 38480Sstevel@tonic-gate 38490Sstevel@tonic-gate kmc->kmc_caches = p; 38500Sstevel@tonic-gate kmc->kmc_size = s; 38510Sstevel@tonic-gate } 38520Sstevel@tonic-gate 38530Sstevel@tonic-gate kmc->kmc_caches[kmc->kmc_nelems++] = addr; 38540Sstevel@tonic-gate return (kmc->kmc_name ? WALK_DONE : WALK_NEXT); 38550Sstevel@tonic-gate } 38560Sstevel@tonic-gate 38570Sstevel@tonic-gate return (WALK_NEXT); 38580Sstevel@tonic-gate } 38590Sstevel@tonic-gate 38600Sstevel@tonic-gate /* 38610Sstevel@tonic-gate * The second piece of ::kmausers is a hash table of allocations. Each 38620Sstevel@tonic-gate * allocation owner is identified by its stack trace and data_size. We then 38630Sstevel@tonic-gate * track the total bytes of all such allocations, and the number of allocations 38640Sstevel@tonic-gate * to report at the end. Once we have a list of caches, we walk through the 38650Sstevel@tonic-gate * allocated bufctls of each, and update our hash table accordingly. 38660Sstevel@tonic-gate */ 38670Sstevel@tonic-gate 38680Sstevel@tonic-gate typedef struct kmowner { 38690Sstevel@tonic-gate struct kmowner *kmo_head; /* First hash elt in bucket */ 38700Sstevel@tonic-gate struct kmowner *kmo_next; /* Next hash elt in chain */ 38710Sstevel@tonic-gate size_t kmo_signature; /* Hash table signature */ 38720Sstevel@tonic-gate uint_t kmo_num; /* Number of allocations */ 38730Sstevel@tonic-gate size_t kmo_data_size; /* Size of each allocation */ 38740Sstevel@tonic-gate size_t kmo_total_size; /* Total bytes of allocation */ 38750Sstevel@tonic-gate int kmo_depth; /* Depth of stack trace */ 38760Sstevel@tonic-gate uintptr_t kmo_stack[KMEM_STACK_DEPTH]; /* Stack trace */ 38770Sstevel@tonic-gate } kmowner_t; 38780Sstevel@tonic-gate 38790Sstevel@tonic-gate typedef struct kmusers { 38800Sstevel@tonic-gate uintptr_t kmu_addr; /* address of interest */ 38810Sstevel@tonic-gate const kmem_cache_t *kmu_cache; /* Current kmem cache */ 38820Sstevel@tonic-gate kmowner_t *kmu_hash; /* Hash table of owners */ 38830Sstevel@tonic-gate int kmu_nelems; /* Number of entries in use */ 38840Sstevel@tonic-gate int kmu_size; /* Total number of entries */ 38850Sstevel@tonic-gate } kmusers_t; 38860Sstevel@tonic-gate 38870Sstevel@tonic-gate static void 38880Sstevel@tonic-gate kmu_add(kmusers_t *kmu, const kmem_bufctl_audit_t *bcp, 38890Sstevel@tonic-gate size_t size, size_t data_size) 38900Sstevel@tonic-gate { 38910Sstevel@tonic-gate int i, depth = MIN(bcp->bc_depth, KMEM_STACK_DEPTH); 38920Sstevel@tonic-gate size_t bucket, signature = data_size; 38930Sstevel@tonic-gate kmowner_t *kmo, *kmoend; 38940Sstevel@tonic-gate 38950Sstevel@tonic-gate /* 38960Sstevel@tonic-gate * If the hash table is full, double its size and rehash everything. 38970Sstevel@tonic-gate */ 38980Sstevel@tonic-gate if (kmu->kmu_nelems >= kmu->kmu_size) { 38990Sstevel@tonic-gate int s = kmu->kmu_size ? kmu->kmu_size * 2 : 1024; 39000Sstevel@tonic-gate 39010Sstevel@tonic-gate kmo = mdb_alloc(sizeof (kmowner_t) * s, UM_SLEEP | UM_GC); 39020Sstevel@tonic-gate bcopy(kmu->kmu_hash, kmo, sizeof (kmowner_t) * kmu->kmu_size); 39030Sstevel@tonic-gate kmu->kmu_hash = kmo; 39040Sstevel@tonic-gate kmu->kmu_size = s; 39050Sstevel@tonic-gate 39060Sstevel@tonic-gate kmoend = kmu->kmu_hash + kmu->kmu_size; 39070Sstevel@tonic-gate for (kmo = kmu->kmu_hash; kmo < kmoend; kmo++) 39080Sstevel@tonic-gate kmo->kmo_head = NULL; 39090Sstevel@tonic-gate 39100Sstevel@tonic-gate kmoend = kmu->kmu_hash + kmu->kmu_nelems; 39110Sstevel@tonic-gate for (kmo = kmu->kmu_hash; kmo < kmoend; kmo++) { 39120Sstevel@tonic-gate bucket = kmo->kmo_signature & (kmu->kmu_size - 1); 39130Sstevel@tonic-gate kmo->kmo_next = kmu->kmu_hash[bucket].kmo_head; 39140Sstevel@tonic-gate kmu->kmu_hash[bucket].kmo_head = kmo; 39150Sstevel@tonic-gate } 39160Sstevel@tonic-gate } 39170Sstevel@tonic-gate 39180Sstevel@tonic-gate /* 39190Sstevel@tonic-gate * Finish computing the hash signature from the stack trace, and then 39200Sstevel@tonic-gate * see if the owner is in the hash table. If so, update our stats. 39210Sstevel@tonic-gate */ 39220Sstevel@tonic-gate for (i = 0; i < depth; i++) 39230Sstevel@tonic-gate signature += bcp->bc_stack[i]; 39240Sstevel@tonic-gate 39250Sstevel@tonic-gate bucket = signature & (kmu->kmu_size - 1); 39260Sstevel@tonic-gate 39270Sstevel@tonic-gate for (kmo = kmu->kmu_hash[bucket].kmo_head; kmo; kmo = kmo->kmo_next) { 39280Sstevel@tonic-gate if (kmo->kmo_signature == signature) { 39290Sstevel@tonic-gate size_t difference = 0; 39300Sstevel@tonic-gate 39310Sstevel@tonic-gate difference |= kmo->kmo_data_size - data_size; 39320Sstevel@tonic-gate difference |= kmo->kmo_depth - depth; 39330Sstevel@tonic-gate 39340Sstevel@tonic-gate for (i = 0; i < depth; i++) { 39350Sstevel@tonic-gate difference |= kmo->kmo_stack[i] - 39360Sstevel@tonic-gate bcp->bc_stack[i]; 39370Sstevel@tonic-gate } 39380Sstevel@tonic-gate 39390Sstevel@tonic-gate if (difference == 0) { 39400Sstevel@tonic-gate kmo->kmo_total_size += size; 39410Sstevel@tonic-gate kmo->kmo_num++; 39420Sstevel@tonic-gate return; 39430Sstevel@tonic-gate } 39440Sstevel@tonic-gate } 39450Sstevel@tonic-gate } 39460Sstevel@tonic-gate 39470Sstevel@tonic-gate /* 39480Sstevel@tonic-gate * If the owner is not yet hashed, grab the next element and fill it 39490Sstevel@tonic-gate * in based on the allocation information. 39500Sstevel@tonic-gate */ 39510Sstevel@tonic-gate kmo = &kmu->kmu_hash[kmu->kmu_nelems++]; 39520Sstevel@tonic-gate kmo->kmo_next = kmu->kmu_hash[bucket].kmo_head; 39530Sstevel@tonic-gate kmu->kmu_hash[bucket].kmo_head = kmo; 39540Sstevel@tonic-gate 39550Sstevel@tonic-gate kmo->kmo_signature = signature; 39560Sstevel@tonic-gate kmo->kmo_num = 1; 39570Sstevel@tonic-gate kmo->kmo_data_size = data_size; 39580Sstevel@tonic-gate kmo->kmo_total_size = size; 39590Sstevel@tonic-gate kmo->kmo_depth = depth; 39600Sstevel@tonic-gate 39610Sstevel@tonic-gate for (i = 0; i < depth; i++) 39620Sstevel@tonic-gate kmo->kmo_stack[i] = bcp->bc_stack[i]; 39630Sstevel@tonic-gate } 39640Sstevel@tonic-gate 39650Sstevel@tonic-gate /* 39660Sstevel@tonic-gate * When ::kmausers is invoked without the -f flag, we simply update our hash 39670Sstevel@tonic-gate * table with the information from each allocated bufctl. 39680Sstevel@tonic-gate */ 39690Sstevel@tonic-gate /*ARGSUSED*/ 39700Sstevel@tonic-gate static int 39710Sstevel@tonic-gate kmause1(uintptr_t addr, const kmem_bufctl_audit_t *bcp, kmusers_t *kmu) 39720Sstevel@tonic-gate { 39730Sstevel@tonic-gate const kmem_cache_t *cp = kmu->kmu_cache; 39740Sstevel@tonic-gate 39750Sstevel@tonic-gate kmu_add(kmu, bcp, cp->cache_bufsize, cp->cache_bufsize); 39760Sstevel@tonic-gate return (WALK_NEXT); 39770Sstevel@tonic-gate } 39780Sstevel@tonic-gate 39790Sstevel@tonic-gate /* 39800Sstevel@tonic-gate * When ::kmausers is invoked with the -f flag, we print out the information 39810Sstevel@tonic-gate * for each bufctl as well as updating the hash table. 39820Sstevel@tonic-gate */ 39830Sstevel@tonic-gate static int 39840Sstevel@tonic-gate kmause2(uintptr_t addr, const kmem_bufctl_audit_t *bcp, kmusers_t *kmu) 39850Sstevel@tonic-gate { 39860Sstevel@tonic-gate int i, depth = MIN(bcp->bc_depth, KMEM_STACK_DEPTH); 39870Sstevel@tonic-gate const kmem_cache_t *cp = kmu->kmu_cache; 39880Sstevel@tonic-gate kmem_bufctl_t bufctl; 39890Sstevel@tonic-gate 39900Sstevel@tonic-gate if (kmu->kmu_addr) { 39910Sstevel@tonic-gate if (mdb_vread(&bufctl, sizeof (bufctl), addr) == -1) 39920Sstevel@tonic-gate mdb_warn("couldn't read bufctl at %p", addr); 39930Sstevel@tonic-gate else if (kmu->kmu_addr < (uintptr_t)bufctl.bc_addr || 39940Sstevel@tonic-gate kmu->kmu_addr >= (uintptr_t)bufctl.bc_addr + 39950Sstevel@tonic-gate cp->cache_bufsize) 39960Sstevel@tonic-gate return (WALK_NEXT); 39970Sstevel@tonic-gate } 39980Sstevel@tonic-gate 39990Sstevel@tonic-gate mdb_printf("size %d, addr %p, thread %p, cache %s\n", 40000Sstevel@tonic-gate cp->cache_bufsize, addr, bcp->bc_thread, cp->cache_name); 40010Sstevel@tonic-gate 40020Sstevel@tonic-gate for (i = 0; i < depth; i++) 40030Sstevel@tonic-gate mdb_printf("\t %a\n", bcp->bc_stack[i]); 40040Sstevel@tonic-gate 40050Sstevel@tonic-gate kmu_add(kmu, bcp, cp->cache_bufsize, cp->cache_bufsize); 40060Sstevel@tonic-gate return (WALK_NEXT); 40070Sstevel@tonic-gate } 40080Sstevel@tonic-gate 40090Sstevel@tonic-gate /* 40100Sstevel@tonic-gate * We sort our results by allocation size before printing them. 40110Sstevel@tonic-gate */ 40120Sstevel@tonic-gate static int 40130Sstevel@tonic-gate kmownercmp(const void *lp, const void *rp) 40140Sstevel@tonic-gate { 40150Sstevel@tonic-gate const kmowner_t *lhs = lp; 40160Sstevel@tonic-gate const kmowner_t *rhs = rp; 40170Sstevel@tonic-gate 40180Sstevel@tonic-gate return (rhs->kmo_total_size - lhs->kmo_total_size); 40190Sstevel@tonic-gate } 40200Sstevel@tonic-gate 40210Sstevel@tonic-gate /* 40220Sstevel@tonic-gate * The main engine of ::kmausers is relatively straightforward: First we 40230Sstevel@tonic-gate * accumulate our list of kmem_cache_t addresses into the kmclist_t. Next we 40240Sstevel@tonic-gate * iterate over the allocated bufctls of each cache in the list. Finally, 40250Sstevel@tonic-gate * we sort and print our results. 40260Sstevel@tonic-gate */ 40270Sstevel@tonic-gate /*ARGSUSED*/ 40280Sstevel@tonic-gate int 40290Sstevel@tonic-gate kmausers(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 40300Sstevel@tonic-gate { 40310Sstevel@tonic-gate int mem_threshold = 8192; /* Minimum # bytes for printing */ 40320Sstevel@tonic-gate int cnt_threshold = 100; /* Minimum # blocks for printing */ 40330Sstevel@tonic-gate int audited_caches = 0; /* Number of KMF_AUDIT caches found */ 40340Sstevel@tonic-gate int do_all_caches = 1; /* Do all caches (no arguments) */ 40350Sstevel@tonic-gate int opt_e = FALSE; /* Include "small" users */ 40360Sstevel@tonic-gate int opt_f = FALSE; /* Print stack traces */ 40370Sstevel@tonic-gate 40380Sstevel@tonic-gate mdb_walk_cb_t callback = (mdb_walk_cb_t)kmause1; 40390Sstevel@tonic-gate kmowner_t *kmo, *kmoend; 40400Sstevel@tonic-gate int i, oelems; 40410Sstevel@tonic-gate 40420Sstevel@tonic-gate kmclist_t kmc; 40430Sstevel@tonic-gate kmusers_t kmu; 40440Sstevel@tonic-gate 40450Sstevel@tonic-gate bzero(&kmc, sizeof (kmc)); 40460Sstevel@tonic-gate bzero(&kmu, sizeof (kmu)); 40470Sstevel@tonic-gate 40480Sstevel@tonic-gate while ((i = mdb_getopts(argc, argv, 40490Sstevel@tonic-gate 'e', MDB_OPT_SETBITS, TRUE, &opt_e, 40500Sstevel@tonic-gate 'f', MDB_OPT_SETBITS, TRUE, &opt_f, NULL)) != argc) { 40510Sstevel@tonic-gate 40520Sstevel@tonic-gate argv += i; /* skip past options we just processed */ 40530Sstevel@tonic-gate argc -= i; /* adjust argc */ 40540Sstevel@tonic-gate 40550Sstevel@tonic-gate if (argv->a_type != MDB_TYPE_STRING || *argv->a_un.a_str == '-') 40560Sstevel@tonic-gate return (DCMD_USAGE); 40570Sstevel@tonic-gate 40580Sstevel@tonic-gate oelems = kmc.kmc_nelems; 40590Sstevel@tonic-gate kmc.kmc_name = argv->a_un.a_str; 40600Sstevel@tonic-gate (void) mdb_walk("kmem_cache", (mdb_walk_cb_t)kmc_add, &kmc); 40610Sstevel@tonic-gate 40620Sstevel@tonic-gate if (kmc.kmc_nelems == oelems) { 40630Sstevel@tonic-gate mdb_warn("unknown kmem cache: %s\n", kmc.kmc_name); 40640Sstevel@tonic-gate return (DCMD_ERR); 40650Sstevel@tonic-gate } 40660Sstevel@tonic-gate 40670Sstevel@tonic-gate do_all_caches = 0; 40680Sstevel@tonic-gate argv++; 40690Sstevel@tonic-gate argc--; 40700Sstevel@tonic-gate } 40710Sstevel@tonic-gate 40720Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 40730Sstevel@tonic-gate opt_f = TRUE; 40740Sstevel@tonic-gate kmu.kmu_addr = addr; 40750Sstevel@tonic-gate } else { 40760Sstevel@tonic-gate kmu.kmu_addr = NULL; 40770Sstevel@tonic-gate } 40780Sstevel@tonic-gate 40790Sstevel@tonic-gate if (opt_e) 40800Sstevel@tonic-gate mem_threshold = cnt_threshold = 0; 40810Sstevel@tonic-gate 40820Sstevel@tonic-gate if (opt_f) 40830Sstevel@tonic-gate callback = (mdb_walk_cb_t)kmause2; 40840Sstevel@tonic-gate 40850Sstevel@tonic-gate if (do_all_caches) { 40860Sstevel@tonic-gate kmc.kmc_name = NULL; /* match all cache names */ 40870Sstevel@tonic-gate (void) mdb_walk("kmem_cache", (mdb_walk_cb_t)kmc_add, &kmc); 40880Sstevel@tonic-gate } 40890Sstevel@tonic-gate 40900Sstevel@tonic-gate for (i = 0; i < kmc.kmc_nelems; i++) { 40910Sstevel@tonic-gate uintptr_t cp = kmc.kmc_caches[i]; 40920Sstevel@tonic-gate kmem_cache_t c; 40930Sstevel@tonic-gate 40940Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), cp) == -1) { 40950Sstevel@tonic-gate mdb_warn("failed to read cache at %p", cp); 40960Sstevel@tonic-gate continue; 40970Sstevel@tonic-gate } 40980Sstevel@tonic-gate 40990Sstevel@tonic-gate if (!(c.cache_flags & KMF_AUDIT)) { 41000Sstevel@tonic-gate if (!do_all_caches) { 41010Sstevel@tonic-gate mdb_warn("KMF_AUDIT is not enabled for %s\n", 41020Sstevel@tonic-gate c.cache_name); 41030Sstevel@tonic-gate } 41040Sstevel@tonic-gate continue; 41050Sstevel@tonic-gate } 41060Sstevel@tonic-gate 41070Sstevel@tonic-gate kmu.kmu_cache = &c; 41080Sstevel@tonic-gate (void) mdb_pwalk("bufctl", callback, &kmu, cp); 41090Sstevel@tonic-gate audited_caches++; 41100Sstevel@tonic-gate } 41110Sstevel@tonic-gate 41120Sstevel@tonic-gate if (audited_caches == 0 && do_all_caches) { 41130Sstevel@tonic-gate mdb_warn("KMF_AUDIT is not enabled for any caches\n"); 41140Sstevel@tonic-gate return (DCMD_ERR); 41150Sstevel@tonic-gate } 41160Sstevel@tonic-gate 41170Sstevel@tonic-gate qsort(kmu.kmu_hash, kmu.kmu_nelems, sizeof (kmowner_t), kmownercmp); 41180Sstevel@tonic-gate kmoend = kmu.kmu_hash + kmu.kmu_nelems; 41190Sstevel@tonic-gate 41200Sstevel@tonic-gate for (kmo = kmu.kmu_hash; kmo < kmoend; kmo++) { 41210Sstevel@tonic-gate if (kmo->kmo_total_size < mem_threshold && 41220Sstevel@tonic-gate kmo->kmo_num < cnt_threshold) 41230Sstevel@tonic-gate continue; 41240Sstevel@tonic-gate mdb_printf("%lu bytes for %u allocations with data size %lu:\n", 41250Sstevel@tonic-gate kmo->kmo_total_size, kmo->kmo_num, kmo->kmo_data_size); 41260Sstevel@tonic-gate for (i = 0; i < kmo->kmo_depth; i++) 41270Sstevel@tonic-gate mdb_printf("\t %a\n", kmo->kmo_stack[i]); 41280Sstevel@tonic-gate } 41290Sstevel@tonic-gate 41300Sstevel@tonic-gate return (DCMD_OK); 41310Sstevel@tonic-gate } 41320Sstevel@tonic-gate 41330Sstevel@tonic-gate void 41340Sstevel@tonic-gate kmausers_help(void) 41350Sstevel@tonic-gate { 41360Sstevel@tonic-gate mdb_printf( 41370Sstevel@tonic-gate "Displays the largest users of the kmem allocator, sorted by \n" 41380Sstevel@tonic-gate "trace. If one or more caches is specified, only those caches\n" 41390Sstevel@tonic-gate "will be searched. By default, all caches are searched. If an\n" 41400Sstevel@tonic-gate "address is specified, then only those allocations which include\n" 41410Sstevel@tonic-gate "the given address are displayed. Specifying an address implies\n" 41420Sstevel@tonic-gate "-f.\n" 41430Sstevel@tonic-gate "\n" 41440Sstevel@tonic-gate "\t-e\tInclude all users, not just the largest\n" 41450Sstevel@tonic-gate "\t-f\tDisplay individual allocations. By default, users are\n" 41460Sstevel@tonic-gate "\t\tgrouped by stack\n"); 41470Sstevel@tonic-gate } 41480Sstevel@tonic-gate 41490Sstevel@tonic-gate static int 41500Sstevel@tonic-gate kmem_ready_check(void) 41510Sstevel@tonic-gate { 41520Sstevel@tonic-gate int ready; 41530Sstevel@tonic-gate 41540Sstevel@tonic-gate if (mdb_readvar(&ready, "kmem_ready") < 0) 41550Sstevel@tonic-gate return (-1); /* errno is set for us */ 41560Sstevel@tonic-gate 41570Sstevel@tonic-gate return (ready); 41580Sstevel@tonic-gate } 41590Sstevel@tonic-gate 41600Sstevel@tonic-gate /*ARGSUSED*/ 41610Sstevel@tonic-gate static void 41621528Sjwadams kmem_statechange_cb(void *arg) 41630Sstevel@tonic-gate { 41641528Sjwadams static int been_ready = 0; 41651528Sjwadams 41661528Sjwadams leaky_cleanup(1); /* state changes invalidate leaky state */ 41671528Sjwadams 41681528Sjwadams if (been_ready) 41691528Sjwadams return; 41701528Sjwadams 41710Sstevel@tonic-gate if (kmem_ready_check() <= 0) 41720Sstevel@tonic-gate return; 41730Sstevel@tonic-gate 41741528Sjwadams been_ready = 1; 41750Sstevel@tonic-gate (void) mdb_walk("kmem_cache", (mdb_walk_cb_t)kmem_init_walkers, NULL); 41760Sstevel@tonic-gate } 41770Sstevel@tonic-gate 41780Sstevel@tonic-gate void 41790Sstevel@tonic-gate kmem_init(void) 41800Sstevel@tonic-gate { 41810Sstevel@tonic-gate mdb_walker_t w = { 41820Sstevel@tonic-gate "kmem_cache", "walk list of kmem caches", kmem_cache_walk_init, 41830Sstevel@tonic-gate kmem_cache_walk_step, kmem_cache_walk_fini 41840Sstevel@tonic-gate }; 41850Sstevel@tonic-gate 41860Sstevel@tonic-gate /* 41870Sstevel@tonic-gate * If kmem is ready, we'll need to invoke the kmem_cache walker 41880Sstevel@tonic-gate * immediately. Walkers in the linkage structure won't be ready until 41890Sstevel@tonic-gate * _mdb_init returns, so we'll need to add this one manually. If kmem 41900Sstevel@tonic-gate * is ready, we'll use the walker to initialize the caches. If kmem 41910Sstevel@tonic-gate * isn't ready, we'll register a callback that will allow us to defer 41920Sstevel@tonic-gate * cache walking until it is. 41930Sstevel@tonic-gate */ 41940Sstevel@tonic-gate if (mdb_add_walker(&w) != 0) { 41950Sstevel@tonic-gate mdb_warn("failed to add kmem_cache walker"); 41960Sstevel@tonic-gate return; 41970Sstevel@tonic-gate } 41980Sstevel@tonic-gate 41991528Sjwadams (void) mdb_callback_add(MDB_CALLBACK_STCHG, kmem_statechange_cb, NULL); 42001528Sjwadams kmem_statechange_cb(NULL); 42010Sstevel@tonic-gate } 42020Sstevel@tonic-gate 42030Sstevel@tonic-gate typedef struct whatthread { 42040Sstevel@tonic-gate uintptr_t wt_target; 42050Sstevel@tonic-gate int wt_verbose; 42060Sstevel@tonic-gate } whatthread_t; 42070Sstevel@tonic-gate 42080Sstevel@tonic-gate static int 42090Sstevel@tonic-gate whatthread_walk_thread(uintptr_t addr, const kthread_t *t, whatthread_t *w) 42100Sstevel@tonic-gate { 42110Sstevel@tonic-gate uintptr_t current, data; 42120Sstevel@tonic-gate 42130Sstevel@tonic-gate if (t->t_stkbase == NULL) 42140Sstevel@tonic-gate return (WALK_NEXT); 42150Sstevel@tonic-gate 42160Sstevel@tonic-gate /* 42170Sstevel@tonic-gate * Warn about swapped out threads, but drive on anyway 42180Sstevel@tonic-gate */ 42190Sstevel@tonic-gate if (!(t->t_schedflag & TS_LOAD)) { 42200Sstevel@tonic-gate mdb_warn("thread %p's stack swapped out\n", addr); 42210Sstevel@tonic-gate return (WALK_NEXT); 42220Sstevel@tonic-gate } 42230Sstevel@tonic-gate 42240Sstevel@tonic-gate /* 42250Sstevel@tonic-gate * Search the thread's stack for the given pointer. Note that it would 42260Sstevel@tonic-gate * be more efficient to follow ::kgrep's lead and read in page-sized 42270Sstevel@tonic-gate * chunks, but this routine is already fast and simple. 42280Sstevel@tonic-gate */ 42290Sstevel@tonic-gate for (current = (uintptr_t)t->t_stkbase; current < (uintptr_t)t->t_stk; 42300Sstevel@tonic-gate current += sizeof (uintptr_t)) { 42310Sstevel@tonic-gate if (mdb_vread(&data, sizeof (data), current) == -1) { 42320Sstevel@tonic-gate mdb_warn("couldn't read thread %p's stack at %p", 42330Sstevel@tonic-gate addr, current); 42340Sstevel@tonic-gate return (WALK_ERR); 42350Sstevel@tonic-gate } 42360Sstevel@tonic-gate 42370Sstevel@tonic-gate if (data == w->wt_target) { 42380Sstevel@tonic-gate if (w->wt_verbose) { 42390Sstevel@tonic-gate mdb_printf("%p in thread %p's stack%s\n", 42400Sstevel@tonic-gate current, addr, stack_active(t, current)); 42410Sstevel@tonic-gate } else { 42420Sstevel@tonic-gate mdb_printf("%#lr\n", addr); 42430Sstevel@tonic-gate return (WALK_NEXT); 42440Sstevel@tonic-gate } 42450Sstevel@tonic-gate } 42460Sstevel@tonic-gate } 42470Sstevel@tonic-gate 42480Sstevel@tonic-gate return (WALK_NEXT); 42490Sstevel@tonic-gate } 42500Sstevel@tonic-gate 42510Sstevel@tonic-gate int 42520Sstevel@tonic-gate whatthread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 42530Sstevel@tonic-gate { 42540Sstevel@tonic-gate whatthread_t w; 42550Sstevel@tonic-gate 42560Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 42570Sstevel@tonic-gate return (DCMD_USAGE); 42580Sstevel@tonic-gate 42590Sstevel@tonic-gate w.wt_verbose = FALSE; 42600Sstevel@tonic-gate w.wt_target = addr; 42610Sstevel@tonic-gate 42620Sstevel@tonic-gate if (mdb_getopts(argc, argv, 42630Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &w.wt_verbose, NULL) != argc) 42640Sstevel@tonic-gate return (DCMD_USAGE); 42650Sstevel@tonic-gate 42660Sstevel@tonic-gate if (mdb_walk("thread", (mdb_walk_cb_t)whatthread_walk_thread, &w) 42670Sstevel@tonic-gate == -1) { 42680Sstevel@tonic-gate mdb_warn("couldn't walk threads"); 42690Sstevel@tonic-gate return (DCMD_ERR); 42700Sstevel@tonic-gate } 42710Sstevel@tonic-gate 42720Sstevel@tonic-gate return (DCMD_OK); 42730Sstevel@tonic-gate } 4274