Add a way to disable backtracing in malloc debug.

The property libc.debug.malloc.nobacktrace set to non-zero disables
getting backtracing when using mode 1 or mode 10.

Bug: 16874447

(cherry picked from 49de01a5be7bfb07baaea7415647d838383e1b59)

Change-Id: I6bbefe5420b14991fe84c2f849222dcd7cb592bf
diff --git a/libc/bionic/malloc_debug_backtrace.h b/libc/bionic/malloc_debug_backtrace.h
new file mode 100644
index 0000000..774548b
--- /dev/null
+++ b/libc/bionic/malloc_debug_backtrace.h
@@ -0,0 +1,37 @@
+/*
+ * 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_BACKTRACE_H
+#define MALLOC_DEBUG_BACKTRACE_H
+
+extern bool g_backtrace_enabled;
+
+#define GET_BACKTRACE(bt, depth) \
+  (g_backtrace_enabled ? get_backtrace(bt, depth) : 0)
+
+#endif  // MALLOC_DEBUG_BACKTRACE_H
diff --git a/libc/bionic/malloc_debug_check.cpp b/libc/bionic/malloc_debug_check.cpp
index 94ba6f5..dee03fa 100644
--- a/libc/bionic/malloc_debug_check.cpp
+++ b/libc/bionic/malloc_debug_check.cpp
@@ -48,6 +48,7 @@
 
 #include "debug_mapinfo.h"
 #include "debug_stacktrace.h"
+#include "malloc_debug_backtrace.h"
 #include "malloc_debug_common.h"
 #include "malloc_debug_disable.h"
 #include "private/bionic_macros.h"
@@ -123,6 +124,10 @@
 // It determines the size of the backlog we use to detect multiple frees.
 static unsigned g_malloc_debug_backlog = 100;
 
+// This variable is set to false if the property libc.debug.malloc.nobacktrace
+// is set to non-zero.
+__LIBC_HIDDEN__ bool g_backtrace_enabled = true;
+
 __LIBC_HIDDEN__ HashTable* g_hash_table;
 __LIBC_HIDDEN__ const MallocDebug* g_malloc_dispatch;
 
@@ -273,7 +278,7 @@
         valid = check_guards(hdr, safe);
     }
 
-    if (!valid && *safe) {
+    if (!valid && *safe && g_backtrace_enabled) {
         log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
                         user(hdr), hdr->size);
         log_backtrace(hdr->bt, hdr->bt_depth);
@@ -344,7 +349,7 @@
     hdr_t* hdr = static_cast<hdr_t*>(g_malloc_dispatch->malloc(size));
     if (hdr) {
         hdr->base = hdr;
-        hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
+        hdr->bt_depth = GET_BACKTRACE(hdr->bt, MAX_BACKTRACE_DEPTH);
         add(hdr, bytes);
         return user(hdr);
     }
@@ -385,7 +390,7 @@
 
         hdr_t* hdr = meta(reinterpret_cast<void*>(ptr));
         hdr->base = base;
-        hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
+        hdr->bt_depth = GET_BACKTRACE(hdr->bt, MAX_BACKTRACE_DEPTH);
         add(hdr, bytes);
         return user(hdr);
     }
@@ -405,27 +410,31 @@
 
     if (del(hdr) < 0) {
         uintptr_t bt[MAX_BACKTRACE_DEPTH];
-        int 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(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(hdr->freed_bt, hdr->freed_bt_depth);
-            log_message("+++ ALLOCATION %p SIZE %d NOW BEING FREED HERE:\n",
-                       user(hdr), hdr->size);
-            log_backtrace(bt, depth);
+            if (g_backtrace_enabled) {
+                log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
+                          user(hdr), hdr->size);
+                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(hdr->freed_bt, hdr->freed_bt_depth);
+                log_message("+++ ALLOCATION %p SIZE %d NOW BEING FREED HERE:\n",
+                          user(hdr), hdr->size);
+                log_backtrace(bt, depth);
+            }
         } else {
             log_message("+++ ALLOCATION %p IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n",
                        user(hdr));
-            log_backtrace(bt, depth);
+            if (g_backtrace_enabled) {
+                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);
     }
 }
@@ -451,22 +460,24 @@
 
     if (del(hdr) < 0) {
         uintptr_t bt[MAX_BACKTRACE_DEPTH];
-        int 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), bytes, hdr->size);
-            log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
-                       user(hdr), hdr->size);
-            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(hdr->freed_bt, hdr->freed_bt_depth);
-            log_message("+++ ALLOCATION %p SIZE %d NOW BEING REALLOCATED HERE:\n",
-                       user(hdr), hdr->size);
-            log_backtrace(bt, depth);
+            if (g_backtrace_enabled) {
+                log_message("+++ ALLOCATION %p SIZE %d ALLOCATED HERE:\n",
+                          user(hdr), hdr->size);
+                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(hdr->freed_bt, hdr->freed_bt_depth);
+                log_message("+++ ALLOCATION %p SIZE %d NOW BEING REALLOCATED HERE:\n",
+                          user(hdr), hdr->size);
+                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
              * can default to this behavior.
              */
@@ -474,7 +485,9 @@
         } else {
             log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n",
                        user(hdr), bytes);
-            log_backtrace(bt, depth);
+            if (g_backtrace_enabled) {
+                log_backtrace(bt, depth);
+            }
             // just get a whole new allocation and leak the old one
             return g_malloc_dispatch->realloc(0, bytes);
             // return realloc(user(hdr), bytes); // assuming it was allocated externally
@@ -501,7 +514,7 @@
     }
     if (hdr) {
         hdr->base = hdr;
-        hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
+        hdr->bt_depth = GET_BACKTRACE(hdr->bt, MAX_BACKTRACE_DEPTH);
         add(hdr, bytes);
         return user(hdr);
     }
@@ -523,7 +536,7 @@
     hdr_t* hdr = static_cast<hdr_t*>(g_malloc_dispatch->calloc(1, size));
     if (hdr) {
         hdr->base = hdr;
-        hdr->bt_depth = get_backtrace(hdr->bt, MAX_BACKTRACE_DEPTH);
+        hdr->bt_depth = GET_BACKTRACE(hdr->bt, MAX_BACKTRACE_DEPTH);
         add(hdr, total_bytes);
         return user(hdr);
     }
@@ -611,7 +624,7 @@
     hdr_t* block = head;
     log_message("+++ %s leaked block of size %d at %p (leak %d of %d)",
                 exe, block->size, user(block), index++, total);
-    if (del_leak(block, &safe)) {
+    if (del_leak(block, &safe) && g_backtrace_enabled) {
       /* safe == 1, because the allocation is valid */
       log_backtrace(block->bt, block->bt_depth);
     }
@@ -636,7 +649,17 @@
     info_log("%s: setting backlog length to %d\n", getprogname(), g_malloc_debug_backlog);
   }
 
-  backtrace_startup();
+  // Check if backtracing should be disabled.
+  char env[PROP_VALUE_MAX];
+  if (__system_property_get("libc.debug.malloc.nobacktrace", env) && atoi(env) != 0) {
+    g_backtrace_enabled = false;
+    __libc_format_log(ANDROID_LOG_INFO, "libc", "not gathering backtrace information\n");
+  }
+
+  if (g_backtrace_enabled) {
+    backtrace_startup();
+  }
+
   return true;
 }
 
@@ -645,7 +668,9 @@
   if (malloc_debug_level == 10) {
     ReportMemoryLeaks();
   }
-  backtrace_shutdown();
+  if (g_backtrace_enabled) {
+    backtrace_shutdown();
+  }
 
   pthread_setspecific(g_debug_calls_disabled, NULL);
 }
diff --git a/libc/bionic/malloc_debug_leak.cpp b/libc/bionic/malloc_debug_leak.cpp
index 7926a1f..837dccc 100644
--- a/libc/bionic/malloc_debug_leak.cpp
+++ b/libc/bionic/malloc_debug_leak.cpp
@@ -47,6 +47,7 @@
 #include <unwind.h>
 
 #include "debug_stacktrace.h"
+#include "malloc_debug_backtrace.h"
 #include "malloc_debug_common.h"
 #include "malloc_debug_disable.h"
 
@@ -311,7 +312,7 @@
         ScopedPthreadMutexLocker locker(&g_hash_table->lock);
 
         uintptr_t backtrace[BACKTRACE_SIZE];
-        size_t numEntries = get_backtrace(backtrace, BACKTRACE_SIZE);
+        size_t numEntries = GET_BACKTRACE(backtrace, BACKTRACE_SIZE);
 
         AllocationEntry* header = reinterpret_cast<AllocationEntry*>(base);
         header->entry = record_backtrace(backtrace, numEntries, bytes);