17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*789d94c2Sjwadams * Common Development and Distribution License (the "License"). 6*789d94c2Sjwadams * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*789d94c2Sjwadams * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate #include <mdb/mdb_param.h> 297c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h> 307c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf.h> 317c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 327c478bd9Sstevel@tonic-gate #include <sys/kmem_impl.h> 337c478bd9Sstevel@tonic-gate #include <sys/vmem_impl.h> 347c478bd9Sstevel@tonic-gate #include <sys/machelf.h> 357c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 367c478bd9Sstevel@tonic-gate #include <sys/kobj.h> 377c478bd9Sstevel@tonic-gate #include <sys/panic.h> 387c478bd9Sstevel@tonic-gate #include <sys/stack.h> 397c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 407c478bd9Sstevel@tonic-gate #include <vm/page.h> 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate #include "kmem.h" 43*789d94c2Sjwadams #include "leaky.h" 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate #define dprintf(x) if (mdb_debug_level) { \ 467c478bd9Sstevel@tonic-gate mdb_printf("kmem debug: "); \ 477c478bd9Sstevel@tonic-gate /*CSTYLED*/\ 487c478bd9Sstevel@tonic-gate mdb_printf x ;\ 497c478bd9Sstevel@tonic-gate } 507c478bd9Sstevel@tonic-gate 517c478bd9Sstevel@tonic-gate #define KM_ALLOCATED 0x01 527c478bd9Sstevel@tonic-gate #define KM_FREE 0x02 537c478bd9Sstevel@tonic-gate #define KM_BUFCTL 0x04 547c478bd9Sstevel@tonic-gate #define KM_CONSTRUCTED 0x08 /* only constructed free buffers */ 557c478bd9Sstevel@tonic-gate #define KM_HASH 0x10 567c478bd9Sstevel@tonic-gate 577c478bd9Sstevel@tonic-gate static int mdb_debug_level = 0; 587c478bd9Sstevel@tonic-gate 597c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 607c478bd9Sstevel@tonic-gate static int 617c478bd9Sstevel@tonic-gate kmem_init_walkers(uintptr_t addr, const kmem_cache_t *c, void *ignored) 627c478bd9Sstevel@tonic-gate { 637c478bd9Sstevel@tonic-gate mdb_walker_t w; 647c478bd9Sstevel@tonic-gate char descr[64]; 657c478bd9Sstevel@tonic-gate 667c478bd9Sstevel@tonic-gate (void) mdb_snprintf(descr, sizeof (descr), 677c478bd9Sstevel@tonic-gate "walk the %s cache", c->cache_name); 687c478bd9Sstevel@tonic-gate 697c478bd9Sstevel@tonic-gate w.walk_name = c->cache_name; 707c478bd9Sstevel@tonic-gate w.walk_descr = descr; 717c478bd9Sstevel@tonic-gate w.walk_init = kmem_walk_init; 727c478bd9Sstevel@tonic-gate w.walk_step = kmem_walk_step; 737c478bd9Sstevel@tonic-gate w.walk_fini = kmem_walk_fini; 747c478bd9Sstevel@tonic-gate w.walk_init_arg = (void *)addr; 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gate if (mdb_add_walker(&w) == -1) 777c478bd9Sstevel@tonic-gate mdb_warn("failed to add %s walker", c->cache_name); 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate return (WALK_NEXT); 807c478bd9Sstevel@tonic-gate } 817c478bd9Sstevel@tonic-gate 827c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 837c478bd9Sstevel@tonic-gate int 847c478bd9Sstevel@tonic-gate kmem_debug(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 857c478bd9Sstevel@tonic-gate { 867c478bd9Sstevel@tonic-gate mdb_debug_level ^= 1; 877c478bd9Sstevel@tonic-gate 887c478bd9Sstevel@tonic-gate mdb_printf("kmem: debugging is now %s\n", 897c478bd9Sstevel@tonic-gate mdb_debug_level ? "on" : "off"); 907c478bd9Sstevel@tonic-gate 917c478bd9Sstevel@tonic-gate return (DCMD_OK); 927c478bd9Sstevel@tonic-gate } 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate typedef struct { 957c478bd9Sstevel@tonic-gate uintptr_t kcw_first; 967c478bd9Sstevel@tonic-gate uintptr_t kcw_current; 977c478bd9Sstevel@tonic-gate } kmem_cache_walk_t; 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate int 1007c478bd9Sstevel@tonic-gate kmem_cache_walk_init(mdb_walk_state_t *wsp) 1017c478bd9Sstevel@tonic-gate { 1027c478bd9Sstevel@tonic-gate kmem_cache_walk_t *kcw; 1037c478bd9Sstevel@tonic-gate kmem_cache_t c; 1047c478bd9Sstevel@tonic-gate uintptr_t cp; 1057c478bd9Sstevel@tonic-gate GElf_Sym sym; 1067c478bd9Sstevel@tonic-gate 1077c478bd9Sstevel@tonic-gate if (mdb_lookup_by_name("kmem_null_cache", &sym) == -1) { 1087c478bd9Sstevel@tonic-gate mdb_warn("couldn't find kmem_null_cache"); 1097c478bd9Sstevel@tonic-gate return (WALK_ERR); 1107c478bd9Sstevel@tonic-gate } 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate cp = (uintptr_t)sym.st_value; 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate if (mdb_vread(&c, sizeof (kmem_cache_t), cp) == -1) { 1157c478bd9Sstevel@tonic-gate mdb_warn("couldn't read cache at %p", cp); 1167c478bd9Sstevel@tonic-gate return (WALK_ERR); 1177c478bd9Sstevel@tonic-gate } 1187c478bd9Sstevel@tonic-gate 1197c478bd9Sstevel@tonic-gate kcw = mdb_alloc(sizeof (kmem_cache_walk_t), UM_SLEEP); 1207c478bd9Sstevel@tonic-gate 1217c478bd9Sstevel@tonic-gate kcw->kcw_first = cp; 1227c478bd9Sstevel@tonic-gate kcw->kcw_current = (uintptr_t)c.cache_next; 1237c478bd9Sstevel@tonic-gate wsp->walk_data = kcw; 1247c478bd9Sstevel@tonic-gate 1257c478bd9Sstevel@tonic-gate return (WALK_NEXT); 1267c478bd9Sstevel@tonic-gate } 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate int 1297c478bd9Sstevel@tonic-gate kmem_cache_walk_step(mdb_walk_state_t *wsp) 1307c478bd9Sstevel@tonic-gate { 1317c478bd9Sstevel@tonic-gate kmem_cache_walk_t *kcw = wsp->walk_data; 1327c478bd9Sstevel@tonic-gate kmem_cache_t c; 1337c478bd9Sstevel@tonic-gate int status; 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate if (mdb_vread(&c, sizeof (kmem_cache_t), kcw->kcw_current) == -1) { 1367c478bd9Sstevel@tonic-gate mdb_warn("couldn't read cache at %p", kcw->kcw_current); 1377c478bd9Sstevel@tonic-gate return (WALK_DONE); 1387c478bd9Sstevel@tonic-gate } 1397c478bd9Sstevel@tonic-gate 1407c478bd9Sstevel@tonic-gate status = wsp->walk_callback(kcw->kcw_current, &c, wsp->walk_cbdata); 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate if ((kcw->kcw_current = (uintptr_t)c.cache_next) == kcw->kcw_first) 1437c478bd9Sstevel@tonic-gate return (WALK_DONE); 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate return (status); 1467c478bd9Sstevel@tonic-gate } 1477c478bd9Sstevel@tonic-gate 1487c478bd9Sstevel@tonic-gate void 1497c478bd9Sstevel@tonic-gate kmem_cache_walk_fini(mdb_walk_state_t *wsp) 1507c478bd9Sstevel@tonic-gate { 1517c478bd9Sstevel@tonic-gate kmem_cache_walk_t *kcw = wsp->walk_data; 1527c478bd9Sstevel@tonic-gate mdb_free(kcw, sizeof (kmem_cache_walk_t)); 1537c478bd9Sstevel@tonic-gate } 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate int 1567c478bd9Sstevel@tonic-gate kmem_cpu_cache_walk_init(mdb_walk_state_t *wsp) 1577c478bd9Sstevel@tonic-gate { 1587c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 1597c478bd9Sstevel@tonic-gate mdb_warn("kmem_cpu_cache doesn't support global walks"); 1607c478bd9Sstevel@tonic-gate return (WALK_ERR); 1617c478bd9Sstevel@tonic-gate } 1627c478bd9Sstevel@tonic-gate 1637c478bd9Sstevel@tonic-gate if (mdb_layered_walk("cpu", wsp) == -1) { 1647c478bd9Sstevel@tonic-gate mdb_warn("couldn't walk 'cpu'"); 1657c478bd9Sstevel@tonic-gate return (WALK_ERR); 1667c478bd9Sstevel@tonic-gate } 1677c478bd9Sstevel@tonic-gate 1687c478bd9Sstevel@tonic-gate wsp->walk_data = (void *)wsp->walk_addr; 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate return (WALK_NEXT); 1717c478bd9Sstevel@tonic-gate } 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate int 1747c478bd9Sstevel@tonic-gate kmem_cpu_cache_walk_step(mdb_walk_state_t *wsp) 1757c478bd9Sstevel@tonic-gate { 1767c478bd9Sstevel@tonic-gate uintptr_t caddr = (uintptr_t)wsp->walk_data; 1777c478bd9Sstevel@tonic-gate const cpu_t *cpu = wsp->walk_layer; 1787c478bd9Sstevel@tonic-gate kmem_cpu_cache_t cc; 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate caddr += cpu->cpu_cache_offset; 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate if (mdb_vread(&cc, sizeof (kmem_cpu_cache_t), caddr) == -1) { 1837c478bd9Sstevel@tonic-gate mdb_warn("couldn't read kmem_cpu_cache at %p", caddr); 1847c478bd9Sstevel@tonic-gate return (WALK_ERR); 1857c478bd9Sstevel@tonic-gate } 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate return (wsp->walk_callback(caddr, &cc, wsp->walk_cbdata)); 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate int 1917c478bd9Sstevel@tonic-gate kmem_slab_walk_init(mdb_walk_state_t *wsp) 1927c478bd9Sstevel@tonic-gate { 1937c478bd9Sstevel@tonic-gate uintptr_t caddr = wsp->walk_addr; 1947c478bd9Sstevel@tonic-gate kmem_cache_t c; 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate if (caddr == NULL) { 1977c478bd9Sstevel@tonic-gate mdb_warn("kmem_slab doesn't support global walks\n"); 1987c478bd9Sstevel@tonic-gate return (WALK_ERR); 1997c478bd9Sstevel@tonic-gate } 2007c478bd9Sstevel@tonic-gate 2017c478bd9Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), caddr) == -1) { 2027c478bd9Sstevel@tonic-gate mdb_warn("couldn't read kmem_cache at %p", caddr); 2037c478bd9Sstevel@tonic-gate return (WALK_ERR); 2047c478bd9Sstevel@tonic-gate } 2057c478bd9Sstevel@tonic-gate 2067c478bd9Sstevel@tonic-gate wsp->walk_data = 2077c478bd9Sstevel@tonic-gate (void *)(caddr + offsetof(kmem_cache_t, cache_nullslab)); 2087c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_next; 2097c478bd9Sstevel@tonic-gate 2107c478bd9Sstevel@tonic-gate return (WALK_NEXT); 2117c478bd9Sstevel@tonic-gate } 2127c478bd9Sstevel@tonic-gate 2137c478bd9Sstevel@tonic-gate int 2147c478bd9Sstevel@tonic-gate kmem_slab_walk_partial_init(mdb_walk_state_t *wsp) 2157c478bd9Sstevel@tonic-gate { 2167c478bd9Sstevel@tonic-gate uintptr_t caddr = wsp->walk_addr; 2177c478bd9Sstevel@tonic-gate kmem_cache_t c; 2187c478bd9Sstevel@tonic-gate 2197c478bd9Sstevel@tonic-gate if (caddr == NULL) { 2207c478bd9Sstevel@tonic-gate mdb_warn("kmem_slab_partial doesn't support global walks\n"); 2217c478bd9Sstevel@tonic-gate return (WALK_ERR); 2227c478bd9Sstevel@tonic-gate } 2237c478bd9Sstevel@tonic-gate 2247c478bd9Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), caddr) == -1) { 2257c478bd9Sstevel@tonic-gate mdb_warn("couldn't read kmem_cache at %p", caddr); 2267c478bd9Sstevel@tonic-gate return (WALK_ERR); 2277c478bd9Sstevel@tonic-gate } 2287c478bd9Sstevel@tonic-gate 2297c478bd9Sstevel@tonic-gate wsp->walk_data = 2307c478bd9Sstevel@tonic-gate (void *)(caddr + offsetof(kmem_cache_t, cache_nullslab)); 2317c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)c.cache_freelist; 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate /* 2347c478bd9Sstevel@tonic-gate * Some consumers (umem_walk_step(), in particular) require at 2357c478bd9Sstevel@tonic-gate * least one callback if there are any buffers in the cache. So 2367c478bd9Sstevel@tonic-gate * if there are *no* partial slabs, report the last full slab, if 2377c478bd9Sstevel@tonic-gate * any. 2387c478bd9Sstevel@tonic-gate * 2397c478bd9Sstevel@tonic-gate * Yes, this is ugly, but it's cleaner than the other possibilities. 2407c478bd9Sstevel@tonic-gate */ 2417c478bd9Sstevel@tonic-gate if ((uintptr_t)wsp->walk_data == wsp->walk_addr) 2427c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)c.cache_nullslab.slab_prev; 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate return (WALK_NEXT); 2457c478bd9Sstevel@tonic-gate } 2467c478bd9Sstevel@tonic-gate 2477c478bd9Sstevel@tonic-gate int 2487c478bd9Sstevel@tonic-gate kmem_slab_walk_step(mdb_walk_state_t *wsp) 2497c478bd9Sstevel@tonic-gate { 2507c478bd9Sstevel@tonic-gate kmem_slab_t s; 2517c478bd9Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 2527c478bd9Sstevel@tonic-gate uintptr_t saddr = (uintptr_t)wsp->walk_data; 2537c478bd9Sstevel@tonic-gate uintptr_t caddr = saddr - offsetof(kmem_cache_t, cache_nullslab); 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate if (addr == saddr) 2567c478bd9Sstevel@tonic-gate return (WALK_DONE); 2577c478bd9Sstevel@tonic-gate 2587c478bd9Sstevel@tonic-gate if (mdb_vread(&s, sizeof (s), addr) == -1) { 2597c478bd9Sstevel@tonic-gate mdb_warn("failed to read slab at %p", wsp->walk_addr); 2607c478bd9Sstevel@tonic-gate return (WALK_ERR); 2617c478bd9Sstevel@tonic-gate } 2627c478bd9Sstevel@tonic-gate 2637c478bd9Sstevel@tonic-gate if ((uintptr_t)s.slab_cache != caddr) { 2647c478bd9Sstevel@tonic-gate mdb_warn("slab %p isn't in cache %p (in cache %p)\n", 2657c478bd9Sstevel@tonic-gate addr, caddr, s.slab_cache); 2667c478bd9Sstevel@tonic-gate return (WALK_ERR); 2677c478bd9Sstevel@tonic-gate } 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)s.slab_next; 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate return (wsp->walk_callback(addr, &s, wsp->walk_cbdata)); 2727c478bd9Sstevel@tonic-gate } 2737c478bd9Sstevel@tonic-gate 2747c478bd9Sstevel@tonic-gate int 2757c478bd9Sstevel@tonic-gate kmem_cache(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *argv) 2767c478bd9Sstevel@tonic-gate { 2777c478bd9Sstevel@tonic-gate kmem_cache_t c; 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 2807c478bd9Sstevel@tonic-gate if (mdb_walk_dcmd("kmem_cache", "kmem_cache", ac, argv) == -1) { 2817c478bd9Sstevel@tonic-gate mdb_warn("can't walk kmem_cache"); 2827c478bd9Sstevel@tonic-gate return (DCMD_ERR); 2837c478bd9Sstevel@tonic-gate } 2847c478bd9Sstevel@tonic-gate return (DCMD_OK); 2857c478bd9Sstevel@tonic-gate } 2867c478bd9Sstevel@tonic-gate 2877c478bd9Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) 2887c478bd9Sstevel@tonic-gate mdb_printf("%-?s %-25s %4s %6s %8s %8s\n", "ADDR", "NAME", 2897c478bd9Sstevel@tonic-gate "FLAG", "CFLAG", "BUFSIZE", "BUFTOTL"); 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), addr) == -1) { 2927c478bd9Sstevel@tonic-gate mdb_warn("couldn't read kmem_cache at %p", addr); 2937c478bd9Sstevel@tonic-gate return (DCMD_ERR); 2947c478bd9Sstevel@tonic-gate } 2957c478bd9Sstevel@tonic-gate 2967c478bd9Sstevel@tonic-gate mdb_printf("%0?p %-25s %04x %06x %8ld %8lld\n", addr, c.cache_name, 2977c478bd9Sstevel@tonic-gate c.cache_flags, c.cache_cflags, c.cache_bufsize, c.cache_buftotal); 2987c478bd9Sstevel@tonic-gate 2997c478bd9Sstevel@tonic-gate return (DCMD_OK); 3007c478bd9Sstevel@tonic-gate } 3017c478bd9Sstevel@tonic-gate 3027c478bd9Sstevel@tonic-gate static int 3037c478bd9Sstevel@tonic-gate addrcmp(const void *lhs, const void *rhs) 3047c478bd9Sstevel@tonic-gate { 3057c478bd9Sstevel@tonic-gate uintptr_t p1 = *((uintptr_t *)lhs); 3067c478bd9Sstevel@tonic-gate uintptr_t p2 = *((uintptr_t *)rhs); 3077c478bd9Sstevel@tonic-gate 3087c478bd9Sstevel@tonic-gate if (p1 < p2) 3097c478bd9Sstevel@tonic-gate return (-1); 3107c478bd9Sstevel@tonic-gate if (p1 > p2) 3117c478bd9Sstevel@tonic-gate return (1); 3127c478bd9Sstevel@tonic-gate return (0); 3137c478bd9Sstevel@tonic-gate } 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate static int 3167c478bd9Sstevel@tonic-gate bufctlcmp(const kmem_bufctl_audit_t **lhs, const kmem_bufctl_audit_t **rhs) 3177c478bd9Sstevel@tonic-gate { 3187c478bd9Sstevel@tonic-gate const kmem_bufctl_audit_t *bcp1 = *lhs; 3197c478bd9Sstevel@tonic-gate const kmem_bufctl_audit_t *bcp2 = *rhs; 3207c478bd9Sstevel@tonic-gate 3217c478bd9Sstevel@tonic-gate if (bcp1->bc_timestamp > bcp2->bc_timestamp) 3227c478bd9Sstevel@tonic-gate return (-1); 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate if (bcp1->bc_timestamp < bcp2->bc_timestamp) 3257c478bd9Sstevel@tonic-gate return (1); 3267c478bd9Sstevel@tonic-gate 3277c478bd9Sstevel@tonic-gate return (0); 3287c478bd9Sstevel@tonic-gate } 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate typedef struct kmem_hash_walk { 3317c478bd9Sstevel@tonic-gate uintptr_t *kmhw_table; 3327c478bd9Sstevel@tonic-gate size_t kmhw_nelems; 3337c478bd9Sstevel@tonic-gate size_t kmhw_pos; 3347c478bd9Sstevel@tonic-gate kmem_bufctl_t kmhw_cur; 3357c478bd9Sstevel@tonic-gate } kmem_hash_walk_t; 3367c478bd9Sstevel@tonic-gate 3377c478bd9Sstevel@tonic-gate int 3387c478bd9Sstevel@tonic-gate kmem_hash_walk_init(mdb_walk_state_t *wsp) 3397c478bd9Sstevel@tonic-gate { 3407c478bd9Sstevel@tonic-gate kmem_hash_walk_t *kmhw; 3417c478bd9Sstevel@tonic-gate uintptr_t *hash; 3427c478bd9Sstevel@tonic-gate kmem_cache_t c; 3437c478bd9Sstevel@tonic-gate uintptr_t haddr, addr = wsp->walk_addr; 3447c478bd9Sstevel@tonic-gate size_t nelems; 3457c478bd9Sstevel@tonic-gate size_t hsize; 3467c478bd9Sstevel@tonic-gate 3477c478bd9Sstevel@tonic-gate if (addr == NULL) { 3487c478bd9Sstevel@tonic-gate mdb_warn("kmem_hash doesn't support global walks\n"); 3497c478bd9Sstevel@tonic-gate return (WALK_ERR); 3507c478bd9Sstevel@tonic-gate } 3517c478bd9Sstevel@tonic-gate 3527c478bd9Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), addr) == -1) { 3537c478bd9Sstevel@tonic-gate mdb_warn("couldn't read cache at addr %p", addr); 3547c478bd9Sstevel@tonic-gate return (WALK_ERR); 3557c478bd9Sstevel@tonic-gate } 3567c478bd9Sstevel@tonic-gate 3577c478bd9Sstevel@tonic-gate if (!(c.cache_flags & KMF_HASH)) { 3587c478bd9Sstevel@tonic-gate mdb_warn("cache %p doesn't have a hash table\n", addr); 3597c478bd9Sstevel@tonic-gate return (WALK_DONE); /* nothing to do */ 3607c478bd9Sstevel@tonic-gate } 3617c478bd9Sstevel@tonic-gate 3627c478bd9Sstevel@tonic-gate kmhw = mdb_zalloc(sizeof (kmem_hash_walk_t), UM_SLEEP); 3637c478bd9Sstevel@tonic-gate kmhw->kmhw_cur.bc_next = NULL; 3647c478bd9Sstevel@tonic-gate kmhw->kmhw_pos = 0; 3657c478bd9Sstevel@tonic-gate 3667c478bd9Sstevel@tonic-gate kmhw->kmhw_nelems = nelems = c.cache_hash_mask + 1; 3677c478bd9Sstevel@tonic-gate hsize = nelems * sizeof (uintptr_t); 3687c478bd9Sstevel@tonic-gate haddr = (uintptr_t)c.cache_hash_table; 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate kmhw->kmhw_table = hash = mdb_alloc(hsize, UM_SLEEP); 3717c478bd9Sstevel@tonic-gate if (mdb_vread(hash, hsize, haddr) == -1) { 3727c478bd9Sstevel@tonic-gate mdb_warn("failed to read hash table at %p", haddr); 3737c478bd9Sstevel@tonic-gate mdb_free(hash, hsize); 3747c478bd9Sstevel@tonic-gate mdb_free(kmhw, sizeof (kmem_hash_walk_t)); 3757c478bd9Sstevel@tonic-gate return (WALK_ERR); 3767c478bd9Sstevel@tonic-gate } 3777c478bd9Sstevel@tonic-gate 3787c478bd9Sstevel@tonic-gate wsp->walk_data = kmhw; 3797c478bd9Sstevel@tonic-gate 3807c478bd9Sstevel@tonic-gate return (WALK_NEXT); 3817c478bd9Sstevel@tonic-gate } 3827c478bd9Sstevel@tonic-gate 3837c478bd9Sstevel@tonic-gate int 3847c478bd9Sstevel@tonic-gate kmem_hash_walk_step(mdb_walk_state_t *wsp) 3857c478bd9Sstevel@tonic-gate { 3867c478bd9Sstevel@tonic-gate kmem_hash_walk_t *kmhw = wsp->walk_data; 3877c478bd9Sstevel@tonic-gate uintptr_t addr = NULL; 3887c478bd9Sstevel@tonic-gate 3897c478bd9Sstevel@tonic-gate if ((addr = (uintptr_t)kmhw->kmhw_cur.bc_next) == NULL) { 3907c478bd9Sstevel@tonic-gate while (kmhw->kmhw_pos < kmhw->kmhw_nelems) { 3917c478bd9Sstevel@tonic-gate if ((addr = kmhw->kmhw_table[kmhw->kmhw_pos++]) != NULL) 3927c478bd9Sstevel@tonic-gate break; 3937c478bd9Sstevel@tonic-gate } 3947c478bd9Sstevel@tonic-gate } 3957c478bd9Sstevel@tonic-gate if (addr == NULL) 3967c478bd9Sstevel@tonic-gate return (WALK_DONE); 3977c478bd9Sstevel@tonic-gate 3987c478bd9Sstevel@tonic-gate if (mdb_vread(&kmhw->kmhw_cur, sizeof (kmem_bufctl_t), addr) == -1) { 3997c478bd9Sstevel@tonic-gate mdb_warn("couldn't read kmem_bufctl_t at addr %p", addr); 4007c478bd9Sstevel@tonic-gate return (WALK_ERR); 4017c478bd9Sstevel@tonic-gate } 4027c478bd9Sstevel@tonic-gate 4037c478bd9Sstevel@tonic-gate return (wsp->walk_callback(addr, &kmhw->kmhw_cur, wsp->walk_cbdata)); 4047c478bd9Sstevel@tonic-gate } 4057c478bd9Sstevel@tonic-gate 4067c478bd9Sstevel@tonic-gate void 4077c478bd9Sstevel@tonic-gate kmem_hash_walk_fini(mdb_walk_state_t *wsp) 4087c478bd9Sstevel@tonic-gate { 4097c478bd9Sstevel@tonic-gate kmem_hash_walk_t *kmhw = wsp->walk_data; 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate if (kmhw == NULL) 4127c478bd9Sstevel@tonic-gate return; 4137c478bd9Sstevel@tonic-gate 4147c478bd9Sstevel@tonic-gate mdb_free(kmhw->kmhw_table, kmhw->kmhw_nelems * sizeof (uintptr_t)); 4157c478bd9Sstevel@tonic-gate mdb_free(kmhw, sizeof (kmem_hash_walk_t)); 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate 4187c478bd9Sstevel@tonic-gate /* 4197c478bd9Sstevel@tonic-gate * Find the address of the bufctl structure for the address 'buf' in cache 4207c478bd9Sstevel@tonic-gate * 'cp', which is at address caddr, and place it in *out. 4217c478bd9Sstevel@tonic-gate */ 4227c478bd9Sstevel@tonic-gate static int 4237c478bd9Sstevel@tonic-gate kmem_hash_lookup(kmem_cache_t *cp, uintptr_t caddr, void *buf, uintptr_t *out) 4247c478bd9Sstevel@tonic-gate { 4257c478bd9Sstevel@tonic-gate uintptr_t bucket = (uintptr_t)KMEM_HASH(cp, buf); 4267c478bd9Sstevel@tonic-gate kmem_bufctl_t *bcp; 4277c478bd9Sstevel@tonic-gate kmem_bufctl_t bc; 4287c478bd9Sstevel@tonic-gate 4297c478bd9Sstevel@tonic-gate if (mdb_vread(&bcp, sizeof (kmem_bufctl_t *), bucket) == -1) { 4307c478bd9Sstevel@tonic-gate mdb_warn("unable to read hash bucket for %p in cache %p", 4317c478bd9Sstevel@tonic-gate buf, caddr); 4327c478bd9Sstevel@tonic-gate return (-1); 4337c478bd9Sstevel@tonic-gate } 4347c478bd9Sstevel@tonic-gate 4357c478bd9Sstevel@tonic-gate while (bcp != NULL) { 4367c478bd9Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (kmem_bufctl_t), 4377c478bd9Sstevel@tonic-gate (uintptr_t)bcp) == -1) { 4387c478bd9Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", bcp); 4397c478bd9Sstevel@tonic-gate return (-1); 4407c478bd9Sstevel@tonic-gate } 4417c478bd9Sstevel@tonic-gate if (bc.bc_addr == buf) { 4427c478bd9Sstevel@tonic-gate *out = (uintptr_t)bcp; 4437c478bd9Sstevel@tonic-gate return (0); 4447c478bd9Sstevel@tonic-gate } 4457c478bd9Sstevel@tonic-gate bcp = bc.bc_next; 4467c478bd9Sstevel@tonic-gate } 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate mdb_warn("unable to find bufctl for %p in cache %p\n", buf, caddr); 4497c478bd9Sstevel@tonic-gate return (-1); 4507c478bd9Sstevel@tonic-gate } 4517c478bd9Sstevel@tonic-gate 4527c478bd9Sstevel@tonic-gate int 4537c478bd9Sstevel@tonic-gate kmem_get_magsize(const kmem_cache_t *cp) 4547c478bd9Sstevel@tonic-gate { 4557c478bd9Sstevel@tonic-gate uintptr_t addr = (uintptr_t)cp->cache_magtype; 4567c478bd9Sstevel@tonic-gate GElf_Sym mt_sym; 4577c478bd9Sstevel@tonic-gate kmem_magtype_t mt; 4587c478bd9Sstevel@tonic-gate int res; 4597c478bd9Sstevel@tonic-gate 4607c478bd9Sstevel@tonic-gate /* 4617c478bd9Sstevel@tonic-gate * if cpu 0 has a non-zero magsize, it must be correct. caches 4627c478bd9Sstevel@tonic-gate * with KMF_NOMAGAZINE have disabled their magazine layers, so 4637c478bd9Sstevel@tonic-gate * it is okay to return 0 for them. 4647c478bd9Sstevel@tonic-gate */ 4657c478bd9Sstevel@tonic-gate if ((res = cp->cache_cpu[0].cc_magsize) != 0 || 4667c478bd9Sstevel@tonic-gate (cp->cache_flags & KMF_NOMAGAZINE)) 4677c478bd9Sstevel@tonic-gate return (res); 4687c478bd9Sstevel@tonic-gate 4697c478bd9Sstevel@tonic-gate if (mdb_lookup_by_name("kmem_magtype", &mt_sym) == -1) { 4707c478bd9Sstevel@tonic-gate mdb_warn("unable to read 'kmem_magtype'"); 4717c478bd9Sstevel@tonic-gate } else if (addr < mt_sym.st_value || 4727c478bd9Sstevel@tonic-gate addr + sizeof (mt) - 1 > mt_sym.st_value + mt_sym.st_size - 1 || 4737c478bd9Sstevel@tonic-gate ((addr - mt_sym.st_value) % sizeof (mt)) != 0) { 4747c478bd9Sstevel@tonic-gate mdb_warn("cache '%s' has invalid magtype pointer (%p)\n", 4757c478bd9Sstevel@tonic-gate cp->cache_name, addr); 4767c478bd9Sstevel@tonic-gate return (0); 4777c478bd9Sstevel@tonic-gate } 4787c478bd9Sstevel@tonic-gate if (mdb_vread(&mt, sizeof (mt), addr) == -1) { 4797c478bd9Sstevel@tonic-gate mdb_warn("unable to read magtype at %a", addr); 4807c478bd9Sstevel@tonic-gate return (0); 4817c478bd9Sstevel@tonic-gate } 4827c478bd9Sstevel@tonic-gate return (mt.mt_magsize); 4837c478bd9Sstevel@tonic-gate } 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 4867c478bd9Sstevel@tonic-gate static int 4877c478bd9Sstevel@tonic-gate kmem_estimate_slab(uintptr_t addr, const kmem_slab_t *sp, size_t *est) 4887c478bd9Sstevel@tonic-gate { 4897c478bd9Sstevel@tonic-gate *est -= (sp->slab_chunks - sp->slab_refcnt); 4907c478bd9Sstevel@tonic-gate 4917c478bd9Sstevel@tonic-gate return (WALK_NEXT); 4927c478bd9Sstevel@tonic-gate } 4937c478bd9Sstevel@tonic-gate 4947c478bd9Sstevel@tonic-gate /* 4957c478bd9Sstevel@tonic-gate * Returns an upper bound on the number of allocated buffers in a given 4967c478bd9Sstevel@tonic-gate * cache. 4977c478bd9Sstevel@tonic-gate */ 4987c478bd9Sstevel@tonic-gate size_t 4997c478bd9Sstevel@tonic-gate kmem_estimate_allocated(uintptr_t addr, const kmem_cache_t *cp) 5007c478bd9Sstevel@tonic-gate { 5017c478bd9Sstevel@tonic-gate int magsize; 5027c478bd9Sstevel@tonic-gate size_t cache_est; 5037c478bd9Sstevel@tonic-gate 5047c478bd9Sstevel@tonic-gate cache_est = cp->cache_buftotal; 5057c478bd9Sstevel@tonic-gate 5067c478bd9Sstevel@tonic-gate (void) mdb_pwalk("kmem_slab_partial", 5077c478bd9Sstevel@tonic-gate (mdb_walk_cb_t)kmem_estimate_slab, &cache_est, addr); 5087c478bd9Sstevel@tonic-gate 5097c478bd9Sstevel@tonic-gate if ((magsize = kmem_get_magsize(cp)) != 0) { 5107c478bd9Sstevel@tonic-gate size_t mag_est = cp->cache_full.ml_total * magsize; 5117c478bd9Sstevel@tonic-gate 5127c478bd9Sstevel@tonic-gate if (cache_est >= mag_est) { 5137c478bd9Sstevel@tonic-gate cache_est -= mag_est; 5147c478bd9Sstevel@tonic-gate } else { 5157c478bd9Sstevel@tonic-gate mdb_warn("cache %p's magazine layer holds more buffers " 5167c478bd9Sstevel@tonic-gate "than the slab layer.\n", addr); 5177c478bd9Sstevel@tonic-gate } 5187c478bd9Sstevel@tonic-gate } 5197c478bd9Sstevel@tonic-gate return (cache_est); 5207c478bd9Sstevel@tonic-gate } 5217c478bd9Sstevel@tonic-gate 5227c478bd9Sstevel@tonic-gate #define READMAG_ROUNDS(rounds) { \ 5237c478bd9Sstevel@tonic-gate if (mdb_vread(mp, magbsize, (uintptr_t)kmp) == -1) { \ 5247c478bd9Sstevel@tonic-gate mdb_warn("couldn't read magazine at %p", kmp); \ 5257c478bd9Sstevel@tonic-gate goto fail; \ 5267c478bd9Sstevel@tonic-gate } \ 5277c478bd9Sstevel@tonic-gate for (i = 0; i < rounds; i++) { \ 5287c478bd9Sstevel@tonic-gate maglist[magcnt++] = mp->mag_round[i]; \ 5297c478bd9Sstevel@tonic-gate if (magcnt == magmax) { \ 5307c478bd9Sstevel@tonic-gate mdb_warn("%d magazines exceeds fudge factor\n", \ 5317c478bd9Sstevel@tonic-gate magcnt); \ 5327c478bd9Sstevel@tonic-gate goto fail; \ 5337c478bd9Sstevel@tonic-gate } \ 5347c478bd9Sstevel@tonic-gate } \ 5357c478bd9Sstevel@tonic-gate } 5367c478bd9Sstevel@tonic-gate 5377c478bd9Sstevel@tonic-gate int 5387c478bd9Sstevel@tonic-gate kmem_read_magazines(kmem_cache_t *cp, uintptr_t addr, int ncpus, 5397c478bd9Sstevel@tonic-gate void ***maglistp, size_t *magcntp, size_t *magmaxp, int alloc_flags) 5407c478bd9Sstevel@tonic-gate { 5417c478bd9Sstevel@tonic-gate kmem_magazine_t *kmp, *mp; 5427c478bd9Sstevel@tonic-gate void **maglist = NULL; 5437c478bd9Sstevel@tonic-gate int i, cpu; 5447c478bd9Sstevel@tonic-gate size_t magsize, magmax, magbsize; 5457c478bd9Sstevel@tonic-gate size_t magcnt = 0; 5467c478bd9Sstevel@tonic-gate 5477c478bd9Sstevel@tonic-gate /* 5487c478bd9Sstevel@tonic-gate * Read the magtype out of the cache, after verifying the pointer's 5497c478bd9Sstevel@tonic-gate * correctness. 5507c478bd9Sstevel@tonic-gate */ 5517c478bd9Sstevel@tonic-gate magsize = kmem_get_magsize(cp); 552*789d94c2Sjwadams if (magsize == 0) { 553*789d94c2Sjwadams *maglistp = NULL; 554*789d94c2Sjwadams *magcntp = 0; 555*789d94c2Sjwadams *magmaxp = 0; 556*789d94c2Sjwadams return (WALK_NEXT); 557*789d94c2Sjwadams } 5587c478bd9Sstevel@tonic-gate 5597c478bd9Sstevel@tonic-gate /* 5607c478bd9Sstevel@tonic-gate * There are several places where we need to go buffer hunting: 5617c478bd9Sstevel@tonic-gate * the per-CPU loaded magazine, the per-CPU spare full magazine, 5627c478bd9Sstevel@tonic-gate * and the full magazine list in the depot. 5637c478bd9Sstevel@tonic-gate * 5647c478bd9Sstevel@tonic-gate * For an upper bound on the number of buffers in the magazine 5657c478bd9Sstevel@tonic-gate * layer, we have the number of magazines on the cache_full 5667c478bd9Sstevel@tonic-gate * list plus at most two magazines per CPU (the loaded and the 5677c478bd9Sstevel@tonic-gate * spare). Toss in 100 magazines as a fudge factor in case this 5687c478bd9Sstevel@tonic-gate * is live (the number "100" comes from the same fudge factor in 5697c478bd9Sstevel@tonic-gate * crash(1M)). 5707c478bd9Sstevel@tonic-gate */ 5717c478bd9Sstevel@tonic-gate magmax = (cp->cache_full.ml_total + 2 * ncpus + 100) * magsize; 5727c478bd9Sstevel@tonic-gate magbsize = offsetof(kmem_magazine_t, mag_round[magsize]); 5737c478bd9Sstevel@tonic-gate 5747c478bd9Sstevel@tonic-gate if (magbsize >= PAGESIZE / 2) { 5757c478bd9Sstevel@tonic-gate mdb_warn("magazine size for cache %p unreasonable (%x)\n", 5767c478bd9Sstevel@tonic-gate addr, magbsize); 577*789d94c2Sjwadams return (WALK_ERR); 5787c478bd9Sstevel@tonic-gate } 5797c478bd9Sstevel@tonic-gate 5807c478bd9Sstevel@tonic-gate maglist = mdb_alloc(magmax * sizeof (void *), alloc_flags); 5817c478bd9Sstevel@tonic-gate mp = mdb_alloc(magbsize, alloc_flags); 5827c478bd9Sstevel@tonic-gate if (mp == NULL || maglist == NULL) 5837c478bd9Sstevel@tonic-gate goto fail; 5847c478bd9Sstevel@tonic-gate 5857c478bd9Sstevel@tonic-gate /* 5867c478bd9Sstevel@tonic-gate * First up: the magazines in the depot (i.e. on the cache_full list). 5877c478bd9Sstevel@tonic-gate */ 5887c478bd9Sstevel@tonic-gate for (kmp = cp->cache_full.ml_list; kmp != NULL; ) { 5897c478bd9Sstevel@tonic-gate READMAG_ROUNDS(magsize); 5907c478bd9Sstevel@tonic-gate kmp = mp->mag_next; 5917c478bd9Sstevel@tonic-gate 5927c478bd9Sstevel@tonic-gate if (kmp == cp->cache_full.ml_list) 5937c478bd9Sstevel@tonic-gate break; /* cache_full list loop detected */ 5947c478bd9Sstevel@tonic-gate } 5957c478bd9Sstevel@tonic-gate 5967c478bd9Sstevel@tonic-gate dprintf(("cache_full list done\n")); 5977c478bd9Sstevel@tonic-gate 5987c478bd9Sstevel@tonic-gate /* 5997c478bd9Sstevel@tonic-gate * Now whip through the CPUs, snagging the loaded magazines 6007c478bd9Sstevel@tonic-gate * and full spares. 6017c478bd9Sstevel@tonic-gate */ 6027c478bd9Sstevel@tonic-gate for (cpu = 0; cpu < ncpus; cpu++) { 6037c478bd9Sstevel@tonic-gate kmem_cpu_cache_t *ccp = &cp->cache_cpu[cpu]; 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate dprintf(("reading cpu cache %p\n", 6067c478bd9Sstevel@tonic-gate (uintptr_t)ccp - (uintptr_t)cp + addr)); 6077c478bd9Sstevel@tonic-gate 6087c478bd9Sstevel@tonic-gate if (ccp->cc_rounds > 0 && 6097c478bd9Sstevel@tonic-gate (kmp = ccp->cc_loaded) != NULL) { 6107c478bd9Sstevel@tonic-gate dprintf(("reading %d loaded rounds\n", ccp->cc_rounds)); 6117c478bd9Sstevel@tonic-gate READMAG_ROUNDS(ccp->cc_rounds); 6127c478bd9Sstevel@tonic-gate } 6137c478bd9Sstevel@tonic-gate 6147c478bd9Sstevel@tonic-gate if (ccp->cc_prounds > 0 && 6157c478bd9Sstevel@tonic-gate (kmp = ccp->cc_ploaded) != NULL) { 6167c478bd9Sstevel@tonic-gate dprintf(("reading %d previously loaded rounds\n", 6177c478bd9Sstevel@tonic-gate ccp->cc_prounds)); 6187c478bd9Sstevel@tonic-gate READMAG_ROUNDS(ccp->cc_prounds); 6197c478bd9Sstevel@tonic-gate } 6207c478bd9Sstevel@tonic-gate } 6217c478bd9Sstevel@tonic-gate 6227c478bd9Sstevel@tonic-gate dprintf(("magazine layer: %d buffers\n", magcnt)); 6237c478bd9Sstevel@tonic-gate 6247c478bd9Sstevel@tonic-gate if (!(alloc_flags & UM_GC)) 6257c478bd9Sstevel@tonic-gate mdb_free(mp, magbsize); 6267c478bd9Sstevel@tonic-gate 6277c478bd9Sstevel@tonic-gate *maglistp = maglist; 6287c478bd9Sstevel@tonic-gate *magcntp = magcnt; 6297c478bd9Sstevel@tonic-gate *magmaxp = magmax; 6307c478bd9Sstevel@tonic-gate 6317c478bd9Sstevel@tonic-gate return (WALK_NEXT); 6327c478bd9Sstevel@tonic-gate 6337c478bd9Sstevel@tonic-gate fail: 6347c478bd9Sstevel@tonic-gate if (!(alloc_flags & UM_GC)) { 6357c478bd9Sstevel@tonic-gate if (mp) 6367c478bd9Sstevel@tonic-gate mdb_free(mp, magbsize); 6377c478bd9Sstevel@tonic-gate if (maglist) 6387c478bd9Sstevel@tonic-gate mdb_free(maglist, magmax * sizeof (void *)); 6397c478bd9Sstevel@tonic-gate } 6407c478bd9Sstevel@tonic-gate return (WALK_ERR); 6417c478bd9Sstevel@tonic-gate } 6427c478bd9Sstevel@tonic-gate 6437c478bd9Sstevel@tonic-gate static int 6447c478bd9Sstevel@tonic-gate kmem_walk_callback(mdb_walk_state_t *wsp, uintptr_t buf) 6457c478bd9Sstevel@tonic-gate { 6467c478bd9Sstevel@tonic-gate return (wsp->walk_callback(buf, NULL, wsp->walk_cbdata)); 6477c478bd9Sstevel@tonic-gate } 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate static int 6507c478bd9Sstevel@tonic-gate bufctl_walk_callback(kmem_cache_t *cp, mdb_walk_state_t *wsp, uintptr_t buf) 6517c478bd9Sstevel@tonic-gate { 6527c478bd9Sstevel@tonic-gate kmem_bufctl_audit_t b; 6537c478bd9Sstevel@tonic-gate 6547c478bd9Sstevel@tonic-gate /* 6557c478bd9Sstevel@tonic-gate * if KMF_AUDIT is not set, we know that we're looking at a 6567c478bd9Sstevel@tonic-gate * kmem_bufctl_t. 6577c478bd9Sstevel@tonic-gate */ 6587c478bd9Sstevel@tonic-gate if (!(cp->cache_flags & KMF_AUDIT) || 6597c478bd9Sstevel@tonic-gate mdb_vread(&b, sizeof (kmem_bufctl_audit_t), buf) == -1) { 6607c478bd9Sstevel@tonic-gate (void) memset(&b, 0, sizeof (b)); 6617c478bd9Sstevel@tonic-gate if (mdb_vread(&b, sizeof (kmem_bufctl_t), buf) == -1) { 6627c478bd9Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", buf); 6637c478bd9Sstevel@tonic-gate return (WALK_ERR); 6647c478bd9Sstevel@tonic-gate } 6657c478bd9Sstevel@tonic-gate } 6667c478bd9Sstevel@tonic-gate 6677c478bd9Sstevel@tonic-gate return (wsp->walk_callback(buf, &b, wsp->walk_cbdata)); 6687c478bd9Sstevel@tonic-gate } 6697c478bd9Sstevel@tonic-gate 6707c478bd9Sstevel@tonic-gate typedef struct kmem_walk { 6717c478bd9Sstevel@tonic-gate int kmw_type; 6727c478bd9Sstevel@tonic-gate 6737c478bd9Sstevel@tonic-gate int kmw_addr; /* cache address */ 6747c478bd9Sstevel@tonic-gate kmem_cache_t *kmw_cp; 6757c478bd9Sstevel@tonic-gate size_t kmw_csize; 6767c478bd9Sstevel@tonic-gate 6777c478bd9Sstevel@tonic-gate /* 6787c478bd9Sstevel@tonic-gate * magazine layer 6797c478bd9Sstevel@tonic-gate */ 6807c478bd9Sstevel@tonic-gate void **kmw_maglist; 6817c478bd9Sstevel@tonic-gate size_t kmw_max; 6827c478bd9Sstevel@tonic-gate size_t kmw_count; 6837c478bd9Sstevel@tonic-gate size_t kmw_pos; 6847c478bd9Sstevel@tonic-gate 6857c478bd9Sstevel@tonic-gate /* 6867c478bd9Sstevel@tonic-gate * slab layer 6877c478bd9Sstevel@tonic-gate */ 6887c478bd9Sstevel@tonic-gate char *kmw_valid; /* to keep track of freed buffers */ 6897c478bd9Sstevel@tonic-gate char *kmw_ubase; /* buffer for slab data */ 6907c478bd9Sstevel@tonic-gate } kmem_walk_t; 6917c478bd9Sstevel@tonic-gate 6927c478bd9Sstevel@tonic-gate static int 6937c478bd9Sstevel@tonic-gate kmem_walk_init_common(mdb_walk_state_t *wsp, int type) 6947c478bd9Sstevel@tonic-gate { 6957c478bd9Sstevel@tonic-gate kmem_walk_t *kmw; 6967c478bd9Sstevel@tonic-gate int ncpus, csize; 6977c478bd9Sstevel@tonic-gate kmem_cache_t *cp; 698*789d94c2Sjwadams size_t vm_quantum; 6997c478bd9Sstevel@tonic-gate 7007c478bd9Sstevel@tonic-gate size_t magmax, magcnt; 7017c478bd9Sstevel@tonic-gate void **maglist = NULL; 7027c478bd9Sstevel@tonic-gate uint_t chunksize, slabsize; 7037c478bd9Sstevel@tonic-gate int status = WALK_ERR; 7047c478bd9Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 7057c478bd9Sstevel@tonic-gate const char *layered; 7067c478bd9Sstevel@tonic-gate 7077c478bd9Sstevel@tonic-gate type &= ~KM_HASH; 7087c478bd9Sstevel@tonic-gate 7097c478bd9Sstevel@tonic-gate if (addr == NULL) { 7107c478bd9Sstevel@tonic-gate mdb_warn("kmem walk doesn't support global walks\n"); 7117c478bd9Sstevel@tonic-gate return (WALK_ERR); 7127c478bd9Sstevel@tonic-gate } 7137c478bd9Sstevel@tonic-gate 7147c478bd9Sstevel@tonic-gate dprintf(("walking %p\n", addr)); 7157c478bd9Sstevel@tonic-gate 7167c478bd9Sstevel@tonic-gate /* 7177c478bd9Sstevel@tonic-gate * First we need to figure out how many CPUs are configured in the 7187c478bd9Sstevel@tonic-gate * system to know how much to slurp out. 7197c478bd9Sstevel@tonic-gate */ 7207c478bd9Sstevel@tonic-gate mdb_readvar(&ncpus, "max_ncpus"); 7217c478bd9Sstevel@tonic-gate 7227c478bd9Sstevel@tonic-gate csize = KMEM_CACHE_SIZE(ncpus); 7237c478bd9Sstevel@tonic-gate cp = mdb_alloc(csize, UM_SLEEP); 7247c478bd9Sstevel@tonic-gate 7257c478bd9Sstevel@tonic-gate if (mdb_vread(cp, csize, addr) == -1) { 7267c478bd9Sstevel@tonic-gate mdb_warn("couldn't read cache at addr %p", addr); 7277c478bd9Sstevel@tonic-gate goto out2; 7287c478bd9Sstevel@tonic-gate } 7297c478bd9Sstevel@tonic-gate 730*789d94c2Sjwadams /* 731*789d94c2Sjwadams * It's easy for someone to hand us an invalid cache address. 732*789d94c2Sjwadams * Unfortunately, it is hard for this walker to survive an 733*789d94c2Sjwadams * invalid cache cleanly. So we make sure that: 734*789d94c2Sjwadams * 735*789d94c2Sjwadams * 1. the vmem arena for the cache is readable, 736*789d94c2Sjwadams * 2. the vmem arena's quantum is a power of 2, 737*789d94c2Sjwadams * 3. our slabsize is a multiple of the quantum, and 738*789d94c2Sjwadams * 4. our chunksize is >0 and less than our slabsize. 739*789d94c2Sjwadams */ 740*789d94c2Sjwadams if (mdb_vread(&vm_quantum, sizeof (vm_quantum), 741*789d94c2Sjwadams (uintptr_t)&cp->cache_arena->vm_quantum) == -1 || 742*789d94c2Sjwadams vm_quantum == 0 || 743*789d94c2Sjwadams (vm_quantum & (vm_quantum - 1)) != 0 || 744*789d94c2Sjwadams cp->cache_slabsize < vm_quantum || 745*789d94c2Sjwadams P2PHASE(cp->cache_slabsize, vm_quantum) != 0 || 746*789d94c2Sjwadams cp->cache_chunksize == 0 || 747*789d94c2Sjwadams cp->cache_chunksize > cp->cache_slabsize) { 748*789d94c2Sjwadams mdb_warn("%p is not a valid kmem_cache_t\n", addr); 749*789d94c2Sjwadams goto out2; 750*789d94c2Sjwadams } 751*789d94c2Sjwadams 7527c478bd9Sstevel@tonic-gate dprintf(("buf total is %d\n", cp->cache_buftotal)); 7537c478bd9Sstevel@tonic-gate 7547c478bd9Sstevel@tonic-gate if (cp->cache_buftotal == 0) { 7557c478bd9Sstevel@tonic-gate mdb_free(cp, csize); 7567c478bd9Sstevel@tonic-gate return (WALK_DONE); 7577c478bd9Sstevel@tonic-gate } 7587c478bd9Sstevel@tonic-gate 7597c478bd9Sstevel@tonic-gate /* 7607c478bd9Sstevel@tonic-gate * If they ask for bufctls, but it's a small-slab cache, 7617c478bd9Sstevel@tonic-gate * there is nothing to report. 7627c478bd9Sstevel@tonic-gate */ 7637c478bd9Sstevel@tonic-gate if ((type & KM_BUFCTL) && !(cp->cache_flags & KMF_HASH)) { 7647c478bd9Sstevel@tonic-gate dprintf(("bufctl requested, not KMF_HASH (flags: %p)\n", 7657c478bd9Sstevel@tonic-gate cp->cache_flags)); 7667c478bd9Sstevel@tonic-gate mdb_free(cp, csize); 7677c478bd9Sstevel@tonic-gate return (WALK_DONE); 7687c478bd9Sstevel@tonic-gate } 7697c478bd9Sstevel@tonic-gate 7707c478bd9Sstevel@tonic-gate /* 7717c478bd9Sstevel@tonic-gate * If they want constructed buffers, but there's no constructor or 7727c478bd9Sstevel@tonic-gate * the cache has DEADBEEF checking enabled, there is nothing to report. 7737c478bd9Sstevel@tonic-gate */ 7747c478bd9Sstevel@tonic-gate if ((type & KM_CONSTRUCTED) && (!(type & KM_FREE) || 7757c478bd9Sstevel@tonic-gate cp->cache_constructor == NULL || 7767c478bd9Sstevel@tonic-gate (cp->cache_flags & (KMF_DEADBEEF | KMF_LITE)) == KMF_DEADBEEF)) { 7777c478bd9Sstevel@tonic-gate mdb_free(cp, csize); 7787c478bd9Sstevel@tonic-gate return (WALK_DONE); 7797c478bd9Sstevel@tonic-gate } 7807c478bd9Sstevel@tonic-gate 7817c478bd9Sstevel@tonic-gate /* 7827c478bd9Sstevel@tonic-gate * Read in the contents of the magazine layer 7837c478bd9Sstevel@tonic-gate */ 7847c478bd9Sstevel@tonic-gate if (kmem_read_magazines(cp, addr, ncpus, &maglist, &magcnt, 7857c478bd9Sstevel@tonic-gate &magmax, UM_SLEEP) == WALK_ERR) 7867c478bd9Sstevel@tonic-gate goto out2; 7877c478bd9Sstevel@tonic-gate 7887c478bd9Sstevel@tonic-gate /* 7897c478bd9Sstevel@tonic-gate * We have all of the buffers from the magazines; if we are walking 7907c478bd9Sstevel@tonic-gate * allocated buffers, sort them so we can bsearch them later. 7917c478bd9Sstevel@tonic-gate */ 7927c478bd9Sstevel@tonic-gate if (type & KM_ALLOCATED) 7937c478bd9Sstevel@tonic-gate qsort(maglist, magcnt, sizeof (void *), addrcmp); 7947c478bd9Sstevel@tonic-gate 7957c478bd9Sstevel@tonic-gate wsp->walk_data = kmw = mdb_zalloc(sizeof (kmem_walk_t), UM_SLEEP); 7967c478bd9Sstevel@tonic-gate 7977c478bd9Sstevel@tonic-gate kmw->kmw_type = type; 7987c478bd9Sstevel@tonic-gate kmw->kmw_addr = addr; 7997c478bd9Sstevel@tonic-gate kmw->kmw_cp = cp; 8007c478bd9Sstevel@tonic-gate kmw->kmw_csize = csize; 8017c478bd9Sstevel@tonic-gate kmw->kmw_maglist = maglist; 8027c478bd9Sstevel@tonic-gate kmw->kmw_max = magmax; 8037c478bd9Sstevel@tonic-gate kmw->kmw_count = magcnt; 8047c478bd9Sstevel@tonic-gate kmw->kmw_pos = 0; 8057c478bd9Sstevel@tonic-gate 8067c478bd9Sstevel@tonic-gate /* 8077c478bd9Sstevel@tonic-gate * When walking allocated buffers in a KMF_HASH cache, we walk the 8087c478bd9Sstevel@tonic-gate * hash table instead of the slab layer. 8097c478bd9Sstevel@tonic-gate */ 8107c478bd9Sstevel@tonic-gate if ((cp->cache_flags & KMF_HASH) && (type & KM_ALLOCATED)) { 8117c478bd9Sstevel@tonic-gate layered = "kmem_hash"; 8127c478bd9Sstevel@tonic-gate 8137c478bd9Sstevel@tonic-gate kmw->kmw_type |= KM_HASH; 8147c478bd9Sstevel@tonic-gate } else { 8157c478bd9Sstevel@tonic-gate /* 8167c478bd9Sstevel@tonic-gate * If we are walking freed buffers, we only need the 8177c478bd9Sstevel@tonic-gate * magazine layer plus the partially allocated slabs. 8187c478bd9Sstevel@tonic-gate * To walk allocated buffers, we need all of the slabs. 8197c478bd9Sstevel@tonic-gate */ 8207c478bd9Sstevel@tonic-gate if (type & KM_ALLOCATED) 8217c478bd9Sstevel@tonic-gate layered = "kmem_slab"; 8227c478bd9Sstevel@tonic-gate else 8237c478bd9Sstevel@tonic-gate layered = "kmem_slab_partial"; 8247c478bd9Sstevel@tonic-gate 8257c478bd9Sstevel@tonic-gate /* 8267c478bd9Sstevel@tonic-gate * for small-slab caches, we read in the entire slab. For 8277c478bd9Sstevel@tonic-gate * freed buffers, we can just walk the freelist. For 8287c478bd9Sstevel@tonic-gate * allocated buffers, we use a 'valid' array to track 8297c478bd9Sstevel@tonic-gate * the freed buffers. 8307c478bd9Sstevel@tonic-gate */ 8317c478bd9Sstevel@tonic-gate if (!(cp->cache_flags & KMF_HASH)) { 8327c478bd9Sstevel@tonic-gate chunksize = cp->cache_chunksize; 8337c478bd9Sstevel@tonic-gate slabsize = cp->cache_slabsize; 8347c478bd9Sstevel@tonic-gate 8357c478bd9Sstevel@tonic-gate kmw->kmw_ubase = mdb_alloc(slabsize + 8367c478bd9Sstevel@tonic-gate sizeof (kmem_bufctl_t), UM_SLEEP); 8377c478bd9Sstevel@tonic-gate 8387c478bd9Sstevel@tonic-gate if (type & KM_ALLOCATED) 8397c478bd9Sstevel@tonic-gate kmw->kmw_valid = 8407c478bd9Sstevel@tonic-gate mdb_alloc(slabsize / chunksize, UM_SLEEP); 8417c478bd9Sstevel@tonic-gate } 8427c478bd9Sstevel@tonic-gate } 8437c478bd9Sstevel@tonic-gate 8447c478bd9Sstevel@tonic-gate status = WALK_NEXT; 8457c478bd9Sstevel@tonic-gate 8467c478bd9Sstevel@tonic-gate if (mdb_layered_walk(layered, wsp) == -1) { 8477c478bd9Sstevel@tonic-gate mdb_warn("unable to start layered '%s' walk", layered); 8487c478bd9Sstevel@tonic-gate status = WALK_ERR; 8497c478bd9Sstevel@tonic-gate } 8507c478bd9Sstevel@tonic-gate 8517c478bd9Sstevel@tonic-gate out1: 8527c478bd9Sstevel@tonic-gate if (status == WALK_ERR) { 8537c478bd9Sstevel@tonic-gate if (kmw->kmw_valid) 8547c478bd9Sstevel@tonic-gate mdb_free(kmw->kmw_valid, slabsize / chunksize); 8557c478bd9Sstevel@tonic-gate 8567c478bd9Sstevel@tonic-gate if (kmw->kmw_ubase) 8577c478bd9Sstevel@tonic-gate mdb_free(kmw->kmw_ubase, slabsize + 8587c478bd9Sstevel@tonic-gate sizeof (kmem_bufctl_t)); 8597c478bd9Sstevel@tonic-gate 860*789d94c2Sjwadams if (kmw->kmw_maglist) 861*789d94c2Sjwadams mdb_free(kmw->kmw_maglist, 862*789d94c2Sjwadams kmw->kmw_max * sizeof (uintptr_t)); 863*789d94c2Sjwadams 8647c478bd9Sstevel@tonic-gate mdb_free(kmw, sizeof (kmem_walk_t)); 8657c478bd9Sstevel@tonic-gate wsp->walk_data = NULL; 8667c478bd9Sstevel@tonic-gate } 8677c478bd9Sstevel@tonic-gate 8687c478bd9Sstevel@tonic-gate out2: 8697c478bd9Sstevel@tonic-gate if (status == WALK_ERR) 8707c478bd9Sstevel@tonic-gate mdb_free(cp, csize); 8717c478bd9Sstevel@tonic-gate 8727c478bd9Sstevel@tonic-gate return (status); 8737c478bd9Sstevel@tonic-gate } 8747c478bd9Sstevel@tonic-gate 8757c478bd9Sstevel@tonic-gate int 8767c478bd9Sstevel@tonic-gate kmem_walk_step(mdb_walk_state_t *wsp) 8777c478bd9Sstevel@tonic-gate { 8787c478bd9Sstevel@tonic-gate kmem_walk_t *kmw = wsp->walk_data; 8797c478bd9Sstevel@tonic-gate int type = kmw->kmw_type; 8807c478bd9Sstevel@tonic-gate kmem_cache_t *cp = kmw->kmw_cp; 8817c478bd9Sstevel@tonic-gate 8827c478bd9Sstevel@tonic-gate void **maglist = kmw->kmw_maglist; 8837c478bd9Sstevel@tonic-gate int magcnt = kmw->kmw_count; 8847c478bd9Sstevel@tonic-gate 8857c478bd9Sstevel@tonic-gate uintptr_t chunksize, slabsize; 8867c478bd9Sstevel@tonic-gate uintptr_t addr; 8877c478bd9Sstevel@tonic-gate const kmem_slab_t *sp; 8887c478bd9Sstevel@tonic-gate const kmem_bufctl_t *bcp; 8897c478bd9Sstevel@tonic-gate kmem_bufctl_t bc; 8907c478bd9Sstevel@tonic-gate 8917c478bd9Sstevel@tonic-gate int chunks; 8927c478bd9Sstevel@tonic-gate char *kbase; 8937c478bd9Sstevel@tonic-gate void *buf; 8947c478bd9Sstevel@tonic-gate int i, ret; 8957c478bd9Sstevel@tonic-gate 8967c478bd9Sstevel@tonic-gate char *valid, *ubase; 8977c478bd9Sstevel@tonic-gate 8987c478bd9Sstevel@tonic-gate /* 8997c478bd9Sstevel@tonic-gate * first, handle the 'kmem_hash' layered walk case 9007c478bd9Sstevel@tonic-gate */ 9017c478bd9Sstevel@tonic-gate if (type & KM_HASH) { 9027c478bd9Sstevel@tonic-gate /* 9037c478bd9Sstevel@tonic-gate * We have a buffer which has been allocated out of the 9047c478bd9Sstevel@tonic-gate * global layer. We need to make sure that it's not 9057c478bd9Sstevel@tonic-gate * actually sitting in a magazine before we report it as 9067c478bd9Sstevel@tonic-gate * an allocated buffer. 9077c478bd9Sstevel@tonic-gate */ 9087c478bd9Sstevel@tonic-gate buf = ((const kmem_bufctl_t *)wsp->walk_layer)->bc_addr; 9097c478bd9Sstevel@tonic-gate 9107c478bd9Sstevel@tonic-gate if (magcnt > 0 && 9117c478bd9Sstevel@tonic-gate bsearch(&buf, maglist, magcnt, sizeof (void *), 9127c478bd9Sstevel@tonic-gate addrcmp) != NULL) 9137c478bd9Sstevel@tonic-gate return (WALK_NEXT); 9147c478bd9Sstevel@tonic-gate 9157c478bd9Sstevel@tonic-gate if (type & KM_BUFCTL) 9167c478bd9Sstevel@tonic-gate return (bufctl_walk_callback(cp, wsp, wsp->walk_addr)); 9177c478bd9Sstevel@tonic-gate 9187c478bd9Sstevel@tonic-gate return (kmem_walk_callback(wsp, (uintptr_t)buf)); 9197c478bd9Sstevel@tonic-gate } 9207c478bd9Sstevel@tonic-gate 9217c478bd9Sstevel@tonic-gate ret = WALK_NEXT; 9227c478bd9Sstevel@tonic-gate 9237c478bd9Sstevel@tonic-gate addr = kmw->kmw_addr; 9247c478bd9Sstevel@tonic-gate 9257c478bd9Sstevel@tonic-gate /* 9267c478bd9Sstevel@tonic-gate * If we're walking freed buffers, report everything in the 9277c478bd9Sstevel@tonic-gate * magazine layer before processing the first slab. 9287c478bd9Sstevel@tonic-gate */ 9297c478bd9Sstevel@tonic-gate if ((type & KM_FREE) && magcnt != 0) { 9307c478bd9Sstevel@tonic-gate kmw->kmw_count = 0; /* only do this once */ 9317c478bd9Sstevel@tonic-gate for (i = 0; i < magcnt; i++) { 9327c478bd9Sstevel@tonic-gate buf = maglist[i]; 9337c478bd9Sstevel@tonic-gate 9347c478bd9Sstevel@tonic-gate if (type & KM_BUFCTL) { 9357c478bd9Sstevel@tonic-gate uintptr_t out; 9367c478bd9Sstevel@tonic-gate 9377c478bd9Sstevel@tonic-gate if (cp->cache_flags & KMF_BUFTAG) { 9387c478bd9Sstevel@tonic-gate kmem_buftag_t *btp; 9397c478bd9Sstevel@tonic-gate kmem_buftag_t tag; 9407c478bd9Sstevel@tonic-gate 9417c478bd9Sstevel@tonic-gate /* LINTED - alignment */ 9427c478bd9Sstevel@tonic-gate btp = KMEM_BUFTAG(cp, buf); 9437c478bd9Sstevel@tonic-gate if (mdb_vread(&tag, sizeof (tag), 9447c478bd9Sstevel@tonic-gate (uintptr_t)btp) == -1) { 9457c478bd9Sstevel@tonic-gate mdb_warn("reading buftag for " 9467c478bd9Sstevel@tonic-gate "%p at %p", buf, btp); 9477c478bd9Sstevel@tonic-gate continue; 9487c478bd9Sstevel@tonic-gate } 9497c478bd9Sstevel@tonic-gate out = (uintptr_t)tag.bt_bufctl; 9507c478bd9Sstevel@tonic-gate } else { 9517c478bd9Sstevel@tonic-gate if (kmem_hash_lookup(cp, addr, buf, 9527c478bd9Sstevel@tonic-gate &out) == -1) 9537c478bd9Sstevel@tonic-gate continue; 9547c478bd9Sstevel@tonic-gate } 9557c478bd9Sstevel@tonic-gate ret = bufctl_walk_callback(cp, wsp, out); 9567c478bd9Sstevel@tonic-gate } else { 9577c478bd9Sstevel@tonic-gate ret = kmem_walk_callback(wsp, (uintptr_t)buf); 9587c478bd9Sstevel@tonic-gate } 9597c478bd9Sstevel@tonic-gate 9607c478bd9Sstevel@tonic-gate if (ret != WALK_NEXT) 9617c478bd9Sstevel@tonic-gate return (ret); 9627c478bd9Sstevel@tonic-gate } 9637c478bd9Sstevel@tonic-gate } 9647c478bd9Sstevel@tonic-gate 9657c478bd9Sstevel@tonic-gate /* 9667c478bd9Sstevel@tonic-gate * If they want constructed buffers, we're finished, since the 9677c478bd9Sstevel@tonic-gate * magazine layer holds them all. 9687c478bd9Sstevel@tonic-gate */ 9697c478bd9Sstevel@tonic-gate if (type & KM_CONSTRUCTED) 9707c478bd9Sstevel@tonic-gate return (WALK_DONE); 9717c478bd9Sstevel@tonic-gate 9727c478bd9Sstevel@tonic-gate /* 9737c478bd9Sstevel@tonic-gate * Handle the buffers in the current slab 9747c478bd9Sstevel@tonic-gate */ 9757c478bd9Sstevel@tonic-gate chunksize = cp->cache_chunksize; 9767c478bd9Sstevel@tonic-gate slabsize = cp->cache_slabsize; 9777c478bd9Sstevel@tonic-gate 9787c478bd9Sstevel@tonic-gate sp = wsp->walk_layer; 9797c478bd9Sstevel@tonic-gate chunks = sp->slab_chunks; 9807c478bd9Sstevel@tonic-gate kbase = sp->slab_base; 9817c478bd9Sstevel@tonic-gate 9827c478bd9Sstevel@tonic-gate dprintf(("kbase is %p\n", kbase)); 9837c478bd9Sstevel@tonic-gate 9847c478bd9Sstevel@tonic-gate if (!(cp->cache_flags & KMF_HASH)) { 9857c478bd9Sstevel@tonic-gate valid = kmw->kmw_valid; 9867c478bd9Sstevel@tonic-gate ubase = kmw->kmw_ubase; 9877c478bd9Sstevel@tonic-gate 9887c478bd9Sstevel@tonic-gate if (mdb_vread(ubase, chunks * chunksize, 9897c478bd9Sstevel@tonic-gate (uintptr_t)kbase) == -1) { 9907c478bd9Sstevel@tonic-gate mdb_warn("failed to read slab contents at %p", kbase); 9917c478bd9Sstevel@tonic-gate return (WALK_ERR); 9927c478bd9Sstevel@tonic-gate } 9937c478bd9Sstevel@tonic-gate 9947c478bd9Sstevel@tonic-gate /* 9957c478bd9Sstevel@tonic-gate * Set up the valid map as fully allocated -- we'll punch 9967c478bd9Sstevel@tonic-gate * out the freelist. 9977c478bd9Sstevel@tonic-gate */ 9987c478bd9Sstevel@tonic-gate if (type & KM_ALLOCATED) 9997c478bd9Sstevel@tonic-gate (void) memset(valid, 1, chunks); 10007c478bd9Sstevel@tonic-gate } else { 10017c478bd9Sstevel@tonic-gate valid = NULL; 10027c478bd9Sstevel@tonic-gate ubase = NULL; 10037c478bd9Sstevel@tonic-gate } 10047c478bd9Sstevel@tonic-gate 10057c478bd9Sstevel@tonic-gate /* 10067c478bd9Sstevel@tonic-gate * walk the slab's freelist 10077c478bd9Sstevel@tonic-gate */ 10087c478bd9Sstevel@tonic-gate bcp = sp->slab_head; 10097c478bd9Sstevel@tonic-gate 10107c478bd9Sstevel@tonic-gate dprintf(("refcnt is %d; chunks is %d\n", sp->slab_refcnt, chunks)); 10117c478bd9Sstevel@tonic-gate 10127c478bd9Sstevel@tonic-gate /* 10137c478bd9Sstevel@tonic-gate * since we could be in the middle of allocating a buffer, 10147c478bd9Sstevel@tonic-gate * our refcnt could be one higher than it aught. So we 10157c478bd9Sstevel@tonic-gate * check one further on the freelist than the count allows. 10167c478bd9Sstevel@tonic-gate */ 10177c478bd9Sstevel@tonic-gate for (i = sp->slab_refcnt; i <= chunks; i++) { 10187c478bd9Sstevel@tonic-gate uint_t ndx; 10197c478bd9Sstevel@tonic-gate 10207c478bd9Sstevel@tonic-gate dprintf(("bcp is %p\n", bcp)); 10217c478bd9Sstevel@tonic-gate 10227c478bd9Sstevel@tonic-gate if (bcp == NULL) { 10237c478bd9Sstevel@tonic-gate if (i == chunks) 10247c478bd9Sstevel@tonic-gate break; 10257c478bd9Sstevel@tonic-gate mdb_warn( 10267c478bd9Sstevel@tonic-gate "slab %p in cache %p freelist too short by %d\n", 10277c478bd9Sstevel@tonic-gate sp, addr, chunks - i); 10287c478bd9Sstevel@tonic-gate break; 10297c478bd9Sstevel@tonic-gate } 10307c478bd9Sstevel@tonic-gate 10317c478bd9Sstevel@tonic-gate if (cp->cache_flags & KMF_HASH) { 10327c478bd9Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), (uintptr_t)bcp) == -1) { 10337c478bd9Sstevel@tonic-gate mdb_warn("failed to read bufctl ptr at %p", 10347c478bd9Sstevel@tonic-gate bcp); 10357c478bd9Sstevel@tonic-gate break; 10367c478bd9Sstevel@tonic-gate } 10377c478bd9Sstevel@tonic-gate buf = bc.bc_addr; 10387c478bd9Sstevel@tonic-gate } else { 10397c478bd9Sstevel@tonic-gate /* 10407c478bd9Sstevel@tonic-gate * Otherwise the buffer is in the slab which 10417c478bd9Sstevel@tonic-gate * we've read in; we just need to determine 10427c478bd9Sstevel@tonic-gate * its offset in the slab to find the 10437c478bd9Sstevel@tonic-gate * kmem_bufctl_t. 10447c478bd9Sstevel@tonic-gate */ 10457c478bd9Sstevel@tonic-gate bc = *((kmem_bufctl_t *) 10467c478bd9Sstevel@tonic-gate ((uintptr_t)bcp - (uintptr_t)kbase + 10477c478bd9Sstevel@tonic-gate (uintptr_t)ubase)); 10487c478bd9Sstevel@tonic-gate 10497c478bd9Sstevel@tonic-gate buf = KMEM_BUF(cp, bcp); 10507c478bd9Sstevel@tonic-gate } 10517c478bd9Sstevel@tonic-gate 10527c478bd9Sstevel@tonic-gate ndx = ((uintptr_t)buf - (uintptr_t)kbase) / chunksize; 10537c478bd9Sstevel@tonic-gate 10547c478bd9Sstevel@tonic-gate if (ndx > slabsize / cp->cache_bufsize) { 10557c478bd9Sstevel@tonic-gate /* 10567c478bd9Sstevel@tonic-gate * This is very wrong; we have managed to find 10577c478bd9Sstevel@tonic-gate * a buffer in the slab which shouldn't 10587c478bd9Sstevel@tonic-gate * actually be here. Emit a warning, and 10597c478bd9Sstevel@tonic-gate * try to continue. 10607c478bd9Sstevel@tonic-gate */ 10617c478bd9Sstevel@tonic-gate mdb_warn("buf %p is out of range for " 10627c478bd9Sstevel@tonic-gate "slab %p, cache %p\n", buf, sp, addr); 10637c478bd9Sstevel@tonic-gate } else if (type & KM_ALLOCATED) { 10647c478bd9Sstevel@tonic-gate /* 10657c478bd9Sstevel@tonic-gate * we have found a buffer on the slab's freelist; 10667c478bd9Sstevel@tonic-gate * clear its entry 10677c478bd9Sstevel@tonic-gate */ 10687c478bd9Sstevel@tonic-gate valid[ndx] = 0; 10697c478bd9Sstevel@tonic-gate } else { 10707c478bd9Sstevel@tonic-gate /* 10717c478bd9Sstevel@tonic-gate * Report this freed buffer 10727c478bd9Sstevel@tonic-gate */ 10737c478bd9Sstevel@tonic-gate if (type & KM_BUFCTL) { 10747c478bd9Sstevel@tonic-gate ret = bufctl_walk_callback(cp, wsp, 10757c478bd9Sstevel@tonic-gate (uintptr_t)bcp); 10767c478bd9Sstevel@tonic-gate } else { 10777c478bd9Sstevel@tonic-gate ret = kmem_walk_callback(wsp, (uintptr_t)buf); 10787c478bd9Sstevel@tonic-gate } 10797c478bd9Sstevel@tonic-gate if (ret != WALK_NEXT) 10807c478bd9Sstevel@tonic-gate return (ret); 10817c478bd9Sstevel@tonic-gate } 10827c478bd9Sstevel@tonic-gate 10837c478bd9Sstevel@tonic-gate bcp = bc.bc_next; 10847c478bd9Sstevel@tonic-gate } 10857c478bd9Sstevel@tonic-gate 10867c478bd9Sstevel@tonic-gate if (bcp != NULL) { 10877c478bd9Sstevel@tonic-gate dprintf(("slab %p in cache %p freelist too long (%p)\n", 10887c478bd9Sstevel@tonic-gate sp, addr, bcp)); 10897c478bd9Sstevel@tonic-gate } 10907c478bd9Sstevel@tonic-gate 10917c478bd9Sstevel@tonic-gate /* 10927c478bd9Sstevel@tonic-gate * If we are walking freed buffers, the loop above handled reporting 10937c478bd9Sstevel@tonic-gate * them. 10947c478bd9Sstevel@tonic-gate */ 10957c478bd9Sstevel@tonic-gate if (type & KM_FREE) 10967c478bd9Sstevel@tonic-gate return (WALK_NEXT); 10977c478bd9Sstevel@tonic-gate 10987c478bd9Sstevel@tonic-gate if (type & KM_BUFCTL) { 10997c478bd9Sstevel@tonic-gate mdb_warn("impossible situation: small-slab KM_BUFCTL walk for " 11007c478bd9Sstevel@tonic-gate "cache %p\n", addr); 11017c478bd9Sstevel@tonic-gate return (WALK_ERR); 11027c478bd9Sstevel@tonic-gate } 11037c478bd9Sstevel@tonic-gate 11047c478bd9Sstevel@tonic-gate /* 11057c478bd9Sstevel@tonic-gate * Report allocated buffers, skipping buffers in the magazine layer. 11067c478bd9Sstevel@tonic-gate * We only get this far for small-slab caches. 11077c478bd9Sstevel@tonic-gate */ 11087c478bd9Sstevel@tonic-gate for (i = 0; ret == WALK_NEXT && i < chunks; i++) { 11097c478bd9Sstevel@tonic-gate buf = (char *)kbase + i * chunksize; 11107c478bd9Sstevel@tonic-gate 11117c478bd9Sstevel@tonic-gate if (!valid[i]) 11127c478bd9Sstevel@tonic-gate continue; /* on slab freelist */ 11137c478bd9Sstevel@tonic-gate 11147c478bd9Sstevel@tonic-gate if (magcnt > 0 && 11157c478bd9Sstevel@tonic-gate bsearch(&buf, maglist, magcnt, sizeof (void *), 11167c478bd9Sstevel@tonic-gate addrcmp) != NULL) 11177c478bd9Sstevel@tonic-gate continue; /* in magazine layer */ 11187c478bd9Sstevel@tonic-gate 11197c478bd9Sstevel@tonic-gate ret = kmem_walk_callback(wsp, (uintptr_t)buf); 11207c478bd9Sstevel@tonic-gate } 11217c478bd9Sstevel@tonic-gate return (ret); 11227c478bd9Sstevel@tonic-gate } 11237c478bd9Sstevel@tonic-gate 11247c478bd9Sstevel@tonic-gate void 11257c478bd9Sstevel@tonic-gate kmem_walk_fini(mdb_walk_state_t *wsp) 11267c478bd9Sstevel@tonic-gate { 11277c478bd9Sstevel@tonic-gate kmem_walk_t *kmw = wsp->walk_data; 11287c478bd9Sstevel@tonic-gate uintptr_t chunksize; 11297c478bd9Sstevel@tonic-gate uintptr_t slabsize; 11307c478bd9Sstevel@tonic-gate 11317c478bd9Sstevel@tonic-gate if (kmw == NULL) 11327c478bd9Sstevel@tonic-gate return; 11337c478bd9Sstevel@tonic-gate 11347c478bd9Sstevel@tonic-gate if (kmw->kmw_maglist != NULL) 11357c478bd9Sstevel@tonic-gate mdb_free(kmw->kmw_maglist, kmw->kmw_max * sizeof (void *)); 11367c478bd9Sstevel@tonic-gate 11377c478bd9Sstevel@tonic-gate chunksize = kmw->kmw_cp->cache_chunksize; 11387c478bd9Sstevel@tonic-gate slabsize = kmw->kmw_cp->cache_slabsize; 11397c478bd9Sstevel@tonic-gate 11407c478bd9Sstevel@tonic-gate if (kmw->kmw_valid != NULL) 11417c478bd9Sstevel@tonic-gate mdb_free(kmw->kmw_valid, slabsize / chunksize); 11427c478bd9Sstevel@tonic-gate if (kmw->kmw_ubase != NULL) 11437c478bd9Sstevel@tonic-gate mdb_free(kmw->kmw_ubase, slabsize + sizeof (kmem_bufctl_t)); 11447c478bd9Sstevel@tonic-gate 11457c478bd9Sstevel@tonic-gate mdb_free(kmw->kmw_cp, kmw->kmw_csize); 11467c478bd9Sstevel@tonic-gate mdb_free(kmw, sizeof (kmem_walk_t)); 11477c478bd9Sstevel@tonic-gate } 11487c478bd9Sstevel@tonic-gate 11497c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 11507c478bd9Sstevel@tonic-gate static int 11517c478bd9Sstevel@tonic-gate kmem_walk_all(uintptr_t addr, const kmem_cache_t *c, mdb_walk_state_t *wsp) 11527c478bd9Sstevel@tonic-gate { 11537c478bd9Sstevel@tonic-gate /* 11547c478bd9Sstevel@tonic-gate * Buffers allocated from NOTOUCH caches can also show up as freed 11557c478bd9Sstevel@tonic-gate * memory in other caches. This can be a little confusing, so we 11567c478bd9Sstevel@tonic-gate * don't walk NOTOUCH caches when walking all caches (thereby assuring 11577c478bd9Sstevel@tonic-gate * that "::walk kmem" and "::walk freemem" yield disjoint output). 11587c478bd9Sstevel@tonic-gate */ 11597c478bd9Sstevel@tonic-gate if (c->cache_cflags & KMC_NOTOUCH) 11607c478bd9Sstevel@tonic-gate return (WALK_NEXT); 11617c478bd9Sstevel@tonic-gate 11627c478bd9Sstevel@tonic-gate if (mdb_pwalk(wsp->walk_data, wsp->walk_callback, 11637c478bd9Sstevel@tonic-gate wsp->walk_cbdata, addr) == -1) 11647c478bd9Sstevel@tonic-gate return (WALK_DONE); 11657c478bd9Sstevel@tonic-gate 11667c478bd9Sstevel@tonic-gate return (WALK_NEXT); 11677c478bd9Sstevel@tonic-gate } 11687c478bd9Sstevel@tonic-gate 11697c478bd9Sstevel@tonic-gate #define KMEM_WALK_ALL(name, wsp) { \ 11707c478bd9Sstevel@tonic-gate wsp->walk_data = (name); \ 11717c478bd9Sstevel@tonic-gate if (mdb_walk("kmem_cache", (mdb_walk_cb_t)kmem_walk_all, wsp) == -1) \ 11727c478bd9Sstevel@tonic-gate return (WALK_ERR); \ 11737c478bd9Sstevel@tonic-gate return (WALK_DONE); \ 11747c478bd9Sstevel@tonic-gate } 11757c478bd9Sstevel@tonic-gate 11767c478bd9Sstevel@tonic-gate int 11777c478bd9Sstevel@tonic-gate kmem_walk_init(mdb_walk_state_t *wsp) 11787c478bd9Sstevel@tonic-gate { 11797c478bd9Sstevel@tonic-gate if (wsp->walk_arg != NULL) 11807c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)wsp->walk_arg; 11817c478bd9Sstevel@tonic-gate 11827c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) 11837c478bd9Sstevel@tonic-gate KMEM_WALK_ALL("kmem", wsp); 11847c478bd9Sstevel@tonic-gate return (kmem_walk_init_common(wsp, KM_ALLOCATED)); 11857c478bd9Sstevel@tonic-gate } 11867c478bd9Sstevel@tonic-gate 11877c478bd9Sstevel@tonic-gate int 11887c478bd9Sstevel@tonic-gate bufctl_walk_init(mdb_walk_state_t *wsp) 11897c478bd9Sstevel@tonic-gate { 11907c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) 11917c478bd9Sstevel@tonic-gate KMEM_WALK_ALL("bufctl", wsp); 11927c478bd9Sstevel@tonic-gate return (kmem_walk_init_common(wsp, KM_ALLOCATED | KM_BUFCTL)); 11937c478bd9Sstevel@tonic-gate } 11947c478bd9Sstevel@tonic-gate 11957c478bd9Sstevel@tonic-gate int 11967c478bd9Sstevel@tonic-gate freemem_walk_init(mdb_walk_state_t *wsp) 11977c478bd9Sstevel@tonic-gate { 11987c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) 11997c478bd9Sstevel@tonic-gate KMEM_WALK_ALL("freemem", wsp); 12007c478bd9Sstevel@tonic-gate return (kmem_walk_init_common(wsp, KM_FREE)); 12017c478bd9Sstevel@tonic-gate } 12027c478bd9Sstevel@tonic-gate 12037c478bd9Sstevel@tonic-gate int 12047c478bd9Sstevel@tonic-gate freemem_constructed_walk_init(mdb_walk_state_t *wsp) 12057c478bd9Sstevel@tonic-gate { 12067c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) 12077c478bd9Sstevel@tonic-gate KMEM_WALK_ALL("freemem_constructed", wsp); 12087c478bd9Sstevel@tonic-gate return (kmem_walk_init_common(wsp, KM_FREE | KM_CONSTRUCTED)); 12097c478bd9Sstevel@tonic-gate } 12107c478bd9Sstevel@tonic-gate 12117c478bd9Sstevel@tonic-gate int 12127c478bd9Sstevel@tonic-gate freectl_walk_init(mdb_walk_state_t *wsp) 12137c478bd9Sstevel@tonic-gate { 12147c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) 12157c478bd9Sstevel@tonic-gate KMEM_WALK_ALL("freectl", wsp); 12167c478bd9Sstevel@tonic-gate return (kmem_walk_init_common(wsp, KM_FREE | KM_BUFCTL)); 12177c478bd9Sstevel@tonic-gate } 12187c478bd9Sstevel@tonic-gate 12197c478bd9Sstevel@tonic-gate int 12207c478bd9Sstevel@tonic-gate freectl_constructed_walk_init(mdb_walk_state_t *wsp) 12217c478bd9Sstevel@tonic-gate { 12227c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) 12237c478bd9Sstevel@tonic-gate KMEM_WALK_ALL("freectl_constructed", wsp); 12247c478bd9Sstevel@tonic-gate return (kmem_walk_init_common(wsp, 12257c478bd9Sstevel@tonic-gate KM_FREE | KM_BUFCTL | KM_CONSTRUCTED)); 12267c478bd9Sstevel@tonic-gate } 12277c478bd9Sstevel@tonic-gate 12287c478bd9Sstevel@tonic-gate typedef struct bufctl_history_walk { 12297c478bd9Sstevel@tonic-gate void *bhw_next; 12307c478bd9Sstevel@tonic-gate kmem_cache_t *bhw_cache; 12317c478bd9Sstevel@tonic-gate kmem_slab_t *bhw_slab; 12327c478bd9Sstevel@tonic-gate hrtime_t bhw_timestamp; 12337c478bd9Sstevel@tonic-gate } bufctl_history_walk_t; 12347c478bd9Sstevel@tonic-gate 12357c478bd9Sstevel@tonic-gate int 12367c478bd9Sstevel@tonic-gate bufctl_history_walk_init(mdb_walk_state_t *wsp) 12377c478bd9Sstevel@tonic-gate { 12387c478bd9Sstevel@tonic-gate bufctl_history_walk_t *bhw; 12397c478bd9Sstevel@tonic-gate kmem_bufctl_audit_t bc; 12407c478bd9Sstevel@tonic-gate kmem_bufctl_audit_t bcn; 12417c478bd9Sstevel@tonic-gate 12427c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 12437c478bd9Sstevel@tonic-gate mdb_warn("bufctl_history walk doesn't support global walks\n"); 12447c478bd9Sstevel@tonic-gate return (WALK_ERR); 12457c478bd9Sstevel@tonic-gate } 12467c478bd9Sstevel@tonic-gate 12477c478bd9Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), wsp->walk_addr) == -1) { 12487c478bd9Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", wsp->walk_addr); 12497c478bd9Sstevel@tonic-gate return (WALK_ERR); 12507c478bd9Sstevel@tonic-gate } 12517c478bd9Sstevel@tonic-gate 12527c478bd9Sstevel@tonic-gate bhw = mdb_zalloc(sizeof (*bhw), UM_SLEEP); 12537c478bd9Sstevel@tonic-gate bhw->bhw_timestamp = 0; 12547c478bd9Sstevel@tonic-gate bhw->bhw_cache = bc.bc_cache; 12557c478bd9Sstevel@tonic-gate bhw->bhw_slab = bc.bc_slab; 12567c478bd9Sstevel@tonic-gate 12577c478bd9Sstevel@tonic-gate /* 12587c478bd9Sstevel@tonic-gate * sometimes the first log entry matches the base bufctl; in that 12597c478bd9Sstevel@tonic-gate * case, skip the base bufctl. 12607c478bd9Sstevel@tonic-gate */ 12617c478bd9Sstevel@tonic-gate if (bc.bc_lastlog != NULL && 12627c478bd9Sstevel@tonic-gate mdb_vread(&bcn, sizeof (bcn), (uintptr_t)bc.bc_lastlog) != -1 && 12637c478bd9Sstevel@tonic-gate bc.bc_addr == bcn.bc_addr && 12647c478bd9Sstevel@tonic-gate bc.bc_cache == bcn.bc_cache && 12657c478bd9Sstevel@tonic-gate bc.bc_slab == bcn.bc_slab && 12667c478bd9Sstevel@tonic-gate bc.bc_timestamp == bcn.bc_timestamp && 12677c478bd9Sstevel@tonic-gate bc.bc_thread == bcn.bc_thread) 12687c478bd9Sstevel@tonic-gate bhw->bhw_next = bc.bc_lastlog; 12697c478bd9Sstevel@tonic-gate else 12707c478bd9Sstevel@tonic-gate bhw->bhw_next = (void *)wsp->walk_addr; 12717c478bd9Sstevel@tonic-gate 12727c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)bc.bc_addr; 12737c478bd9Sstevel@tonic-gate wsp->walk_data = bhw; 12747c478bd9Sstevel@tonic-gate 12757c478bd9Sstevel@tonic-gate return (WALK_NEXT); 12767c478bd9Sstevel@tonic-gate } 12777c478bd9Sstevel@tonic-gate 12787c478bd9Sstevel@tonic-gate int 12797c478bd9Sstevel@tonic-gate bufctl_history_walk_step(mdb_walk_state_t *wsp) 12807c478bd9Sstevel@tonic-gate { 12817c478bd9Sstevel@tonic-gate bufctl_history_walk_t *bhw = wsp->walk_data; 12827c478bd9Sstevel@tonic-gate uintptr_t addr = (uintptr_t)bhw->bhw_next; 12837c478bd9Sstevel@tonic-gate uintptr_t baseaddr = wsp->walk_addr; 12847c478bd9Sstevel@tonic-gate kmem_bufctl_audit_t bc; 12857c478bd9Sstevel@tonic-gate 12867c478bd9Sstevel@tonic-gate if (addr == NULL) 12877c478bd9Sstevel@tonic-gate return (WALK_DONE); 12887c478bd9Sstevel@tonic-gate 12897c478bd9Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), addr) == -1) { 12907c478bd9Sstevel@tonic-gate mdb_warn("unable to read bufctl at %p", bhw->bhw_next); 12917c478bd9Sstevel@tonic-gate return (WALK_ERR); 12927c478bd9Sstevel@tonic-gate } 12937c478bd9Sstevel@tonic-gate 12947c478bd9Sstevel@tonic-gate /* 12957c478bd9Sstevel@tonic-gate * The bufctl is only valid if the address, cache, and slab are 12967c478bd9Sstevel@tonic-gate * correct. We also check that the timestamp is decreasing, to 12977c478bd9Sstevel@tonic-gate * prevent infinite loops. 12987c478bd9Sstevel@tonic-gate */ 12997c478bd9Sstevel@tonic-gate if ((uintptr_t)bc.bc_addr != baseaddr || 13007c478bd9Sstevel@tonic-gate bc.bc_cache != bhw->bhw_cache || 13017c478bd9Sstevel@tonic-gate bc.bc_slab != bhw->bhw_slab || 13027c478bd9Sstevel@tonic-gate (bhw->bhw_timestamp != 0 && bc.bc_timestamp >= bhw->bhw_timestamp)) 13037c478bd9Sstevel@tonic-gate return (WALK_DONE); 13047c478bd9Sstevel@tonic-gate 13057c478bd9Sstevel@tonic-gate bhw->bhw_next = bc.bc_lastlog; 13067c478bd9Sstevel@tonic-gate bhw->bhw_timestamp = bc.bc_timestamp; 13077c478bd9Sstevel@tonic-gate 13087c478bd9Sstevel@tonic-gate return (wsp->walk_callback(addr, &bc, wsp->walk_cbdata)); 13097c478bd9Sstevel@tonic-gate } 13107c478bd9Sstevel@tonic-gate 13117c478bd9Sstevel@tonic-gate void 13127c478bd9Sstevel@tonic-gate bufctl_history_walk_fini(mdb_walk_state_t *wsp) 13137c478bd9Sstevel@tonic-gate { 13147c478bd9Sstevel@tonic-gate bufctl_history_walk_t *bhw = wsp->walk_data; 13157c478bd9Sstevel@tonic-gate 13167c478bd9Sstevel@tonic-gate mdb_free(bhw, sizeof (*bhw)); 13177c478bd9Sstevel@tonic-gate } 13187c478bd9Sstevel@tonic-gate 13197c478bd9Sstevel@tonic-gate typedef struct kmem_log_walk { 13207c478bd9Sstevel@tonic-gate kmem_bufctl_audit_t *klw_base; 13217c478bd9Sstevel@tonic-gate kmem_bufctl_audit_t **klw_sorted; 13227c478bd9Sstevel@tonic-gate kmem_log_header_t klw_lh; 13237c478bd9Sstevel@tonic-gate size_t klw_size; 13247c478bd9Sstevel@tonic-gate size_t klw_maxndx; 13257c478bd9Sstevel@tonic-gate size_t klw_ndx; 13267c478bd9Sstevel@tonic-gate } kmem_log_walk_t; 13277c478bd9Sstevel@tonic-gate 13287c478bd9Sstevel@tonic-gate int 13297c478bd9Sstevel@tonic-gate kmem_log_walk_init(mdb_walk_state_t *wsp) 13307c478bd9Sstevel@tonic-gate { 13317c478bd9Sstevel@tonic-gate uintptr_t lp = wsp->walk_addr; 13327c478bd9Sstevel@tonic-gate kmem_log_walk_t *klw; 13337c478bd9Sstevel@tonic-gate kmem_log_header_t *lhp; 13347c478bd9Sstevel@tonic-gate int maxndx, i, j, k; 13357c478bd9Sstevel@tonic-gate 13367c478bd9Sstevel@tonic-gate /* 13377c478bd9Sstevel@tonic-gate * By default (global walk), walk the kmem_transaction_log. Otherwise 13387c478bd9Sstevel@tonic-gate * read the log whose kmem_log_header_t is stored at walk_addr. 13397c478bd9Sstevel@tonic-gate */ 13407c478bd9Sstevel@tonic-gate if (lp == NULL && mdb_readvar(&lp, "kmem_transaction_log") == -1) { 13417c478bd9Sstevel@tonic-gate mdb_warn("failed to read 'kmem_transaction_log'"); 13427c478bd9Sstevel@tonic-gate return (WALK_ERR); 13437c478bd9Sstevel@tonic-gate } 13447c478bd9Sstevel@tonic-gate 13457c478bd9Sstevel@tonic-gate if (lp == NULL) { 13467c478bd9Sstevel@tonic-gate mdb_warn("log is disabled\n"); 13477c478bd9Sstevel@tonic-gate return (WALK_ERR); 13487c478bd9Sstevel@tonic-gate } 13497c478bd9Sstevel@tonic-gate 13507c478bd9Sstevel@tonic-gate klw = mdb_zalloc(sizeof (kmem_log_walk_t), UM_SLEEP); 13517c478bd9Sstevel@tonic-gate lhp = &klw->klw_lh; 13527c478bd9Sstevel@tonic-gate 13537c478bd9Sstevel@tonic-gate if (mdb_vread(lhp, sizeof (kmem_log_header_t), lp) == -1) { 13547c478bd9Sstevel@tonic-gate mdb_warn("failed to read log header at %p", lp); 13557c478bd9Sstevel@tonic-gate mdb_free(klw, sizeof (kmem_log_walk_t)); 13567c478bd9Sstevel@tonic-gate return (WALK_ERR); 13577c478bd9Sstevel@tonic-gate } 13587c478bd9Sstevel@tonic-gate 13597c478bd9Sstevel@tonic-gate klw->klw_size = lhp->lh_chunksize * lhp->lh_nchunks; 13607c478bd9Sstevel@tonic-gate klw->klw_base = mdb_alloc(klw->klw_size, UM_SLEEP); 13617c478bd9Sstevel@tonic-gate maxndx = lhp->lh_chunksize / sizeof (kmem_bufctl_audit_t) - 1; 13627c478bd9Sstevel@tonic-gate 13637c478bd9Sstevel@tonic-gate if (mdb_vread(klw->klw_base, klw->klw_size, 13647c478bd9Sstevel@tonic-gate (uintptr_t)lhp->lh_base) == -1) { 13657c478bd9Sstevel@tonic-gate mdb_warn("failed to read log at base %p", lhp->lh_base); 13667c478bd9Sstevel@tonic-gate mdb_free(klw->klw_base, klw->klw_size); 13677c478bd9Sstevel@tonic-gate mdb_free(klw, sizeof (kmem_log_walk_t)); 13687c478bd9Sstevel@tonic-gate return (WALK_ERR); 13697c478bd9Sstevel@tonic-gate } 13707c478bd9Sstevel@tonic-gate 13717c478bd9Sstevel@tonic-gate klw->klw_sorted = mdb_alloc(maxndx * lhp->lh_nchunks * 13727c478bd9Sstevel@tonic-gate sizeof (kmem_bufctl_audit_t *), UM_SLEEP); 13737c478bd9Sstevel@tonic-gate 13747c478bd9Sstevel@tonic-gate for (i = 0, k = 0; i < lhp->lh_nchunks; i++) { 13757c478bd9Sstevel@tonic-gate kmem_bufctl_audit_t *chunk = (kmem_bufctl_audit_t *) 13767c478bd9Sstevel@tonic-gate ((uintptr_t)klw->klw_base + i * lhp->lh_chunksize); 13777c478bd9Sstevel@tonic-gate 13787c478bd9Sstevel@tonic-gate for (j = 0; j < maxndx; j++) 13797c478bd9Sstevel@tonic-gate klw->klw_sorted[k++] = &chunk[j]; 13807c478bd9Sstevel@tonic-gate } 13817c478bd9Sstevel@tonic-gate 13827c478bd9Sstevel@tonic-gate qsort(klw->klw_sorted, k, sizeof (kmem_bufctl_audit_t *), 13837c478bd9Sstevel@tonic-gate (int(*)(const void *, const void *))bufctlcmp); 13847c478bd9Sstevel@tonic-gate 13857c478bd9Sstevel@tonic-gate klw->klw_maxndx = k; 13867c478bd9Sstevel@tonic-gate wsp->walk_data = klw; 13877c478bd9Sstevel@tonic-gate 13887c478bd9Sstevel@tonic-gate return (WALK_NEXT); 13897c478bd9Sstevel@tonic-gate } 13907c478bd9Sstevel@tonic-gate 13917c478bd9Sstevel@tonic-gate int 13927c478bd9Sstevel@tonic-gate kmem_log_walk_step(mdb_walk_state_t *wsp) 13937c478bd9Sstevel@tonic-gate { 13947c478bd9Sstevel@tonic-gate kmem_log_walk_t *klw = wsp->walk_data; 13957c478bd9Sstevel@tonic-gate kmem_bufctl_audit_t *bcp; 13967c478bd9Sstevel@tonic-gate 13977c478bd9Sstevel@tonic-gate if (klw->klw_ndx == klw->klw_maxndx) 13987c478bd9Sstevel@tonic-gate return (WALK_DONE); 13997c478bd9Sstevel@tonic-gate 14007c478bd9Sstevel@tonic-gate bcp = klw->klw_sorted[klw->klw_ndx++]; 14017c478bd9Sstevel@tonic-gate 14027c478bd9Sstevel@tonic-gate return (wsp->walk_callback((uintptr_t)bcp - (uintptr_t)klw->klw_base + 14037c478bd9Sstevel@tonic-gate (uintptr_t)klw->klw_lh.lh_base, bcp, wsp->walk_cbdata)); 14047c478bd9Sstevel@tonic-gate } 14057c478bd9Sstevel@tonic-gate 14067c478bd9Sstevel@tonic-gate void 14077c478bd9Sstevel@tonic-gate kmem_log_walk_fini(mdb_walk_state_t *wsp) 14087c478bd9Sstevel@tonic-gate { 14097c478bd9Sstevel@tonic-gate kmem_log_walk_t *klw = wsp->walk_data; 14107c478bd9Sstevel@tonic-gate 14117c478bd9Sstevel@tonic-gate mdb_free(klw->klw_base, klw->klw_size); 14127c478bd9Sstevel@tonic-gate mdb_free(klw->klw_sorted, klw->klw_maxndx * 14137c478bd9Sstevel@tonic-gate sizeof (kmem_bufctl_audit_t *)); 14147c478bd9Sstevel@tonic-gate mdb_free(klw, sizeof (kmem_log_walk_t)); 14157c478bd9Sstevel@tonic-gate } 14167c478bd9Sstevel@tonic-gate 14177c478bd9Sstevel@tonic-gate typedef struct allocdby_bufctl { 14187c478bd9Sstevel@tonic-gate uintptr_t abb_addr; 14197c478bd9Sstevel@tonic-gate hrtime_t abb_ts; 14207c478bd9Sstevel@tonic-gate } allocdby_bufctl_t; 14217c478bd9Sstevel@tonic-gate 14227c478bd9Sstevel@tonic-gate typedef struct allocdby_walk { 14237c478bd9Sstevel@tonic-gate const char *abw_walk; 14247c478bd9Sstevel@tonic-gate uintptr_t abw_thread; 14257c478bd9Sstevel@tonic-gate size_t abw_nbufs; 14267c478bd9Sstevel@tonic-gate size_t abw_size; 14277c478bd9Sstevel@tonic-gate allocdby_bufctl_t *abw_buf; 14287c478bd9Sstevel@tonic-gate size_t abw_ndx; 14297c478bd9Sstevel@tonic-gate } allocdby_walk_t; 14307c478bd9Sstevel@tonic-gate 14317c478bd9Sstevel@tonic-gate int 14327c478bd9Sstevel@tonic-gate allocdby_walk_bufctl(uintptr_t addr, const kmem_bufctl_audit_t *bcp, 14337c478bd9Sstevel@tonic-gate allocdby_walk_t *abw) 14347c478bd9Sstevel@tonic-gate { 14357c478bd9Sstevel@tonic-gate if ((uintptr_t)bcp->bc_thread != abw->abw_thread) 14367c478bd9Sstevel@tonic-gate return (WALK_NEXT); 14377c478bd9Sstevel@tonic-gate 14387c478bd9Sstevel@tonic-gate if (abw->abw_nbufs == abw->abw_size) { 14397c478bd9Sstevel@tonic-gate allocdby_bufctl_t *buf; 14407c478bd9Sstevel@tonic-gate size_t oldsize = sizeof (allocdby_bufctl_t) * abw->abw_size; 14417c478bd9Sstevel@tonic-gate 14427c478bd9Sstevel@tonic-gate buf = mdb_zalloc(oldsize << 1, UM_SLEEP); 14437c478bd9Sstevel@tonic-gate 14447c478bd9Sstevel@tonic-gate bcopy(abw->abw_buf, buf, oldsize); 14457c478bd9Sstevel@tonic-gate mdb_free(abw->abw_buf, oldsize); 14467c478bd9Sstevel@tonic-gate 14477c478bd9Sstevel@tonic-gate abw->abw_size <<= 1; 14487c478bd9Sstevel@tonic-gate abw->abw_buf = buf; 14497c478bd9Sstevel@tonic-gate } 14507c478bd9Sstevel@tonic-gate 14517c478bd9Sstevel@tonic-gate abw->abw_buf[abw->abw_nbufs].abb_addr = addr; 14527c478bd9Sstevel@tonic-gate abw->abw_buf[abw->abw_nbufs].abb_ts = bcp->bc_timestamp; 14537c478bd9Sstevel@tonic-gate abw->abw_nbufs++; 14547c478bd9Sstevel@tonic-gate 14557c478bd9Sstevel@tonic-gate return (WALK_NEXT); 14567c478bd9Sstevel@tonic-gate } 14577c478bd9Sstevel@tonic-gate 14587c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 14597c478bd9Sstevel@tonic-gate int 14607c478bd9Sstevel@tonic-gate allocdby_walk_cache(uintptr_t addr, const kmem_cache_t *c, allocdby_walk_t *abw) 14617c478bd9Sstevel@tonic-gate { 14627c478bd9Sstevel@tonic-gate if (mdb_pwalk(abw->abw_walk, (mdb_walk_cb_t)allocdby_walk_bufctl, 14637c478bd9Sstevel@tonic-gate abw, addr) == -1) { 14647c478bd9Sstevel@tonic-gate mdb_warn("couldn't walk bufctl for cache %p", addr); 14657c478bd9Sstevel@tonic-gate return (WALK_DONE); 14667c478bd9Sstevel@tonic-gate } 14677c478bd9Sstevel@tonic-gate 14687c478bd9Sstevel@tonic-gate return (WALK_NEXT); 14697c478bd9Sstevel@tonic-gate } 14707c478bd9Sstevel@tonic-gate 14717c478bd9Sstevel@tonic-gate static int 14727c478bd9Sstevel@tonic-gate allocdby_cmp(const allocdby_bufctl_t *lhs, const allocdby_bufctl_t *rhs) 14737c478bd9Sstevel@tonic-gate { 14747c478bd9Sstevel@tonic-gate if (lhs->abb_ts < rhs->abb_ts) 14757c478bd9Sstevel@tonic-gate return (1); 14767c478bd9Sstevel@tonic-gate if (lhs->abb_ts > rhs->abb_ts) 14777c478bd9Sstevel@tonic-gate return (-1); 14787c478bd9Sstevel@tonic-gate return (0); 14797c478bd9Sstevel@tonic-gate } 14807c478bd9Sstevel@tonic-gate 14817c478bd9Sstevel@tonic-gate static int 14827c478bd9Sstevel@tonic-gate allocdby_walk_init_common(mdb_walk_state_t *wsp, const char *walk) 14837c478bd9Sstevel@tonic-gate { 14847c478bd9Sstevel@tonic-gate allocdby_walk_t *abw; 14857c478bd9Sstevel@tonic-gate 14867c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 14877c478bd9Sstevel@tonic-gate mdb_warn("allocdby walk doesn't support global walks\n"); 14887c478bd9Sstevel@tonic-gate return (WALK_ERR); 14897c478bd9Sstevel@tonic-gate } 14907c478bd9Sstevel@tonic-gate 14917c478bd9Sstevel@tonic-gate abw = mdb_zalloc(sizeof (allocdby_walk_t), UM_SLEEP); 14927c478bd9Sstevel@tonic-gate 14937c478bd9Sstevel@tonic-gate abw->abw_thread = wsp->walk_addr; 14947c478bd9Sstevel@tonic-gate abw->abw_walk = walk; 14957c478bd9Sstevel@tonic-gate abw->abw_size = 128; /* something reasonable */ 14967c478bd9Sstevel@tonic-gate abw->abw_buf = 14977c478bd9Sstevel@tonic-gate mdb_zalloc(abw->abw_size * sizeof (allocdby_bufctl_t), UM_SLEEP); 14987c478bd9Sstevel@tonic-gate 14997c478bd9Sstevel@tonic-gate wsp->walk_data = abw; 15007c478bd9Sstevel@tonic-gate 15017c478bd9Sstevel@tonic-gate if (mdb_walk("kmem_cache", 15027c478bd9Sstevel@tonic-gate (mdb_walk_cb_t)allocdby_walk_cache, abw) == -1) { 15037c478bd9Sstevel@tonic-gate mdb_warn("couldn't walk kmem_cache"); 15047c478bd9Sstevel@tonic-gate allocdby_walk_fini(wsp); 15057c478bd9Sstevel@tonic-gate return (WALK_ERR); 15067c478bd9Sstevel@tonic-gate } 15077c478bd9Sstevel@tonic-gate 15087c478bd9Sstevel@tonic-gate qsort(abw->abw_buf, abw->abw_nbufs, sizeof (allocdby_bufctl_t), 15097c478bd9Sstevel@tonic-gate (int(*)(const void *, const void *))allocdby_cmp); 15107c478bd9Sstevel@tonic-gate 15117c478bd9Sstevel@tonic-gate return (WALK_NEXT); 15127c478bd9Sstevel@tonic-gate } 15137c478bd9Sstevel@tonic-gate 15147c478bd9Sstevel@tonic-gate int 15157c478bd9Sstevel@tonic-gate allocdby_walk_init(mdb_walk_state_t *wsp) 15167c478bd9Sstevel@tonic-gate { 15177c478bd9Sstevel@tonic-gate return (allocdby_walk_init_common(wsp, "bufctl")); 15187c478bd9Sstevel@tonic-gate } 15197c478bd9Sstevel@tonic-gate 15207c478bd9Sstevel@tonic-gate int 15217c478bd9Sstevel@tonic-gate freedby_walk_init(mdb_walk_state_t *wsp) 15227c478bd9Sstevel@tonic-gate { 15237c478bd9Sstevel@tonic-gate return (allocdby_walk_init_common(wsp, "freectl")); 15247c478bd9Sstevel@tonic-gate } 15257c478bd9Sstevel@tonic-gate 15267c478bd9Sstevel@tonic-gate int 15277c478bd9Sstevel@tonic-gate allocdby_walk_step(mdb_walk_state_t *wsp) 15287c478bd9Sstevel@tonic-gate { 15297c478bd9Sstevel@tonic-gate allocdby_walk_t *abw = wsp->walk_data; 15307c478bd9Sstevel@tonic-gate kmem_bufctl_audit_t bc; 15317c478bd9Sstevel@tonic-gate uintptr_t addr; 15327c478bd9Sstevel@tonic-gate 15337c478bd9Sstevel@tonic-gate if (abw->abw_ndx == abw->abw_nbufs) 15347c478bd9Sstevel@tonic-gate return (WALK_DONE); 15357c478bd9Sstevel@tonic-gate 15367c478bd9Sstevel@tonic-gate addr = abw->abw_buf[abw->abw_ndx++].abb_addr; 15377c478bd9Sstevel@tonic-gate 15387c478bd9Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), addr) == -1) { 15397c478bd9Sstevel@tonic-gate mdb_warn("couldn't read bufctl at %p", addr); 15407c478bd9Sstevel@tonic-gate return (WALK_DONE); 15417c478bd9Sstevel@tonic-gate } 15427c478bd9Sstevel@tonic-gate 15437c478bd9Sstevel@tonic-gate return (wsp->walk_callback(addr, &bc, wsp->walk_cbdata)); 15447c478bd9Sstevel@tonic-gate } 15457c478bd9Sstevel@tonic-gate 15467c478bd9Sstevel@tonic-gate void 15477c478bd9Sstevel@tonic-gate allocdby_walk_fini(mdb_walk_state_t *wsp) 15487c478bd9Sstevel@tonic-gate { 15497c478bd9Sstevel@tonic-gate allocdby_walk_t *abw = wsp->walk_data; 15507c478bd9Sstevel@tonic-gate 15517c478bd9Sstevel@tonic-gate mdb_free(abw->abw_buf, sizeof (allocdby_bufctl_t) * abw->abw_size); 15527c478bd9Sstevel@tonic-gate mdb_free(abw, sizeof (allocdby_walk_t)); 15537c478bd9Sstevel@tonic-gate } 15547c478bd9Sstevel@tonic-gate 15557c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 15567c478bd9Sstevel@tonic-gate int 15577c478bd9Sstevel@tonic-gate allocdby_walk(uintptr_t addr, const kmem_bufctl_audit_t *bcp, void *ignored) 15587c478bd9Sstevel@tonic-gate { 15597c478bd9Sstevel@tonic-gate char c[MDB_SYM_NAMLEN]; 15607c478bd9Sstevel@tonic-gate GElf_Sym sym; 15617c478bd9Sstevel@tonic-gate int i; 15627c478bd9Sstevel@tonic-gate 15637c478bd9Sstevel@tonic-gate mdb_printf("%0?p %12llx ", addr, bcp->bc_timestamp); 15647c478bd9Sstevel@tonic-gate for (i = 0; i < bcp->bc_depth; i++) { 15657c478bd9Sstevel@tonic-gate if (mdb_lookup_by_addr(bcp->bc_stack[i], 15667c478bd9Sstevel@tonic-gate MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1) 15677c478bd9Sstevel@tonic-gate continue; 15687c478bd9Sstevel@tonic-gate if (strncmp(c, "kmem_", 5) == 0) 15697c478bd9Sstevel@tonic-gate continue; 15707c478bd9Sstevel@tonic-gate mdb_printf("%s+0x%lx", 15717c478bd9Sstevel@tonic-gate c, bcp->bc_stack[i] - (uintptr_t)sym.st_value); 15727c478bd9Sstevel@tonic-gate break; 15737c478bd9Sstevel@tonic-gate } 15747c478bd9Sstevel@tonic-gate mdb_printf("\n"); 15757c478bd9Sstevel@tonic-gate 15767c478bd9Sstevel@tonic-gate return (WALK_NEXT); 15777c478bd9Sstevel@tonic-gate } 15787c478bd9Sstevel@tonic-gate 15797c478bd9Sstevel@tonic-gate static int 15807c478bd9Sstevel@tonic-gate allocdby_common(uintptr_t addr, uint_t flags, const char *w) 15817c478bd9Sstevel@tonic-gate { 15827c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 15837c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 15847c478bd9Sstevel@tonic-gate 15857c478bd9Sstevel@tonic-gate mdb_printf("%-?s %12s %s\n", "BUFCTL", "TIMESTAMP", "CALLER"); 15867c478bd9Sstevel@tonic-gate 15877c478bd9Sstevel@tonic-gate if (mdb_pwalk(w, (mdb_walk_cb_t)allocdby_walk, NULL, addr) == -1) { 15887c478bd9Sstevel@tonic-gate mdb_warn("can't walk '%s' for %p", w, addr); 15897c478bd9Sstevel@tonic-gate return (DCMD_ERR); 15907c478bd9Sstevel@tonic-gate } 15917c478bd9Sstevel@tonic-gate 15927c478bd9Sstevel@tonic-gate return (DCMD_OK); 15937c478bd9Sstevel@tonic-gate } 15947c478bd9Sstevel@tonic-gate 15957c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 15967c478bd9Sstevel@tonic-gate int 15977c478bd9Sstevel@tonic-gate allocdby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 15987c478bd9Sstevel@tonic-gate { 15997c478bd9Sstevel@tonic-gate return (allocdby_common(addr, flags, "allocdby")); 16007c478bd9Sstevel@tonic-gate } 16017c478bd9Sstevel@tonic-gate 16027c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 16037c478bd9Sstevel@tonic-gate int 16047c478bd9Sstevel@tonic-gate freedby(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 16057c478bd9Sstevel@tonic-gate { 16067c478bd9Sstevel@tonic-gate return (allocdby_common(addr, flags, "freedby")); 16077c478bd9Sstevel@tonic-gate } 16087c478bd9Sstevel@tonic-gate 16097c478bd9Sstevel@tonic-gate /* 16107c478bd9Sstevel@tonic-gate * Return a string describing the address in relation to the given thread's 16117c478bd9Sstevel@tonic-gate * stack. 16127c478bd9Sstevel@tonic-gate * 16137c478bd9Sstevel@tonic-gate * - If the thread state is TS_FREE, return " (inactive interrupt thread)". 16147c478bd9Sstevel@tonic-gate * 16157c478bd9Sstevel@tonic-gate * - If the address is above the stack pointer, return an empty string 16167c478bd9Sstevel@tonic-gate * signifying that the address is active. 16177c478bd9Sstevel@tonic-gate * 16187c478bd9Sstevel@tonic-gate * - If the address is below the stack pointer, and the thread is not on proc, 16197c478bd9Sstevel@tonic-gate * return " (below sp)". 16207c478bd9Sstevel@tonic-gate * 16217c478bd9Sstevel@tonic-gate * - If the address is below the stack pointer, and the thread is on proc, 16227c478bd9Sstevel@tonic-gate * return " (possibly below sp)". Depending on context, we may or may not 16237c478bd9Sstevel@tonic-gate * have an accurate t_sp. 16247c478bd9Sstevel@tonic-gate */ 16257c478bd9Sstevel@tonic-gate static const char * 16267c478bd9Sstevel@tonic-gate stack_active(const kthread_t *t, uintptr_t addr) 16277c478bd9Sstevel@tonic-gate { 16287c478bd9Sstevel@tonic-gate uintptr_t panicstk; 16297c478bd9Sstevel@tonic-gate GElf_Sym sym; 16307c478bd9Sstevel@tonic-gate 16317c478bd9Sstevel@tonic-gate if (t->t_state == TS_FREE) 16327c478bd9Sstevel@tonic-gate return (" (inactive interrupt thread)"); 16337c478bd9Sstevel@tonic-gate 16347c478bd9Sstevel@tonic-gate /* 16357c478bd9Sstevel@tonic-gate * Check to see if we're on the panic stack. If so, ignore t_sp, as it 16367c478bd9Sstevel@tonic-gate * no longer relates to the thread's real stack. 16377c478bd9Sstevel@tonic-gate */ 16387c478bd9Sstevel@tonic-gate if (mdb_lookup_by_name("panic_stack", &sym) == 0) { 16397c478bd9Sstevel@tonic-gate panicstk = (uintptr_t)sym.st_value; 16407c478bd9Sstevel@tonic-gate 16417c478bd9Sstevel@tonic-gate if (t->t_sp >= panicstk && t->t_sp < panicstk + PANICSTKSIZE) 16427c478bd9Sstevel@tonic-gate return (""); 16437c478bd9Sstevel@tonic-gate } 16447c478bd9Sstevel@tonic-gate 16457c478bd9Sstevel@tonic-gate if (addr >= t->t_sp + STACK_BIAS) 16467c478bd9Sstevel@tonic-gate return (""); 16477c478bd9Sstevel@tonic-gate 16487c478bd9Sstevel@tonic-gate if (t->t_state == TS_ONPROC) 16497c478bd9Sstevel@tonic-gate return (" (possibly below sp)"); 16507c478bd9Sstevel@tonic-gate 16517c478bd9Sstevel@tonic-gate return (" (below sp)"); 16527c478bd9Sstevel@tonic-gate } 16537c478bd9Sstevel@tonic-gate 16547c478bd9Sstevel@tonic-gate typedef struct whatis { 16557c478bd9Sstevel@tonic-gate uintptr_t w_addr; 16567c478bd9Sstevel@tonic-gate const kmem_cache_t *w_cache; 16577c478bd9Sstevel@tonic-gate const vmem_t *w_vmem; 16587c478bd9Sstevel@tonic-gate size_t w_slab_align; 16597c478bd9Sstevel@tonic-gate int w_slab_found; 16607c478bd9Sstevel@tonic-gate int w_found; 16617c478bd9Sstevel@tonic-gate int w_kmem_lite_count; 16627c478bd9Sstevel@tonic-gate uint_t w_verbose; 16637c478bd9Sstevel@tonic-gate uint_t w_freemem; 16647c478bd9Sstevel@tonic-gate uint_t w_all; 16657c478bd9Sstevel@tonic-gate uint_t w_bufctl; 16667c478bd9Sstevel@tonic-gate uint_t w_idspace; 16677c478bd9Sstevel@tonic-gate } whatis_t; 16687c478bd9Sstevel@tonic-gate 16697c478bd9Sstevel@tonic-gate static void 16707c478bd9Sstevel@tonic-gate whatis_print_kmem(uintptr_t addr, uintptr_t baddr, whatis_t *w) 16717c478bd9Sstevel@tonic-gate { 16727c478bd9Sstevel@tonic-gate /* LINTED pointer cast may result in improper alignment */ 16737c478bd9Sstevel@tonic-gate uintptr_t btaddr = (uintptr_t)KMEM_BUFTAG(w->w_cache, addr); 16747c478bd9Sstevel@tonic-gate intptr_t stat; 16757c478bd9Sstevel@tonic-gate int count = 0; 16767c478bd9Sstevel@tonic-gate int i; 16777c478bd9Sstevel@tonic-gate pc_t callers[16]; 16787c478bd9Sstevel@tonic-gate 16797c478bd9Sstevel@tonic-gate if (w->w_cache->cache_flags & KMF_REDZONE) { 16807c478bd9Sstevel@tonic-gate kmem_buftag_t bt; 16817c478bd9Sstevel@tonic-gate 16827c478bd9Sstevel@tonic-gate if (mdb_vread(&bt, sizeof (bt), btaddr) == -1) 16837c478bd9Sstevel@tonic-gate goto done; 16847c478bd9Sstevel@tonic-gate 16857c478bd9Sstevel@tonic-gate stat = (intptr_t)bt.bt_bufctl ^ bt.bt_bxstat; 16867c478bd9Sstevel@tonic-gate 16877c478bd9Sstevel@tonic-gate if (stat != KMEM_BUFTAG_ALLOC && stat != KMEM_BUFTAG_FREE) 16887c478bd9Sstevel@tonic-gate goto done; 16897c478bd9Sstevel@tonic-gate 16907c478bd9Sstevel@tonic-gate /* 16917c478bd9Sstevel@tonic-gate * provide the bufctl ptr if it has useful information 16927c478bd9Sstevel@tonic-gate */ 16937c478bd9Sstevel@tonic-gate if (baddr == 0 && (w->w_cache->cache_flags & KMF_AUDIT)) 16947c478bd9Sstevel@tonic-gate baddr = (uintptr_t)bt.bt_bufctl; 16957c478bd9Sstevel@tonic-gate 16967c478bd9Sstevel@tonic-gate if (w->w_cache->cache_flags & KMF_LITE) { 16977c478bd9Sstevel@tonic-gate count = w->w_kmem_lite_count; 16987c478bd9Sstevel@tonic-gate 16997c478bd9Sstevel@tonic-gate if (count * sizeof (pc_t) > sizeof (callers)) 17007c478bd9Sstevel@tonic-gate count = 0; 17017c478bd9Sstevel@tonic-gate 17027c478bd9Sstevel@tonic-gate if (count > 0 && 17037c478bd9Sstevel@tonic-gate mdb_vread(callers, count * sizeof (pc_t), 17047c478bd9Sstevel@tonic-gate btaddr + 17057c478bd9Sstevel@tonic-gate offsetof(kmem_buftag_lite_t, bt_history)) == -1) 17067c478bd9Sstevel@tonic-gate count = 0; 17077c478bd9Sstevel@tonic-gate 17087c478bd9Sstevel@tonic-gate /* 17097c478bd9Sstevel@tonic-gate * skip unused callers 17107c478bd9Sstevel@tonic-gate */ 17117c478bd9Sstevel@tonic-gate while (count > 0 && callers[count - 1] == 17127c478bd9Sstevel@tonic-gate (pc_t)KMEM_UNINITIALIZED_PATTERN) 17137c478bd9Sstevel@tonic-gate count--; 17147c478bd9Sstevel@tonic-gate } 17157c478bd9Sstevel@tonic-gate } 17167c478bd9Sstevel@tonic-gate 17177c478bd9Sstevel@tonic-gate done: 17187c478bd9Sstevel@tonic-gate if (baddr == 0) 17197c478bd9Sstevel@tonic-gate mdb_printf("%p is %p+%p, %s from %s\n", 17207c478bd9Sstevel@tonic-gate w->w_addr, addr, w->w_addr - addr, 17217c478bd9Sstevel@tonic-gate w->w_freemem == FALSE ? "allocated" : "freed", 17227c478bd9Sstevel@tonic-gate w->w_cache->cache_name); 17237c478bd9Sstevel@tonic-gate else 17247c478bd9Sstevel@tonic-gate mdb_printf("%p is %p+%p, bufctl %p %s from %s\n", 17257c478bd9Sstevel@tonic-gate w->w_addr, addr, w->w_addr - addr, baddr, 17267c478bd9Sstevel@tonic-gate w->w_freemem == FALSE ? "allocated" : "freed", 17277c478bd9Sstevel@tonic-gate w->w_cache->cache_name); 17287c478bd9Sstevel@tonic-gate 17297c478bd9Sstevel@tonic-gate if (count > 0) { 17307c478bd9Sstevel@tonic-gate mdb_inc_indent(8); 17317c478bd9Sstevel@tonic-gate mdb_printf("recent caller%s: %a%s", (count != 1)? "s":"", 17327c478bd9Sstevel@tonic-gate callers[0], (count != 1)? ", ":"\n"); 17337c478bd9Sstevel@tonic-gate for (i = 1; i < count; i++) 17347c478bd9Sstevel@tonic-gate mdb_printf("%a%s", callers[i], 17357c478bd9Sstevel@tonic-gate (i + 1 < count)? ", ":"\n"); 17367c478bd9Sstevel@tonic-gate mdb_dec_indent(8); 17377c478bd9Sstevel@tonic-gate } 17387c478bd9Sstevel@tonic-gate } 17397c478bd9Sstevel@tonic-gate 17407c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 17417c478bd9Sstevel@tonic-gate static int 17427c478bd9Sstevel@tonic-gate whatis_walk_kmem(uintptr_t addr, void *ignored, whatis_t *w) 17437c478bd9Sstevel@tonic-gate { 17447c478bd9Sstevel@tonic-gate if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize) 17457c478bd9Sstevel@tonic-gate return (WALK_NEXT); 17467c478bd9Sstevel@tonic-gate 17477c478bd9Sstevel@tonic-gate whatis_print_kmem(addr, 0, w); 17487c478bd9Sstevel@tonic-gate w->w_found++; 17497c478bd9Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 17507c478bd9Sstevel@tonic-gate } 17517c478bd9Sstevel@tonic-gate 17527c478bd9Sstevel@tonic-gate static int 17537c478bd9Sstevel@tonic-gate whatis_walk_seg(uintptr_t addr, const vmem_seg_t *vs, whatis_t *w) 17547c478bd9Sstevel@tonic-gate { 17557c478bd9Sstevel@tonic-gate if (w->w_addr < vs->vs_start || w->w_addr >= vs->vs_end) 17567c478bd9Sstevel@tonic-gate return (WALK_NEXT); 17577c478bd9Sstevel@tonic-gate 17587c478bd9Sstevel@tonic-gate mdb_printf("%p is %p+%p ", w->w_addr, 17597c478bd9Sstevel@tonic-gate vs->vs_start, w->w_addr - vs->vs_start); 17607c478bd9Sstevel@tonic-gate 17617c478bd9Sstevel@tonic-gate /* 17627c478bd9Sstevel@tonic-gate * Always provide the vmem_seg pointer if it has a stack trace. 17637c478bd9Sstevel@tonic-gate */ 17647c478bd9Sstevel@tonic-gate if (w->w_bufctl == TRUE || 17657c478bd9Sstevel@tonic-gate (vs->vs_type == VMEM_ALLOC && vs->vs_depth != 0)) { 17667c478bd9Sstevel@tonic-gate mdb_printf("(vmem_seg %p) ", addr); 17677c478bd9Sstevel@tonic-gate } 17687c478bd9Sstevel@tonic-gate 17697c478bd9Sstevel@tonic-gate mdb_printf("%sfrom %s vmem arena\n", w->w_freemem == TRUE ? 17707c478bd9Sstevel@tonic-gate "freed " : "", w->w_vmem->vm_name); 17717c478bd9Sstevel@tonic-gate 17727c478bd9Sstevel@tonic-gate w->w_found++; 17737c478bd9Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 17747c478bd9Sstevel@tonic-gate } 17757c478bd9Sstevel@tonic-gate 17767c478bd9Sstevel@tonic-gate static int 17777c478bd9Sstevel@tonic-gate whatis_walk_vmem(uintptr_t addr, const vmem_t *vmem, whatis_t *w) 17787c478bd9Sstevel@tonic-gate { 17797c478bd9Sstevel@tonic-gate const char *nm = vmem->vm_name; 17807c478bd9Sstevel@tonic-gate w->w_vmem = vmem; 17817c478bd9Sstevel@tonic-gate w->w_freemem = FALSE; 17827c478bd9Sstevel@tonic-gate 17837c478bd9Sstevel@tonic-gate if (((vmem->vm_cflags & VMC_IDENTIFIER) != 0) ^ w->w_idspace) 17847c478bd9Sstevel@tonic-gate return (WALK_NEXT); 17857c478bd9Sstevel@tonic-gate 17867c478bd9Sstevel@tonic-gate if (w->w_verbose) 17877c478bd9Sstevel@tonic-gate mdb_printf("Searching vmem arena %s...\n", nm); 17887c478bd9Sstevel@tonic-gate 17897c478bd9Sstevel@tonic-gate if (mdb_pwalk("vmem_alloc", 17907c478bd9Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) { 17917c478bd9Sstevel@tonic-gate mdb_warn("can't walk vmem seg for %p", addr); 17927c478bd9Sstevel@tonic-gate return (WALK_NEXT); 17937c478bd9Sstevel@tonic-gate } 17947c478bd9Sstevel@tonic-gate 17957c478bd9Sstevel@tonic-gate if (w->w_found && w->w_all == FALSE) 17967c478bd9Sstevel@tonic-gate return (WALK_DONE); 17977c478bd9Sstevel@tonic-gate 17987c478bd9Sstevel@tonic-gate if (w->w_verbose) 17997c478bd9Sstevel@tonic-gate mdb_printf("Searching vmem arena %s for free virtual...\n", nm); 18007c478bd9Sstevel@tonic-gate 18017c478bd9Sstevel@tonic-gate w->w_freemem = TRUE; 18027c478bd9Sstevel@tonic-gate 18037c478bd9Sstevel@tonic-gate if (mdb_pwalk("vmem_free", 18047c478bd9Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_seg, w, addr) == -1) { 18057c478bd9Sstevel@tonic-gate mdb_warn("can't walk vmem seg for %p", addr); 18067c478bd9Sstevel@tonic-gate return (WALK_NEXT); 18077c478bd9Sstevel@tonic-gate } 18087c478bd9Sstevel@tonic-gate 18097c478bd9Sstevel@tonic-gate return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT); 18107c478bd9Sstevel@tonic-gate } 18117c478bd9Sstevel@tonic-gate 18127c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 18137c478bd9Sstevel@tonic-gate static int 18147c478bd9Sstevel@tonic-gate whatis_walk_bufctl(uintptr_t baddr, const kmem_bufctl_t *bcp, whatis_t *w) 18157c478bd9Sstevel@tonic-gate { 18167c478bd9Sstevel@tonic-gate uintptr_t addr; 18177c478bd9Sstevel@tonic-gate 18187c478bd9Sstevel@tonic-gate if (bcp == NULL) 18197c478bd9Sstevel@tonic-gate return (WALK_NEXT); 18207c478bd9Sstevel@tonic-gate 18217c478bd9Sstevel@tonic-gate addr = (uintptr_t)bcp->bc_addr; 18227c478bd9Sstevel@tonic-gate 18237c478bd9Sstevel@tonic-gate if (w->w_addr < addr || w->w_addr >= addr + w->w_cache->cache_bufsize) 18247c478bd9Sstevel@tonic-gate return (WALK_NEXT); 18257c478bd9Sstevel@tonic-gate 18267c478bd9Sstevel@tonic-gate whatis_print_kmem(addr, baddr, w); 18277c478bd9Sstevel@tonic-gate w->w_found++; 18287c478bd9Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 18297c478bd9Sstevel@tonic-gate } 18307c478bd9Sstevel@tonic-gate 18317c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 18327c478bd9Sstevel@tonic-gate static int 18337c478bd9Sstevel@tonic-gate whatis_walk_slab(uintptr_t saddr, const kmem_slab_t *sp, whatis_t *w) 18347c478bd9Sstevel@tonic-gate { 18357c478bd9Sstevel@tonic-gate uintptr_t base = P2ALIGN((uintptr_t)sp->slab_base, w->w_slab_align); 18367c478bd9Sstevel@tonic-gate 18377c478bd9Sstevel@tonic-gate if ((w->w_addr - base) >= w->w_cache->cache_slabsize) 18387c478bd9Sstevel@tonic-gate return (WALK_NEXT); 18397c478bd9Sstevel@tonic-gate 18407c478bd9Sstevel@tonic-gate w->w_slab_found++; 18417c478bd9Sstevel@tonic-gate return (WALK_DONE); 18427c478bd9Sstevel@tonic-gate } 18437c478bd9Sstevel@tonic-gate 18447c478bd9Sstevel@tonic-gate static int 18457c478bd9Sstevel@tonic-gate whatis_walk_cache(uintptr_t addr, const kmem_cache_t *c, whatis_t *w) 18467c478bd9Sstevel@tonic-gate { 18477c478bd9Sstevel@tonic-gate char *walk, *freewalk; 18487c478bd9Sstevel@tonic-gate mdb_walk_cb_t func; 18497c478bd9Sstevel@tonic-gate vmem_t *vmp = c->cache_arena; 18507c478bd9Sstevel@tonic-gate 18517c478bd9Sstevel@tonic-gate if (((c->cache_flags & VMC_IDENTIFIER) != 0) ^ w->w_idspace) 18527c478bd9Sstevel@tonic-gate return (WALK_NEXT); 18537c478bd9Sstevel@tonic-gate 18547c478bd9Sstevel@tonic-gate if (w->w_bufctl == FALSE) { 18557c478bd9Sstevel@tonic-gate walk = "kmem"; 18567c478bd9Sstevel@tonic-gate freewalk = "freemem"; 18577c478bd9Sstevel@tonic-gate func = (mdb_walk_cb_t)whatis_walk_kmem; 18587c478bd9Sstevel@tonic-gate } else { 18597c478bd9Sstevel@tonic-gate walk = "bufctl"; 18607c478bd9Sstevel@tonic-gate freewalk = "freectl"; 18617c478bd9Sstevel@tonic-gate func = (mdb_walk_cb_t)whatis_walk_bufctl; 18627c478bd9Sstevel@tonic-gate } 18637c478bd9Sstevel@tonic-gate 18647c478bd9Sstevel@tonic-gate w->w_cache = c; 18657c478bd9Sstevel@tonic-gate 18667c478bd9Sstevel@tonic-gate if (w->w_verbose) 18677c478bd9Sstevel@tonic-gate mdb_printf("Searching %s's slabs...\n", c->cache_name); 18687c478bd9Sstevel@tonic-gate 18697c478bd9Sstevel@tonic-gate /* 18707c478bd9Sstevel@tonic-gate * Verify that the address is in one of the cache's slabs. If not, 18717c478bd9Sstevel@tonic-gate * we can skip the more expensive walkers. (this is purely a 18727c478bd9Sstevel@tonic-gate * heuristic -- as long as there are no false-negatives, we'll be fine) 18737c478bd9Sstevel@tonic-gate * 18747c478bd9Sstevel@tonic-gate * We try to get the cache's arena's quantum, since to accurately 18757c478bd9Sstevel@tonic-gate * get the base of a slab, you have to align it to the quantum. If 18767c478bd9Sstevel@tonic-gate * it doesn't look sensible, we fall back to not aligning. 18777c478bd9Sstevel@tonic-gate */ 18787c478bd9Sstevel@tonic-gate if (mdb_vread(&w->w_slab_align, sizeof (w->w_slab_align), 18797c478bd9Sstevel@tonic-gate (uintptr_t)&vmp->vm_quantum) == -1) { 18807c478bd9Sstevel@tonic-gate mdb_warn("unable to read %p->cache_arena->vm_quantum", c); 18817c478bd9Sstevel@tonic-gate w->w_slab_align = 1; 18827c478bd9Sstevel@tonic-gate } 18837c478bd9Sstevel@tonic-gate 18847c478bd9Sstevel@tonic-gate if ((c->cache_slabsize < w->w_slab_align) || w->w_slab_align == 0 || 18857c478bd9Sstevel@tonic-gate (w->w_slab_align & (w->w_slab_align - 1))) { 18867c478bd9Sstevel@tonic-gate mdb_warn("%p's arena has invalid quantum (0x%p)\n", c, 18877c478bd9Sstevel@tonic-gate w->w_slab_align); 18887c478bd9Sstevel@tonic-gate w->w_slab_align = 1; 18897c478bd9Sstevel@tonic-gate } 18907c478bd9Sstevel@tonic-gate 18917c478bd9Sstevel@tonic-gate w->w_slab_found = 0; 18927c478bd9Sstevel@tonic-gate if (mdb_pwalk("kmem_slab", (mdb_walk_cb_t)whatis_walk_slab, w, 18937c478bd9Sstevel@tonic-gate addr) == -1) { 18947c478bd9Sstevel@tonic-gate mdb_warn("can't find kmem_slab walker"); 18957c478bd9Sstevel@tonic-gate return (WALK_DONE); 18967c478bd9Sstevel@tonic-gate } 18977c478bd9Sstevel@tonic-gate if (w->w_slab_found == 0) 18987c478bd9Sstevel@tonic-gate return (WALK_NEXT); 18997c478bd9Sstevel@tonic-gate 19007c478bd9Sstevel@tonic-gate if (c->cache_flags & KMF_LITE) { 19017c478bd9Sstevel@tonic-gate if (mdb_readvar(&w->w_kmem_lite_count, 19027c478bd9Sstevel@tonic-gate "kmem_lite_count") == -1 || w->w_kmem_lite_count > 16) 19037c478bd9Sstevel@tonic-gate w->w_kmem_lite_count = 0; 19047c478bd9Sstevel@tonic-gate } 19057c478bd9Sstevel@tonic-gate 19067c478bd9Sstevel@tonic-gate if (w->w_verbose) 19077c478bd9Sstevel@tonic-gate mdb_printf("Searching %s...\n", c->cache_name); 19087c478bd9Sstevel@tonic-gate 19097c478bd9Sstevel@tonic-gate w->w_freemem = FALSE; 19107c478bd9Sstevel@tonic-gate 19117c478bd9Sstevel@tonic-gate if (mdb_pwalk(walk, func, w, addr) == -1) { 19127c478bd9Sstevel@tonic-gate mdb_warn("can't find %s walker", walk); 19137c478bd9Sstevel@tonic-gate return (WALK_DONE); 19147c478bd9Sstevel@tonic-gate } 19157c478bd9Sstevel@tonic-gate 19167c478bd9Sstevel@tonic-gate if (w->w_found && w->w_all == FALSE) 19177c478bd9Sstevel@tonic-gate return (WALK_DONE); 19187c478bd9Sstevel@tonic-gate 19197c478bd9Sstevel@tonic-gate /* 19207c478bd9Sstevel@tonic-gate * We have searched for allocated memory; now search for freed memory. 19217c478bd9Sstevel@tonic-gate */ 19227c478bd9Sstevel@tonic-gate if (w->w_verbose) 19237c478bd9Sstevel@tonic-gate mdb_printf("Searching %s for free memory...\n", c->cache_name); 19247c478bd9Sstevel@tonic-gate 19257c478bd9Sstevel@tonic-gate w->w_freemem = TRUE; 19267c478bd9Sstevel@tonic-gate 19277c478bd9Sstevel@tonic-gate if (mdb_pwalk(freewalk, func, w, addr) == -1) { 19287c478bd9Sstevel@tonic-gate mdb_warn("can't find %s walker", freewalk); 19297c478bd9Sstevel@tonic-gate return (WALK_DONE); 19307c478bd9Sstevel@tonic-gate } 19317c478bd9Sstevel@tonic-gate 19327c478bd9Sstevel@tonic-gate return (w->w_found && w->w_all == FALSE ? WALK_DONE : WALK_NEXT); 19337c478bd9Sstevel@tonic-gate } 19347c478bd9Sstevel@tonic-gate 19357c478bd9Sstevel@tonic-gate static int 19367c478bd9Sstevel@tonic-gate whatis_walk_touch(uintptr_t addr, const kmem_cache_t *c, whatis_t *w) 19377c478bd9Sstevel@tonic-gate { 19387c478bd9Sstevel@tonic-gate if (c->cache_cflags & KMC_NOTOUCH) 19397c478bd9Sstevel@tonic-gate return (WALK_NEXT); 19407c478bd9Sstevel@tonic-gate 19417c478bd9Sstevel@tonic-gate return (whatis_walk_cache(addr, c, w)); 19427c478bd9Sstevel@tonic-gate } 19437c478bd9Sstevel@tonic-gate 19447c478bd9Sstevel@tonic-gate static int 19457c478bd9Sstevel@tonic-gate whatis_walk_notouch(uintptr_t addr, const kmem_cache_t *c, whatis_t *w) 19467c478bd9Sstevel@tonic-gate { 19477c478bd9Sstevel@tonic-gate if (!(c->cache_cflags & KMC_NOTOUCH)) 19487c478bd9Sstevel@tonic-gate return (WALK_NEXT); 19497c478bd9Sstevel@tonic-gate 19507c478bd9Sstevel@tonic-gate return (whatis_walk_cache(addr, c, w)); 19517c478bd9Sstevel@tonic-gate } 19527c478bd9Sstevel@tonic-gate 19537c478bd9Sstevel@tonic-gate static int 19547c478bd9Sstevel@tonic-gate whatis_walk_thread(uintptr_t addr, const kthread_t *t, whatis_t *w) 19557c478bd9Sstevel@tonic-gate { 19567c478bd9Sstevel@tonic-gate /* 19577c478bd9Sstevel@tonic-gate * Often, one calls ::whatis on an address from a thread structure. 19587c478bd9Sstevel@tonic-gate * We use this opportunity to short circuit this case... 19597c478bd9Sstevel@tonic-gate */ 19607c478bd9Sstevel@tonic-gate if (w->w_addr >= addr && w->w_addr < addr + sizeof (kthread_t)) { 19617c478bd9Sstevel@tonic-gate mdb_printf("%p is %p+%p, allocated as a thread structure\n", 19627c478bd9Sstevel@tonic-gate w->w_addr, addr, w->w_addr - addr); 19637c478bd9Sstevel@tonic-gate w->w_found++; 19647c478bd9Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 19657c478bd9Sstevel@tonic-gate } 19667c478bd9Sstevel@tonic-gate 19677c478bd9Sstevel@tonic-gate if (w->w_addr < (uintptr_t)t->t_stkbase || 19687c478bd9Sstevel@tonic-gate w->w_addr > (uintptr_t)t->t_stk) 19697c478bd9Sstevel@tonic-gate return (WALK_NEXT); 19707c478bd9Sstevel@tonic-gate 19717c478bd9Sstevel@tonic-gate if (t->t_stkbase == NULL) 19727c478bd9Sstevel@tonic-gate return (WALK_NEXT); 19737c478bd9Sstevel@tonic-gate 19747c478bd9Sstevel@tonic-gate mdb_printf("%p is in thread %p's stack%s\n", w->w_addr, addr, 19757c478bd9Sstevel@tonic-gate stack_active(t, w->w_addr)); 19767c478bd9Sstevel@tonic-gate 19777c478bd9Sstevel@tonic-gate w->w_found++; 19787c478bd9Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 19797c478bd9Sstevel@tonic-gate } 19807c478bd9Sstevel@tonic-gate 19817c478bd9Sstevel@tonic-gate static int 19827c478bd9Sstevel@tonic-gate whatis_walk_modctl(uintptr_t addr, const struct modctl *m, whatis_t *w) 19837c478bd9Sstevel@tonic-gate { 19847c478bd9Sstevel@tonic-gate struct module mod; 19857c478bd9Sstevel@tonic-gate char name[MODMAXNAMELEN], *where; 19867c478bd9Sstevel@tonic-gate char c[MDB_SYM_NAMLEN]; 19877c478bd9Sstevel@tonic-gate Shdr shdr; 19887c478bd9Sstevel@tonic-gate GElf_Sym sym; 19897c478bd9Sstevel@tonic-gate 19907c478bd9Sstevel@tonic-gate if (m->mod_mp == NULL) 19917c478bd9Sstevel@tonic-gate return (WALK_NEXT); 19927c478bd9Sstevel@tonic-gate 19937c478bd9Sstevel@tonic-gate if (mdb_vread(&mod, sizeof (mod), (uintptr_t)m->mod_mp) == -1) { 19947c478bd9Sstevel@tonic-gate mdb_warn("couldn't read modctl %p's module", addr); 19957c478bd9Sstevel@tonic-gate return (WALK_NEXT); 19967c478bd9Sstevel@tonic-gate } 19977c478bd9Sstevel@tonic-gate 19987c478bd9Sstevel@tonic-gate if (w->w_addr >= (uintptr_t)mod.text && 19997c478bd9Sstevel@tonic-gate w->w_addr < (uintptr_t)mod.text + mod.text_size) { 20007c478bd9Sstevel@tonic-gate where = "text segment"; 20017c478bd9Sstevel@tonic-gate goto found; 20027c478bd9Sstevel@tonic-gate } 20037c478bd9Sstevel@tonic-gate 20047c478bd9Sstevel@tonic-gate if (w->w_addr >= (uintptr_t)mod.data && 20057c478bd9Sstevel@tonic-gate w->w_addr < (uintptr_t)mod.data + mod.data_size) { 20067c478bd9Sstevel@tonic-gate where = "data segment"; 20077c478bd9Sstevel@tonic-gate goto found; 20087c478bd9Sstevel@tonic-gate } 20097c478bd9Sstevel@tonic-gate 20107c478bd9Sstevel@tonic-gate if (w->w_addr >= (uintptr_t)mod.bss && 20117c478bd9Sstevel@tonic-gate w->w_addr < (uintptr_t)mod.bss + mod.bss_size) { 20127c478bd9Sstevel@tonic-gate where = "bss"; 20137c478bd9Sstevel@tonic-gate goto found; 20147c478bd9Sstevel@tonic-gate } 20157c478bd9Sstevel@tonic-gate 20167c478bd9Sstevel@tonic-gate if (mdb_vread(&shdr, sizeof (shdr), (uintptr_t)mod.symhdr) == -1) { 20177c478bd9Sstevel@tonic-gate mdb_warn("couldn't read symbol header for %p's module", addr); 20187c478bd9Sstevel@tonic-gate return (WALK_NEXT); 20197c478bd9Sstevel@tonic-gate } 20207c478bd9Sstevel@tonic-gate 20217c478bd9Sstevel@tonic-gate if (w->w_addr >= (uintptr_t)mod.symtbl && w->w_addr < 20227c478bd9Sstevel@tonic-gate (uintptr_t)mod.symtbl + (uintptr_t)mod.nsyms * shdr.sh_entsize) { 20237c478bd9Sstevel@tonic-gate where = "symtab"; 20247c478bd9Sstevel@tonic-gate goto found; 20257c478bd9Sstevel@tonic-gate } 20267c478bd9Sstevel@tonic-gate 20277c478bd9Sstevel@tonic-gate if (w->w_addr >= (uintptr_t)mod.symspace && 20287c478bd9Sstevel@tonic-gate w->w_addr < (uintptr_t)mod.symspace + (uintptr_t)mod.symsize) { 20297c478bd9Sstevel@tonic-gate where = "symspace"; 20307c478bd9Sstevel@tonic-gate goto found; 20317c478bd9Sstevel@tonic-gate } 20327c478bd9Sstevel@tonic-gate 20337c478bd9Sstevel@tonic-gate return (WALK_NEXT); 20347c478bd9Sstevel@tonic-gate 20357c478bd9Sstevel@tonic-gate found: 20367c478bd9Sstevel@tonic-gate if (mdb_readstr(name, sizeof (name), (uintptr_t)m->mod_modname) == -1) 20377c478bd9Sstevel@tonic-gate (void) mdb_snprintf(name, sizeof (name), "0x%p", addr); 20387c478bd9Sstevel@tonic-gate 20397c478bd9Sstevel@tonic-gate mdb_printf("%p is ", w->w_addr); 20407c478bd9Sstevel@tonic-gate 20417c478bd9Sstevel@tonic-gate /* 20427c478bd9Sstevel@tonic-gate * If we found this address in a module, then there's a chance that 20437c478bd9Sstevel@tonic-gate * it's actually a named symbol. Try the symbol lookup. 20447c478bd9Sstevel@tonic-gate */ 20457c478bd9Sstevel@tonic-gate if (mdb_lookup_by_addr(w->w_addr, MDB_SYM_FUZZY, c, sizeof (c), 20467c478bd9Sstevel@tonic-gate &sym) != -1 && w->w_addr >= (uintptr_t)sym.st_value && 20477c478bd9Sstevel@tonic-gate w->w_addr < (uintptr_t)sym.st_value + sym.st_size) { 20487c478bd9Sstevel@tonic-gate mdb_printf("%s+%lx ", c, w->w_addr - (uintptr_t)sym.st_value); 20497c478bd9Sstevel@tonic-gate } 20507c478bd9Sstevel@tonic-gate 20517c478bd9Sstevel@tonic-gate mdb_printf("in %s's %s\n", name, where); 20527c478bd9Sstevel@tonic-gate 20537c478bd9Sstevel@tonic-gate w->w_found++; 20547c478bd9Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 20557c478bd9Sstevel@tonic-gate } 20567c478bd9Sstevel@tonic-gate 20577c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 20587c478bd9Sstevel@tonic-gate static int 20597c478bd9Sstevel@tonic-gate whatis_walk_page(uintptr_t addr, const void *ignored, whatis_t *w) 20607c478bd9Sstevel@tonic-gate { 20617c478bd9Sstevel@tonic-gate static int machsize = 0; 20627c478bd9Sstevel@tonic-gate mdb_ctf_id_t id; 20637c478bd9Sstevel@tonic-gate 20647c478bd9Sstevel@tonic-gate if (machsize == 0) { 20657c478bd9Sstevel@tonic-gate if (mdb_ctf_lookup_by_name("unix`page_t", &id) == 0) 20667c478bd9Sstevel@tonic-gate machsize = mdb_ctf_type_size(id); 20677c478bd9Sstevel@tonic-gate else { 20687c478bd9Sstevel@tonic-gate mdb_warn("could not get size of page_t"); 20697c478bd9Sstevel@tonic-gate machsize = sizeof (page_t); 20707c478bd9Sstevel@tonic-gate } 20717c478bd9Sstevel@tonic-gate } 20727c478bd9Sstevel@tonic-gate 20737c478bd9Sstevel@tonic-gate if (w->w_addr < addr || w->w_addr >= addr + machsize) 20747c478bd9Sstevel@tonic-gate return (WALK_NEXT); 20757c478bd9Sstevel@tonic-gate 20767c478bd9Sstevel@tonic-gate mdb_printf("%p is %p+%p, allocated as a page structure\n", 20777c478bd9Sstevel@tonic-gate w->w_addr, addr, w->w_addr - addr); 20787c478bd9Sstevel@tonic-gate 20797c478bd9Sstevel@tonic-gate w->w_found++; 20807c478bd9Sstevel@tonic-gate return (w->w_all == TRUE ? WALK_NEXT : WALK_DONE); 20817c478bd9Sstevel@tonic-gate } 20827c478bd9Sstevel@tonic-gate 20837c478bd9Sstevel@tonic-gate int 20847c478bd9Sstevel@tonic-gate whatis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 20857c478bd9Sstevel@tonic-gate { 20867c478bd9Sstevel@tonic-gate whatis_t w; 20877c478bd9Sstevel@tonic-gate 20887c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 20897c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 20907c478bd9Sstevel@tonic-gate 20917c478bd9Sstevel@tonic-gate w.w_verbose = FALSE; 20927c478bd9Sstevel@tonic-gate w.w_bufctl = FALSE; 20937c478bd9Sstevel@tonic-gate w.w_all = FALSE; 20947c478bd9Sstevel@tonic-gate w.w_idspace = FALSE; 20957c478bd9Sstevel@tonic-gate 20967c478bd9Sstevel@tonic-gate if (mdb_getopts(argc, argv, 20977c478bd9Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &w.w_verbose, 20987c478bd9Sstevel@tonic-gate 'a', MDB_OPT_SETBITS, TRUE, &w.w_all, 20997c478bd9Sstevel@tonic-gate 'i', MDB_OPT_SETBITS, TRUE, &w.w_idspace, 21007c478bd9Sstevel@tonic-gate 'b', MDB_OPT_SETBITS, TRUE, &w.w_bufctl, NULL) != argc) 21017c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 21027c478bd9Sstevel@tonic-gate 21037c478bd9Sstevel@tonic-gate w.w_addr = addr; 21047c478bd9Sstevel@tonic-gate w.w_found = 0; 21057c478bd9Sstevel@tonic-gate 21067c478bd9Sstevel@tonic-gate if (w.w_verbose) 21077c478bd9Sstevel@tonic-gate mdb_printf("Searching modules...\n"); 21087c478bd9Sstevel@tonic-gate 21097c478bd9Sstevel@tonic-gate if (!w.w_idspace) { 21107c478bd9Sstevel@tonic-gate if (mdb_walk("modctl", (mdb_walk_cb_t)whatis_walk_modctl, &w) 21117c478bd9Sstevel@tonic-gate == -1) { 21127c478bd9Sstevel@tonic-gate mdb_warn("couldn't find modctl walker"); 21137c478bd9Sstevel@tonic-gate return (DCMD_ERR); 21147c478bd9Sstevel@tonic-gate } 21157c478bd9Sstevel@tonic-gate 21167c478bd9Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 21177c478bd9Sstevel@tonic-gate return (DCMD_OK); 21187c478bd9Sstevel@tonic-gate 21197c478bd9Sstevel@tonic-gate /* 21207c478bd9Sstevel@tonic-gate * Now search all thread stacks. Yes, this is a little weak; we 21217c478bd9Sstevel@tonic-gate * can save a lot of work by first checking to see if the 21227c478bd9Sstevel@tonic-gate * address is in segkp vs. segkmem. But hey, computers are 21237c478bd9Sstevel@tonic-gate * fast. 21247c478bd9Sstevel@tonic-gate */ 21257c478bd9Sstevel@tonic-gate if (w.w_verbose) 21267c478bd9Sstevel@tonic-gate mdb_printf("Searching threads...\n"); 21277c478bd9Sstevel@tonic-gate 21287c478bd9Sstevel@tonic-gate if (mdb_walk("thread", (mdb_walk_cb_t)whatis_walk_thread, &w) 21297c478bd9Sstevel@tonic-gate == -1) { 21307c478bd9Sstevel@tonic-gate mdb_warn("couldn't find thread walker"); 21317c478bd9Sstevel@tonic-gate return (DCMD_ERR); 21327c478bd9Sstevel@tonic-gate } 21337c478bd9Sstevel@tonic-gate 21347c478bd9Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 21357c478bd9Sstevel@tonic-gate return (DCMD_OK); 21367c478bd9Sstevel@tonic-gate 21377c478bd9Sstevel@tonic-gate if (w.w_verbose) 21387c478bd9Sstevel@tonic-gate mdb_printf("Searching page structures...\n"); 21397c478bd9Sstevel@tonic-gate 21407c478bd9Sstevel@tonic-gate if (mdb_walk("page", (mdb_walk_cb_t)whatis_walk_page, &w) 21417c478bd9Sstevel@tonic-gate == -1) { 21427c478bd9Sstevel@tonic-gate mdb_warn("couldn't find page walker"); 21437c478bd9Sstevel@tonic-gate return (DCMD_ERR); 21447c478bd9Sstevel@tonic-gate } 21457c478bd9Sstevel@tonic-gate 21467c478bd9Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 21477c478bd9Sstevel@tonic-gate return (DCMD_OK); 21487c478bd9Sstevel@tonic-gate } 21497c478bd9Sstevel@tonic-gate 21507c478bd9Sstevel@tonic-gate if (mdb_walk("kmem_cache", 21517c478bd9Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_touch, &w) == -1) { 21527c478bd9Sstevel@tonic-gate mdb_warn("couldn't find kmem_cache walker"); 21537c478bd9Sstevel@tonic-gate return (DCMD_ERR); 21547c478bd9Sstevel@tonic-gate } 21557c478bd9Sstevel@tonic-gate 21567c478bd9Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 21577c478bd9Sstevel@tonic-gate return (DCMD_OK); 21587c478bd9Sstevel@tonic-gate 21597c478bd9Sstevel@tonic-gate if (mdb_walk("kmem_cache", 21607c478bd9Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_notouch, &w) == -1) { 21617c478bd9Sstevel@tonic-gate mdb_warn("couldn't find kmem_cache walker"); 21627c478bd9Sstevel@tonic-gate return (DCMD_ERR); 21637c478bd9Sstevel@tonic-gate } 21647c478bd9Sstevel@tonic-gate 21657c478bd9Sstevel@tonic-gate if (w.w_found && w.w_all == FALSE) 21667c478bd9Sstevel@tonic-gate return (DCMD_OK); 21677c478bd9Sstevel@tonic-gate 21687c478bd9Sstevel@tonic-gate if (mdb_walk("vmem_postfix", 21697c478bd9Sstevel@tonic-gate (mdb_walk_cb_t)whatis_walk_vmem, &w) == -1) { 21707c478bd9Sstevel@tonic-gate mdb_warn("couldn't find vmem_postfix walker"); 21717c478bd9Sstevel@tonic-gate return (DCMD_ERR); 21727c478bd9Sstevel@tonic-gate } 21737c478bd9Sstevel@tonic-gate 21747c478bd9Sstevel@tonic-gate if (w.w_found == 0) 21757c478bd9Sstevel@tonic-gate mdb_printf("%p is unknown\n", addr); 21767c478bd9Sstevel@tonic-gate 21777c478bd9Sstevel@tonic-gate return (DCMD_OK); 21787c478bd9Sstevel@tonic-gate } 21797c478bd9Sstevel@tonic-gate 21807c478bd9Sstevel@tonic-gate void 21817c478bd9Sstevel@tonic-gate whatis_help(void) 21827c478bd9Sstevel@tonic-gate { 21837c478bd9Sstevel@tonic-gate mdb_printf( 21847c478bd9Sstevel@tonic-gate "Given a virtual address, attempt to determine where it came\n" 21857c478bd9Sstevel@tonic-gate "from.\n" 21867c478bd9Sstevel@tonic-gate "\n" 21877c478bd9Sstevel@tonic-gate "\t-v\tVerbose output; display caches/arenas/etc as they are\n" 21887c478bd9Sstevel@tonic-gate "\t\tsearched\n" 21897c478bd9Sstevel@tonic-gate "\t-a\tFind all possible sources. Default behavior is to stop at\n" 21907c478bd9Sstevel@tonic-gate "\t\tthe first (most specific) source.\n" 21917c478bd9Sstevel@tonic-gate "\t-i\tSearch only identifier arenas and caches. By default\n" 21927c478bd9Sstevel@tonic-gate "\t\tthese are ignored.\n" 21937c478bd9Sstevel@tonic-gate "\t-b\tReport bufctls and vmem_segs for matches in kmem and vmem,\n" 21947c478bd9Sstevel@tonic-gate "\t\trespectively. Warning: if the buffer exists, but does not\n" 21957c478bd9Sstevel@tonic-gate "\t\thave a bufctl, it will not be reported.\n"); 21967c478bd9Sstevel@tonic-gate } 21977c478bd9Sstevel@tonic-gate 21987c478bd9Sstevel@tonic-gate typedef struct kmem_log_cpu { 21997c478bd9Sstevel@tonic-gate uintptr_t kmc_low; 22007c478bd9Sstevel@tonic-gate uintptr_t kmc_high; 22017c478bd9Sstevel@tonic-gate } kmem_log_cpu_t; 22027c478bd9Sstevel@tonic-gate 22037c478bd9Sstevel@tonic-gate typedef struct kmem_log_data { 22047c478bd9Sstevel@tonic-gate uintptr_t kmd_addr; 22057c478bd9Sstevel@tonic-gate kmem_log_cpu_t *kmd_cpu; 22067c478bd9Sstevel@tonic-gate } kmem_log_data_t; 22077c478bd9Sstevel@tonic-gate 22087c478bd9Sstevel@tonic-gate int 22097c478bd9Sstevel@tonic-gate kmem_log_walk(uintptr_t addr, const kmem_bufctl_audit_t *b, 22107c478bd9Sstevel@tonic-gate kmem_log_data_t *kmd) 22117c478bd9Sstevel@tonic-gate { 22127c478bd9Sstevel@tonic-gate int i; 22137c478bd9Sstevel@tonic-gate kmem_log_cpu_t *kmc = kmd->kmd_cpu; 22147c478bd9Sstevel@tonic-gate size_t bufsize; 22157c478bd9Sstevel@tonic-gate 22167c478bd9Sstevel@tonic-gate for (i = 0; i < NCPU; i++) { 22177c478bd9Sstevel@tonic-gate if (addr >= kmc[i].kmc_low && addr < kmc[i].kmc_high) 22187c478bd9Sstevel@tonic-gate break; 22197c478bd9Sstevel@tonic-gate } 22207c478bd9Sstevel@tonic-gate 22217c478bd9Sstevel@tonic-gate if (kmd->kmd_addr) { 22227c478bd9Sstevel@tonic-gate if (b->bc_cache == NULL) 22237c478bd9Sstevel@tonic-gate return (WALK_NEXT); 22247c478bd9Sstevel@tonic-gate 22257c478bd9Sstevel@tonic-gate if (mdb_vread(&bufsize, sizeof (bufsize), 22267c478bd9Sstevel@tonic-gate (uintptr_t)&b->bc_cache->cache_bufsize) == -1) { 22277c478bd9Sstevel@tonic-gate mdb_warn( 22287c478bd9Sstevel@tonic-gate "failed to read cache_bufsize for cache at %p", 22297c478bd9Sstevel@tonic-gate b->bc_cache); 22307c478bd9Sstevel@tonic-gate return (WALK_ERR); 22317c478bd9Sstevel@tonic-gate } 22327c478bd9Sstevel@tonic-gate 22337c478bd9Sstevel@tonic-gate if (kmd->kmd_addr < (uintptr_t)b->bc_addr || 22347c478bd9Sstevel@tonic-gate kmd->kmd_addr >= (uintptr_t)b->bc_addr + bufsize) 22357c478bd9Sstevel@tonic-gate return (WALK_NEXT); 22367c478bd9Sstevel@tonic-gate } 22377c478bd9Sstevel@tonic-gate 22387c478bd9Sstevel@tonic-gate if (i == NCPU) 22397c478bd9Sstevel@tonic-gate mdb_printf(" "); 22407c478bd9Sstevel@tonic-gate else 22417c478bd9Sstevel@tonic-gate mdb_printf("%3d", i); 22427c478bd9Sstevel@tonic-gate 22437c478bd9Sstevel@tonic-gate mdb_printf(" %0?p %0?p %16llx %0?p\n", addr, b->bc_addr, 22447c478bd9Sstevel@tonic-gate b->bc_timestamp, b->bc_thread); 22457c478bd9Sstevel@tonic-gate 22467c478bd9Sstevel@tonic-gate return (WALK_NEXT); 22477c478bd9Sstevel@tonic-gate } 22487c478bd9Sstevel@tonic-gate 22497c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 22507c478bd9Sstevel@tonic-gate int 22517c478bd9Sstevel@tonic-gate kmem_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 22527c478bd9Sstevel@tonic-gate { 22537c478bd9Sstevel@tonic-gate kmem_log_header_t lh; 22547c478bd9Sstevel@tonic-gate kmem_cpu_log_header_t clh; 22557c478bd9Sstevel@tonic-gate uintptr_t lhp, clhp; 22567c478bd9Sstevel@tonic-gate int ncpus; 22577c478bd9Sstevel@tonic-gate uintptr_t *cpu; 22587c478bd9Sstevel@tonic-gate GElf_Sym sym; 22597c478bd9Sstevel@tonic-gate kmem_log_cpu_t *kmc; 22607c478bd9Sstevel@tonic-gate int i; 22617c478bd9Sstevel@tonic-gate kmem_log_data_t kmd; 22627c478bd9Sstevel@tonic-gate uint_t opt_b = FALSE; 22637c478bd9Sstevel@tonic-gate 22647c478bd9Sstevel@tonic-gate if (mdb_getopts(argc, argv, 22657c478bd9Sstevel@tonic-gate 'b', MDB_OPT_SETBITS, TRUE, &opt_b, NULL) != argc) 22667c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 22677c478bd9Sstevel@tonic-gate 22687c478bd9Sstevel@tonic-gate if (mdb_readvar(&lhp, "kmem_transaction_log") == -1) { 22697c478bd9Sstevel@tonic-gate mdb_warn("failed to read 'kmem_transaction_log'"); 22707c478bd9Sstevel@tonic-gate return (DCMD_ERR); 22717c478bd9Sstevel@tonic-gate } 22727c478bd9Sstevel@tonic-gate 22737c478bd9Sstevel@tonic-gate if (lhp == NULL) { 22747c478bd9Sstevel@tonic-gate mdb_warn("no kmem transaction log\n"); 22757c478bd9Sstevel@tonic-gate return (DCMD_ERR); 22767c478bd9Sstevel@tonic-gate } 22777c478bd9Sstevel@tonic-gate 22787c478bd9Sstevel@tonic-gate mdb_readvar(&ncpus, "ncpus"); 22797c478bd9Sstevel@tonic-gate 22807c478bd9Sstevel@tonic-gate if (mdb_vread(&lh, sizeof (kmem_log_header_t), lhp) == -1) { 22817c478bd9Sstevel@tonic-gate mdb_warn("failed to read log header at %p", lhp); 22827c478bd9Sstevel@tonic-gate return (DCMD_ERR); 22837c478bd9Sstevel@tonic-gate } 22847c478bd9Sstevel@tonic-gate 22857c478bd9Sstevel@tonic-gate clhp = lhp + ((uintptr_t)&lh.lh_cpu[0] - (uintptr_t)&lh); 22867c478bd9Sstevel@tonic-gate 22877c478bd9Sstevel@tonic-gate cpu = mdb_alloc(sizeof (uintptr_t) * NCPU, UM_SLEEP | UM_GC); 22887c478bd9Sstevel@tonic-gate 22897c478bd9Sstevel@tonic-gate if (mdb_lookup_by_name("cpu", &sym) == -1) { 22907c478bd9Sstevel@tonic-gate mdb_warn("couldn't find 'cpu' array"); 22917c478bd9Sstevel@tonic-gate return (DCMD_ERR); 22927c478bd9Sstevel@tonic-gate } 22937c478bd9Sstevel@tonic-gate 22947c478bd9Sstevel@tonic-gate if (sym.st_size != NCPU * sizeof (uintptr_t)) { 22957c478bd9Sstevel@tonic-gate mdb_warn("expected 'cpu' to be of size %d; found %d\n", 22967c478bd9Sstevel@tonic-gate NCPU * sizeof (uintptr_t), sym.st_size); 22977c478bd9Sstevel@tonic-gate return (DCMD_ERR); 22987c478bd9Sstevel@tonic-gate } 22997c478bd9Sstevel@tonic-gate 23007c478bd9Sstevel@tonic-gate if (mdb_vread(cpu, sym.st_size, (uintptr_t)sym.st_value) == -1) { 23017c478bd9Sstevel@tonic-gate mdb_warn("failed to read cpu array at %p", sym.st_value); 23027c478bd9Sstevel@tonic-gate return (DCMD_ERR); 23037c478bd9Sstevel@tonic-gate } 23047c478bd9Sstevel@tonic-gate 23057c478bd9Sstevel@tonic-gate kmc = mdb_zalloc(sizeof (kmem_log_cpu_t) * NCPU, UM_SLEEP | UM_GC); 23067c478bd9Sstevel@tonic-gate kmd.kmd_addr = NULL; 23077c478bd9Sstevel@tonic-gate kmd.kmd_cpu = kmc; 23087c478bd9Sstevel@tonic-gate 23097c478bd9Sstevel@tonic-gate for (i = 0; i < NCPU; i++) { 23107c478bd9Sstevel@tonic-gate 23117c478bd9Sstevel@tonic-gate if (cpu[i] == NULL) 23127c478bd9Sstevel@tonic-gate continue; 23137c478bd9Sstevel@tonic-gate 23147c478bd9Sstevel@tonic-gate if (mdb_vread(&clh, sizeof (clh), clhp) == -1) { 23157c478bd9Sstevel@tonic-gate mdb_warn("cannot read cpu %d's log header at %p", 23167c478bd9Sstevel@tonic-gate i, clhp); 23177c478bd9Sstevel@tonic-gate return (DCMD_ERR); 23187c478bd9Sstevel@tonic-gate } 23197c478bd9Sstevel@tonic-gate 23207c478bd9Sstevel@tonic-gate kmc[i].kmc_low = clh.clh_chunk * lh.lh_chunksize + 23217c478bd9Sstevel@tonic-gate (uintptr_t)lh.lh_base; 23227c478bd9Sstevel@tonic-gate kmc[i].kmc_high = (uintptr_t)clh.clh_current; 23237c478bd9Sstevel@tonic-gate 23247c478bd9Sstevel@tonic-gate clhp += sizeof (kmem_cpu_log_header_t); 23257c478bd9Sstevel@tonic-gate } 23267c478bd9Sstevel@tonic-gate 23277c478bd9Sstevel@tonic-gate mdb_printf("%3s %-?s %-?s %16s %-?s\n", "CPU", "ADDR", "BUFADDR", 23287c478bd9Sstevel@tonic-gate "TIMESTAMP", "THREAD"); 23297c478bd9Sstevel@tonic-gate 23307c478bd9Sstevel@tonic-gate /* 23317c478bd9Sstevel@tonic-gate * If we have been passed an address, print out only log entries 23327c478bd9Sstevel@tonic-gate * corresponding to that address. If opt_b is specified, then interpret 23337c478bd9Sstevel@tonic-gate * the address as a bufctl. 23347c478bd9Sstevel@tonic-gate */ 23357c478bd9Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 23367c478bd9Sstevel@tonic-gate kmem_bufctl_audit_t b; 23377c478bd9Sstevel@tonic-gate 23387c478bd9Sstevel@tonic-gate if (opt_b) { 23397c478bd9Sstevel@tonic-gate kmd.kmd_addr = addr; 23407c478bd9Sstevel@tonic-gate } else { 23417c478bd9Sstevel@tonic-gate if (mdb_vread(&b, 23427c478bd9Sstevel@tonic-gate sizeof (kmem_bufctl_audit_t), addr) == -1) { 23437c478bd9Sstevel@tonic-gate mdb_warn("failed to read bufctl at %p", addr); 23447c478bd9Sstevel@tonic-gate return (DCMD_ERR); 23457c478bd9Sstevel@tonic-gate } 23467c478bd9Sstevel@tonic-gate 23477c478bd9Sstevel@tonic-gate (void) kmem_log_walk(addr, &b, &kmd); 23487c478bd9Sstevel@tonic-gate 23497c478bd9Sstevel@tonic-gate return (DCMD_OK); 23507c478bd9Sstevel@tonic-gate } 23517c478bd9Sstevel@tonic-gate } 23527c478bd9Sstevel@tonic-gate 23537c478bd9Sstevel@tonic-gate if (mdb_walk("kmem_log", (mdb_walk_cb_t)kmem_log_walk, &kmd) == -1) { 23547c478bd9Sstevel@tonic-gate mdb_warn("can't find kmem log walker"); 23557c478bd9Sstevel@tonic-gate return (DCMD_ERR); 23567c478bd9Sstevel@tonic-gate } 23577c478bd9Sstevel@tonic-gate 23587c478bd9Sstevel@tonic-gate return (DCMD_OK); 23597c478bd9Sstevel@tonic-gate } 23607c478bd9Sstevel@tonic-gate 23617c478bd9Sstevel@tonic-gate typedef struct bufctl_history_cb { 23627c478bd9Sstevel@tonic-gate int bhc_flags; 23637c478bd9Sstevel@tonic-gate int bhc_argc; 23647c478bd9Sstevel@tonic-gate const mdb_arg_t *bhc_argv; 23657c478bd9Sstevel@tonic-gate int bhc_ret; 23667c478bd9Sstevel@tonic-gate } bufctl_history_cb_t; 23677c478bd9Sstevel@tonic-gate 23687c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 23697c478bd9Sstevel@tonic-gate static int 23707c478bd9Sstevel@tonic-gate bufctl_history_callback(uintptr_t addr, const void *ign, void *arg) 23717c478bd9Sstevel@tonic-gate { 23727c478bd9Sstevel@tonic-gate bufctl_history_cb_t *bhc = arg; 23737c478bd9Sstevel@tonic-gate 23747c478bd9Sstevel@tonic-gate bhc->bhc_ret = 23757c478bd9Sstevel@tonic-gate bufctl(addr, bhc->bhc_flags, bhc->bhc_argc, bhc->bhc_argv); 23767c478bd9Sstevel@tonic-gate 23777c478bd9Sstevel@tonic-gate bhc->bhc_flags &= ~DCMD_LOOPFIRST; 23787c478bd9Sstevel@tonic-gate 23797c478bd9Sstevel@tonic-gate return ((bhc->bhc_ret == DCMD_OK)? WALK_NEXT : WALK_DONE); 23807c478bd9Sstevel@tonic-gate } 23817c478bd9Sstevel@tonic-gate 23827c478bd9Sstevel@tonic-gate void 23837c478bd9Sstevel@tonic-gate bufctl_help(void) 23847c478bd9Sstevel@tonic-gate { 23857c478bd9Sstevel@tonic-gate mdb_printf("%s\n", 23867c478bd9Sstevel@tonic-gate "Display the contents of kmem_bufctl_audit_ts, with optional filtering.\n"); 23877c478bd9Sstevel@tonic-gate mdb_dec_indent(2); 23887c478bd9Sstevel@tonic-gate mdb_printf("%<b>OPTIONS%</b>\n"); 23897c478bd9Sstevel@tonic-gate mdb_inc_indent(2); 23907c478bd9Sstevel@tonic-gate mdb_printf("%s", 23917c478bd9Sstevel@tonic-gate " -v Display the full content of the bufctl, including its stack trace\n" 23927c478bd9Sstevel@tonic-gate " -h retrieve the bufctl's transaction history, if available\n" 23937c478bd9Sstevel@tonic-gate " -a addr\n" 23947c478bd9Sstevel@tonic-gate " filter out bufctls not involving the buffer at addr\n" 23957c478bd9Sstevel@tonic-gate " -c caller\n" 23967c478bd9Sstevel@tonic-gate " filter out bufctls without the function/PC in their stack trace\n" 23977c478bd9Sstevel@tonic-gate " -e earliest\n" 23987c478bd9Sstevel@tonic-gate " filter out bufctls timestamped before earliest\n" 23997c478bd9Sstevel@tonic-gate " -l latest\n" 24007c478bd9Sstevel@tonic-gate " filter out bufctls timestamped after latest\n" 24017c478bd9Sstevel@tonic-gate " -t thread\n" 24027c478bd9Sstevel@tonic-gate " filter out bufctls not involving thread\n"); 24037c478bd9Sstevel@tonic-gate } 24047c478bd9Sstevel@tonic-gate 24057c478bd9Sstevel@tonic-gate int 24067c478bd9Sstevel@tonic-gate bufctl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 24077c478bd9Sstevel@tonic-gate { 24087c478bd9Sstevel@tonic-gate kmem_bufctl_audit_t bc; 24097c478bd9Sstevel@tonic-gate uint_t verbose = FALSE; 24107c478bd9Sstevel@tonic-gate uint_t history = FALSE; 24117c478bd9Sstevel@tonic-gate uint_t in_history = FALSE; 24127c478bd9Sstevel@tonic-gate uintptr_t caller = NULL, thread = NULL; 24137c478bd9Sstevel@tonic-gate uintptr_t laddr, haddr, baddr = NULL; 24147c478bd9Sstevel@tonic-gate hrtime_t earliest = 0, latest = 0; 24157c478bd9Sstevel@tonic-gate int i, depth; 24167c478bd9Sstevel@tonic-gate char c[MDB_SYM_NAMLEN]; 24177c478bd9Sstevel@tonic-gate GElf_Sym sym; 24187c478bd9Sstevel@tonic-gate 24197c478bd9Sstevel@tonic-gate if (mdb_getopts(argc, argv, 24207c478bd9Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &verbose, 24217c478bd9Sstevel@tonic-gate 'h', MDB_OPT_SETBITS, TRUE, &history, 24227c478bd9Sstevel@tonic-gate 'H', MDB_OPT_SETBITS, TRUE, &in_history, /* internal */ 24237c478bd9Sstevel@tonic-gate 'c', MDB_OPT_UINTPTR, &caller, 24247c478bd9Sstevel@tonic-gate 't', MDB_OPT_UINTPTR, &thread, 24257c478bd9Sstevel@tonic-gate 'e', MDB_OPT_UINT64, &earliest, 24267c478bd9Sstevel@tonic-gate 'l', MDB_OPT_UINT64, &latest, 24277c478bd9Sstevel@tonic-gate 'a', MDB_OPT_UINTPTR, &baddr, NULL) != argc) 24287c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 24297c478bd9Sstevel@tonic-gate 24307c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 24317c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 24327c478bd9Sstevel@tonic-gate 24337c478bd9Sstevel@tonic-gate if (in_history && !history) 24347c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 24357c478bd9Sstevel@tonic-gate 24367c478bd9Sstevel@tonic-gate if (history && !in_history) { 24377c478bd9Sstevel@tonic-gate mdb_arg_t *nargv = mdb_zalloc(sizeof (*nargv) * (argc + 1), 24387c478bd9Sstevel@tonic-gate UM_SLEEP | UM_GC); 24397c478bd9Sstevel@tonic-gate bufctl_history_cb_t bhc; 24407c478bd9Sstevel@tonic-gate 24417c478bd9Sstevel@tonic-gate nargv[0].a_type = MDB_TYPE_STRING; 24427c478bd9Sstevel@tonic-gate nargv[0].a_un.a_str = "-H"; /* prevent recursion */ 24437c478bd9Sstevel@tonic-gate 24447c478bd9Sstevel@tonic-gate for (i = 0; i < argc; i++) 24457c478bd9Sstevel@tonic-gate nargv[i + 1] = argv[i]; 24467c478bd9Sstevel@tonic-gate 24477c478bd9Sstevel@tonic-gate /* 24487c478bd9Sstevel@tonic-gate * When in history mode, we treat each element as if it 24497c478bd9Sstevel@tonic-gate * were in a seperate loop, so that the headers group 24507c478bd9Sstevel@tonic-gate * bufctls with similar histories. 24517c478bd9Sstevel@tonic-gate */ 24527c478bd9Sstevel@tonic-gate bhc.bhc_flags = flags | DCMD_LOOP | DCMD_LOOPFIRST; 24537c478bd9Sstevel@tonic-gate bhc.bhc_argc = argc + 1; 24547c478bd9Sstevel@tonic-gate bhc.bhc_argv = nargv; 24557c478bd9Sstevel@tonic-gate bhc.bhc_ret = DCMD_OK; 24567c478bd9Sstevel@tonic-gate 24577c478bd9Sstevel@tonic-gate if (mdb_pwalk("bufctl_history", bufctl_history_callback, &bhc, 24587c478bd9Sstevel@tonic-gate addr) == -1) { 24597c478bd9Sstevel@tonic-gate mdb_warn("unable to walk bufctl_history"); 24607c478bd9Sstevel@tonic-gate return (DCMD_ERR); 24617c478bd9Sstevel@tonic-gate } 24627c478bd9Sstevel@tonic-gate 24637c478bd9Sstevel@tonic-gate if (bhc.bhc_ret == DCMD_OK && !(flags & DCMD_PIPE_OUT)) 24647c478bd9Sstevel@tonic-gate mdb_printf("\n"); 24657c478bd9Sstevel@tonic-gate 24667c478bd9Sstevel@tonic-gate return (bhc.bhc_ret); 24677c478bd9Sstevel@tonic-gate } 24687c478bd9Sstevel@tonic-gate 24697c478bd9Sstevel@tonic-gate if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) { 24707c478bd9Sstevel@tonic-gate if (verbose) { 24717c478bd9Sstevel@tonic-gate mdb_printf("%16s %16s %16s %16s\n" 24727c478bd9Sstevel@tonic-gate "%<u>%16s %16s %16s %16s%</u>\n", 24737c478bd9Sstevel@tonic-gate "ADDR", "BUFADDR", "TIMESTAMP", "THREAD", 24747c478bd9Sstevel@tonic-gate "", "CACHE", "LASTLOG", "CONTENTS"); 24757c478bd9Sstevel@tonic-gate } else { 24767c478bd9Sstevel@tonic-gate mdb_printf("%<u>%-?s %-?s %-12s %-?s %s%</u>\n", 24777c478bd9Sstevel@tonic-gate "ADDR", "BUFADDR", "TIMESTAMP", "THREAD", "CALLER"); 24787c478bd9Sstevel@tonic-gate } 24797c478bd9Sstevel@tonic-gate } 24807c478bd9Sstevel@tonic-gate 24817c478bd9Sstevel@tonic-gate if (mdb_vread(&bc, sizeof (bc), addr) == -1) { 24827c478bd9Sstevel@tonic-gate mdb_warn("couldn't read bufctl at %p", addr); 24837c478bd9Sstevel@tonic-gate return (DCMD_ERR); 24847c478bd9Sstevel@tonic-gate } 24857c478bd9Sstevel@tonic-gate 24867c478bd9Sstevel@tonic-gate /* 24877c478bd9Sstevel@tonic-gate * Guard against bogus bc_depth in case the bufctl is corrupt or 24887c478bd9Sstevel@tonic-gate * the address does not really refer to a bufctl. 24897c478bd9Sstevel@tonic-gate */ 24907c478bd9Sstevel@tonic-gate depth = MIN(bc.bc_depth, KMEM_STACK_DEPTH); 24917c478bd9Sstevel@tonic-gate 24927c478bd9Sstevel@tonic-gate if (caller != NULL) { 24937c478bd9Sstevel@tonic-gate laddr = caller; 24947c478bd9Sstevel@tonic-gate haddr = caller + sizeof (caller); 24957c478bd9Sstevel@tonic-gate 24967c478bd9Sstevel@tonic-gate if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c, sizeof (c), 24977c478bd9Sstevel@tonic-gate &sym) != -1 && caller == (uintptr_t)sym.st_value) { 24987c478bd9Sstevel@tonic-gate /* 24997c478bd9Sstevel@tonic-gate * We were provided an exact symbol value; any 25007c478bd9Sstevel@tonic-gate * address in the function is valid. 25017c478bd9Sstevel@tonic-gate */ 25027c478bd9Sstevel@tonic-gate laddr = (uintptr_t)sym.st_value; 25037c478bd9Sstevel@tonic-gate haddr = (uintptr_t)sym.st_value + sym.st_size; 25047c478bd9Sstevel@tonic-gate } 25057c478bd9Sstevel@tonic-gate 25067c478bd9Sstevel@tonic-gate for (i = 0; i < depth; i++) 25077c478bd9Sstevel@tonic-gate if (bc.bc_stack[i] >= laddr && bc.bc_stack[i] < haddr) 25087c478bd9Sstevel@tonic-gate break; 25097c478bd9Sstevel@tonic-gate 25107c478bd9Sstevel@tonic-gate if (i == depth) 25117c478bd9Sstevel@tonic-gate return (DCMD_OK); 25127c478bd9Sstevel@tonic-gate } 25137c478bd9Sstevel@tonic-gate 25147c478bd9Sstevel@tonic-gate if (thread != NULL && (uintptr_t)bc.bc_thread != thread) 25157c478bd9Sstevel@tonic-gate return (DCMD_OK); 25167c478bd9Sstevel@tonic-gate 25177c478bd9Sstevel@tonic-gate if (earliest != 0 && bc.bc_timestamp < earliest) 25187c478bd9Sstevel@tonic-gate return (DCMD_OK); 25197c478bd9Sstevel@tonic-gate 25207c478bd9Sstevel@tonic-gate if (latest != 0 && bc.bc_timestamp > latest) 25217c478bd9Sstevel@tonic-gate return (DCMD_OK); 25227c478bd9Sstevel@tonic-gate 25237c478bd9Sstevel@tonic-gate if (baddr != 0 && (uintptr_t)bc.bc_addr != baddr) 25247c478bd9Sstevel@tonic-gate return (DCMD_OK); 25257c478bd9Sstevel@tonic-gate 25267c478bd9Sstevel@tonic-gate if (flags & DCMD_PIPE_OUT) { 25277c478bd9Sstevel@tonic-gate mdb_printf("%#lr\n", addr); 25287c478bd9Sstevel@tonic-gate return (DCMD_OK); 25297c478bd9Sstevel@tonic-gate } 25307c478bd9Sstevel@tonic-gate 25317c478bd9Sstevel@tonic-gate if (verbose) { 25327c478bd9Sstevel@tonic-gate mdb_printf( 25337c478bd9Sstevel@tonic-gate "%<b>%16p%</b> %16p %16llx %16p\n" 25347c478bd9Sstevel@tonic-gate "%16s %16p %16p %16p\n", 25357c478bd9Sstevel@tonic-gate addr, bc.bc_addr, bc.bc_timestamp, bc.bc_thread, 25367c478bd9Sstevel@tonic-gate "", bc.bc_cache, bc.bc_lastlog, bc.bc_contents); 25377c478bd9Sstevel@tonic-gate 25387c478bd9Sstevel@tonic-gate mdb_inc_indent(17); 25397c478bd9Sstevel@tonic-gate for (i = 0; i < depth; i++) 25407c478bd9Sstevel@tonic-gate mdb_printf("%a\n", bc.bc_stack[i]); 25417c478bd9Sstevel@tonic-gate mdb_dec_indent(17); 25427c478bd9Sstevel@tonic-gate mdb_printf("\n"); 25437c478bd9Sstevel@tonic-gate } else { 25447c478bd9Sstevel@tonic-gate mdb_printf("%0?p %0?p %12llx %0?p", addr, bc.bc_addr, 25457c478bd9Sstevel@tonic-gate bc.bc_timestamp, bc.bc_thread); 25467c478bd9Sstevel@tonic-gate 25477c478bd9Sstevel@tonic-gate for (i = 0; i < depth; i++) { 25487c478bd9Sstevel@tonic-gate if (mdb_lookup_by_addr(bc.bc_stack[i], 25497c478bd9Sstevel@tonic-gate MDB_SYM_FUZZY, c, sizeof (c), &sym) == -1) 25507c478bd9Sstevel@tonic-gate continue; 25517c478bd9Sstevel@tonic-gate if (strncmp(c, "kmem_", 5) == 0) 25527c478bd9Sstevel@tonic-gate continue; 25537c478bd9Sstevel@tonic-gate mdb_printf(" %a\n", bc.bc_stack[i]); 25547c478bd9Sstevel@tonic-gate break; 25557c478bd9Sstevel@tonic-gate } 25567c478bd9Sstevel@tonic-gate 25577c478bd9Sstevel@tonic-gate if (i >= depth) 25587c478bd9Sstevel@tonic-gate mdb_printf("\n"); 25597c478bd9Sstevel@tonic-gate } 25607c478bd9Sstevel@tonic-gate 25617c478bd9Sstevel@tonic-gate return (DCMD_OK); 25627c478bd9Sstevel@tonic-gate } 25637c478bd9Sstevel@tonic-gate 25647c478bd9Sstevel@tonic-gate typedef struct kmem_verify { 25657c478bd9Sstevel@tonic-gate uint64_t *kmv_buf; /* buffer to read cache contents into */ 25667c478bd9Sstevel@tonic-gate size_t kmv_size; /* number of bytes in kmv_buf */ 25677c478bd9Sstevel@tonic-gate int kmv_corruption; /* > 0 if corruption found. */ 25687c478bd9Sstevel@tonic-gate int kmv_besilent; /* report actual corruption sites */ 25697c478bd9Sstevel@tonic-gate struct kmem_cache kmv_cache; /* the cache we're operating on */ 25707c478bd9Sstevel@tonic-gate } kmem_verify_t; 25717c478bd9Sstevel@tonic-gate 25727c478bd9Sstevel@tonic-gate /* 25737c478bd9Sstevel@tonic-gate * verify_pattern() 25747c478bd9Sstevel@tonic-gate * verify that buf is filled with the pattern pat. 25757c478bd9Sstevel@tonic-gate */ 25767c478bd9Sstevel@tonic-gate static int64_t 25777c478bd9Sstevel@tonic-gate verify_pattern(uint64_t *buf_arg, size_t size, uint64_t pat) 25787c478bd9Sstevel@tonic-gate { 25797c478bd9Sstevel@tonic-gate /*LINTED*/ 25807c478bd9Sstevel@tonic-gate uint64_t *bufend = (uint64_t *)((char *)buf_arg + size); 25817c478bd9Sstevel@tonic-gate uint64_t *buf; 25827c478bd9Sstevel@tonic-gate 25837c478bd9Sstevel@tonic-gate for (buf = buf_arg; buf < bufend; buf++) 25847c478bd9Sstevel@tonic-gate if (*buf != pat) 25857c478bd9Sstevel@tonic-gate return ((uintptr_t)buf - (uintptr_t)buf_arg); 25867c478bd9Sstevel@tonic-gate return (-1); 25877c478bd9Sstevel@tonic-gate } 25887c478bd9Sstevel@tonic-gate 25897c478bd9Sstevel@tonic-gate /* 25907c478bd9Sstevel@tonic-gate * verify_buftag() 25917c478bd9Sstevel@tonic-gate * verify that btp->bt_bxstat == (bcp ^ pat) 25927c478bd9Sstevel@tonic-gate */ 25937c478bd9Sstevel@tonic-gate static int 25947c478bd9Sstevel@tonic-gate verify_buftag(kmem_buftag_t *btp, uintptr_t pat) 25957c478bd9Sstevel@tonic-gate { 25967c478bd9Sstevel@tonic-gate return (btp->bt_bxstat == ((intptr_t)btp->bt_bufctl ^ pat) ? 0 : -1); 25977c478bd9Sstevel@tonic-gate } 25987c478bd9Sstevel@tonic-gate 25997c478bd9Sstevel@tonic-gate /* 26007c478bd9Sstevel@tonic-gate * verify_free() 26017c478bd9Sstevel@tonic-gate * verify the integrity of a free block of memory by checking 26027c478bd9Sstevel@tonic-gate * that it is filled with 0xdeadbeef and that its buftag is sane. 26037c478bd9Sstevel@tonic-gate */ 26047c478bd9Sstevel@tonic-gate /*ARGSUSED1*/ 26057c478bd9Sstevel@tonic-gate static int 26067c478bd9Sstevel@tonic-gate verify_free(uintptr_t addr, const void *data, void *private) 26077c478bd9Sstevel@tonic-gate { 26087c478bd9Sstevel@tonic-gate kmem_verify_t *kmv = (kmem_verify_t *)private; 26097c478bd9Sstevel@tonic-gate uint64_t *buf = kmv->kmv_buf; /* buf to validate */ 26107c478bd9Sstevel@tonic-gate int64_t corrupt; /* corruption offset */ 26117c478bd9Sstevel@tonic-gate kmem_buftag_t *buftagp; /* ptr to buftag */ 26127c478bd9Sstevel@tonic-gate kmem_cache_t *cp = &kmv->kmv_cache; 26137c478bd9Sstevel@tonic-gate int besilent = kmv->kmv_besilent; 26147c478bd9Sstevel@tonic-gate 26157c478bd9Sstevel@tonic-gate /*LINTED*/ 26167c478bd9Sstevel@tonic-gate buftagp = KMEM_BUFTAG(cp, buf); 26177c478bd9Sstevel@tonic-gate 26187c478bd9Sstevel@tonic-gate /* 26197c478bd9Sstevel@tonic-gate * Read the buffer to check. 26207c478bd9Sstevel@tonic-gate */ 26217c478bd9Sstevel@tonic-gate if (mdb_vread(buf, kmv->kmv_size, addr) == -1) { 26227c478bd9Sstevel@tonic-gate if (!besilent) 26237c478bd9Sstevel@tonic-gate mdb_warn("couldn't read %p", addr); 26247c478bd9Sstevel@tonic-gate return (WALK_NEXT); 26257c478bd9Sstevel@tonic-gate } 26267c478bd9Sstevel@tonic-gate 26277c478bd9Sstevel@tonic-gate if ((corrupt = verify_pattern(buf, cp->cache_verify, 26287c478bd9Sstevel@tonic-gate KMEM_FREE_PATTERN)) >= 0) { 26297c478bd9Sstevel@tonic-gate if (!besilent) 26307c478bd9Sstevel@tonic-gate mdb_printf("buffer %p (free) seems corrupted, at %p\n", 26317c478bd9Sstevel@tonic-gate addr, (uintptr_t)addr + corrupt); 26327c478bd9Sstevel@tonic-gate goto corrupt; 26337c478bd9Sstevel@tonic-gate } 26347c478bd9Sstevel@tonic-gate /* 26357c478bd9Sstevel@tonic-gate * When KMF_LITE is set, buftagp->bt_redzone is used to hold 26367c478bd9Sstevel@tonic-gate * the first bytes of the buffer, hence we cannot check for red 26377c478bd9Sstevel@tonic-gate * zone corruption. 26387c478bd9Sstevel@tonic-gate */ 26397c478bd9Sstevel@tonic-gate if ((cp->cache_flags & (KMF_HASH | KMF_LITE)) == KMF_HASH && 26407c478bd9Sstevel@tonic-gate buftagp->bt_redzone != KMEM_REDZONE_PATTERN) { 26417c478bd9Sstevel@tonic-gate if (!besilent) 26427c478bd9Sstevel@tonic-gate mdb_printf("buffer %p (free) seems to " 26437c478bd9Sstevel@tonic-gate "have a corrupt redzone pattern\n", addr); 26447c478bd9Sstevel@tonic-gate goto corrupt; 26457c478bd9Sstevel@tonic-gate } 26467c478bd9Sstevel@tonic-gate 26477c478bd9Sstevel@tonic-gate /* 26487c478bd9Sstevel@tonic-gate * confirm bufctl pointer integrity. 26497c478bd9Sstevel@tonic-gate */ 26507c478bd9Sstevel@tonic-gate if (verify_buftag(buftagp, KMEM_BUFTAG_FREE) == -1) { 26517c478bd9Sstevel@tonic-gate if (!besilent) 26527c478bd9Sstevel@tonic-gate mdb_printf("buffer %p (free) has a corrupt " 26537c478bd9Sstevel@tonic-gate "buftag\n", addr); 26547c478bd9Sstevel@tonic-gate goto corrupt; 26557c478bd9Sstevel@tonic-gate } 26567c478bd9Sstevel@tonic-gate 26577c478bd9Sstevel@tonic-gate return (WALK_NEXT); 26587c478bd9Sstevel@tonic-gate corrupt: 26597c478bd9Sstevel@tonic-gate kmv->kmv_corruption++; 26607c478bd9Sstevel@tonic-gate return (WALK_NEXT); 26617c478bd9Sstevel@tonic-gate } 26627c478bd9Sstevel@tonic-gate 26637c478bd9Sstevel@tonic-gate /* 26647c478bd9Sstevel@tonic-gate * verify_alloc() 26657c478bd9Sstevel@tonic-gate * Verify that the buftag of an allocated buffer makes sense with respect 26667c478bd9Sstevel@tonic-gate * to the buffer. 26677c478bd9Sstevel@tonic-gate */ 26687c478bd9Sstevel@tonic-gate /*ARGSUSED1*/ 26697c478bd9Sstevel@tonic-gate static int 26707c478bd9Sstevel@tonic-gate verify_alloc(uintptr_t addr, const void *data, void *private) 26717c478bd9Sstevel@tonic-gate { 26727c478bd9Sstevel@tonic-gate kmem_verify_t *kmv = (kmem_verify_t *)private; 26737c478bd9Sstevel@tonic-gate kmem_cache_t *cp = &kmv->kmv_cache; 26747c478bd9Sstevel@tonic-gate uint64_t *buf = kmv->kmv_buf; /* buf to validate */ 26757c478bd9Sstevel@tonic-gate /*LINTED*/ 26767c478bd9Sstevel@tonic-gate kmem_buftag_t *buftagp = KMEM_BUFTAG(cp, buf); 26777c478bd9Sstevel@tonic-gate uint32_t *ip = (uint32_t *)buftagp; 26787c478bd9Sstevel@tonic-gate uint8_t *bp = (uint8_t *)buf; 26797c478bd9Sstevel@tonic-gate int looks_ok = 0, size_ok = 1; /* flags for finding corruption */ 26807c478bd9Sstevel@tonic-gate int besilent = kmv->kmv_besilent; 26817c478bd9Sstevel@tonic-gate 26827c478bd9Sstevel@tonic-gate /* 26837c478bd9Sstevel@tonic-gate * Read the buffer to check. 26847c478bd9Sstevel@tonic-gate */ 26857c478bd9Sstevel@tonic-gate if (mdb_vread(buf, kmv->kmv_size, addr) == -1) { 26867c478bd9Sstevel@tonic-gate if (!besilent) 26877c478bd9Sstevel@tonic-gate mdb_warn("couldn't read %p", addr); 26887c478bd9Sstevel@tonic-gate return (WALK_NEXT); 26897c478bd9Sstevel@tonic-gate } 26907c478bd9Sstevel@tonic-gate 26917c478bd9Sstevel@tonic-gate /* 26927c478bd9Sstevel@tonic-gate * There are two cases to handle: 26937c478bd9Sstevel@tonic-gate * 1. If the buf was alloc'd using kmem_cache_alloc, it will have 26947c478bd9Sstevel@tonic-gate * 0xfeedfacefeedface at the end of it 26957c478bd9Sstevel@tonic-gate * 2. If the buf was alloc'd using kmem_alloc, it will have 26967c478bd9Sstevel@tonic-gate * 0xbb just past the end of the region in use. At the buftag, 26977c478bd9Sstevel@tonic-gate * it will have 0xfeedface (or, if the whole buffer is in use, 26987c478bd9Sstevel@tonic-gate * 0xfeedface & bb000000 or 0xfeedfacf & 000000bb depending on 26997c478bd9Sstevel@tonic-gate * endianness), followed by 32 bits containing the offset of the 27007c478bd9Sstevel@tonic-gate * 0xbb byte in the buffer. 27017c478bd9Sstevel@tonic-gate * 27027c478bd9Sstevel@tonic-gate * Finally, the two 32-bit words that comprise the second half of the 27037c478bd9Sstevel@tonic-gate * buftag should xor to KMEM_BUFTAG_ALLOC 27047c478bd9Sstevel@tonic-gate */ 27057c478bd9Sstevel@tonic-gate 27067c478bd9Sstevel@tonic-gate if (buftagp->bt_redzone == KMEM_REDZONE_PATTERN) 27077c478bd9Sstevel@tonic-gate looks_ok = 1; 27087c478bd9Sstevel@tonic-gate else if (!KMEM_SIZE_VALID(ip[1])) 27097c478bd9Sstevel@tonic-gate size_ok = 0; 27107c478bd9Sstevel@tonic-gate else if (bp[KMEM_SIZE_DECODE(ip[1])] == KMEM_REDZONE_BYTE) 27117c478bd9Sstevel@tonic-gate looks_ok = 1; 27127c478bd9Sstevel@tonic-gate else 27137c478bd9Sstevel@tonic-gate size_ok = 0; 27147c478bd9Sstevel@tonic-gate 27157c478bd9Sstevel@tonic-gate if (!size_ok) { 27167c478bd9Sstevel@tonic-gate if (!besilent) 27177c478bd9Sstevel@tonic-gate mdb_printf("buffer %p (allocated) has a corrupt " 27187c478bd9Sstevel@tonic-gate "redzone size encoding\n", addr); 27197c478bd9Sstevel@tonic-gate goto corrupt; 27207c478bd9Sstevel@tonic-gate } 27217c478bd9Sstevel@tonic-gate 27227c478bd9Sstevel@tonic-gate if (!looks_ok) { 27237c478bd9Sstevel@tonic-gate if (!besilent) 27247c478bd9Sstevel@tonic-gate mdb_printf("buffer %p (allocated) has a corrupt " 27257c478bd9Sstevel@tonic-gate "redzone signature\n", addr); 27267c478bd9Sstevel@tonic-gate goto corrupt; 27277c478bd9Sstevel@tonic-gate } 27287c478bd9Sstevel@tonic-gate 27297c478bd9Sstevel@tonic-gate if (verify_buftag(buftagp, KMEM_BUFTAG_ALLOC) == -1) { 27307c478bd9Sstevel@tonic-gate if (!besilent) 27317c478bd9Sstevel@tonic-gate mdb_printf("buffer %p (allocated) has a " 27327c478bd9Sstevel@tonic-gate "corrupt buftag\n", addr); 27337c478bd9Sstevel@tonic-gate goto corrupt; 27347c478bd9Sstevel@tonic-gate } 27357c478bd9Sstevel@tonic-gate 27367c478bd9Sstevel@tonic-gate return (WALK_NEXT); 27377c478bd9Sstevel@tonic-gate corrupt: 27387c478bd9Sstevel@tonic-gate kmv->kmv_corruption++; 27397c478bd9Sstevel@tonic-gate return (WALK_NEXT); 27407c478bd9Sstevel@tonic-gate } 27417c478bd9Sstevel@tonic-gate 27427c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 27437c478bd9Sstevel@tonic-gate int 27447c478bd9Sstevel@tonic-gate kmem_verify(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 27457c478bd9Sstevel@tonic-gate { 27467c478bd9Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 27477c478bd9Sstevel@tonic-gate int check_alloc = 0, check_free = 0; 27487c478bd9Sstevel@tonic-gate kmem_verify_t kmv; 27497c478bd9Sstevel@tonic-gate 27507c478bd9Sstevel@tonic-gate if (mdb_vread(&kmv.kmv_cache, sizeof (kmv.kmv_cache), 27517c478bd9Sstevel@tonic-gate addr) == -1) { 27527c478bd9Sstevel@tonic-gate mdb_warn("couldn't read kmem_cache %p", addr); 27537c478bd9Sstevel@tonic-gate return (DCMD_ERR); 27547c478bd9Sstevel@tonic-gate } 27557c478bd9Sstevel@tonic-gate 27567c478bd9Sstevel@tonic-gate kmv.kmv_size = kmv.kmv_cache.cache_buftag + 27577c478bd9Sstevel@tonic-gate sizeof (kmem_buftag_t); 27587c478bd9Sstevel@tonic-gate kmv.kmv_buf = mdb_alloc(kmv.kmv_size, UM_SLEEP | UM_GC); 27597c478bd9Sstevel@tonic-gate kmv.kmv_corruption = 0; 27607c478bd9Sstevel@tonic-gate 27617c478bd9Sstevel@tonic-gate if ((kmv.kmv_cache.cache_flags & KMF_REDZONE)) { 27627c478bd9Sstevel@tonic-gate check_alloc = 1; 27637c478bd9Sstevel@tonic-gate if (kmv.kmv_cache.cache_flags & KMF_DEADBEEF) 27647c478bd9Sstevel@tonic-gate check_free = 1; 27657c478bd9Sstevel@tonic-gate } else { 27667c478bd9Sstevel@tonic-gate if (!(flags & DCMD_LOOP)) { 27677c478bd9Sstevel@tonic-gate mdb_warn("cache %p (%s) does not have " 27687c478bd9Sstevel@tonic-gate "redzone checking enabled\n", addr, 27697c478bd9Sstevel@tonic-gate kmv.kmv_cache.cache_name); 27707c478bd9Sstevel@tonic-gate } 27717c478bd9Sstevel@tonic-gate return (DCMD_ERR); 27727c478bd9Sstevel@tonic-gate } 27737c478bd9Sstevel@tonic-gate 27747c478bd9Sstevel@tonic-gate if (flags & DCMD_LOOP) { 27757c478bd9Sstevel@tonic-gate /* 27767c478bd9Sstevel@tonic-gate * table mode, don't print out every corrupt buffer 27777c478bd9Sstevel@tonic-gate */ 27787c478bd9Sstevel@tonic-gate kmv.kmv_besilent = 1; 27797c478bd9Sstevel@tonic-gate } else { 27807c478bd9Sstevel@tonic-gate mdb_printf("Summary for cache '%s'\n", 27817c478bd9Sstevel@tonic-gate kmv.kmv_cache.cache_name); 27827c478bd9Sstevel@tonic-gate mdb_inc_indent(2); 27837c478bd9Sstevel@tonic-gate kmv.kmv_besilent = 0; 27847c478bd9Sstevel@tonic-gate } 27857c478bd9Sstevel@tonic-gate 27867c478bd9Sstevel@tonic-gate if (check_alloc) 27877c478bd9Sstevel@tonic-gate (void) mdb_pwalk("kmem", verify_alloc, &kmv, addr); 27887c478bd9Sstevel@tonic-gate if (check_free) 27897c478bd9Sstevel@tonic-gate (void) mdb_pwalk("freemem", verify_free, &kmv, addr); 27907c478bd9Sstevel@tonic-gate 27917c478bd9Sstevel@tonic-gate if (flags & DCMD_LOOP) { 27927c478bd9Sstevel@tonic-gate if (kmv.kmv_corruption == 0) { 27937c478bd9Sstevel@tonic-gate mdb_printf("%-*s %?p clean\n", 27947c478bd9Sstevel@tonic-gate KMEM_CACHE_NAMELEN, 27957c478bd9Sstevel@tonic-gate kmv.kmv_cache.cache_name, addr); 27967c478bd9Sstevel@tonic-gate } else { 27977c478bd9Sstevel@tonic-gate char *s = ""; /* optional s in "buffer[s]" */ 27987c478bd9Sstevel@tonic-gate if (kmv.kmv_corruption > 1) 27997c478bd9Sstevel@tonic-gate s = "s"; 28007c478bd9Sstevel@tonic-gate 28017c478bd9Sstevel@tonic-gate mdb_printf("%-*s %?p %d corrupt buffer%s\n", 28027c478bd9Sstevel@tonic-gate KMEM_CACHE_NAMELEN, 28037c478bd9Sstevel@tonic-gate kmv.kmv_cache.cache_name, addr, 28047c478bd9Sstevel@tonic-gate kmv.kmv_corruption, s); 28057c478bd9Sstevel@tonic-gate } 28067c478bd9Sstevel@tonic-gate } else { 28077c478bd9Sstevel@tonic-gate /* 28087c478bd9Sstevel@tonic-gate * This is the more verbose mode, when the user has 28097c478bd9Sstevel@tonic-gate * type addr::kmem_verify. If the cache was clean, 28107c478bd9Sstevel@tonic-gate * nothing will have yet been printed. So say something. 28117c478bd9Sstevel@tonic-gate */ 28127c478bd9Sstevel@tonic-gate if (kmv.kmv_corruption == 0) 28137c478bd9Sstevel@tonic-gate mdb_printf("clean\n"); 28147c478bd9Sstevel@tonic-gate 28157c478bd9Sstevel@tonic-gate mdb_dec_indent(2); 28167c478bd9Sstevel@tonic-gate } 28177c478bd9Sstevel@tonic-gate } else { 28187c478bd9Sstevel@tonic-gate /* 28197c478bd9Sstevel@tonic-gate * If the user didn't specify a cache to verify, we'll walk all 28207c478bd9Sstevel@tonic-gate * kmem_cache's, specifying ourself as a callback for each... 28217c478bd9Sstevel@tonic-gate * this is the equivalent of '::walk kmem_cache .::kmem_verify' 28227c478bd9Sstevel@tonic-gate */ 28237c478bd9Sstevel@tonic-gate mdb_printf("%<u>%-*s %-?s %-20s%</b>\n", KMEM_CACHE_NAMELEN, 28247c478bd9Sstevel@tonic-gate "Cache Name", "Addr", "Cache Integrity"); 28257c478bd9Sstevel@tonic-gate (void) (mdb_walk_dcmd("kmem_cache", "kmem_verify", 0, NULL)); 28267c478bd9Sstevel@tonic-gate } 28277c478bd9Sstevel@tonic-gate 28287c478bd9Sstevel@tonic-gate return (DCMD_OK); 28297c478bd9Sstevel@tonic-gate } 28307c478bd9Sstevel@tonic-gate 28317c478bd9Sstevel@tonic-gate typedef struct vmem_node { 28327c478bd9Sstevel@tonic-gate struct vmem_node *vn_next; 28337c478bd9Sstevel@tonic-gate struct vmem_node *vn_parent; 28347c478bd9Sstevel@tonic-gate struct vmem_node *vn_sibling; 28357c478bd9Sstevel@tonic-gate struct vmem_node *vn_children; 28367c478bd9Sstevel@tonic-gate uintptr_t vn_addr; 28377c478bd9Sstevel@tonic-gate int vn_marked; 28387c478bd9Sstevel@tonic-gate vmem_t vn_vmem; 28397c478bd9Sstevel@tonic-gate } vmem_node_t; 28407c478bd9Sstevel@tonic-gate 28417c478bd9Sstevel@tonic-gate typedef struct vmem_walk { 28427c478bd9Sstevel@tonic-gate vmem_node_t *vw_root; 28437c478bd9Sstevel@tonic-gate vmem_node_t *vw_current; 28447c478bd9Sstevel@tonic-gate } vmem_walk_t; 28457c478bd9Sstevel@tonic-gate 28467c478bd9Sstevel@tonic-gate int 28477c478bd9Sstevel@tonic-gate vmem_walk_init(mdb_walk_state_t *wsp) 28487c478bd9Sstevel@tonic-gate { 28497c478bd9Sstevel@tonic-gate uintptr_t vaddr, paddr; 28507c478bd9Sstevel@tonic-gate vmem_node_t *head = NULL, *root = NULL, *current = NULL, *parent, *vp; 28517c478bd9Sstevel@tonic-gate vmem_walk_t *vw; 28527c478bd9Sstevel@tonic-gate 28537c478bd9Sstevel@tonic-gate if (mdb_readvar(&vaddr, "vmem_list") == -1) { 28547c478bd9Sstevel@tonic-gate mdb_warn("couldn't read 'vmem_list'"); 28557c478bd9Sstevel@tonic-gate return (WALK_ERR); 28567c478bd9Sstevel@tonic-gate } 28577c478bd9Sstevel@tonic-gate 28587c478bd9Sstevel@tonic-gate while (vaddr != NULL) { 28597c478bd9Sstevel@tonic-gate vp = mdb_zalloc(sizeof (vmem_node_t), UM_SLEEP); 28607c478bd9Sstevel@tonic-gate vp->vn_addr = vaddr; 28617c478bd9Sstevel@tonic-gate vp->vn_next = head; 28627c478bd9Sstevel@tonic-gate head = vp; 28637c478bd9Sstevel@tonic-gate 28647c478bd9Sstevel@tonic-gate if (vaddr == wsp->walk_addr) 28657c478bd9Sstevel@tonic-gate current = vp; 28667c478bd9Sstevel@tonic-gate 28677c478bd9Sstevel@tonic-gate if (mdb_vread(&vp->vn_vmem, sizeof (vmem_t), vaddr) == -1) { 28687c478bd9Sstevel@tonic-gate mdb_warn("couldn't read vmem_t at %p", vaddr); 28697c478bd9Sstevel@tonic-gate goto err; 28707c478bd9Sstevel@tonic-gate } 28717c478bd9Sstevel@tonic-gate 28727c478bd9Sstevel@tonic-gate vaddr = (uintptr_t)vp->vn_vmem.vm_next; 28737c478bd9Sstevel@tonic-gate } 28747c478bd9Sstevel@tonic-gate 28757c478bd9Sstevel@tonic-gate for (vp = head; vp != NULL; vp = vp->vn_next) { 28767c478bd9Sstevel@tonic-gate 28777c478bd9Sstevel@tonic-gate if ((paddr = (uintptr_t)vp->vn_vmem.vm_source) == NULL) { 28787c478bd9Sstevel@tonic-gate vp->vn_sibling = root; 28797c478bd9Sstevel@tonic-gate root = vp; 28807c478bd9Sstevel@tonic-gate continue; 28817c478bd9Sstevel@tonic-gate } 28827c478bd9Sstevel@tonic-gate 28837c478bd9Sstevel@tonic-gate for (parent = head; parent != NULL; parent = parent->vn_next) { 28847c478bd9Sstevel@tonic-gate if (parent->vn_addr != paddr) 28857c478bd9Sstevel@tonic-gate continue; 28867c478bd9Sstevel@tonic-gate vp->vn_sibling = parent->vn_children; 28877c478bd9Sstevel@tonic-gate parent->vn_children = vp; 28887c478bd9Sstevel@tonic-gate vp->vn_parent = parent; 28897c478bd9Sstevel@tonic-gate break; 28907c478bd9Sstevel@tonic-gate } 28917c478bd9Sstevel@tonic-gate 28927c478bd9Sstevel@tonic-gate if (parent == NULL) { 28937c478bd9Sstevel@tonic-gate mdb_warn("couldn't find %p's parent (%p)\n", 28947c478bd9Sstevel@tonic-gate vp->vn_addr, paddr); 28957c478bd9Sstevel@tonic-gate goto err; 28967c478bd9Sstevel@tonic-gate } 28977c478bd9Sstevel@tonic-gate } 28987c478bd9Sstevel@tonic-gate 28997c478bd9Sstevel@tonic-gate vw = mdb_zalloc(sizeof (vmem_walk_t), UM_SLEEP); 29007c478bd9Sstevel@tonic-gate vw->vw_root = root; 29017c478bd9Sstevel@tonic-gate 29027c478bd9Sstevel@tonic-gate if (current != NULL) 29037c478bd9Sstevel@tonic-gate vw->vw_current = current; 29047c478bd9Sstevel@tonic-gate else 29057c478bd9Sstevel@tonic-gate vw->vw_current = root; 29067c478bd9Sstevel@tonic-gate 29077c478bd9Sstevel@tonic-gate wsp->walk_data = vw; 29087c478bd9Sstevel@tonic-gate return (WALK_NEXT); 29097c478bd9Sstevel@tonic-gate err: 29107c478bd9Sstevel@tonic-gate for (vp = head; head != NULL; vp = head) { 29117c478bd9Sstevel@tonic-gate head = vp->vn_next; 29127c478bd9Sstevel@tonic-gate mdb_free(vp, sizeof (vmem_node_t)); 29137c478bd9Sstevel@tonic-gate } 29147c478bd9Sstevel@tonic-gate 29157c478bd9Sstevel@tonic-gate return (WALK_ERR); 29167c478bd9Sstevel@tonic-gate } 29177c478bd9Sstevel@tonic-gate 29187c478bd9Sstevel@tonic-gate int 29197c478bd9Sstevel@tonic-gate vmem_walk_step(mdb_walk_state_t *wsp) 29207c478bd9Sstevel@tonic-gate { 29217c478bd9Sstevel@tonic-gate vmem_walk_t *vw = wsp->walk_data; 29227c478bd9Sstevel@tonic-gate vmem_node_t *vp; 29237c478bd9Sstevel@tonic-gate int rval; 29247c478bd9Sstevel@tonic-gate 29257c478bd9Sstevel@tonic-gate if ((vp = vw->vw_current) == NULL) 29267c478bd9Sstevel@tonic-gate return (WALK_DONE); 29277c478bd9Sstevel@tonic-gate 29287c478bd9Sstevel@tonic-gate rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata); 29297c478bd9Sstevel@tonic-gate 29307c478bd9Sstevel@tonic-gate if (vp->vn_children != NULL) { 29317c478bd9Sstevel@tonic-gate vw->vw_current = vp->vn_children; 29327c478bd9Sstevel@tonic-gate return (rval); 29337c478bd9Sstevel@tonic-gate } 29347c478bd9Sstevel@tonic-gate 29357c478bd9Sstevel@tonic-gate do { 29367c478bd9Sstevel@tonic-gate vw->vw_current = vp->vn_sibling; 29377c478bd9Sstevel@tonic-gate vp = vp->vn_parent; 29387c478bd9Sstevel@tonic-gate } while (vw->vw_current == NULL && vp != NULL); 29397c478bd9Sstevel@tonic-gate 29407c478bd9Sstevel@tonic-gate return (rval); 29417c478bd9Sstevel@tonic-gate } 29427c478bd9Sstevel@tonic-gate 29437c478bd9Sstevel@tonic-gate /* 29447c478bd9Sstevel@tonic-gate * The "vmem_postfix" walk walks the vmem arenas in post-fix order; all 29457c478bd9Sstevel@tonic-gate * children are visited before their parent. We perform the postfix walk 29467c478bd9Sstevel@tonic-gate * iteratively (rather than recursively) to allow mdb to regain control 29477c478bd9Sstevel@tonic-gate * after each callback. 29487c478bd9Sstevel@tonic-gate */ 29497c478bd9Sstevel@tonic-gate int 29507c478bd9Sstevel@tonic-gate vmem_postfix_walk_step(mdb_walk_state_t *wsp) 29517c478bd9Sstevel@tonic-gate { 29527c478bd9Sstevel@tonic-gate vmem_walk_t *vw = wsp->walk_data; 29537c478bd9Sstevel@tonic-gate vmem_node_t *vp = vw->vw_current; 29547c478bd9Sstevel@tonic-gate int rval; 29557c478bd9Sstevel@tonic-gate 29567c478bd9Sstevel@tonic-gate /* 29577c478bd9Sstevel@tonic-gate * If this node is marked, then we know that we have already visited 29587c478bd9Sstevel@tonic-gate * all of its children. If the node has any siblings, they need to 29597c478bd9Sstevel@tonic-gate * be visited next; otherwise, we need to visit the parent. Note 29607c478bd9Sstevel@tonic-gate * that vp->vn_marked will only be zero on the first invocation of 29617c478bd9Sstevel@tonic-gate * the step function. 29627c478bd9Sstevel@tonic-gate */ 29637c478bd9Sstevel@tonic-gate if (vp->vn_marked) { 29647c478bd9Sstevel@tonic-gate if (vp->vn_sibling != NULL) 29657c478bd9Sstevel@tonic-gate vp = vp->vn_sibling; 29667c478bd9Sstevel@tonic-gate else if (vp->vn_parent != NULL) 29677c478bd9Sstevel@tonic-gate vp = vp->vn_parent; 29687c478bd9Sstevel@tonic-gate else { 29697c478bd9Sstevel@tonic-gate /* 29707c478bd9Sstevel@tonic-gate * We have neither a parent, nor a sibling, and we 29717c478bd9Sstevel@tonic-gate * have already been visited; we're done. 29727c478bd9Sstevel@tonic-gate */ 29737c478bd9Sstevel@tonic-gate return (WALK_DONE); 29747c478bd9Sstevel@tonic-gate } 29757c478bd9Sstevel@tonic-gate } 29767c478bd9Sstevel@tonic-gate 29777c478bd9Sstevel@tonic-gate /* 29787c478bd9Sstevel@tonic-gate * Before we visit this node, visit its children. 29797c478bd9Sstevel@tonic-gate */ 29807c478bd9Sstevel@tonic-gate while (vp->vn_children != NULL && !vp->vn_children->vn_marked) 29817c478bd9Sstevel@tonic-gate vp = vp->vn_children; 29827c478bd9Sstevel@tonic-gate 29837c478bd9Sstevel@tonic-gate vp->vn_marked = 1; 29847c478bd9Sstevel@tonic-gate vw->vw_current = vp; 29857c478bd9Sstevel@tonic-gate rval = wsp->walk_callback(vp->vn_addr, &vp->vn_vmem, wsp->walk_cbdata); 29867c478bd9Sstevel@tonic-gate 29877c478bd9Sstevel@tonic-gate return (rval); 29887c478bd9Sstevel@tonic-gate } 29897c478bd9Sstevel@tonic-gate 29907c478bd9Sstevel@tonic-gate void 29917c478bd9Sstevel@tonic-gate vmem_walk_fini(mdb_walk_state_t *wsp) 29927c478bd9Sstevel@tonic-gate { 29937c478bd9Sstevel@tonic-gate vmem_walk_t *vw = wsp->walk_data; 29947c478bd9Sstevel@tonic-gate vmem_node_t *root = vw->vw_root; 29957c478bd9Sstevel@tonic-gate int done; 29967c478bd9Sstevel@tonic-gate 29977c478bd9Sstevel@tonic-gate if (root == NULL) 29987c478bd9Sstevel@tonic-gate return; 29997c478bd9Sstevel@tonic-gate 30007c478bd9Sstevel@tonic-gate if ((vw->vw_root = root->vn_children) != NULL) 30017c478bd9Sstevel@tonic-gate vmem_walk_fini(wsp); 30027c478bd9Sstevel@tonic-gate 30037c478bd9Sstevel@tonic-gate vw->vw_root = root->vn_sibling; 30047c478bd9Sstevel@tonic-gate done = (root->vn_sibling == NULL && root->vn_parent == NULL); 30057c478bd9Sstevel@tonic-gate mdb_free(root, sizeof (vmem_node_t)); 30067c478bd9Sstevel@tonic-gate 30077c478bd9Sstevel@tonic-gate if (done) { 30087c478bd9Sstevel@tonic-gate mdb_free(vw, sizeof (vmem_walk_t)); 30097c478bd9Sstevel@tonic-gate } else { 30107c478bd9Sstevel@tonic-gate vmem_walk_fini(wsp); 30117c478bd9Sstevel@tonic-gate } 30127c478bd9Sstevel@tonic-gate } 30137c478bd9Sstevel@tonic-gate 30147c478bd9Sstevel@tonic-gate typedef struct vmem_seg_walk { 30157c478bd9Sstevel@tonic-gate uint8_t vsw_type; 30167c478bd9Sstevel@tonic-gate uintptr_t vsw_start; 30177c478bd9Sstevel@tonic-gate uintptr_t vsw_current; 30187c478bd9Sstevel@tonic-gate } vmem_seg_walk_t; 30197c478bd9Sstevel@tonic-gate 30207c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 30217c478bd9Sstevel@tonic-gate int 30227c478bd9Sstevel@tonic-gate vmem_seg_walk_common_init(mdb_walk_state_t *wsp, uint8_t type, char *name) 30237c478bd9Sstevel@tonic-gate { 30247c478bd9Sstevel@tonic-gate vmem_seg_walk_t *vsw; 30257c478bd9Sstevel@tonic-gate 30267c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 30277c478bd9Sstevel@tonic-gate mdb_warn("vmem_%s does not support global walks\n", name); 30287c478bd9Sstevel@tonic-gate return (WALK_ERR); 30297c478bd9Sstevel@tonic-gate } 30307c478bd9Sstevel@tonic-gate 30317c478bd9Sstevel@tonic-gate wsp->walk_data = vsw = mdb_alloc(sizeof (vmem_seg_walk_t), UM_SLEEP); 30327c478bd9Sstevel@tonic-gate 30337c478bd9Sstevel@tonic-gate vsw->vsw_type = type; 30347c478bd9Sstevel@tonic-gate vsw->vsw_start = wsp->walk_addr + offsetof(vmem_t, vm_seg0); 30357c478bd9Sstevel@tonic-gate vsw->vsw_current = vsw->vsw_start; 30367c478bd9Sstevel@tonic-gate 30377c478bd9Sstevel@tonic-gate return (WALK_NEXT); 30387c478bd9Sstevel@tonic-gate } 30397c478bd9Sstevel@tonic-gate 30407c478bd9Sstevel@tonic-gate /* 30417c478bd9Sstevel@tonic-gate * vmem segments can't have type 0 (this should be added to vmem_impl.h). 30427c478bd9Sstevel@tonic-gate */ 30437c478bd9Sstevel@tonic-gate #define VMEM_NONE 0 30447c478bd9Sstevel@tonic-gate 30457c478bd9Sstevel@tonic-gate int 30467c478bd9Sstevel@tonic-gate vmem_alloc_walk_init(mdb_walk_state_t *wsp) 30477c478bd9Sstevel@tonic-gate { 30487c478bd9Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_ALLOC, "alloc")); 30497c478bd9Sstevel@tonic-gate } 30507c478bd9Sstevel@tonic-gate 30517c478bd9Sstevel@tonic-gate int 30527c478bd9Sstevel@tonic-gate vmem_free_walk_init(mdb_walk_state_t *wsp) 30537c478bd9Sstevel@tonic-gate { 30547c478bd9Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_FREE, "free")); 30557c478bd9Sstevel@tonic-gate } 30567c478bd9Sstevel@tonic-gate 30577c478bd9Sstevel@tonic-gate int 30587c478bd9Sstevel@tonic-gate vmem_span_walk_init(mdb_walk_state_t *wsp) 30597c478bd9Sstevel@tonic-gate { 30607c478bd9Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_SPAN, "span")); 30617c478bd9Sstevel@tonic-gate } 30627c478bd9Sstevel@tonic-gate 30637c478bd9Sstevel@tonic-gate int 30647c478bd9Sstevel@tonic-gate vmem_seg_walk_init(mdb_walk_state_t *wsp) 30657c478bd9Sstevel@tonic-gate { 30667c478bd9Sstevel@tonic-gate return (vmem_seg_walk_common_init(wsp, VMEM_NONE, "seg")); 30677c478bd9Sstevel@tonic-gate } 30687c478bd9Sstevel@tonic-gate 30697c478bd9Sstevel@tonic-gate int 30707c478bd9Sstevel@tonic-gate vmem_seg_walk_step(mdb_walk_state_t *wsp) 30717c478bd9Sstevel@tonic-gate { 30727c478bd9Sstevel@tonic-gate vmem_seg_t seg; 30737c478bd9Sstevel@tonic-gate vmem_seg_walk_t *vsw = wsp->walk_data; 30747c478bd9Sstevel@tonic-gate uintptr_t addr = vsw->vsw_current; 30757c478bd9Sstevel@tonic-gate static size_t seg_size = 0; 30767c478bd9Sstevel@tonic-gate int rval; 30777c478bd9Sstevel@tonic-gate 30787c478bd9Sstevel@tonic-gate if (!seg_size) { 30797c478bd9Sstevel@tonic-gate if (mdb_readvar(&seg_size, "vmem_seg_size") == -1) { 30807c478bd9Sstevel@tonic-gate mdb_warn("failed to read 'vmem_seg_size'"); 30817c478bd9Sstevel@tonic-gate seg_size = sizeof (vmem_seg_t); 30827c478bd9Sstevel@tonic-gate } 30837c478bd9Sstevel@tonic-gate } 30847c478bd9Sstevel@tonic-gate 30857c478bd9Sstevel@tonic-gate if (seg_size < sizeof (seg)) 30867c478bd9Sstevel@tonic-gate bzero((caddr_t)&seg + seg_size, sizeof (seg) - seg_size); 30877c478bd9Sstevel@tonic-gate 30887c478bd9Sstevel@tonic-gate if (mdb_vread(&seg, seg_size, addr) == -1) { 30897c478bd9Sstevel@tonic-gate mdb_warn("couldn't read vmem_seg at %p", addr); 30907c478bd9Sstevel@tonic-gate return (WALK_ERR); 30917c478bd9Sstevel@tonic-gate } 30927c478bd9Sstevel@tonic-gate 30937c478bd9Sstevel@tonic-gate vsw->vsw_current = (uintptr_t)seg.vs_anext; 30947c478bd9Sstevel@tonic-gate if (vsw->vsw_type != VMEM_NONE && seg.vs_type != vsw->vsw_type) { 30957c478bd9Sstevel@tonic-gate rval = WALK_NEXT; 30967c478bd9Sstevel@tonic-gate } else { 30977c478bd9Sstevel@tonic-gate rval = wsp->walk_callback(addr, &seg, wsp->walk_cbdata); 30987c478bd9Sstevel@tonic-gate } 30997c478bd9Sstevel@tonic-gate 31007c478bd9Sstevel@tonic-gate if (vsw->vsw_current == vsw->vsw_start) 31017c478bd9Sstevel@tonic-gate return (WALK_DONE); 31027c478bd9Sstevel@tonic-gate 31037c478bd9Sstevel@tonic-gate return (rval); 31047c478bd9Sstevel@tonic-gate } 31057c478bd9Sstevel@tonic-gate 31067c478bd9Sstevel@tonic-gate void 31077c478bd9Sstevel@tonic-gate vmem_seg_walk_fini(mdb_walk_state_t *wsp) 31087c478bd9Sstevel@tonic-gate { 31097c478bd9Sstevel@tonic-gate vmem_seg_walk_t *vsw = wsp->walk_data; 31107c478bd9Sstevel@tonic-gate 31117c478bd9Sstevel@tonic-gate mdb_free(vsw, sizeof (vmem_seg_walk_t)); 31127c478bd9Sstevel@tonic-gate } 31137c478bd9Sstevel@tonic-gate 31147c478bd9Sstevel@tonic-gate #define VMEM_NAMEWIDTH 22 31157c478bd9Sstevel@tonic-gate 31167c478bd9Sstevel@tonic-gate int 31177c478bd9Sstevel@tonic-gate vmem(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 31187c478bd9Sstevel@tonic-gate { 31197c478bd9Sstevel@tonic-gate vmem_t v, parent; 31207c478bd9Sstevel@tonic-gate vmem_kstat_t *vkp = &v.vm_kstat; 31217c478bd9Sstevel@tonic-gate uintptr_t paddr; 31227c478bd9Sstevel@tonic-gate int ident = 0; 31237c478bd9Sstevel@tonic-gate char c[VMEM_NAMEWIDTH]; 31247c478bd9Sstevel@tonic-gate 31257c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 31267c478bd9Sstevel@tonic-gate if (mdb_walk_dcmd("vmem", "vmem", argc, argv) == -1) { 31277c478bd9Sstevel@tonic-gate mdb_warn("can't walk vmem"); 31287c478bd9Sstevel@tonic-gate return (DCMD_ERR); 31297c478bd9Sstevel@tonic-gate } 31307c478bd9Sstevel@tonic-gate return (DCMD_OK); 31317c478bd9Sstevel@tonic-gate } 31327c478bd9Sstevel@tonic-gate 31337c478bd9Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) 31347c478bd9Sstevel@tonic-gate mdb_printf("%-?s %-*s %10s %12s %9s %5s\n", 31357c478bd9Sstevel@tonic-gate "ADDR", VMEM_NAMEWIDTH, "NAME", "INUSE", 31367c478bd9Sstevel@tonic-gate "TOTAL", "SUCCEED", "FAIL"); 31377c478bd9Sstevel@tonic-gate 31387c478bd9Sstevel@tonic-gate if (mdb_vread(&v, sizeof (v), addr) == -1) { 31397c478bd9Sstevel@tonic-gate mdb_warn("couldn't read vmem at %p", addr); 31407c478bd9Sstevel@tonic-gate return (DCMD_ERR); 31417c478bd9Sstevel@tonic-gate } 31427c478bd9Sstevel@tonic-gate 31437c478bd9Sstevel@tonic-gate for (paddr = (uintptr_t)v.vm_source; paddr != NULL; ident += 2) { 31447c478bd9Sstevel@tonic-gate if (mdb_vread(&parent, sizeof (parent), paddr) == -1) { 31457c478bd9Sstevel@tonic-gate mdb_warn("couldn't trace %p's ancestry", addr); 31467c478bd9Sstevel@tonic-gate ident = 0; 31477c478bd9Sstevel@tonic-gate break; 31487c478bd9Sstevel@tonic-gate } 31497c478bd9Sstevel@tonic-gate paddr = (uintptr_t)parent.vm_source; 31507c478bd9Sstevel@tonic-gate } 31517c478bd9Sstevel@tonic-gate 31527c478bd9Sstevel@tonic-gate (void) mdb_snprintf(c, VMEM_NAMEWIDTH, "%*s%s", ident, "", v.vm_name); 31537c478bd9Sstevel@tonic-gate 31547c478bd9Sstevel@tonic-gate mdb_printf("%0?p %-*s %10llu %12llu %9llu %5llu\n", 31557c478bd9Sstevel@tonic-gate addr, VMEM_NAMEWIDTH, c, 31567c478bd9Sstevel@tonic-gate vkp->vk_mem_inuse.value.ui64, vkp->vk_mem_total.value.ui64, 31577c478bd9Sstevel@tonic-gate vkp->vk_alloc.value.ui64, vkp->vk_fail.value.ui64); 31587c478bd9Sstevel@tonic-gate 31597c478bd9Sstevel@tonic-gate return (DCMD_OK); 31607c478bd9Sstevel@tonic-gate } 31617c478bd9Sstevel@tonic-gate 31627c478bd9Sstevel@tonic-gate void 31637c478bd9Sstevel@tonic-gate vmem_seg_help(void) 31647c478bd9Sstevel@tonic-gate { 31657c478bd9Sstevel@tonic-gate mdb_printf("%s\n", 31667c478bd9Sstevel@tonic-gate "Display the contents of vmem_seg_ts, with optional filtering.\n" 31677c478bd9Sstevel@tonic-gate "\n" 31687c478bd9Sstevel@tonic-gate "A vmem_seg_t represents a range of addresses (or arbitrary numbers),\n" 31697c478bd9Sstevel@tonic-gate "representing a single chunk of data. Only ALLOC segments have debugging\n" 31707c478bd9Sstevel@tonic-gate "information.\n"); 31717c478bd9Sstevel@tonic-gate mdb_dec_indent(2); 31727c478bd9Sstevel@tonic-gate mdb_printf("%<b>OPTIONS%</b>\n"); 31737c478bd9Sstevel@tonic-gate mdb_inc_indent(2); 31747c478bd9Sstevel@tonic-gate mdb_printf("%s", 31757c478bd9Sstevel@tonic-gate " -v Display the full content of the vmem_seg, including its stack trace\n" 31767c478bd9Sstevel@tonic-gate " -s report the size of the segment, instead of the end address\n" 31777c478bd9Sstevel@tonic-gate " -c caller\n" 31787c478bd9Sstevel@tonic-gate " filter out segments without the function/PC in their stack trace\n" 31797c478bd9Sstevel@tonic-gate " -e earliest\n" 31807c478bd9Sstevel@tonic-gate " filter out segments timestamped before earliest\n" 31817c478bd9Sstevel@tonic-gate " -l latest\n" 31827c478bd9Sstevel@tonic-gate " filter out segments timestamped after latest\n" 31837c478bd9Sstevel@tonic-gate " -m minsize\n" 31847c478bd9Sstevel@tonic-gate " filer out segments smaller than minsize\n" 31857c478bd9Sstevel@tonic-gate " -M maxsize\n" 31867c478bd9Sstevel@tonic-gate " filer out segments larger than maxsize\n" 31877c478bd9Sstevel@tonic-gate " -t thread\n" 31887c478bd9Sstevel@tonic-gate " filter out segments not involving thread\n" 31897c478bd9Sstevel@tonic-gate " -T type\n" 31907c478bd9Sstevel@tonic-gate " filter out segments not of type 'type'\n" 31917c478bd9Sstevel@tonic-gate " type is one of: ALLOC/FREE/SPAN/ROTOR/WALKER\n"); 31927c478bd9Sstevel@tonic-gate } 31937c478bd9Sstevel@tonic-gate 31947c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 31957c478bd9Sstevel@tonic-gate int 31967c478bd9Sstevel@tonic-gate vmem_seg(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 31977c478bd9Sstevel@tonic-gate { 31987c478bd9Sstevel@tonic-gate vmem_seg_t vs; 31997c478bd9Sstevel@tonic-gate pc_t *stk = vs.vs_stack; 32007c478bd9Sstevel@tonic-gate uintptr_t sz; 32017c478bd9Sstevel@tonic-gate uint8_t t; 32027c478bd9Sstevel@tonic-gate const char *type = NULL; 32037c478bd9Sstevel@tonic-gate GElf_Sym sym; 32047c478bd9Sstevel@tonic-gate char c[MDB_SYM_NAMLEN]; 32057c478bd9Sstevel@tonic-gate int no_debug; 32067c478bd9Sstevel@tonic-gate int i; 32077c478bd9Sstevel@tonic-gate int depth; 32087c478bd9Sstevel@tonic-gate uintptr_t laddr, haddr; 32097c478bd9Sstevel@tonic-gate 32107c478bd9Sstevel@tonic-gate uintptr_t caller = NULL, thread = NULL; 32117c478bd9Sstevel@tonic-gate uintptr_t minsize = 0, maxsize = 0; 32127c478bd9Sstevel@tonic-gate 32137c478bd9Sstevel@tonic-gate hrtime_t earliest = 0, latest = 0; 32147c478bd9Sstevel@tonic-gate 32157c478bd9Sstevel@tonic-gate uint_t size = 0; 32167c478bd9Sstevel@tonic-gate uint_t verbose = 0; 32177c478bd9Sstevel@tonic-gate 32187c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 32197c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 32207c478bd9Sstevel@tonic-gate 32217c478bd9Sstevel@tonic-gate if (mdb_getopts(argc, argv, 32227c478bd9Sstevel@tonic-gate 'c', MDB_OPT_UINTPTR, &caller, 32237c478bd9Sstevel@tonic-gate 'e', MDB_OPT_UINT64, &earliest, 32247c478bd9Sstevel@tonic-gate 'l', MDB_OPT_UINT64, &latest, 32257c478bd9Sstevel@tonic-gate 's', MDB_OPT_SETBITS, TRUE, &size, 32267c478bd9Sstevel@tonic-gate 'm', MDB_OPT_UINTPTR, &minsize, 32277c478bd9Sstevel@tonic-gate 'M', MDB_OPT_UINTPTR, &maxsize, 32287c478bd9Sstevel@tonic-gate 't', MDB_OPT_UINTPTR, &thread, 32297c478bd9Sstevel@tonic-gate 'T', MDB_OPT_STR, &type, 32307c478bd9Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &verbose, 32317c478bd9Sstevel@tonic-gate NULL) != argc) 32327c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 32337c478bd9Sstevel@tonic-gate 32347c478bd9Sstevel@tonic-gate if (DCMD_HDRSPEC(flags) && !(flags & DCMD_PIPE_OUT)) { 32357c478bd9Sstevel@tonic-gate if (verbose) { 32367c478bd9Sstevel@tonic-gate mdb_printf("%16s %4s %16s %16s %16s\n" 32377c478bd9Sstevel@tonic-gate "%<u>%16s %4s %16s %16s %16s%</u>\n", 32387c478bd9Sstevel@tonic-gate "ADDR", "TYPE", "START", "END", "SIZE", 32397c478bd9Sstevel@tonic-gate "", "", "THREAD", "TIMESTAMP", ""); 32407c478bd9Sstevel@tonic-gate } else { 32417c478bd9Sstevel@tonic-gate mdb_printf("%?s %4s %?s %?s %s\n", "ADDR", "TYPE", 32427c478bd9Sstevel@tonic-gate "START", size? "SIZE" : "END", "WHO"); 32437c478bd9Sstevel@tonic-gate } 32447c478bd9Sstevel@tonic-gate } 32457c478bd9Sstevel@tonic-gate 32467c478bd9Sstevel@tonic-gate if (mdb_vread(&vs, sizeof (vs), addr) == -1) { 32477c478bd9Sstevel@tonic-gate mdb_warn("couldn't read vmem_seg at %p", addr); 32487c478bd9Sstevel@tonic-gate return (DCMD_ERR); 32497c478bd9Sstevel@tonic-gate } 32507c478bd9Sstevel@tonic-gate 32517c478bd9Sstevel@tonic-gate if (type != NULL) { 32527c478bd9Sstevel@tonic-gate if (strcmp(type, "ALLC") == 0 || strcmp(type, "ALLOC") == 0) 32537c478bd9Sstevel@tonic-gate t = VMEM_ALLOC; 32547c478bd9Sstevel@tonic-gate else if (strcmp(type, "FREE") == 0) 32557c478bd9Sstevel@tonic-gate t = VMEM_FREE; 32567c478bd9Sstevel@tonic-gate else if (strcmp(type, "SPAN") == 0) 32577c478bd9Sstevel@tonic-gate t = VMEM_SPAN; 32587c478bd9Sstevel@tonic-gate else if (strcmp(type, "ROTR") == 0 || 32597c478bd9Sstevel@tonic-gate strcmp(type, "ROTOR") == 0) 32607c478bd9Sstevel@tonic-gate t = VMEM_ROTOR; 32617c478bd9Sstevel@tonic-gate else if (strcmp(type, "WLKR") == 0 || 32627c478bd9Sstevel@tonic-gate strcmp(type, "WALKER") == 0) 32637c478bd9Sstevel@tonic-gate t = VMEM_WALKER; 32647c478bd9Sstevel@tonic-gate else { 32657c478bd9Sstevel@tonic-gate mdb_warn("\"%s\" is not a recognized vmem_seg type\n", 32667c478bd9Sstevel@tonic-gate type); 32677c478bd9Sstevel@tonic-gate return (DCMD_ERR); 32687c478bd9Sstevel@tonic-gate } 32697c478bd9Sstevel@tonic-gate 32707c478bd9Sstevel@tonic-gate if (vs.vs_type != t) 32717c478bd9Sstevel@tonic-gate return (DCMD_OK); 32727c478bd9Sstevel@tonic-gate } 32737c478bd9Sstevel@tonic-gate 32747c478bd9Sstevel@tonic-gate sz = vs.vs_end - vs.vs_start; 32757c478bd9Sstevel@tonic-gate 32767c478bd9Sstevel@tonic-gate if (minsize != 0 && sz < minsize) 32777c478bd9Sstevel@tonic-gate return (DCMD_OK); 32787c478bd9Sstevel@tonic-gate 32797c478bd9Sstevel@tonic-gate if (maxsize != 0 && sz > maxsize) 32807c478bd9Sstevel@tonic-gate return (DCMD_OK); 32817c478bd9Sstevel@tonic-gate 32827c478bd9Sstevel@tonic-gate t = vs.vs_type; 32837c478bd9Sstevel@tonic-gate depth = vs.vs_depth; 32847c478bd9Sstevel@tonic-gate 32857c478bd9Sstevel@tonic-gate /* 32867c478bd9Sstevel@tonic-gate * debug info, when present, is only accurate for VMEM_ALLOC segments 32877c478bd9Sstevel@tonic-gate */ 32887c478bd9Sstevel@tonic-gate no_debug = (t != VMEM_ALLOC) || 32897c478bd9Sstevel@tonic-gate (depth == 0 || depth > VMEM_STACK_DEPTH); 32907c478bd9Sstevel@tonic-gate 32917c478bd9Sstevel@tonic-gate if (no_debug) { 32927c478bd9Sstevel@tonic-gate if (caller != NULL || thread != NULL || earliest != 0 || 32937c478bd9Sstevel@tonic-gate latest != 0) 32947c478bd9Sstevel@tonic-gate return (DCMD_OK); /* not enough info */ 32957c478bd9Sstevel@tonic-gate } else { 32967c478bd9Sstevel@tonic-gate if (caller != NULL) { 32977c478bd9Sstevel@tonic-gate laddr = caller; 32987c478bd9Sstevel@tonic-gate haddr = caller + sizeof (caller); 32997c478bd9Sstevel@tonic-gate 33007c478bd9Sstevel@tonic-gate if (mdb_lookup_by_addr(caller, MDB_SYM_FUZZY, c, 33017c478bd9Sstevel@tonic-gate sizeof (c), &sym) != -1 && 33027c478bd9Sstevel@tonic-gate caller == (uintptr_t)sym.st_value) { 33037c478bd9Sstevel@tonic-gate /* 33047c478bd9Sstevel@tonic-gate * We were provided an exact symbol value; any 33057c478bd9Sstevel@tonic-gate * address in the function is valid. 33067c478bd9Sstevel@tonic-gate */ 33077c478bd9Sstevel@tonic-gate laddr = (uintptr_t)sym.st_value; 33087c478bd9Sstevel@tonic-gate haddr = (uintptr_t)sym.st_value + sym.st_size; 33097c478bd9Sstevel@tonic-gate } 33107c478bd9Sstevel@tonic-gate 33117c478bd9Sstevel@tonic-gate for (i = 0; i < depth; i++) 33127c478bd9Sstevel@tonic-gate if (vs.vs_stack[i] >= laddr && 33137c478bd9Sstevel@tonic-gate vs.vs_stack[i] < haddr) 33147c478bd9Sstevel@tonic-gate break; 33157c478bd9Sstevel@tonic-gate 33167c478bd9Sstevel@tonic-gate if (i == depth) 33177c478bd9Sstevel@tonic-gate return (DCMD_OK); 33187c478bd9Sstevel@tonic-gate } 33197c478bd9Sstevel@tonic-gate 33207c478bd9Sstevel@tonic-gate if (thread != NULL && (uintptr_t)vs.vs_thread != thread) 33217c478bd9Sstevel@tonic-gate return (DCMD_OK); 33227c478bd9Sstevel@tonic-gate 33237c478bd9Sstevel@tonic-gate if (earliest != 0 && vs.vs_timestamp < earliest) 33247c478bd9Sstevel@tonic-gate return (DCMD_OK); 33257c478bd9Sstevel@tonic-gate 33267c478bd9Sstevel@tonic-gate if (latest != 0 && vs.vs_timestamp > latest) 33277c478bd9Sstevel@tonic-gate return (DCMD_OK); 33287c478bd9Sstevel@tonic-gate } 33297c478bd9Sstevel@tonic-gate 33307c478bd9Sstevel@tonic-gate type = (t == VMEM_ALLOC ? "ALLC" : 33317c478bd9Sstevel@tonic-gate t == VMEM_FREE ? "FREE" : 33327c478bd9Sstevel@tonic-gate t == VMEM_SPAN ? "SPAN" : 33337c478bd9Sstevel@tonic-gate t == VMEM_ROTOR ? "ROTR" : 33347c478bd9Sstevel@tonic-gate t == VMEM_WALKER ? "WLKR" : 33357c478bd9Sstevel@tonic-gate "????"); 33367c478bd9Sstevel@tonic-gate 33377c478bd9Sstevel@tonic-gate if (flags & DCMD_PIPE_OUT) { 33387c478bd9Sstevel@tonic-gate mdb_printf("%#lr\n", addr); 33397c478bd9Sstevel@tonic-gate return (DCMD_OK); 33407c478bd9Sstevel@tonic-gate } 33417c478bd9Sstevel@tonic-gate 33427c478bd9Sstevel@tonic-gate if (verbose) { 33437c478bd9Sstevel@tonic-gate mdb_printf("%<b>%16p%</b> %4s %16p %16p %16d\n", 33447c478bd9Sstevel@tonic-gate addr, type, vs.vs_start, vs.vs_end, sz); 33457c478bd9Sstevel@tonic-gate 33467c478bd9Sstevel@tonic-gate if (no_debug) 33477c478bd9Sstevel@tonic-gate return (DCMD_OK); 33487c478bd9Sstevel@tonic-gate 33497c478bd9Sstevel@tonic-gate mdb_printf("%16s %4s %16p %16llx\n", 33507c478bd9Sstevel@tonic-gate "", "", vs.vs_thread, vs.vs_timestamp); 33517c478bd9Sstevel@tonic-gate 33527c478bd9Sstevel@tonic-gate mdb_inc_indent(17); 33537c478bd9Sstevel@tonic-gate for (i = 0; i < depth; i++) { 33547c478bd9Sstevel@tonic-gate mdb_printf("%a\n", stk[i]); 33557c478bd9Sstevel@tonic-gate } 33567c478bd9Sstevel@tonic-gate mdb_dec_indent(17); 33577c478bd9Sstevel@tonic-gate mdb_printf("\n"); 33587c478bd9Sstevel@tonic-gate } else { 33597c478bd9Sstevel@tonic-gate mdb_printf("%0?p %4s %0?p %0?p", addr, type, 33607c478bd9Sstevel@tonic-gate vs.vs_start, size? sz : vs.vs_end); 33617c478bd9Sstevel@tonic-gate 33627c478bd9Sstevel@tonic-gate if (no_debug) { 33637c478bd9Sstevel@tonic-gate mdb_printf("\n"); 33647c478bd9Sstevel@tonic-gate return (DCMD_OK); 33657c478bd9Sstevel@tonic-gate } 33667c478bd9Sstevel@tonic-gate 33677c478bd9Sstevel@tonic-gate for (i = 0; i < depth; i++) { 33687c478bd9Sstevel@tonic-gate if (mdb_lookup_by_addr(stk[i], MDB_SYM_FUZZY, 33697c478bd9Sstevel@tonic-gate c, sizeof (c), &sym) == -1) 33707c478bd9Sstevel@tonic-gate continue; 33717c478bd9Sstevel@tonic-gate if (strncmp(c, "vmem_", 5) == 0) 33727c478bd9Sstevel@tonic-gate continue; 33737c478bd9Sstevel@tonic-gate break; 33747c478bd9Sstevel@tonic-gate } 33757c478bd9Sstevel@tonic-gate mdb_printf(" %a\n", stk[i]); 33767c478bd9Sstevel@tonic-gate } 33777c478bd9Sstevel@tonic-gate return (DCMD_OK); 33787c478bd9Sstevel@tonic-gate } 33797c478bd9Sstevel@tonic-gate 33807c478bd9Sstevel@tonic-gate typedef struct kmalog_data { 33817c478bd9Sstevel@tonic-gate uintptr_t kma_addr; 33827c478bd9Sstevel@tonic-gate hrtime_t kma_newest; 33837c478bd9Sstevel@tonic-gate } kmalog_data_t; 33847c478bd9Sstevel@tonic-gate 33857c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 33867c478bd9Sstevel@tonic-gate static int 33877c478bd9Sstevel@tonic-gate showbc(uintptr_t addr, const kmem_bufctl_audit_t *bcp, kmalog_data_t *kma) 33887c478bd9Sstevel@tonic-gate { 33897c478bd9Sstevel@tonic-gate char name[KMEM_CACHE_NAMELEN + 1]; 33907c478bd9Sstevel@tonic-gate hrtime_t delta; 33917c478bd9Sstevel@tonic-gate int i, depth; 33927c478bd9Sstevel@tonic-gate size_t bufsize; 33937c478bd9Sstevel@tonic-gate 33947c478bd9Sstevel@tonic-gate if (bcp->bc_timestamp == 0) 33957c478bd9Sstevel@tonic-gate return (WALK_DONE); 33967c478bd9Sstevel@tonic-gate 33977c478bd9Sstevel@tonic-gate if (kma->kma_newest == 0) 33987c478bd9Sstevel@tonic-gate kma->kma_newest = bcp->bc_timestamp; 33997c478bd9Sstevel@tonic-gate 34007c478bd9Sstevel@tonic-gate if (kma->kma_addr) { 34017c478bd9Sstevel@tonic-gate if (mdb_vread(&bufsize, sizeof (bufsize), 34027c478bd9Sstevel@tonic-gate (uintptr_t)&bcp->bc_cache->cache_bufsize) == -1) { 34037c478bd9Sstevel@tonic-gate mdb_warn( 34047c478bd9Sstevel@tonic-gate "failed to read cache_bufsize for cache at %p", 34057c478bd9Sstevel@tonic-gate bcp->bc_cache); 34067c478bd9Sstevel@tonic-gate return (WALK_ERR); 34077c478bd9Sstevel@tonic-gate } 34087c478bd9Sstevel@tonic-gate 34097c478bd9Sstevel@tonic-gate if (kma->kma_addr < (uintptr_t)bcp->bc_addr || 34107c478bd9Sstevel@tonic-gate kma->kma_addr >= (uintptr_t)bcp->bc_addr + bufsize) 34117c478bd9Sstevel@tonic-gate return (WALK_NEXT); 34127c478bd9Sstevel@tonic-gate } 34137c478bd9Sstevel@tonic-gate 34147c478bd9Sstevel@tonic-gate delta = kma->kma_newest - bcp->bc_timestamp; 34157c478bd9Sstevel@tonic-gate depth = MIN(bcp->bc_depth, KMEM_STACK_DEPTH); 34167c478bd9Sstevel@tonic-gate 34177c478bd9Sstevel@tonic-gate if (mdb_readstr(name, sizeof (name), (uintptr_t) 34187c478bd9Sstevel@tonic-gate &bcp->bc_cache->cache_name) <= 0) 34197c478bd9Sstevel@tonic-gate (void) mdb_snprintf(name, sizeof (name), "%a", bcp->bc_cache); 34207c478bd9Sstevel@tonic-gate 34217c478bd9Sstevel@tonic-gate mdb_printf("\nT-%lld.%09lld addr=%p %s\n", 34227c478bd9Sstevel@tonic-gate delta / NANOSEC, delta % NANOSEC, bcp->bc_addr, name); 34237c478bd9Sstevel@tonic-gate 34247c478bd9Sstevel@tonic-gate for (i = 0; i < depth; i++) 34257c478bd9Sstevel@tonic-gate mdb_printf("\t %a\n", bcp->bc_stack[i]); 34267c478bd9Sstevel@tonic-gate 34277c478bd9Sstevel@tonic-gate return (WALK_NEXT); 34287c478bd9Sstevel@tonic-gate } 34297c478bd9Sstevel@tonic-gate 34307c478bd9Sstevel@tonic-gate int 34317c478bd9Sstevel@tonic-gate kmalog(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 34327c478bd9Sstevel@tonic-gate { 34337c478bd9Sstevel@tonic-gate const char *logname = "kmem_transaction_log"; 34347c478bd9Sstevel@tonic-gate kmalog_data_t kma; 34357c478bd9Sstevel@tonic-gate 34367c478bd9Sstevel@tonic-gate if (argc > 1) 34377c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 34387c478bd9Sstevel@tonic-gate 34397c478bd9Sstevel@tonic-gate kma.kma_newest = 0; 34407c478bd9Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) 34417c478bd9Sstevel@tonic-gate kma.kma_addr = addr; 34427c478bd9Sstevel@tonic-gate else 34437c478bd9Sstevel@tonic-gate kma.kma_addr = NULL; 34447c478bd9Sstevel@tonic-gate 34457c478bd9Sstevel@tonic-gate if (argc > 0) { 34467c478bd9Sstevel@tonic-gate if (argv->a_type != MDB_TYPE_STRING) 34477c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 34487c478bd9Sstevel@tonic-gate if (strcmp(argv->a_un.a_str, "fail") == 0) 34497c478bd9Sstevel@tonic-gate logname = "kmem_failure_log"; 34507c478bd9Sstevel@tonic-gate else if (strcmp(argv->a_un.a_str, "slab") == 0) 34517c478bd9Sstevel@tonic-gate logname = "kmem_slab_log"; 34527c478bd9Sstevel@tonic-gate else 34537c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 34547c478bd9Sstevel@tonic-gate } 34557c478bd9Sstevel@tonic-gate 34567c478bd9Sstevel@tonic-gate if (mdb_readvar(&addr, logname) == -1) { 34577c478bd9Sstevel@tonic-gate mdb_warn("failed to read %s log header pointer"); 34587c478bd9Sstevel@tonic-gate return (DCMD_ERR); 34597c478bd9Sstevel@tonic-gate } 34607c478bd9Sstevel@tonic-gate 34617c478bd9Sstevel@tonic-gate if (mdb_pwalk("kmem_log", (mdb_walk_cb_t)showbc, &kma, addr) == -1) { 34627c478bd9Sstevel@tonic-gate mdb_warn("failed to walk kmem log"); 34637c478bd9Sstevel@tonic-gate return (DCMD_ERR); 34647c478bd9Sstevel@tonic-gate } 34657c478bd9Sstevel@tonic-gate 34667c478bd9Sstevel@tonic-gate return (DCMD_OK); 34677c478bd9Sstevel@tonic-gate } 34687c478bd9Sstevel@tonic-gate 34697c478bd9Sstevel@tonic-gate /* 34707c478bd9Sstevel@tonic-gate * As the final lure for die-hard crash(1M) users, we provide ::kmausers here. 34717c478bd9Sstevel@tonic-gate * The first piece is a structure which we use to accumulate kmem_cache_t 34727c478bd9Sstevel@tonic-gate * addresses of interest. The kmc_add is used as a callback for the kmem_cache 34737c478bd9Sstevel@tonic-gate * walker; we either add all caches, or ones named explicitly as arguments. 34747c478bd9Sstevel@tonic-gate */ 34757c478bd9Sstevel@tonic-gate 34767c478bd9Sstevel@tonic-gate typedef struct kmclist { 34777c478bd9Sstevel@tonic-gate const char *kmc_name; /* Name to match (or NULL) */ 34787c478bd9Sstevel@tonic-gate uintptr_t *kmc_caches; /* List of kmem_cache_t addrs */ 34797c478bd9Sstevel@tonic-gate int kmc_nelems; /* Num entries in kmc_caches */ 34807c478bd9Sstevel@tonic-gate int kmc_size; /* Size of kmc_caches array */ 34817c478bd9Sstevel@tonic-gate } kmclist_t; 34827c478bd9Sstevel@tonic-gate 34837c478bd9Sstevel@tonic-gate static int 34847c478bd9Sstevel@tonic-gate kmc_add(uintptr_t addr, const kmem_cache_t *cp, kmclist_t *kmc) 34857c478bd9Sstevel@tonic-gate { 34867c478bd9Sstevel@tonic-gate void *p; 34877c478bd9Sstevel@tonic-gate int s; 34887c478bd9Sstevel@tonic-gate 34897c478bd9Sstevel@tonic-gate if (kmc->kmc_name == NULL || 34907c478bd9Sstevel@tonic-gate strcmp(cp->cache_name, kmc->kmc_name) == 0) { 34917c478bd9Sstevel@tonic-gate /* 34927c478bd9Sstevel@tonic-gate * If we have a match, grow our array (if necessary), and then 34937c478bd9Sstevel@tonic-gate * add the virtual address of the matching cache to our list. 34947c478bd9Sstevel@tonic-gate */ 34957c478bd9Sstevel@tonic-gate if (kmc->kmc_nelems >= kmc->kmc_size) { 34967c478bd9Sstevel@tonic-gate s = kmc->kmc_size ? kmc->kmc_size * 2 : 256; 34977c478bd9Sstevel@tonic-gate p = mdb_alloc(sizeof (uintptr_t) * s, UM_SLEEP | UM_GC); 34987c478bd9Sstevel@tonic-gate 34997c478bd9Sstevel@tonic-gate bcopy(kmc->kmc_caches, p, 35007c478bd9Sstevel@tonic-gate sizeof (uintptr_t) * kmc->kmc_size); 35017c478bd9Sstevel@tonic-gate 35027c478bd9Sstevel@tonic-gate kmc->kmc_caches = p; 35037c478bd9Sstevel@tonic-gate kmc->kmc_size = s; 35047c478bd9Sstevel@tonic-gate } 35057c478bd9Sstevel@tonic-gate 35067c478bd9Sstevel@tonic-gate kmc->kmc_caches[kmc->kmc_nelems++] = addr; 35077c478bd9Sstevel@tonic-gate return (kmc->kmc_name ? WALK_DONE : WALK_NEXT); 35087c478bd9Sstevel@tonic-gate } 35097c478bd9Sstevel@tonic-gate 35107c478bd9Sstevel@tonic-gate return (WALK_NEXT); 35117c478bd9Sstevel@tonic-gate } 35127c478bd9Sstevel@tonic-gate 35137c478bd9Sstevel@tonic-gate /* 35147c478bd9Sstevel@tonic-gate * The second piece of ::kmausers is a hash table of allocations. Each 35157c478bd9Sstevel@tonic-gate * allocation owner is identified by its stack trace and data_size. We then 35167c478bd9Sstevel@tonic-gate * track the total bytes of all such allocations, and the number of allocations 35177c478bd9Sstevel@tonic-gate * to report at the end. Once we have a list of caches, we walk through the 35187c478bd9Sstevel@tonic-gate * allocated bufctls of each, and update our hash table accordingly. 35197c478bd9Sstevel@tonic-gate */ 35207c478bd9Sstevel@tonic-gate 35217c478bd9Sstevel@tonic-gate typedef struct kmowner { 35227c478bd9Sstevel@tonic-gate struct kmowner *kmo_head; /* First hash elt in bucket */ 35237c478bd9Sstevel@tonic-gate struct kmowner *kmo_next; /* Next hash elt in chain */ 35247c478bd9Sstevel@tonic-gate size_t kmo_signature; /* Hash table signature */ 35257c478bd9Sstevel@tonic-gate uint_t kmo_num; /* Number of allocations */ 35267c478bd9Sstevel@tonic-gate size_t kmo_data_size; /* Size of each allocation */ 35277c478bd9Sstevel@tonic-gate size_t kmo_total_size; /* Total bytes of allocation */ 35287c478bd9Sstevel@tonic-gate int kmo_depth; /* Depth of stack trace */ 35297c478bd9Sstevel@tonic-gate uintptr_t kmo_stack[KMEM_STACK_DEPTH]; /* Stack trace */ 35307c478bd9Sstevel@tonic-gate } kmowner_t; 35317c478bd9Sstevel@tonic-gate 35327c478bd9Sstevel@tonic-gate typedef struct kmusers { 35337c478bd9Sstevel@tonic-gate uintptr_t kmu_addr; /* address of interest */ 35347c478bd9Sstevel@tonic-gate const kmem_cache_t *kmu_cache; /* Current kmem cache */ 35357c478bd9Sstevel@tonic-gate kmowner_t *kmu_hash; /* Hash table of owners */ 35367c478bd9Sstevel@tonic-gate int kmu_nelems; /* Number of entries in use */ 35377c478bd9Sstevel@tonic-gate int kmu_size; /* Total number of entries */ 35387c478bd9Sstevel@tonic-gate } kmusers_t; 35397c478bd9Sstevel@tonic-gate 35407c478bd9Sstevel@tonic-gate static void 35417c478bd9Sstevel@tonic-gate kmu_add(kmusers_t *kmu, const kmem_bufctl_audit_t *bcp, 35427c478bd9Sstevel@tonic-gate size_t size, size_t data_size) 35437c478bd9Sstevel@tonic-gate { 35447c478bd9Sstevel@tonic-gate int i, depth = MIN(bcp->bc_depth, KMEM_STACK_DEPTH); 35457c478bd9Sstevel@tonic-gate size_t bucket, signature = data_size; 35467c478bd9Sstevel@tonic-gate kmowner_t *kmo, *kmoend; 35477c478bd9Sstevel@tonic-gate 35487c478bd9Sstevel@tonic-gate /* 35497c478bd9Sstevel@tonic-gate * If the hash table is full, double its size and rehash everything. 35507c478bd9Sstevel@tonic-gate */ 35517c478bd9Sstevel@tonic-gate if (kmu->kmu_nelems >= kmu->kmu_size) { 35527c478bd9Sstevel@tonic-gate int s = kmu->kmu_size ? kmu->kmu_size * 2 : 1024; 35537c478bd9Sstevel@tonic-gate 35547c478bd9Sstevel@tonic-gate kmo = mdb_alloc(sizeof (kmowner_t) * s, UM_SLEEP | UM_GC); 35557c478bd9Sstevel@tonic-gate bcopy(kmu->kmu_hash, kmo, sizeof (kmowner_t) * kmu->kmu_size); 35567c478bd9Sstevel@tonic-gate kmu->kmu_hash = kmo; 35577c478bd9Sstevel@tonic-gate kmu->kmu_size = s; 35587c478bd9Sstevel@tonic-gate 35597c478bd9Sstevel@tonic-gate kmoend = kmu->kmu_hash + kmu->kmu_size; 35607c478bd9Sstevel@tonic-gate for (kmo = kmu->kmu_hash; kmo < kmoend; kmo++) 35617c478bd9Sstevel@tonic-gate kmo->kmo_head = NULL; 35627c478bd9Sstevel@tonic-gate 35637c478bd9Sstevel@tonic-gate kmoend = kmu->kmu_hash + kmu->kmu_nelems; 35647c478bd9Sstevel@tonic-gate for (kmo = kmu->kmu_hash; kmo < kmoend; kmo++) { 35657c478bd9Sstevel@tonic-gate bucket = kmo->kmo_signature & (kmu->kmu_size - 1); 35667c478bd9Sstevel@tonic-gate kmo->kmo_next = kmu->kmu_hash[bucket].kmo_head; 35677c478bd9Sstevel@tonic-gate kmu->kmu_hash[bucket].kmo_head = kmo; 35687c478bd9Sstevel@tonic-gate } 35697c478bd9Sstevel@tonic-gate } 35707c478bd9Sstevel@tonic-gate 35717c478bd9Sstevel@tonic-gate /* 35727c478bd9Sstevel@tonic-gate * Finish computing the hash signature from the stack trace, and then 35737c478bd9Sstevel@tonic-gate * see if the owner is in the hash table. If so, update our stats. 35747c478bd9Sstevel@tonic-gate */ 35757c478bd9Sstevel@tonic-gate for (i = 0; i < depth; i++) 35767c478bd9Sstevel@tonic-gate signature += bcp->bc_stack[i]; 35777c478bd9Sstevel@tonic-gate 35787c478bd9Sstevel@tonic-gate bucket = signature & (kmu->kmu_size - 1); 35797c478bd9Sstevel@tonic-gate 35807c478bd9Sstevel@tonic-gate for (kmo = kmu->kmu_hash[bucket].kmo_head; kmo; kmo = kmo->kmo_next) { 35817c478bd9Sstevel@tonic-gate if (kmo->kmo_signature == signature) { 35827c478bd9Sstevel@tonic-gate size_t difference = 0; 35837c478bd9Sstevel@tonic-gate 35847c478bd9Sstevel@tonic-gate difference |= kmo->kmo_data_size - data_size; 35857c478bd9Sstevel@tonic-gate difference |= kmo->kmo_depth - depth; 35867c478bd9Sstevel@tonic-gate 35877c478bd9Sstevel@tonic-gate for (i = 0; i < depth; i++) { 35887c478bd9Sstevel@tonic-gate difference |= kmo->kmo_stack[i] - 35897c478bd9Sstevel@tonic-gate bcp->bc_stack[i]; 35907c478bd9Sstevel@tonic-gate } 35917c478bd9Sstevel@tonic-gate 35927c478bd9Sstevel@tonic-gate if (difference == 0) { 35937c478bd9Sstevel@tonic-gate kmo->kmo_total_size += size; 35947c478bd9Sstevel@tonic-gate kmo->kmo_num++; 35957c478bd9Sstevel@tonic-gate return; 35967c478bd9Sstevel@tonic-gate } 35977c478bd9Sstevel@tonic-gate } 35987c478bd9Sstevel@tonic-gate } 35997c478bd9Sstevel@tonic-gate 36007c478bd9Sstevel@tonic-gate /* 36017c478bd9Sstevel@tonic-gate * If the owner is not yet hashed, grab the next element and fill it 36027c478bd9Sstevel@tonic-gate * in based on the allocation information. 36037c478bd9Sstevel@tonic-gate */ 36047c478bd9Sstevel@tonic-gate kmo = &kmu->kmu_hash[kmu->kmu_nelems++]; 36057c478bd9Sstevel@tonic-gate kmo->kmo_next = kmu->kmu_hash[bucket].kmo_head; 36067c478bd9Sstevel@tonic-gate kmu->kmu_hash[bucket].kmo_head = kmo; 36077c478bd9Sstevel@tonic-gate 36087c478bd9Sstevel@tonic-gate kmo->kmo_signature = signature; 36097c478bd9Sstevel@tonic-gate kmo->kmo_num = 1; 36107c478bd9Sstevel@tonic-gate kmo->kmo_data_size = data_size; 36117c478bd9Sstevel@tonic-gate kmo->kmo_total_size = size; 36127c478bd9Sstevel@tonic-gate kmo->kmo_depth = depth; 36137c478bd9Sstevel@tonic-gate 36147c478bd9Sstevel@tonic-gate for (i = 0; i < depth; i++) 36157c478bd9Sstevel@tonic-gate kmo->kmo_stack[i] = bcp->bc_stack[i]; 36167c478bd9Sstevel@tonic-gate } 36177c478bd9Sstevel@tonic-gate 36187c478bd9Sstevel@tonic-gate /* 36197c478bd9Sstevel@tonic-gate * When ::kmausers is invoked without the -f flag, we simply update our hash 36207c478bd9Sstevel@tonic-gate * table with the information from each allocated bufctl. 36217c478bd9Sstevel@tonic-gate */ 36227c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 36237c478bd9Sstevel@tonic-gate static int 36247c478bd9Sstevel@tonic-gate kmause1(uintptr_t addr, const kmem_bufctl_audit_t *bcp, kmusers_t *kmu) 36257c478bd9Sstevel@tonic-gate { 36267c478bd9Sstevel@tonic-gate const kmem_cache_t *cp = kmu->kmu_cache; 36277c478bd9Sstevel@tonic-gate 36287c478bd9Sstevel@tonic-gate kmu_add(kmu, bcp, cp->cache_bufsize, cp->cache_bufsize); 36297c478bd9Sstevel@tonic-gate return (WALK_NEXT); 36307c478bd9Sstevel@tonic-gate } 36317c478bd9Sstevel@tonic-gate 36327c478bd9Sstevel@tonic-gate /* 36337c478bd9Sstevel@tonic-gate * When ::kmausers is invoked with the -f flag, we print out the information 36347c478bd9Sstevel@tonic-gate * for each bufctl as well as updating the hash table. 36357c478bd9Sstevel@tonic-gate */ 36367c478bd9Sstevel@tonic-gate static int 36377c478bd9Sstevel@tonic-gate kmause2(uintptr_t addr, const kmem_bufctl_audit_t *bcp, kmusers_t *kmu) 36387c478bd9Sstevel@tonic-gate { 36397c478bd9Sstevel@tonic-gate int i, depth = MIN(bcp->bc_depth, KMEM_STACK_DEPTH); 36407c478bd9Sstevel@tonic-gate const kmem_cache_t *cp = kmu->kmu_cache; 36417c478bd9Sstevel@tonic-gate kmem_bufctl_t bufctl; 36427c478bd9Sstevel@tonic-gate 36437c478bd9Sstevel@tonic-gate if (kmu->kmu_addr) { 36447c478bd9Sstevel@tonic-gate if (mdb_vread(&bufctl, sizeof (bufctl), addr) == -1) 36457c478bd9Sstevel@tonic-gate mdb_warn("couldn't read bufctl at %p", addr); 36467c478bd9Sstevel@tonic-gate else if (kmu->kmu_addr < (uintptr_t)bufctl.bc_addr || 36477c478bd9Sstevel@tonic-gate kmu->kmu_addr >= (uintptr_t)bufctl.bc_addr + 36487c478bd9Sstevel@tonic-gate cp->cache_bufsize) 36497c478bd9Sstevel@tonic-gate return (WALK_NEXT); 36507c478bd9Sstevel@tonic-gate } 36517c478bd9Sstevel@tonic-gate 36527c478bd9Sstevel@tonic-gate mdb_printf("size %d, addr %p, thread %p, cache %s\n", 36537c478bd9Sstevel@tonic-gate cp->cache_bufsize, addr, bcp->bc_thread, cp->cache_name); 36547c478bd9Sstevel@tonic-gate 36557c478bd9Sstevel@tonic-gate for (i = 0; i < depth; i++) 36567c478bd9Sstevel@tonic-gate mdb_printf("\t %a\n", bcp->bc_stack[i]); 36577c478bd9Sstevel@tonic-gate 36587c478bd9Sstevel@tonic-gate kmu_add(kmu, bcp, cp->cache_bufsize, cp->cache_bufsize); 36597c478bd9Sstevel@tonic-gate return (WALK_NEXT); 36607c478bd9Sstevel@tonic-gate } 36617c478bd9Sstevel@tonic-gate 36627c478bd9Sstevel@tonic-gate /* 36637c478bd9Sstevel@tonic-gate * We sort our results by allocation size before printing them. 36647c478bd9Sstevel@tonic-gate */ 36657c478bd9Sstevel@tonic-gate static int 36667c478bd9Sstevel@tonic-gate kmownercmp(const void *lp, const void *rp) 36677c478bd9Sstevel@tonic-gate { 36687c478bd9Sstevel@tonic-gate const kmowner_t *lhs = lp; 36697c478bd9Sstevel@tonic-gate const kmowner_t *rhs = rp; 36707c478bd9Sstevel@tonic-gate 36717c478bd9Sstevel@tonic-gate return (rhs->kmo_total_size - lhs->kmo_total_size); 36727c478bd9Sstevel@tonic-gate } 36737c478bd9Sstevel@tonic-gate 36747c478bd9Sstevel@tonic-gate /* 36757c478bd9Sstevel@tonic-gate * The main engine of ::kmausers is relatively straightforward: First we 36767c478bd9Sstevel@tonic-gate * accumulate our list of kmem_cache_t addresses into the kmclist_t. Next we 36777c478bd9Sstevel@tonic-gate * iterate over the allocated bufctls of each cache in the list. Finally, 36787c478bd9Sstevel@tonic-gate * we sort and print our results. 36797c478bd9Sstevel@tonic-gate */ 36807c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 36817c478bd9Sstevel@tonic-gate int 36827c478bd9Sstevel@tonic-gate kmausers(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 36837c478bd9Sstevel@tonic-gate { 36847c478bd9Sstevel@tonic-gate int mem_threshold = 8192; /* Minimum # bytes for printing */ 36857c478bd9Sstevel@tonic-gate int cnt_threshold = 100; /* Minimum # blocks for printing */ 36867c478bd9Sstevel@tonic-gate int audited_caches = 0; /* Number of KMF_AUDIT caches found */ 36877c478bd9Sstevel@tonic-gate int do_all_caches = 1; /* Do all caches (no arguments) */ 36887c478bd9Sstevel@tonic-gate int opt_e = FALSE; /* Include "small" users */ 36897c478bd9Sstevel@tonic-gate int opt_f = FALSE; /* Print stack traces */ 36907c478bd9Sstevel@tonic-gate 36917c478bd9Sstevel@tonic-gate mdb_walk_cb_t callback = (mdb_walk_cb_t)kmause1; 36927c478bd9Sstevel@tonic-gate kmowner_t *kmo, *kmoend; 36937c478bd9Sstevel@tonic-gate int i, oelems; 36947c478bd9Sstevel@tonic-gate 36957c478bd9Sstevel@tonic-gate kmclist_t kmc; 36967c478bd9Sstevel@tonic-gate kmusers_t kmu; 36977c478bd9Sstevel@tonic-gate 36987c478bd9Sstevel@tonic-gate bzero(&kmc, sizeof (kmc)); 36997c478bd9Sstevel@tonic-gate bzero(&kmu, sizeof (kmu)); 37007c478bd9Sstevel@tonic-gate 37017c478bd9Sstevel@tonic-gate while ((i = mdb_getopts(argc, argv, 37027c478bd9Sstevel@tonic-gate 'e', MDB_OPT_SETBITS, TRUE, &opt_e, 37037c478bd9Sstevel@tonic-gate 'f', MDB_OPT_SETBITS, TRUE, &opt_f, NULL)) != argc) { 37047c478bd9Sstevel@tonic-gate 37057c478bd9Sstevel@tonic-gate argv += i; /* skip past options we just processed */ 37067c478bd9Sstevel@tonic-gate argc -= i; /* adjust argc */ 37077c478bd9Sstevel@tonic-gate 37087c478bd9Sstevel@tonic-gate if (argv->a_type != MDB_TYPE_STRING || *argv->a_un.a_str == '-') 37097c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 37107c478bd9Sstevel@tonic-gate 37117c478bd9Sstevel@tonic-gate oelems = kmc.kmc_nelems; 37127c478bd9Sstevel@tonic-gate kmc.kmc_name = argv->a_un.a_str; 37137c478bd9Sstevel@tonic-gate (void) mdb_walk("kmem_cache", (mdb_walk_cb_t)kmc_add, &kmc); 37147c478bd9Sstevel@tonic-gate 37157c478bd9Sstevel@tonic-gate if (kmc.kmc_nelems == oelems) { 37167c478bd9Sstevel@tonic-gate mdb_warn("unknown kmem cache: %s\n", kmc.kmc_name); 37177c478bd9Sstevel@tonic-gate return (DCMD_ERR); 37187c478bd9Sstevel@tonic-gate } 37197c478bd9Sstevel@tonic-gate 37207c478bd9Sstevel@tonic-gate do_all_caches = 0; 37217c478bd9Sstevel@tonic-gate argv++; 37227c478bd9Sstevel@tonic-gate argc--; 37237c478bd9Sstevel@tonic-gate } 37247c478bd9Sstevel@tonic-gate 37257c478bd9Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 37267c478bd9Sstevel@tonic-gate opt_f = TRUE; 37277c478bd9Sstevel@tonic-gate kmu.kmu_addr = addr; 37287c478bd9Sstevel@tonic-gate } else { 37297c478bd9Sstevel@tonic-gate kmu.kmu_addr = NULL; 37307c478bd9Sstevel@tonic-gate } 37317c478bd9Sstevel@tonic-gate 37327c478bd9Sstevel@tonic-gate if (opt_e) 37337c478bd9Sstevel@tonic-gate mem_threshold = cnt_threshold = 0; 37347c478bd9Sstevel@tonic-gate 37357c478bd9Sstevel@tonic-gate if (opt_f) 37367c478bd9Sstevel@tonic-gate callback = (mdb_walk_cb_t)kmause2; 37377c478bd9Sstevel@tonic-gate 37387c478bd9Sstevel@tonic-gate if (do_all_caches) { 37397c478bd9Sstevel@tonic-gate kmc.kmc_name = NULL; /* match all cache names */ 37407c478bd9Sstevel@tonic-gate (void) mdb_walk("kmem_cache", (mdb_walk_cb_t)kmc_add, &kmc); 37417c478bd9Sstevel@tonic-gate } 37427c478bd9Sstevel@tonic-gate 37437c478bd9Sstevel@tonic-gate for (i = 0; i < kmc.kmc_nelems; i++) { 37447c478bd9Sstevel@tonic-gate uintptr_t cp = kmc.kmc_caches[i]; 37457c478bd9Sstevel@tonic-gate kmem_cache_t c; 37467c478bd9Sstevel@tonic-gate 37477c478bd9Sstevel@tonic-gate if (mdb_vread(&c, sizeof (c), cp) == -1) { 37487c478bd9Sstevel@tonic-gate mdb_warn("failed to read cache at %p", cp); 37497c478bd9Sstevel@tonic-gate continue; 37507c478bd9Sstevel@tonic-gate } 37517c478bd9Sstevel@tonic-gate 37527c478bd9Sstevel@tonic-gate if (!(c.cache_flags & KMF_AUDIT)) { 37537c478bd9Sstevel@tonic-gate if (!do_all_caches) { 37547c478bd9Sstevel@tonic-gate mdb_warn("KMF_AUDIT is not enabled for %s\n", 37557c478bd9Sstevel@tonic-gate c.cache_name); 37567c478bd9Sstevel@tonic-gate } 37577c478bd9Sstevel@tonic-gate continue; 37587c478bd9Sstevel@tonic-gate } 37597c478bd9Sstevel@tonic-gate 37607c478bd9Sstevel@tonic-gate kmu.kmu_cache = &c; 37617c478bd9Sstevel@tonic-gate (void) mdb_pwalk("bufctl", callback, &kmu, cp); 37627c478bd9Sstevel@tonic-gate audited_caches++; 37637c478bd9Sstevel@tonic-gate } 37647c478bd9Sstevel@tonic-gate 37657c478bd9Sstevel@tonic-gate if (audited_caches == 0 && do_all_caches) { 37667c478bd9Sstevel@tonic-gate mdb_warn("KMF_AUDIT is not enabled for any caches\n"); 37677c478bd9Sstevel@tonic-gate return (DCMD_ERR); 37687c478bd9Sstevel@tonic-gate } 37697c478bd9Sstevel@tonic-gate 37707c478bd9Sstevel@tonic-gate qsort(kmu.kmu_hash, kmu.kmu_nelems, sizeof (kmowner_t), kmownercmp); 37717c478bd9Sstevel@tonic-gate kmoend = kmu.kmu_hash + kmu.kmu_nelems; 37727c478bd9Sstevel@tonic-gate 37737c478bd9Sstevel@tonic-gate for (kmo = kmu.kmu_hash; kmo < kmoend; kmo++) { 37747c478bd9Sstevel@tonic-gate if (kmo->kmo_total_size < mem_threshold && 37757c478bd9Sstevel@tonic-gate kmo->kmo_num < cnt_threshold) 37767c478bd9Sstevel@tonic-gate continue; 37777c478bd9Sstevel@tonic-gate mdb_printf("%lu bytes for %u allocations with data size %lu:\n", 37787c478bd9Sstevel@tonic-gate kmo->kmo_total_size, kmo->kmo_num, kmo->kmo_data_size); 37797c478bd9Sstevel@tonic-gate for (i = 0; i < kmo->kmo_depth; i++) 37807c478bd9Sstevel@tonic-gate mdb_printf("\t %a\n", kmo->kmo_stack[i]); 37817c478bd9Sstevel@tonic-gate } 37827c478bd9Sstevel@tonic-gate 37837c478bd9Sstevel@tonic-gate return (DCMD_OK); 37847c478bd9Sstevel@tonic-gate } 37857c478bd9Sstevel@tonic-gate 37867c478bd9Sstevel@tonic-gate void 37877c478bd9Sstevel@tonic-gate kmausers_help(void) 37887c478bd9Sstevel@tonic-gate { 37897c478bd9Sstevel@tonic-gate mdb_printf( 37907c478bd9Sstevel@tonic-gate "Displays the largest users of the kmem allocator, sorted by \n" 37917c478bd9Sstevel@tonic-gate "trace. If one or more caches is specified, only those caches\n" 37927c478bd9Sstevel@tonic-gate "will be searched. By default, all caches are searched. If an\n" 37937c478bd9Sstevel@tonic-gate "address is specified, then only those allocations which include\n" 37947c478bd9Sstevel@tonic-gate "the given address are displayed. Specifying an address implies\n" 37957c478bd9Sstevel@tonic-gate "-f.\n" 37967c478bd9Sstevel@tonic-gate "\n" 37977c478bd9Sstevel@tonic-gate "\t-e\tInclude all users, not just the largest\n" 37987c478bd9Sstevel@tonic-gate "\t-f\tDisplay individual allocations. By default, users are\n" 37997c478bd9Sstevel@tonic-gate "\t\tgrouped by stack\n"); 38007c478bd9Sstevel@tonic-gate } 38017c478bd9Sstevel@tonic-gate 38027c478bd9Sstevel@tonic-gate static int 38037c478bd9Sstevel@tonic-gate kmem_ready_check(void) 38047c478bd9Sstevel@tonic-gate { 38057c478bd9Sstevel@tonic-gate int ready; 38067c478bd9Sstevel@tonic-gate 38077c478bd9Sstevel@tonic-gate if (mdb_readvar(&ready, "kmem_ready") < 0) 38087c478bd9Sstevel@tonic-gate return (-1); /* errno is set for us */ 38097c478bd9Sstevel@tonic-gate 38107c478bd9Sstevel@tonic-gate return (ready); 38117c478bd9Sstevel@tonic-gate } 38127c478bd9Sstevel@tonic-gate 38137c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 38147c478bd9Sstevel@tonic-gate static void 3815*789d94c2Sjwadams kmem_statechange_cb(void *arg) 38167c478bd9Sstevel@tonic-gate { 3817*789d94c2Sjwadams static int been_ready = 0; 3818*789d94c2Sjwadams 3819*789d94c2Sjwadams leaky_cleanup(1); /* state changes invalidate leaky state */ 3820*789d94c2Sjwadams 3821*789d94c2Sjwadams if (been_ready) 38227c478bd9Sstevel@tonic-gate return; 38237c478bd9Sstevel@tonic-gate 3824*789d94c2Sjwadams if (kmem_ready_check() <= 0) 3825*789d94c2Sjwadams return; 38267c478bd9Sstevel@tonic-gate 3827*789d94c2Sjwadams been_ready = 1; 38287c478bd9Sstevel@tonic-gate (void) mdb_walk("kmem_cache", (mdb_walk_cb_t)kmem_init_walkers, NULL); 38297c478bd9Sstevel@tonic-gate } 38307c478bd9Sstevel@tonic-gate 38317c478bd9Sstevel@tonic-gate void 38327c478bd9Sstevel@tonic-gate kmem_init(void) 38337c478bd9Sstevel@tonic-gate { 38347c478bd9Sstevel@tonic-gate mdb_walker_t w = { 38357c478bd9Sstevel@tonic-gate "kmem_cache", "walk list of kmem caches", kmem_cache_walk_init, 38367c478bd9Sstevel@tonic-gate kmem_cache_walk_step, kmem_cache_walk_fini 38377c478bd9Sstevel@tonic-gate }; 38387c478bd9Sstevel@tonic-gate 38397c478bd9Sstevel@tonic-gate /* 38407c478bd9Sstevel@tonic-gate * If kmem is ready, we'll need to invoke the kmem_cache walker 38417c478bd9Sstevel@tonic-gate * immediately. Walkers in the linkage structure won't be ready until 38427c478bd9Sstevel@tonic-gate * _mdb_init returns, so we'll need to add this one manually. If kmem 38437c478bd9Sstevel@tonic-gate * is ready, we'll use the walker to initialize the caches. If kmem 38447c478bd9Sstevel@tonic-gate * isn't ready, we'll register a callback that will allow us to defer 38457c478bd9Sstevel@tonic-gate * cache walking until it is. 38467c478bd9Sstevel@tonic-gate */ 38477c478bd9Sstevel@tonic-gate if (mdb_add_walker(&w) != 0) { 38487c478bd9Sstevel@tonic-gate mdb_warn("failed to add kmem_cache walker"); 38497c478bd9Sstevel@tonic-gate return; 38507c478bd9Sstevel@tonic-gate } 38517c478bd9Sstevel@tonic-gate 3852*789d94c2Sjwadams (void) mdb_callback_add(MDB_CALLBACK_STCHG, kmem_statechange_cb, NULL); 3853*789d94c2Sjwadams kmem_statechange_cb(NULL); 38547c478bd9Sstevel@tonic-gate } 38557c478bd9Sstevel@tonic-gate 38567c478bd9Sstevel@tonic-gate typedef struct whatthread { 38577c478bd9Sstevel@tonic-gate uintptr_t wt_target; 38587c478bd9Sstevel@tonic-gate int wt_verbose; 38597c478bd9Sstevel@tonic-gate } whatthread_t; 38607c478bd9Sstevel@tonic-gate 38617c478bd9Sstevel@tonic-gate static int 38627c478bd9Sstevel@tonic-gate whatthread_walk_thread(uintptr_t addr, const kthread_t *t, whatthread_t *w) 38637c478bd9Sstevel@tonic-gate { 38647c478bd9Sstevel@tonic-gate uintptr_t current, data; 38657c478bd9Sstevel@tonic-gate 38667c478bd9Sstevel@tonic-gate if (t->t_stkbase == NULL) 38677c478bd9Sstevel@tonic-gate return (WALK_NEXT); 38687c478bd9Sstevel@tonic-gate 38697c478bd9Sstevel@tonic-gate /* 38707c478bd9Sstevel@tonic-gate * Warn about swapped out threads, but drive on anyway 38717c478bd9Sstevel@tonic-gate */ 38727c478bd9Sstevel@tonic-gate if (!(t->t_schedflag & TS_LOAD)) { 38737c478bd9Sstevel@tonic-gate mdb_warn("thread %p's stack swapped out\n", addr); 38747c478bd9Sstevel@tonic-gate return (WALK_NEXT); 38757c478bd9Sstevel@tonic-gate } 38767c478bd9Sstevel@tonic-gate 38777c478bd9Sstevel@tonic-gate /* 38787c478bd9Sstevel@tonic-gate * Search the thread's stack for the given pointer. Note that it would 38797c478bd9Sstevel@tonic-gate * be more efficient to follow ::kgrep's lead and read in page-sized 38807c478bd9Sstevel@tonic-gate * chunks, but this routine is already fast and simple. 38817c478bd9Sstevel@tonic-gate */ 38827c478bd9Sstevel@tonic-gate for (current = (uintptr_t)t->t_stkbase; current < (uintptr_t)t->t_stk; 38837c478bd9Sstevel@tonic-gate current += sizeof (uintptr_t)) { 38847c478bd9Sstevel@tonic-gate if (mdb_vread(&data, sizeof (data), current) == -1) { 38857c478bd9Sstevel@tonic-gate mdb_warn("couldn't read thread %p's stack at %p", 38867c478bd9Sstevel@tonic-gate addr, current); 38877c478bd9Sstevel@tonic-gate return (WALK_ERR); 38887c478bd9Sstevel@tonic-gate } 38897c478bd9Sstevel@tonic-gate 38907c478bd9Sstevel@tonic-gate if (data == w->wt_target) { 38917c478bd9Sstevel@tonic-gate if (w->wt_verbose) { 38927c478bd9Sstevel@tonic-gate mdb_printf("%p in thread %p's stack%s\n", 38937c478bd9Sstevel@tonic-gate current, addr, stack_active(t, current)); 38947c478bd9Sstevel@tonic-gate } else { 38957c478bd9Sstevel@tonic-gate mdb_printf("%#lr\n", addr); 38967c478bd9Sstevel@tonic-gate return (WALK_NEXT); 38977c478bd9Sstevel@tonic-gate } 38987c478bd9Sstevel@tonic-gate } 38997c478bd9Sstevel@tonic-gate } 39007c478bd9Sstevel@tonic-gate 39017c478bd9Sstevel@tonic-gate return (WALK_NEXT); 39027c478bd9Sstevel@tonic-gate } 39037c478bd9Sstevel@tonic-gate 39047c478bd9Sstevel@tonic-gate int 39057c478bd9Sstevel@tonic-gate whatthread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 39067c478bd9Sstevel@tonic-gate { 39077c478bd9Sstevel@tonic-gate whatthread_t w; 39087c478bd9Sstevel@tonic-gate 39097c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) 39107c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 39117c478bd9Sstevel@tonic-gate 39127c478bd9Sstevel@tonic-gate w.wt_verbose = FALSE; 39137c478bd9Sstevel@tonic-gate w.wt_target = addr; 39147c478bd9Sstevel@tonic-gate 39157c478bd9Sstevel@tonic-gate if (mdb_getopts(argc, argv, 39167c478bd9Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &w.wt_verbose, NULL) != argc) 39177c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 39187c478bd9Sstevel@tonic-gate 39197c478bd9Sstevel@tonic-gate if (mdb_walk("thread", (mdb_walk_cb_t)whatthread_walk_thread, &w) 39207c478bd9Sstevel@tonic-gate == -1) { 39217c478bd9Sstevel@tonic-gate mdb_warn("couldn't walk threads"); 39227c478bd9Sstevel@tonic-gate return (DCMD_ERR); 39237c478bd9Sstevel@tonic-gate } 39247c478bd9Sstevel@tonic-gate 39257c478bd9Sstevel@tonic-gate return (DCMD_OK); 39267c478bd9Sstevel@tonic-gate } 3927