summaryrefslogtreecommitdiffstats
path: root/lib/libmemstat
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2005-08-06 13:54:03 +0000
committerrwatson <rwatson@FreeBSD.org>2005-08-06 13:54:03 +0000
commitd65e7588d3510a9ad0d93d4b0fa93b5e4c434dd5 (patch)
tree885f8f5cd1f7551ea42d39da8b233242b20cdc4c /lib/libmemstat
parentf2028e439d4f832b126345ea8bf8c110d43cd946 (diff)
downloadFreeBSD-src-d65e7588d3510a9ad0d93d4b0fa93b5e4c434dd5.zip
FreeBSD-src-d65e7588d3510a9ad0d93d4b0fa93b5e4c434dd5.tar.gz
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).
Diffstat (limited to 'lib/libmemstat')
-rw-r--r--lib/libmemstat/libmemstat.314
-rw-r--r--lib/libmemstat/memstat.h1
-rw-r--r--lib/libmemstat/memstat_all.c2
-rw-r--r--lib/libmemstat/memstat_malloc.c180
4 files changed, 190 insertions, 7 deletions
diff --git a/lib/libmemstat/libmemstat.3 b/lib/libmemstat/libmemstat.3
index a851e5e..ebab812 100644
--- a/lib/libmemstat/libmemstat.3
+++ b/lib/libmemstat/libmemstat.3
@@ -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
diff --git a/lib/libmemstat/memstat.h b/lib/libmemstat/memstat.h
index 561f6ee..904e77e 100644
--- a/lib/libmemstat/memstat.h
+++ b/lib/libmemstat/memstat.h
@@ -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);
/*
diff --git a/lib/libmemstat/memstat_all.c b/lib/libmemstat/memstat_all.c
index b363ab9..bd74b8a 100644
--- a/lib/libmemstat/memstat_all.c
+++ b/lib/libmemstat/memstat_all.c
@@ -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);
diff --git a/lib/libmemstat/memstat_malloc.c b/lib/libmemstat/memstat_malloc.c
index e94d005..b9b686f 100644
--- a/lib/libmemstat/memstat_malloc.c
+++ b/lib/libmemstat/memstat_malloc.c
@@ -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 @@ retry:
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);
+}
OpenPOWER on IntegriCloud