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

(cherry picked from commit 861c0ef37b)

Change-Id: I04445f0cf9a1e85172b64d57df92eb7939ce2332
This commit is contained in:
Christopher Ferris 2014-07-24 17:52:23 -07:00
parent 1242f7eb67
commit c701e5b335
7 changed files with 198 additions and 28 deletions

View File

@ -979,8 +979,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 \
@ -1046,7 +1044,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 \
@ -1061,6 +1062,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