xref: /f-stack/tools/libmemstat/memstat_malloc.c (revision 22ce4aff)
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