summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIliyan Malchev <malchev@google.com>2012-06-02 08:14:36 -0700
committerIliyan Malchev <malchev@google.com>2012-06-02 08:14:36 -0700
commit252a5c854a08e89fc7337ea679220161fe4ea98f (patch)
tree8e0e154a5c1c94613138ba8cee0b77b8e34d0a49
parentf0ddaa2fac00ac20059c0b2c142da9de2838a7b6 (diff)
parente1dd3c287ba836281de0197670018bd9bbfbd62b (diff)
downloadbionic-252a5c854a08e89fc7337ea679220161fe4ea98f.zip
bionic-252a5c854a08e89fc7337ea679220161fe4ea98f.tar.gz
bionic-252a5c854a08e89fc7337ea679220161fe4ea98f.tar.bz2
resolved conflicts for merge of e1dd3c28 to jb-dev-plus-aosp
Change-Id: I58b9c13d20771aa39b703ec05cbff8aeaad38fe8
-rw-r--r--libc/Android.mk5
-rw-r--r--libc/bionic/libc_init_common.c8
-rw-r--r--libc/bionic/libc_init_dynamic.c6
-rw-r--r--libc/bionic/malloc_debug_check.c574
-rw-r--r--libc/bionic/malloc_debug_check_mapinfo.c126
-rw-r--r--libc/bionic/malloc_debug_check_mapinfo.h46
-rw-r--r--libc/bionic/malloc_debug_common.c52
-rw-r--r--libc/bionic/malloc_debug_common.h28
-rw-r--r--libc/bionic/malloc_debug_leak.c265
-rw-r--r--libc/bionic/malloc_debug_qemu.c18
-rw-r--r--libc/bionic/malloc_debug_stacktrace.c76
11 files changed, 916 insertions, 288 deletions
diff --git a/libc/Android.mk b/libc/Android.mk
index 6c535dc..f55f9fe 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -731,7 +731,10 @@ LOCAL_CFLAGS := \
LOCAL_C_INCLUDES := $(libc_common_c_includes)
LOCAL_SRC_FILES := \
- bionic/malloc_debug_leak.c
+ bionic/malloc_debug_leak.c \
+ bionic/malloc_debug_check.c \
+ bionic/malloc_debug_check_mapinfo.c \
+ bionic/malloc_debug_stacktrace.c
LOCAL_MODULE:= libc_malloc_debug_leak
diff --git a/libc/bionic/libc_init_common.c b/libc/bionic/libc_init_common.c
index 7fb1246..4ce4db6 100644
--- a/libc/bionic/libc_init_common.c
+++ b/libc/bionic/libc_init_common.c
@@ -138,4 +138,12 @@ void __libc_fini(void* array)
func();
}
+
+#ifndef LIBC_STATIC
+ {
+ extern void __libc_postfini(void) __attribute__((weak));
+ if (__libc_postfini)
+ __libc_postfini();
+ }
+#endif
}
diff --git a/libc/bionic/libc_init_dynamic.c b/libc/bionic/libc_init_dynamic.c
index 1c8480c..3a7e8e2 100644
--- a/libc/bionic/libc_init_dynamic.c
+++ b/libc/bionic/libc_init_dynamic.c
@@ -89,6 +89,12 @@ void __libc_preinit(void)
malloc_debug_init();
}
+void __libc_postfini(void)
+{
+ extern void malloc_debug_fini(void);
+ malloc_debug_fini();
+}
+
/* This function is called from the executable's _start entry point
* (see arch-$ARCH/bionic/crtbegin_dynamic.S), which is itself
* called by the dynamic linker after it has loaded all shared
diff --git a/libc/bionic/malloc_debug_check.c b/libc/bionic/malloc_debug_check.c
new file mode 100644
index 0000000..4ae21fe
--- /dev/null
+++ b/libc/bionic/malloc_debug_check.c
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unwind.h>
+#include <dlfcn.h>
+#include <stdbool.h>
+
+#include <sys/types.h>
+#include <sys/system_properties.h>
+
+#include "dlmalloc.h"
+#include "logd.h"
+
+#include "malloc_debug_common.h"
+#include "malloc_debug_check_mapinfo.h"
+
+static mapinfo *milist;
+
+/* libc.debug.malloc.backlog */
+extern unsigned int malloc_double_free_backlog;
+
+#define MAX_BACKTRACE_DEPTH 15
+#define ALLOCATION_TAG 0x1ee7d00d
+#define BACKLOG_TAG 0xbabecafe
+#define FREE_POISON 0xa5
+#define BACKLOG_DEFAULT_LEN 100
+#define FRONT_GUARD 0xaa
+#define FRONT_GUARD_LEN (1<<5)
+#define REAR_GUARD 0xbb
+#define REAR_GUARD_LEN (1<<5)
+
+static void print_backtrace(const intptr_t *bt, unsigned int depth);
+
+static void log_message(const char* format, ...)
+{
+ extern pthread_mutex_t gAllocationsMutex;
+ extern const MallocDebug __libc_malloc_default_dispatch;
+ extern const MallocDebug* __libc_malloc_dispatch;
+
+ va_list args;
+
+ pthread_mutex_lock(&gAllocationsMutex);
+ {
+ const MallocDebug* current_dispatch = __libc_malloc_dispatch;
+ __libc_malloc_dispatch = &__libc_malloc_default_dispatch;
+ va_start(args, format);
+ __libc_android_log_vprint(ANDROID_LOG_ERROR, "libc",
+ format, args);
+ va_end(args);
+ __libc_malloc_dispatch = current_dispatch;
+ }
+ pthread_mutex_unlock(&gAllocationsMutex);
+}
+
+struct hdr {
+ uint32_t tag;
+ struct hdr *prev;
+ struct hdr *next;
+ intptr_t bt[MAX_BACKTRACE_DEPTH];
+ int bt_depth;
+ intptr_t freed_bt[MAX_BACKTRACE_DEPTH];
+ int freed_bt_depth;
+ size_t size;
+ char front_guard[FRONT_GUARD_LEN];
+} __attribute__((packed));
+
+struct ftr {
+ char rear_guard[REAR_GUARD_LEN];
+} __attribute__((packed));
+
+static inline struct ftr * to_ftr(struct hdr *hdr)
+{
+ return (struct ftr *)(((char *)(hdr + 1)) + hdr->size);
+}
+
+static inline void *user(struct hdr *hdr)
+{
+ return hdr + 1;
+}
+
+static inline struct hdr *meta(void *user)
+{
+ return ((struct hdr *)user) - 1;
+}
+
+/* Call this on exit() to get leaked memory */
+void free_leaked_memory(void);
+
+static unsigned num;
+static struct hdr *tail;
+static struct hdr *head;
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+static unsigned backlog_num;
+static struct hdr *backlog_tail;
+static struct hdr *backlog_head;
+static pthread_mutex_t backlog_lock = PTHREAD_MUTEX_INITIALIZER;
+
+extern __LIBC_HIDDEN__
+int get_backtrace(intptr_t* addrs, size_t max_entries);
+
+static void print_backtrace(const intptr_t *bt, unsigned int depth)
+{
+ const mapinfo *mi;
+ unsigned int cnt;
+ unsigned int rel_pc;
+ intptr_t self_bt[MAX_BACKTRACE_DEPTH];
+
+ if (!bt) {
+ depth = get_backtrace(self_bt, MAX_BACKTRACE_DEPTH);
+ bt = self_bt;
+ }
+
+ log_message("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
+ for (cnt = 0; cnt < depth && cnt < MAX_BACKTRACE_DEPTH; cnt++) {
+ mi = pc_to_mapinfo(milist, bt[cnt], &rel_pc);
+ log_message("\t#%02d pc %08x %s\n", cnt,
+ mi ? (intptr_t)rel_pc : bt[cnt],
+ mi ? mi->name : "(unknown)");
+ }
+}
+
+static inline void init_front_guard(struct hdr *hdr)
+{
+ memset(hdr->front_guard, FRONT_GUARD, FRONT_GUARD_LEN);
+}
+
+static inline bool is_front_guard_valid(struct hdr *hdr)
+{
+ unsigned i;
+ for (i = 0; i < FRONT_GUARD_LEN; i++)
+ if (hdr->front_guard[i] != FRONT_GUARD)
+ return 0;
+ return 1;
+}
+
+static inline void init_rear_guard(struct hdr *hdr)
+{
+ struct ftr *ftr = to_ftr(hdr);
+ memset(ftr->rear_guard, REAR_GUARD, REAR_GUARD_LEN);
+}
+
+static inline bool is_rear_guard_valid(struct hdr *hdr)
+{
+ unsigned i;
+ int valid = 1;
+ int first_mismatch = -1;
+ struct ftr *ftr = to_ftr(hdr);
+ for (i = 0; i < REAR_GUARD_LEN; i++) {
+ if (ftr->rear_guard[i] != REAR_GUARD) {
+ if (first_mismatch < 0)
+ first_mismatch = i;
+ valid = 0;
+ }
+ else if (first_mismatch >= 0) {
+ log_message("+++ REAR GUARD MISMATCH [%d, %d)\n", first_mismatch, i);
+ first_mismatch = -1;
+ }
+ }
+
+ if (first_mismatch >= 0)
+ log_message("+++ REAR GUARD MISMATCH [%d, %d)\n", first_mismatch, i);
+ return valid;
+}
+
+static inline void add_locked(struct hdr *hdr, struct hdr **tail, struct hdr **head)
+{
+ hdr->prev = NULL;
+ hdr->next = *head;
+ if (*head)
+ (*head)->prev = hdr;
+ else
+ *tail = hdr;
+ *head = hdr;
+}
+
+static inline int del_locked(struct hdr *hdr, struct hdr **tail, struct hdr **head)
+{
+ if (hdr->prev)
+ hdr->prev->next = hdr->next;
+ else
+ *head = hdr->next;
+ if (hdr->next)
+ hdr->next->prev = hdr->prev;
+ else
+ *tail = hdr->prev;
+ return 0;
+}
+
+static inline void add(struct hdr *hdr, size_t size)
+{
+ pthread_mutex_lock(&lock);
+ hdr->tag = ALLOCATION_TAG;
+ hdr->size = size;
+ init_front_guard(hdr);
+ init_rear_guard(hdr);
+ num++;
+ add_locked(hdr, &tail, &head);
+ pthread_mutex_unlock(&lock);
+}
+
+static inline int del(struct hdr *hdr)
+{
+ if (hdr->tag != ALLOCATION_TAG)
+ return -1;
+
+ pthread_mutex_lock(&lock);
+ del_locked(hdr, &tail, &head);
+ num--;
+ pthread_mutex_unlock(&lock);
+ return 0;
+}
+
+static inline void poison(struct hdr *hdr)
+{
+ memset(user(hdr), FREE_POISON, hdr->size);
+}
+
+static int was_used_after_free(struct hdr *hdr)
+{
+ unsigned i;
+ const char *data = (const char *)user(hdr);
+ for (i = 0; i < hdr->size; i++)
+ if (data[i] != FREE_POISON)
+ return 1;
+ return 0;
+}
+
+/* returns 1 if valid, *safe == 1 if safe to dump stack */
+static inline int check_guards(struct hdr *hdr, int *safe)
+{
+ *safe = 1;
+ if (!is_front_guard_valid(hdr)) {
+ if (hdr->front_guard[0] == FRONT_GUARD) {
+ log_message("+++ ALLOCATION %p SIZE %d HAS A CORRUPTED FRONT GUARD\n",
+ user(hdr), hdr->size);
+ } else {
+ log_message("+++ ALLOCATION %p HAS A CORRUPTED FRONT GUARD "\
+ "(NOT DUMPING STACKTRACE)\n", user(hdr));
+ /* Allocation header is probably corrupt, do not print stack trace */
+ *safe = 0;
+ }
+ return 0;
+ }
+
+ if (!is_rear_guard_valid(hdr)) {
+ log_message("+++ ALLOCATION %p SIZE %d HAS A CORRUPTED REAR GUARD\n",
+ user(hdr), hdr->size);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* returns 1 if valid, *safe == 1 if safe to dump stack */
+static inline int check_allocation_locked(struct hdr *hdr, int *safe)
+{
+ int valid = 1;
+ *safe = 1;
+
+ if (hdr->tag != ALLOCATION_TAG && hdr->tag != BACKLOG_TAG) {
+ log_message("+++ ALLOCATION %p HAS INVALID TAG %08x (NOT DUMPING STACKTRACE)\n",
+ user(hdr), hdr->tag);
+ /* Allocation header is probably corrupt, do not dequeue or dump stack
+ * trace.
+ */
+ *safe = 0;
+ return 0;
+ }
+
+ if (hdr->tag == BACKLOG_TAG && was_used_after_free(hdr)) {
+ log_message("+++ ALLOCATION %p SIZE %d WAS USED AFTER BEING FREED\n",
+ user(hdr), hdr->size);
+ valid = 0;
+ /* check the guards to see if it's safe to dump a stack trace */
+ (void)check_guards(hdr, safe);
+ }
+ else
+ valid = check_guards(hdr, safe);
+
+ if (!valid && *safe) {
+ log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
+ user(hdr), hdr->size);
+ print_backtrace(hdr->bt, hdr->bt_depth);
+ if (hdr->tag == BACKLOG_TAG) {
+ log_message("+++ ALLOCATION %p SIZE %d FREED HERE:\n",
+ user(hdr), hdr->size);
+ print_backtrace(hdr->freed_bt, hdr->freed_bt_depth);
+ }
+ }
+
+ return valid;
+}
+
+static inline int del_and_check_locked(struct hdr *hdr,
+ struct hdr **tail, struct hdr **head, unsigned *cnt,
+ int *safe)
+{
+ int valid;
+ valid = check_allocation_locked(hdr, safe);
+ if (safe) {
+ (*cnt)--;
+ del_locked(hdr, tail, head);
+ }
+ return valid;
+}
+
+static inline void del_from_backlog_locked(struct hdr *hdr)
+{
+ int safe;
+ (void)del_and_check_locked(hdr,
+ &backlog_tail, &backlog_head, &backlog_num,
+ &safe);
+ hdr->tag = 0; /* clear the tag */
+}
+
+static inline void del_from_backlog(struct hdr *hdr)
+{
+ pthread_mutex_lock(&backlog_lock);
+ del_from_backlog_locked(hdr);
+ pthread_mutex_unlock(&backlog_lock);
+}
+
+static inline int del_leak(struct hdr *hdr, int *safe)
+{
+ int valid;
+ pthread_mutex_lock(&lock);
+ valid = del_and_check_locked(hdr,
+ &tail, &head, &num,
+ safe);
+ pthread_mutex_unlock(&lock);
+ return valid;
+}
+
+static inline void add_to_backlog(struct hdr *hdr)
+{
+ pthread_mutex_lock(&backlog_lock);
+ hdr->tag = BACKLOG_TAG;
+ backlog_num++;
+ add_locked(hdr, &backlog_tail, &backlog_head);
+ poison(hdr);
+ /* If we've exceeded the maximum backlog, clear it up */
+ while (backlog_num > malloc_double_free_backlog) {
+ struct hdr *gone = backlog_tail;
+ del_from_backlog_locked(gone);
+ dlfree(gone);
+ }
+ pthread_mutex_unlock(&backlog_lock);
+}
+
+void* chk_malloc(size_t size)
+{
+ struct hdr *hdr;
+
+// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+
+ hdr = dlmalloc(sizeof(struct hdr) + size + sizeof(struct ftr));
+ if (hdr) {
+ hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
+ add(hdr, size);
+ return user(hdr);
+ }
+ return NULL;
+}
+
+void* chk_memalign(size_t alignment, size_t bytes)
+{
+// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+ // XXX: it's better to use malloc, than being wrong
+ return chk_malloc(bytes);
+}
+
+void chk_free(void *ptr)
+{
+ struct hdr *hdr;
+
+// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+
+ if (!ptr) /* ignore free(NULL) */
+ return;
+
+ hdr = meta(ptr);
+
+ if (del(hdr) < 0) {
+ intptr_t bt[MAX_BACKTRACE_DEPTH];
+ int depth;
+ depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
+ if (hdr->tag == BACKLOG_TAG) {
+ log_message("+++ ALLOCATION %p SIZE %d BYTES MULTIPLY FREED!\n",
+ user(hdr), hdr->size);
+ log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
+ user(hdr), hdr->size);
+ print_backtrace(hdr->bt, hdr->bt_depth);
+ /* hdr->freed_bt_depth should be nonzero here */
+ log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n",
+ user(hdr), hdr->size);
+ print_backtrace(hdr->freed_bt, hdr->freed_bt_depth);
+ log_message("+++ ALLOCATION %p SIZE %d NOW BEING FREED HERE:\n",
+ user(hdr), hdr->size);
+ print_backtrace(bt, depth);
+ }
+ else {
+ log_message("+++ ALLOCATION %p IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n",
+ user(hdr));
+ print_backtrace(bt, depth);
+ /* Leak here so that we do not crash */
+ //dlfree(user(hdr));
+ }
+ }
+ else {
+ hdr->freed_bt_depth = get_backtrace(hdr->freed_bt,
+ MAX_BACKTRACE_DEPTH);
+ add_to_backlog(hdr);
+ }
+}
+
+void *chk_realloc(void *ptr, size_t size)
+{
+ struct hdr *hdr;
+
+// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+
+ if (!size) {
+ chk_free(ptr);
+ return NULL;
+ }
+
+ if (!ptr)
+ return chk_malloc(size);
+
+ hdr = meta(ptr);
+
+ if (del(hdr) < 0) {
+ intptr_t bt[MAX_BACKTRACE_DEPTH];
+ int depth;
+ depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
+ if (hdr->tag == BACKLOG_TAG) {
+ log_message("+++ REALLOCATION %p SIZE %d OF FREED MEMORY!\n",
+ user(hdr), size, hdr->size);
+ log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
+ user(hdr), hdr->size);
+ print_backtrace(hdr->bt, hdr->bt_depth);
+ /* hdr->freed_bt_depth should be nonzero here */
+ log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n",
+ user(hdr), hdr->size);
+ print_backtrace(hdr->freed_bt, hdr->freed_bt_depth);
+ log_message("+++ ALLOCATION %p SIZE %d NOW BEING REALLOCATED HERE:\n",
+ user(hdr), hdr->size);
+ print_backtrace(bt, depth);
+
+ /* We take the memory out of the backlog and fall through so the
+ * reallocation below succeeds. Since we didn't really free it, we
+ * can default to this behavior.
+ */
+ del_from_backlog(hdr);
+ }
+ else {
+ log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n",
+ user(hdr), size);
+ print_backtrace(bt, depth);
+ // just get a whole new allocation and leak the old one
+ return dlrealloc(0, size);
+ // return dlrealloc(user(hdr), size); // assuming it was allocated externally
+ }
+ }
+
+ hdr = dlrealloc(hdr, sizeof(struct hdr) + size + sizeof(struct ftr));
+ if (hdr) {
+ hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
+ add(hdr, size);
+ return user(hdr);
+ }
+
+ return NULL;
+}
+
+void *chk_calloc(int nmemb, size_t size)
+{
+// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+ struct hdr *hdr;
+ size_t total_size = nmemb * size;
+ hdr = dlcalloc(1, sizeof(struct hdr) + total_size + sizeof(struct ftr));
+ if (hdr) {
+ hdr->bt_depth = get_backtrace(
+ hdr->bt, MAX_BACKTRACE_DEPTH);
+ add(hdr, total_size);
+ return user(hdr);
+ }
+ return NULL;
+}
+
+static void heaptracker_free_leaked_memory(void)
+{
+ struct hdr *del; int cnt;
+
+ if (num)
+ log_message("+++ THERE ARE %d LEAKED ALLOCATIONS\n", num);
+
+ while (head) {
+ int safe;
+ del = head;
+ log_message("+++ DELETING %d BYTES OF LEAKED MEMORY AT %p (%d REMAINING)\n",
+ del->size, user(del), num);
+ if (del_leak(del, &safe)) {
+ /* safe == 1, because the allocation is valid */
+ log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
+ user(del), del->size);
+ print_backtrace(del->bt, del->bt_depth);
+ }
+ dlfree(del);
+ }
+
+// log_message("+++ DELETING %d BACKLOGGED ALLOCATIONS\n", backlog_num);
+ while (backlog_head) {
+ del = backlog_tail;
+ del_from_backlog(del);
+ dlfree(del);
+ }
+}
+
+/* Initializes malloc debugging framework.
+ * See comments on MallocDebugInit in malloc_debug_common.h
+ */
+int malloc_debug_initialize(void)
+{
+ if (!malloc_double_free_backlog)
+ malloc_double_free_backlog = BACKLOG_DEFAULT_LEN;
+ milist = init_mapinfo(getpid());
+ return 0;
+}
+
+void malloc_debug_finalize(void)
+{
+ heaptracker_free_leaked_memory();
+ deinit_mapinfo(milist);
+}
diff --git a/libc/bionic/malloc_debug_check_mapinfo.c b/libc/bionic/malloc_debug_check_mapinfo.c
new file mode 100644
index 0000000..044fc65
--- /dev/null
+++ b/libc/bionic/malloc_debug_check_mapinfo.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "dlmalloc.h"
+#include "malloc_debug_check_mapinfo.h"
+
+// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
+// 012345678901234567890123456789012345678901234567890123456789
+// 0 1 2 3 4 5
+
+static mapinfo *parse_maps_line(char *line)
+{
+ mapinfo *mi;
+ int len = strlen(line);
+
+ if(len < 1) return 0;
+ line[--len] = 0;
+
+ if(len < 50) return 0;
+ if(line[20] != 'x') return 0;
+
+ mi = dlmalloc(sizeof(mapinfo) + (len - 47));
+ if(mi == 0) return 0;
+
+ mi->start = strtoul(line, 0, 16);
+ mi->end = strtoul(line + 9, 0, 16);
+ /* To be filled in parse_elf_info if the mapped section starts with
+ * elf_header
+ */
+ mi->next = 0;
+ strcpy(mi->name, line + 49);
+
+ return mi;
+}
+
+__LIBC_HIDDEN__
+mapinfo *init_mapinfo(int pid)
+{
+ struct mapinfo *milist = NULL;
+ char data[1024];
+ sprintf(data, "/proc/%d/maps", pid);
+ FILE *fp = fopen(data, "r");
+ if(fp) {
+ while(fgets(data, sizeof(data), fp)) {
+ mapinfo *mi = parse_maps_line(data);
+ if(mi) {
+ mi->next = milist;
+ milist = mi;
+ }
+ }
+ fclose(fp);
+ }
+
+ return milist;
+}
+
+__LIBC_HIDDEN__
+void deinit_mapinfo(mapinfo *mi)
+{
+ mapinfo *del;
+ while(mi) {
+ del = mi;
+ mi = mi->next;
+ dlfree(del);
+ }
+}
+
+/* Map a pc address to the name of the containing ELF file */
+__LIBC_HIDDEN__
+const char *map_to_name(mapinfo *mi, unsigned pc, const char* def)
+{
+ while(mi) {
+ if((pc >= mi->start) && (pc < mi->end)){
+ return mi->name;
+ }
+ mi = mi->next;
+ }
+ return def;
+}
+
+/* Find the containing map info for the pc */
+__LIBC_HIDDEN__
+const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc)
+{
+ *rel_pc = pc;
+ while(mi) {
+ if((pc >= mi->start) && (pc < mi->end)){
+ // Only calculate the relative offset for shared libraries
+ if (strstr(mi->name, ".so")) {
+ *rel_pc -= mi->start;
+ }
+ return mi;
+ }
+ mi = mi->next;
+ }
+ return NULL;
+}
diff --git a/libc/bionic/malloc_debug_check_mapinfo.h b/libc/bionic/malloc_debug_check_mapinfo.h
new file mode 100644
index 0000000..8a01cd3
--- /dev/null
+++ b/libc/bionic/malloc_debug_check_mapinfo.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_DEBUG_CHECK_MAPINFO_H
+#define MALLOC_DEBUG_CHECK_MAPINFO_H
+
+#include <sys/cdefs.h>
+
+typedef struct mapinfo {
+ struct mapinfo *next;
+ unsigned start;
+ unsigned end;
+ char name[];
+} mapinfo;
+
+__LIBC_HIDDEN__ mapinfo *init_mapinfo(int pid);
+__LIBC_HIDDEN__ void deinit_mapinfo(mapinfo *mi);
+__LIBC_HIDDEN__ const char *map_to_name(mapinfo *mi, unsigned pc, const char* def);
+__LIBC_HIDDEN__ const mapinfo *pc_to_mapinfo(mapinfo *mi, unsigned pc, unsigned *rel_pc);
+
+#endif/*MALLOC_DEBUG_CHECK_MAPINFO_H*/
diff --git a/libc/bionic/malloc_debug_common.c b/libc/bionic/malloc_debug_common.c
index 6837e39..4105ab8 100644
--- a/libc/bionic/malloc_debug_common.c
+++ b/libc/bionic/malloc_debug_common.c
@@ -240,17 +240,6 @@ void* memalign(size_t alignment, size_t bytes) {
#include <dlfcn.h>
#include "logd.h"
-// =============================================================================
-// log functions
-// =============================================================================
-
-#define debug_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_DEBUG, "libc", (format), ##__VA_ARGS__ )
-#define error_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_ERROR, "libc", (format), ##__VA_ARGS__ )
-#define info_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ )
-
/* Table for dispatching malloc calls, depending on environment. */
static MallocDebug gMallocUse __attribute__((aligned(32))) = {
dlmalloc, dlfree, dlcalloc, dlrealloc, dlmemalign
@@ -288,6 +277,14 @@ static void* libc_malloc_impl_handle = NULL;
#define MALLOC_ALIGNMENT ((size_t)8U)
#endif /* MALLOC_ALIGNMENT */
+/* This variable is set to the value of property libc.debug.malloc.backlog,
+ * when the value of libc.debug.malloc = 10. It determines the size of the
+ * backlog we use to detect multiple frees. If the property is not set, the
+ * backlog length defaults to an internal constant defined in
+ * malloc_debug_check.c
+ */
+unsigned int malloc_double_free_backlog;
+
/* Initializes memory allocation framework once per process. */
static void malloc_init_impl(void)
{
@@ -339,9 +336,17 @@ static void malloc_init_impl(void)
switch (debug_level) {
case 1:
case 5:
- case 10:
+ case 10: {
+ char debug_backlog[PROP_VALUE_MAX];
+ if (__system_property_get("libc.debug.malloc.backlog", debug_backlog)) {
+ malloc_double_free_backlog = atoi(debug_backlog);
+ info_log("%s: setting backlog length to %d\n",
+ __progname, malloc_double_free_backlog);
+ }
+
so_name = "/system/lib/libc_malloc_debug_leak.so";
break;
+ }
case 20:
// Quick check: debug level 20 can only be handled in emulator.
if (!qemu_running) {
@@ -485,7 +490,19 @@ static void malloc_init_impl(void)
}
}
+static void malloc_fini_impl(void)
+{
+ if (libc_malloc_impl_handle) {
+ MallocDebugFini malloc_debug_finalize = NULL;
+ malloc_debug_finalize =
+ dlsym(libc_malloc_impl_handle, "malloc_debug_finalize");
+ if (malloc_debug_finalize)
+ malloc_debug_finalize();
+ }
+}
+
static pthread_once_t malloc_init_once_ctl = PTHREAD_ONCE_INIT;
+static pthread_once_t malloc_fini_once_ctl = PTHREAD_ONCE_INIT;
#endif // !LIBC_STATIC
#endif // USE_DL_PREFIX
@@ -504,3 +521,14 @@ void malloc_debug_init(void)
}
#endif // USE_DL_PREFIX && !LIBC_STATIC
}
+
+void malloc_debug_fini(void)
+{
+ /* We need to finalize malloc iff we implement here custom
+ * malloc routines (i.e. USE_DL_PREFIX is defined) for libc.so */
+#if defined(USE_DL_PREFIX) && !defined(LIBC_STATIC)
+ if (pthread_once(&malloc_fini_once_ctl, malloc_fini_impl)) {
+ error_log("Unable to finalize malloc_debug component.");
+ }
+#endif // USE_DL_PREFIX && !LIBC_STATIC
+}
diff --git a/libc/bionic/malloc_debug_common.h b/libc/bionic/malloc_debug_common.h
index 87600d6..c78846b 100644
--- a/libc/bionic/malloc_debug_common.h
+++ b/libc/bionic/malloc_debug_common.h
@@ -82,15 +82,31 @@ struct MallocDebug {
void* (*memalign)(size_t alignment, size_t bytes);
};
-/* Malloc debugging initialization routine.
- * This routine must be implemented in .so modules that implement malloc
- * debugging. This routine is called once per process from malloc_init_impl
- * routine implemented in bionic/libc/bionic/malloc_debug_common.c when malloc
+/* Malloc debugging initialization and finalization routines.
+ *
+ * These routines must be implemented in .so modules that implement malloc
+ * debugging. The are is called once per process from malloc_init_impl and
+ * malloc_fini_impl respectively.
+ *
+ * They are implemented in bionic/libc/bionic/malloc_debug_common.c when malloc
* debugging gets initialized for the process.
- * Return:
- * 0 on success, -1 on failure.
+ *
+ * MallocDebugInit returns:
+ * 0 on success, -1 on failure.
*/
typedef int (*MallocDebugInit)(void);
+typedef void (*MallocDebugFini)(void);
+
+// =============================================================================
+// log functions
+// =============================================================================
+
+#define debug_log(format, ...) \
+ __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak_check", (format), ##__VA_ARGS__ )
+#define error_log(format, ...) \
+ __libc_android_log_print(ANDROID_LOG_ERROR, "malloc_leak_check", (format), ##__VA_ARGS__ )
+#define info_log(format, ...) \
+ __libc_android_log_print(ANDROID_LOG_INFO, "malloc_leak_check", (format), ##__VA_ARGS__ )
#ifdef __cplusplus
}; /* end of extern "C" */
diff --git a/libc/bionic/malloc_debug_leak.c b/libc/bionic/malloc_debug_leak.c
index e584502..316d5fe 100644
--- a/libc/bionic/malloc_debug_leak.c
+++ b/libc/bionic/malloc_debug_leak.c
@@ -61,22 +61,11 @@
extern int gMallocLeakZygoteChild;
extern pthread_mutex_t gAllocationsMutex;
extern HashTable gHashTable;
-extern const MallocDebug __libc_malloc_default_dispatch;
-extern const MallocDebug* __libc_malloc_dispatch;
// =============================================================================
-// log functions
+// stack trace functions
// =============================================================================
-#define debug_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_DEBUG, "malloc_leak_check", (format), ##__VA_ARGS__ )
-#define error_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_ERROR, "malloc_leak_check", (format), ##__VA_ARGS__ )
-#define info_log(format, ...) \
- __libc_android_log_print(ANDROID_LOG_INFO, "malloc_leak_check", (format), ##__VA_ARGS__ )
-
-static int gTrapOnError = 1;
-
#define MALLOC_ALIGNMENT 8
#define GUARD 0x48151642
#define DEBUG 0
@@ -210,250 +199,12 @@ static void remove_entry(HashEntry* entry)
gHashTable.count--;
}
-
-// =============================================================================
-// stack trace functions
-// =============================================================================
-
-typedef struct
-{
- size_t count;
- intptr_t* addrs;
-} stack_crawl_state_t;
-
-
-/* depends how the system includes define this */
-#ifdef HAVE_UNWIND_CONTEXT_STRUCT
-typedef struct _Unwind_Context __unwind_context;
-#else
-typedef _Unwind_Context __unwind_context;
-#endif
-
-static _Unwind_Reason_Code trace_function(__unwind_context *context, void *arg)
-{
- stack_crawl_state_t* state = (stack_crawl_state_t*)arg;
- if (state->count) {
- intptr_t ip = (intptr_t)_Unwind_GetIP(context);
- if (ip) {
- state->addrs[0] = ip;
- state->addrs++;
- state->count--;
- return _URC_NO_REASON;
- }
- }
- /*
- * If we run out of space to record the address or 0 has been seen, stop
- * unwinding the stack.
- */
- return _URC_END_OF_STACK;
-}
-
-static inline
-int get_backtrace(intptr_t* addrs, size_t max_entries)
-{
- stack_crawl_state_t state;
- state.count = max_entries;
- state.addrs = (intptr_t*)addrs;
- _Unwind_Backtrace(trace_function, (void*)&state);
- return max_entries - state.count;
-}
-
// =============================================================================
-// malloc check functions
+// malloc fill functions
// =============================================================================
#define CHK_FILL_FREE 0xef
#define CHK_SENTINEL_VALUE (char)0xeb
-#define CHK_SENTINEL_HEAD_SIZE 16
-#define CHK_SENTINEL_TAIL_SIZE 16
-#define CHK_OVERHEAD_SIZE ( CHK_SENTINEL_HEAD_SIZE + \
- CHK_SENTINEL_TAIL_SIZE + \
- sizeof(size_t) )
-
-static void dump_stack_trace()
-{
- intptr_t addrs[20];
- int c = get_backtrace(addrs, 20);
- char buf[16];
- char tmp[16*20];
- int i;
-
- tmp[0] = 0; // Need to initialize tmp[0] for the first strcat
- for (i=0 ; i<c; i++) {
- snprintf(buf, sizeof buf, "%2d: %08x\n", i, addrs[i]);
- strlcat(tmp, buf, sizeof tmp);
- }
- __libc_android_log_print(ANDROID_LOG_ERROR, "libc", "call stack:\n%s", tmp);
-}
-
-static int is_valid_malloc_pointer(void* addr)
-{
- return 1;
-}
-
-static void assert_log_message(const char* format, ...)
-{
- va_list args;
-
- pthread_mutex_lock(&gAllocationsMutex);
- {
- const MallocDebug* current_dispatch = __libc_malloc_dispatch;
- __libc_malloc_dispatch = &__libc_malloc_default_dispatch;
- va_start(args, format);
- __libc_android_log_vprint(ANDROID_LOG_ERROR, "libc",
- format, args);
- va_end(args);
- dump_stack_trace();
- if (gTrapOnError) {
- __builtin_trap();
- }
- __libc_malloc_dispatch = current_dispatch;
- }
- pthread_mutex_unlock(&gAllocationsMutex);
-}
-
-static void assert_valid_malloc_pointer(void* mem)
-{
- if (mem && !is_valid_malloc_pointer(mem)) {
- assert_log_message(
- "*** MALLOC CHECK: buffer %p, is not a valid "
- "malloc pointer (are you mixing up new/delete "
- "and malloc/free?)", mem);
- }
-}
-
-/* Check that a given address corresponds to a guarded block,
- * and returns its original allocation size in '*allocated'.
- * 'func' is the capitalized name of the caller function.
- * Returns 0 on success, or -1 on failure.
- * NOTE: Does not return if gTrapOnError is set.
- */
-static int chk_mem_check(void* mem,
- size_t* allocated,
- const char* func)
-{
- char* buffer;
- size_t offset, bytes;
- int i;
- char* buf;
-
- /* first check the bytes in the sentinel header */
- buf = (char*)mem - CHK_SENTINEL_HEAD_SIZE;
- for (i=0 ; i<CHK_SENTINEL_HEAD_SIZE ; i++) {
- if (buf[i] != CHK_SENTINEL_VALUE) {
- assert_log_message(
- "*** %s CHECK: buffer %p "
- "corrupted %d bytes before allocation",
- func, mem, CHK_SENTINEL_HEAD_SIZE-i);
- return -1;
- }
- }
-
- /* then the ones in the sentinel trailer */
- buffer = (char*)mem - CHK_SENTINEL_HEAD_SIZE;
- offset = dlmalloc_usable_size(buffer) - sizeof(size_t);
- bytes = *(size_t *)(buffer + offset);
-
- buf = (char*)mem + bytes;
- for (i=CHK_SENTINEL_TAIL_SIZE-1 ; i>=0 ; i--) {
- if (buf[i] != CHK_SENTINEL_VALUE) {
- assert_log_message(
- "*** %s CHECK: buffer %p, size=%lu, "
- "corrupted %d bytes after allocation",
- func, buffer, bytes, i+1);
- return -1;
- }
- }
-
- *allocated = bytes;
- return 0;
-}
-
-
-void* chk_malloc(size_t bytes)
-{
- size_t size = bytes + CHK_OVERHEAD_SIZE;
- if (size < bytes) { // Overflow.
- return NULL;
- }
- uint8_t* buffer = (uint8_t*) dlmalloc(size);
- if (buffer) {
- memset(buffer, CHK_SENTINEL_VALUE, bytes + CHK_OVERHEAD_SIZE);
- size_t offset = dlmalloc_usable_size(buffer) - sizeof(size_t);
- *(size_t *)(buffer + offset) = bytes;
- buffer += CHK_SENTINEL_HEAD_SIZE;
- }
- return buffer;
-}
-
-void chk_free(void* mem)
-{
- assert_valid_malloc_pointer(mem);
- if (mem) {
- size_t size;
- char* buffer;
-
- if (chk_mem_check(mem, &size, "FREE") == 0) {
- buffer = (char*)mem - CHK_SENTINEL_HEAD_SIZE;
- memset(buffer, CHK_FILL_FREE, size + CHK_OVERHEAD_SIZE);
- dlfree(buffer);
- }
- }
-}
-
-void* chk_calloc(size_t n_elements, size_t elem_size)
-{
- size_t size;
- void* ptr;
-
- /* Fail on overflow - just to be safe even though this code runs only
- * within the debugging C library, not the production one */
- if (n_elements && MAX_SIZE_T / n_elements < elem_size) {
- return NULL;
- }
- size = n_elements * elem_size;
- ptr = chk_malloc(size);
- if (ptr != NULL) {
- memset(ptr, 0, size);
- }
- return ptr;
-}
-
-void* chk_realloc(void* mem, size_t bytes)
-{
- char* buffer;
- int ret;
- size_t old_bytes = 0;
-
- assert_valid_malloc_pointer(mem);
-
- if (mem != NULL && chk_mem_check(mem, &old_bytes, "REALLOC") < 0)
- return NULL;
-
- char* new_buffer = chk_malloc(bytes);
- if (mem == NULL) {
- return new_buffer;
- }
-
- if (new_buffer) {
- if (bytes > old_bytes)
- bytes = old_bytes;
- memcpy(new_buffer, mem, bytes);
- chk_free(mem);
- }
-
- return new_buffer;
-}
-
-void* chk_memalign(size_t alignment, size_t bytes)
-{
- // XXX: it's better to use malloc, than being wrong
- return chk_malloc(bytes);
-}
-
-// =============================================================================
-// malloc fill functions
-// =============================================================================
void* fill_malloc(size_t bytes)
{
@@ -501,6 +252,9 @@ void* fill_memalign(size_t alignment, size_t bytes)
#define MEMALIGN_GUARD ((void*)0xA1A41520)
+extern __LIBC_HIDDEN__
+int get_backtrace(intptr_t* addrs, size_t max_entries);
+
void* leak_malloc(size_t bytes)
{
// allocate enough space infront of the allocation to store the pointer for
@@ -645,12 +399,3 @@ void* leak_memalign(size_t alignment, size_t bytes)
}
return base;
}
-
-/* Initializes malloc debugging framework.
- * See comments on MallocDebugInit in malloc_debug_common.h
- */
-int malloc_debug_initialize(void)
-{
- // We don't really have anything that requires initialization here.
- return 0;
-}
diff --git a/libc/bionic/malloc_debug_qemu.c b/libc/bionic/malloc_debug_qemu.c
index 4b694e9..bcbf1e6 100644
--- a/libc/bionic/malloc_debug_qemu.c
+++ b/libc/bionic/malloc_debug_qemu.c
@@ -287,7 +287,7 @@ static void dump_malloc_descriptor(char* str,
/*
* Logging helper macros.
*/
-#define debug_log(format, ...) \
+#define qemu_debug_log(format, ...) \
do { \
__libc_android_log_print(ANDROID_LOG_DEBUG, "memcheck", \
(format), ##__VA_ARGS__ ); \
@@ -296,7 +296,7 @@ static void dump_malloc_descriptor(char* str,
} \
} while (0)
-#define error_log(format, ...) \
+#define qemu_error_log(format, ...) \
do { \
__libc_android_log_print(ANDROID_LOG_ERROR, "memcheck", \
(format), ##__VA_ARGS__ ); \
@@ -305,7 +305,7 @@ static void dump_malloc_descriptor(char* str,
} \
} while (0)
-#define info_log(format, ...) \
+#define qemu_info_log(format, ...) \
do { \
__libc_android_log_print(ANDROID_LOG_INFO, "memcheck", \
(format), ##__VA_ARGS__ ); \
@@ -692,7 +692,7 @@ memcheck_initialize(int alignment, const char* memcheck_param)
notify_qemu_libc_initialized(malloc_pid);
- debug_log("Instrumented for pid=%03u: malloc=%p, free=%p, calloc=%p, realloc=%p, memalign=%p",
+ qemu_debug_log("Instrumented for pid=%03u: malloc=%p, free=%p, calloc=%p, realloc=%p, memalign=%p",
malloc_pid, qemu_instrumented_malloc, qemu_instrumented_free,
qemu_instrumented_calloc, qemu_instrumented_realloc,
qemu_instrumented_memalign);
@@ -717,7 +717,7 @@ qemu_instrumented_malloc(size_t bytes)
desc.suffix_size = DEFAULT_SUFFIX_SIZE;
desc.ptr = dlmalloc(mallocdesc_alloc_size(&desc));
if (desc.ptr == NULL) {
- error_log("<libc_pid=%03u, pid=%03u> malloc(%u): dlmalloc(%u) failed.",
+ qemu_error_log("<libc_pid=%03u, pid=%03u> malloc(%u): dlmalloc(%u) failed.",
malloc_pid, getpid(), bytes, mallocdesc_alloc_size(&desc));
return NULL;
}
@@ -797,7 +797,7 @@ qemu_instrumented_calloc(size_t n_elements, size_t elem_size)
if (n_elements == 0 || elem_size == 0) {
// Just let go zero bytes allocation.
- info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc",
+ qemu_info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc",
malloc_pid, getpid());
return qemu_instrumented_malloc(0);
}
@@ -874,14 +874,14 @@ qemu_instrumented_realloc(void* mem, size_t bytes)
if (mem == NULL) {
// Nothing to realloc. just do regular malloc.
- info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to malloc",
+ qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to malloc",
malloc_pid, getpid(), mem, bytes);
return qemu_instrumented_malloc(bytes);
}
if (bytes == 0) {
// This is a "free" condition.
- info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to free and malloc",
+ qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %u) redir to free and malloc",
malloc_pid, getpid(), mem, bytes);
qemu_instrumented_free(mem);
@@ -977,7 +977,7 @@ qemu_instrumented_memalign(size_t alignment, size_t bytes)
if (bytes == 0) {
// Just let go zero bytes allocation.
- info_log("::: <libc_pid=%03u, pid=%03u>: memalign(%X, %u) redir to malloc",
+ qemu_info_log("::: <libc_pid=%03u, pid=%03u>: memalign(%X, %u) redir to malloc",
malloc_pid, getpid(), alignment, bytes);
return qemu_instrumented_malloc(0);
}
diff --git a/libc/bionic/malloc_debug_stacktrace.c b/libc/bionic/malloc_debug_stacktrace.c
new file mode 100644
index 0000000..c71b1c5
--- /dev/null
+++ b/libc/bionic/malloc_debug_stacktrace.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <unwind.h>
+#include <sys/types.h>
+
+// =============================================================================
+// stack trace functions
+// =============================================================================
+
+typedef struct
+{
+ size_t count;
+ intptr_t* addrs;
+} stack_crawl_state_t;
+
+
+/* depends how the system includes define this */
+#ifdef HAVE_UNWIND_CONTEXT_STRUCT
+typedef struct _Unwind_Context __unwind_context;
+#else
+typedef _Unwind_Context __unwind_context;
+#endif
+
+static _Unwind_Reason_Code trace_function(__unwind_context *context, void *arg)
+{
+ stack_crawl_state_t* state = (stack_crawl_state_t*)arg;
+ if (state->count) {
+ intptr_t ip = (intptr_t)_Unwind_GetIP(context);
+ if (ip) {
+ state->addrs[0] = ip;
+ state->addrs++;
+ state->count--;
+ return _URC_NO_REASON;
+ }
+ }
+ /*
+ * If we run out of space to record the address or 0 has been seen, stop
+ * unwinding the stack.
+ */
+ return _URC_END_OF_STACK;
+}
+
+__LIBC_HIDDEN__
+int get_backtrace(intptr_t* addrs, size_t max_entries)
+{
+ stack_crawl_state_t state;
+ state.count = max_entries;
+ state.addrs = (intptr_t*)addrs;
+ _Unwind_Backtrace(trace_function, (void*)&state);
+ return max_entries - state.count;
+}