Merge changes Iac8109b3,I5ad67001,Ic1adb4df,I08a50349

* changes:
  malloc_debug: reread /proc/pid/maps when entry is missing
  Add backtrace_string and export to libmemunreachable
  Export malloc_backtrace
  Export malloc_iterate and friends
diff --git a/libc/bionic/jemalloc.h b/libc/bionic/jemalloc.h
index 98ea0ee..fceb323 100644
--- a/libc/bionic/jemalloc.h
+++ b/libc/bionic/jemalloc.h
@@ -26,6 +26,10 @@
 __BEGIN_DECLS
 
 struct mallinfo je_mallinfo();
+int je_iterate(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
+void je_malloc_disable();
+void je_malloc_enable();
+int je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
 void* je_memalign_round_up_boundary(size_t, size_t);
 void* je_pvalloc(size_t);
 
diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp
index 67bb052..fffaea8 100644
--- a/libc/bionic/libc_logging.cpp
+++ b/libc/bionic/libc_logging.cpp
@@ -548,7 +548,7 @@
   uint32_t tv_nsec;
 };
 
-static int __libc_write_log(int priority, const char* tag, const char* msg) {
+int __libc_write_log(int priority, const char* tag, const char* msg) {
   int main_log_fd = __libc_open_log_socket();
   if (main_log_fd == -1) {
     // Try stderr instead.
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index 56f1c10..863b07c 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -40,6 +40,8 @@
 //   free_malloc_leak_info: Frees the data allocated by the call to
 //                          get_malloc_leak_info.
 
+#include <pthread.h>
+
 #include <private/bionic_config.h>
 #include <private/bionic_globals.h>
 #include <private/bionic_malloc_dispatch.h>
@@ -63,6 +65,9 @@
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
     Malloc(valloc),
 #endif
+    Malloc(iterate),
+    Malloc(malloc_disable),
+    Malloc(malloc_enable),
   };
 
 // In a VM process, this is set to 1 after fork()ing out of zygote.
@@ -159,7 +164,6 @@
 #if !defined(LIBC_STATIC)
 
 #include <dlfcn.h>
-#include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -179,6 +183,7 @@
 static void (*g_debug_finalize_func)();
 static void (*g_debug_get_malloc_leak_info_func)(uint8_t**, size_t*, size_t*, size_t*, size_t*);
 static void (*g_debug_free_malloc_leak_info_func)(uint8_t*);
+static ssize_t (*g_debug_malloc_backtrace_func)(void*, uintptr_t*, size_t);
 
 // =============================================================================
 // Log functions
@@ -215,6 +220,7 @@
   }
   g_debug_free_malloc_leak_info_func(info);
 }
+
 // =============================================================================
 
 template<typename FunctionType>
@@ -262,6 +268,18 @@
                                          prefix, "realloc")) {
     return false;
   }
+  if (!InitMallocFunction<MallocIterate>(malloc_impl_handler, &table->iterate,
+                                         prefix, "iterate")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMallocDisable>(malloc_impl_handler, &table->malloc_disable,
+                                         prefix, "malloc_disable")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMallocEnable>(malloc_impl_handler, &table->malloc_enable,
+                                         prefix, "malloc_enable")) {
+    return false;
+  }
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
   if (!InitMallocFunction<MallocPvalloc>(malloc_impl_handler, &table->pvalloc,
                                          prefix, "pvalloc")) {
@@ -352,6 +370,15 @@
   }
   g_debug_free_malloc_leak_info_func = reinterpret_cast<void (*)(uint8_t*)>(sym);
 
+  sym = dlsym(malloc_impl_handle, "debug_malloc_backtrace");
+  if (sym == nullptr) {
+    error_log("%s: debug_malloc_backtrace routine not found in %s", getprogname(),
+              DEBUG_SHARED_LIB);
+    dlclose(malloc_impl_handle);
+    return;
+  }
+  g_debug_malloc_backtrace_func = reinterpret_cast<ssize_t (*)(void*, uintptr_t*, size_t)>(sym);
+
   if (!init_func(&__libc_malloc_default_dispatch, &gMallocLeakZygoteChild)) {
     dlclose(malloc_impl_handle);
     return;
@@ -385,3 +412,50 @@
   malloc_init_impl(globals);
 }
 #endif  // !LIBC_STATIC
+
+// =============================================================================
+// Exported for use by libmemunreachable.
+// =============================================================================
+
+// Calls callback for every allocation in the anonymous heap mapping
+// [base, base+size).  Must be called between malloc_disable and malloc_enable.
+extern "C" int malloc_iterate(uintptr_t base, size_t size,
+    void (*callback)(uintptr_t base, size_t size, void* arg), void* arg) {
+  auto _iterate = __libc_globals->malloc_dispatch.iterate;
+  if (__predict_false(_iterate != nullptr)) {
+    return _iterate(base, size, callback, arg);
+  }
+  return Malloc(iterate)(base, size, callback, arg);
+}
+
+// Disable calls to malloc so malloc_iterate gets a consistent view of
+// allocated memory.
+extern "C" void malloc_disable() {
+  auto _malloc_disable = __libc_globals->malloc_dispatch.malloc_disable;
+  if (__predict_false(_malloc_disable != nullptr)) {
+    return _malloc_disable();
+  }
+  return Malloc(malloc_disable)();
+}
+
+// Re-enable calls to malloc after a previous call to malloc_disable.
+extern "C" void malloc_enable() {
+  auto _malloc_enable = __libc_globals->malloc_dispatch.malloc_enable;
+  if (__predict_false(_malloc_enable != nullptr)) {
+    return _malloc_enable();
+  }
+  return Malloc(malloc_enable)();
+}
+
+#ifndef LIBC_STATIC
+extern "C" ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count) {
+  if (g_debug_malloc_backtrace_func == nullptr) {
+    return 0;
+  }
+  return g_debug_malloc_backtrace_func(pointer, frames, frame_count);
+}
+#else
+extern "C" ssize_t malloc_backtrace(void*, uintptr_t*, size_t) {
+  return 0;
+}
+#endif
diff --git a/libc/libc.arm.brillo.map b/libc/libc.arm.brillo.map
index 80a88bd..2c0ab15 100644
--- a/libc/libc.arm.brillo.map
+++ b/libc/libc.arm.brillo.map
@@ -627,6 +627,10 @@
     madvise;
     mallinfo;
     malloc;
+    malloc_backtrace;
+    malloc_disable;
+    malloc_enable;
+    malloc_iterate;
     malloc_info;
     malloc_usable_size;
     mbrlen;
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 18e51d4..c22535d 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -627,6 +627,10 @@
     madvise;
     mallinfo;
     malloc;
+    malloc_backtrace;
+    malloc_disable;
+    malloc_enable;
+    malloc_iterate;
     malloc_info;
     malloc_usable_size;
     mbrlen;
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index e1ccd4e..63c82e5 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -552,6 +552,10 @@
     madvise;
     mallinfo;
     malloc;
+    malloc_backtrace;
+    malloc_disable;
+    malloc_enable;
+    malloc_iterate;
     malloc_info;
     malloc_usable_size;
     mbrlen;
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index b87a5e3..c000fb1 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -630,6 +630,10 @@
     madvise;
     mallinfo;
     malloc;
+    malloc_backtrace;
+    malloc_disable;
+    malloc_enable;
+    malloc_iterate;
     malloc_info;
     malloc_usable_size;
     mbrlen;
diff --git a/libc/libc.mips.brillo.map b/libc/libc.mips.brillo.map
index 57dfd50..8e5fdf4 100644
--- a/libc/libc.mips.brillo.map
+++ b/libc/libc.mips.brillo.map
@@ -625,6 +625,10 @@
     madvise;
     mallinfo;
     malloc;
+    malloc_backtrace;
+    malloc_disable;
+    malloc_enable;
+    malloc_iterate;
     malloc_info;
     malloc_usable_size;
     mbrlen;
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index ed72fd4..d30ead8 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -625,6 +625,10 @@
     madvise;
     mallinfo;
     malloc;
+    malloc_backtrace;
+    malloc_disable;
+    malloc_enable;
+    malloc_iterate;
     malloc_info;
     malloc_usable_size;
     mbrlen;
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index e1ccd4e..63c82e5 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -552,6 +552,10 @@
     madvise;
     mallinfo;
     malloc;
+    malloc_backtrace;
+    malloc_disable;
+    malloc_enable;
+    malloc_iterate;
     malloc_info;
     malloc_usable_size;
     mbrlen;
diff --git a/libc/libc.x86.brillo.map b/libc/libc.x86.brillo.map
index 3dcf1ea..7ac0976 100644
--- a/libc/libc.x86.brillo.map
+++ b/libc/libc.x86.brillo.map
@@ -624,6 +624,10 @@
     madvise;
     mallinfo;
     malloc;
+    malloc_backtrace;
+    malloc_disable;
+    malloc_enable;
+    malloc_iterate;
     malloc_info;
     malloc_usable_size;
     mbrlen;
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 1e44e2a..11fb710 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -624,6 +624,10 @@
     madvise;
     mallinfo;
     malloc;
+    malloc_backtrace;
+    malloc_disable;
+    malloc_enable;
+    malloc_iterate;
     malloc_info;
     malloc_usable_size;
     mbrlen;
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index e1ccd4e..63c82e5 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -552,6 +552,10 @@
     madvise;
     mallinfo;
     malloc;
+    malloc_backtrace;
+    malloc_disable;
+    malloc_enable;
+    malloc_iterate;
     malloc_info;
     malloc_usable_size;
     mbrlen;
diff --git a/libc/malloc_debug/Android.mk b/libc/malloc_debug/Android.mk
index 3eb0790..fb36643 100644
--- a/libc/malloc_debug/Android.mk
+++ b/libc/malloc_debug/Android.mk
@@ -11,6 +11,38 @@
     TrackData.cpp \
 
 # ==============================================================
+# libc_malloc_debug_backtrace.a
+# ==============================================================
+# Used by libmemunreachable
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libc_malloc_debug_backtrace
+
+LOCAL_SRC_FILES := \
+    backtrace.cpp \
+    MapData.cpp \
+
+LOCAL_CXX_STL := libc++_static
+
+LOCAL_STATIC_LIBRARIES += \
+    libc_logging \
+
+LOCAL_C_INCLUDES += bionic/libc
+LOCAL_EXPORT_C_INCLUDE_DIRS += $(LOCAL_PATH)
+
+LOCAL_SANITIZE := never
+LOCAL_NATIVE_COVERAGE := false
+
+# -Wno-error=format-zero-length needed for gcc to compile.
+LOCAL_CFLAGS := \
+    -Wall \
+    -Werror \
+    -fno-stack-protector \
+    -Wno-error=format-zero-length \
+
+include $(BUILD_STATIC_LIBRARY)
+
+# ==============================================================
 # libc_malloc_debug.so
 # ==============================================================
 include $(CLEAR_VARS)
@@ -19,16 +51,17 @@
 
 LOCAL_SRC_FILES := \
     $(libc_malloc_debug_src_files) \
-    backtrace.cpp \
-    MapData.cpp \
 
-LOCAL_CXX_STL := none
+LOCAL_CXX_STL := libc++_static
 
 # Only need this for arm since libc++ uses its own unwind code that
 # doesn't mix with the other default unwind code.
 LOCAL_STATIC_LIBRARIES_arm := libunwind_llvm
 
-LOCAL_STATIC_LIBRARIES += libc++abi libc++_static libc_logging
+LOCAL_STATIC_LIBRARIES += \
+    libc_malloc_debug_backtrace \
+    libc_logging \
+
 LOCAL_LDFLAGS_32 := -Wl,--version-script,$(LOCAL_PATH)/exported32.map
 LOCAL_LDFLAGS_64 := -Wl,--version-script,$(LOCAL_PATH)/exported64.map
 LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
diff --git a/libc/malloc_debug/MapData.cpp b/libc/malloc_debug/MapData.cpp
index ce2b999..8e9c806 100644
--- a/libc/malloc_debug/MapData.cpp
+++ b/libc/malloc_debug/MapData.cpp
@@ -36,7 +36,6 @@
 
 #include <vector>
 
-#include "debug_disable.h"
 #include "MapData.h"
 
 // Format of /proc/<PID>/maps:
@@ -111,9 +110,7 @@
   }
 }
 
-bool MapData::Initialize() {
-  ScopedDisableDebugCalls disable;
-
+bool MapData::ReadMaps() {
   FILE* fp = fopen("/proc/self/maps", "re");
   if (fp == nullptr) {
     return false;
@@ -123,26 +120,22 @@
   while (fgets(buffer.data(), buffer.size(), fp) != nullptr) {
     MapEntry* entry = parse_line(buffer.data());
     if (entry == nullptr) {
+      fclose(fp);
       return false;
     }
-    entries_.push_back(entry);
+
+    auto it = entries_.find(entry);
+    if (it == entries_.end()) {
+      entries_.insert(entry);
+    } else {
+      delete entry;
+    }
   }
   fclose(fp);
   return true;
 }
 
-MapData* MapData::Create() {
-  MapData* maps = new MapData();
-  if (!maps->Initialize()) {
-    delete maps;
-    return nullptr;
-  }
-  return maps;
-}
-
 MapData::~MapData() {
-  ScopedDisableDebugCalls disable;
-
   for (auto* entry : entries_) {
     delete entry;
   }
@@ -151,19 +144,25 @@
 
 // Find the containing map info for the PC.
 const MapEntry* MapData::find(uintptr_t pc, uintptr_t* rel_pc) {
-  for (auto* entry : entries_) {
-    if ((pc >= entry->start) && (pc < entry->end)) {
-      if (!entry->load_base_read) {
-        read_loadbase(entry);
-      }
-      if (rel_pc) {
-        *rel_pc = pc - entry->start + entry->load_base;
-      }
-      return entry;
-    }
+  MapEntry pc_entry(pc);
+
+  std::lock_guard<std::mutex> lock(m_);
+
+  auto it = entries_.find(&pc_entry);
+  if (it == entries_.end()) {
+    ReadMaps();
+  }
+  it = entries_.find(&pc_entry);
+  if (it == entries_.end()) {
+    return nullptr;
+  }
+
+  MapEntry *entry = *it;
+  if (!entry->load_base_read) {
+    read_loadbase(entry);
   }
   if (rel_pc) {
-    *rel_pc = pc;
+    *rel_pc = pc - entry->start + entry->load_base;
   }
-  return nullptr;
+  return entry;
 }
diff --git a/libc/malloc_debug/MapData.h b/libc/malloc_debug/MapData.h
index d5f315ab..0238139 100644
--- a/libc/malloc_debug/MapData.h
+++ b/libc/malloc_debug/MapData.h
@@ -31,8 +31,9 @@
 
 #include <sys/cdefs.h>
 
+#include <mutex>
 #include <string>
-#include <vector>
+#include <set>
 
 #include <private/bionic_macros.h>
 
@@ -40,6 +41,8 @@
   MapEntry(uintptr_t start, uintptr_t end, uintptr_t offset, const char* name, size_t name_len)
       : start(start), end(end), offset(offset), name(name, name_len) {}
 
+  MapEntry(uintptr_t pc) : start(pc), end(pc) {}
+
   uintptr_t start;
   uintptr_t end;
   uintptr_t offset;
@@ -48,18 +51,26 @@
   std::string name;
 };
 
+
+// Ordering comparator that returns equivalence for overlapping entries
+struct compare_entries {
+  bool operator()(const MapEntry* a, const MapEntry* b) const {
+    return a->end <= b->start;
+  }
+};
+
 class MapData {
  public:
-  static MapData* Create();
+  MapData() = default;
   ~MapData();
 
   const MapEntry* find(uintptr_t pc, uintptr_t* rel_pc = nullptr);
 
  private:
-  MapData() = default;
-  bool Initialize();
+  bool ReadMaps();
 
-  std::vector<MapEntry*> entries_;
+  std::mutex m_;
+  std::set<MapEntry*, compare_entries> entries_;
 
   DISALLOW_COPY_AND_ASSIGN(MapData);
 };
diff --git a/libc/malloc_debug/TrackData.cpp b/libc/malloc_debug/TrackData.cpp
index e5260a9..c9828d0 100644
--- a/libc/malloc_debug/TrackData.cpp
+++ b/libc/malloc_debug/TrackData.cpp
@@ -44,7 +44,7 @@
 #include "malloc_debug.h"
 #include "TrackData.h"
 
-void TrackData::GetList(std::vector<Header*>* list) {
+void TrackData::GetList(std::vector<const Header*>* list) {
   ScopedDisableDebugCalls disable;
 
   for (const auto& header : headers_) {
@@ -52,13 +52,13 @@
   }
 
   // Sort by the size of the allocation.
-  std::sort(list->begin(), list->end(), [](Header* a, Header* b) {
+  std::sort(list->begin(), list->end(), [](const Header* a, const Header* b) {
     if (a->size == b->size) return a < b;
     return a->size > b->size;
   });
 }
 
-void TrackData::Add(Header* header, bool backtrace_found) {
+void TrackData::Add(const Header* header, bool backtrace_found) {
   ScopedDisableDebugCalls disable;
 
   pthread_mutex_lock(&mutex_);
@@ -69,7 +69,7 @@
   pthread_mutex_unlock(&mutex_);
 }
 
-void TrackData::Remove(Header* header, bool backtrace_found) {
+void TrackData::Remove(const Header* header, bool backtrace_found) {
   ScopedDisableDebugCalls disable;
 
   pthread_mutex_lock(&mutex_);
@@ -80,10 +80,19 @@
   pthread_mutex_unlock(&mutex_);
 }
 
+bool TrackData::Contains(const Header* header) {
+  ScopedDisableDebugCalls disable;
+
+  pthread_mutex_lock(&mutex_);
+  bool found = headers_.count(header);
+  pthread_mutex_unlock(&mutex_);
+  return found;
+}
+
 void TrackData::DisplayLeaks(DebugData& debug) {
   ScopedDisableDebugCalls disable;
 
-  std::vector<Header*> list;
+  std::vector<const Header*> list;
   GetList(&list);
 
   size_t track_count = 0;
@@ -117,7 +126,7 @@
   }
   *overall_size = *info_size * total_backtrace_allocs_;
 
-  std::vector<Header*> list;
+  std::vector<const Header*> list;
   GetList(&list);
 
   uint8_t* data = *info;
diff --git a/libc/malloc_debug/TrackData.h b/libc/malloc_debug/TrackData.h
index dcf0ede..1234316 100644
--- a/libc/malloc_debug/TrackData.h
+++ b/libc/malloc_debug/TrackData.h
@@ -47,11 +47,13 @@
   TrackData() = default;
   virtual ~TrackData() = default;
 
-  void GetList(std::vector<Header*>* list);
+  void GetList(std::vector<const Header*>* list);
 
-  void Add(Header* header, bool backtrace_found);
+  void Add(const Header* header, bool backtrace_found);
 
-  void Remove(Header* header, bool backtrace_found);
+  void Remove(const Header* header, bool backtrace_found);
+
+  bool Contains(const Header *header);
 
   void GetInfo(DebugData& debug, uint8_t** info, size_t* overall_size,
                size_t* info_size, size_t* total_memory, size_t* backtrace_size);
@@ -64,7 +66,7 @@
 
  private:
   pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
-  std::unordered_set<Header*> headers_;
+  std::unordered_set<const Header*> headers_;
   size_t total_backtrace_allocs_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(TrackData);
diff --git a/libc/malloc_debug/backtrace.cpp b/libc/malloc_debug/backtrace.cpp
index 8549dc4..18ce8b8 100644
--- a/libc/malloc_debug/backtrace.cpp
+++ b/libc/malloc_debug/backtrace.cpp
@@ -37,7 +37,6 @@
 #include <unwind.h>
 
 #include "backtrace.h"
-#include "debug_disable.h"
 #include "debug_log.h"
 #include "MapData.h"
 
@@ -51,7 +50,7 @@
 
 extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
 
-static MapData* g_map_data = nullptr;
+static MapData g_map_data;
 static const MapEntry* g_current_code_map = nullptr;
 
 static _Unwind_Reason_Code find_current_map(__unwind_context* context, void*) {
@@ -60,24 +59,15 @@
   if (ip == 0) {
     return _URC_END_OF_STACK;
   }
-  g_current_code_map = g_map_data->find(ip);
+  g_current_code_map = g_map_data.find(ip);
   return _URC_END_OF_STACK;
 }
 
 void backtrace_startup() {
-  ScopedDisableDebugCalls disable;
-
-  g_map_data = MapData::Create();
-  if (g_map_data) {
-    _Unwind_Backtrace(find_current_map, nullptr);
-  }
+  _Unwind_Backtrace(find_current_map, nullptr);
 }
 
 void backtrace_shutdown() {
-  ScopedDisableDebugCalls disable;
-
-  delete g_map_data;
-  g_map_data = nullptr;
 }
 
 struct stack_crawl_state_t {
@@ -136,15 +126,13 @@
 }
 
 size_t backtrace_get(uintptr_t* frames, size_t frame_count) {
-  ScopedDisableDebugCalls disable;
-
   stack_crawl_state_t state(frames, frame_count);
   _Unwind_Backtrace(trace_function, &state);
   return state.cur_frame;
 }
 
-void backtrace_log(const uintptr_t* frames, size_t frame_count) {
-  ScopedDisableDebugCalls disable;
+std::string backtrace_string(const uintptr_t* frames, size_t frame_count) {
+  std::string str;
 
   for (size_t frame_num = 0; frame_num < frame_count; frame_num++) {
     uintptr_t offset = 0;
@@ -157,23 +145,31 @@
     }
 
     uintptr_t rel_pc = offset;
-    const MapEntry* entry = nullptr;
-    if (g_map_data) {
-      entry = g_map_data->find(frames[frame_num], &rel_pc);
-    }
+    const MapEntry* entry = g_map_data.find(frames[frame_num], &rel_pc);
+
     const char* soname = (entry != nullptr) ? entry->name.c_str() : info.dli_fname;
     if (soname == nullptr) {
       soname = "<unknown>";
     }
+    char buf[1024];
     if (symbol != nullptr) {
       char* demangled_symbol = __cxa_demangle(symbol, nullptr, nullptr, nullptr);
       const char* best_name = (demangled_symbol != nullptr) ? demangled_symbol : symbol;
 
-      error_log("          #%02zd  pc %" PAD_PTR "  %s (%s+%" PRIuPTR ")",
-                frame_num, rel_pc, soname, best_name, frames[frame_num] - offset);
+      __libc_format_buffer(buf, sizeof(buf),
+          "          #%02zd  pc %" PAD_PTR "  %s (%s+%" PRIuPTR ")\n", frame_num,
+          rel_pc, soname, best_name, frames[frame_num] - offset);
       free(demangled_symbol);
     } else {
-      error_log("          #%02zd  pc %" PAD_PTR "  %s", frame_num, rel_pc, soname);
+      __libc_format_buffer(buf, sizeof(buf),
+          "          #%02zd  pc %" PAD_PTR "  %s\n", frame_num, rel_pc, soname);
     }
+    str += buf;
   }
+
+  return str;
+}
+
+void backtrace_log(const uintptr_t* frames, size_t frame_count) {
+  error_log_string(backtrace_string(frames, frame_count).c_str());
 }
diff --git a/libc/malloc_debug/backtrace.h b/libc/malloc_debug/backtrace.h
index 9e01009..f570873 100644
--- a/libc/malloc_debug/backtrace.h
+++ b/libc/malloc_debug/backtrace.h
@@ -32,9 +32,12 @@
 #include <stdint.h>
 #include <sys/cdefs.h>
 
+#include <string>
+
 void backtrace_startup();
 void backtrace_shutdown();
 size_t backtrace_get(uintptr_t* frames, size_t frame_count);
 void backtrace_log(const uintptr_t* frames, size_t frame_count);
+std::string backtrace_string(const uintptr_t* frames, size_t frame_count);
 
 #endif // MALLOC_DEBUG_BACKTRACE_H
diff --git a/libc/malloc_debug/debug_log.h b/libc/malloc_debug/debug_log.h
index 67f584d..4df0408 100644
--- a/libc/malloc_debug/debug_log.h
+++ b/libc/malloc_debug/debug_log.h
@@ -38,6 +38,8 @@
     __libc_format_log(ANDROID_LOG_DEBUG, "malloc_debug", (format), ##__VA_ARGS__ )
 #define error_log(format, ...)  \
     __libc_format_log(ANDROID_LOG_ERROR, "malloc_debug", (format), ##__VA_ARGS__ )
+#define error_log_string(str)  \
+    __libc_write_log(ANDROID_LOG_ERROR, "malloc_debug", (str))
 #define info_log(format, ...)  \
     __libc_format_log(ANDROID_LOG_INFO, "malloc_debug", (format), ##__VA_ARGS__ )
 
diff --git a/libc/malloc_debug/exported32.map b/libc/malloc_debug/exported32.map
index aef8818..a985ef9 100644
--- a/libc/malloc_debug/exported32.map
+++ b/libc/malloc_debug/exported32.map
@@ -6,8 +6,12 @@
     debug_free_malloc_leak_info;
     debug_get_malloc_leak_info;
     debug_initialize;
+    debug_iterate;
     debug_mallinfo;
     debug_malloc;
+    debug_malloc_backtrace;
+    debug_malloc_disable;
+    debug_malloc_enable;
     debug_malloc_usable_size;
     debug_memalign;
     debug_posix_memalign;
diff --git a/libc/malloc_debug/exported64.map b/libc/malloc_debug/exported64.map
index 70fd581..1a6b30f 100644
--- a/libc/malloc_debug/exported64.map
+++ b/libc/malloc_debug/exported64.map
@@ -6,8 +6,12 @@
     debug_free_malloc_leak_info;
     debug_get_malloc_leak_info;
     debug_initialize;
+    debug_iterate;
     debug_mallinfo;
     debug_malloc;
+    debug_malloc_backtrace;
+    debug_malloc_disable;
+    debug_malloc_enable;
     debug_malloc_usable_size;
     debug_memalign;
     debug_posix_memalign;
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index dcc6048..aa6f5f7 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -67,6 +67,7 @@
 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);
+ssize_t debug_malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
 void debug_free_malloc_leak_info(uint8_t* info);
 size_t debug_malloc_usable_size(void* pointer);
 void* debug_malloc(size_t size);
@@ -76,6 +77,10 @@
 void* debug_calloc(size_t nmemb, size_t bytes);
 struct mallinfo debug_mallinfo();
 int debug_posix_memalign(void** memptr, size_t alignment, size_t size);
+int debug_iterate(uintptr_t base, size_t size,
+    void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+void debug_malloc_disable();
+void debug_malloc_enable();
 
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
 void* debug_pvalloc(size_t bytes);
@@ -159,6 +164,7 @@
   if (g_debug->config().options & BACKTRACE) {
     BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
     if (g_debug->backtrace->enabled()) {
+      ScopedDisableDebugCalls disable;
       back_header->num_frames = backtrace_get(
           &back_header->frames[0], g_debug->config().backtrace_frames);
       backtrace_found = back_header->num_frames > 0;
@@ -217,10 +223,10 @@
     g_debug->track->DisplayLeaks(*g_debug);
   }
 
-  backtrace_shutdown();
-
   DebugDisableSet(true);
 
+  backtrace_shutdown();
+
   delete g_debug;
   g_debug = nullptr;
 
@@ -569,6 +575,82 @@
   return (*memptr != nullptr) ? 0 : ENOMEM;
 }
 
+int debug_iterate(uintptr_t base, size_t size,
+    void (*callback)(uintptr_t base, size_t size, void* arg), void* arg) {
+  // Can't allocate, malloc is disabled
+  // Manual capture of the arguments to pass to the lambda below as void* arg
+  struct iterate_ctx {
+    decltype(callback) callback;
+    decltype(arg) arg;
+  } ctx = { callback, arg };
+
+  return g_dispatch->iterate(base, size,
+      [](uintptr_t base, size_t size, void* arg) {
+        const iterate_ctx* ctx = reinterpret_cast<iterate_ctx*>(arg);
+        const void* pointer = reinterpret_cast<void*>(base);
+        if (g_debug->need_header()) {
+          const Header* header = reinterpret_cast<const Header*>(pointer);
+          if (g_debug->config().options & TRACK_ALLOCS) {
+            if (g_debug->track->Contains(header)) {
+              // Return just the body of the allocation if we're sure the header exists
+              ctx->callback(reinterpret_cast<uintptr_t>(g_debug->GetPointer(header)),
+                  header->real_size(), ctx->arg);
+              return;
+            }
+          }
+        }
+        // Fall back to returning the whole allocation
+        ctx->callback(base, size, ctx->arg);
+      }, &ctx);
+}
+
+void debug_malloc_disable() {
+  g_dispatch->malloc_disable();
+  if (g_debug->track) {
+    g_debug->track->PrepareFork();
+  }
+}
+
+void debug_malloc_enable() {
+  if (g_debug->track) {
+    g_debug->track->PostForkParent();
+  }
+  g_dispatch->malloc_enable();
+}
+
+ssize_t debug_malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count) {
+  if (DebugCallsDisabled() || pointer == nullptr) {
+    return 0;
+  }
+
+  if (g_debug->need_header()) {
+    Header* header;
+    if (g_debug->config().options & TRACK_ALLOCS) {
+      header = g_debug->GetHeader(pointer);
+      if (!g_debug->track->Contains(header)) {
+        return 0;
+      }
+    } else {
+      header = reinterpret_cast<Header*>(pointer);
+    }
+    if (header->tag != DEBUG_TAG) {
+      return 0;
+    }
+    if (g_debug->config().options & BACKTRACE) {
+      BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
+      if (back_header->num_frames > 0) {
+        if (frame_count > back_header->num_frames) {
+          frame_count = back_header->num_frames;
+        }
+        memcpy(frames, &back_header->frames[0], frame_count * sizeof(uintptr_t));
+        return frame_count;
+      }
+    }
+  }
+
+  return 0;
+}
+
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
 void* debug_pvalloc(size_t bytes) {
   if (DebugCallsDisabled()) {
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index b316e8a..28729e8 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -121,6 +121,9 @@
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
   nullptr,
 #endif
+  nullptr,
+  nullptr,
+  nullptr,
 };
 
 void VerifyAllocCalls() {
diff --git a/libc/private/bionic_malloc_dispatch.h b/libc/private/bionic_malloc_dispatch.h
index 5dcd37a..02a092f 100644
--- a/libc/private/bionic_malloc_dispatch.h
+++ b/libc/private/bionic_malloc_dispatch.h
@@ -30,6 +30,7 @@
 #define _PRIVATE_BIONIC_MALLOC_DISPATCH_H
 
 #include <stddef.h>
+#include <stdint.h>
 #include <private/bionic_config.h>
 
 // Entry in malloc dispatch table.
@@ -41,6 +42,10 @@
 typedef void* (*MallocMemalign)(size_t, size_t);
 typedef int (*MallocPosixMemalign)(void**, size_t, size_t);
 typedef void* (*MallocRealloc)(void*, size_t);
+typedef int (*MallocIterate)(uintptr_t, size_t, void (*)(uintptr_t, size_t, void*), void*);
+typedef void (*MallocMallocDisable)();
+typedef void (*MallocMallocEnable)();
+
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
 typedef void* (*MallocPvalloc)(size_t);
 typedef void* (*MallocValloc)(size_t);
@@ -61,6 +66,9 @@
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
   MallocValloc valloc;
 #endif
+  MallocIterate iterate;
+  MallocMallocDisable malloc_disable;
+  MallocMallocEnable malloc_enable;
 } __attribute__((aligned(32)));
 
 #endif
diff --git a/libc/private/libc_logging.h b/libc/private/libc_logging.h
index da2192b..e389565 100644
--- a/libc/private/libc_logging.h
+++ b/libc/private/libc_logging.h
@@ -101,6 +101,8 @@
 __LIBC_HIDDEN__ int __libc_format_log_va_list(int priority, const char* tag, const char* format,
                                               va_list ap);
 
+__LIBC_HIDDEN__ int __libc_write_log(int priority, const char* tag, const char* msg);
+
 //
 // Event logging.
 //