summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2014-07-24 17:52:23 -0700
committerChristopher Ferris <cferris@google.com>2014-07-29 18:10:00 -0700
commit861c0ef37bcfcae56d88572cb01c18bcfe1faded (patch)
treef63fc703a50b23213537962e4f60519f8b34db47
parenta7dc7600fe1be1f3fd61856b407bb7065307e711 (diff)
downloadbionic-861c0ef37bcfcae56d88572cb01c18bcfe1faded.zip
bionic-861c0ef37bcfcae56d88572cb01c18bcfe1faded.tar.gz
bionic-861c0ef37bcfcae56d88572cb01c18bcfe1faded.tar.bz2
Use libunwindbacktrace for debug malloc code.
Create a method of disabling the debug allocation code paths so that it's possible to use the libunwindbacktrace library without any modifications. Use this path to create and destroy the maps for the process. It's not stricly necessary in the init code since the symbols are not modified until after the initialize calls. Also, remove the debug_XXX source files that doesn't need to be in libc.so. Fix the maps reading code since it was completely broken for 64 bit. Bug: 16408686 Change-Id: I6b02ef6ce26fdb7a59ad1029e7cbba9accceb704
-rw-r--r--libc/Android.mk8
-rw-r--r--libc/bionic/debug_mapinfo.cpp53
-rw-r--r--libc/bionic/debug_mapinfo.h4
-rw-r--r--libc/bionic/debug_stacktrace.cpp13
-rw-r--r--libc/bionic/malloc_debug_check.cpp43
-rw-r--r--libc/bionic/malloc_debug_disable.h64
-rw-r--r--libc/bionic/malloc_debug_leak.cpp41
7 files changed, 198 insertions, 28 deletions
diff --git a/libc/Android.mk b/libc/Android.mk
index 15a68b9..9554e18 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -980,8 +980,6 @@ LOCAL_SRC_FILES := \
$(libc_arch_dynamic_src_files) \
$(libc_static_common_src_files) \
bionic/malloc_debug_common.cpp \
- bionic/debug_mapinfo.cpp \
- bionic/debug_stacktrace.cpp \
bionic/libc_init_dynamic.cpp \
bionic/NetdClient.cpp \
@@ -1047,7 +1045,10 @@ LOCAL_CFLAGS := \
LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
LOCAL_CPPFLAGS := $(libc_common_cppflags)
-LOCAL_C_INCLUDES := $(libc_common_c_includes)
+# Make sure that unwind.h comes from libunwind.
+LOCAL_C_INCLUDES := \
+ external/libunwind/include \
+ $(libc_common_c_includes) \
LOCAL_SRC_FILES := \
bionic/debug_mapinfo.cpp \
@@ -1062,6 +1063,7 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
LOCAL_SHARED_LIBRARIES := libc libdl
LOCAL_SYSTEM_SHARED_LIBRARIES :=
+LOCAL_WHOLE_STATIC_LIBRARIES := libunwindbacktrace
LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
# Don't install on release build
diff --git a/libc/bionic/debug_mapinfo.cpp b/libc/bionic/debug_mapinfo.cpp
index 17276ce..d83799a 100644
--- a/libc/bionic/debug_mapinfo.cpp
+++ b/libc/bionic/debug_mapinfo.cpp
@@ -26,39 +26,48 @@
* SUCH DAMAGE.
*/
+#include <ctype.h>
+#include <inttypes.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
-#include <sys/mman.h>
#include "debug_mapinfo.h"
+#include "malloc_debug_disable.h"
-// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
-// 012345678901234567890123456789012345678901234567890123456789
-// 0 1 2 3 4 5
-
+// Format of /proc/<PID>/maps:
+// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so
static mapinfo_t* parse_maps_line(char* line) {
- int len = strlen(line);
-
- if (len < 1) return 0;
- line[--len] = 0;
-
- if (len < 50) return 0;
- if (line[20] != 'x') return 0;
-
- mapinfo_t* mi = static_cast<mapinfo_t*>(
- mmap(NULL, sizeof(mapinfo_t) + (len - 47), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0));
- if (mi == MAP_FAILED) return 0;
+ uintptr_t start;
+ uintptr_t end;
+ int name_pos;
+ if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %*4s %*x %*x:%*x %*d%n", &start,
+ &end, &name_pos) < 2) {
+ return NULL;
+ }
- mi->start = strtoul(line, 0, 16);
- mi->end = strtoul(line + 9, 0, 16);
- mi->next = 0;
- strcpy(mi->name, line + 49);
+ while (isspace(line[name_pos])) {
+ name_pos += 1;
+ }
+ const char* name = line + name_pos;
+ size_t name_len = strlen(name);
+ if (name_len && name[name_len - 1] == '\n') {
+ name_len -= 1;
+ }
+ mapinfo_t* mi = reinterpret_cast<mapinfo_t*>(calloc(1, sizeof(mapinfo_t) + name_len + 1));
+ if (mi) {
+ mi->start = start;
+ mi->end = end;
+ memcpy(mi->name, name, name_len);
+ mi->name[name_len] = '\0';
+ }
return mi;
}
__LIBC_HIDDEN__ mapinfo_t* mapinfo_create(pid_t pid) {
+ ScopedDisableDebugCalls disable;
+
struct mapinfo_t* milist = NULL;
char data[1024]; // Used to read lines as well as to construct the filename.
snprintf(data, sizeof(data), "/proc/%d/maps", pid);
@@ -77,10 +86,12 @@ __LIBC_HIDDEN__ mapinfo_t* mapinfo_create(pid_t pid) {
}
__LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi) {
+ ScopedDisableDebugCalls disable;
+
while (mi != NULL) {
mapinfo_t* del = mi;
mi = mi->next;
- munmap(del, sizeof(mapinfo_t) + strlen(del->name) + 2);
+ free(del);
}
}
diff --git a/libc/bionic/debug_mapinfo.h b/libc/bionic/debug_mapinfo.h
index cccd2e3..926b377 100644
--- a/libc/bionic/debug_mapinfo.h
+++ b/libc/bionic/debug_mapinfo.h
@@ -33,8 +33,8 @@
struct mapinfo_t {
struct mapinfo_t* next;
- unsigned start;
- unsigned end;
+ uintptr_t start;
+ uintptr_t end;
char name[];
};
diff --git a/libc/bionic/debug_stacktrace.cpp b/libc/bionic/debug_stacktrace.cpp
index 713e761..b86e2af 100644
--- a/libc/bionic/debug_stacktrace.cpp
+++ b/libc/bionic/debug_stacktrace.cpp
@@ -35,6 +35,7 @@
#include <sys/types.h>
#include "debug_mapinfo.h"
+#include "malloc_debug_disable.h"
#include "private/libc_logging.h"
#if defined(__LP64__)
@@ -56,6 +57,8 @@ typedef char* (*DemanglerFn)(const char*, char*, size_t*, int*);
static DemanglerFn g_demangler_fn = NULL;
__LIBC_HIDDEN__ void backtrace_startup() {
+ ScopedDisableDebugCalls disable;
+
g_map_info = mapinfo_create(getpid());
g_demangler = dlopen("libgccdemangle.so", RTLD_NOW);
if (g_demangler != NULL) {
@@ -65,6 +68,8 @@ __LIBC_HIDDEN__ void backtrace_startup() {
}
__LIBC_HIDDEN__ void backtrace_shutdown() {
+ ScopedDisableDebugCalls disable;
+
mapinfo_destroy(g_map_info);
dlclose(g_demangler);
}
@@ -98,7 +103,7 @@ static _Unwind_Reason_Code trace_function(__unwind_context* context, void* arg)
return _URC_NO_REASON;
}
-#ifdef __arm__
+#if defined(__arm__)
/*
* The instruction pointer is pointing at the instruction after the bl(x), and
* the _Unwind_Backtrace routine already masks the Thumb mode indicator (LSB
@@ -121,12 +126,16 @@ static _Unwind_Reason_Code trace_function(__unwind_context* context, void* arg)
}
__LIBC_HIDDEN__ int get_backtrace(uintptr_t* frames, size_t max_depth) {
+ ScopedDisableDebugCalls disable;
+
stack_crawl_state_t state(frames, max_depth);
_Unwind_Backtrace(trace_function, &state);
return state.frame_count;
}
__LIBC_HIDDEN__ void log_backtrace(uintptr_t* frames, size_t frame_count) {
+ ScopedDisableDebugCalls disable;
+
uintptr_t self_bt[16];
if (frames == NULL) {
frame_count = get_backtrace(self_bt, 16);
@@ -146,7 +155,7 @@ __LIBC_HIDDEN__ void log_backtrace(uintptr_t* frames, size_t frame_count) {
symbol = info.dli_sname;
}
- uintptr_t rel_pc;
+ uintptr_t rel_pc = offset;
const mapinfo_t* mi = (g_map_info != NULL) ? mapinfo_find(g_map_info, frames[i], &rel_pc) : NULL;
const char* soname = (mi != NULL) ? mi->name : info.dli_fname;
if (soname == NULL) {
diff --git a/libc/bionic/malloc_debug_check.cpp b/libc/bionic/malloc_debug_check.cpp
index 1c63d4d..94ba6f5 100644
--- a/libc/bionic/malloc_debug_check.cpp
+++ b/libc/bionic/malloc_debug_check.cpp
@@ -49,6 +49,7 @@
#include "debug_mapinfo.h"
#include "debug_stacktrace.h"
#include "malloc_debug_common.h"
+#include "malloc_debug_disable.h"
#include "private/bionic_macros.h"
#include "private/libc_logging.h"
#include "private/ScopedPthreadMutexLocker.h"
@@ -331,6 +332,9 @@ static inline void add_to_backlog(hdr_t* hdr) {
extern "C" void* chk_malloc(size_t bytes) {
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->malloc(bytes);
+ }
size_t size = sizeof(hdr_t) + bytes + sizeof(ftr_t);
if (size < bytes) { // Overflow
@@ -348,6 +352,10 @@ extern "C" void* chk_malloc(size_t bytes) {
}
extern "C" void* chk_memalign(size_t alignment, size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->memalign(alignment, bytes);
+ }
+
if (alignment <= MALLOC_ALIGNMENT) {
return chk_malloc(bytes);
}
@@ -386,6 +394,9 @@ extern "C" void* chk_memalign(size_t alignment, size_t bytes) {
extern "C" void chk_free(void* ptr) {
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->free(ptr);
+ }
if (!ptr) /* ignore free(NULL) */
return;
@@ -421,6 +432,9 @@ extern "C" void chk_free(void* ptr) {
extern "C" void* chk_realloc(void* ptr, size_t bytes) {
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->realloc(ptr, bytes);
+ }
if (!ptr) {
return chk_malloc(bytes);
@@ -496,6 +510,10 @@ extern "C" void* chk_realloc(void* ptr, size_t bytes) {
extern "C" void* chk_calloc(size_t nmemb, size_t bytes) {
// log_message("%s: %s\n", __FILE__, __FUNCTION__);
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->calloc(nmemb, bytes);
+ }
+
size_t total_bytes = nmemb * bytes;
size_t size = sizeof(hdr_t) + total_bytes + sizeof(ftr_t);
if (size < total_bytes || (nmemb && SIZE_MAX / nmemb < bytes)) { // Overflow
@@ -513,6 +531,10 @@ extern "C" void* chk_calloc(size_t nmemb, size_t bytes) {
}
extern "C" size_t chk_malloc_usable_size(const void* ptr) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->malloc_usable_size(ptr);
+ }
+
// malloc_usable_size returns 0 for NULL and unknown blocks.
if (ptr == NULL)
return 0;
@@ -529,6 +551,10 @@ extern "C" struct mallinfo chk_mallinfo() {
}
extern "C" int chk_posix_memalign(void** memptr, size_t alignment, size_t size) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->posix_memalign(memptr, alignment, size);
+ }
+
if (!powerof2(alignment)) {
return EINVAL;
}
@@ -538,7 +564,12 @@ extern "C" int chk_posix_memalign(void** memptr, size_t alignment, size_t size)
return (*memptr != NULL) ? 0 : ENOMEM;
}
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
extern "C" void* chk_pvalloc(size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->pvalloc(bytes);
+ }
+
size_t pagesize = getpagesize();
size_t size = BIONIC_ALIGN(bytes, pagesize);
if (size < bytes) { // Overflow
@@ -548,10 +579,16 @@ extern "C" void* chk_pvalloc(size_t bytes) {
}
extern "C" void* chk_valloc(size_t size) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->valloc(size);
+ }
return chk_memalign(getpagesize(), size);
}
+#endif
static void ReportMemoryLeaks() {
+ ScopedDisableDebugCalls disable;
+
// Use /proc/self/exe link to obtain the program name for logging
// purposes. If it's not available, we set it to "<unknown>".
char exe[PATH_MAX];
@@ -585,10 +622,14 @@ static void ReportMemoryLeaks() {
}
}
+pthread_key_t g_debug_calls_disabled;
+
extern "C" bool malloc_debug_initialize(HashTable* hash_table, const MallocDebug* malloc_dispatch) {
g_hash_table = hash_table;
g_malloc_dispatch = malloc_dispatch;
+ pthread_key_create(&g_debug_calls_disabled, NULL);
+
char debug_backlog[PROP_VALUE_MAX];
if (__system_property_get("libc.debug.malloc.backlog", debug_backlog)) {
g_malloc_debug_backlog = atoi(debug_backlog);
@@ -605,4 +646,6 @@ extern "C" void malloc_debug_finalize(int malloc_debug_level) {
ReportMemoryLeaks();
}
backtrace_shutdown();
+
+ pthread_setspecific(g_debug_calls_disabled, NULL);
}
diff --git a/libc/bionic/malloc_debug_disable.h b/libc/bionic/malloc_debug_disable.h
new file mode 100644
index 0000000..9503128
--- /dev/null
+++ b/libc/bionic/malloc_debug_disable.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 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_DISABLE_H
+#define MALLOC_DEBUG_DISABLE_H
+
+#include <pthread.h>
+
+#include "private/bionic_macros.h"
+
+// =============================================================================
+// Used to disable the debug allocation calls.
+// =============================================================================
+extern pthread_key_t g_debug_calls_disabled;
+
+static inline bool DebugCallsDisabled() {
+ return pthread_getspecific(g_debug_calls_disabled) != NULL;
+}
+
+class ScopedDisableDebugCalls {
+ public:
+ ScopedDisableDebugCalls() : disabled_(DebugCallsDisabled()) {
+ if (!disabled_) {
+ pthread_setspecific(g_debug_calls_disabled, reinterpret_cast<const void*>(1));
+ }
+ }
+ ~ScopedDisableDebugCalls() {
+ if (!disabled_) {
+ pthread_setspecific(g_debug_calls_disabled, NULL);
+ }
+ }
+
+ private:
+ bool disabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisableDebugCalls);
+};
+
+#endif // MALLOC_DEBUG_DISABLE_H
diff --git a/libc/bionic/malloc_debug_leak.cpp b/libc/bionic/malloc_debug_leak.cpp
index d9824f0..7926a1f 100644
--- a/libc/bionic/malloc_debug_leak.cpp
+++ b/libc/bionic/malloc_debug_leak.cpp
@@ -48,6 +48,7 @@
#include "debug_stacktrace.h"
#include "malloc_debug_common.h"
+#include "malloc_debug_disable.h"
#include "private/bionic_macros.h"
#include "private/libc_logging.h"
@@ -267,6 +268,7 @@ extern "C" int fill_posix_memalign(void** memptr, size_t alignment, size_t size)
return (*memptr != NULL) ? 0 : ENOMEM;
}
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
extern "C" void* fill_pvalloc(size_t bytes) {
size_t pagesize = getpagesize();
size_t size = BIONIC_ALIGN(bytes, pagesize);
@@ -279,6 +281,7 @@ extern "C" void* fill_pvalloc(size_t bytes) {
extern "C" void* fill_valloc(size_t size) {
return fill_memalign(getpagesize(), size);
}
+#endif
// =============================================================================
// malloc leak functions
@@ -287,6 +290,10 @@ extern "C" void* fill_valloc(size_t size) {
static uint32_t MEMALIGN_GUARD = 0xA1A41520;
extern "C" void* leak_malloc(size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->malloc(bytes);
+ }
+
// allocate enough space infront of the allocation to store the pointer for
// the alloc structure. This will making free'ing the structer really fast!
@@ -319,6 +326,10 @@ extern "C" void* leak_malloc(size_t bytes) {
}
extern "C" void leak_free(void* mem) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->free(mem);
+ }
+
if (mem == NULL) {
return;
}
@@ -355,6 +366,10 @@ extern "C" void leak_free(void* mem) {
}
extern "C" void* leak_calloc(size_t n_elements, size_t elem_size) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->calloc(n_elements, elem_size);
+ }
+
// 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 && SIZE_MAX / n_elements < elem_size) {
@@ -370,6 +385,10 @@ extern "C" void* leak_calloc(size_t n_elements, size_t elem_size) {
}
extern "C" void* leak_realloc(void* oldMem, size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->realloc(oldMem, bytes);
+ }
+
if (oldMem == NULL) {
return leak_malloc(bytes);
}
@@ -398,6 +417,10 @@ extern "C" void* leak_realloc(void* oldMem, size_t bytes) {
}
extern "C" void* leak_memalign(size_t alignment, size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->memalign(alignment, bytes);
+ }
+
// we can just use malloc
if (alignment <= MALLOC_ALIGNMENT) {
return leak_malloc(bytes);
@@ -439,6 +462,10 @@ extern "C" void* leak_memalign(size_t alignment, size_t bytes) {
}
extern "C" size_t leak_malloc_usable_size(const void* mem) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->malloc_usable_size(mem);
+ }
+
if (mem != NULL) {
// Check the guard to make sure it is valid.
const AllocationEntry* header = const_to_header((void*)mem);
@@ -467,6 +494,10 @@ extern "C" struct mallinfo leak_mallinfo() {
}
extern "C" int leak_posix_memalign(void** memptr, size_t alignment, size_t size) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->posix_memalign(memptr, alignment, size);
+ }
+
if (!powerof2(alignment)) {
return EINVAL;
}
@@ -476,7 +507,12 @@ extern "C" int leak_posix_memalign(void** memptr, size_t alignment, size_t size)
return (*memptr != NULL) ? 0 : ENOMEM;
}
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
extern "C" void* leak_pvalloc(size_t bytes) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->pvalloc(bytes);
+ }
+
size_t pagesize = getpagesize();
size_t size = BIONIC_ALIGN(bytes, pagesize);
if (size < bytes) { // Overflow
@@ -486,5 +522,10 @@ extern "C" void* leak_pvalloc(size_t bytes) {
}
extern "C" void* leak_valloc(size_t size) {
+ if (DebugCallsDisabled()) {
+ return g_malloc_dispatch->valloc(size);
+ }
+
return leak_memalign(getpagesize(), size);
}
+#endif