qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 2/2] malloc: add statistics infrastructure


From: Paolo Bonzini
Subject: [Qemu-devel] [PATCH 2/2] malloc: add statistics infrastructure
Date: Mon, 2 May 2011 09:59:22 +0200

The infrastructure tracks the following items for each allocation locus:
number of currently allocated blocks, total number of allocated blocks,
number of currently allocated bytes, maximum number of allocated bytes,
total number of allocated bytes.  Accounting is thread safe, which is
why I didn't use qemu-queue functions.

Tracing is done by sticking an extra header in front of the allocated
block.  The header is needed so that free can account bytes correctly
and to the correct allocation locus.  The header is variable-sized
because of memalign, with the information stored at the tail so it is
easily accessible from free's parameter.  Hence it also has to include
a pointer to the actual base of the memory block.

Signed-off-by: Paolo Bonzini <address@hidden>
---
 configure       |    9 ++
 hmp-commands.hx |   16 ++++
 monitor.c       |   12 +++
 qemu-common.h   |   45 ++++++++++--
 qemu-malloc.c   |  223 +++++++++++++++++++++++++++++++++++++++++++++++++------
 5 files changed, 276 insertions(+), 29 deletions(-)

diff --git a/configure b/configure
index 6f75e2e..48003cf 100755
--- a/configure
+++ b/configure
@@ -171,6 +171,7 @@ pkgversion=""
 check_utests="no"
 user_pie="no"
 zero_malloc=""
+malloc_stats="no"
 trace_backend="nop"
 trace_file="trace"
 spice=""
@@ -665,6 +666,10 @@ for opt do
   ;;
   --disable-guest-base) guest_base="no"
   ;;
+  --enable-malloc-stats) malloc_stats="yes"
+  ;;
+  --disable-malloc-stats) malloc_stats="no"
+  ;;
   --enable-user-pie) user_pie="yes"
   ;;
   --disable-user-pie) user_pie="no"
@@ -2602,6 +2607,7 @@ echo "Documentation     $docs"
 echo "uname -r          $uname_release"
 echo "NPTL support      $nptl"
 echo "GUEST_BASE        $guest_base"
+echo "Malloc statistics $malloc_stats"
 echo "PIE user targets  $user_pie"
 echo "vde support       $vde"
 echo "IO thread         $io_thread"
@@ -2924,6 +2930,9 @@ fi
 
 echo "CONFIG_UNAME_RELEASE=\"$uname_release\"" >> $config_host_mak
 
+if test "$malloc_stats" = "yes" ; then
+  echo "CONFIG_MALLOC_STATS=y" >> $config_host_mak
+fi
 if test "$zero_malloc" = "yes" ; then
   echo "CONFIG_ZERO_MALLOC=y" >> $config_host_mak
 fi
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 834e6a8..54567dc 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -180,6 +180,22 @@ STEXI
 Output logs to @var{filename}.
 ETEXI
 
+#ifdef CONFIG_MALLOC_STATS
+    {
+        .name       = "dump-malloc-stats",
+        .args_type  = "name:F",
+        .params     = "name",
+        .help       = "dump memory allocation statistics to a file",
+        .mhandler.cmd = do_dump_malloc_stats,
+    },
+
+STEXI
address@hidden dump-malloc-stats @var{filename}
address@hidden dump-malloc-stats
+Dump memory allocation statistics to @var{filename}.
+ETEXI
+#endif
+
 #ifdef CONFIG_SIMPLE_TRACE
     {
         .name       = "trace-event",
diff --git a/monitor.c b/monitor.c
index 5f3bc72..361c90b 100644
--- a/monitor.c
+++ b/monitor.c
@@ -592,6 +592,18 @@ static void do_help_cmd(Monitor *mon, const QDict *qdict)
     help_cmd(mon, qdict_get_try_str(qdict, "name"));
 }
 
+#ifdef CONFIG_MALLOC_STATS
+static void do_dump_malloc_stats(Monitor *mon, const QDict *qdict)
+{
+    const char *file_name = qdict_get_str(qdict, "name");
+    int ret = dump_malloc_stats(file_name);
+
+    if (ret < 0) {
+        monitor_printf(mon, "error writing file: %s\n", strerror(-ret));
+    }
+}
+#endif
+
 #ifdef CONFIG_SIMPLE_TRACE
 static void do_change_trace_event_state(Monitor *mon, const QDict *qdict)
 {
diff --git a/qemu-common.h b/qemu-common.h
index 1382e7e..f2d4614 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -206,17 +206,48 @@ const char *path(const char *pathname);
 int ffs(int i);
 #endif
 
-void *qemu_malloc(size_t size);
-void *qemu_realloc(void *ptr, size_t size);
-void *qemu_mallocz(size_t size);
-char *qemu_strdup(const char *str);
-char *qemu_strndup(const char *str, size_t size);
-void *qemu_memalign(size_t alignment, size_t size);
-void *qemu_vmalloc(size_t size);
+typedef struct QEMUMallocStatsLocus QEMUMallocStatsLocus;
+
+struct QEMUMallocStatsLocus {
+    const char *file;
+    const char *func;
+    int line;
+    uint64_t live;
+    uint64_t count;
+    uint64_t alloc_tot;
+    size_t alloc;
+    size_t alloc_max;
+    struct QEMUMallocStatsLocus *next;
+};
+
+#ifdef CONFIG_MALLOC_STATS
+#define MALLOC_STATS_ARG ({ \
+    static struct QEMUMallocStatsLocus t = { __FILE__, __func__, __LINE__ }; \
+    &t; })
+#else
+#define MALLOC_STATS_ARG NULL
+#endif
+
+void *qemu_malloc(size_t size, struct QEMUMallocStatsLocus *locus);
+void *qemu_mallocz(size_t size, struct QEMUMallocStatsLocus *locus);
+void *qemu_realloc(void *ptr, size_t size, struct QEMUMallocStatsLocus *locus);
+char *qemu_strdup(const char *str, struct QEMUMallocStatsLocus *locus);
+char *qemu_strndup(const char *str, size_t size, struct QEMUMallocStatsLocus 
*locus);
+void *qemu_memalign(size_t alignment, size_t size, QEMUMallocStatsLocus 
*locus);
+void *qemu_vmalloc(size_t size, QEMUMallocStatsLocus *locus);
+int dump_malloc_stats(const char *file_name);
 
 void qemu_free(void *ptr);
 void qemu_vfree(void *ptr);
 
+#define qemu_malloc(size) ((qemu_malloc)(size, MALLOC_STATS_ARG))
+#define qemu_mallocz(size) ((qemu_mallocz)(size, MALLOC_STATS_ARG))
+#define qemu_realloc(ptr, size) ((qemu_realloc)(ptr, size, MALLOC_STATS_ARG))
+#define qemu_memalign(alignment, size) ((qemu_memalign)(alignment, size, 
MALLOC_STATS_ARG))
+#define qemu_vmalloc(size) ((qemu_vmalloc)(size, MALLOC_STATS_ARG))
+#define qemu_strdup(str) ((qemu_strdup)(str, MALLOC_STATS_ARG))
+#define qemu_strndup(str, size) ((qemu_strndup)(str, size, MALLOC_STATS_ARG))
+
 void qemu_mutex_lock_iothread(void);
 void qemu_mutex_unlock_iothread(void);
 
diff --git a/qemu-malloc.c b/qemu-malloc.c
index cc1305c..ae00083 100644
--- a/qemu-malloc.c
+++ b/qemu-malloc.c
@@ -25,11 +25,19 @@
 #include "trace.h"
 #include <stdlib.h>
 
-void qemu_free(void *ptr)
-{
-    trace_qemu_free(ptr);
-    free(ptr);
-}
+#ifdef CONFIG_MALLOC_STATS
+struct QEMUMallocStatsData {
+    void *base;
+    QEMUMallocStatsLocus *locus;
+    size_t size;
+};
+
+typedef struct QEMUMallocStatsData QEMUMallocStatsData;
+
+#define STATS_DATA_SIZE sizeof(QEMUMallocStatsData)
+#else
+#define STATS_DATA_SIZE 0
+#endif
 
 static void *qemu_oom_check(void *ptr)
 {
@@ -49,49 +57,153 @@ static int allow_zero_malloc(void)
 #endif
 }
 
-void *qemu_malloc(size_t size)
+#ifdef CONFIG_MALLOC_STATS
+static inline QEMUMallocStatsData *fetch_mem_data(void *ptr)
 {
-    void *ptr;
+    return ((QEMUMallocStatsData *)ptr) - 1;
+}
+#endif
+
+static inline void *store_mem_data(char *ptr, int extra,
+                                   QEMUMallocStatsLocus *locus, size_t size)
+{
+#ifdef CONFIG_MALLOC_STATS
+    QEMUMallocStatsData *data;
+    data = (QEMUMallocStatsData *) (ptr + extra - STATS_DATA_SIZE);
+    data->base = ptr;
+    data->locus = locus;
+    data->size = size;
+    return &data[1];
+#else
+    return ptr;
+#endif
+}
+
+#ifdef CONFIG_MALLOC_STATS
+static QEMUMallocStatsLocus *malloc_loci_head;
+#endif
+
+static inline void update_mem_stats(QEMUMallocStatsLocus *locus,
+                                    ptrdiff_t size, int dir)
+{
+#ifdef CONFIG_MALLOC_STATS
+    size_t alloc, alloc_max, alloc_tot;
+
+    __sync_add_and_fetch(&locus->live, dir);
+    alloc = __sync_add_and_fetch(&locus->alloc, size);
+    if (dir < 0) {
+        return;
+    }
+
+    __sync_add_and_fetch(&locus->count, dir);
+    do {
+        alloc_max = locus->alloc_max;
+    } while (alloc > alloc_max &&
+             !__sync_bool_compare_and_swap(&locus->alloc_max, alloc_max, 
alloc));
+
+    if (size < 0) {
+        return;
+    }
+
+    /* On the first allocation, put the struct into the list.  */
+    alloc_tot = __sync_fetch_and_add(&locus->alloc_tot, size);
+    if (alloc_tot == 0) {
+        do {
+            locus->next = malloc_loci_head;
+        } while (!__sync_bool_compare_and_swap(&malloc_loci_head,
+                                               locus->next, locus));
+    }
+#endif
+}
+
+void qemu_free(void *ptr)
+{
+    trace_qemu_free(ptr);
+#ifdef CONFIG_MALLOC_STATS
+    if (ptr) {
+        QEMUMallocStatsData *data = fetch_mem_data(ptr);
+        update_mem_stats(data->locus, -data->size, -1);
+        ptr = data->base;
+    }
+#endif
+    free(ptr);
+}
+
+#undef qemu_malloc
+void *qemu_malloc(size_t size, QEMUMallocStatsLocus *locus)
+{
+    char *ptr;
+    size_t real_size = size;
+    assert(size <= LONG_MAX);
     if (!size && !allow_zero_malloc()) {
         abort();
     }
+    size += STATS_DATA_SIZE;
     ptr = qemu_oom_check(malloc(size ? size : 1));
-    trace_qemu_malloc(size, ptr);
+    ptr = store_mem_data(ptr, STATS_DATA_SIZE, locus, real_size);
+    trace_qemu_malloc(real_size, ptr);
+    update_mem_stats(locus, real_size, 1);
     return ptr;
 }
 
-void *qemu_realloc(void *ptr, size_t size)
+#undef qemu_realloc
+void *qemu_realloc(void *ptr, size_t size, QEMUMallocStatsLocus *locus)
 {
-    void *newptr;
+    void *oldptr, *newptr;
+    size_t old_size;
+    size_t real_size = size;
+    assert(size <= LONG_MAX);
+    oldptr = ptr;
+    old_size = 0;
+
+#ifdef CONFIG_MALLOC_STATS
+    if (ptr) {
+        QEMUMallocStatsData *data = fetch_mem_data(ptr);
+        locus = data->locus;
+        old_size = data->size;
+        oldptr = data->base;
+    }
+#endif
     if (!size && !allow_zero_malloc()) {
         abort();
     }
-    newptr = qemu_oom_check(realloc(ptr, size ? size : 1));
-    trace_qemu_realloc(ptr, size, newptr);
+    size += STATS_DATA_SIZE;
+    newptr = qemu_oom_check(realloc(oldptr, size ? size : 1));
+    newptr = store_mem_data(newptr, STATS_DATA_SIZE, locus, real_size);
+    trace_qemu_realloc(ptr, real_size, newptr);
+    update_mem_stats(locus, real_size - old_size, ptr ? 0 : 1);
     return newptr;
 }
 
-void *qemu_mallocz(size_t size)
+#undef qemu_mallocz
+void *qemu_mallocz(size_t size, QEMUMallocStatsLocus *locus)
 {
     void *ptr;
+    size_t real_size = size;
+    assert(size <= LONG_MAX);
     if (!size && !allow_zero_malloc()) {
         abort();
     }
+    size += STATS_DATA_SIZE;
     ptr = qemu_oom_check(calloc(1, size ? size : 1));
-    trace_qemu_malloc(size, ptr);
+    ptr = store_mem_data(ptr, STATS_DATA_SIZE, locus, real_size);
+    trace_qemu_malloc(real_size, ptr);
+    update_mem_stats(locus, real_size, 1);
     return ptr;
 }
 
-char *qemu_strdup(const char *str)
+#undef qemu_strdup
+char *qemu_strdup(const char *str, QEMUMallocStatsLocus *locus)
 {
     char *ptr;
     size_t len = strlen(str);
-    ptr = qemu_malloc(len + 1);
+    ptr = (qemu_malloc)(len + 1, locus);
     memcpy(ptr, str, len + 1);
     return ptr;
 }
 
-char *qemu_strndup(const char *str, size_t size)
+#undef qemu_strndup
+char *qemu_strndup(const char *str, size_t size, QEMUMallocStatsLocus *locus)
 {
     const char *end = memchr(str, 0, size);
     char *new;
@@ -100,32 +212,99 @@ char *qemu_strndup(const char *str, size_t size)
         size = end - str;
     }
 
-    new = qemu_malloc(size + 1);
+    new = (qemu_malloc)(size + 1, locus);
     new[size] = 0;
 
     return memcpy(new, str, size);
 }
 
-void *qemu_memalign(size_t alignment, size_t size)
+#ifdef CONFIG_MALLOC_STATS
+int dump_malloc_stats(const char *file_name)
+{
+    FILE *file = fopen(file_name, "w");
+    QEMUMallocStatsLocus *p = malloc_loci_head;
+
+    const char *this_file = __FILE__;
+    const char *base = strrchr(this_file, '/');
+    int skip = base ? base + 1 - this_file : 0;
+    int rc = 0;
+
+    fprintf(file,
+            "FILE:LINE (FUNCTION)                      Live  "
+            "Count   Curr bytes    Max bytes        Tot bytes\n"
+            "------------------------------------------------"
+            "------------------------------------------------\n");
+
+    while (p && !ferror(file)) {
+        const char *locus_file, *prefix = "";
+        char s[40];
+        if (skip && !memcmp(p->file, this_file, skip)) {
+            locus_file = p->file + skip;
+        } else {
+            locus_file = strrchr(p->file, '/');
+            if (locus_file) {
+                prefix = "...";
+            } else {
+                locus_file = p->file;
+            }
+        }
+        snprintf(s, sizeof(s), "%s%s:%d (%s)",
+                 prefix, locus_file, p->line, p->func);
+        fprintf(file,
+                "%-40s %5"PRIu64 " %6"PRIu64 " %12"PRIu64" %12"PRIu64
+                " %16"PRIu64"\n",
+                s, p->live, p->count, p->alloc, p->alloc_max, p->alloc_tot);
+        p = p->next;
+    }
+
+    if (ferror(file)) {
+        rc = -errno;
+    }
+    if (fflush(file) == EOF && rc == 0) {
+        rc = -errno;
+    }
+    if (fclose(file) == EOF && rc == 0) {
+        rc = -errno;
+    }
+    return rc;
+}
+#endif
+
+#undef qemu_memalign
+void *qemu_memalign(size_t alignment, size_t size, QEMUMallocStatsLocus *locus)
 {
     char *ptr;
+    size_t real_size = size;
+    size_t extra;
     assert(size <= LONG_MAX);
     if (!size && !allow_zero_malloc()) {
         abort();
     }
+    extra = (STATS_DATA_SIZE + alignment - 1) & -alignment;
+    size += extra;
     ptr = qemu_oom_check(os_memalign(alignment, size ? size : 1));
-    trace_qemu_memalign(alignment, size, (char *)ptr);
+    ptr = store_mem_data(ptr, extra, locus, real_size);
+    trace_qemu_memalign(alignment, real_size, ptr);
+    update_mem_stats(locus, real_size, 1);
     return ptr;
 }
 
 /* alloc shared memory pages */
-void *qemu_vmalloc(size_t size)
+#undef qemu_vmalloc
+void *qemu_vmalloc(size_t size, QEMUMallocStatsLocus *locus)
 {
-    return qemu_memalign(getpagesize(), size);
+    return qemu_memalign(getpagesize(), size, locus);
 }
 
 void qemu_vfree(void *ptr)
 {
     trace_qemu_vfree(ptr);
+#ifdef CONFIG_MALLOC_STATS
+    if (ptr) {
+        QEMUMallocStatsData *data = fetch_mem_data(ptr);
+        update_mem_stats(data->locus, -data->size, -1);
+        ptr = data->base;
+    }
+#endif
     os_vfree(ptr);
 }
-- 
1.7.4.4




reply via email to

[Prev in Thread] Current Thread [Next in Thread]