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:
Robert Watson 2005-08-06 13:54:03 +00:00
parent 587c3309fb
commit ba23fa9bd8
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=148789
4 changed files with 190 additions and 7 deletions

View File

@ -53,6 +53,10 @@
.Fn memstat_mtl_geterror "struct memory_type_list *list"
.Ss Allocator Query Functions
.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"
.Ft int
.Fn memstat_sysctl_all "struct memory_type_list *list" "int flags"
@ -147,6 +151,8 @@ and freed on completion using
.Fn memstat_mtl_free .
Lists of memory types are populated via calls that query the kernel for
statistics information; currently:
.Fn memstat_kvm_all ,
.Fn memstat_kvm_malloc ,
.Fn memstat_kvm_uma ,
.Fn memstat_sysctl_all ,
.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
.An Robert Watson Aq rwatson@FreeBSD.org
.Sh BUGS
.Nm
cannot yet extract
.Xr malloc 9
statistics from kernel core dumps, although this should be straight forward
to implement.
There are memory allocators in the kernel, such as the VM page allocator
and sf_buf allocator, which are not currently supported by
.Nm.
.Pp
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

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).
*/
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);
/*

View File

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

View File

@ -32,6 +32,8 @@
#include <err.h>
#include <errno.h>
#include <kvm.h>
#include <nlist.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -39,6 +41,14 @@
#include "memstat.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
* 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);
}
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);
}