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:
parent
f2028e439d
commit
d65e7588d3
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user