Merge "Use libunwindbacktrace for debug malloc code."

This commit is contained in:
Christopher Ferris 2014-07-30 02:16:16 +00:00 committed by Gerrit Code Review
commit 4ad5066e1d
7 changed files with 198 additions and 28 deletions

View File

@ -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

View File

@ -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);
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<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;
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);
}
}

View File

@ -33,8 +33,8 @@
struct mapinfo_t {
struct mapinfo_t* next;
unsigned start;
unsigned end;
uintptr_t start;
uintptr_t end;
char name[];
};

View File

@ -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) {

View File

@ -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);
}

View File

@ -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

View File

@ -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