Index: lib/libmemstat/memstat.h =================================================================== --- lib/libmemstat/memstat.h +++ lib/libmemstat/memstat.h @@ -117,6 +117,13 @@ int memstat_kvm_malloc(struct memory_type_list *list, void *kvm_handle); int memstat_kvm_uma(struct memory_type_list *list, void *kvm_handle); +/* + * General malloc routines. + */ +size_t memstat_malloc_zone_get_count(void); +size_t memstat_malloc_zone_get_size(size_t n); +int memstat_malloc_zone_used(const struct memory_type *mtp, size_t n); + /* * Accessor methods for struct memory_type. */ Index: lib/libmemstat/memstat_malloc.c =================================================================== --- lib/libmemstat/memstat_malloc.c +++ lib/libmemstat/memstat_malloc.c @@ -44,10 +44,22 @@ #include "memstat.h" #include "memstat_internal.h" +static int memstat_malloc_zone_count; +static int memstat_malloc_zone_sizes[32]; + +static int memstat_malloc_zone_init(void); +static int memstat_malloc_zone_init_kvm(kvm_t *kvm); + static struct nlist namelist[] = { #define X_KMEMSTATISTICS 0 { .n_name = "_kmemstatistics" }, -#define X_MP_MAXCPUS 1 +#define X_KMEMZONES 1 + { .n_name = "_kmemzones" }, +#define X_NUMZONES 2 + { .n_name = "_numzones" }, +#define X_VM_MALLOC_ZONE_COUNT 3 + { .n_name = "_vm_malloc_zone_count" }, +#define X_MP_MAXCPUS 4 { .n_name = "_mp_maxcpus" }, { .n_name = "" }, }; @@ -111,6 +123,11 @@ return (-1); } + if (memstat_malloc_zone_init() == -1) { + list->mtl_error = MEMSTAT_ERROR_VERSION; + return (-1); + } + size = sizeof(*mthp) + count * (sizeof(*mthp) + sizeof(*mtsp) * maxcpus); @@ -333,6 +350,12 @@ return (-1); } + ret = memstat_malloc_zone_init_kvm(kvm); + if (ret != 0) { + list->mtl_error = ret; + return (-1); + } + mp_ncpus = kvm_getncpus(kvm); for (typep = kmemstatistics; typep != NULL; typep = type.ks_next) { @@ -416,3 +439,107 @@ return (0); } + +static int +memstat_malloc_zone_init(void) +{ + size_t size; + + size = sizeof(memstat_malloc_zone_count); + if (sysctlbyname("vm.malloc.zone_count", &memstat_malloc_zone_count, &size, NULL, 0) < 0) { + return (-1); + } + + if (memstat_malloc_zone_count > (int)nitems(memstat_malloc_zone_sizes)) { + return (-1); + } + + size = sizeof(memstat_malloc_zone_sizes); + if (sysctlbyname("vm.malloc.zone_sizes", &memstat_malloc_zone_sizes, &size, NULL, 0) < 0) { + return (-1); + } + + return (0); +} + +/* + * Copied from kern_malloc.c + * + * kz_zone is an array sized at compilation time, the size is exported in + * "numzones". Below we need to iterate kz_size. + */ +struct memstat_kmemzone { + int kz_size; + const char *kz_name; + void *kz_zone[1]; +}; + +static int +memstat_malloc_zone_init_kvm(kvm_t *kvm) +{ + struct memstat_kmemzone *kmemzones, *kz; + int numzones, objsize, allocsize, ret; + int i; + + ret = kread_symbol(kvm, X_VM_MALLOC_ZONE_COUNT, + &memstat_malloc_zone_count, sizeof(memstat_malloc_zone_count), 0); + if (ret != 0) { + return (ret); + } + + ret = kread_symbol(kvm, X_NUMZONES, &numzones, sizeof(numzones), 0); + if (ret != 0) { + return (ret); + } + + objsize = __offsetof(struct memstat_kmemzone, kz_zone) + + sizeof(void *) * numzones; + + allocsize = objsize * memstat_malloc_zone_count; + kmemzones = malloc(allocsize); + if (kmemzones == NULL) { + return (MEMSTAT_ERROR_NOMEMORY); + } + ret = kread_symbol(kvm, X_KMEMZONES, kmemzones, allocsize, 0); + if (ret != 0) { + free(kmemzones); + return (ret); + } + + kz = kmemzones; + for (i = 0; i < (int)nitems(memstat_malloc_zone_sizes); i++) { + memstat_malloc_zone_sizes[i] = kz->kz_size; + kz = (struct memstat_kmemzone *)((char *)kz + objsize); + } + + free(kmemzones); + return (0); +} + +size_t +memstat_malloc_zone_get_count(void) +{ + + return (memstat_malloc_zone_count); +} + +size_t +memstat_malloc_zone_get_size(size_t n) +{ + + if (n >= nitems(memstat_malloc_zone_sizes)) { + return (-1); + } + + return (memstat_malloc_zone_sizes[n]); +} + +int +memstat_malloc_zone_used(const struct memory_type *mtp, size_t n) +{ + + if (memstat_get_sizemask(mtp) & (1 << n)) + return (1); + + return (0); +} Index: sys/kern/kern_malloc.c =================================================================== --- sys/kern/kern_malloc.c +++ sys/kern/kern_malloc.c @@ -147,6 +147,8 @@ * Small malloc(9) memory allocations are allocated from a set of UMA buckets * of various sizes. * + * Warning: the layout of the struct is duplicated in libmemstat for KVM support. + * * XXX: The comment here used to read "These won't be powers of two for * long." It's possible that a significant amount of wasted memory could be * recovered by tuning the sizes of these buckets. @@ -217,6 +219,19 @@ CTLFLAG_RD | CTLTYPE_ULONG | CTLFLAG_MPSAFE, NULL, 0, sysctl_kmem_map_free, "LU", "Free space in kmem"); +static SYSCTL_NODE(_vm, OID_AUTO, malloc, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, + "Malloc information"); + +static u_int vm_malloc_zone_count = nitems(kmemzones); +SYSCTL_UINT(_vm_malloc, OID_AUTO, zone_count, + CTLFLAG_RD, &vm_malloc_zone_count, 0, + "Number of malloc zones"); + +static int sysctl_vm_malloc_zone_sizes(SYSCTL_HANDLER_ARGS); +SYSCTL_PROC(_vm_malloc, OID_AUTO, zone_sizes, + CTLFLAG_RD | CTLTYPE_OPAQUE | CTLFLAG_MPSAFE, NULL, 0, + sysctl_vm_malloc_zone_sizes, "S", "Zone sizes used by malloc"); + /* * The malloc_mtx protects the kmemstatistics linked list. */ @@ -278,6 +293,19 @@ return (sysctl_handle_long(oidp, &size, 0, req)); } +static int +sysctl_vm_malloc_zone_sizes(SYSCTL_HANDLER_ARGS) +{ + int sizes[nitems(kmemzones)]; + int i; + + for (i = 0; i < nitems(kmemzones); i++) { + sizes[i] = kmemzones[i].kz_size; + } + + return (SYSCTL_OUT(req, &sizes, sizeof(sizes))); +} + /* * malloc(9) uma zone separation -- sub-page buffer overruns in one * malloc type will affect only a subset of other malloc types. Index: usr.bin/vmstat/vmstat.c =================================================================== --- usr.bin/vmstat/vmstat.c +++ usr.bin/vmstat/vmstat.c @@ -1407,7 +1407,8 @@ { struct memory_type_list *mtlp; struct memory_type *mtp; - int error, first, i; + size_t i, zones; + int error, first; mtlp = memstat_mtl_alloc(); if (mtlp == NULL) { @@ -1435,6 +1436,7 @@ xo_emit("{T:/%13s} {T:/%5s} {T:/%6s} {T:/%7s} {T:/%8s} {T:Size(s)}\n", "Type", "InUse", "MemUse", "HighUse", "Requests"); xo_open_list("memory"); + zones = memstat_malloc_zone_get_count(); for (mtp = memstat_mtl_first(mtlp); mtp != NULL; mtp = memstat_mtl_next(mtp)) { if (memstat_get_numallocs(mtp) == 0 && @@ -1449,11 +1451,11 @@ (uintmax_t)memstat_get_numallocs(mtp)); first = 1; xo_open_list("size"); - for (i = 0; i < 32; i++) { - if (memstat_get_sizemask(mtp) & (1 << i)) { + for (i = 0; i < zones; i++) { + if (memstat_malloc_zone_used(mtp, i)) { if (!first) xo_emit(","); - xo_emit("{l:size/%d}", 1 << (i + 4)); + xo_emit("{l:size/%d}", memstat_malloc_zone_get_size(i)); first = 0; } }