am f5d6238c: am 3f20ecc2: am 3db2fc5a: Merge "Don\'t collect useless stack frames; do demangle C++ symbols."

# Via Android Git Automerger (2) and others
* commit 'f5d6238c4b6433f193c6d06c1dd89110b497e449':
  Don't collect useless stack frames; do demangle C++ symbols.
This commit is contained in:
Elliott Hughes 2013-01-29 12:09:31 -08:00 committed by Android Git Automerger
commit 3638641014
6 changed files with 107 additions and 97 deletions

View File

@ -57,7 +57,7 @@ static mapinfo_t* parse_maps_line(char* line) {
return mi; return mi;
} }
__LIBC_HIDDEN__ mapinfo_t* mapinfo_create(int pid) { __LIBC_HIDDEN__ mapinfo_t* mapinfo_create(pid_t pid) {
struct mapinfo_t* milist = NULL; struct mapinfo_t* milist = NULL;
char data[1024]; // Used to read lines as well as to construct the filename. char data[1024]; // Used to read lines as well as to construct the filename.
snprintf(data, sizeof(data), "/proc/%d/maps", pid); snprintf(data, sizeof(data), "/proc/%d/maps", pid);
@ -76,7 +76,7 @@ __LIBC_HIDDEN__ mapinfo_t* mapinfo_create(int pid) {
} }
__LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi) { __LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi) {
while (mi) { while (mi != NULL) {
mapinfo_t* del = mi; mapinfo_t* del = mi;
mi = mi->next; mi = mi->next;
dlfree(del); dlfree(del);
@ -84,13 +84,13 @@ __LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi) {
} }
// Find the containing map info for the PC. // Find the containing map info for the PC.
__LIBC_HIDDEN__ const mapinfo_t* mapinfo_find(mapinfo_t* mi, unsigned pc, unsigned* rel_pc) { __LIBC_HIDDEN__ const mapinfo_t* mapinfo_find(mapinfo_t* mi, uintptr_t pc, uintptr_t* rel_pc) {
*rel_pc = pc;
for (; mi != NULL; mi = mi->next) { for (; mi != NULL; mi = mi->next) {
if ((pc >= mi->start) && (pc < mi->end)) { if ((pc >= mi->start) && (pc < mi->end)) {
*rel_pc -= mi->start; *rel_pc = pc - mi->start;
return mi; return mi;
} }
} }
*rel_pc = pc;
return NULL; return NULL;
} }

View File

@ -38,8 +38,8 @@ struct mapinfo_t {
char name[]; char name[];
}; };
__LIBC_HIDDEN__ mapinfo_t* mapinfo_create(int pid); __LIBC_HIDDEN__ mapinfo_t* mapinfo_create(pid_t pid);
__LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi); __LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi);
__LIBC_HIDDEN__ const mapinfo_t* mapinfo_find(mapinfo_t* mi, unsigned pc, unsigned* rel_pc); __LIBC_HIDDEN__ const mapinfo_t* mapinfo_find(mapinfo_t* mi, uintptr_t pc, uintptr_t* rel_pc);
#endif /* DEBUG_MAPINFO_H */ #endif /* DEBUG_MAPINFO_H */

View File

@ -44,69 +44,102 @@ typedef struct _Unwind_Context __unwind_context;
typedef _Unwind_Context __unwind_context; typedef _Unwind_Context __unwind_context;
#endif #endif
static mapinfo_t* gMapInfo = NULL;
static void* gDemangler;
typedef char* (*DemanglerFn)(const char*, char*, size_t*, int*);
static DemanglerFn gDemanglerFn = NULL;
__LIBC_HIDDEN__ void backtrace_startup() {
gMapInfo = mapinfo_create(getpid());
gDemangler = dlopen("libgccdemangle.so", RTLD_NOW);
if (gDemangler != NULL) {
void* sym = dlsym(gDemangler, "__cxa_demangle");
gDemanglerFn = reinterpret_cast<DemanglerFn>(sym);
}
}
__LIBC_HIDDEN__ void backtrace_shutdown() {
mapinfo_destroy(gMapInfo);
dlclose(gDemangler);
}
static char* demangle(const char* symbol) {
if (gDemanglerFn == NULL) {
return NULL;
}
return (*gDemanglerFn)(symbol, NULL, NULL, NULL);
}
struct stack_crawl_state_t {
uintptr_t* frames;
size_t frame_count;
size_t max_depth;
bool have_skipped_self;
stack_crawl_state_t(uintptr_t* frames, size_t max_depth)
: frames(frames), frame_count(0), max_depth(max_depth), have_skipped_self(false) {
}
};
static _Unwind_Reason_Code trace_function(__unwind_context* context, void* arg) { static _Unwind_Reason_Code trace_function(__unwind_context* context, void* arg) {
stack_crawl_state_t* state = static_cast<stack_crawl_state_t*>(arg); stack_crawl_state_t* state = static_cast<stack_crawl_state_t*>(arg);
if (state->count) {
uintptr_t ip = _Unwind_GetIP(context); uintptr_t ip = _Unwind_GetIP(context);
if (ip) {
state->addrs[0] = ip; // The first stack frame is get_backtrace itself. Skip it.
state->addrs++; if (ip != 0 && !state->have_skipped_self) {
state->count--; state->have_skipped_self = true;
return _URC_NO_REASON; return _URC_NO_REASON;
}
} }
// If we run out of space to record the address or 0 has been seen, stop
// unwinding the stack. state->frames[state->frame_count++] = ip;
return _URC_END_OF_STACK; return (state->frame_count >= state->max_depth) ? _URC_END_OF_STACK : _URC_NO_REASON;
} }
__LIBC_HIDDEN__ int get_backtrace(uintptr_t* addrs, size_t max_entries) { __LIBC_HIDDEN__ int get_backtrace(uintptr_t* frames, size_t max_depth) {
stack_crawl_state_t state; stack_crawl_state_t state(frames, max_depth);
state.count = max_entries;
state.addrs = addrs;
_Unwind_Backtrace(trace_function, &state); _Unwind_Backtrace(trace_function, &state);
return max_entries - state.count; return state.frame_count;
} }
__LIBC_HIDDEN__ void log_backtrace(mapinfo_t* map_info, uintptr_t* addrs, size_t c) { __LIBC_HIDDEN__ void log_backtrace(uintptr_t* frames, size_t frame_count) {
uintptr_t self_bt[16]; uintptr_t self_bt[16];
if (addrs == NULL) { if (frames == NULL) {
c = get_backtrace(self_bt, 16); frame_count = get_backtrace(self_bt, 16);
addrs = self_bt; frames = self_bt;
} }
__libc_format_log(ANDROID_LOG_ERROR, "libc", __libc_format_log(ANDROID_LOG_ERROR, "libc",
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"); "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
int index = 0; for (size_t i = 0 ; i < frame_count; ++i) {
for (size_t i = 0 ; i < c; ++i) {
void* offset = 0; void* offset = 0;
const char* symbol = NULL; const char* symbol = NULL;
Dl_info info; Dl_info info;
if (dladdr((void*) addrs[i], &info) != 0) { if (dladdr((void*) frames[i], &info) != 0) {
offset = info.dli_saddr; offset = info.dli_saddr;
symbol = info.dli_sname; symbol = info.dli_sname;
} }
// This test is a bit sketchy, but it allows us to skip the uintptr_t rel_pc;
// stack trace entries due to this debugging code. it works const mapinfo_t* mi = (gMapInfo != NULL) ? mapinfo_find(gMapInfo, frames[i], &rel_pc) : NULL;
// because those don't have a symbol (they're not exported). const char* soname = (mi != NULL) ? mi->name : info.dli_fname;
if (symbol != NULL || index > 0) { if (soname == NULL) {
unsigned int rel_pc; soname = "<unknown>";
const mapinfo_t* mi = mapinfo_find(map_info, addrs[i], &rel_pc); }
const char* soname = mi ? mi->name : info.dli_fname; if (symbol != NULL) {
if (soname == NULL) { // TODO: we might need a flag to say whether it's safe to allocate (demangling allocates).
soname = "unknown"; char* demangled_symbol = demangle(symbol);
} const char* best_name = (demangled_symbol != NULL) ? demangled_symbol : symbol;
if (symbol) {
__libc_format_log(ANDROID_LOG_ERROR, "libc", " #%02d pc %08x %s (%s+0x%x)", __libc_format_log(ANDROID_LOG_ERROR, "libc", " #%02d pc %08x %s (%s+0x%x)",
index, rel_pc, soname, symbol, addrs[i] - (uintptr_t) offset); i, rel_pc, soname, best_name, frames[i] - (uintptr_t) offset);
} else {
__libc_format_log(ANDROID_LOG_ERROR, "libc", " #%02d pc %08x %s", free(demangled_symbol);
index, rel_pc, soname); } else {
} __libc_format_log(ANDROID_LOG_ERROR, "libc", " #%02d pc %08x %s",
++index; i, rel_pc, soname);
} }
} }
} }

View File

@ -32,14 +32,9 @@
#include <stdint.h> #include <stdint.h>
#include <sys/cdefs.h> #include <sys/cdefs.h>
struct stack_crawl_state_t { __LIBC_HIDDEN__ void backtrace_startup();
size_t count; __LIBC_HIDDEN__ void backtrace_shutdown();
uintptr_t* addrs; __LIBC_HIDDEN__ int get_backtrace(uintptr_t* stack_frames, size_t max_depth);
}; __LIBC_HIDDEN__ void log_backtrace(uintptr_t* stack_frames, size_t frame_count);
struct mapinfo_t;
__LIBC_HIDDEN__ int get_backtrace(uintptr_t* stack_frames, size_t max_entries);
__LIBC_HIDDEN__ void log_backtrace(mapinfo_t* map_info, uintptr_t* stack_frames, size_t frame_count);
#endif /* DEBUG_STACKTRACE_H */ #endif /* DEBUG_STACKTRACE_H */

View File

@ -52,8 +52,6 @@
#include "malloc_debug_common.h" #include "malloc_debug_common.h"
#include "ScopedPthreadMutexLocker.h" #include "ScopedPthreadMutexLocker.h"
static mapinfo_t* gMapInfo;
/* libc.debug.malloc.backlog */ /* libc.debug.malloc.backlog */
extern unsigned int malloc_double_free_backlog; extern unsigned int malloc_double_free_backlog;
@ -261,11 +259,11 @@ static inline int check_allocation_locked(hdr_t *hdr, int *safe) {
if (!valid && *safe) { if (!valid && *safe) {
log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
user(hdr), hdr->size); user(hdr), hdr->size);
log_backtrace(gMapInfo, hdr->bt, hdr->bt_depth); log_backtrace(hdr->bt, hdr->bt_depth);
if (hdr->tag == BACKLOG_TAG) { if (hdr->tag == BACKLOG_TAG) {
log_message("+++ ALLOCATION %p SIZE %d FREED HERE:\n", log_message("+++ ALLOCATION %p SIZE %d FREED HERE:\n",
user(hdr), hdr->size); user(hdr), hdr->size);
log_backtrace(gMapInfo, hdr->freed_bt, hdr->freed_bt_depth); log_backtrace(hdr->freed_bt, hdr->freed_bt_depth);
} }
} }
@ -343,29 +341,27 @@ extern "C" void chk_free(void *ptr) {
if (del(hdr) < 0) { if (del(hdr) < 0) {
uintptr_t bt[MAX_BACKTRACE_DEPTH]; uintptr_t bt[MAX_BACKTRACE_DEPTH];
int depth; int depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
if (hdr->tag == BACKLOG_TAG) { if (hdr->tag == BACKLOG_TAG) {
log_message("+++ ALLOCATION %p SIZE %d BYTES MULTIPLY FREED!\n", log_message("+++ ALLOCATION %p SIZE %d BYTES MULTIPLY FREED!\n",
user(hdr), hdr->size); user(hdr), hdr->size);
log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
user(hdr), hdr->size); user(hdr), hdr->size);
log_backtrace(gMapInfo, hdr->bt, hdr->bt_depth); log_backtrace(hdr->bt, hdr->bt_depth);
/* hdr->freed_bt_depth should be nonzero here */ /* hdr->freed_bt_depth should be nonzero here */
log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n", log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n",
user(hdr), hdr->size); user(hdr), hdr->size);
log_backtrace(gMapInfo, hdr->freed_bt, hdr->freed_bt_depth); log_backtrace(hdr->freed_bt, hdr->freed_bt_depth);
log_message("+++ ALLOCATION %p SIZE %d NOW BEING FREED HERE:\n", log_message("+++ ALLOCATION %p SIZE %d NOW BEING FREED HERE:\n",
user(hdr), hdr->size); user(hdr), hdr->size);
log_backtrace(gMapInfo, bt, depth); log_backtrace(bt, depth);
} else { } else {
log_message("+++ ALLOCATION %p IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", log_message("+++ ALLOCATION %p IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n",
user(hdr)); user(hdr));
log_backtrace(gMapInfo, bt, depth); log_backtrace(bt, depth);
} }
} else { } else {
hdr->freed_bt_depth = get_backtrace(hdr->freed_bt, hdr->freed_bt_depth = get_backtrace(hdr->freed_bt, MAX_BACKTRACE_DEPTH);
MAX_BACKTRACE_DEPTH);
add_to_backlog(hdr); add_to_backlog(hdr);
} }
} }
@ -388,21 +384,20 @@ extern "C" void *chk_realloc(void *ptr, size_t size) {
if (del(hdr) < 0) { if (del(hdr) < 0) {
uintptr_t bt[MAX_BACKTRACE_DEPTH]; uintptr_t bt[MAX_BACKTRACE_DEPTH];
int depth; int depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
if (hdr->tag == BACKLOG_TAG) { if (hdr->tag == BACKLOG_TAG) {
log_message("+++ REALLOCATION %p SIZE %d OF FREED MEMORY!\n", log_message("+++ REALLOCATION %p SIZE %d OF FREED MEMORY!\n",
user(hdr), size, hdr->size); user(hdr), size, hdr->size);
log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n", log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
user(hdr), hdr->size); user(hdr), hdr->size);
log_backtrace(gMapInfo, hdr->bt, hdr->bt_depth); log_backtrace(hdr->bt, hdr->bt_depth);
/* hdr->freed_bt_depth should be nonzero here */ /* hdr->freed_bt_depth should be nonzero here */
log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n", log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n",
user(hdr), hdr->size); user(hdr), hdr->size);
log_backtrace(gMapInfo, hdr->freed_bt, hdr->freed_bt_depth); log_backtrace(hdr->freed_bt, hdr->freed_bt_depth);
log_message("+++ ALLOCATION %p SIZE %d NOW BEING REALLOCATED HERE:\n", log_message("+++ ALLOCATION %p SIZE %d NOW BEING REALLOCATED HERE:\n",
user(hdr), hdr->size); user(hdr), hdr->size);
log_backtrace(gMapInfo, bt, depth); log_backtrace(bt, depth);
/* We take the memory out of the backlog and fall through so the /* We take the memory out of the backlog and fall through so the
* reallocation below succeeds. Since we didn't really free it, we * reallocation below succeeds. Since we didn't really free it, we
@ -412,7 +407,7 @@ extern "C" void *chk_realloc(void *ptr, size_t size) {
} else { } else {
log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n", log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n",
user(hdr), size); user(hdr), size);
log_backtrace(gMapInfo, bt, depth); log_backtrace(bt, depth);
// just get a whole new allocation and leak the old one // just get a whole new allocation and leak the old one
return dlrealloc(0, size); return dlrealloc(0, size);
// return dlrealloc(user(hdr), size); // assuming it was allocated externally // return dlrealloc(user(hdr), size); // assuming it was allocated externally
@ -465,7 +460,7 @@ static void heaptracker_free_leaked_memory() {
exe, block->size, user(block), index++, total); exe, block->size, user(block), index++, total);
if (del_leak(block, &safe)) { if (del_leak(block, &safe)) {
/* safe == 1, because the allocation is valid */ /* safe == 1, because the allocation is valid */
log_backtrace(gMapInfo, block->bt, block->bt_depth); log_backtrace(block->bt, block->bt_depth);
} }
} }
@ -474,18 +469,15 @@ static void heaptracker_free_leaked_memory() {
} }
} }
/* Initializes malloc debugging framework.
* See comments on MallocDebugInit in malloc_debug_common.h
*/
extern "C" int malloc_debug_initialize() { extern "C" int malloc_debug_initialize() {
if (!malloc_double_free_backlog) { if (!malloc_double_free_backlog) {
malloc_double_free_backlog = BACKLOG_DEFAULT_LEN; malloc_double_free_backlog = BACKLOG_DEFAULT_LEN;
} }
gMapInfo = mapinfo_create(getpid()); backtrace_startup();
return 0; return 0;
} }
extern "C" void malloc_debug_finalize() { extern "C" void malloc_debug_finalize() {
heaptracker_free_leaked_memory(); heaptracker_free_leaked_memory();
mapinfo_destroy(gMapInfo); backtrace_shutdown();
} }

View File

@ -120,8 +120,6 @@ extern char* __progname;
#define STACK_TRACE_DEPTH 16 #define STACK_TRACE_DEPTH 16
static mapinfo_t* gMapInfo;
/****************************************************************************/ /****************************************************************************/
/* /*
@ -370,17 +368,14 @@ static int traverseTree(MutexInfo* obj, MutexInfo const* objParent)
/* Turn off prediction temporarily in this thread while logging */ /* Turn off prediction temporarily in this thread while logging */
sPthreadDebugDisabledThread = gettid(); sPthreadDebugDisabledThread = gettid();
if (gMapInfo == NULL) { backtrace_startup();
// note: we're protected by sDbgLock.
gMapInfo = mapinfo_create(getpid());
}
LOGW("%s\n", kStartBanner); LOGW("%s\n", kStartBanner);
LOGW("pid: %d, tid: %d >>> %s <<<", getpid(), gettid(), __progname); LOGW("pid: %d, tid: %d >>> %s <<<", getpid(), gettid(), __progname);
LOGW("Illegal lock attempt:\n"); LOGW("Illegal lock attempt:\n");
LOGW("--- pthread_mutex_t at %p\n", obj->mutex); LOGW("--- pthread_mutex_t at %p\n", obj->mutex);
stackDepth = get_backtrace(addrs, STACK_TRACE_DEPTH); stackDepth = get_backtrace(addrs, STACK_TRACE_DEPTH);
log_backtrace(gMapInfo, addrs, stackDepth); log_backtrace(addrs, stackDepth);
LOGW("+++ Currently held locks in this thread (in reverse order):"); LOGW("+++ Currently held locks in this thread (in reverse order):");
MutexInfo* cur = obj; MutexInfo* cur = obj;
@ -391,7 +386,7 @@ static int traverseTree(MutexInfo* obj, MutexInfo const* objParent)
if (parent->owner == ourtid) { if (parent->owner == ourtid) {
LOGW("--- pthread_mutex_t at %p\n", parent->mutex); LOGW("--- pthread_mutex_t at %p\n", parent->mutex);
if (sPthreadDebugLevel >= CAPTURE_CALLSTACK) { if (sPthreadDebugLevel >= CAPTURE_CALLSTACK) {
log_backtrace(gMapInfo, parent->stackTrace, parent->stackDepth); log_backtrace(parent->stackTrace, parent->stackDepth);
} }
cur = parent; cur = parent;
break; break;
@ -414,13 +409,9 @@ static int traverseTree(MutexInfo* obj, MutexInfo const* objParent)
if (sPthreadDebugLevel >= CAPTURE_CALLSTACK) { if (sPthreadDebugLevel >= CAPTURE_CALLSTACK) {
int index = historyListHas(&obj->parents, objParent); int index = historyListHas(&obj->parents, objParent);
if ((size_t)index < (size_t)obj->stacks.count) { if ((size_t)index < (size_t)obj->stacks.count) {
log_backtrace(gMapInfo, log_backtrace(obj->stacks.stack[index].addrs, obj->stacks.stack[index].depth);
obj->stacks.stack[index].addrs,
obj->stacks.stack[index].depth);
} else { } else {
log_backtrace(gMapInfo, log_backtrace(obj->stackTrace, obj->stackDepth);
obj->stackTrace,
obj->stackDepth);
} }
} }
result = 0; result = 0;
@ -465,8 +456,7 @@ static void mutex_lock_checked(MutexInfo* mrl, MutexInfo* object)
linkParentToChild(mrl, object); linkParentToChild(mrl, object);
if (!traverseTree(object, mrl)) { if (!traverseTree(object, mrl)) {
mapinfo_destroy(gMapInfo); backtrace_shutdown();
gMapInfo = NULL;
LOGW("%s\n", kEndBanner); LOGW("%s\n", kEndBanner);
unlinkParentFromChild(mrl, object); unlinkParentFromChild(mrl, object);
// reenable pthread debugging for this thread // reenable pthread debugging for this thread