11eaf0ac3Slogwang /*- 2*22ce4affSfengbojiang * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*22ce4affSfengbojiang * 41eaf0ac3Slogwang * Copyright (c) 2005 Robert N. M. Watson 51eaf0ac3Slogwang * All rights reserved. 61eaf0ac3Slogwang * 71eaf0ac3Slogwang * Redistribution and use in source and binary forms, with or without 81eaf0ac3Slogwang * modification, are permitted provided that the following conditions 91eaf0ac3Slogwang * are met: 101eaf0ac3Slogwang * 1. Redistributions of source code must retain the above copyright 111eaf0ac3Slogwang * notice, this list of conditions and the following disclaimer. 121eaf0ac3Slogwang * 2. Redistributions in binary form must reproduce the above copyright 131eaf0ac3Slogwang * notice, this list of conditions and the following disclaimer in the 141eaf0ac3Slogwang * documentation and/or other materials provided with the distribution. 151eaf0ac3Slogwang * 161eaf0ac3Slogwang * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 171eaf0ac3Slogwang * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 181eaf0ac3Slogwang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 191eaf0ac3Slogwang * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 201eaf0ac3Slogwang * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 211eaf0ac3Slogwang * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 221eaf0ac3Slogwang * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 231eaf0ac3Slogwang * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 241eaf0ac3Slogwang * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251eaf0ac3Slogwang * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261eaf0ac3Slogwang * SUCH DAMAGE. 271eaf0ac3Slogwang * 281eaf0ac3Slogwang * $FreeBSD$ 291eaf0ac3Slogwang */ 301eaf0ac3Slogwang 311eaf0ac3Slogwang #include <sys/cdefs.h> 321eaf0ac3Slogwang #include <sys/param.h> 331eaf0ac3Slogwang #include <sys/malloc.h> 341eaf0ac3Slogwang #include <sys/sysctl.h> 351eaf0ac3Slogwang 361eaf0ac3Slogwang #include <err.h> 371eaf0ac3Slogwang #include <errno.h> 381eaf0ac3Slogwang #include <kvm.h> 391eaf0ac3Slogwang #include <nlist.h> 401eaf0ac3Slogwang #include <stdio.h> 411eaf0ac3Slogwang #include <stdlib.h> 421eaf0ac3Slogwang #include <string.h> 431eaf0ac3Slogwang 441eaf0ac3Slogwang #include "memstat.h" 451eaf0ac3Slogwang #include "memstat_internal.h" 461eaf0ac3Slogwang 47*22ce4affSfengbojiang static int memstat_malloc_zone_count; 48*22ce4affSfengbojiang static int memstat_malloc_zone_sizes[32]; 49*22ce4affSfengbojiang 50*22ce4affSfengbojiang static int memstat_malloc_zone_init(void); 51*22ce4affSfengbojiang static int memstat_malloc_zone_init_kvm(kvm_t *kvm); 52*22ce4affSfengbojiang 531eaf0ac3Slogwang static struct nlist namelist[] = { 541eaf0ac3Slogwang #define X_KMEMSTATISTICS 0 551eaf0ac3Slogwang { .n_name = "_kmemstatistics" }, 56*22ce4affSfengbojiang #define X_KMEMZONES 1 57*22ce4affSfengbojiang { .n_name = "_kmemzones" }, 58*22ce4affSfengbojiang #define X_NUMZONES 2 59*22ce4affSfengbojiang { .n_name = "_numzones" }, 60*22ce4affSfengbojiang #define X_VM_MALLOC_ZONE_COUNT 3 61*22ce4affSfengbojiang { .n_name = "_vm_malloc_zone_count" }, 62*22ce4affSfengbojiang #define X_MP_MAXCPUS 4 631eaf0ac3Slogwang { .n_name = "_mp_maxcpus" }, 641eaf0ac3Slogwang { .n_name = "" }, 651eaf0ac3Slogwang }; 661eaf0ac3Slogwang 671eaf0ac3Slogwang /* 681eaf0ac3Slogwang * Extract malloc(9) statistics from the running kernel, and store all memory 691eaf0ac3Slogwang * type information in the passed list. For each type, check the list for an 701eaf0ac3Slogwang * existing entry with the right name/allocator -- if present, update that 711eaf0ac3Slogwang * entry. Otherwise, add a new entry. On error, the entire list will be 721eaf0ac3Slogwang * cleared, as entries will be in an inconsistent state. 731eaf0ac3Slogwang * 741eaf0ac3Slogwang * To reduce the level of work for a list that starts empty, we keep around a 751eaf0ac3Slogwang * hint as to whether it was empty when we began, so we can avoid searching 761eaf0ac3Slogwang * the list for entries to update. Updates are O(n^2) due to searching for 771eaf0ac3Slogwang * each entry before adding it. 781eaf0ac3Slogwang */ 791eaf0ac3Slogwang int 801eaf0ac3Slogwang memstat_sysctl_malloc(struct memory_type_list *list, int flags) 811eaf0ac3Slogwang { 821eaf0ac3Slogwang struct malloc_type_stream_header *mtshp; 831eaf0ac3Slogwang struct malloc_type_header *mthp; 841eaf0ac3Slogwang struct malloc_type_stats *mtsp; 851eaf0ac3Slogwang struct memory_type *mtp; 861eaf0ac3Slogwang int count, hint_dontsearch, i, j, maxcpus; 871eaf0ac3Slogwang char *buffer, *p; 881eaf0ac3Slogwang size_t size; 891eaf0ac3Slogwang 901eaf0ac3Slogwang hint_dontsearch = LIST_EMPTY(&list->mtl_list); 911eaf0ac3Slogwang 921eaf0ac3Slogwang /* 931eaf0ac3Slogwang * Query the number of CPUs, number of malloc types so that we can 941eaf0ac3Slogwang * guess an initial buffer size. We loop until we succeed or really 951eaf0ac3Slogwang * fail. Note that the value of maxcpus we query using sysctl is not 961eaf0ac3Slogwang * the version we use when processing the real data -- that is read 971eaf0ac3Slogwang * from the header. 981eaf0ac3Slogwang */ 991eaf0ac3Slogwang retry: 1001eaf0ac3Slogwang size = sizeof(maxcpus); 1011eaf0ac3Slogwang if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) { 1021eaf0ac3Slogwang if (errno == EACCES || errno == EPERM) 1031eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_PERMISSION; 1041eaf0ac3Slogwang else 1051eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_DATAERROR; 1061eaf0ac3Slogwang return (-1); 1071eaf0ac3Slogwang } 1081eaf0ac3Slogwang if (size != sizeof(maxcpus)) { 1091eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_DATAERROR; 1101eaf0ac3Slogwang return (-1); 1111eaf0ac3Slogwang } 1121eaf0ac3Slogwang 1131eaf0ac3Slogwang size = sizeof(count); 1141eaf0ac3Slogwang if (sysctlbyname("kern.malloc_count", &count, &size, NULL, 0) < 0) { 1151eaf0ac3Slogwang if (errno == EACCES || errno == EPERM) 1161eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_PERMISSION; 1171eaf0ac3Slogwang else 1181eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_VERSION; 1191eaf0ac3Slogwang return (-1); 1201eaf0ac3Slogwang } 1211eaf0ac3Slogwang if (size != sizeof(count)) { 1221eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_DATAERROR; 1231eaf0ac3Slogwang return (-1); 1241eaf0ac3Slogwang } 1251eaf0ac3Slogwang 126*22ce4affSfengbojiang if (memstat_malloc_zone_init() == -1) { 127*22ce4affSfengbojiang list->mtl_error = MEMSTAT_ERROR_VERSION; 128*22ce4affSfengbojiang return (-1); 129*22ce4affSfengbojiang } 130*22ce4affSfengbojiang 1311eaf0ac3Slogwang size = sizeof(*mthp) + count * (sizeof(*mthp) + sizeof(*mtsp) * 1321eaf0ac3Slogwang maxcpus); 1331eaf0ac3Slogwang 1341eaf0ac3Slogwang buffer = malloc(size); 1351eaf0ac3Slogwang if (buffer == NULL) { 1361eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_NOMEMORY; 1371eaf0ac3Slogwang return (-1); 1381eaf0ac3Slogwang } 1391eaf0ac3Slogwang 1401eaf0ac3Slogwang if (sysctlbyname("kern.malloc_stats", buffer, &size, NULL, 0) < 0) { 1411eaf0ac3Slogwang /* 1421eaf0ac3Slogwang * XXXRW: ENOMEM is an ambiguous return, we should bound the 1431eaf0ac3Slogwang * number of loops, perhaps. 1441eaf0ac3Slogwang */ 1451eaf0ac3Slogwang if (errno == ENOMEM) { 1461eaf0ac3Slogwang free(buffer); 1471eaf0ac3Slogwang goto retry; 1481eaf0ac3Slogwang } 1491eaf0ac3Slogwang if (errno == EACCES || errno == EPERM) 1501eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_PERMISSION; 1511eaf0ac3Slogwang else 1521eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_VERSION; 1531eaf0ac3Slogwang free(buffer); 1541eaf0ac3Slogwang return (-1); 1551eaf0ac3Slogwang } 1561eaf0ac3Slogwang 1571eaf0ac3Slogwang if (size == 0) { 1581eaf0ac3Slogwang free(buffer); 1591eaf0ac3Slogwang return (0); 1601eaf0ac3Slogwang } 1611eaf0ac3Slogwang 1621eaf0ac3Slogwang if (size < sizeof(*mtshp)) { 1631eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_VERSION; 1641eaf0ac3Slogwang free(buffer); 1651eaf0ac3Slogwang return (-1); 1661eaf0ac3Slogwang } 1671eaf0ac3Slogwang p = buffer; 1681eaf0ac3Slogwang mtshp = (struct malloc_type_stream_header *)p; 1691eaf0ac3Slogwang p += sizeof(*mtshp); 1701eaf0ac3Slogwang 1711eaf0ac3Slogwang if (mtshp->mtsh_version != MALLOC_TYPE_STREAM_VERSION) { 1721eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_VERSION; 1731eaf0ac3Slogwang free(buffer); 1741eaf0ac3Slogwang return (-1); 1751eaf0ac3Slogwang } 1761eaf0ac3Slogwang 1771eaf0ac3Slogwang /* 1781eaf0ac3Slogwang * For the remainder of this function, we are quite trusting about 1791eaf0ac3Slogwang * the layout of structures and sizes, since we've determined we have 1801eaf0ac3Slogwang * a matching version and acceptable CPU count. 1811eaf0ac3Slogwang */ 1821eaf0ac3Slogwang maxcpus = mtshp->mtsh_maxcpus; 1831eaf0ac3Slogwang count = mtshp->mtsh_count; 1841eaf0ac3Slogwang for (i = 0; i < count; i++) { 1851eaf0ac3Slogwang mthp = (struct malloc_type_header *)p; 1861eaf0ac3Slogwang p += sizeof(*mthp); 1871eaf0ac3Slogwang 1881eaf0ac3Slogwang if (hint_dontsearch == 0) { 1891eaf0ac3Slogwang mtp = memstat_mtl_find(list, ALLOCATOR_MALLOC, 1901eaf0ac3Slogwang mthp->mth_name); 1911eaf0ac3Slogwang } else 1921eaf0ac3Slogwang mtp = NULL; 1931eaf0ac3Slogwang if (mtp == NULL) 1941eaf0ac3Slogwang mtp = _memstat_mt_allocate(list, ALLOCATOR_MALLOC, 1951eaf0ac3Slogwang mthp->mth_name, maxcpus); 1961eaf0ac3Slogwang if (mtp == NULL) { 1971eaf0ac3Slogwang _memstat_mtl_empty(list); 1981eaf0ac3Slogwang free(buffer); 1991eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_NOMEMORY; 2001eaf0ac3Slogwang return (-1); 2011eaf0ac3Slogwang } 2021eaf0ac3Slogwang 2031eaf0ac3Slogwang /* 2041eaf0ac3Slogwang * Reset the statistics on a current node. 2051eaf0ac3Slogwang */ 2061eaf0ac3Slogwang _memstat_mt_reset_stats(mtp, maxcpus); 2071eaf0ac3Slogwang 2081eaf0ac3Slogwang for (j = 0; j < maxcpus; j++) { 2091eaf0ac3Slogwang mtsp = (struct malloc_type_stats *)p; 2101eaf0ac3Slogwang p += sizeof(*mtsp); 2111eaf0ac3Slogwang 2121eaf0ac3Slogwang /* 2131eaf0ac3Slogwang * Sumarize raw statistics across CPUs into coalesced 2141eaf0ac3Slogwang * statistics. 2151eaf0ac3Slogwang */ 2161eaf0ac3Slogwang mtp->mt_memalloced += mtsp->mts_memalloced; 2171eaf0ac3Slogwang mtp->mt_memfreed += mtsp->mts_memfreed; 2181eaf0ac3Slogwang mtp->mt_numallocs += mtsp->mts_numallocs; 2191eaf0ac3Slogwang mtp->mt_numfrees += mtsp->mts_numfrees; 2201eaf0ac3Slogwang mtp->mt_sizemask |= mtsp->mts_size; 2211eaf0ac3Slogwang 2221eaf0ac3Slogwang /* 2231eaf0ac3Slogwang * Copies of per-CPU statistics. 2241eaf0ac3Slogwang */ 2251eaf0ac3Slogwang mtp->mt_percpu_alloc[j].mtp_memalloced = 2261eaf0ac3Slogwang mtsp->mts_memalloced; 2271eaf0ac3Slogwang mtp->mt_percpu_alloc[j].mtp_memfreed = 2281eaf0ac3Slogwang mtsp->mts_memfreed; 2291eaf0ac3Slogwang mtp->mt_percpu_alloc[j].mtp_numallocs = 2301eaf0ac3Slogwang mtsp->mts_numallocs; 2311eaf0ac3Slogwang mtp->mt_percpu_alloc[j].mtp_numfrees = 2321eaf0ac3Slogwang mtsp->mts_numfrees; 2331eaf0ac3Slogwang mtp->mt_percpu_alloc[j].mtp_sizemask = 2341eaf0ac3Slogwang mtsp->mts_size; 2351eaf0ac3Slogwang } 2361eaf0ac3Slogwang 2371eaf0ac3Slogwang /* 2381eaf0ac3Slogwang * Derived cross-CPU statistics. 2391eaf0ac3Slogwang */ 2401eaf0ac3Slogwang mtp->mt_bytes = mtp->mt_memalloced - mtp->mt_memfreed; 2411eaf0ac3Slogwang mtp->mt_count = mtp->mt_numallocs - mtp->mt_numfrees; 2421eaf0ac3Slogwang } 2431eaf0ac3Slogwang 2441eaf0ac3Slogwang free(buffer); 2451eaf0ac3Slogwang 2461eaf0ac3Slogwang return (0); 2471eaf0ac3Slogwang } 2481eaf0ac3Slogwang 2491eaf0ac3Slogwang static int 2501eaf0ac3Slogwang kread(kvm_t *kvm, void *kvm_pointer, void *address, size_t size, 2511eaf0ac3Slogwang size_t offset) 2521eaf0ac3Slogwang { 2531eaf0ac3Slogwang ssize_t ret; 2541eaf0ac3Slogwang 2551eaf0ac3Slogwang ret = kvm_read(kvm, (unsigned long)kvm_pointer + offset, address, 2561eaf0ac3Slogwang size); 2571eaf0ac3Slogwang if (ret < 0) 2581eaf0ac3Slogwang return (MEMSTAT_ERROR_KVM); 2591eaf0ac3Slogwang if ((size_t)ret != size) 2601eaf0ac3Slogwang return (MEMSTAT_ERROR_KVM_SHORTREAD); 2611eaf0ac3Slogwang return (0); 2621eaf0ac3Slogwang } 2631eaf0ac3Slogwang 2641eaf0ac3Slogwang static int 2651eaf0ac3Slogwang kread_string(kvm_t *kvm, const void *kvm_pointer, char *buffer, int buflen) 2661eaf0ac3Slogwang { 2671eaf0ac3Slogwang ssize_t ret; 2681eaf0ac3Slogwang int i; 2691eaf0ac3Slogwang 2701eaf0ac3Slogwang for (i = 0; i < buflen; i++) { 2711eaf0ac3Slogwang ret = kvm_read(kvm, __DECONST(unsigned long, kvm_pointer) + 2721eaf0ac3Slogwang i, &(buffer[i]), sizeof(char)); 2731eaf0ac3Slogwang if (ret < 0) 2741eaf0ac3Slogwang return (MEMSTAT_ERROR_KVM); 2751eaf0ac3Slogwang if ((size_t)ret != sizeof(char)) 2761eaf0ac3Slogwang return (MEMSTAT_ERROR_KVM_SHORTREAD); 2771eaf0ac3Slogwang if (buffer[i] == '\0') 2781eaf0ac3Slogwang return (0); 2791eaf0ac3Slogwang } 2801eaf0ac3Slogwang /* Truncate. */ 2811eaf0ac3Slogwang buffer[i-1] = '\0'; 2821eaf0ac3Slogwang return (0); 2831eaf0ac3Slogwang } 2841eaf0ac3Slogwang 2851eaf0ac3Slogwang static int 2861eaf0ac3Slogwang kread_symbol(kvm_t *kvm, int index, void *address, size_t size, 2871eaf0ac3Slogwang size_t offset) 2881eaf0ac3Slogwang { 2891eaf0ac3Slogwang ssize_t ret; 2901eaf0ac3Slogwang 2911eaf0ac3Slogwang ret = kvm_read(kvm, namelist[index].n_value + offset, address, size); 2921eaf0ac3Slogwang if (ret < 0) 2931eaf0ac3Slogwang return (MEMSTAT_ERROR_KVM); 2941eaf0ac3Slogwang if ((size_t)ret != size) 2951eaf0ac3Slogwang return (MEMSTAT_ERROR_KVM_SHORTREAD); 2961eaf0ac3Slogwang return (0); 2971eaf0ac3Slogwang } 2981eaf0ac3Slogwang 299*22ce4affSfengbojiang static int 300*22ce4affSfengbojiang kread_zpcpu(kvm_t *kvm, u_long base, void *buf, size_t size, int cpu) 301*22ce4affSfengbojiang { 302*22ce4affSfengbojiang ssize_t ret; 303*22ce4affSfengbojiang 304*22ce4affSfengbojiang ret = kvm_read_zpcpu(kvm, base, buf, size, cpu); 305*22ce4affSfengbojiang if (ret < 0) 306*22ce4affSfengbojiang return (MEMSTAT_ERROR_KVM); 307*22ce4affSfengbojiang if ((size_t)ret != size) 308*22ce4affSfengbojiang return (MEMSTAT_ERROR_KVM_SHORTREAD); 309*22ce4affSfengbojiang return (0); 310*22ce4affSfengbojiang } 311*22ce4affSfengbojiang 3121eaf0ac3Slogwang int 3131eaf0ac3Slogwang memstat_kvm_malloc(struct memory_type_list *list, void *kvm_handle) 3141eaf0ac3Slogwang { 3151eaf0ac3Slogwang struct memory_type *mtp; 3161eaf0ac3Slogwang void *kmemstatistics; 317*22ce4affSfengbojiang int hint_dontsearch, j, mp_maxcpus, mp_ncpus, ret; 3181eaf0ac3Slogwang char name[MEMTYPE_MAXNAME]; 319*22ce4affSfengbojiang struct malloc_type_stats mts; 3201eaf0ac3Slogwang struct malloc_type_internal *mtip; 3211eaf0ac3Slogwang struct malloc_type type, *typep; 3221eaf0ac3Slogwang kvm_t *kvm; 3231eaf0ac3Slogwang 3241eaf0ac3Slogwang kvm = (kvm_t *)kvm_handle; 3251eaf0ac3Slogwang 3261eaf0ac3Slogwang hint_dontsearch = LIST_EMPTY(&list->mtl_list); 3271eaf0ac3Slogwang 3281eaf0ac3Slogwang if (kvm_nlist(kvm, namelist) != 0) { 3291eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_KVM; 3301eaf0ac3Slogwang return (-1); 3311eaf0ac3Slogwang } 3321eaf0ac3Slogwang 3331eaf0ac3Slogwang if (namelist[X_KMEMSTATISTICS].n_type == 0 || 3341eaf0ac3Slogwang namelist[X_KMEMSTATISTICS].n_value == 0) { 3351eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_KVM_NOSYMBOL; 3361eaf0ac3Slogwang return (-1); 3371eaf0ac3Slogwang } 3381eaf0ac3Slogwang 3391eaf0ac3Slogwang ret = kread_symbol(kvm, X_MP_MAXCPUS, &mp_maxcpus, 3401eaf0ac3Slogwang sizeof(mp_maxcpus), 0); 3411eaf0ac3Slogwang if (ret != 0) { 3421eaf0ac3Slogwang list->mtl_error = ret; 3431eaf0ac3Slogwang return (-1); 3441eaf0ac3Slogwang } 3451eaf0ac3Slogwang 3461eaf0ac3Slogwang ret = kread_symbol(kvm, X_KMEMSTATISTICS, &kmemstatistics, 3471eaf0ac3Slogwang sizeof(kmemstatistics), 0); 3481eaf0ac3Slogwang if (ret != 0) { 3491eaf0ac3Slogwang list->mtl_error = ret; 3501eaf0ac3Slogwang return (-1); 3511eaf0ac3Slogwang } 3521eaf0ac3Slogwang 353*22ce4affSfengbojiang ret = memstat_malloc_zone_init_kvm(kvm); 354*22ce4affSfengbojiang if (ret != 0) { 355*22ce4affSfengbojiang list->mtl_error = ret; 3561eaf0ac3Slogwang return (-1); 3571eaf0ac3Slogwang } 3581eaf0ac3Slogwang 359*22ce4affSfengbojiang mp_ncpus = kvm_getncpus(kvm); 360*22ce4affSfengbojiang 3611eaf0ac3Slogwang for (typep = kmemstatistics; typep != NULL; typep = type.ks_next) { 3621eaf0ac3Slogwang ret = kread(kvm, typep, &type, sizeof(type), 0); 3631eaf0ac3Slogwang if (ret != 0) { 3641eaf0ac3Slogwang _memstat_mtl_empty(list); 3651eaf0ac3Slogwang list->mtl_error = ret; 3661eaf0ac3Slogwang return (-1); 3671eaf0ac3Slogwang } 3681eaf0ac3Slogwang ret = kread_string(kvm, (void *)type.ks_shortdesc, name, 3691eaf0ac3Slogwang MEMTYPE_MAXNAME); 3701eaf0ac3Slogwang if (ret != 0) { 3711eaf0ac3Slogwang _memstat_mtl_empty(list); 3721eaf0ac3Slogwang list->mtl_error = ret; 3731eaf0ac3Slogwang return (-1); 3741eaf0ac3Slogwang } 375*22ce4affSfengbojiang if (type.ks_version != M_VERSION) { 376*22ce4affSfengbojiang warnx("type %s with unsupported version %lu; skipped", 377*22ce4affSfengbojiang name, type.ks_version); 378*22ce4affSfengbojiang continue; 379*22ce4affSfengbojiang } 3801eaf0ac3Slogwang 3811eaf0ac3Slogwang /* 3821eaf0ac3Slogwang * Since our compile-time value for MAXCPU may differ from the 3831eaf0ac3Slogwang * kernel's, we populate our own array. 3841eaf0ac3Slogwang */ 385*22ce4affSfengbojiang mtip = &type.ks_mti; 3861eaf0ac3Slogwang 3871eaf0ac3Slogwang if (hint_dontsearch == 0) { 3881eaf0ac3Slogwang mtp = memstat_mtl_find(list, ALLOCATOR_MALLOC, name); 3891eaf0ac3Slogwang } else 3901eaf0ac3Slogwang mtp = NULL; 3911eaf0ac3Slogwang if (mtp == NULL) 3921eaf0ac3Slogwang mtp = _memstat_mt_allocate(list, ALLOCATOR_MALLOC, 3931eaf0ac3Slogwang name, mp_maxcpus); 3941eaf0ac3Slogwang if (mtp == NULL) { 3951eaf0ac3Slogwang _memstat_mtl_empty(list); 3961eaf0ac3Slogwang list->mtl_error = MEMSTAT_ERROR_NOMEMORY; 3971eaf0ac3Slogwang return (-1); 3981eaf0ac3Slogwang } 3991eaf0ac3Slogwang 4001eaf0ac3Slogwang /* 4011eaf0ac3Slogwang * This logic is replicated from kern_malloc.c, and should 4021eaf0ac3Slogwang * be kept in sync. 4031eaf0ac3Slogwang */ 4041eaf0ac3Slogwang _memstat_mt_reset_stats(mtp, mp_maxcpus); 405*22ce4affSfengbojiang for (j = 0; j < mp_ncpus; j++) { 406*22ce4affSfengbojiang ret = kread_zpcpu(kvm, (u_long)mtip->mti_stats, &mts, 407*22ce4affSfengbojiang sizeof(mts), j); 408*22ce4affSfengbojiang if (ret != 0) { 409*22ce4affSfengbojiang _memstat_mtl_empty(list); 410*22ce4affSfengbojiang list->mtl_error = ret; 411*22ce4affSfengbojiang return (-1); 412*22ce4affSfengbojiang } 413*22ce4affSfengbojiang mtp->mt_memalloced += mts.mts_memalloced; 414*22ce4affSfengbojiang mtp->mt_memfreed += mts.mts_memfreed; 415*22ce4affSfengbojiang mtp->mt_numallocs += mts.mts_numallocs; 416*22ce4affSfengbojiang mtp->mt_numfrees += mts.mts_numfrees; 417*22ce4affSfengbojiang mtp->mt_sizemask |= mts.mts_size; 4181eaf0ac3Slogwang 4191eaf0ac3Slogwang mtp->mt_percpu_alloc[j].mtp_memalloced = 420*22ce4affSfengbojiang mts.mts_memalloced; 4211eaf0ac3Slogwang mtp->mt_percpu_alloc[j].mtp_memfreed = 422*22ce4affSfengbojiang mts.mts_memfreed; 4231eaf0ac3Slogwang mtp->mt_percpu_alloc[j].mtp_numallocs = 424*22ce4affSfengbojiang mts.mts_numallocs; 4251eaf0ac3Slogwang mtp->mt_percpu_alloc[j].mtp_numfrees = 426*22ce4affSfengbojiang mts.mts_numfrees; 4271eaf0ac3Slogwang mtp->mt_percpu_alloc[j].mtp_sizemask = 428*22ce4affSfengbojiang mts.mts_size; 429*22ce4affSfengbojiang } 430*22ce4affSfengbojiang for (; j < mp_maxcpus; j++) { 431*22ce4affSfengbojiang bzero(&mtp->mt_percpu_alloc[j], 432*22ce4affSfengbojiang sizeof(mtp->mt_percpu_alloc[0])); 4331eaf0ac3Slogwang } 4341eaf0ac3Slogwang 4351eaf0ac3Slogwang mtp->mt_bytes = mtp->mt_memalloced - mtp->mt_memfreed; 4361eaf0ac3Slogwang mtp->mt_count = mtp->mt_numallocs - mtp->mt_numfrees; 4371eaf0ac3Slogwang } 4381eaf0ac3Slogwang 4391eaf0ac3Slogwang return (0); 4401eaf0ac3Slogwang } 4411eaf0ac3Slogwang 442*22ce4affSfengbojiang static int 443*22ce4affSfengbojiang memstat_malloc_zone_init(void) 444*22ce4affSfengbojiang { 445*22ce4affSfengbojiang size_t size; 446*22ce4affSfengbojiang 447*22ce4affSfengbojiang size = sizeof(memstat_malloc_zone_count); 448*22ce4affSfengbojiang if (sysctlbyname("vm.malloc.zone_count", &memstat_malloc_zone_count, 449*22ce4affSfengbojiang &size, NULL, 0) < 0) { 450*22ce4affSfengbojiang return (-1); 451*22ce4affSfengbojiang } 452*22ce4affSfengbojiang 453*22ce4affSfengbojiang if (memstat_malloc_zone_count > (int)nitems(memstat_malloc_zone_sizes)) { 454*22ce4affSfengbojiang return (-1); 455*22ce4affSfengbojiang } 456*22ce4affSfengbojiang 457*22ce4affSfengbojiang size = sizeof(memstat_malloc_zone_sizes); 458*22ce4affSfengbojiang if (sysctlbyname("vm.malloc.zone_sizes", &memstat_malloc_zone_sizes, 459*22ce4affSfengbojiang &size, NULL, 0) < 0) { 460*22ce4affSfengbojiang return (-1); 461*22ce4affSfengbojiang } 462*22ce4affSfengbojiang 463*22ce4affSfengbojiang return (0); 464*22ce4affSfengbojiang } 465*22ce4affSfengbojiang 466*22ce4affSfengbojiang /* 467*22ce4affSfengbojiang * Copied from kern_malloc.c 468*22ce4affSfengbojiang * 469*22ce4affSfengbojiang * kz_zone is an array sized at compilation time, the size is exported in 470*22ce4affSfengbojiang * "numzones". Below we need to iterate kz_size. 471*22ce4affSfengbojiang */ 472*22ce4affSfengbojiang struct memstat_kmemzone { 473*22ce4affSfengbojiang int kz_size; 474*22ce4affSfengbojiang const char *kz_name; 475*22ce4affSfengbojiang void *kz_zone[1]; 476*22ce4affSfengbojiang }; 477*22ce4affSfengbojiang 478*22ce4affSfengbojiang static int 479*22ce4affSfengbojiang memstat_malloc_zone_init_kvm(kvm_t *kvm) 480*22ce4affSfengbojiang { 481*22ce4affSfengbojiang struct memstat_kmemzone *kmemzones, *kz; 482*22ce4affSfengbojiang int numzones, objsize, allocsize, ret; 483*22ce4affSfengbojiang int i; 484*22ce4affSfengbojiang 485*22ce4affSfengbojiang ret = kread_symbol(kvm, X_VM_MALLOC_ZONE_COUNT, 486*22ce4affSfengbojiang &memstat_malloc_zone_count, sizeof(memstat_malloc_zone_count), 0); 487*22ce4affSfengbojiang if (ret != 0) { 488*22ce4affSfengbojiang return (ret); 489*22ce4affSfengbojiang } 490*22ce4affSfengbojiang 491*22ce4affSfengbojiang ret = kread_symbol(kvm, X_NUMZONES, &numzones, sizeof(numzones), 0); 492*22ce4affSfengbojiang if (ret != 0) { 493*22ce4affSfengbojiang return (ret); 494*22ce4affSfengbojiang } 495*22ce4affSfengbojiang 496*22ce4affSfengbojiang objsize = __offsetof(struct memstat_kmemzone, kz_zone) + 497*22ce4affSfengbojiang sizeof(void *) * numzones; 498*22ce4affSfengbojiang 499*22ce4affSfengbojiang allocsize = objsize * memstat_malloc_zone_count; 500*22ce4affSfengbojiang kmemzones = malloc(allocsize); 501*22ce4affSfengbojiang if (kmemzones == NULL) { 502*22ce4affSfengbojiang return (MEMSTAT_ERROR_NOMEMORY); 503*22ce4affSfengbojiang } 504*22ce4affSfengbojiang ret = kread_symbol(kvm, X_KMEMZONES, kmemzones, allocsize, 0); 505*22ce4affSfengbojiang if (ret != 0) { 506*22ce4affSfengbojiang free(kmemzones); 507*22ce4affSfengbojiang return (ret); 508*22ce4affSfengbojiang } 509*22ce4affSfengbojiang 510*22ce4affSfengbojiang kz = kmemzones; 511*22ce4affSfengbojiang for (i = 0; i < (int)nitems(memstat_malloc_zone_sizes); i++) { 512*22ce4affSfengbojiang memstat_malloc_zone_sizes[i] = kz->kz_size; 513*22ce4affSfengbojiang kz = (struct memstat_kmemzone *)((char *)kz + objsize); 514*22ce4affSfengbojiang } 515*22ce4affSfengbojiang 516*22ce4affSfengbojiang free(kmemzones); 517*22ce4affSfengbojiang return (0); 518*22ce4affSfengbojiang } 519*22ce4affSfengbojiang 520*22ce4affSfengbojiang size_t 521*22ce4affSfengbojiang memstat_malloc_zone_get_count(void) 522*22ce4affSfengbojiang { 523*22ce4affSfengbojiang 524*22ce4affSfengbojiang return (memstat_malloc_zone_count); 525*22ce4affSfengbojiang } 526*22ce4affSfengbojiang 527*22ce4affSfengbojiang size_t 528*22ce4affSfengbojiang memstat_malloc_zone_get_size(size_t n) 529*22ce4affSfengbojiang { 530*22ce4affSfengbojiang 531*22ce4affSfengbojiang if (n >= nitems(memstat_malloc_zone_sizes)) { 532*22ce4affSfengbojiang return (-1); 533*22ce4affSfengbojiang } 534*22ce4affSfengbojiang 535*22ce4affSfengbojiang return (memstat_malloc_zone_sizes[n]); 536*22ce4affSfengbojiang } 537*22ce4affSfengbojiang 538*22ce4affSfengbojiang int 539*22ce4affSfengbojiang memstat_malloc_zone_used(const struct memory_type *mtp, size_t n) 540*22ce4affSfengbojiang { 541*22ce4affSfengbojiang 542*22ce4affSfengbojiang if (memstat_get_sizemask(mtp) & (1 << n)) 543*22ce4affSfengbojiang return (1); 544*22ce4affSfengbojiang 545*22ce4affSfengbojiang return (0); 546*22ce4affSfengbojiang } 547