From 861c0ef37bcfcae56d88572cb01c18bcfe1faded Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Thu, 24 Jul 2014 17:52:23 -0700 Subject: [PATCH] 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 --- libc/Android.mk | 8 ++-- libc/bionic/debug_mapinfo.cpp | 53 +++++++++++++++---------- libc/bionic/debug_mapinfo.h | 4 +- libc/bionic/debug_stacktrace.cpp | 13 +++++- libc/bionic/malloc_debug_check.cpp | 43 ++++++++++++++++++++ libc/bionic/malloc_debug_disable.h | 64 ++++++++++++++++++++++++++++++ libc/bionic/malloc_debug_leak.cpp | 41 +++++++++++++++++++ 7 files changed, 198 insertions(+), 28 deletions(-) create mode 100644 libc/bionic/malloc_debug_disable.h diff --git a/libc/Android.mk b/libc/Android.mk index 15a68b921..9554e1892 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 17276ce40..d83799afa 100644 --- a/libc/bionic/debug_mapinfo.cpp +++ b/libc/bionic/debug_mapinfo.cpp @@ -26,39 +26,48 @@ * SUCH DAMAGE. */ +#include +#include #include #include #include -#include #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//maps: +// 6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so static mapinfo_t* parse_maps_line(char* line) { - int len = strlen(line); + 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; + } - if (len < 1) return 0; - line[--len] = 0; - - if (len < 50) return 0; - if (line[20] != 'x') return 0; - - mapinfo_t* mi = static_cast( - mmap(NULL, sizeof(mapinfo_t) + (len - 47), PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0)); - if (mi == MAP_FAILED) return 0; - - 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(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 cccd2e374..926b37762 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 713e76113..b86e2afdc 100644 --- a/libc/bionic/debug_stacktrace.cpp +++ b/libc/bionic/debug_stacktrace.cpp @@ -35,6 +35,7 @@ #include #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 1c63d4dcf..94ba6f5ec 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 "". 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 000000000..9503128c4 --- /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 + +#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(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 d9824f09f..7926a1f27 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