Merge "Don't collect useless stack frames; do demangle C++ symbols."
diff --git a/libc/bionic/debug_mapinfo.cpp b/libc/bionic/debug_mapinfo.cpp
index 174cc28..c5b9aa7 100644
--- a/libc/bionic/debug_mapinfo.cpp
+++ b/libc/bionic/debug_mapinfo.cpp
@@ -57,7 +57,7 @@
   return mi;
 }
 
-__LIBC_HIDDEN__ mapinfo_t* mapinfo_create(int pid) {
+__LIBC_HIDDEN__ mapinfo_t* mapinfo_create(pid_t pid) {
   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);
@@ -76,7 +76,7 @@
 }
 
 __LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi) {
-  while (mi) {
+  while (mi != NULL) {
     mapinfo_t* del = mi;
     mi = mi->next;
     dlfree(del);
@@ -84,13 +84,13 @@
 }
 
 // Find the containing map info for the PC.
-__LIBC_HIDDEN__ const mapinfo_t* mapinfo_find(mapinfo_t* mi, unsigned pc, unsigned* rel_pc) {
-  *rel_pc = pc;
+__LIBC_HIDDEN__ const mapinfo_t* mapinfo_find(mapinfo_t* mi, uintptr_t pc, uintptr_t* rel_pc) {
   for (; mi != NULL; mi = mi->next) {
     if ((pc >= mi->start) && (pc < mi->end)) {
-      *rel_pc -= mi->start;
+      *rel_pc = pc - mi->start;
       return mi;
     }
   }
+  *rel_pc = pc;
   return NULL;
 }
diff --git a/libc/bionic/debug_mapinfo.h b/libc/bionic/debug_mapinfo.h
index 6df55c5..cccd2e3 100644
--- a/libc/bionic/debug_mapinfo.h
+++ b/libc/bionic/debug_mapinfo.h
@@ -38,8 +38,8 @@
   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__ 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 */
diff --git a/libc/bionic/debug_stacktrace.cpp b/libc/bionic/debug_stacktrace.cpp
index 5c82205..9d53ad2 100644
--- a/libc/bionic/debug_stacktrace.cpp
+++ b/libc/bionic/debug_stacktrace.cpp
@@ -44,69 +44,102 @@
 typedef _Unwind_Context __unwind_context;
 #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) {
   stack_crawl_state_t* state = static_cast<stack_crawl_state_t*>(arg);
-  if (state->count) {
-    uintptr_t ip = _Unwind_GetIP(context);
-    if (ip) {
-      state->addrs[0] = ip;
-      state->addrs++;
-      state->count--;
-      return _URC_NO_REASON;
-    }
+
+  uintptr_t ip = _Unwind_GetIP(context);
+
+  // The first stack frame is get_backtrace itself. Skip it.
+  if (ip != 0 && !state->have_skipped_self) {
+    state->have_skipped_self = true;
+    return _URC_NO_REASON;
   }
-  // If we run out of space to record the address or 0 has been seen, stop
-  // unwinding the stack.
-  return _URC_END_OF_STACK;
+
+  state->frames[state->frame_count++] = ip;
+  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) {
-  stack_crawl_state_t state;
-  state.count = max_entries;
-  state.addrs = addrs;
+__LIBC_HIDDEN__ int get_backtrace(uintptr_t* frames, size_t max_depth) {
+  stack_crawl_state_t state(frames, max_depth);
   _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];
-  if (addrs == NULL) {
-    c = get_backtrace(self_bt, 16);
-    addrs = self_bt;
+  if (frames == NULL) {
+    frame_count = get_backtrace(self_bt, 16);
+    frames = self_bt;
   }
 
   __libc_format_log(ANDROID_LOG_ERROR, "libc",
                     "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
 
-  int index = 0;
-  for (size_t i = 0 ; i < c; ++i) {
+  for (size_t i = 0 ; i < frame_count; ++i) {
     void* offset = 0;
     const char* symbol = NULL;
 
     Dl_info info;
-    if (dladdr((void*) addrs[i], &info) != 0) {
+    if (dladdr((void*) frames[i], &info) != 0) {
       offset = info.dli_saddr;
       symbol = info.dli_sname;
     }
 
-    // This test is a bit sketchy, but it allows us to skip the
-    // stack trace entries due to this debugging code. it works
-    // because those don't have a symbol (they're not exported).
-    if (symbol != NULL || index > 0) {
-      unsigned int rel_pc;
-      const mapinfo_t* mi = mapinfo_find(map_info, addrs[i], &rel_pc);
-      const char* soname = mi ? mi->name : info.dli_fname;
-      if (soname == NULL) {
-        soname = "unknown";
-      }
-      if (symbol) {
-        __libc_format_log(ANDROID_LOG_ERROR, "libc", "          #%02d  pc %08x  %s (%s+0x%x)",
-                          index, rel_pc, soname, symbol, addrs[i] - (uintptr_t) offset);
-      } else {
-        __libc_format_log(ANDROID_LOG_ERROR, "libc", "          #%02d  pc %08x  %s",
-                          index, rel_pc, soname);
-      }
-      ++index;
+    uintptr_t rel_pc;
+    const mapinfo_t* mi = (gMapInfo != NULL) ? mapinfo_find(gMapInfo, frames[i], &rel_pc) : NULL;
+    const char* soname = (mi != NULL) ? mi->name : info.dli_fname;
+    if (soname == NULL) {
+      soname = "<unknown>";
+    }
+    if (symbol != NULL) {
+      // TODO: we might need a flag to say whether it's safe to allocate (demangling allocates).
+      char* demangled_symbol = demangle(symbol);
+      const char* best_name = (demangled_symbol != NULL) ? demangled_symbol : symbol;
+
+      __libc_format_log(ANDROID_LOG_ERROR, "libc", "          #%02d  pc %08x  %s (%s+0x%x)",
+                        i, rel_pc, soname, best_name, frames[i] - (uintptr_t) offset);
+
+      free(demangled_symbol);
+    } else {
+      __libc_format_log(ANDROID_LOG_ERROR, "libc", "          #%02d  pc %08x  %s",
+                        i, rel_pc, soname);
     }
   }
 }
diff --git a/libc/bionic/debug_stacktrace.h b/libc/bionic/debug_stacktrace.h
index 800a172..2cf8636 100644
--- a/libc/bionic/debug_stacktrace.h
+++ b/libc/bionic/debug_stacktrace.h
@@ -32,14 +32,9 @@
 #include <stdint.h>
 #include <sys/cdefs.h>
 
-struct stack_crawl_state_t {
-  size_t count;
-  uintptr_t* addrs;
-};
-
-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);
+__LIBC_HIDDEN__ void backtrace_startup();
+__LIBC_HIDDEN__ void backtrace_shutdown();
+__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);
 
 #endif /* DEBUG_STACKTRACE_H */
diff --git a/libc/bionic/malloc_debug_check.cpp b/libc/bionic/malloc_debug_check.cpp
index d366b56..18c3ed4 100644
--- a/libc/bionic/malloc_debug_check.cpp
+++ b/libc/bionic/malloc_debug_check.cpp
@@ -52,8 +52,6 @@
 #include "malloc_debug_common.h"
 #include "ScopedPthreadMutexLocker.h"
 
-static mapinfo_t* gMapInfo;
-
 /* libc.debug.malloc.backlog */
 extern unsigned int malloc_double_free_backlog;
 
@@ -261,11 +259,11 @@
     if (!valid && *safe) {
         log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
                         user(hdr), hdr->size);
-        log_backtrace(gMapInfo, hdr->bt, hdr->bt_depth);
+        log_backtrace(hdr->bt, hdr->bt_depth);
         if (hdr->tag == BACKLOG_TAG) {
             log_message("+++ ALLOCATION %p SIZE %d FREED HERE:\n",
                        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 @@
 
     if (del(hdr) < 0) {
         uintptr_t bt[MAX_BACKTRACE_DEPTH];
-        int depth;
-        depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
+        int depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
         if (hdr->tag == BACKLOG_TAG) {
             log_message("+++ ALLOCATION %p SIZE %d BYTES MULTIPLY FREED!\n",
                        user(hdr), hdr->size);
             log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
                        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 */
             log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n",
                        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",
                        user(hdr), hdr->size);
-            log_backtrace(gMapInfo, bt, depth);
+            log_backtrace(bt, depth);
         } else {
             log_message("+++ ALLOCATION %p IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n",
                        user(hdr));
-            log_backtrace(gMapInfo, bt, depth);
+            log_backtrace(bt, depth);
         }
     } else {
-        hdr->freed_bt_depth = get_backtrace(hdr->freed_bt,
-                                      MAX_BACKTRACE_DEPTH);
+        hdr->freed_bt_depth = get_backtrace(hdr->freed_bt, MAX_BACKTRACE_DEPTH);
         add_to_backlog(hdr);
     }
 }
@@ -388,21 +384,20 @@
 
     if (del(hdr) < 0) {
         uintptr_t bt[MAX_BACKTRACE_DEPTH];
-        int depth;
-        depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
+        int depth = get_backtrace(bt, MAX_BACKTRACE_DEPTH);
         if (hdr->tag == BACKLOG_TAG) {
             log_message("+++ REALLOCATION %p SIZE %d OF FREED MEMORY!\n",
                        user(hdr), size, hdr->size);
             log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
                        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 */
             log_message("+++ ALLOCATION %p SIZE %d FIRST FREED HERE:\n",
                        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",
                        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
              * reallocation below succeeds.  Since we didn't really free it, we
@@ -412,7 +407,7 @@
         } else {
             log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n",
                        user(hdr), size);
-            log_backtrace(gMapInfo, bt, depth);
+            log_backtrace(bt, depth);
             // just get a whole new allocation and leak the old one
             return dlrealloc(0, size);
             // return dlrealloc(user(hdr), size); // assuming it was allocated externally
@@ -465,7 +460,7 @@
                 exe, block->size, user(block), index++, total);
     if (del_leak(block, &safe)) {
       /* 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 @@
   }
 }
 
-/* Initializes malloc debugging framework.
- * See comments on MallocDebugInit in malloc_debug_common.h
- */
 extern "C" int malloc_debug_initialize() {
   if (!malloc_double_free_backlog) {
     malloc_double_free_backlog = BACKLOG_DEFAULT_LEN;
   }
-  gMapInfo = mapinfo_create(getpid());
+  backtrace_startup();
   return 0;
 }
 
 extern "C" void malloc_debug_finalize() {
   heaptracker_free_leaked_memory();
-  mapinfo_destroy(gMapInfo);
+  backtrace_shutdown();
 }
diff --git a/libc/bionic/pthread_debug.cpp b/libc/bionic/pthread_debug.cpp
index ac81837..21eac63 100644
--- a/libc/bionic/pthread_debug.cpp
+++ b/libc/bionic/pthread_debug.cpp
@@ -120,8 +120,6 @@
 
 #define STACK_TRACE_DEPTH 16
 
-static mapinfo_t* gMapInfo;
-
 /****************************************************************************/
 
 /*
@@ -370,17 +368,14 @@
         /* Turn off prediction temporarily in this thread while logging */
         sPthreadDebugDisabledThread = gettid();
 
-        if (gMapInfo == NULL) {
-            // note: we're protected by sDbgLock.
-            gMapInfo = mapinfo_create(getpid());
-        }
+        backtrace_startup();
 
         LOGW("%s\n", kStartBanner);
         LOGW("pid: %d, tid: %d >>> %s <<<", getpid(), gettid(), __progname);
         LOGW("Illegal lock attempt:\n");
         LOGW("--- pthread_mutex_t at %p\n", obj->mutex);
         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):");
         MutexInfo* cur = obj;
@@ -391,7 +386,7 @@
             if (parent->owner == ourtid) {
                 LOGW("--- pthread_mutex_t at %p\n", parent->mutex);
                 if (sPthreadDebugLevel >= CAPTURE_CALLSTACK) {
-                    log_backtrace(gMapInfo, parent->stackTrace, parent->stackDepth);
+                    log_backtrace(parent->stackTrace, parent->stackDepth);
                 }
                 cur = parent;
                 break;
@@ -414,13 +409,9 @@
             if (sPthreadDebugLevel >= CAPTURE_CALLSTACK) {
                 int index = historyListHas(&obj->parents, objParent);
                 if ((size_t)index < (size_t)obj->stacks.count) {
-                    log_backtrace(gMapInfo,
-                                  obj->stacks.stack[index].addrs,
-                                  obj->stacks.stack[index].depth);
+                    log_backtrace(obj->stacks.stack[index].addrs, obj->stacks.stack[index].depth);
                 } else {
-                    log_backtrace(gMapInfo,
-                                  obj->stackTrace,
-                                  obj->stackDepth);
+                    log_backtrace(obj->stackTrace, obj->stackDepth);
                 }
             }
             result = 0;
@@ -465,8 +456,7 @@
 
     linkParentToChild(mrl, object);
     if (!traverseTree(object, mrl)) {
-        mapinfo_destroy(gMapInfo);
-        gMapInfo = NULL;
+        backtrace_shutdown();
         LOGW("%s\n", kEndBanner);
         unlinkParentFromChild(mrl, object);
         // reenable pthread debugging for this thread