Index: lib/libmemstat/memstat_malloc.c =================================================================== --- lib/libmemstat/memstat_malloc.c +++ lib/libmemstat/memstat_malloc.c @@ -45,13 +45,21 @@ #include "memstat_internal.h" static struct nlist namelist[] = { -#define X_KMEMSTATISTICS 0 +#define X_KMEMSTATISTICSFORMAT 0 + { .n_name = "_kmemstatisticsformat" }, +#define X_KMEMSTATISTICS 1 { .n_name = "_kmemstatistics" }, -#define X_MP_MAXCPUS 1 +#define X_MP_MAXCPUS 2 { .n_name = "_mp_maxcpus" }, { .n_name = "" }, }; +static int memstat_kvm_malloc_format20180923(kvm_t *kvm, struct memory_type *mtp, + struct malloc_type_internal *mti, int mp_maxcpus); +static int memstat_kvm_malloc_format20180924(kvm_t *kvm, struct memory_type *mtp, + struct malloc_type_internal *mti, int mp_maxcpus); + + /* * Extract malloc(9) statistics from the running kernel, and store all memory * type information in the passed list. For each type, check the list for an @@ -284,10 +292,11 @@ { struct memory_type *mtp; void *kmemstatistics; - int hint_dontsearch, j, mp_maxcpus, ret; + unsigned long format; + int hint_dontsearch, mp_maxcpus, ret; char name[MEMTYPE_MAXNAME]; - struct malloc_type_stats *mts, *mtsp; - struct malloc_type_internal *mtip; + struct malloc_type_stats *mts; + struct malloc_type_internal *mtip, mti; struct malloc_type type, *typep; kvm_t *kvm; @@ -295,9 +304,18 @@ hint_dontsearch = LIST_EMPTY(&list->mtl_list); + format = 0; + if (kvm_nlist(kvm, namelist) != 0) { - list->mtl_error = MEMSTAT_ERROR_KVM; - return (-1); + /* + * This may be a kernel without the statistics format symbol. + * Try again, but without asking for it. + */ + if (kvm_nlist(kvm, &namelist[1]) != 0) { + list->mtl_error = MEMSTAT_ERROR_KVM; + return (-1); + } + format = 20180923; } if (namelist[X_KMEMSTATISTICS].n_type == 0 || @@ -320,6 +338,15 @@ return (-1); } + if (format == 0) { + ret = kread_symbol(kvm, X_KMEMSTATISTICSFORMAT, &format, + sizeof(format), 0); + if (ret != 0) { + list->mtl_error = ret; + return (-1); + } + } + mts = malloc(sizeof(struct malloc_type_stats) * mp_maxcpus); if (mts == NULL) { list->mtl_error = MEMSTAT_ERROR_NOMEMORY; @@ -347,9 +374,9 @@ * Since our compile-time value for MAXCPU may differ from the * kernel's, we populate our own array. */ + mtip = type.ks_handle; - ret = kread(kvm, mtip->mti_stats, mts, mp_maxcpus * - sizeof(struct malloc_type_stats), 0); + ret = kread(kvm, mtip, &mti, sizeof(mti), 0); if (ret != 0) { _memstat_mtl_empty(list); free(mts); @@ -371,34 +398,73 @@ return (-1); } - /* - * This logic is replicated from kern_malloc.c, and should - * be kept in sync. - */ - _memstat_mt_reset_stats(mtp, mp_maxcpus); - for (j = 0; j < mp_maxcpus; j++) { - mtsp = &mts[j]; - mtp->mt_memalloced += mtsp->mts_memalloced; - mtp->mt_memfreed += mtsp->mts_memfreed; - mtp->mt_numallocs += mtsp->mts_numallocs; - mtp->mt_numfrees += mtsp->mts_numfrees; - mtp->mt_sizemask |= mtsp->mts_size; + switch (format) { + case 20180923: + ret = memstat_kvm_malloc_format20180923(kvm, mtp, &mti, + mp_maxcpus); + break; + default: + ret = MEMSTAT_ERROR_VERSION; + } - mtp->mt_percpu_alloc[j].mtp_memalloced = - mtsp->mts_memalloced; - mtp->mt_percpu_alloc[j].mtp_memfreed = - mtsp->mts_memfreed; - mtp->mt_percpu_alloc[j].mtp_numallocs = - mtsp->mts_numallocs; - mtp->mt_percpu_alloc[j].mtp_numfrees = - mtsp->mts_numfrees; - mtp->mt_percpu_alloc[j].mtp_sizemask = - mtsp->mts_size; + if (ret != 0) { + _memstat_mtl_empty(list); + free(mts); + list->mtl_error = ret; + return (-1); } + } - mtp->mt_bytes = mtp->mt_memalloced - mtp->mt_memfreed; - mtp->mt_count = mtp->mt_numallocs - mtp->mt_numfrees; + return (0); +} + +static int +memstat_kvm_malloc_format20180923(kvm_t *kvm, struct memory_type *mtp, + struct malloc_type_internal *mti, int mp_maxcpus) +{ + int mp_ncpus, i, ret; + struct malloc_type_stats mts; + + mp_ncpus = kvm_getncpus(kvm); + + /* + * This logic is replicated from kern_malloc.c, and should + * be kept in sync. + */ + _memstat_mt_reset_stats(mtp, mp_maxcpus); + for (i = 0; i < mp_ncpus; i++) { + ret = kvm_read_zpcpu(kvm, (u_long)mti->mti_stats, + &mts, sizeof(mts), i); + if (ret != sizeof(mts)) { + if (ret < 0) + return (MEMSTAT_ERROR_KVM); + else + return (MEMSTAT_ERROR_KVM_SHORTREAD); + } + mtp->mt_memalloced += mts.mts_memalloced; + mtp->mt_memfreed += mts.mts_memfreed; + mtp->mt_numallocs += mts.mts_numallocs; + mtp->mt_numfrees += mts.mts_numfrees; + mtp->mt_sizemask |= mts.mts_size; + + mtp->mt_percpu_alloc[i].mtp_memalloced = + mts.mts_memalloced; + mtp->mt_percpu_alloc[i].mtp_memfreed = + mts.mts_memfreed; + mtp->mt_percpu_alloc[i].mtp_numallocs = + mts.mts_numallocs; + mtp->mt_percpu_alloc[i].mtp_numfrees = + mts.mts_numfrees; + mtp->mt_percpu_alloc[i].mtp_sizemask = + mts.mts_size; + } + for (; i < mp_maxcpus; i++) { + bzero(&mtp->mt_percpu_alloc[i], + sizeof(mtp->mt_percpu_alloc[0])); } + mtp->mt_bytes = mtp->mt_memalloced - mtp->mt_memfreed; + mtp->mt_count = mtp->mt_numallocs - mtp->mt_numfrees; + return (0); } Index: sys/kern/kern_malloc.c =================================================================== --- sys/kern/kern_malloc.c +++ sys/kern/kern_malloc.c @@ -120,6 +120,7 @@ MALLOC_DEFINE(M_DEVBUF, "devbuf", "device driver memory"); MALLOC_DEFINE(M_TEMP, "temp", "misc temporary data buffers"); +static u_long kmemstatisticsformat = 20180923; static struct malloc_type *kmemstatistics; static int kmemcount; @@ -224,10 +225,11 @@ */ static time_t t_malloc_fail; -#if defined(MALLOC_MAKE_FAILURES) || (MALLOC_DEBUG_MAXZONES > 1) static SYSCTL_NODE(_debug, OID_AUTO, malloc, CTLFLAG_RD, 0, "Kernel malloc debugging options"); -#endif + +SYSCTL_ULONG(_debug_malloc, OID_AUTO, kmemstatisticsformat, CTLFLAG_RD, + &kmemstatisticsformat, 0, "Version number of the statistics layout"); /* * malloc(9) fault injection -- cause malloc failures every (n) mallocs when