Provide method to dump backtrace heap data.

For non-zygote spawned processes, we might want to dump the backtrace
data. Provide a method to send a signal to a process and then dump the
data to a file.

Adds a method to dump the backtrace data on exit.

Update documentation and explain format of heap dump data.

Test: Ran unit tests, enabled new options and used them.
Change-Id: Ie2fa706694160731afe02c1382b037d06df1d069
diff --git a/libc/malloc_debug/BacktraceData.cpp b/libc/malloc_debug/BacktraceData.cpp
index 752d507..65ae6fa 100644
--- a/libc/malloc_debug/BacktraceData.cpp
+++ b/libc/malloc_debug/BacktraceData.cpp
@@ -41,12 +41,12 @@
 #include "debug_log.h"
 #include "malloc_debug.h"
 
-static void EnableToggle(int, siginfo_t*, void*) {
-  if (g_debug->backtrace->enabled()) {
-    g_debug->backtrace->set_enabled(false);
-  } else {
-    g_debug->backtrace->set_enabled(true);
-  }
+static void ToggleBacktraceEnable(int, siginfo_t*, void*) {
+  g_debug->backtrace->ToggleBacktraceEnabled();
+}
+
+static void EnableDump(int, siginfo_t*, void*) {
+  g_debug->backtrace->EnableDumping();
 }
 
 BacktraceData::BacktraceData(DebugData* debug_data, const Config& config, size_t* offset)
@@ -62,7 +62,7 @@
     struct sigaction enable_act;
     memset(&enable_act, 0, sizeof(enable_act));
 
-    enable_act.sa_sigaction = EnableToggle;
+    enable_act.sa_sigaction = ToggleBacktraceEnable;
     enable_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
     sigemptyset(&enable_act.sa_mask);
     if (sigaction(config.backtrace_signal(), &enable_act, nullptr) != 0) {
@@ -72,5 +72,20 @@
     info_log("%s: Run: 'kill -%d %d' to enable backtracing.", getprogname(),
              config.backtrace_signal(), getpid());
   }
+
+  struct sigaction act;
+  memset(&act, 0, sizeof(act));
+
+  act.sa_sigaction = EnableDump;
+  act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+  sigemptyset(&act.sa_mask);
+  if (sigaction(config.backtrace_dump_signal(), &act, nullptr) != 0) {
+    error_log("Unable to set up backtrace dump signal function: %s", strerror(errno));
+    return false;
+  }
+  info_log("%s: Run: 'kill -%d %d' to dump the backtrace.", getprogname(),
+           config.backtrace_dump_signal(), getpid());
+
+  dump_ = false;
   return true;
 }
diff --git a/libc/malloc_debug/BacktraceData.h b/libc/malloc_debug/BacktraceData.h
index 6dee505..c8234dc 100644
--- a/libc/malloc_debug/BacktraceData.h
+++ b/libc/malloc_debug/BacktraceData.h
@@ -31,6 +31,8 @@
 
 #include <stdint.h>
 
+#include <atomic>
+
 #include <private/bionic_macros.h>
 
 #include "OptionData.h"
@@ -47,13 +49,21 @@
 
   inline size_t alloc_offset() { return alloc_offset_; }
 
-  bool enabled() { return enabled_; }
-  void set_enabled(bool enabled) { enabled_ = enabled; }
+  bool ShouldBacktrace() { return enabled_ == 1; }
+  void ToggleBacktraceEnabled() { enabled_.fetch_xor(1); }
+
+  void EnableDumping() { dump_ = true; }
+  bool ShouldDumpAndReset() {
+    bool expected = true;
+    return dump_.compare_exchange_strong(expected, false);
+  }
 
  private:
   size_t alloc_offset_ = 0;
 
-  volatile bool enabled_ = false;
+  std::atomic_uint8_t enabled_;
+
+  std::atomic_bool dump_;
 
   DISALLOW_COPY_AND_ASSIGN(BacktraceData);
 };
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
index df453a9..e3798ab 100644
--- a/libc/malloc_debug/Config.cpp
+++ b/libc/malloc_debug/Config.cpp
@@ -56,6 +56,7 @@
 
 static constexpr size_t DEFAULT_BACKTRACE_FRAMES = 16;
 static constexpr size_t MAX_BACKTRACE_FRAMES = 256;
+static constexpr const char DEFAULT_BACKTRACE_DUMP_PREFIX[] = "/data/local/tmp/backtrace_heap";
 
 static constexpr size_t DEFAULT_EXPAND_BYTES = 16;
 static constexpr size_t MAX_EXPAND_BYTES = 16384;
@@ -85,6 +86,13 @@
       {BACKTRACE | TRACK_ALLOCS, &Config::SetBacktraceEnableOnSignal},
     },
 
+    {"backtrace_dump_on_exit",
+      {0, &Config::SetBacktraceDumpOnExit},
+    },
+    {"backtrace_dump_prefix",
+      {0, &Config::SetBacktraceDumpPrefix},
+    },
+
     {"fill",
       {FILL_ON_ALLOC | FILL_ON_FREE, &Config::SetFill},
     },
@@ -236,6 +244,24 @@
                     &backtrace_frames_);
 }
 
+bool Config::SetBacktraceDumpOnExit(const std::string& option, const std::string& value) {
+  if (Config::VerifyValueEmpty(option, value)) {
+    backtrace_dump_on_exit_ = true;
+    return true;
+  }
+  return false;
+}
+
+bool Config::SetBacktraceDumpPrefix(const std::string&, const std::string& value) {
+  if (value.empty()) {
+    backtrace_dump_prefix_ = DEFAULT_BACKTRACE_DUMP_PREFIX;
+  } else {
+    backtrace_dump_prefix_ = value;
+  }
+  return true;
+}
+
+
 bool Config::SetExpandAlloc(const std::string& option, const std::string& value) {
   return ParseValue(option, value, DEFAULT_EXPAND_BYTES, 1, MAX_EXPAND_BYTES, &expand_alloc_bytes_);
 }
@@ -309,6 +335,9 @@
   error_log("  backtrace[=XX]");
   error_log("    Enable capturing the backtrace at the point of allocation.");
   error_log("    If XX is set it sets the number of backtrace frames.");
+  error_log("    This option also enables dumping the backtrace heap data");
+  error_log("    when a signal is received. The data is dumped to the file");
+  error_log("    backtrace_dump_prefix.<PID>.txt.");
   error_log("    The default is %zu frames, the max number of frames is %zu.",
             DEFAULT_BACKTRACE_FRAMES, MAX_BACKTRACE_FRAMES);
   error_log("");
@@ -319,6 +348,19 @@
   error_log("    frames. The default is %zu frames, the max number of frames is %zu.",
             DEFAULT_BACKTRACE_FRAMES, MAX_BACKTRACE_FRAMES);
   error_log("");
+  error_log("  backtrace_dump_prefix[=FILE]");
+  error_log("    This option only has meaning if the backtrace option has been specified.");
+  error_log("    This is the prefix of the name of the file to which backtrace heap");
+  error_log("    data will be dumped. The file will be named backtrace_dump_prefix.<PID>.txt.");
+  error_log("    The default is %s.", DEFAULT_BACKTRACE_DUMP_PREFIX);
+
+  error_log("");
+  error_log("  backtrace_dump_on_exit");
+  error_log("    This option only has meaning if the backtrace option has been specified.");
+  error_log("    This will cause all live allocations to be dumped to the file");
+  error_log("    backtrace_dump_prefix.<PID>.final.txt.");
+  error_log("    The default is false.");
+  error_log("");
   error_log("  fill_on_alloc[=XX]");
   error_log("    On first allocation, fill with the value 0x%02x.", DEFAULT_FILL_ALLOC_VALUE);
   error_log("    If XX is set it will only fill up to XX bytes of the");
@@ -426,12 +468,15 @@
   front_guard_value_ = DEFAULT_FRONT_GUARD_VALUE;
   rear_guard_value_ = DEFAULT_REAR_GUARD_VALUE;
   backtrace_signal_ = SIGRTMAX - 19;
+  backtrace_dump_signal_ = SIGRTMAX - 17;
   record_allocs_signal_ = SIGRTMAX - 18;
   free_track_backtrace_num_frames_ = 0;
   record_allocs_file_.clear();
   fill_on_free_bytes_ = 0;
   backtrace_enable_on_signal_ = false;
   backtrace_enabled_ = false;
+  backtrace_dump_on_exit_ = false;
+  backtrace_dump_prefix_ = DEFAULT_BACKTRACE_DUMP_PREFIX;
 
   // Process each option name we can find.
   std::string option;
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
index d8a7069..349ad77 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -65,9 +65,12 @@
   uint64_t options() const { return options_; }
 
   int backtrace_signal() const { return backtrace_signal_; }
+  int backtrace_dump_signal() const { return backtrace_dump_signal_; }
   size_t backtrace_frames() const { return backtrace_frames_; }
   size_t backtrace_enabled() const { return backtrace_enabled_; }
   size_t backtrace_enable_on_signal() const { return backtrace_enable_on_signal_; }
+  bool backtrace_dump_on_exit() const { return backtrace_dump_on_exit_; }
+  const std::string& backtrace_dump_prefix() const { return backtrace_dump_prefix_; }
 
   size_t front_guard_bytes() const { return front_guard_bytes_; }
   size_t rear_guard_bytes() const { return rear_guard_bytes_; }
@@ -110,6 +113,8 @@
 
   bool SetBacktrace(const std::string& option, const std::string& value);
   bool SetBacktraceEnableOnSignal(const std::string& option, const std::string& value);
+  bool SetBacktraceDumpOnExit(const std::string& option, const std::string& value);
+  bool SetBacktraceDumpPrefix(const std::string& option, const std::string& value);
 
   bool SetExpandAlloc(const std::string& option, const std::string& value);
 
@@ -130,8 +135,11 @@
 
   bool backtrace_enable_on_signal_ = false;
   int backtrace_signal_ = 0;
+  int backtrace_dump_signal_ = 0;
   bool backtrace_enabled_ = false;
   size_t backtrace_frames_ = 0;
+  bool backtrace_dump_on_exit_ = false;
+  std::string backtrace_dump_prefix_;
 
   size_t fill_on_alloc_bytes_ = 0;
   size_t fill_on_free_bytes_ = 0;
diff --git a/libc/malloc_debug/README.md b/libc/malloc_debug/README.md
index 03a8a73..bb56cb8 100644
--- a/libc/malloc_debug/README.md
+++ b/libc/malloc_debug/README.md
@@ -108,6 +108,17 @@
 This option adds a special header to all allocations that contains the
 backtrace and information about the original allocation.
 
+As of P, this option will also enable dumping backtrace heap data to a
+file when the process receives the signal SIGRTMAX - 17 ( which is 47 on most
+Android devices). The format of this dumped data is the same format as
+that dumped when running am dumpheap -n. The default is to dump this data
+to the file /data/local/tmp/backtrace\_heap.**PID**.txt. This is useful when
+used with native only executables that run for a while since these processes
+are not spawned from a zygote process.
+
+Note that when the signal is received, the heap is not dumped until the next
+malloc/free occurs.
+
 ### backtrace\_enable\_on\_signal[=MAX\_FRAMES]
 Enable capturing the backtrace of each allocation site. If the
 backtrace capture is toggled when the process receives the signal
@@ -123,6 +134,26 @@
 This option adds a special header to all allocations that contains the
 backtrace and information about the original allocation.
 
+### backtrace\_dump\_on\_exit
+As of P, when the backtrace option has been enabled, this causes the backtrace
+dump heap data to be dumped to a file when the program exits. If the backtrace
+option has not been enabled, this does nothing. The default is to dump this
+to the file named /data/local/tmp/backtrace\_heap.**PID**.exit.txt.
+
+The file location can be changed by setting the backtrace\_dump\_prefix
+option.
+
+### backtrace\_dump\_prefix
+As of P, when the backtrace options has been enabled, this sets the prefix
+used for dumping files when the signal SIGRTMAX - 17 is received or when
+the program exits and backtrace\_dump\_on\_exit is set.
+
+The default is /data/local/tmp/backtrace\_heap.
+
+When this value is changed from the default, then the filename chosen
+on the signal will be backtrace\_dump\_prefix.**PID**.txt. The filename chosen
+when the program exits will be backtrace\_dump\_prefix.**PID**.exit.txt.
+
 ### fill\_on\_alloc[=MAX\_FILLED\_BYTES]
 Any allocation routine, other than calloc, will result in the allocation being
 filled with the value 0xeb. When doing a realloc to a larger size, the bytes
@@ -369,6 +400,66 @@
 As with the other error message, the function in parenthesis is the
 function that was called with the bad pointer.
 
+Backtrace Heap Dump Format
+==========================
+
+This section describes the format of the backtrace heap dump. This data is
+generated by am dumpheap -n or, as of P, by the signal or on exit.
+
+The data has this header:
+
+    Android Native Heap Dump v1.0
+
+    Total memory: XXXX
+    Allocation records: YYYY
+    Backtrace size: ZZZZ
+
+Total memory is the total of all of the currently live allocations.
+Allocation records is the total number of allocation records.
+Backtrace size is the maximum number of backtrace frames that can be present.
+
+Following this header are two different sections, the first section is the
+allocation records, the second section is the map data.
+
+The allocation record data has this format:
+
+    z ZYGOTE_CHILD_ALLOC  sz    ALLOCATION_SIZE  num  NUM_ALLOCATIONS bt FRAMES
+
+ZYGOTE\_CHILD\_ALLOC is either 0 or 1. 0 means this was allocated by the
+zygote process or in a process not spawned from the zygote. 1 means this
+was allocated by an application after it forked off from the zygote process.
+
+ALLOCATION\_SIZE is the size of the allocation.
+NUM\_ALLOCATIONS is the number of allocations that have this size and have the
+same backtrace.
+FRAMES is a list of instruction pointers that represent the backtrace of the
+allocation.
+
+Example:
+
+    z 0  sz      400  num    1  bt 0000a230 0000b500
+    z 1  sz      500  num    3  bt 0000b000 0000c000
+
+The first allocation record was created by the zygote of size 400 only one
+with this backtrace/size and a backtrace of 0xa230, 0xb500.
+The second allocation record was create by an application spawned from the
+zygote of size 500, where there are three of these allocation with the same
+backtrace/size and a backtrace of 0xb000, 0xc000.
+
+The final section is the map data for the process:
+
+    MAPS
+    7fe9181000-7fe91a2000 rw-p 00000000 00:00 0                              /system/lib/libc.so
+    .
+    .
+    .
+    END
+
+The map data is simply the output of /proc/PID/maps. This data can be used to
+decode the frames in the backtraces.
+
+There is a tool to visualize this data, development/scripts/native\_heapdump\_viewer.py.
+
 Examples
 ========
 
diff --git a/libc/malloc_debug/TrackData.cpp b/libc/malloc_debug/TrackData.cpp
index 7ce477c..4266aa2 100644
--- a/libc/malloc_debug/TrackData.cpp
+++ b/libc/malloc_debug/TrackData.cpp
@@ -59,6 +59,49 @@
   });
 }
 
+void TrackData::GetListBySizeThenBacktrace(std::vector<const Header*>* list, size_t* total_memory) {
+  if (!(debug_->config().options() & BACKTRACE)) {
+    return;
+  }
+
+  *total_memory = 0;
+  for (const auto& header : headers_) {
+    list->push_back(header);
+    *total_memory += header->real_size();
+  }
+
+  // Put all zygote allocations first by size and backtrace.
+  // Then all zygote child allocation by size and backtrace.
+  std::sort(list->begin(), list->end(), [&](const Header* a, const Header* b) {
+    if (a->zygote_child_alloc() && !b->zygote_child_alloc()) {
+      return false;
+    } else if (!a->zygote_child_alloc() && b->zygote_child_alloc()) {
+      return true;
+    }
+    if (a->real_size() != b->real_size()) return a->real_size() < b->real_size();
+    // If the size is the same, compare backtrace elements.
+    BacktraceHeader* a_back = debug_->GetAllocBacktrace(a);
+    BacktraceHeader* b_back = debug_->GetAllocBacktrace(b);
+    for (size_t i = 0; i < a_back->num_frames; i++) {
+      if (i > b_back->num_frames) {
+        // All frames equal up to this point, but a has more frames available.
+        return false;
+      }
+      if (a_back->frames[i] < b_back->frames[i]) {
+        return false;
+      } else if (a_back->frames[i] > b_back->frames[i]) {
+        return true;
+      }
+    }
+    if (a_back->num_frames < b_back->num_frames) {
+      // All frames equal up to this point, but b has more frames available.
+      return true;
+    }
+    return false;
+  });
+
+}
+
 void TrackData::Add(const Header* header, bool backtrace_found) {
   pthread_mutex_lock(&mutex_);
   if (backtrace_found) {
diff --git a/libc/malloc_debug/TrackData.h b/libc/malloc_debug/TrackData.h
index e4c8951..f7486e9 100644
--- a/libc/malloc_debug/TrackData.h
+++ b/libc/malloc_debug/TrackData.h
@@ -51,6 +51,8 @@
 
   void GetList(std::vector<const Header*>* list);
 
+  void GetListBySizeThenBacktrace(std::vector<const Header*>* list, size_t* total_memory);
+
   void Add(const Header* header, bool backtrace_found);
 
   void Remove(const Header* header, bool backtrace_found);
diff --git a/libc/malloc_debug/exported32.map b/libc/malloc_debug/exported32.map
index 59bb102..e92a7cf 100644
--- a/libc/malloc_debug/exported32.map
+++ b/libc/malloc_debug/exported32.map
@@ -1,6 +1,7 @@
 LIBC_MALLOC_DEBUG {
   global:
     debug_calloc;
+    debug_dump_heap;
     debug_finalize;
     debug_free;
     debug_free_malloc_leak_info;
diff --git a/libc/malloc_debug/exported64.map b/libc/malloc_debug/exported64.map
index ec9d840..94104b0 100644
--- a/libc/malloc_debug/exported64.map
+++ b/libc/malloc_debug/exported64.map
@@ -1,6 +1,7 @@
 LIBC_MALLOC_DEBUG {
   global:
     debug_calloc;
+    debug_dump_heap;
     debug_finalize;
     debug_free;
     debug_free_malloc_leak_info;
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 014d385..d890a1c 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -34,8 +34,11 @@
 #include <sys/param.h>
 #include <unistd.h>
 
+#include <mutex>
 #include <vector>
 
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
 #include <private/bionic_malloc_dispatch.h>
 
 #include "backtrace.h"
@@ -65,6 +68,7 @@
 bool debug_initialize(const MallocDispatch* malloc_dispatch, int* malloc_zygote_child,
     const char* options);
 void debug_finalize();
+bool debug_dump_heap(const char* file_name);
 void debug_get_malloc_leak_info(
     uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory,
     size_t* backtrace_size);
@@ -138,7 +142,7 @@
   header->orig_pointer = orig_pointer;
   header->size = size;
   if (*g_malloc_zygote_child) {
-    header->set_zygote();
+    header->set_zygote_child_alloc();
   }
   header->usable_size = g_dispatch->malloc_usable_size(orig_pointer);
   if (header->usable_size == 0) {
@@ -164,7 +168,7 @@
   bool backtrace_found = false;
   if (g_debug->config().options() & BACKTRACE) {
     BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
-    if (g_debug->backtrace->enabled()) {
+    if (g_debug->backtrace->ShouldBacktrace()) {
       back_header->num_frames = backtrace_get(
           &back_header->frames[0], g_debug->config().backtrace_frames());
       backtrace_found = back_header->num_frames > 0;
@@ -224,6 +228,13 @@
     g_debug->track->DisplayLeaks();
   }
 
+  if ((g_debug->config().options() & BACKTRACE) && g_debug->config().backtrace_dump_on_exit()) {
+    ScopedDisableDebugCalls disable;
+    debug_dump_heap(
+        android::base::StringPrintf("%s.%d.exit.txt",
+                                    g_debug->config().backtrace_dump_prefix().c_str(),                              getpid()).c_str());
+  }
+
   DebugDisableSet(true);
 
   backtrace_shutdown();
@@ -288,6 +299,13 @@
 }
 
 static void *internal_malloc(size_t size) {
+  if ((g_debug->config().options() & BACKTRACE) && g_debug->backtrace->ShouldDumpAndReset()) {
+    debug_dump_heap(
+        android::base::StringPrintf("%s.%d.txt",
+                                    g_debug->config().backtrace_dump_prefix().c_str(),
+                                    getpid()).c_str());
+  }
+
   if (size == 0) {
     size = 1;
   }
@@ -341,6 +359,13 @@
 }
 
 static void internal_free(void* pointer) {
+  if ((g_debug->config().options() & BACKTRACE) && g_debug->backtrace->ShouldDumpAndReset()) {
+    debug_dump_heap(
+        android::base::StringPrintf("%s.%d.txt",
+                                    g_debug->config().backtrace_dump_prefix().c_str(),
+                                    getpid()).c_str());
+  }
+
   void* free_pointer = pointer;
   size_t bytes;
   Header* header;
@@ -538,7 +563,7 @@
     if (real_size < header->usable_size) {
       header->size = real_size;
       if (*g_malloc_zygote_child) {
-        header->set_zygote();
+        header->set_zygote_child_alloc();
       }
       if (g_debug->config().options() & REAR_GUARD) {
         // Don't bother allocating a smaller pointer in this case, simply
@@ -758,3 +783,65 @@
   return debug_memalign(getpagesize(), size);
 }
 #endif
+
+static std::mutex g_dump_lock;
+
+bool debug_dump_heap(const char* file_name) {
+  ScopedDisableDebugCalls disable;
+
+  std::lock_guard<std::mutex> guard(g_dump_lock);
+
+  FILE* fp = fopen(file_name, "w+e");
+  if (fp == nullptr) {
+    error_log("Unable to create file: %s", file_name);
+    return false;
+  }
+  error_log("Dumping to file: %s\n", file_name);
+
+  if (!(g_debug->config().options() & BACKTRACE)) {
+    fprintf(fp, "Native heap dump not available. To enable, run these commands (requires root):\n");
+    fprintf(fp, "# adb shell stop\n");
+    fprintf(fp, "# adb shell setprop libc.debug.malloc.options backtrace\n");
+    fprintf(fp, "# adb shell start\n");
+    fclose(fp);
+    return false;
+  }
+
+  fprintf(fp, "Android Native Heap Dump v1.0\n\n");
+
+  std::vector<const Header*> list;
+  size_t total_memory;
+  g_debug->track->GetListBySizeThenBacktrace(&list, &total_memory);
+  fprintf(fp, "Total memory: %zu\n", total_memory);
+  fprintf(fp, "Allocation records: %zd\n", list.size());
+  fprintf(fp, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
+  fprintf(fp, "\n");
+
+  for (const auto& header : list) {
+    const BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
+    fprintf(fp, "z %d  sz %8zu  num    1  bt", (header->zygote_child_alloc()) ? 1 : 0,
+            header->real_size());
+    for (size_t i = 0; i < back_header->num_frames; i++) {
+      if (back_header->frames[i] == 0) {
+        break;
+      }
+#ifdef __LP64__
+      fprintf(fp, " %016" PRIxPTR, back_header->frames[i]);
+#else
+      fprintf(fp, " %08" PRIxPTR, back_header->frames[i]);
+#endif
+    }
+    fprintf(fp, "\n");
+  }
+
+  fprintf(fp, "MAPS\n");
+  std::string content;
+  if (!android::base::ReadFileToString("/proc/self/maps", &content)) {
+    fprintf(fp, "Could not open /proc/self/maps\n");
+  } else {
+    fprintf(fp, "%s", content.c_str());
+  }
+  fprintf(fp, "END\n");
+  fclose(fp);
+  return true;
+}
diff --git a/libc/malloc_debug/malloc_debug.h b/libc/malloc_debug/malloc_debug.h
index 347fae2..4a1e8da 100644
--- a/libc/malloc_debug/malloc_debug.h
+++ b/libc/malloc_debug/malloc_debug.h
@@ -54,7 +54,8 @@
   size_t size;
   size_t usable_size;
   size_t real_size() const { return size & ~(1U << 31); }
-  void set_zygote() { size |= 1U << 31; }
+  void set_zygote_child_alloc() { size |= 1U << 31; }
+  bool zygote_child_alloc() const { return size & (1U << 31); }
   static size_t max_size() { return (1U << 31) - 1; }
 } __attribute__((packed));
 
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index 77dc848..ee8fe06 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -60,6 +60,9 @@
   "6 malloc_debug   backtrace[=XX]\n"
   "6 malloc_debug     Enable capturing the backtrace at the point of allocation.\n"
   "6 malloc_debug     If XX is set it sets the number of backtrace frames.\n"
+  "6 malloc_debug     This option also enables dumping the backtrace heap data\n"
+  "6 malloc_debug     when a signal is received. The data is dumped to the file\n"
+  "6 malloc_debug     backtrace_dump_prefix.<PID>.txt.\n"
   "6 malloc_debug     The default is 16 frames, the max number of frames is 256.\n"
   "6 malloc_debug \n"
   "6 malloc_debug   backtrace_enable_on_signal[=XX]\n"
@@ -68,6 +71,18 @@
   "6 malloc_debug     receives a signal. If XX is set it sets the number of backtrace\n"
   "6 malloc_debug     frames. The default is 16 frames, the max number of frames is 256.\n"
   "6 malloc_debug \n"
+  "6 malloc_debug   backtrace_dump_prefix[=FILE]\n"
+  "6 malloc_debug     This option only has meaning if the backtrace option has been specified.\n"
+  "6 malloc_debug     This is the prefix of the name of the file to which backtrace heap\n"
+  "6 malloc_debug     data will be dumped. The file will be named backtrace_dump_prefix.<PID>.txt.\n"
+  "6 malloc_debug     The default is /data/local/tmp/backtrace_heap.\n"
+  "6 malloc_debug \n"
+  "6 malloc_debug   backtrace_dump_on_exit\n"
+  "6 malloc_debug     This option only has meaning if the backtrace option has been specified.\n"
+  "6 malloc_debug     This will cause all live allocations to be dumped to the file\n"
+  "6 malloc_debug     backtrace_dump_prefix.<PID>.final.txt.\n"
+  "6 malloc_debug     The default is false.\n"
+  "6 malloc_debug \n"
   "6 malloc_debug   fill_on_alloc[=XX]\n"
   "6 malloc_debug     On first allocation, fill with the value 0xeb.\n"
   "6 malloc_debug     If XX is set it will only fill up to XX bytes of the\n"
@@ -292,12 +307,14 @@
   ASSERT_EQ(64U, config->backtrace_frames());
   ASSERT_TRUE(config->backtrace_enabled());
   ASSERT_FALSE(config->backtrace_enable_on_signal());
+  ASSERT_FALSE(config->backtrace_dump_on_exit());
 
   ASSERT_TRUE(InitConfig("backtrace")) << getFakeLogPrint();
   ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
   ASSERT_EQ(16U, config->backtrace_frames());
   ASSERT_TRUE(config->backtrace_enabled());
   ASSERT_FALSE(config->backtrace_enable_on_signal());
+  ASSERT_FALSE(config->backtrace_dump_on_exit());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -309,12 +326,14 @@
   ASSERT_EQ(64U, config->backtrace_frames());
   ASSERT_FALSE(config->backtrace_enabled());
   ASSERT_TRUE(config->backtrace_enable_on_signal());
+  ASSERT_FALSE(config->backtrace_dump_on_exit());
 
   ASSERT_TRUE(InitConfig("backtrace_enable_on_signal")) << getFakeLogPrint();
   ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
   ASSERT_EQ(16U, config->backtrace_frames());
   ASSERT_FALSE(config->backtrace_enabled());
   ASSERT_TRUE(config->backtrace_enable_on_signal());
+  ASSERT_FALSE(config->backtrace_dump_on_exit());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -326,12 +345,14 @@
   ASSERT_EQ(64U, config->backtrace_frames());
   ASSERT_FALSE(config->backtrace_enabled());
   ASSERT_TRUE(config->backtrace_enable_on_signal());
+  ASSERT_FALSE(config->backtrace_dump_on_exit());
 
   ASSERT_TRUE(InitConfig("backtrace")) << getFakeLogPrint();
   ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
   ASSERT_EQ(16U, config->backtrace_frames());
   ASSERT_TRUE(config->backtrace_enabled());
   ASSERT_FALSE(config->backtrace_enable_on_signal());
+  ASSERT_FALSE(config->backtrace_dump_on_exit());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
@@ -343,12 +364,46 @@
   ASSERT_EQ(16U, config->backtrace_frames());
   ASSERT_TRUE(config->backtrace_enabled());
   ASSERT_TRUE(config->backtrace_enable_on_signal());
+  ASSERT_FALSE(config->backtrace_dump_on_exit());
 
   ASSERT_TRUE(InitConfig("backtrace backtrace_enable_on_signal")) << getFakeLogPrint();
   ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options());
   ASSERT_EQ(16U, config->backtrace_frames());
   ASSERT_TRUE(config->backtrace_enabled());
   ASSERT_TRUE(config->backtrace_enable_on_signal());
+  ASSERT_FALSE(config->backtrace_dump_on_exit());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, backtrace_dump_on_exit) {
+  ASSERT_TRUE(InitConfig("backtrace_dump_on_exit")) << getFakeLogPrint();
+  ASSERT_EQ(0U, config->options());
+  ASSERT_TRUE(config->backtrace_dump_on_exit());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, backtrace_dump_on_exit_error) {
+  ASSERT_FALSE(InitConfig("backtrace_dump_on_exit=something")) << getFakeLogPrint();
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg(
+      "6 malloc_debug malloc_testing: value set for option 'backtrace_dump_on_exit' "
+      "which does not take a value\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, backtrace_dump_prefix) {
+  ASSERT_TRUE(InitConfig("backtrace_dump_prefix")) << getFakeLogPrint();
+  ASSERT_EQ(0U, config->options());
+  ASSERT_EQ("/data/local/tmp/backtrace_heap", config->backtrace_dump_prefix());
+
+  ASSERT_TRUE(InitConfig("backtrace_dump_prefix=/fake/location")) << getFakeLogPrint();
+  ASSERT_EQ(0U, config->options());
+  ASSERT_EQ("/fake/location", config->backtrace_dump_prefix());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 4fdba2e..37d8057 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -32,6 +32,7 @@
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 
 #include <private/bionic_macros.h>
 #include <private/bionic_malloc_dispatch.h>
@@ -82,6 +83,8 @@
 
 static constexpr const char RECORD_ALLOCS_FILE[] = "/data/local/tmp/record_allocs.txt";
 
+static constexpr const char BACKTRACE_DUMP_PREFIX[] = "/data/local/tmp/backtrace_heap";
+
 class MallocDebugTest : public ::testing::Test {
  protected:
   void SetUp() override {
@@ -104,6 +107,8 @@
     initialized = true;
   }
 
+  void BacktraceDumpOnSignal(bool trigger_with_alloc);
+
   bool initialized;
 
   int zygote;
@@ -132,7 +137,7 @@
   mallopt,
 };
 
-void VerifyAllocCalls() {
+void VerifyAllocCalls(bool backtrace_enabled) {
   size_t alloc_size = 1024;
 
   // Verify debug_malloc.
@@ -186,17 +191,23 @@
   ASSERT_TRUE(pointer == nullptr);
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
-  ASSERT_STREQ("", getFakeLogPrint().c_str());
+  std::string expected_log;
+  if (backtrace_enabled) {
+    expected_log += android::base::StringPrintf(
+        "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+        SIGRTMAX - 17, getpid());
+  }
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
 }
 
 TEST_F(MallocDebugTest, fill_generic) {
   Init("fill");
-  VerifyAllocCalls();
+  VerifyAllocCalls(false);
 }
 
 TEST_F(MallocDebugTest, fill_on_alloc_generic) {
   Init("fill_on_alloc");
-  VerifyAllocCalls();
+  VerifyAllocCalls(false);
 }
 
 TEST_F(MallocDebugTest, fill_on_alloc_partial) {
@@ -275,7 +286,7 @@
 
 TEST_F(MallocDebugTest, all_options) {
   Init("guard backtrace fill expand_alloc free_track leak_track");
-  VerifyAllocCalls();
+  VerifyAllocCalls(true);
 }
 
 TEST_F(MallocDebugTest, expand_alloc) {
@@ -624,6 +635,9 @@
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   std::string expected_log = android::base::StringPrintf(
+      "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+      SIGRTMAX - 17, getpid());
+  expected_log += android::base::StringPrintf(
       "6 malloc_debug +++ malloc_testing leaked block of size 1024 at %p (leak 1 of 3)\n",
       pointer3);
   expected_log += "6 malloc_debug Backtrace at time of allocation:\n";
@@ -649,7 +663,6 @@
   expected_log += "6 malloc_debug   #00 pc 0x1000\n";
   expected_log += "6 malloc_debug   #01 pc 0x2000\n";
   expected_log += "6 malloc_debug   #02 pc 0x3000\n";
-
   ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
 }
 
@@ -1022,7 +1035,10 @@
   ASSERT_EQ(0U, backtrace_size);
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
-  ASSERT_STREQ("", getFakeLogPrint().c_str());
+  std::string expected_log = android::base::StringPrintf(
+      "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+      SIGRTMAX - 17, getpid());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
 }
 
 TEST_F(MallocDebugTest, get_malloc_leak_info_single) {
@@ -1065,7 +1081,10 @@
   debug_free(pointer);
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
-  ASSERT_STREQ("", getFakeLogPrint().c_str());
+  std::string expected_log = android::base::StringPrintf(
+      "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+      SIGRTMAX - 17, getpid());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
 }
 
 TEST_F(MallocDebugTest, get_malloc_leak_info_multi) {
@@ -1144,7 +1163,10 @@
   debug_free(pointers[2]);
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
-  ASSERT_STREQ("", getFakeLogPrint().c_str());
+  std::string expected_log = android::base::StringPrintf(
+      "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+      SIGRTMAX - 17, getpid());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
 }
 
 TEST_F(MallocDebugTest, get_malloc_leak_info_multi_skip_empty_backtrace) {
@@ -1215,6 +1237,185 @@
   debug_free(pointers[2]);
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string expected_log = android::base::StringPrintf(
+      "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+      SIGRTMAX - 17, getpid());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+static std::string SanitizeHeapData(const std::string& data) {
+  // Remove the map data since it's not consistent.
+  std::string sanitized;
+  bool skip_map_data = false;
+  bool map_data_found = false;
+  for (auto& line : android::base::Split(data, "\n")) {
+    if (skip_map_data) {
+      if (line == "END") {
+        if (map_data_found) {
+          sanitized += "MAP_DATA\n";
+          map_data_found = false;
+        }
+        skip_map_data = false;
+      } else {
+        map_data_found = true;
+        continue;
+      }
+    }
+    if (line == "MAPS") {
+      skip_map_data = true;
+    }
+    sanitized += line + '\n';
+  }
+  return sanitized;
+}
+
+void MallocDebugTest::BacktraceDumpOnSignal(bool trigger_with_alloc) {
+  Init("backtrace=4");
+
+  backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200});
+  backtrace_fake_add(std::vector<uintptr_t> {0x300, 0x400});
+  backtrace_fake_add(std::vector<uintptr_t> {0x500, 0x600});
+
+  backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000});
+  backtrace_fake_add(std::vector<uintptr_t> {0xa100, 0xb200});
+  backtrace_fake_add(std::vector<uintptr_t> {0xa300, 0xb300});
+
+  std::vector<void*> pointers;
+  zygote = 1;
+  pointers.push_back(debug_malloc(100));
+  ASSERT_TRUE(pointers.back() != nullptr);
+  pointers.push_back(debug_malloc(40));
+  ASSERT_TRUE(pointers.back() != nullptr);
+  pointers.push_back(debug_malloc(200));
+  ASSERT_TRUE(pointers.back() != nullptr);
+
+  zygote = 0;
+  pointers.push_back(debug_malloc(10));
+  ASSERT_TRUE(pointers.back() != nullptr);
+  pointers.push_back(debug_malloc(50));
+  ASSERT_TRUE(pointers.back() != nullptr);
+  pointers.push_back(debug_malloc(5));
+  ASSERT_TRUE(pointers.back() != nullptr);
+
+  // Dump all of the data accumulated so far.
+  ASSERT_TRUE(kill(getpid(), SIGRTMAX - 17) == 0);
+  sleep(1);
+
+  // This triggers the dumping.
+  if (trigger_with_alloc) {
+    pointers.push_back(debug_malloc(23));
+    ASSERT_TRUE(pointers.back() != nullptr);
+  } else {
+    debug_free(pointers.back());
+    pointers.pop_back();
+  }
+
+  for (auto* pointer : pointers) {
+    debug_free(pointer);
+  }
+
+  // Read all of the contents.
+  std::string actual;
+  std::string name = android::base::StringPrintf("%s.%d.txt", BACKTRACE_DUMP_PREFIX, getpid());
+  ASSERT_TRUE(android::base::ReadFileToString(name, &actual));
+  ASSERT_EQ(0, unlink(name.c_str()));
+
+  std::string sanitized(SanitizeHeapData(actual));
+
+  std::string expected =
+      "Android Native Heap Dump v1.0\n"
+      "\n"
+      "Total memory: 405\n"
+      "Allocation records: 6\n"
+      "Backtrace size: 4\n"
+      "\n"
+#if defined(__LP64__)
+      "z 0  sz        5  num    1  bt 000000000000a300 000000000000b300\n"
+      "z 0  sz       10  num    1  bt 000000000000a000 000000000000b000\n"
+      "z 0  sz       50  num    1  bt 000000000000a100 000000000000b200\n"
+      "z 1  sz       40  num    1  bt 0000000000000300 0000000000000400\n"
+      "z 1  sz      100  num    1  bt 0000000000000100 0000000000000200\n"
+      "z 1  sz      200  num    1  bt 0000000000000500 0000000000000600\n"
+#else
+      "z 0  sz        5  num    1  bt 0000a300 0000b300\n"
+      "z 0  sz       10  num    1  bt 0000a000 0000b000\n"
+      "z 0  sz       50  num    1  bt 0000a100 0000b200\n"
+      "z 1  sz       40  num    1  bt 00000300 00000400\n"
+      "z 1  sz      100  num    1  bt 00000100 00000200\n"
+      "z 1  sz      200  num    1  bt 00000500 00000600\n"
+#endif
+      "MAPS\n"
+      "MAP_DATA\n"
+      "END\n\n";
+  ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string expected_log = android::base::StringPrintf(
+      "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+      SIGRTMAX - 17, getpid());
+  expected_log += android::base::StringPrintf(
+      "6 malloc_debug Dumping to file: /data/local/tmp/backtrace_heap.%d.txt\n\n", getpid());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, backtrace_dump_on_signal_by_malloc) {
+  BacktraceDumpOnSignal(true);
+}
+
+TEST_F(MallocDebugTest, backtrace_dump_on_signal_by_free) {
+  BacktraceDumpOnSignal(false);
+}
+
+TEST_F(MallocDebugTest, backtrace_dump_on_exit) {
+  pid_t pid;
+  if ((pid = fork()) == 0) {
+    Init("backtrace=4 backtrace_dump_on_exit");
+    backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200});
+    backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000});
+    backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000, 0xc000});
+
+    std::vector<void*> pointers;
+    pointers.push_back(debug_malloc(300));
+    pointers.push_back(debug_malloc(400));
+    pointers.push_back(debug_malloc(500));
+
+    // Call the exit function manually.
+    debug_finalize();
+    exit(0);
+  }
+  ASSERT_NE(-1, pid);
+  ASSERT_EQ(pid, waitpid(pid, nullptr, 0));
+
+  // Read all of the contents.
+  std::string actual;
+  std::string name = android::base::StringPrintf("%s.%d.exit.txt", BACKTRACE_DUMP_PREFIX, pid);
+  ASSERT_TRUE(android::base::ReadFileToString(name, &actual));
+  ASSERT_EQ(0, unlink(name.c_str()));
+
+  std::string sanitized(SanitizeHeapData(actual));
+
+  std::string expected =
+      "Android Native Heap Dump v1.0\n"
+      "\n"
+      "Total memory: 1200\n"
+      "Allocation records: 3\n"
+      "Backtrace size: 4\n"
+      "\n"
+#if defined(__LP64__)
+      "z 0  sz      300  num    1  bt 0000000000000100 0000000000000200\n"
+      "z 0  sz      400  num    1  bt 000000000000a000 000000000000b000\n"
+      "z 0  sz      500  num    1  bt 000000000000a000 000000000000b000 000000000000c000\n"
+#else
+      "z 0  sz      300  num    1  bt 00000100 00000200\n"
+      "z 0  sz      400  num    1  bt 0000a000 0000b000\n"
+      "z 0  sz      500  num    1  bt 0000a000 0000b000 0000c000\n"
+#endif
+      "MAPS\n"
+      "MAP_DATA\n"
+      "END\n\n";
+  ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
@@ -1317,6 +1518,9 @@
   std::string expected_log = android::base::StringPrintf(
       "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to enable backtracing.\n",
       SIGRTMAX - 19, getpid());
+  expected_log += android::base::StringPrintf(
+      "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+      SIGRTMAX - 17, getpid());
   ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
 }
 
@@ -1439,7 +1643,10 @@
   debug_free(pointer);
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());
-  ASSERT_STREQ("", getFakeLogPrint().c_str());
+  std::string expected_log = android::base::StringPrintf(
+      "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+      SIGRTMAX - 17, getpid());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
 }
 
 TEST_F(MallocDebugTest, max_size) {
@@ -1596,6 +1803,7 @@
   // Read all of the contents.
   std::string actual;
   ASSERT_TRUE(android::base::ReadFileToString(RECORD_ALLOCS_FILE, &actual));
+  ASSERT_EQ(0, unlink(RECORD_ALLOCS_FILE));
 
   ASSERT_STREQ(expected.c_str(), actual.c_str());
 
@@ -1653,6 +1861,7 @@
   // Read all of the contents.
   std::string actual;
   ASSERT_TRUE(android::base::ReadFileToString(RECORD_ALLOCS_FILE, &actual));
+  ASSERT_EQ(0, unlink(RECORD_ALLOCS_FILE));
 
   ASSERT_STREQ(expected.c_str(), actual.c_str());
 
@@ -1694,6 +1903,7 @@
   // Read all of the contents.
   std::string actual;
   ASSERT_TRUE(android::base::ReadFileToString(RECORD_ALLOCS_FILE, &actual));
+  ASSERT_EQ(0, unlink(RECORD_ALLOCS_FILE));
 
   ASSERT_STREQ(expected.c_str(), actual.c_str());
 
@@ -1748,6 +1958,7 @@
   expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
 
   ASSERT_TRUE(android::base::ReadFileToString(RECORD_ALLOCS_FILE, &actual));
+  ASSERT_EQ(0, unlink(RECORD_ALLOCS_FILE));
   ASSERT_STREQ(expected.c_str(), actual.c_str());
 
   ASSERT_STREQ("", getFakeLogBuf().c_str());