Teach libmemstat(3) how to extract malloc(9) statistics using kvm(3),

so that libmemstat can be used to view full memory statistics from
kernel core dumps and /dev/mem.  This is provided via a new query
function, memstat_kvm_malloc(), which is also automatically invoked
by memstat_kvm_all().  A kvm handle must be passed in.

This will allow malloc(9)-specific code to be removed from vmstat(8).
This commit is contained in:
rwatson 2005-08-06 13:54:03 +00:00
parent f2028e439d
commit d65e7588d3
4 changed files with 190 additions and 7 deletions

View File

@ -53,6 +53,10 @@
.Fn memstat_mtl_geterror "struct memory_type_list *list" .Fn memstat_mtl_geterror "struct memory_type_list *list"
.Ss Allocator Query Functions .Ss Allocator Query Functions
.Ft int .Ft int
.Fn memstat_kvm_all "struct memory_type_list *list" "void *kvm_handle"
.Ft int
.Fn memstat_kvm_malloc "struct memory_type_list *list" "void *kvm_handle"
.Ft int
.Fn memstat_kvm_uma "struct memory_type_list *list" "void *kvm_handle" .Fn memstat_kvm_uma "struct memory_type_list *list" "void *kvm_handle"
.Ft int .Ft int
.Fn memstat_sysctl_all "struct memory_type_list *list" "int flags" .Fn memstat_sysctl_all "struct memory_type_list *list" "int flags"
@ -147,6 +151,8 @@ and freed on completion using
.Fn memstat_mtl_free . .Fn memstat_mtl_free .
Lists of memory types are populated via calls that query the kernel for Lists of memory types are populated via calls that query the kernel for
statistics information; currently: statistics information; currently:
.Fn memstat_kvm_all ,
.Fn memstat_kvm_malloc ,
.Fn memstat_kvm_uma , .Fn memstat_kvm_uma ,
.Fn memstat_sysctl_all , .Fn memstat_sysctl_all ,
.Fn memstat_sysctl_uma , .Fn memstat_sysctl_uma ,
@ -445,11 +451,9 @@ The kernel memory allocator changes necessary to support a general purpose
monitoring library, along with the library, were written by monitoring library, along with the library, were written by
.An Robert Watson Aq rwatson@FreeBSD.org .An Robert Watson Aq rwatson@FreeBSD.org
.Sh BUGS .Sh BUGS
.Nm There are memory allocators in the kernel, such as the VM page allocator
cannot yet extract and sf_buf allocator, which are not currently supported by
.Xr malloc 9 .Nm.
statistics from kernel core dumps, although this should be straight forward
to implement.
.Pp .Pp
Once a memory type is present on a memory type list, it will not be removed Once a memory type is present on a memory type list, it will not be removed
even if the kernel no longer presents information on the type via its even if the kernel no longer presents information on the type via its

View File

@ -119,6 +119,7 @@ int memstat_sysctl_uma(struct memory_type_list *list, int flags);
* Functions to retrieve data from a kernel core (or /dev/kmem). * Functions to retrieve data from a kernel core (or /dev/kmem).
*/ */
int memstat_kvm_all(struct memory_type_list *list, void *kvm_handle); int memstat_kvm_all(struct memory_type_list *list, void *kvm_handle);
int memstat_kvm_malloc(struct memory_type_list *list, void *kvm_handle);
int memstat_kvm_uma(struct memory_type_list *list, void *kvm_handle); int memstat_kvm_uma(struct memory_type_list *list, void *kvm_handle);
/* /*

View File

@ -50,10 +50,8 @@ int
memstat_kvm_all(struct memory_type_list *mtlp, void *kvm_handle) memstat_kvm_all(struct memory_type_list *mtlp, void *kvm_handle)
{ {
#if NOTYET
if (memstat_kvm_malloc(mtlp, kvm_handle) < 0) if (memstat_kvm_malloc(mtlp, kvm_handle) < 0)
return (-1); return (-1);
#endif
if (memstat_kvm_uma(mtlp, kvm_handle) < 0) if (memstat_kvm_uma(mtlp, kvm_handle) < 0)
return (-1); return (-1);
return (0); return (0);

View File

@ -32,6 +32,8 @@
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <kvm.h>
#include <nlist.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -39,6 +41,14 @@
#include "memstat.h" #include "memstat.h"
#include "memstat_internal.h" #include "memstat_internal.h"
static struct nlist namelist[] = {
#define X_KMEMSTATISTICS 0
{ .n_name = "_kmemstatistics" },
#define X_MP_MAXCPUS 1
{ .n_name = "_mp_maxcpus" },
{ .n_name = "" },
};
/* /*
* Extract malloc(9) statistics from the running kernel, and store all memory * 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 * type information in the passed list. For each type, check the list for an
@ -226,3 +236,173 @@ memstat_sysctl_malloc(struct memory_type_list *list, int flags)
return (0); return (0);
} }
static int
kread(kvm_t *kvm, void *kvm_pointer, void *address, size_t size,
size_t offset)
{
ssize_t ret;
ret = kvm_read(kvm, (unsigned long)kvm_pointer + offset, address,
size);
if (ret < 0)
return (MEMSTAT_ERROR_KVM);
if ((size_t)ret != size)
return (MEMSTAT_ERROR_KVM_SHORTREAD);
return (0);
}
static int
kread_string(kvm_t *kvm, void *kvm_pointer, char *buffer, int buflen)
{
ssize_t ret;
int i;
for (i = 0; i < buflen; i++) {
ret = kvm_read(kvm, (unsigned long)kvm_pointer + i,
&(buffer[i]), sizeof(char));
if (ret < 0)
return (MEMSTAT_ERROR_KVM);
if ((size_t)ret != sizeof(char))
return (MEMSTAT_ERROR_KVM_SHORTREAD);
if (buffer[i] == '\0')
return (0);
}
/* Truncate. */
buffer[i-1] = '\0';
return (0);
}
static int
kread_symbol(kvm_t *kvm, int index, void *address, size_t size,
size_t offset)
{
ssize_t ret;
ret = kvm_read(kvm, namelist[index].n_value + offset, address, size);
if (ret < 0)
return (MEMSTAT_ERROR_KVM);
if ((size_t)ret != size)
return (MEMSTAT_ERROR_KVM_SHORTREAD);
return (0);
}
int
memstat_kvm_malloc(struct memory_type_list *list, void *kvm_handle)
{
struct memory_type *mtp;
void *kmemstatistics;
int hint_dontsearch, j, mp_maxcpus, ret;
char name[MEMTYPE_MAXNAME];
struct malloc_type_stats mts[MEMSTAT_MAXCPU], *mtsp;
struct malloc_type type, *typep;
kvm_t *kvm;
kvm = (kvm_t *)kvm_handle;
hint_dontsearch = LIST_EMPTY(&list->mtl_list);
if (kvm_nlist(kvm, namelist) != 0) {
list->mtl_error = MEMSTAT_ERROR_KVM;
return (-1);
}
if (namelist[X_KMEMSTATISTICS].n_type == 0 ||
namelist[X_KMEMSTATISTICS].n_value == 0) {
list->mtl_error = MEMSTAT_ERROR_KVM_NOSYMBOL;
return (-1);
}
ret = kread_symbol(kvm, X_MP_MAXCPUS, &mp_maxcpus,
sizeof(mp_maxcpus), 0);
if (ret != 0) {
list->mtl_error = ret;
return (-1);
}
if (mp_maxcpus > MEMSTAT_MAXCPU) {
list->mtl_error = MEMSTAT_ERROR_TOOMANYCPUS;
return (-1);
}
ret = kread_symbol(kvm, X_KMEMSTATISTICS, &kmemstatistics,
sizeof(kmemstatistics), 0);
if (ret != 0) {
list->mtl_error = ret;
return (-1);
}
for (typep = kmemstatistics; typep != NULL; typep = type.ks_next) {
ret = kread(kvm, typep, &type, sizeof(type), 0);
if (ret != 0) {
_memstat_mtl_empty(list);
list->mtl_error = ret;
return (-1);
}
ret = kread_string(kvm, (void *)type.ks_shortdesc, name,
MEMTYPE_MAXNAME);
if (ret != 0) {
_memstat_mtl_empty(list);
list->mtl_error = ret;
return (-1);
}
/*
* Take advantage of explicit knowledge that
* malloc_type_internal is simply an array of statistics
* structures of number MAXCPU. Since our compile-time
* value for MAXCPU may differ from the kernel's, we
* populate our own array.
*/
ret = kread(kvm, type.ks_handle, mts, mp_maxcpus *
sizeof(struct malloc_type_stats), 0);
if (ret != 0) {
_memstat_mtl_empty(list);
list->mtl_error = ret;
return (-1);
}
if (hint_dontsearch == 0) {
mtp = memstat_mtl_find(list, ALLOCATOR_MALLOC, name);
} else
mtp = NULL;
if (mtp == NULL)
mtp = _memstat_mt_allocate(list, ALLOCATOR_MALLOC,
name);
if (mtp == NULL) {
_memstat_mtl_empty(list);
list->mtl_error = MEMSTAT_ERROR_NOMEMORY;
return (-1);
}
/*
* This logic is replicated from kern_malloc.c, and should
* be kept in sync.
*/
_memstat_mt_reset_stats(mtp);
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;
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;
}
mtp->mt_bytes = mtp->mt_memalloced - mtp->mt_memfreed;
mtp->mt_count = mtp->mt_numallocs - mtp->mt_numfrees;
}
return (0);
}