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
587c3309fb
commit
ba23fa9bd8
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=148789
@ -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
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user