diff options
-rw-r--r-- | libc/Android.mk | 8 | ||||
-rw-r--r-- | libc/bionic/debug_mapinfo.cpp | 53 | ||||
-rw-r--r-- | libc/bionic/debug_mapinfo.h | 4 | ||||
-rw-r--r-- | libc/bionic/debug_stacktrace.cpp | 13 | ||||
-rw-r--r-- | libc/bionic/malloc_debug_check.cpp | 43 | ||||
-rw-r--r-- | libc/bionic/malloc_debug_disable.h | 64 | ||||
-rw-r--r-- | libc/bionic/malloc_debug_leak.cpp | 41 |
7 files changed, 198 insertions, 28 deletions
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 <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 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 <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 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 "<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 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 <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 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 |