Malloc debug rewrite.

The major components of the rewrite:

- Completely remove the qemu shared library code. Nobody was using it
  and it appears to have broken at some point.
- Adds the ability to enable/disable different options independently.
- Adds a new option that can enable the backtrace on alloc/free when
  a process gets a specific signal.
- Adds a new way to enable malloc debug. If a special property is
  set, and the process has an environment variable set, then debug
  malloc will be enabled. This allows something that might be
  a derivative of app_process to be started with an environment variable
  being enabled.
- get_malloc_leak_info() used to return one element for each pointer that
  had the exact same backtrace. The new version returns information for
  every one of the pointers with same backtrace. It turns out ddms already
  automatically coalesces these, so the old method simply hid the fact
  that there where multiple pointers with the same amount of backtrace.
- Moved all of the malloc debug specific code into the library.
  Nothing related to the malloc debug data structures remains in libc.
- Removed the calls to the debug malloc cleanup routine. Instead, I
  added an atexit call with the debug malloc cleanup routine. This gets
  around most problems related to the timing of doing the cleanup.

The new properties and environment variables:

libc.debug.malloc.options
  Set by option name (such as "backtrace"). Setting this to a bad value
  will cause a usage statement to be printed to the log.

libc.debug.malloc.program
  Same as before. If this is set, then only the program named will
  be launched with malloc debug enabled. This is not a complete match,
  but if any part of the property is in the program name, malloc debug is
  enabled.

libc.debug.malloc.env_enabled
  If set, then malloc debug is only enabled if the running process has the
  environment variable LIBC_DEBUG_MALLOC_ENABLE set.

Bug: 19145921

Change-Id: I7b0e58cc85cc6d4118173fe1f8627a391b64c0d7
diff --git a/README.md b/README.md
index 6739926..c6b9278 100644
--- a/README.md
+++ b/README.md
@@ -118,6 +118,10 @@
     # current upstream source in one of the upstream directories or by
     # switching the file to C++ and cleaning it up.
 
+  malloc_debug/
+    # The code that implements the functionality to enable debugging of
+    # native allocation problems.
+
   stdio/
     # These are legacy files of dubious provenance. We're working to clean
     # this mess up, and this directory should disappear.
diff --git a/libc/Android.mk b/libc/Android.mk
index 801202b..0923939 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -1181,7 +1181,7 @@
     $(libc_common_src_files) \
     $(libc_arch_dynamic_src_files) \
     $(libc_ndk_stub_src_files) \
-    bionic/malloc_debug_common.cpp \
+    bionic/malloc_common.cpp \
 
 LOCAL_SRC_FILES_arm += \
     arch-common/bionic/crtbegin_so.c \
@@ -1326,7 +1326,8 @@
 
 LOCAL_SRC_FILES := \
     $(libc_arch_static_src_files) \
-    bionic/malloc_debug_common.cpp \
+    bionic/malloc_common.cpp \
+    bionic/libc_init_static.cpp \
 
 LOCAL_CFLAGS := $(libc_common_cflags) \
     -DLIBC_STATIC \
@@ -1363,7 +1364,7 @@
     arch-common/bionic/crtbegin_so.c \
     arch-common/bionic/crtbrand.S \
     $(libc_arch_dynamic_src_files) \
-    bionic/malloc_debug_common.cpp \
+    bionic/malloc_common.cpp \
     bionic/libc_init_dynamic.cpp \
     bionic/NetdClient.cpp \
     arch-common/bionic/crtend_so.S \
@@ -1448,111 +1449,30 @@
 
 include $(BUILD_SHARED_LIBRARY)
 
-
-# For all builds, except for the -user build we will enable memory
-# allocation checking (including memory leaks, buffer overwrites, etc.)
-# Note that all these checks are also controlled by env. settings
-# that can enable, or disable specific checks. Note also that some of
-# the checks are available only in emulator and are implemeted in
-# libc_malloc_qemu_instrumented.so.
-ifneq ($(TARGET_BUILD_VARIANT),user)
-
 # ========================================================
-# libc_malloc_debug_leak.so
+# libc_logging.a
 # ========================================================
 include $(CLEAR_VARS)
 
 LOCAL_CFLAGS := $(libc_common_cflags)
 LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
 LOCAL_CPPFLAGS := $(libc_common_cppflags)
-
-# Make sure that unwind.h comes from libunwind.
-LOCAL_C_INCLUDES := \
-    $(libc_common_c_includes) \
-
-LOCAL_SRC_FILES := \
-    bionic/debug_backtrace.cpp \
-    bionic/debug_mapinfo.cpp \
-    bionic/libc_logging.cpp \
-    bionic/malloc_debug_leak.cpp \
-    bionic/malloc_debug_check.cpp \
-
-LOCAL_MODULE := libc_malloc_debug_leak
-LOCAL_CLANG := $(use_clang)
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-    $(libc_common_additional_dependencies) \
-    $(LOCAL_PATH)/version_script.txt \
-
-LOCAL_SHARED_LIBRARIES := libc libdl
-LOCAL_CXX_STL := none
-LOCAL_SYSTEM_SHARED_LIBRARIES :=
-# 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_LDFLAGS_arm := -Wl,--exclude-libs,libunwind_llvm.a
-LOCAL_STATIC_LIBRARIES += libc++abi
-LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
-
-# Don't re-export new/delete and friends, even if the compiler really wants to.
-LOCAL_LDFLAGS := -Wl,--version-script,$(LOCAL_PATH)/version_script.txt
-
-# Unfortunately --exclude-libs clobbers our version script, so we have to
-# prevent the build system from using this flag.
-LOCAL_NO_EXCLUDE_LIBS := true
-
-# Don't install on release build
-LOCAL_MODULE_TAGS := eng debug
-LOCAL_SANITIZE := never
-LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
-
-$(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
-include $(BUILD_SHARED_LIBRARY)
-
-
-# ========================================================
-# libc_malloc_debug_qemu.so
-# ========================================================
-include $(CLEAR_VARS)
-
-LOCAL_CFLAGS := \
-    $(libc_common_cflags) \
-    -DMALLOC_QEMU_INSTRUMENT \
-
-LOCAL_CONLYFLAGS := $(libc_common_conlyflags)
-LOCAL_CPPFLAGS := $(libc_common_cppflags)
-
 LOCAL_C_INCLUDES := $(libc_common_c_includes)
 
 LOCAL_SRC_FILES := \
     bionic/libc_logging.cpp \
-    bionic/malloc_debug_qemu.cpp \
 
-LOCAL_MODULE := libc_malloc_debug_qemu
+LOCAL_MODULE := libc_logging
+
 LOCAL_CLANG := $(use_clang)
-LOCAL_ADDITIONAL_DEPENDENCIES := \
-    $(libc_common_additional_dependencies) \
-    $(LOCAL_PATH)/version_script.txt \
-
-LOCAL_SHARED_LIBRARIES := libc libdl
-LOCAL_CXX_STL := none
+LOCAL_ADDITIONAL_DEPENDENCIES := $(libc_common_additional_dependencies)
 LOCAL_SYSTEM_SHARED_LIBRARIES :=
 
-# Don't re-export new/delete and friends, even if the compiler really wants to.
-LOCAL_LDFLAGS := -Wl,--version-script,$(LOCAL_PATH)/version_script.txt
-
-# Unfortunately --exclude-libs clobbers our version script, so we have to
-# prevent the build system from using this flag.
-LOCAL_NO_EXCLUDE_LIBS := true
-
-# Don't install on release build
-LOCAL_MODULE_TAGS := eng debug
 LOCAL_SANITIZE := never
 LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
 
 $(eval $(call patch-up-arch-specific-flags,LOCAL_CFLAGS,libc_common_cflags))
-include $(BUILD_SHARED_LIBRARY)
-
-endif  #!user
+include $(BUILD_STATIC_LIBRARY)
 
 # ========================================================
 # libstdc++.so
@@ -1561,7 +1481,6 @@
     bionic/__cxa_guard.cpp \
     bionic/__cxa_pure_virtual.cpp \
     bionic/new.cpp \
-    bionic/libc_logging.cpp \
 
 include $(CLEAR_VARS)
 LOCAL_C_INCLUDES := $(libc_common_c_includes) bionic/libstdc++/include
@@ -1577,6 +1496,7 @@
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 LOCAL_CXX_STL := none
 LOCAL_SYSTEM_SHARED_LIBRARIES := libc
+LOCAL_STATIC_LIBRARIES := libc_logging
 LOCAL_SANITIZE := never
 LOCAL_NATIVE_COVERAGE := $(bionic_coverage)
 include $(BUILD_SHARED_LIBRARY)
diff --git a/libc/bionic/debug_mapinfo.cpp b/libc/bionic/debug_mapinfo.cpp
deleted file mode 100644
index 6fb8ebe..0000000
--- a/libc/bionic/debug_mapinfo.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#include <ctype.h>
-#include <elf.h>
-#include <inttypes.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "debug_mapinfo.h"
-#include "malloc_debug_disable.h"
-
-#if defined(__LP64__)
-#define Elf_W(x) Elf64_##x
-#else
-#define Elf_W(x) Elf32_##x
-#endif
-
-// Format of /proc/<PID>/maps:
-//   6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
-static mapinfo_t* parse_maps_line(char* line) {
-  uintptr_t start;
-  uintptr_t end;
-  uintptr_t offset;
-  char permissions[4];
-  int name_pos;
-  if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n", &start,
-             &end, permissions, &offset, &name_pos) < 2) {
-    return NULL;
-  }
-
-  const char* name = line + name_pos;
-  size_t name_len = strlen(name);
-  if (name_len && name[name_len - 1] == '\n') {
-    name_len -= 1;
-  }
-
-  mapinfo_t* mi = reinterpret_cast<mapinfo_t*>(calloc(1, sizeof(mapinfo_t) + name_len + 1));
-  if (mi) {
-    mi->start = start;
-    mi->end = end;
-    mi->offset = offset;
-    if (permissions[0] != 'r') {
-      // Any unreadable map will just get a zero load base.
-      mi->load_base = 0;
-      mi->load_base_read = true;
-    } else {
-      mi->load_base_read = false;
-    }
-    memcpy(mi->name, name, name_len);
-    mi->name[name_len] = '\0';
-  }
-  return mi;
-}
-
-__LIBC_HIDDEN__ mapinfo_t* mapinfo_create(pid_t pid) {
-  ScopedDisableDebugCalls disable;
-
-  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);
-  FILE* fp = fopen(data, "re");
-  if (fp != NULL) {
-    while (fgets(data, sizeof(data), fp) != NULL) {
-      mapinfo_t* mi = parse_maps_line(data);
-      if (mi) {
-        mi->next = milist;
-        milist = mi;
-      }
-    }
-    fclose(fp);
-  }
-  return milist;
-}
-
-__LIBC_HIDDEN__ void mapinfo_destroy(mapinfo_t* mi) {
-  ScopedDisableDebugCalls disable;
-
-  while (mi != NULL) {
-    mapinfo_t* del = mi;
-    mi = mi->next;
-    free(del);
-  }
-}
-
-template<typename T>
-static inline bool get_val(mapinfo_t* mi, uintptr_t addr, T* store) {
-  if (addr < mi->start || addr + sizeof(T) > mi->end) {
-    return false;
-  }
-  // Make sure the address is aligned properly.
-  if (addr & (sizeof(T)-1)) {
-    return false;
-  }
-  *store = *reinterpret_cast<T*>(addr);
-  return true;
-}
-
-__LIBC_HIDDEN__ void mapinfo_read_loadbase(mapinfo_t* mi) {
-  mi->load_base = 0;
-  mi->load_base_read = true;
-  uintptr_t addr = mi->start;
-  Elf_W(Ehdr) ehdr;
-  if (!get_val<Elf_W(Half)>(mi, addr + offsetof(Elf_W(Ehdr), e_phnum), &ehdr.e_phnum)) {
-    return;
-  }
-  if (!get_val<Elf_W(Off)>(mi, addr + offsetof(Elf_W(Ehdr), e_phoff), &ehdr.e_phoff)) {
-    return;
-  }
-  addr += ehdr.e_phoff;
-  for (size_t i = 0; i < ehdr.e_phnum; i++) {
-    Elf_W(Phdr) phdr;
-    if (!get_val<Elf_W(Word)>(mi, addr + offsetof(Elf_W(Phdr), p_type), &phdr.p_type)) {
-      return;
-    }
-    if (!get_val<Elf_W(Off)>(mi, addr + offsetof(Elf_W(Phdr), p_offset), &phdr.p_offset)) {
-      return;
-    }
-    if (phdr.p_type == PT_LOAD && phdr.p_offset == mi->offset) {
-      if (!get_val<Elf_W(Addr)>(mi, addr + offsetof(Elf_W(Phdr), p_vaddr), &phdr.p_vaddr)) {
-        return;
-      }
-      mi->load_base = phdr.p_vaddr;
-      return;
-    }
-    addr += sizeof(phdr);
-  }
-}
-
-// Find the containing map info for the 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)) {
-      if (!mi->load_base_read) {
-        mapinfo_read_loadbase(mi);
-      }
-      *rel_pc = pc - mi->start + mi->load_base;
-      return mi;
-    }
-  }
-  *rel_pc = pc;
-  return NULL;
-}
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 532dab9..ebd1595 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -340,13 +340,4 @@
 
     dtor();
   }
-
-#ifndef LIBC_STATIC
-  {
-    extern void __libc_postfini(void) __attribute__((weak));
-    if (__libc_postfini) {
-      __libc_postfini();
-    }
-  }
-#endif
 }
diff --git a/libc/bionic/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp
index 97d9e39..c34daef 100644
--- a/libc/bionic/libc_init_dynamic.cpp
+++ b/libc/bionic/libc_init_dynamic.cpp
@@ -56,7 +56,6 @@
 #include "private/KernelArgumentBlock.h"
 
 extern "C" {
-  extern void malloc_debug_fini(void);
   extern void netdClientInit(void);
   extern int __cxa_atexit(void (*)(void *), void *, void *);
 };
@@ -83,11 +82,6 @@
   netdClientInit();
 }
 
-__LIBC_HIDDEN__ void __libc_postfini() {
-  // A hook for the debug malloc library to let it know that we're shutting down.
-  malloc_debug_fini();
-}
-
 // This function is called from the executable's _start entry point
 // (see arch-$ARCH/bionic/crtbegin_dynamic.S), which is itself
 // called by the dynamic linker after it has loaded all shared
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
new file mode 100644
index 0000000..56f1c10
--- /dev/null
+++ b/libc/bionic/malloc_common.cpp
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+// Contains a thin layer that calls whatever real native allocator
+// has been defined. For the libc shared library, this allows the
+// implementation of a debug malloc that can intercept all of the allocation
+// calls and add special debugging code to attempt to catch allocation
+// errors. All of the debugging code is implemented in a separate shared
+// library that is only loaded when the property "libc.debug.malloc.options"
+// is set to a non-zero value. There are two functions exported to
+// allow ddms, or other external users to get information from the debug
+// allocation.
+//   get_malloc_leak_info: Returns information about all of the known native
+//                         allocations that are currently in use.
+//   free_malloc_leak_info: Frees the data allocated by the call to
+//                          get_malloc_leak_info.
+
+#include <private/bionic_config.h>
+#include <private/bionic_globals.h>
+#include <private/bionic_malloc_dispatch.h>
+
+#include "jemalloc.h"
+#define Malloc(function)  je_ ## function
+
+static constexpr MallocDispatch __libc_malloc_default_dispatch
+  __attribute__((unused)) = {
+    Malloc(calloc),
+    Malloc(free),
+    Malloc(mallinfo),
+    Malloc(malloc),
+    Malloc(malloc_usable_size),
+    Malloc(memalign),
+    Malloc(posix_memalign),
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+    Malloc(pvalloc),
+#endif
+    Malloc(realloc),
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+    Malloc(valloc),
+#endif
+  };
+
+// In a VM process, this is set to 1 after fork()ing out of zygote.
+int gMallocLeakZygoteChild = 0;
+
+// =============================================================================
+// Allocation functions
+// =============================================================================
+extern "C" void* calloc(size_t n_elements, size_t elem_size) {
+  auto _calloc = __libc_globals->malloc_dispatch.calloc;
+  if (__predict_false(_calloc != nullptr)) {
+    return _calloc(n_elements, elem_size);
+  }
+  return Malloc(calloc)(n_elements, elem_size);
+}
+
+extern "C" void free(void* mem) {
+  auto _free = __libc_globals->malloc_dispatch.free;
+  if (__predict_false(_free != nullptr)) {
+    _free(mem);
+  } else {
+    Malloc(free)(mem);
+  }
+}
+
+extern "C" struct mallinfo mallinfo() {
+  auto _mallinfo = __libc_globals->malloc_dispatch.mallinfo;
+  if (__predict_false(_mallinfo != nullptr)) {
+    return _mallinfo();
+  }
+  return Malloc(mallinfo)();
+}
+
+extern "C" void* malloc(size_t bytes) {
+  auto _malloc = __libc_globals->malloc_dispatch.malloc;
+  if (__predict_false(_malloc != nullptr)) {
+    return _malloc(bytes);
+  }
+  return Malloc(malloc)(bytes);
+}
+
+extern "C" size_t malloc_usable_size(const void* mem) {
+  auto _malloc_usable_size = __libc_globals->malloc_dispatch.malloc_usable_size;
+  if (__predict_false(_malloc_usable_size != nullptr)) {
+    return _malloc_usable_size(mem);
+  }
+  return Malloc(malloc_usable_size)(mem);
+}
+
+extern "C" void* memalign(size_t alignment, size_t bytes) {
+  auto _memalign = __libc_globals->malloc_dispatch.memalign;
+  if (__predict_false(_memalign != nullptr)) {
+    return _memalign(alignment, bytes);
+  }
+  return Malloc(memalign)(alignment, bytes);
+}
+
+extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size) {
+  auto _posix_memalign = __libc_globals->malloc_dispatch.posix_memalign;
+  if (__predict_false(_posix_memalign != nullptr)) {
+    return _posix_memalign(memptr, alignment, size);
+  }
+  return Malloc(posix_memalign)(memptr, alignment, size);
+}
+
+extern "C" void* realloc(void* old_mem, size_t bytes) {
+  auto _realloc = __libc_globals->malloc_dispatch.realloc;
+  if (__predict_false(_realloc != nullptr)) {
+    return _realloc(old_mem, bytes);
+  }
+  return Malloc(realloc)(old_mem, bytes);
+}
+
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+extern "C" void* pvalloc(size_t bytes) {
+  auto _pvalloc = __libc_globals->malloc_dispatch.pvalloc;
+  if (__predict_false(_pvalloc != nullptr)) {
+    return _pvalloc(bytes);
+  }
+  return Malloc(pvalloc)(bytes);
+}
+
+extern "C" void* valloc(size_t bytes) {
+  auto _valloc = __libc_globals->malloc_dispatch.valloc;
+  if (__predict_false(_valloc != nullptr)) {
+    return _valloc(bytes);
+  }
+  return Malloc(valloc)(bytes);
+}
+#endif
+
+// We implement malloc debugging only in libc.so, so the code below
+// must be excluded if we compile this file for static libc.a
+#if !defined(LIBC_STATIC)
+
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <private/libc_logging.h>
+#include <sys/system_properties.h>
+
+extern "C" int __cxa_atexit(void (*func)(void *), void *arg, void *dso);
+
+static const char* DEBUG_SHARED_LIB = "libc_malloc_debug.so";
+static const char* DEBUG_MALLOC_PROPERTY_OPTIONS = "libc.debug.malloc.options";
+static const char* DEBUG_MALLOC_PROPERTY_PROGRAM = "libc.debug.malloc.program";
+static const char* DEBUG_MALLOC_PROPERTY_ENV_ENABLED = "libc.debug.malloc.env_enabled";
+static const char* DEBUG_MALLOC_ENV_ENABLE = "LIBC_DEBUG_MALLOC_ENABLE";
+
+static void* libc_malloc_impl_handle = nullptr;
+
+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*);
+
+// =============================================================================
+// Log functions
+// =============================================================================
+#define error_log(format, ...)  \
+    __libc_format_log(ANDROID_LOG_ERROR, "libc", (format), ##__VA_ARGS__ )
+#define info_log(format, ...)  \
+    __libc_format_log(ANDROID_LOG_INFO, "libc", (format), ##__VA_ARGS__ )
+// =============================================================================
+
+// =============================================================================
+// Exported for use by ddms.
+// =============================================================================
+
+// Retrieve native heap information.
+//
+// "*info" is set to a buffer we allocate
+// "*overall_size" is set to the size of the "info" buffer
+// "*info_size" is set to the size of a single entry
+// "*total_memory" is set to the sum of all allocations we're tracking; does
+//   not include heap overhead
+// "*backtrace_size" is set to the maximum number of entries in the back trace
+extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overall_size,
+    size_t* info_size, size_t* total_memory, size_t* backtrace_size) {
+  if (g_debug_get_malloc_leak_info_func == nullptr) {
+    return;
+  }
+  g_debug_get_malloc_leak_info_func(info, overall_size, info_size, total_memory, backtrace_size);
+}
+
+extern "C" void free_malloc_leak_info(uint8_t* info) {
+  if (g_debug_free_malloc_leak_info_func == nullptr) {
+    return;
+  }
+  g_debug_free_malloc_leak_info_func(info);
+}
+// =============================================================================
+
+template<typename FunctionType>
+static bool InitMallocFunction(void* malloc_impl_handler, FunctionType* func, const char* prefix, const char* suffix) {
+  char symbol[128];
+  snprintf(symbol, sizeof(symbol), "%s_%s", prefix, suffix);
+  *func = reinterpret_cast<FunctionType>(dlsym(malloc_impl_handler, symbol));
+  if (*func == nullptr) {
+    error_log("%s: dlsym(\"%s\") failed", getprogname(), symbol);
+    return false;
+  }
+  return true;
+}
+
+static bool InitMalloc(void* malloc_impl_handler, MallocDispatch* table, const char* prefix) {
+  if (!InitMallocFunction<MallocCalloc>(malloc_impl_handler, &table->calloc,
+                                        prefix, "calloc")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocFree>(malloc_impl_handler, &table->free,
+                                      prefix, "free")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMallinfo>(malloc_impl_handler, &table->mallinfo,
+                                          prefix, "mallinfo")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMalloc>(malloc_impl_handler, &table->malloc,
+                                        prefix, "malloc")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMallocUsableSize>(
+      malloc_impl_handler, &table->malloc_usable_size, prefix, "malloc_usable_size")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocMemalign>(malloc_impl_handler, &table->memalign,
+                                          prefix, "memalign")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocPosixMemalign>(malloc_impl_handler, &table->posix_memalign,
+                                               prefix, "posix_memalign")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocRealloc>(malloc_impl_handler, &table->realloc,
+                                         prefix, "realloc")) {
+    return false;
+  }
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+  if (!InitMallocFunction<MallocPvalloc>(malloc_impl_handler, &table->pvalloc,
+                                         prefix, "pvalloc")) {
+    return false;
+  }
+  if (!InitMallocFunction<MallocValloc>(malloc_impl_handler, &table->valloc,
+                                        prefix, "valloc")) {
+    return false;
+  }
+#endif
+
+  return true;
+}
+
+static void malloc_fini_impl(void*) {
+  // Our BSD stdio implementation doesn't close the standard streams,
+  // it only flushes them. Other unclosed FILE*s will show up as
+  // malloc leaks, but to avoid the standard streams showing up in
+  // leak reports, close them here.
+  fclose(stdin);
+  fclose(stdout);
+  fclose(stderr);
+
+  g_debug_finalize_func();
+}
+
+// Initializes memory allocation framework once per process.
+static void malloc_init_impl(libc_globals* globals) {
+  char value[PROP_VALUE_MAX];
+  if (__system_property_get(DEBUG_MALLOC_PROPERTY_OPTIONS, value) == 0 || value[0] == '\0') {
+    return;
+  }
+
+  // Check to see if only a specific program should have debug malloc enabled.
+  if (__system_property_get(DEBUG_MALLOC_PROPERTY_PROGRAM, value) != 0 &&
+      strstr(getprogname(), value) == nullptr) {
+    return;
+  }
+
+  // Check for the special environment variable instead.
+  if (__system_property_get(DEBUG_MALLOC_PROPERTY_ENV_ENABLED, value) != 0
+      && value[0] != '\0' && getenv(DEBUG_MALLOC_ENV_ENABLE) == nullptr) {
+    return;
+  }
+
+  // Load the debug malloc shared library.
+  void* malloc_impl_handle = dlopen(DEBUG_SHARED_LIB, RTLD_NOW | RTLD_LOCAL);
+  if (malloc_impl_handle == nullptr) {
+    error_log("%s: Unable to open debug malloc shared library %s: %s",
+              getprogname(), DEBUG_SHARED_LIB, dlerror());
+    return;
+  }
+
+  // Initialize malloc debugging in the loaded module.
+  void* sym = dlsym(malloc_impl_handle, "debug_initialize");
+  auto init_func = reinterpret_cast<bool (*)(const MallocDispatch*, int*)>(sym);
+  if (init_func == nullptr) {
+    error_log("%s: debug_initialize routine not found in %s", getprogname(), DEBUG_SHARED_LIB);
+    dlclose(malloc_impl_handle);
+    return;
+  }
+
+  // Get the syms for the external functions.
+  sym = dlsym(malloc_impl_handle, "debug_finalize");
+  if (sym == nullptr) {
+    error_log("%s: debug_finalize routine not found in %s", getprogname(), DEBUG_SHARED_LIB);
+    dlclose(malloc_impl_handle);
+    return;
+  }
+  g_debug_finalize_func = reinterpret_cast<void (*)()>(sym);
+
+  sym = dlsym(malloc_impl_handle, "debug_get_malloc_leak_info");
+  if (sym == nullptr) {
+    error_log("%s: debug_get_malloc_leak_info routine not found in %s", getprogname(),
+              DEBUG_SHARED_LIB);
+    dlclose(malloc_impl_handle);
+    return;
+  }
+  g_debug_get_malloc_leak_info_func = reinterpret_cast<void (*)(uint8_t**, size_t*, size_t*,
+                                                                size_t*, size_t*)>(sym);
+
+  sym = dlsym(malloc_impl_handle, "debug_free_malloc_leak_info");
+  if (sym == nullptr) {
+    error_log("%s: debug_free_malloc_leak_info routine not found in %s", getprogname(),
+              DEBUG_SHARED_LIB);
+    dlclose(malloc_impl_handle);
+    return;
+  }
+  g_debug_free_malloc_leak_info_func = reinterpret_cast<void (*)(uint8_t*)>(sym);
+
+  if (!init_func(&__libc_malloc_default_dispatch, &gMallocLeakZygoteChild)) {
+    dlclose(malloc_impl_handle);
+    return;
+  }
+
+  MallocDispatch malloc_dispatch_table;
+  if (!InitMalloc(malloc_impl_handle, &malloc_dispatch_table, "debug")) {
+    g_debug_finalize_func();
+    dlclose(malloc_impl_handle);
+    return;
+  }
+
+  globals->malloc_dispatch = malloc_dispatch_table;
+  libc_malloc_impl_handle = malloc_impl_handle;
+
+  info_log("%s: malloc debug enabled", getprogname());
+
+  // Use atexit to trigger the cleanup function. This avoids a problem
+  // where another atexit function is used to cleanup allocated memory,
+  // but the finalize function was already called. This particular error
+  // seems to be triggered by a zygote spawned process calling exit.
+  int ret_value = __cxa_atexit(malloc_fini_impl, nullptr, nullptr);
+  if (ret_value != 0) {
+    error_log("failed to set atexit cleanup function: %d", ret_value);
+  }
+}
+
+// Initializes memory allocation framework.
+// This routine is called from __libc_init routines in libc_init_dynamic.cpp.
+__LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals) {
+  malloc_init_impl(globals);
+}
+#endif  // !LIBC_STATIC
diff --git a/libc/bionic/malloc_debug_check.cpp b/libc/bionic/malloc_debug_check.cpp
deleted file mode 100644
index 07186ce..0000000
--- a/libc/bionic/malloc_debug_check.cpp
+++ /dev/null
@@ -1,676 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#include <arpa/inet.h>
-#include <dlfcn.h>
-#include <errno.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <sys/system_properties.h>
-#include <sys/types.h>
-#include <time.h>
-#include <unistd.h>
-#include <unwind.h>
-
-#include "debug_mapinfo.h"
-#include "debug_backtrace.h"
-#include "malloc_debug_backtrace.h"
-#include "malloc_debug_common.h"
-#include "malloc_debug_disable.h"
-#include "private/bionic_macros.h"
-#include "private/libc_logging.h"
-#include "private/ScopedPthreadMutexLocker.h"
-
-#define MAX_BACKTRACE_DEPTH 16
-#define ALLOCATION_TAG      0x1ee7d00d
-#define BACKLOG_TAG         0xbabecafe
-#define FREE_POISON         0xa5
-#define FRONT_GUARD         0xaa
-#define FRONT_GUARD_LEN     (1<<5)
-#define REAR_GUARD          0xbb
-#define REAR_GUARD_LEN      (1<<5)
-
-static void log_message(const char* format, ...) {
-  va_list args;
-  va_start(args, format);
-  __libc_format_log_va_list(ANDROID_LOG_ERROR, "libc", format, args);
-  va_end(args);
-}
-
-struct hdr_t {
-    uint32_t tag;
-    void* base;  // Always points to the memory allocated using malloc.
-                 // For memory allocated in chk_memalign, this value will
-                 // not be the same as the location of the start of this
-                 // structure.
-    hdr_t* prev;
-    hdr_t* next;
-    uintptr_t bt[MAX_BACKTRACE_DEPTH];
-    int bt_depth;
-    uintptr_t freed_bt[MAX_BACKTRACE_DEPTH];
-    int freed_bt_depth;
-    size_t size;
-    uint8_t front_guard[FRONT_GUARD_LEN];
-} __attribute__((packed, aligned(MALLOC_ALIGNMENT)));
-
-struct ftr_t {
-    uint8_t rear_guard[REAR_GUARD_LEN];
-} __attribute__((packed));
-
-static inline ftr_t* to_ftr(hdr_t* hdr) {
-    return reinterpret_cast<ftr_t*>(reinterpret_cast<char*>(hdr + 1) + hdr->size);
-}
-
-static inline void* user(hdr_t* hdr) {
-    return hdr + 1;
-}
-
-static inline hdr_t* meta(void* user) {
-    return reinterpret_cast<hdr_t*>(user) - 1;
-}
-
-static inline const hdr_t* const_meta(const void* user) {
-    return reinterpret_cast<const hdr_t*>(user) - 1;
-}
-
-// TODO: introduce a struct for this global state.
-// There are basically two lists here, the regular list and the backlog list.
-// We should be able to remove the duplication.
-static unsigned g_allocated_block_count;
-static hdr_t* tail;
-static hdr_t* head;
-static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-
-static unsigned backlog_num;
-static hdr_t* backlog_tail;
-static hdr_t* backlog_head;
-static pthread_mutex_t backlog_lock = PTHREAD_MUTEX_INITIALIZER;
-
-// This variable is set to the value of property libc.debug.malloc.backlog.
-// 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;
-
-static inline void init_front_guard(hdr_t* hdr) {
-    memset(hdr->front_guard, FRONT_GUARD, FRONT_GUARD_LEN);
-}
-
-static inline bool is_front_guard_valid(hdr_t* hdr) {
-    for (size_t i = 0; i < FRONT_GUARD_LEN; i++) {
-        if (hdr->front_guard[i] != FRONT_GUARD) {
-            return false;
-        }
-    }
-    return true;
-}
-
-static inline void init_rear_guard(hdr_t* hdr) {
-    ftr_t* ftr = to_ftr(hdr);
-    memset(ftr->rear_guard, REAR_GUARD, REAR_GUARD_LEN);
-}
-
-static inline bool is_rear_guard_valid(hdr_t* hdr) {
-    unsigned i;
-    int valid = 1;
-    int first_mismatch = -1;
-    ftr_t* ftr = to_ftr(hdr);
-    for (i = 0; i < REAR_GUARD_LEN; i++) {
-        if (ftr->rear_guard[i] != REAR_GUARD) {
-            if (first_mismatch < 0)
-                first_mismatch = i;
-            valid = 0;
-        } else if (first_mismatch >= 0) {
-            log_message("+++ REAR GUARD MISMATCH [%d, %d)\n", first_mismatch, i);
-            first_mismatch = -1;
-        }
-    }
-
-    if (first_mismatch >= 0)
-        log_message("+++ REAR GUARD MISMATCH [%d, %d)\n", first_mismatch, i);
-    return valid;
-}
-
-static inline void add_locked(hdr_t* hdr, hdr_t** tail, hdr_t** head) {
-    hdr->prev = NULL;
-    hdr->next = *head;
-    if (*head)
-        (*head)->prev = hdr;
-    else
-        *tail = hdr;
-    *head = hdr;
-}
-
-static inline int del_locked(hdr_t* hdr, hdr_t** tail, hdr_t** head) {
-    if (hdr->prev) {
-        hdr->prev->next = hdr->next;
-    } else {
-        *head = hdr->next;
-    }
-    if (hdr->next) {
-        hdr->next->prev = hdr->prev;
-    } else {
-        *tail = hdr->prev;
-    }
-    return 0;
-}
-
-static inline void add(hdr_t* hdr, size_t size) {
-    ScopedPthreadMutexLocker locker(&lock);
-    hdr->tag = ALLOCATION_TAG;
-    hdr->size = size;
-    init_front_guard(hdr);
-    init_rear_guard(hdr);
-    ++g_allocated_block_count;
-    add_locked(hdr, &tail, &head);
-}
-
-static inline int del(hdr_t* hdr) {
-    if (hdr->tag != ALLOCATION_TAG) {
-        return -1;
-    }
-
-    ScopedPthreadMutexLocker locker(&lock);
-    del_locked(hdr, &tail, &head);
-    --g_allocated_block_count;
-    return 0;
-}
-
-static inline void poison(hdr_t* hdr) {
-    memset(user(hdr), FREE_POISON, hdr->size);
-}
-
-static bool was_used_after_free(hdr_t* hdr) {
-    const uint8_t* data = reinterpret_cast<const uint8_t*>(user(hdr));
-    for (size_t i = 0; i < hdr->size; i++) {
-        if (data[i] != FREE_POISON) {
-            return true;
-        }
-    }
-    return false;
-}
-
-/* returns 1 if valid, *safe == 1 if safe to dump stack */
-static inline int check_guards(hdr_t* hdr, int* safe) {
-    *safe = 1;
-    if (!is_front_guard_valid(hdr)) {
-        if (hdr->front_guard[0] == FRONT_GUARD) {
-            log_message("+++ ALLOCATION %p SIZE %d HAS A CORRUPTED FRONT GUARD\n",
-                       user(hdr), hdr->size);
-        } else {
-            log_message("+++ ALLOCATION %p HAS A CORRUPTED FRONT GUARD "\
-                      "(NOT DUMPING STACKTRACE)\n", user(hdr));
-            /* Allocation header is probably corrupt, do not print stack trace */
-            *safe = 0;
-        }
-        return 0;
-    }
-
-    if (!is_rear_guard_valid(hdr)) {
-        log_message("+++ ALLOCATION %p SIZE %d HAS A CORRUPTED REAR GUARD\n",
-                   user(hdr), hdr->size);
-        return 0;
-    }
-
-    return 1;
-}
-
-/* returns 1 if valid, *safe == 1 if safe to dump stack */
-static inline int check_allocation_locked(hdr_t* hdr, int* safe) {
-    int valid = 1;
-    *safe = 1;
-
-    if (hdr->tag != ALLOCATION_TAG && hdr->tag != BACKLOG_TAG) {
-        log_message("+++ ALLOCATION %p HAS INVALID TAG %08x (NOT DUMPING STACKTRACE)\n",
-                   user(hdr), hdr->tag);
-        // Allocation header is probably corrupt, do not dequeue or dump stack
-        // trace.
-        *safe = 0;
-        return 0;
-    }
-
-    if (hdr->tag == BACKLOG_TAG && was_used_after_free(hdr)) {
-        log_message("+++ ALLOCATION %p SIZE %d WAS USED AFTER BEING FREED\n",
-                   user(hdr), hdr->size);
-        valid = 0;
-        /* check the guards to see if it's safe to dump a stack trace */
-        check_guards(hdr, safe);
-    } else {
-        valid = check_guards(hdr, 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);
-        if (hdr->tag == BACKLOG_TAG) {
-            log_message("+++ ALLOCATION %p SIZE %d FREED HERE:\n",
-                       user(hdr), hdr->size);
-            log_backtrace(hdr->freed_bt, hdr->freed_bt_depth);
-        }
-    }
-
-    return valid;
-}
-
-static inline int del_and_check_locked(hdr_t* hdr,
-                                       hdr_t** tail, hdr_t** head, unsigned* cnt,
-                                       int* safe) {
-    int valid = check_allocation_locked(hdr, safe);
-    if (safe) {
-        (*cnt)--;
-        del_locked(hdr, tail, head);
-    }
-    return valid;
-}
-
-static inline void del_from_backlog_locked(hdr_t* hdr) {
-    int safe;
-    del_and_check_locked(hdr,
-                         &backlog_tail, &backlog_head, &backlog_num,
-                         &safe);
-    hdr->tag = 0; /* clear the tag */
-}
-
-static inline void del_from_backlog(hdr_t* hdr) {
-    ScopedPthreadMutexLocker locker(&backlog_lock);
-    del_from_backlog_locked(hdr);
-}
-
-static inline int del_leak(hdr_t* hdr, int* safe) {
-    ScopedPthreadMutexLocker locker(&lock);
-    return del_and_check_locked(hdr, &tail, &head, &g_allocated_block_count, safe);
-}
-
-static inline void add_to_backlog(hdr_t* hdr) {
-    ScopedPthreadMutexLocker locker(&backlog_lock);
-    hdr->tag = BACKLOG_TAG;
-    backlog_num++;
-    add_locked(hdr, &backlog_tail, &backlog_head);
-    poison(hdr);
-    /* If we've exceeded the maximum backlog, clear it up */
-    while (backlog_num > g_malloc_debug_backlog) {
-        hdr_t* gone = backlog_tail;
-        del_from_backlog_locked(gone);
-        g_malloc_dispatch->free(gone->base);
-    }
-}
-
-extern "C" void* chk_malloc(size_t bytes) {
-//  log_message("%s: %s\n", __FILE__, __FUNCTION__);
-    if (DebugCallsDisabled()) {
-        return g_malloc_dispatch->malloc(bytes);
-    }
-
-    size_t size = sizeof(hdr_t) + bytes + sizeof(ftr_t);
-    if (size < bytes) { // Overflow
-        errno = ENOMEM;
-        return NULL;
-    }
-    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);
-        add(hdr, bytes);
-        return user(hdr);
-    }
-    return NULL;
-}
-
-extern "C" void* chk_memalign(size_t alignment, size_t bytes) {
-    if (DebugCallsDisabled()) {
-        return g_malloc_dispatch->memalign(alignment, bytes);
-    }
-
-    if (alignment <= MALLOC_ALIGNMENT) {
-        return chk_malloc(bytes);
-    }
-
-    // Make the alignment a power of two.
-    if (!powerof2(alignment)) {
-        alignment = BIONIC_ROUND_UP_POWER_OF_2(alignment);
-    }
-
-    // here, alignment is at least MALLOC_ALIGNMENT<<1 bytes
-    // we will align by at least MALLOC_ALIGNMENT bytes
-    // and at most alignment-MALLOC_ALIGNMENT bytes
-    size_t size = (alignment-MALLOC_ALIGNMENT) + bytes;
-    if (size < bytes) { // Overflow.
-        return NULL;
-    }
-
-    void* base = g_malloc_dispatch->malloc(sizeof(hdr_t) + size + sizeof(ftr_t));
-    if (base != NULL) {
-        // Check that the actual pointer that will be returned is aligned
-        // properly.
-        uintptr_t ptr = reinterpret_cast<uintptr_t>(user(reinterpret_cast<hdr_t*>(base)));
-        if ((ptr % alignment) != 0) {
-            // Align the pointer.
-            ptr += ((-ptr) % alignment);
-        }
-
-        hdr_t* hdr = meta(reinterpret_cast<void*>(ptr));
-        hdr->base = base;
-        hdr->bt_depth = GET_BACKTRACE(hdr->bt, MAX_BACKTRACE_DEPTH);
-        add(hdr, bytes);
-        return user(hdr);
-    }
-    return base;
-}
-
-extern "C" void chk_free(void* ptr) {
-//  log_message("%s: %s\n", __FILE__, __FUNCTION__);
-    if (DebugCallsDisabled()) {
-        return g_malloc_dispatch->free(ptr);
-    }
-
-    if (!ptr) /* ignore free(NULL) */
-        return;
-
-    hdr_t* hdr = meta(ptr);
-
-    if (del(hdr) < 0) {
-        uintptr_t 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);
-            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));
-            if (g_backtrace_enabled) {
-                log_backtrace(bt, depth);
-            }
-        }
-    } else {
-        hdr->freed_bt_depth = GET_BACKTRACE(hdr->freed_bt, MAX_BACKTRACE_DEPTH);
-        add_to_backlog(hdr);
-    }
-}
-
-extern "C" void* chk_realloc(void* ptr, size_t bytes) {
-//  log_message("%s: %s\n", __FILE__, __FUNCTION__);
-    if (DebugCallsDisabled()) {
-        return g_malloc_dispatch->realloc(ptr, bytes);
-    }
-
-    if (!ptr) {
-        return chk_malloc(bytes);
-    }
-
-#ifdef REALLOC_ZERO_BYTES_FREE
-    if (!bytes) {
-        chk_free(ptr);
-        return NULL;
-    }
-#endif
-
-    hdr_t* hdr = meta(ptr);
-
-    if (del(hdr) < 0) {
-        uintptr_t 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);
-            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
-             * reallocation below succeeds.  Since we didn't really free it, we
-             * can default to this behavior.
-             */
-            del_from_backlog(hdr);
-        } else {
-            log_message("+++ REALLOCATION %p SIZE %d IS CORRUPTED OR NOT ALLOCATED VIA TRACKER!\n",
-                       user(hdr), bytes);
-            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
-        }
-    }
-
-    size_t size = sizeof(hdr_t) + bytes + sizeof(ftr_t);
-    if (size < bytes) { // Overflow
-        errno = ENOMEM;
-        return NULL;
-    }
-    if (hdr->base != hdr) {
-        // An allocation from memalign, so create another allocation and
-        // copy the data out.
-        void* newMem = g_malloc_dispatch->malloc(size);
-        if (newMem == NULL) {
-            return NULL;
-        }
-        memcpy(newMem, hdr, sizeof(hdr_t) + hdr->size);
-        g_malloc_dispatch->free(hdr->base);
-        hdr = static_cast<hdr_t*>(newMem);
-    } else {
-        hdr = static_cast<hdr_t*>(g_malloc_dispatch->realloc(hdr, size));
-    }
-    if (hdr) {
-        hdr->base = hdr;
-        hdr->bt_depth = GET_BACKTRACE(hdr->bt, MAX_BACKTRACE_DEPTH);
-        add(hdr, bytes);
-        return user(hdr);
-    }
-    return NULL;
-}
-
-extern "C" void* chk_calloc(size_t nmemb, size_t bytes) {
-//  log_message("%s: %s\n", __FILE__, __FUNCTION__);
-    if (DebugCallsDisabled()) {
-        return g_malloc_dispatch->calloc(nmemb, bytes);
-    }
-
-    size_t total_bytes = nmemb * bytes;
-    size_t size = sizeof(hdr_t) + total_bytes + sizeof(ftr_t);
-    if (size < total_bytes || (nmemb && SIZE_MAX / nmemb < bytes)) { // Overflow
-        errno = ENOMEM;
-        return NULL;
-    }
-    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);
-        add(hdr, total_bytes);
-        return user(hdr);
-    }
-    return NULL;
-}
-
-extern "C" size_t chk_malloc_usable_size(const void* ptr) {
-    if (DebugCallsDisabled()) {
-        return g_malloc_dispatch->malloc_usable_size(ptr);
-    }
-
-    // malloc_usable_size returns 0 for NULL and unknown blocks.
-    if (ptr == NULL)
-        return 0;
-
-    const hdr_t* hdr = const_meta(ptr);
-
-    // The sentinel tail is written just after the request block bytes
-    // so there is no extra room we can report here.
-    return hdr->size;
-}
-
-extern "C" struct mallinfo chk_mallinfo() {
-  return g_malloc_dispatch->mallinfo();
-}
-
-extern "C" int chk_posix_memalign(void** memptr, size_t alignment, size_t size) {
-  if (DebugCallsDisabled()) {
-    return g_malloc_dispatch->posix_memalign(memptr, alignment, size);
-  }
-
-  if (!powerof2(alignment)) {
-    return EINVAL;
-  }
-  int saved_errno = errno;
-  *memptr = chk_memalign(alignment, size);
-  errno = saved_errno;
-  return (*memptr != NULL) ? 0 : ENOMEM;
-}
-
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-extern "C" void* chk_pvalloc(size_t bytes) {
-  if (DebugCallsDisabled()) {
-    return g_malloc_dispatch->pvalloc(bytes);
-  }
-
-  size_t pagesize = getpagesize();
-  size_t size = BIONIC_ALIGN(bytes, pagesize);
-  if (size < bytes) { // Overflow
-    return NULL;
-  }
-  return chk_memalign(pagesize, size);
-}
-
-extern "C" void* chk_valloc(size_t size) {
-  if (DebugCallsDisabled()) {
-    return g_malloc_dispatch->valloc(size);
-  }
-  return chk_memalign(getpagesize(), size);
-}
-#endif
-
-static void ReportMemoryLeaks() {
-  ScopedDisableDebugCalls disable;
-
-  // Use /proc/self/exe link to obtain the program name for logging
-  // purposes. If it's not available, we set it to "<unknown>".
-  char exe[PATH_MAX];
-  int count;
-  if ((count = readlink("/proc/self/exe", exe, sizeof(exe) - 1)) == -1) {
-    strlcpy(exe, "<unknown>", sizeof(exe));
-  } else {
-    exe[count] = '\0';
-  }
-
-  if (g_allocated_block_count == 0) {
-    log_message("+++ %s did not leak", exe);
-    return;
-  }
-
-  size_t index = 1;
-  const size_t total = g_allocated_block_count;
-  while (head != NULL) {
-    int safe;
-    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) && g_backtrace_enabled) {
-      /* safe == 1, because the allocation is valid */
-      log_backtrace(block->bt, block->bt_depth);
-    }
-  }
-
-  while (backlog_head != NULL) {
-    del_from_backlog(backlog_tail);
-  }
-}
-
-pthread_key_t g_debug_calls_disabled;
-
-extern "C" bool malloc_debug_initialize(HashTable* hash_table, const MallocDebug* malloc_dispatch) {
-  g_hash_table = hash_table;
-  g_malloc_dispatch = malloc_dispatch;
-
-  pthread_key_create(&g_debug_calls_disabled, NULL);
-
-  char debug_backlog[PROP_VALUE_MAX];
-  if (__system_property_get("libc.debug.malloc.backlog", debug_backlog)) {
-    g_malloc_debug_backlog = atoi(debug_backlog);
-    info_log("%s: setting backlog length to %d\n", getprogname(), g_malloc_debug_backlog);
-  }
-
-  // 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;
-}
-
-extern "C" void malloc_debug_finalize(int malloc_debug_level) {
-  // We only track leaks at level 10.
-  if (malloc_debug_level == 10) {
-    ReportMemoryLeaks();
-  }
-  if (g_backtrace_enabled) {
-    backtrace_shutdown();
-  }
-
-  pthread_setspecific(g_debug_calls_disabled, NULL);
-}
diff --git a/libc/bionic/malloc_debug_common.h b/libc/bionic/malloc_debug_common.h
deleted file mode 100644
index f8745da..0000000
--- a/libc/bionic/malloc_debug_common.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-/*
- * Contains declarations of types and constants used by malloc leak
- * detection code in both, libc and libc_malloc_debug libraries.
- */
-#ifndef MALLOC_DEBUG_COMMON_H
-#define MALLOC_DEBUG_COMMON_H
-
-#include <pthread.h>
-#include <stdint.h>
-#include <stdlib.h>
-
-#include "private/bionic_config.h"
-#include "private/bionic_malloc_dispatch.h"
-#include "private/libc_logging.h"
-
-#define HASHTABLE_SIZE      1543
-#define BACKTRACE_SIZE      32
-/* flag definitions, currently sharing storage with "size" */
-#define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
-#define SIZE_FLAG_MASK          (SIZE_FLAG_ZYGOTE_CHILD)
-
-// This must match the alignment used by the malloc implementation.
-#ifndef MALLOC_ALIGNMENT
-#define MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *)))
-#endif
-
-// =============================================================================
-// Structures
-// =============================================================================
-
-struct HashEntry {
-    size_t slot;
-    HashEntry* prev;
-    HashEntry* next;
-    size_t numEntries;
-    // fields above "size" are NOT sent to the host
-    size_t size;
-    size_t allocations;
-    uintptr_t backtrace[0];
-};
-
-struct HashTable {
-    pthread_mutex_t lock;
-    size_t count;
-    HashEntry* slots[HASHTABLE_SIZE];
-};
-
-typedef bool (*MallocDebugInit)(HashTable*, const MallocDebug*);
-typedef void (*MallocDebugFini)(int);
-
-// =============================================================================
-// log functions
-// =============================================================================
-
-#define debug_log(format, ...)  \
-    __libc_format_log(ANDROID_LOG_DEBUG, "malloc_leak_check", (format), ##__VA_ARGS__ )
-#define error_log(format, ...)  \
-    __libc_format_log(ANDROID_LOG_ERROR, "malloc_leak_check", (format), ##__VA_ARGS__ )
-#define info_log(format, ...)  \
-    __libc_format_log(ANDROID_LOG_INFO, "malloc_leak_check", (format), ##__VA_ARGS__ )
-
-#endif  // MALLOC_DEBUG_COMMON_H
diff --git a/libc/bionic/malloc_debug_leak.cpp b/libc/bionic/malloc_debug_leak.cpp
deleted file mode 100644
index 1ffbee2..0000000
--- a/libc/bionic/malloc_debug_leak.cpp
+++ /dev/null
@@ -1,525 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-#include <arpa/inet.h>
-#include <dlfcn.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <pthread.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/param.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/system_properties.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-#include <unwind.h>
-
-#include "debug_backtrace.h"
-#include "malloc_debug_backtrace.h"
-#include "malloc_debug_common.h"
-#include "malloc_debug_disable.h"
-
-#include "private/bionic_macros.h"
-#include "private/libc_logging.h"
-#include "private/ScopedPthreadMutexLocker.h"
-
-extern int gMallocLeakZygoteChild;
-extern HashTable* g_hash_table;
-extern const MallocDebug* g_malloc_dispatch;
-
-// =============================================================================
-// stack trace functions
-// =============================================================================
-
-#define GUARD               0x48151642
-#define DEBUG               0
-
-// =============================================================================
-// Structures
-// =============================================================================
-
-struct AllocationEntry {
-    HashEntry* entry;
-    uint32_t guard;
-} __attribute__((aligned(MALLOC_ALIGNMENT)));
-
-static inline AllocationEntry* to_header(void* mem) {
-  return reinterpret_cast<AllocationEntry*>(mem) - 1;
-}
-
-static inline const AllocationEntry* const_to_header(const void* mem) {
-  return reinterpret_cast<const AllocationEntry*>(mem) - 1;
-}
-
-// =============================================================================
-// Hash Table functions
-// =============================================================================
-
-static uint32_t get_hash(uintptr_t* backtrace, size_t numEntries) {
-    if (backtrace == NULL) return 0;
-
-    int hash = 0;
-    size_t i;
-    for (i = 0 ; i < numEntries ; i++) {
-        hash = (hash * 33) + (backtrace[i] >> 2);
-    }
-
-    return hash;
-}
-
-static HashEntry* find_entry(HashTable* table, int slot,
-                             uintptr_t* backtrace, size_t numEntries, size_t size) {
-    HashEntry* entry = table->slots[slot];
-    while (entry != NULL) {
-        //debug_log("backtrace: %p, entry: %p entry->backtrace: %p\n",
-        //        backtrace, entry, (entry != NULL) ? entry->backtrace : NULL);
-        /*
-         * See if the entry matches exactly.  We compare the "size" field,
-         * including the flag bits.
-         */
-        if (entry->size == size && entry->numEntries == numEntries &&
-                !memcmp(backtrace, entry->backtrace, numEntries * sizeof(uintptr_t))) {
-            return entry;
-        }
-
-        entry = entry->next;
-    }
-
-    return NULL;
-}
-
-static HashEntry* record_backtrace(uintptr_t* backtrace, size_t numEntries, size_t size) {
-    size_t hash = get_hash(backtrace, numEntries);
-    size_t slot = hash % HASHTABLE_SIZE;
-
-    if (size & SIZE_FLAG_MASK) {
-        debug_log("malloc_debug: allocation %zx exceeds bit width\n", size);
-        abort();
-    }
-
-    if (gMallocLeakZygoteChild) {
-        size |= SIZE_FLAG_ZYGOTE_CHILD;
-    }
-
-    // Keep the lock held for as little time as possible to prevent deadlocks.
-    ScopedPthreadMutexLocker locker(&g_hash_table->lock);
-    HashEntry* entry = find_entry(g_hash_table, slot, backtrace, numEntries, size);
-    if (entry != NULL) {
-        entry->allocations++;
-    } else {
-        // create a new entry
-        entry = static_cast<HashEntry*>(g_malloc_dispatch->malloc(sizeof(HashEntry) + numEntries*sizeof(uintptr_t)));
-        if (!entry) {
-            return NULL;
-        }
-        entry->allocations = 1;
-        entry->slot = slot;
-        entry->prev = NULL;
-        entry->next = g_hash_table->slots[slot];
-        entry->numEntries = numEntries;
-        entry->size = size;
-
-        memcpy(entry->backtrace, backtrace, numEntries * sizeof(uintptr_t));
-
-        g_hash_table->slots[slot] = entry;
-
-        if (entry->next != NULL) {
-            entry->next->prev = entry;
-        }
-
-        // we just added an entry, increase the size of the hashtable
-        g_hash_table->count++;
-    }
-
-    return entry;
-}
-
-static int is_valid_entry(HashEntry* entry) {
-  if (entry != NULL) {
-    for (size_t i = 0; i < HASHTABLE_SIZE; ++i) {
-      HashEntry* e1 = g_hash_table->slots[i];
-      while (e1 != NULL) {
-        if (e1 == entry) {
-          return 1;
-        }
-        e1 = e1->next;
-      }
-    }
-  }
-  return 0;
-}
-
-static void remove_entry(HashEntry* entry) {
-  HashEntry* prev = entry->prev;
-  HashEntry* next = entry->next;
-
-  if (prev != NULL) entry->prev->next = next;
-  if (next != NULL) entry->next->prev = prev;
-
-  if (prev == NULL) {
-    // we are the head of the list. set the head to be next
-    g_hash_table->slots[entry->slot] = entry->next;
-  }
-
-  // we just removed and entry, decrease the size of the hashtable
-  g_hash_table->count--;
-}
-
-// =============================================================================
-// malloc fill functions
-// =============================================================================
-
-#define CHK_FILL_FREE           0xef
-#define CHK_SENTINEL_VALUE      0xeb
-
-extern "C" void* fill_calloc(size_t n_elements, size_t elem_size) {
-    return g_malloc_dispatch->calloc(n_elements, elem_size);
-}
-
-extern "C" void* fill_malloc(size_t bytes) {
-    void* buffer = g_malloc_dispatch->malloc(bytes);
-    if (buffer) {
-        memset(buffer, CHK_SENTINEL_VALUE, bytes);
-    }
-    return buffer;
-}
-
-extern "C" void fill_free(void* mem) {
-    size_t bytes = g_malloc_dispatch->malloc_usable_size(mem);
-    memset(mem, CHK_FILL_FREE, bytes);
-    g_malloc_dispatch->free(mem);
-}
-
-extern "C" void* fill_realloc(void* mem, size_t bytes) {
-    size_t oldSize = g_malloc_dispatch->malloc_usable_size(mem);
-    void* newMem = g_malloc_dispatch->realloc(mem, bytes);
-    if (newMem) {
-        // If this is larger than before, fill the extra with our pattern.
-        size_t newSize = g_malloc_dispatch->malloc_usable_size(newMem);
-        if (newSize > oldSize) {
-            memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(newMem)+oldSize), CHK_FILL_FREE, newSize-oldSize);
-        }
-    }
-    return newMem;
-}
-
-extern "C" void* fill_memalign(size_t alignment, size_t bytes) {
-    void* buffer = g_malloc_dispatch->memalign(alignment, bytes);
-    if (buffer) {
-        memset(buffer, CHK_SENTINEL_VALUE, bytes);
-    }
-    return buffer;
-}
-
-extern "C" size_t fill_malloc_usable_size(const void* mem) {
-    // Since we didn't allocate extra bytes before or after, we can
-    // report the normal usable size here.
-    return g_malloc_dispatch->malloc_usable_size(mem);
-}
-
-extern "C" struct mallinfo fill_mallinfo() {
-  return g_malloc_dispatch->mallinfo();
-}
-
-extern "C" int fill_posix_memalign(void** memptr, size_t alignment, size_t size) {
-  if (!powerof2(alignment)) {
-    return EINVAL;
-  }
-  int saved_errno = errno;
-  *memptr = fill_memalign(alignment, size);
-  errno = saved_errno;
-  return (*memptr != NULL) ? 0 : ENOMEM;
-}
-
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-extern "C" void* fill_pvalloc(size_t bytes) {
-  size_t pagesize = getpagesize();
-  size_t size = BIONIC_ALIGN(bytes, pagesize);
-  if (size < bytes) { // Overflow
-    return NULL;
-  }
-  return fill_memalign(pagesize, size);
-}
-
-extern "C" void* fill_valloc(size_t size) {
-  return fill_memalign(getpagesize(), size);
-}
-#endif
-
-// =============================================================================
-// malloc leak functions
-// =============================================================================
-
-static uint32_t MEMALIGN_GUARD      = 0xA1A41520;
-
-extern "C" void* leak_malloc(size_t bytes) {
-    if (DebugCallsDisabled()) {
-        return g_malloc_dispatch->malloc(bytes);
-    }
-
-    // allocate enough space infront of the allocation to store the pointer for
-    // the alloc structure. This will making free'ing the structer really fast!
-
-    // 1. allocate enough memory and include our header
-    // 2. set the base pointer to be right after our header
-
-    size_t size = bytes + sizeof(AllocationEntry);
-    if (size < bytes) { // Overflow.
-        errno = ENOMEM;
-        return NULL;
-    }
-
-    void* base = g_malloc_dispatch->malloc(size);
-    if (base != NULL) {
-        uintptr_t backtrace[BACKTRACE_SIZE];
-        size_t numEntries = GET_BACKTRACE(backtrace, BACKTRACE_SIZE);
-
-        AllocationEntry* header = reinterpret_cast<AllocationEntry*>(base);
-        header->entry = record_backtrace(backtrace, numEntries, bytes);
-        header->guard = GUARD;
-
-        // now increment base to point to after our header.
-        // this should just work since our header is 8 bytes.
-        base = reinterpret_cast<AllocationEntry*>(base) + 1;
-    }
-
-    return base;
-}
-
-extern "C" void leak_free(void* mem) {
-  if (DebugCallsDisabled()) {
-    return g_malloc_dispatch->free(mem);
-  }
-
-  if (mem == NULL) {
-    return;
-  }
-
-  // check the guard to make sure it is valid
-  AllocationEntry* header = to_header(mem);
-
-  if (header->guard != GUARD) {
-    // could be a memaligned block
-    if (header->guard == MEMALIGN_GUARD) {
-      // For memaligned blocks, header->entry points to the memory
-      // allocated through leak_malloc.
-      header = to_header(header->entry);
-    }
-  }
-
-  ScopedPthreadMutexLocker locker(&g_hash_table->lock);
-  if (header->guard == GUARD || is_valid_entry(header->entry)) {
-    // decrement the allocations
-    HashEntry* entry = header->entry;
-    entry->allocations--;
-    if (entry->allocations <= 0) {
-      remove_entry(entry);
-      g_malloc_dispatch->free(entry);
-    }
-
-    // now free the memory!
-    g_malloc_dispatch->free(header);
-  } else {
-    debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n",
-              header->guard, header->entry);
-  }
-}
-
-extern "C" void* leak_calloc(size_t n_elements, size_t elem_size) {
-    if (DebugCallsDisabled()) {
-        return g_malloc_dispatch->calloc(n_elements, elem_size);
-    }
-
-    // Fail on overflow - just to be safe even though this code runs only
-    // within the debugging C library, not the production one.
-    if (n_elements && SIZE_MAX / n_elements < elem_size) {
-        errno = ENOMEM;
-        return NULL;
-    }
-    size_t size = n_elements * elem_size;
-    void* ptr  = leak_malloc(size);
-    if (ptr != NULL) {
-        memset(ptr, 0, size);
-    }
-    return ptr;
-}
-
-extern "C" size_t leak_malloc_usable_size(const void* mem) {
-    if (DebugCallsDisabled()) {
-        return g_malloc_dispatch->malloc_usable_size(mem);
-    }
-
-    if (mem == NULL) {
-        return 0;
-    }
-
-    // Check the guard to make sure it is valid.
-    const AllocationEntry* header = const_to_header(mem);
-
-    if (header->guard == MEMALIGN_GUARD) {
-        // If this is a memalign'd pointer, then grab the header from
-        // entry.
-        header = const_to_header(header->entry);
-    } else if (header->guard != GUARD) {
-        debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n",
-                  header->guard, header->entry);
-        return 0;
-    }
-
-    size_t ret = g_malloc_dispatch->malloc_usable_size(header);
-    if (ret != 0) {
-        // The usable area starts at 'mem' and stops at 'header+ret'.
-        return reinterpret_cast<uintptr_t>(header) + ret - reinterpret_cast<uintptr_t>(mem);
-    }
-    return 0;
-}
-
-extern "C" void* leak_realloc(void* oldMem, size_t bytes) {
-    if (DebugCallsDisabled()) {
-        return g_malloc_dispatch->realloc(oldMem, bytes);
-    }
-
-    if (oldMem == NULL) {
-        return leak_malloc(bytes);
-    }
-
-    void* newMem = NULL;
-    AllocationEntry* header = to_header(oldMem);
-    if (header->guard == MEMALIGN_GUARD) {
-        // Get the real header.
-        header = to_header(header->entry);
-    } else if (header->guard != GUARD) {
-        debug_log("WARNING bad header guard: '0x%x'! and invalid entry: %p\n",
-                   header->guard, header->entry);
-        errno = ENOMEM;
-        return NULL;
-    }
-
-    newMem = leak_malloc(bytes);
-    if (newMem != NULL) {
-        size_t oldSize = leak_malloc_usable_size(oldMem);
-        size_t copySize = (oldSize <= bytes) ? oldSize : bytes;
-        memcpy(newMem, oldMem, copySize);
-        leak_free(oldMem);
-    }
-
-    return newMem;
-}
-
-extern "C" void* leak_memalign(size_t alignment, size_t bytes) {
-    if (DebugCallsDisabled()) {
-        return g_malloc_dispatch->memalign(alignment, bytes);
-    }
-
-    // we can just use malloc
-    if (alignment <= MALLOC_ALIGNMENT) {
-        return leak_malloc(bytes);
-    }
-
-    // need to make sure it's a power of two
-    if (!powerof2(alignment)) {
-        alignment = BIONIC_ROUND_UP_POWER_OF_2(alignment);
-    }
-
-    // here, alignment is at least MALLOC_ALIGNMENT<<1 bytes
-    // we will align by at least MALLOC_ALIGNMENT bytes
-    // and at most alignment-MALLOC_ALIGNMENT bytes
-    size_t size = (alignment-MALLOC_ALIGNMENT) + bytes;
-    if (size < bytes) { // Overflow.
-        return NULL;
-    }
-
-    void* base = leak_malloc(size);
-    if (base != NULL) {
-        uintptr_t ptr = reinterpret_cast<uintptr_t>(base);
-        if ((ptr % alignment) == 0) {
-            return base;
-        }
-
-        // align the pointer
-        ptr += ((-ptr) % alignment);
-
-        // Already allocated enough space for the header. This assumes
-        // that the malloc alignment is at least 8, otherwise, this is
-        // not guaranteed to have the space for the header.
-        AllocationEntry* header = to_header(reinterpret_cast<void*>(ptr));
-        header->guard = MEMALIGN_GUARD;
-        header->entry = reinterpret_cast<HashEntry*>(base);
-
-        return reinterpret_cast<void*>(ptr);
-    }
-    return base;
-}
-
-extern "C" struct mallinfo leak_mallinfo() {
-  return g_malloc_dispatch->mallinfo();
-}
-
-extern "C" int leak_posix_memalign(void** memptr, size_t alignment, size_t size) {
-  if (DebugCallsDisabled()) {
-    return g_malloc_dispatch->posix_memalign(memptr, alignment, size);
-  }
-
-  if (!powerof2(alignment)) {
-    return EINVAL;
-  }
-  int saved_errno = errno;
-  *memptr = leak_memalign(alignment, size);
-  errno = saved_errno;
-  return (*memptr != NULL) ? 0 : ENOMEM;
-}
-
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-extern "C" void* leak_pvalloc(size_t bytes) {
-  if (DebugCallsDisabled()) {
-    return g_malloc_dispatch->pvalloc(bytes);
-  }
-
-  size_t pagesize = getpagesize();
-  size_t size = BIONIC_ALIGN(bytes, pagesize);
-  if (size < bytes) { // Overflow
-    return NULL;
-  }
-  return leak_memalign(pagesize, size);
-}
-
-extern "C" void* leak_valloc(size_t size) {
-  if (DebugCallsDisabled()) {
-    return g_malloc_dispatch->valloc(size);
-  }
-
-  return leak_memalign(getpagesize(), size);
-}
-#endif
diff --git a/libc/bionic/malloc_debug_qemu.cpp b/libc/bionic/malloc_debug_qemu.cpp
deleted file mode 100644
index b01cef2..0000000
--- a/libc/bionic/malloc_debug_qemu.cpp
+++ /dev/null
@@ -1,1069 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-/*
- * Contains implementation of memory allocation routines instrumented for
- * usage in the emulator to detect memory allocation violations, such as
- * memory leaks, buffer overruns, etc.
- * Code, implemented here is intended to run in the emulated environment only,
- * and serves simply as hooks into memory allocation routines. Main job of this
- * code is to notify the emulator about memory being allocated/deallocated,
- * providing information about each allocation. The idea is that emulator will
- * keep list of currently allocated blocks, and, knowing boundaries of each
- * block it will be able to verify that ld/st access to these blocks don't step
- * over boundaries set for the user. To enforce that, each memory block
- * allocated by this code is guarded with "prefix" and "suffix" areas, so
- * every time emulator detects access to any of these guarding areas, it can be
- * considered as access violation.
- */
-
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <sys/param.h>
-#include <pthread.h>
-#include <unistd.h>
-#include <errno.h>
-#include "malloc_debug_common.h"
-#include "private/bionic_macros.h"
-#include "private/libc_logging.h"
-
-/* This file should be included into the build only when
- * MALLOC_QEMU_INSTRUMENT macro is defined. */
-#ifndef MALLOC_QEMU_INSTRUMENT
-#error MALLOC_QEMU_INSTRUMENT is not defined.
-#endif  // !MALLOC_QEMU_INSTRUMENT
-
-/* Controls access violation test performed to make sure that we catch AVs
- * all the time they occur. See test_access_violation for more info. This macro
- * is used for internal testing purposes and should always be set to zero for
- * the production builds. */
-#define TEST_ACCESS_VIOLATIONS  0
-
-// =============================================================================
-// Communication structures
-// =============================================================================
-
-/* Describes memory block allocated from the heap. This structure is passed
- * along with TRACE_DEV_REG_MALLOC event. This descriptor is used to inform
- * the emulator about new memory block being allocated from the heap. The entire
- * structure is initialized by the guest system before event is fired up. It is
- * important to remember that same structure (an exact copy, except for
- * replacing pointers with target_ulong) is also declared in the emulator's
- * sources (file memcheck/memcheck_common.h). So, every time a change is made to
- * any of these two declaration, another one must be also updated accordingly.
- */
-struct MallocDesc {
-    /* Pointer to the memory block actually allocated from the heap. Note that
-     * this is not the pointer that is returned to the malloc's caller. Pointer
-     * returned to the caller is calculated by adding value stored in this field
-     * to the value stored in prefix_size field of this structure.
-     */
-    void*       ptr;
-
-    /* Number of bytes requested by the malloc's caller. */
-    uint32_t    requested_bytes;
-
-    /* Byte size of the prefix data. Actual pointer returned to the malloc's
-     * caller is calculated by adding value stored in this field to the value
-     * stored in in the ptr field of this structure.
-     */
-    uint32_t    prefix_size;
-
-    /* Byte size of the suffix data. */
-    uint32_t    suffix_size;
-
-    /* Id of the process that initialized libc instance, in which allocation
-     * has occurred. This field is used by the emulator to report errors in
-     * the course of TRACE_DEV_REG_MALLOC event handling. In case of an error,
-     * emulator sets this field to zero (invalid value for a process ID).
-     */
-    uint32_t    libc_pid;
-
-    /* Id of the process in context of which allocation has occurred.
-     * Value in this field may differ from libc_pid value, if process that
-     * is doing allocation has been forked from the process that initialized
-     * libc instance.
-     */
-    uint32_t    allocator_pid;
-
-    /* Number of access violations detected on this allocation. */
-    uint32_t    av_count;
-};
-
-/* Describes memory block info queried from emulator. This structure is passed
- * along with TRACE_DEV_REG_QUERY_MALLOC event. When handling free and realloc
- * calls, it is required that we have information about memory blocks that were
- * actually allocated in previous calls to malloc, calloc, memalign, or realloc.
- * Since we don't keep this information directly in the allocated block, but
- * rather we keep it in the emulator, we need to query emulator for that
- * information with TRACE_DEV_REG_QUERY_MALLOC query. The entire structure is
- * initialized by the guest system before event is fired up. It is important to
- * remember that same structure (an exact copy, except for replacing pointers
- * with target_ulong) is also declared in the emulator's sources (file
- * memcheck/memecheck_common.h). So, every time a change is made to any of these
- * two declaration, another one must be also updated accordingly.
- */
-struct MallocDescQuery {
-    /* Pointer, for which information is queried. Note that this pointer doesn't
-     * have to be exact pointer returned to malloc's caller, but can point
-     * anywhere inside an allocated block, including guarding areas. Emulator
-     * will respond with information about allocated block that contains this
-     * pointer.
-     */
-    const void*       ptr;
-
-    /* Id of the process that initialized libc instance, in which this query
-     * is called. This field is used by the emulator to report errors in
-     * the course of TRACE_DEV_REG_QUERY_MALLOC event handling. In case of an
-     * error, emulator sets this field to zero (invalid value for a process ID).
-     */
-    uint32_t    libc_pid;
-
-    /* Process ID in context of which query is made. */
-    uint32_t    query_pid;
-
-    /* Code of the allocation routine, in context of which query has been made:
-     *  1 - free
-     *  2 - realloc
-     */
-    uint32_t    routine;
-
-    /* Address of memory allocation descriptor for the queried pointer.
-     * Descriptor, addressed by this field is initialized by the emulator in
-     * response to the query.
-     */
-    MallocDesc*  desc;
-};
-
-/* Describes memory block that is being freed back to the heap. This structure
- * is passed along with TRACE_DEV_REG_FREE_PTR event. The entire structure is
- * initialized by the guest system before event is fired up. It is important to
- * remember that same structure (an exact copy, except for replacing pointers
- * with target_ulong) is also declared in the emulator's sources (file
- * memcheck/memecheck_common.h). So, every time a change is made to any of these
- * two declaration, another one must be also updated accordingly.
- */
-struct MallocFree {
-    /* Pointer to be freed. */
-    void*       ptr;
-
-    /* Id of the process that initialized libc instance, in which this free
-     * is called. This field is used by the emulator to report errors in
-     * the course of TRACE_DEV_REG_FREE_PTR event handling. In case of an
-     * error, emulator sets this field to zero (invalid value for a process ID).
-     */
-    uint32_t    libc_pid;
-
-    /* Process ID in context of which memory is being freed. */
-    uint32_t    free_pid;
-};
-
-// =============================================================================
-// Communication events
-// =============================================================================
-
-/* Notifies the emulator that libc has been initialized for a process.
- * Event's value parameter is PID for the process in context of which libc has
- * been initialized.
- */
-#define TRACE_DEV_REG_LIBC_INIT             1536
-
-/* Notifies the emulator about new memory block been allocated.
- * Event's value parameter points to MallocDesc instance that contains
- * allocated block information. Note that 'libc_pid' field of the descriptor
- * is used by emulator to report failure in handling this event. In case
- * of a failure emulator will zero that field before completing this event.
- */
-#define TRACE_DEV_REG_MALLOC                1537
-
-/* Notifies the emulator about memory block being freed.
- * Event's value parameter points to MallocFree descriptor that contains
- * information about block that's being freed. Note that 'libc_pid' field
- * of the descriptor is used by emulator to report failure in handling this
- * event. In case of a failure emulator will zero that field before completing
- * this event.
- */
-#define TRACE_DEV_REG_FREE_PTR              1538
-
-/* Queries the emulator about allocated memory block information.
- * Event's value parameter points to MallocDescQuery descriptor that contains
- * query parameters. Note that 'libc_pid' field of the descriptor is used by
- * emulator to report failure in handling this event. In case of a failure
- * emulator will zero that field before completing this event.
- */
-#define TRACE_DEV_REG_QUERY_MALLOC          1539
-
-/* Queries the emulator to print a string to its stdout.
- * Event's value parameter points to a zero-terminated string to be printed.
- */
-#define TRACE_DEV_REG_PRINT_USER_STR        1540
-
-static void notify_qemu_string(const char* str);
-static void qemu_log(int prio, const char* fmt, ...);
-static void dump_malloc_descriptor(char* str,
-                                   size_t str_buf_size,
-                                   const MallocDesc* desc);
-
-// =============================================================================
-// Macros
-// =============================================================================
-
-/* Defines default size of allocation prefix.
- * Note that we make prefix area quite large in order to increase chances of
- * catching buffer overflow. */
-#define DEFAULT_PREFIX_SIZE     (malloc_alignment * 4)
-
-/* Defines default size of allocation suffix.
- * Note that we make suffix area quite large in order to increase chances of
- * catching buffer overflow. */
-#define DEFAULT_SUFFIX_SIZE     (malloc_alignment * 4)
-
-/* Debug tracing has been enabled by the emulator. */
-#define DEBUG_TRACING_ENABLED   0x00000001
-/* Error tracing has been enabled by the emulator. */
-#define ERROR_TRACING_ENABLED   0x00000002
-/* Info tracing has been enabled by the emulator. */
-#define INFO_TRACING_ENABLED    0x00000004
-/* All tracing flags combined. */
-#define ALL_TRACING_ENABLED (DEBUG_TRACING_ENABLED |    \
-                             ERROR_TRACING_ENABLED |    \
-                             INFO_TRACING_ENABLED)
-
-/* Prints a string to the emulator's stdout.
- * In early stages of system loading, logging messages to logcat
- * is not available, because ADB API has not been
- * hooked up yet. So, in order to see such messages we need to print them to
- * the emulator's stdout.
- * Parameters passed to this macro are the same as parameters for printf
- * routine.
- */
-#define TR(...)                                         \
-    do {                                                \
-        char tr_str[4096];                              \
-        snprintf(tr_str, sizeof(tr_str), __VA_ARGS__);  \
-        tr_str[sizeof(tr_str) - 1] = '\0';              \
-        notify_qemu_string(&tr_str[0]);                 \
-    } while (0)
-
-// =============================================================================
-// Logging macros. Note that we simultaneously log messages to ADB and emulator.
-// =============================================================================
-
-/*
- * Helper macros for checking if particular trace level is enabled.
- */
-#define debug_LOG_ENABLED       ((tracing_flags & DEBUG_TRACING_ENABLED) != 0)
-#define error_LOG_ENABLED       ((tracing_flags & ERROR_TRACING_ENABLED) != 0)
-#define info_LOG_ENABLED        ((tracing_flags & INFO_TRACING_ENABLED)  != 0)
-#define tracing_enabled(type)   (type##_LOG_ENABLED)
-
-/*
- * Logging helper macros.
- */
-#define qemu_debug_log(format, ...)                                         \
-    do {                                                                    \
-        __libc_format_log(ANDROID_LOG_DEBUG, "memcheck", (format), ##__VA_ARGS__); \
-        if (tracing_flags & DEBUG_TRACING_ENABLED) {                        \
-            qemu_log(ANDROID_LOG_DEBUG, (format), ##__VA_ARGS__);           \
-        }                                                                   \
-    } while (0)
-
-#define qemu_error_log(format, ...)                                         \
-    do {                                                                    \
-        __libc_format_log(ANDROID_LOG_ERROR, "memcheck", (format), ##__VA_ARGS__); \
-        if (tracing_flags & ERROR_TRACING_ENABLED) {                        \
-            qemu_log(ANDROID_LOG_ERROR, (format), ##__VA_ARGS__);           \
-        }                                                                   \
-    } while (0)
-
-#define qemu_info_log(format, ...)                                          \
-    do {                                                                    \
-        __libc_format_log(ANDROID_LOG_INFO, "memcheck", (format), ##__VA_ARGS__); \
-        if (tracing_flags & INFO_TRACING_ENABLED) {                         \
-            qemu_log(ANDROID_LOG_INFO, (format), ##__VA_ARGS__);            \
-        }                                                                   \
-    } while (0)
-
-/* Logs message dumping MallocDesc instance at the end of the message.
- * Param:
- *  type - Message type: debug, error, or info
- *  desc - MallocDesc instance to dump.
- *  fmt + rest - Formats message preceding dumped descriptor.
-*/
-#define log_mdesc(type, desc, fmt, ...)                                    \
-    do {                                                                    \
-        if (tracing_enabled(type)) {                                        \
-            char log_str[4096];                                             \
-            __libc_format_buffer(log_str, sizeof(log_str), fmt, ##__VA_ARGS__); \
-            log_str[sizeof(log_str) - 1] = '\0';                            \
-            size_t str_len = strlen(log_str);                               \
-            dump_malloc_descriptor(log_str + str_len,                       \
-                                   sizeof(log_str) - str_len,               \
-                                   (desc));                                 \
-            type##_log("%s", log_str);                                      \
-        }                                                                   \
-    } while (0)
-
-// =============================================================================
-// Static data
-// =============================================================================
-
-// The underlying malloc implementation to use to get memory.
-static const MallocDebug* g_malloc_dispatch = NULL;
-
-/* Emulator's magic page address.
- * This page (mapped on /dev/qemu_trace device) is used to fire up events
- * in the emulator. */
-static volatile void* qtrace = NULL;
-
-/* Cached PID of the process in context of which this libc instance
- * has been initialized. */
-static uint32_t malloc_pid = 0;
-
-/* Memory allocation alignment that is used in the malloc implementation.
- * This variable is updated by memcheck_initialize routine. */
-static uint32_t malloc_alignment = 8;
-
-/* Tracing flags. These flags control which types of logging messages are
- * enabled by the emulator. See XXX_TRACING_ENABLED for the values of flags
- * stored in this variable. This variable is updated by memcheck_initialize
- * routine. */
-static uint32_t tracing_flags = 0;
-
-// =============================================================================
-// Static routines
-// =============================================================================
-
-/* Gets pointer, returned to malloc caller for the given allocation decriptor.
- * Param:
- *  desc - Allocation descriptor.
- * Return:
- *  Pointer to the allocated memory returned to the malloc caller.
- */
-static inline void* mallocdesc_user_ptr(const MallocDesc* desc) {
-    return static_cast<char*>(desc->ptr) + desc->prefix_size;
-}
-
-/* Gets size of memory block actually allocated from the heap for the given
- * allocation decriptor.
- * Param:
- *  desc - Allocation descriptor.
- * Return:
- *  Size of memory block actually allocated from the heap.
- */
-static inline uint32_t mallocdesc_alloc_size(const MallocDesc* desc) {
-    return desc->prefix_size + desc->requested_bytes + desc->suffix_size;
-}
-
-/* Gets pointer to the end of the allocated block for the given descriptor.
- * Param:
- *  desc - Descriptor for the memory block, allocated in malloc handler.
- * Return:
- *  Pointer to the end of (one byte past) the allocated block.
- */
-static inline void* mallocdesc_alloc_end(const MallocDesc* desc) {
-    return static_cast<char*>(desc->ptr) + mallocdesc_alloc_size(desc);
-}
-
-/* Fires up an event in the emulator.
- * Param:
- *  code - Event code (one of the TRACE_DEV_XXX).
- *  val  - Event's value parameter.
- */
-static inline void notify_qemu(uint32_t code, uintptr_t val) {
-    if (NULL != qtrace) {
-        *(volatile uintptr_t*)((uintptr_t)qtrace + ((code - 1024) << 2)) = val;
-    }
-}
-
-/* Prints a zero-terminated string to the emulator's stdout (fires up
- * TRACE_DEV_REG_PRINT_USER_STR event in the emulator).
- * Param:
- *  str - Zero-terminated string to print.
- */
-static void notify_qemu_string(const char* str) {
-    if (str != NULL) {
-        notify_qemu(TRACE_DEV_REG_PRINT_USER_STR, reinterpret_cast<uintptr_t>(str));
-    }
-}
-
-/* Fires up TRACE_DEV_REG_LIBC_INIT event in the emulator.
- * Param:
- *  pid - ID of the process that initialized libc.
- */
-static void notify_qemu_libc_initialized(uint32_t pid) {
-    notify_qemu(TRACE_DEV_REG_LIBC_INIT, pid);
-}
-
-/* Fires up TRACE_DEV_REG_MALLOC event in the emulator.
- * Param:
- *  desc - Pointer to MallocDesc instance containing allocated block
- *      information.
- * Return:
- *  Zero on success, or -1 on failure. Note that on failure libc_pid field of
- *  the desc parameter passed to this routine has been zeroed out by the
- *  emulator.
- */
-static inline int notify_qemu_malloc(volatile MallocDesc* desc) {
-    desc->libc_pid = malloc_pid;
-    desc->allocator_pid = getpid();
-    desc->av_count = 0;
-    notify_qemu(TRACE_DEV_REG_MALLOC, reinterpret_cast<uintptr_t>(desc));
-
-    /* Emulator reports failure by zeroing libc_pid field of the
-     * descriptor. */
-    return desc->libc_pid != 0 ? 0 : -1;
-}
-
-/* Fires up TRACE_DEV_REG_FREE_PTR event in the emulator.
- * Param:
- *  ptr - Pointer to the memory block that's being freed.
- * Return:
- *  Zero on success, or -1 on failure.
- */
-static inline int notify_qemu_free(void* ptr_to_free) {
-    volatile MallocFree free_desc;
-
-    free_desc.ptr = ptr_to_free;
-    free_desc.libc_pid = malloc_pid;
-    free_desc.free_pid = getpid();
-    notify_qemu(TRACE_DEV_REG_FREE_PTR, reinterpret_cast<uintptr_t>(&free_desc));
-
-    /* Emulator reports failure by zeroing libc_pid field of the
-     * descriptor. */
-    return free_desc.libc_pid != 0 ? 0 : -1;
-}
-
-/* Fires up TRACE_DEV_REG_QUERY_MALLOC event in the emulator.
- * Param:
- *  ptr - Pointer to request allocation information for.
- *  desc - Pointer to MallocDesc instance that will receive allocation
- *      information.
- *  routine - Code of the allocation routine, in context of which query is made:
- *      1 - free
- *      2 - realloc
- * Return:
- *  Zero on success, or -1 on failure.
- */
-static inline int query_qemu_malloc_info(const void* ptr, MallocDesc* desc, uint32_t routine) {
-    volatile MallocDescQuery query;
-
-    query.ptr = ptr;
-    query.libc_pid = malloc_pid;
-    query.query_pid = getpid();
-    query.routine = routine;
-    query.desc = desc;
-    notify_qemu(TRACE_DEV_REG_QUERY_MALLOC, reinterpret_cast<uintptr_t>(&query));
-
-    /* Emulator reports failure by zeroing libc_pid field of the
-     * descriptor. */
-    return query.libc_pid != 0 ? 0 : -1;
-}
-
-/* Logs a message to emulator's stdout.
- * Param:
- *  prio - Message priority (debug, info, or error)
- *  fmt + rest - Message format and parameters.
- */
-static void qemu_log(int prio, const char* fmt, ...) {
-    va_list ap;
-    char buf[4096];
-    const char* prefix;
-
-    /* Choose message prefix depending on the priority value. */
-    switch (prio) {
-        case ANDROID_LOG_ERROR:
-            if (!tracing_enabled(error)) {
-                return;
-            }
-            prefix = "E";
-            break;
-        case ANDROID_LOG_INFO:
-            if (!tracing_enabled(info)) {
-                return;
-            }
-            prefix = "I";
-            break;
-        case ANDROID_LOG_DEBUG:
-        default:
-            if (!tracing_enabled(debug)) {
-                return;
-            }
-            prefix = "D";
-            break;
-    }
-
-    va_start(ap, fmt);
-    vsnprintf(buf, sizeof(buf), fmt, ap);
-    va_end(ap);
-    buf[sizeof(buf) - 1] = '\0';
-
-    TR("%s/memcheck: %s\n", prefix, buf);
-}
-
-/* Dumps content of memory allocation descriptor to a string.
- * Param:
- *  str - String to dump descriptor to.
- *  str_buf_size - Size of string's buffer.
- *  desc - Descriptor to dump.
- */
-static void dump_malloc_descriptor(char* str, size_t str_buf_size, const MallocDesc* desc) {
-    if (str_buf_size) {
-        snprintf(str, str_buf_size,
-                 "MDesc: %p: %p <-> %p [%u + %u + %u] by pid=%03u in libc_pid=%03u",
-                 mallocdesc_user_ptr(desc), desc->ptr,
-                 mallocdesc_alloc_end(desc), desc->prefix_size,
-                 desc->requested_bytes, desc->suffix_size, desc->allocator_pid,
-                 desc->libc_pid);
-        str[str_buf_size - 1] = '\0';
-    }
-}
-
-#if TEST_ACCESS_VIOLATIONS
-/* Causes an access violation on allocation descriptor, and verifies that
- * violation has been detected by memory checker in the emulator.
- */
-static void test_access_violation(const MallocDesc* desc) {
-    MallocDesc desc_chk;
-    char ch;
-    volatile char* prefix = (volatile char*)desc->ptr;
-    volatile char* suffix = (volatile char*)mallocdesc_user_ptr(desc) +
-                                            desc->requested_bytes;
-    /* We're causing AV by reading from the prefix and suffix areas of the
-     * allocated block. This should produce two access violations, so when we
-     * get allocation descriptor from QEMU, av_counter should be bigger than
-     * av_counter of the original descriptor by 2. */
-    ch = *prefix;
-    ch = *suffix;
-    if (!query_qemu_malloc_info(mallocdesc_user_ptr(desc), &desc_chk, 2) &&
-        desc_chk.av_count != (desc->av_count + 2)) {
-        log_mdesc(error, &desc_chk,
-                  "<libc_pid=%03u, pid=%03u>: malloc: Access violation test failed:\n"
-                  "Expected violations count %u is not equal to the actually reported %u",
-                  malloc_pid, getpid(), desc->av_count + 2,
-                  desc_chk.av_count);
-    }
-}
-#endif  // TEST_ACCESS_VIOLATIONS
-
-// =============================================================================
-// API routines
-// =============================================================================
-
-extern "C" void* qemu_instrumented_calloc(size_t, size_t);
-extern "C" void  qemu_instrumented_free(void*);
-extern "C" struct mallinfo qemu_instrumented_mallinfo();
-extern "C" void* qemu_instrumented_malloc(size_t);
-extern "C" size_t qemu_instrumented_malloc_usable_size(const void*);
-extern "C" void* qemu_instrumented_memalign(size_t, size_t);
-extern "C" int qemu_instrumented_posix_memalign(void**, size_t, size_t);
-extern "C" void* qemu_instrumented_pvalloc(size_t);
-extern "C" void* qemu_instrumented_realloc(void*, size_t);
-extern "C" void* qemu_instrumented_valloc(size_t);
-
-/* Initializes malloc debugging instrumentation for the emulator.
- * This routine is called from malloc_init_impl routine implemented in
- * bionic/libc/bionic/malloc_debug_common.c when malloc debugging gets
- * initialized for a process. The way malloc debugging implementation is
- * done, it is guaranteed that this routine will be called just once per
- * process.
- * Return:
- *  0 on success, or -1 on failure.
-*/
-extern "C" bool malloc_debug_initialize(HashTable*, const MallocDebug* malloc_dispatch) {
-    g_malloc_dispatch = malloc_dispatch;
-
-    /* We will be using emulator's magic page to report memory allocation
-     * activities. In essence, what magic page does, it translates writes to
-     * the memory mapped spaces into writes to an I/O port that emulator
-     * "listens to" on the other end. Note that until we open and map that
-     * device, logging to emulator's stdout will not be available. */
-    int fd = open("/dev/qemu_trace", O_CLOEXEC | O_RDWR);
-    if (fd < 0) {
-        error_log("Unable to open /dev/qemu_trace");
-        return false;
-    } else {
-        qtrace = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-        close(fd);
-
-        if (qtrace == MAP_FAILED) {
-            qtrace = NULL;
-            error_log("Unable to mmap /dev/qemu_trace");
-            return false;
-        }
-    }
-
-    /* Cache pid of the process this library has been initialized for. */
-    malloc_pid = getpid();
-    return true;
-}
-
-/* Completes malloc debugging instrumentation for the emulator.
- * Note that this routine is called after successful return from
- * malloc_debug_initialize, which means that connection to the emulator via
- * "magic page" has been established.
- * Param:
- *  alignment - Alignment requirement set for memiry allocations.
- *  memcheck_param - Emulator's -memcheck option parameters. This string
- *      contains abbreviation for guest events that are enabled for tracing.
- * Return:
- *  0 on success, or -1 on failure.
-*/
-extern "C" int memcheck_initialize(int alignment, const char* memcheck_param) {
-    malloc_alignment = alignment;
-
-    /* Parse -memcheck parameter for the guest tracing flags. */
-    while (*memcheck_param != '\0') {
-        switch (*memcheck_param) {
-            case 'a':
-                // Enable all messages from the guest.
-                tracing_flags |= ALL_TRACING_ENABLED;
-                break;
-            case 'd':
-                // Enable debug messages from the guest.
-                tracing_flags |= DEBUG_TRACING_ENABLED;
-                break;
-            case 'e':
-                // Enable error messages from the guest.
-                tracing_flags |= ERROR_TRACING_ENABLED;
-                break;
-            case 'i':
-                // Enable info messages from the guest.
-                tracing_flags |= INFO_TRACING_ENABLED;
-                break;
-            default:
-                break;
-        }
-        if (tracing_flags == ALL_TRACING_ENABLED) {
-            break;
-        }
-        memcheck_param++;
-    }
-
-    notify_qemu_libc_initialized(malloc_pid);
-
-    qemu_debug_log("Instrumented for pid=%03u: malloc=%p, free=%p, calloc=%p, realloc=%p, memalign=%p",
-              malloc_pid, qemu_instrumented_malloc, qemu_instrumented_free,
-              qemu_instrumented_calloc, qemu_instrumented_realloc,
-              qemu_instrumented_memalign);
-
-    return 0;
-}
-
-/* This routine serves as entry point for 'malloc'.
- * Primary responsibility of this routine is to allocate requested number of
- * bytes (plus prefix, and suffix guards), and report allocation to the
- * emulator.
- */
-extern "C" void* qemu_instrumented_malloc(size_t bytes) {
-    MallocDesc desc;
-
-    /* Initialize block descriptor and allocate memory. Note that malloc
-     * returns a valid pointer on zero allocation. Lets mimic this behavior. */
-    desc.prefix_size = DEFAULT_PREFIX_SIZE;
-    desc.requested_bytes = bytes;
-    desc.suffix_size = DEFAULT_SUFFIX_SIZE;
-    size_t size = mallocdesc_alloc_size(&desc);
-    if (size < bytes) { // Overflow
-        qemu_error_log("<libc_pid=%03u, pid=%03u> malloc: malloc(%zu) overflow caused failure.",
-                       malloc_pid, getpid(), bytes);
-        errno = ENOMEM;
-        return NULL;
-    }
-    desc.ptr = g_malloc_dispatch->malloc(size);
-    if (desc.ptr == NULL) {
-        qemu_error_log("<libc_pid=%03u, pid=%03u> malloc(%zu): malloc(%zu) failed.",
-                       malloc_pid, getpid(), bytes, size);
-        return NULL;
-    }
-
-    // Fire up event in the emulator.
-    if (notify_qemu_malloc(&desc)) {
-        log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: malloc: notify_malloc failed for ",
-                  malloc_pid, getpid());
-        g_malloc_dispatch->free(desc.ptr);
-        errno = ENOMEM;
-        return NULL;
-    } else {
-#if TEST_ACCESS_VIOLATIONS
-        test_access_violation(&desc);
-#endif  // TEST_ACCESS_VIOLATIONS
-        log_mdesc(info, &desc, "+++ <libc_pid=%03u, pid=%03u> malloc(%zu) -> ",
-                  malloc_pid, getpid(), bytes);
-        return mallocdesc_user_ptr(&desc);
-    }
-}
-
-/* This routine serves as entry point for 'malloc'.
- * Primary responsibility of this routine is to free requested memory, and
- * report free block to the emulator.
- */
-extern "C" void qemu_instrumented_free(void* mem) {
-    MallocDesc desc;
-
-    if (mem == NULL) {
-        // Just let go NULL free
-        g_malloc_dispatch->free(mem);
-        return;
-    }
-
-    // Query emulator for the freeing block information.
-    if (query_qemu_malloc_info(mem, &desc, 1)) {
-        error_log("<libc_pid=%03u, pid=%03u>: free(%p) query_info failed.",
-                  malloc_pid, getpid(), mem);
-        return;
-    }
-
-#if TEST_ACCESS_VIOLATIONS
-    test_access_violation(&desc);
-#endif  // TEST_ACCESS_VIOLATIONS
-
-    /* Make sure that pointer that's being freed matches what we expect
-     * for this memory block. Note that this violation should be already
-     * caught in the emulator. */
-    if (mem != mallocdesc_user_ptr(&desc)) {
-        log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: free(%p) is invalid for ",
-                  malloc_pid, getpid(), mem);
-        return;
-    }
-
-    // Fire up event in the emulator and free block that was actually allocated.
-    if (notify_qemu_free(mem)) {
-        log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: free(%p) notify_free failed for ",
-                  malloc_pid, getpid(), mem);
-    } else {
-        log_mdesc(info, &desc, "--- <libc_pid=%03u, pid=%03u> free(%p) -> ",
-                  malloc_pid, getpid(), mem);
-        g_malloc_dispatch->free(desc.ptr);
-    }
-}
-
-/* This routine serves as entry point for 'calloc'.
- * This routine behaves similarly to qemu_instrumented_malloc.
- */
-extern "C" void* qemu_instrumented_calloc(size_t n_elements, size_t elem_size) {
-    if (n_elements == 0 || elem_size == 0) {
-        // Just let go zero bytes allocation.
-        qemu_info_log("::: <libc_pid=%03u, pid=%03u>: Zero calloc redir to malloc",
-                      malloc_pid, getpid());
-        return qemu_instrumented_malloc(0);
-    }
-
-    // Fail on overflow - just to be safe even though this code runs only
-    // within the debugging C library, not the production one.
-    if (n_elements && SIZE_MAX / n_elements < elem_size) {
-        qemu_error_log("<libc_pid=%03u, pid=%03u> calloc: calloc(%zu, %zu) overflow caused failure.",
-                       malloc_pid, getpid(), n_elements, elem_size);
-        errno = ENOMEM;
-        return NULL;
-    }
-
-    MallocDesc desc;
-
-    /* Calculating prefix size. The trick here is to make sure that
-     * first element (returned to the caller) is properly aligned. */
-    if (DEFAULT_PREFIX_SIZE >= elem_size) {
-        /* If default alignment is bigger than element size, we will
-         * set our prefix size to the default alignment size. */
-        desc.prefix_size = DEFAULT_PREFIX_SIZE;
-        /* For the suffix we will use whatever bytes remain from the prefix
-         * allocation size, aligned to the size of an element, plus the usual
-         * default suffix size. */
-        desc.suffix_size = (DEFAULT_PREFIX_SIZE % elem_size) +
-                           DEFAULT_SUFFIX_SIZE;
-    } else {
-        /* Make sure that prefix, and suffix sizes is at least elem_size,
-         * and first element returned to the caller is properly aligned. */
-        desc.prefix_size = elem_size + DEFAULT_PREFIX_SIZE - 1;
-        desc.prefix_size &= ~(malloc_alignment - 1);
-        desc.suffix_size = DEFAULT_SUFFIX_SIZE;
-    }
-    desc.requested_bytes = n_elements * elem_size;
-    size_t total_size = desc.requested_bytes + desc.prefix_size + desc.suffix_size;
-    if (total_size < desc.requested_bytes) { // Overflow
-        qemu_error_log("<libc_pid=%03u, pid=%03u> calloc: calloc(%zu, %zu) overflow caused failure.",
-                       malloc_pid, getpid(), n_elements, elem_size);
-        errno = ENOMEM;
-        return NULL;
-    }
-    size_t total_elements = total_size / elem_size;
-    total_size %= elem_size;
-    if (total_size != 0) {
-        // Add extra to the suffix area.
-        total_elements++;
-        desc.suffix_size += (elem_size - total_size);
-    }
-    desc.ptr = g_malloc_dispatch->calloc(total_elements, elem_size);
-    if (desc.ptr == NULL) {
-        error_log("<libc_pid=%03u, pid=%03u> calloc: calloc(%zu(%zu), %zu) (prx=%u, sfx=%u) failed.",
-                   malloc_pid, getpid(), n_elements, total_elements, elem_size,
-                   desc.prefix_size, desc.suffix_size);
-        return NULL;
-    }
-
-    if (notify_qemu_malloc(&desc)) {
-        log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: calloc(%zu(%zu), %zu): notify_malloc failed for ",
-                  malloc_pid, getpid(), n_elements, total_elements, elem_size);
-        g_malloc_dispatch->free(desc.ptr);
-        errno = ENOMEM;
-        return NULL;
-    } else {
-#if TEST_ACCESS_VIOLATIONS
-        test_access_violation(&desc);
-#endif  // TEST_ACCESS_VIOLATIONS
-        log_mdesc(info, &desc, "### <libc_pid=%03u, pid=%03u> calloc(%zu(%zu), %zu) -> ",
-                  malloc_pid, getpid(), n_elements, total_elements, elem_size);
-        return mallocdesc_user_ptr(&desc);
-    }
-}
-
-/* This routine serves as entry point for 'realloc'.
- * This routine behaves similarly to qemu_instrumented_free +
- * qemu_instrumented_malloc. Note that this modifies behavior of "shrinking" an
- * allocation, but overall it doesn't seem to matter, as caller of realloc
- * should not expect that pointer returned after shrinking will remain the same.
- */
-extern "C" void* qemu_instrumented_realloc(void* mem, size_t bytes) {
-    if (mem == NULL) {
-        // Nothing to realloc. just do regular malloc.
-        qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zu) redir to malloc",
-                      malloc_pid, getpid(), mem, bytes);
-        return qemu_instrumented_malloc(bytes);
-    }
-
-    if (bytes == 0) {
-        // This is a "free" condition.
-        qemu_info_log("::: <libc_pid=%03u, pid=%03u>: realloc(%p, %zu) redir to free and malloc",
-                      malloc_pid, getpid(), mem, bytes);
-        qemu_instrumented_free(mem);
-
-        // This is what realloc does for a "free" realloc.
-        return NULL;
-    }
-
-    // Query emulator for the reallocating block information.
-    MallocDesc cur_desc;
-    if (query_qemu_malloc_info(mem, &cur_desc, 2)) {
-        // Note that this violation should be already caught in the emulator.
-        error_log("<libc_pid=%03u, pid=%03u>: realloc(%p, %zu) query_info failed.",
-                  malloc_pid, getpid(), mem, bytes);
-        errno = ENOMEM;
-        return NULL;
-    }
-
-#if TEST_ACCESS_VIOLATIONS
-    test_access_violation(&cur_desc);
-#endif  // TEST_ACCESS_VIOLATIONS
-
-    /* Make sure that reallocating pointer value is what we would expect
-     * for this memory block. Note that this violation should be already caught
-     * in the emulator.*/
-    if (mem != mallocdesc_user_ptr(&cur_desc)) {
-        log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu) is invalid for ",
-                  malloc_pid, getpid(), mem, bytes);
-        errno = ENOMEM;
-        return NULL;
-    }
-
-    /* TODO: We're a bit inefficient here, always allocating new block from
-     * the heap. If this realloc shrinks current buffer, we can just do the
-     * shrinking "in place", adjusting suffix_size in the allocation descriptor
-     * for this block that is stored in the emulator. */
-
-    // Initialize descriptor for the new block.
-    MallocDesc new_desc;
-    new_desc.prefix_size = DEFAULT_PREFIX_SIZE;
-    new_desc.requested_bytes = bytes;
-    new_desc.suffix_size = DEFAULT_SUFFIX_SIZE;
-    size_t new_size = mallocdesc_alloc_size(&new_desc);
-    if (new_size < bytes) { // Overflow
-        qemu_error_log("<libc_pid=%03u, pid=%03u>: realloc(%p, %zu): malloc(%zu) failed due to overflow",
-                       malloc_pid, getpid(), mem, bytes, new_size);
-        errno = ENOMEM;
-        return NULL;
-    }
-    new_desc.ptr = g_malloc_dispatch->malloc(new_size);
-    if (new_desc.ptr == NULL) {
-        log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu): malloc(%zu) failed on ",
-                  malloc_pid, getpid(), mem, bytes, new_size);
-        return NULL;
-    }
-    void* new_mem = mallocdesc_user_ptr(&new_desc);
-
-    // Copy user data from old block to the new one.
-    size_t to_copy = bytes < cur_desc.requested_bytes ? bytes : cur_desc.requested_bytes;
-    if (to_copy != 0) {
-        memcpy(new_mem, mallocdesc_user_ptr(&cur_desc), to_copy);
-    }
-
-    // Register new block with emulator.
-    if (notify_qemu_malloc(&new_desc)) {
-        log_mdesc(error, &new_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu) notify_malloc failed -> ",
-                  malloc_pid, getpid(), mem, bytes);
-        log_mdesc(error, &cur_desc, "                                                                <- ");
-        g_malloc_dispatch->free(new_desc.ptr);
-        errno = ENOMEM;
-        return NULL;
-    }
-
-#if TEST_ACCESS_VIOLATIONS
-    test_access_violation(&new_desc);
-#endif  // TEST_ACCESS_VIOLATIONS
-
-    // Free old block.
-    if (notify_qemu_free(mem)) {
-        log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: realloc(%p, %zu): notify_free failed for ",
-                  malloc_pid, getpid(), mem, bytes);
-        /* Since we registered new decriptor with the emulator, we need
-         * to unregister it before freeing newly allocated block. */
-        notify_qemu_free(mallocdesc_user_ptr(&new_desc));
-        g_malloc_dispatch->free(new_desc.ptr);
-        errno = ENOMEM;
-        return NULL;
-    }
-    g_malloc_dispatch->free(cur_desc.ptr);
-
-    log_mdesc(info, &new_desc, "=== <libc_pid=%03u, pid=%03u>: realloc(%p, %zu) -> ",
-              malloc_pid, getpid(), mem, bytes);
-    log_mdesc(info, &cur_desc, "                                               <- ");
-
-    return new_mem;
-}
-
-/* This routine serves as entry point for 'memalign'.
- * This routine behaves similarly to qemu_instrumented_malloc.
- */
-extern "C" void* qemu_instrumented_memalign(size_t alignment, size_t bytes) {
-    MallocDesc desc;
-
-    if (bytes == 0) {
-        // Just let go zero bytes allocation.
-        qemu_info_log("::: <libc_pid=%03u, pid=%03u>: memalign(%zx, %zu) redir to malloc",
-                 malloc_pid, getpid(), alignment, bytes);
-        return qemu_instrumented_malloc(0);
-    }
-
-    // Prefix size for aligned allocation must be equal to the alignment used
-    // for allocation in order to ensure proper alignment of the returned
-    // pointer. in case that alignment requirement is greater than prefix
-    // size.
-    if (alignment < DEFAULT_PREFIX_SIZE) {
-        alignment = DEFAULT_PREFIX_SIZE;
-    } else if (!powerof2(alignment)) {
-        alignment = BIONIC_ROUND_UP_POWER_OF_2(alignment);
-    }
-    desc.prefix_size = alignment;
-    desc.requested_bytes = bytes;
-    desc.suffix_size = DEFAULT_SUFFIX_SIZE;
-    size_t size = mallocdesc_alloc_size(&desc);
-    if (size < bytes) { // Overflow
-        qemu_error_log("<libc_pid=%03u, pid=%03u> memalign(%zx, %zu): malloc(%zu) failed due to overflow.",
-                       malloc_pid, getpid(), alignment, bytes, size);
-
-        return NULL;
-    }
-    desc.ptr = g_malloc_dispatch->memalign(desc.prefix_size, size);
-    if (desc.ptr == NULL) {
-        error_log("<libc_pid=%03u, pid=%03u> memalign(%zx, %zu): malloc(%zu) failed.",
-                  malloc_pid, getpid(), alignment, bytes, size);
-        return NULL;
-    }
-    if (notify_qemu_malloc(&desc)) {
-        log_mdesc(error, &desc, "<libc_pid=%03u, pid=%03u>: memalign(%zx, %zu): notify_malloc failed for ",
-                  malloc_pid, getpid(), alignment, bytes);
-        g_malloc_dispatch->free(desc.ptr);
-        return NULL;
-    }
-
-#if TEST_ACCESS_VIOLATIONS
-    test_access_violation(&desc);
-#endif  // TEST_ACCESS_VIOLATIONS
-
-    log_mdesc(info, &desc, "@@@ <libc_pid=%03u, pid=%03u> memalign(%zx, %zu) -> ",
-              malloc_pid, getpid(), alignment, bytes);
-    return mallocdesc_user_ptr(&desc);
-}
-
-extern "C" size_t qemu_instrumented_malloc_usable_size(const void* mem) {
-    MallocDesc cur_desc;
-
-    // Query emulator for the reallocating block information.
-    if (query_qemu_malloc_info(mem, &cur_desc, 2)) {
-        // Note that this violation should be already caught in the emulator.
-        error_log("<libc_pid=%03u, pid=%03u>: malloc_usable_size(%p) query_info failed.",
-                  malloc_pid, getpid(), mem);
-        return 0;
-    }
-
-    /* Make sure that reallocating pointer value is what we would expect
-     * for this memory block. Note that this violation should be already caught
-     * in the emulator.*/
-    if (mem != mallocdesc_user_ptr(&cur_desc)) {
-        log_mdesc(error, &cur_desc, "<libc_pid=%03u, pid=%03u>: malloc_usable_size(%p) is invalid for ",
-                  malloc_pid, getpid(), mem);
-        return 0;
-    }
-
-    /* during instrumentation, we can't really report anything more than requested_bytes */
-    return cur_desc.requested_bytes;
-}
-
-extern "C" struct mallinfo qemu_instrumented_mallinfo() {
-  return g_malloc_dispatch->mallinfo();
-}
-
-extern "C" int qemu_instrumented_posix_memalign(void** memptr, size_t alignment, size_t size) {
-  if ((alignment & (alignment - 1)) != 0) {
-    qemu_error_log("<libc_pid=%03u, pid=%03u> posix_memalign(%p, %zu, %zu): invalid alignment.",
-                   malloc_pid, getpid(), memptr, alignment, size);
-    return EINVAL;
-  }
-  int saved_errno = errno;
-  *memptr = qemu_instrumented_memalign(alignment, size);
-  errno = saved_errno;
-  return (*memptr != NULL) ? 0 : ENOMEM;
-}
-
-extern "C" void* qemu_instrumented_pvalloc(size_t bytes) {
-  size_t pagesize = getpagesize();
-  size_t size = BIONIC_ALIGN(bytes, pagesize);
-  if (size < bytes) { // Overflow
-    qemu_error_log("<libc_pid=%03u, pid=%03u> pvalloc(%zu): overflow (%zu).",
-                   malloc_pid, getpid(), bytes, size);
-    return NULL;
-  }
-  return qemu_instrumented_memalign(pagesize, size);
-}
-
-extern "C" void* qemu_instrumented_valloc(size_t size) {
-  return qemu_instrumented_memalign(getpagesize(), size);
-}
diff --git a/libc/malloc_debug/Android.mk b/libc/malloc_debug/Android.mk
new file mode 100644
index 0000000..f0f4056
--- /dev/null
+++ b/libc/malloc_debug/Android.mk
@@ -0,0 +1,74 @@
+LOCAL_PATH := $(call my-dir)
+
+libc_malloc_debug_src_files := \
+    BacktraceData.cpp \
+    Config.cpp \
+    DebugData.cpp \
+    debug_disable.cpp \
+    FreeTrackData.cpp \
+    GuardData.cpp \
+    malloc_debug.cpp \
+    TrackData.cpp \
+
+# ==============================================================
+# libc_malloc_debug.so
+# ==============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libc_malloc_debug
+
+LOCAL_SRC_FILES := \
+    $(libc_malloc_debug_src_files) \
+    backtrace.cpp \
+    MapData.cpp \
+
+LOCAL_CXX_STL := none
+
+# 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_LDFLAGS_32 := -Wl,--version-script,$(LOCAL_PATH)/exported32.map
+LOCAL_LDFLAGS_64 := -Wl,--version-script,$(LOCAL_PATH)/exported64.map
+LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
+LOCAL_C_INCLUDES += bionic/libc
+
+LOCAL_SANITIZE := never
+LOCAL_NATIVE_COVERAGE := false
+
+LOCAL_CFLAGS := \
+    -Wall \
+    -Werror \
+    -fno-stack-protector \
+
+include $(BUILD_SHARED_LIBRARY)
+
+# ==============================================================
+# Unit Tests
+# ==============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := malloc_debug_unit_tests
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SRC_FILES := \
+    tests/backtrace_fake.cpp \
+    tests/log_fake.cpp \
+    tests/libc_fake.cpp \
+    tests/property_fake.cpp \
+    tests/malloc_debug_config_tests.cpp \
+    tests/malloc_debug_unit_tests.cpp \
+    $(libc_malloc_debug_src_files) \
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/tests
+LOCAL_C_INCLUDES += bionic/libc
+
+LOCAL_SHARED_LIBRARIES := libbase
+
+LOCAL_CFLAGS := \
+    -Wall \
+    -Werror \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libc/malloc_debug/BacktraceData.cpp b/libc/malloc_debug/BacktraceData.cpp
new file mode 100644
index 0000000..9f39068
--- /dev/null
+++ b/libc/malloc_debug/BacktraceData.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <private/bionic_macros.h>
+
+#include "BacktraceData.h"
+#include "Config.h"
+#include "DebugData.h"
+#include "debug_log.h"
+#include "malloc_debug.h"
+
+BacktraceData::BacktraceData(const Config& config, size_t* offset) {
+  size_t hdr_len = sizeof(BacktraceHeader) + sizeof(uintptr_t) * config.backtrace_frames - 1;
+  alloc_offset_ = *offset;
+  *offset += BIONIC_ALIGN(hdr_len, sizeof(uintptr_t));
+  free_offset_ = *offset;
+  *offset += BIONIC_ALIGN(hdr_len, sizeof(uintptr_t));
+}
+
+static BacktraceData* g_backtrace_data = nullptr;
+
+static void EnableToggle(int, siginfo_t*, void*) {
+  if (g_backtrace_data->enabled()) {
+    g_backtrace_data->set_enabled(false);
+  } else {
+    g_backtrace_data->set_enabled(true);
+  }
+}
+
+bool BacktraceData::Initialize(const Config& config) {
+  enabled_ = config.backtrace_enabled;
+  if (config.backtrace_enable_on_signal) {
+    g_backtrace_data = this;
+
+    struct sigaction enable_act;
+    memset(&enable_act, 0, sizeof(enable_act));
+
+    enable_act.sa_sigaction = EnableToggle;
+    enable_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+    sigemptyset(&enable_act.sa_mask);
+    if (sigaction(config.backtrace_signal, &enable_act, nullptr) != 0) {
+      error_log("Unable to set up backtrace signal enable function: %s", strerror(errno));
+      return false;
+    }
+    info_log("%s: Run: 'kill -%d %d' to enable backtracing.", getprogname(),
+             config.backtrace_signal, getpid());
+  }
+  return true;
+}
diff --git a/libc/bionic/debug_backtrace.h b/libc/malloc_debug/BacktraceData.h
similarity index 63%
rename from libc/bionic/debug_backtrace.h
rename to libc/malloc_debug/BacktraceData.h
index adc9d1d..10daec7 100644
--- a/libc/bionic/debug_backtrace.h
+++ b/libc/malloc_debug/BacktraceData.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,15 +26,36 @@
  * SUCH DAMAGE.
  */
 
-#ifndef DEBUG_BACKTRACE_H
-#define DEBUG_BACKTRACE_H
+#ifndef DEBUG_MALLOC_BACKTRACEDATA_H
+#define DEBUG_MALLOC_BACKTRACEDATA_H
 
 #include <stdint.h>
-#include <sys/cdefs.h>
 
-__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);
+#include <private/bionic_macros.h>
 
-#endif /* DEBUG_BACKTRACE_H */
+// Forward declarations.
+struct Config;
+
+class BacktraceData {
+ public:
+  BacktraceData(const Config& config, size_t* offset);
+  virtual ~BacktraceData() = default;
+
+  bool Initialize(const Config& config);
+
+  inline size_t alloc_offset() { return alloc_offset_; }
+  inline size_t free_offset() { return free_offset_; }
+
+  bool enabled() { return enabled_; }
+  void set_enabled(bool enabled) { enabled_ = enabled; }
+
+ private:
+  size_t alloc_offset_ = 0;
+  size_t free_offset_ = 0;
+
+  volatile bool enabled_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(BacktraceData);
+};
+
+#endif // DEBUG_MALLOC_BACKTRACEDATA_H
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
new file mode 100644
index 0000000..e27076d
--- /dev/null
+++ b/libc/malloc_debug/Config.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+
+#include <string>
+#include <vector>
+
+#include <sys/system_properties.h>
+
+#include <private/bionic_macros.h>
+
+#include "Config.h"
+#include "debug_log.h"
+
+struct Feature {
+  std::string name;
+  size_t default_value = 0;
+
+  uint64_t option = 0;
+  size_t* value = nullptr;
+  bool* config = nullptr;
+  // If set to true, then all of the options following are set on until
+  // for which the combo_option value is set.
+  bool combo_option = false;
+};
+
+class PropertyParser {
+ public:
+  PropertyParser(const char* property) : cur_(property) {}
+
+  bool Get(std::string* property, size_t* value, bool* value_set);
+
+  bool Done() { return done_; }
+
+  void LogUsage();
+
+  static constexpr uint8_t DEFAULT_FILL_ALLOC_VALUE = 0xeb;
+  static constexpr uint8_t DEFAULT_FILL_FREE_VALUE = 0xef;
+
+  static constexpr uint8_t DEFAULT_FRONT_GUARD_VALUE = 0xaa;
+  static constexpr uint8_t DEFAULT_REAR_GUARD_VALUE = 0xbb;
+
+ private:
+  const char* cur_ = nullptr;
+
+  bool done_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(PropertyParser);
+};
+
+bool PropertyParser::Get(std::string* property, size_t* value, bool* value_set) {
+  // Process each property name we can find.
+  while (isspace(*cur_))
+    ++cur_;
+
+  if (*cur_ == '\0') {
+    done_ = true;
+    return false;
+  }
+
+  const char* property_start = cur_;
+  while (!isspace(*cur_) && *cur_ != '=' && *cur_ != '\0')
+    ++cur_;
+
+  *property = std::string(property_start, cur_ - property_start);
+
+  // Skip any spaces after the name.
+  while (isspace(*cur_) && *cur_ != '=' && *cur_ != '\0')
+    ++cur_;
+
+  if (*cur_ == '=') {
+    ++cur_;
+    errno = 0;
+    *value_set = true;
+    char* end;
+    long read_value = strtol(cur_, const_cast<char**>(&end), 10);
+    if (errno != 0) {
+      error_log("%s: bad value for option '%s': %s", getprogname(), property->c_str(),
+                strerror(errno));
+      return false;
+    }
+    if (cur_ == end || (!isspace(*end) && *end != '\0')) {
+      if (cur_ == end) {
+        error_log("%s: bad value for option '%s'", getprogname(), property->c_str());
+      } else {
+        error_log("%s: bad value for option '%s', non space found after option: %s",
+                  getprogname(), property->c_str(), end);
+      }
+      return false;
+    } else if (read_value <= 0) {
+      error_log("%s: bad value for option '%s', value must be > 0: %ld",
+                getprogname(), property->c_str(), read_value);
+      return false;
+    }
+    *value = static_cast<size_t>(read_value);
+    cur_ = end;
+  } else {
+    *value_set = false;
+  }
+  return true;
+}
+
+void PropertyParser::LogUsage() {
+  error_log("malloc debug options usage:");
+  error_log("");
+  error_log("  front_guard[=XX]");
+  error_log("    Enables a front guard on all allocations. If XX is set");
+  error_log("    it sets the number of bytes in the guard. The default is");
+  error_log("    32 bytes.");
+  error_log("");
+  error_log("  rear_guard[=XX]");
+  error_log("    Enables a rear guard on all allocations. If XX is set");
+  error_log("    it sets the number of bytes in the guard. The default is");
+  error_log("    32 bytes.");
+  error_log("");
+  error_log("  guard[=XX]");
+  error_log("    Enables both a front guard and a rear guard on all allocations.");
+  error_log("    If XX is set it sets the number of bytes in both guards.");
+  error_log("    The default is 32 bytes.");
+  error_log("");
+  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("    The default is 16 frames.");
+  error_log("");
+  error_log("  backtrace_enable_on_signal[=XX]");
+  error_log("    Enable capturing the backtrace at the point of allocation.");
+  error_log("    The backtrace capture is not enabled until the process");
+  error_log("    receives a signal. If XX is set it sets the number of backtrace");
+  error_log("    frames. The default is 16 frames.");
+  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");
+  error_log("    allocation. The default is to fill the entire allocation.");
+  error_log("");
+  error_log("  fill_on_free[=XX]");
+  error_log("    On free, fill with the value 0x%02x. If XX is set it will",
+            DEFAULT_FILL_FREE_VALUE);
+  error_log("    only fill up to XX bytes of the allocation. The default is to");
+  error_log("    fill the entire allocation.");
+  error_log("");
+  error_log("  fill[=XX]");
+  error_log("    On both first allocation free, fill with the value 0x%02x on",
+            DEFAULT_FILL_ALLOC_VALUE);
+  error_log("    first allocation and the value 0x%02x. If XX is set, only fill",
+            DEFAULT_FILL_FREE_VALUE);
+  error_log("    up to XX bytes. The default is to fill the entire allocation.");
+  error_log("");
+  error_log("  expand_alloc[=XX]");
+  error_log("    Allocate an extra number of bytes for every allocation call.");
+  error_log("    If XX is set, that is the number of bytes to expand the");
+  error_log("    allocation by. The default is 16 bytes.");
+  error_log("");
+  error_log("  free_track[=XX]");
+  error_log("    When a pointer is freed, do not free the memory right away.");
+  error_log("    Instead, keep XX of these allocations around and then verify");
+  error_log("    that they have not been modified when the total number of freed");
+  error_log("    allocations exceeds the XX amount. When the program terminates,");
+  error_log("    the rest of these allocations are verified.");
+  error_log("    The default is to record 100 allocations.");
+  error_log("");
+  error_log("  leak_track");
+  error_log("    Enable the leak tracking of memory allocations.");
+}
+
+static bool SetFeature(const Feature& feature, size_t value, bool value_set) {
+  if (feature.config) {
+    *feature.config = true;
+  }
+  if (feature.value != nullptr) {
+    if (value_set) {
+      *feature.value = value;
+    } else {
+      *feature.value = feature.default_value;
+    }
+  } else if (value_set) {
+     error_log("%s: value set for option '%s' which does not take a value",
+               getprogname(), feature.name.c_str());
+     return false;
+  }
+  return true;
+}
+
+// This function is designed to be called once. A second call will not
+// reset all variables.
+bool Config::SetFromProperties() {
+  char property_str[PROP_VALUE_MAX];
+  memset(property_str, 0, sizeof(property_str));
+  if (!__system_property_get("libc.debug.malloc.options", property_str)) {
+    return false;
+  }
+
+  // Initialize a few default values.
+  fill_alloc_value = PropertyParser::DEFAULT_FILL_ALLOC_VALUE;
+  fill_free_value = PropertyParser::DEFAULT_FILL_FREE_VALUE;
+  front_guard_value = PropertyParser::DEFAULT_FRONT_GUARD_VALUE;
+  rear_guard_value = PropertyParser::DEFAULT_REAR_GUARD_VALUE;
+  backtrace_signal = SIGRTMIN + 10;
+
+  // Parse the options are of the format:
+  //   option_name or option_name=XX
+
+  // Supported features:
+  const Feature features[] = {
+    { .name="guard", .default_value=32, .option=0, .combo_option=true },
+    // Enable front guard. Value is the size of the guard.
+    { .name="front_guard", .default_value=32, .option=FRONT_GUARD,
+      .value=&this->front_guard_bytes, .combo_option=true },
+    // Enable end guard. Value is the size of the guard.
+    { .name="rear_guard", .default_value=32, .option=REAR_GUARD,
+      .value=&this->rear_guard_bytes, .combo_option=true },
+
+    // Enable logging the backtrace on allocation. Value is the total
+    // number of frames to log.
+    { .name="backtrace", .default_value=16, .option=BACKTRACE | TRACK_ALLOCS,
+      .value=&this->backtrace_frames, .config=&this->backtrace_enabled },
+    // Enable gathering backtrace values on a signal.
+    { .name="backtrace_enable_on_signal", .default_value=16, .option=BACKTRACE | TRACK_ALLOCS,
+      .value=&this->backtrace_frames, .config=&this->backtrace_enable_on_signal },
+
+    { .name="fill", .default_value=SIZE_MAX, .option=0, .combo_option=true },
+    // Fill the allocation with an arbitrary pattern on allocation.
+    // Value is the number of bytes of the allocation to fill
+    // (default entire allocation).
+    { .name="fill_on_alloc", .default_value=SIZE_MAX, .option=FILL_ON_ALLOC,
+      .value=&this->fill_on_alloc_bytes, .combo_option=true },
+    // Fill the allocation with an arbitrary pattern on free.
+    // Value is the number of bytes of the allocation to fill
+    // (default entire allocation).
+    { .name="fill_on_free", .default_value=SIZE_MAX, .option=FILL_ON_FREE,
+      .value=&this->fill_on_free_bytes, .combo_option=true },
+
+    // Expand the size of every alloc by this number bytes. Value is
+    // the total number of bytes to expand every allocation by.
+    { .name="expand_alloc", .default_value=16, .option=EXPAND_ALLOC,
+      .value=&this->expand_alloc_bytes, },
+
+    // Keep track of the freed allocations and verify at a later date
+    // that they have not been used. Turning this on, also turns on
+    // fill on free.
+    { .name="free_track", .default_value=100, .option=FREE_TRACK | FILL_ON_FREE,
+      .value=&this->free_track_allocations, },
+
+    // Enable printing leaked allocations.
+    { .name="leak_track", .option=LEAK_TRACK | TRACK_ALLOCS },
+  };
+
+  // Process each property name we can find.
+  std::string property;
+  size_t value;
+  bool value_set;
+  PropertyParser parser(property_str);
+  bool found = false;
+  bool valid = true;
+  while (valid && parser.Get(&property, &value, &value_set)) {
+    for (size_t i = 0; i < sizeof(features)/sizeof(Feature); i++) {
+      if (property == features[i].name) {
+        if (features[i].option == 0 && features[i].combo_option) {
+          i++;
+          for (; i < sizeof(features)/sizeof(Feature) && features[i].combo_option; i++) {
+            if (!SetFeature(features[i], value, value_set)) {
+              valid = false;
+              break;
+            }
+            options |= features[i].option;
+          }
+          if (!valid) {
+            break;
+          }
+        } else {
+          if (!SetFeature(features[i], value, value_set)) {
+            valid = false;
+            break;
+          }
+          options |= features[i].option;
+        }
+        found = true;
+        break;
+      }
+    }
+    if (valid && !found) {
+      error_log("%s: unknown option %s", getprogname(), property.c_str());
+      valid = false;
+      break;
+    }
+  }
+
+  valid = valid && parser.Done();
+
+  if (valid) {
+    // It's necessary to align the front guard to sizeof(uintptr_t) to
+    // make sure that the header is aligned properly.
+    if (options & FRONT_GUARD) {
+      front_guard_bytes = BIONIC_ALIGN(front_guard_bytes, sizeof(uintptr_t));
+    }
+
+    // This situation can occur if the free_track option is specified and
+    // the fill_on_free option is not. In this case, indicate the whole
+    // allocation should be filled.
+    if ((options & FILL_ON_FREE) && fill_on_free_bytes == 0) {
+      fill_on_free_bytes = SIZE_MAX;
+    }
+  } else {
+    parser.LogUsage();
+  }
+
+  return valid;
+}
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
new file mode 100644
index 0000000..4b91e2b
--- /dev/null
+++ b/libc/malloc_debug/Config.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 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_CONFIG_H
+#define MALLOC_DEBUG_CONFIG_H
+
+#include <stdint.h>
+
+constexpr uint64_t FRONT_GUARD = 0x1;
+constexpr uint64_t REAR_GUARD = 0x2;
+constexpr uint64_t BACKTRACE = 0x4;
+constexpr uint64_t FILL_ON_ALLOC = 0x8;
+constexpr uint64_t FILL_ON_FREE = 0x10;
+constexpr uint64_t EXPAND_ALLOC = 0x20;
+constexpr uint64_t FREE_TRACK = 0x40;
+constexpr uint64_t TRACK_ALLOCS = 0x80;
+constexpr uint64_t LEAK_TRACK = 0x100;
+
+// If only one or more of these options is set, then no special header is needed.
+constexpr uint64_t NO_HEADER_OPTIONS = FILL_ON_ALLOC | FILL_ON_FREE | EXPAND_ALLOC;
+
+struct Config {
+  bool SetFromProperties();
+
+  size_t front_guard_bytes = 0;
+  size_t rear_guard_bytes = 0;
+
+  bool backtrace_enable_on_signal = false;
+  int backtrace_signal = 0;
+  bool backtrace_enabled = false;
+  size_t backtrace_frames = 0;
+
+  size_t fill_on_alloc_bytes = 0;
+  size_t fill_on_free_bytes = 0;
+
+  size_t expand_alloc_bytes = 0;
+
+  size_t free_track_allocations = 0;
+
+  uint64_t options = 0;
+  uint8_t fill_alloc_value;
+  uint8_t fill_free_value;
+  uint8_t front_guard_value;
+  uint8_t rear_guard_value;
+};
+
+#endif  // MALLOC_DEBUG_CONFIG_H
diff --git a/libc/malloc_debug/DebugData.cpp b/libc/malloc_debug/DebugData.cpp
new file mode 100644
index 0000000..bf2a0f5
--- /dev/null
+++ b/libc/malloc_debug/DebugData.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <stdint.h>
+
+#include "BacktraceData.h"
+#include "Config.h"
+#include "DebugData.h"
+#include "debug_disable.h"
+#include "FreeTrackData.h"
+#include "GuardData.h"
+#include "malloc_debug.h"
+#include "TrackData.h"
+
+bool DebugData::Initialize() {
+  if (!config_.SetFromProperties()) {
+    return false;
+  }
+
+  // Check to see if the options that require a header are enabled.
+  if ((config_.options & ~(NO_HEADER_OPTIONS)) != 0) {
+    need_header_ = true;
+
+    // Initialize all of the static header offsets.
+    pointer_offset_ = BIONIC_ALIGN(sizeof(Header), sizeof(uintptr_t));
+
+    if (config_.options & BACKTRACE) {
+      backtrace.reset(new BacktraceData(config_, &pointer_offset_));
+      if (!backtrace->Initialize(config_)) {
+        return false;
+      }
+    }
+
+    if (config_.options & FRONT_GUARD) {
+      front_guard.reset(new FrontGuardData(config_, &pointer_offset_));
+    }
+
+    extra_bytes_ = pointer_offset_;
+
+    // Initialize all of the non-header data.
+    if (config_.options & REAR_GUARD) {
+      rear_guard.reset(new RearGuardData(config_));
+      extra_bytes_ += config_.rear_guard_bytes;
+    }
+
+    if (config_.options & FREE_TRACK) {
+      free_track.reset(new FreeTrackData(config_));
+    }
+
+    if (config_.options & TRACK_ALLOCS) {
+      track.reset(new TrackData());
+    }
+  }
+
+  if (config_.options & EXPAND_ALLOC) {
+    extra_bytes_ += config_.expand_alloc_bytes;
+  }
+  return true;
+}
diff --git a/libc/malloc_debug/DebugData.h b/libc/malloc_debug/DebugData.h
new file mode 100644
index 0000000..e023c51
--- /dev/null
+++ b/libc/malloc_debug/DebugData.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 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 DEBUG_MALLOC_DEBUGDATA_H
+#define DEBUG_MALLOC_DEBUGDATA_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include <private/bionic_macros.h>
+
+#include "BacktraceData.h"
+#include "Config.h"
+#include "FreeTrackData.h"
+#include "GuardData.h"
+#include "malloc_debug.h"
+#include "TrackData.h"
+
+class DebugData {
+ public:
+  DebugData() = default;
+  ~DebugData() = default;
+
+  bool Initialize();
+
+  static bool Disabled();
+
+  inline void* GetPointer(const Header* header) {
+    uintptr_t value = reinterpret_cast<uintptr_t>(header);
+    return reinterpret_cast<void*>(value + pointer_offset_);
+  }
+
+  Header* GetHeader(const void* pointer) {
+    uintptr_t value = reinterpret_cast<uintptr_t>(pointer);
+    return reinterpret_cast<Header*>(value - pointer_offset_);
+  }
+
+  BacktraceHeader* GetAllocBacktrace(const Header* header) {
+    uintptr_t value = reinterpret_cast<uintptr_t>(header);
+    return reinterpret_cast<BacktraceHeader*>(value + backtrace->alloc_offset());
+  }
+
+  BacktraceHeader* GetFreeBacktrace(const Header* header) {
+    uintptr_t value = reinterpret_cast<uintptr_t>(header);
+    return reinterpret_cast<BacktraceHeader*>(value + backtrace->free_offset());
+  }
+
+  uint8_t* GetFrontGuard(const Header* header) {
+    uintptr_t value = reinterpret_cast<uintptr_t>(header);
+    return reinterpret_cast<uint8_t*>(value + front_guard->offset());
+  }
+
+  uint8_t* GetRearGuard(const Header* header) {
+    uintptr_t value = reinterpret_cast<uintptr_t>(GetPointer(header));
+    return reinterpret_cast<uint8_t*>(value + header->real_size());
+  }
+
+  const Config& config() { return config_; }
+  size_t pointer_offset() { return pointer_offset_; }
+  bool need_header() { return need_header_; }
+  size_t extra_bytes() { return extra_bytes_; }
+
+  std::unique_ptr<BacktraceData> backtrace;
+  std::unique_ptr<TrackData> track;
+  std::unique_ptr<FrontGuardData> front_guard;
+  std::unique_ptr<RearGuardData> rear_guard;
+  std::unique_ptr<FreeTrackData> free_track;
+
+ private:
+  size_t extra_bytes_ = 0;
+
+  size_t pointer_offset_ = 0;
+  bool need_header_ = false;
+
+  Config config_;
+
+  DISALLOW_COPY_AND_ASSIGN(DebugData);
+};
+
+#endif // MALLOC_DEBUG_DEBUGDATA_H
diff --git a/libc/malloc_debug/FreeTrackData.cpp b/libc/malloc_debug/FreeTrackData.cpp
new file mode 100644
index 0000000..3466861
--- /dev/null
+++ b/libc/malloc_debug/FreeTrackData.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <stdint.h>
+
+#include "backtrace.h"
+#include "Config.h"
+#include "DebugData.h"
+#include "debug_disable.h"
+#include "debug_log.h"
+#include "FreeTrackData.h"
+#include "malloc_debug.h"
+
+FreeTrackData::FreeTrackData(const Config& config) {
+  cmp_mem_.resize(4096);
+  memset(cmp_mem_.data(), config.fill_free_value, cmp_mem_.size());
+}
+
+void FreeTrackData::LogFreeError(DebugData& debug, const Header* header,
+                                 const uint8_t* pointer) {
+  ScopedDisableDebugCalls disable;
+
+  error_log(LOG_DIVIDER);
+  error_log("+++ ALLOCATION %p USED AFTER FREE", pointer);
+  uint8_t fill_free_value = debug.config().fill_free_value;
+  for (size_t i = 0; i < header->usable_size; i++) {
+    if (pointer[i] != fill_free_value) {
+      error_log("  pointer[%zu] = 0x%02x (expected 0x%02x)", i, pointer[i], fill_free_value);
+    }
+  }
+  if (debug.config().options & BACKTRACE) {
+    BacktraceHeader* back_header = debug.GetFreeBacktrace(header);
+    if (back_header->num_frames > 0) {
+      error_log("Backtrace at time of free:");
+      backtrace_log(&back_header->frames[0], back_header->num_frames);
+    }
+  }
+  error_log(LOG_DIVIDER);
+}
+
+void FreeTrackData::VerifyAndFree(DebugData& debug, const Header* header,
+                                  const void* pointer) {
+  const uint8_t* memory = reinterpret_cast<const uint8_t*>(pointer);
+  size_t bytes = header->usable_size;
+  bytes = (bytes < debug.config().fill_on_free_bytes) ? bytes : debug.config().fill_on_free_bytes;
+  while (bytes > 0) {
+    size_t bytes_to_cmp = (bytes < cmp_mem_.size()) ? bytes : cmp_mem_.size();
+    if (memcmp(memory, cmp_mem_.data(), bytes_to_cmp) != 0) {
+      LogFreeError(debug, header, reinterpret_cast<const uint8_t*>(pointer));
+      break;
+    }
+    bytes -= bytes_to_cmp;
+    memory = &memory[bytes_to_cmp];
+  }
+  g_dispatch->free(header->orig_pointer);
+}
+
+void FreeTrackData::Add(DebugData& debug, const Header* header) {
+  // Make sure the stl calls below don't call the debug_XXX functions.
+  ScopedDisableDebugCalls disable;
+
+  pthread_mutex_lock(&mutex_);
+  if (list_.size() == debug.config().free_track_allocations) {
+    VerifyAndFree(debug, list_.back(), debug.GetPointer(list_.back()));
+    list_.pop_back();
+  }
+
+  list_.push_front(header);
+
+  pthread_mutex_unlock(&mutex_);
+}
+
+void FreeTrackData::VerifyAll(DebugData& debug) {
+  // Make sure the stl calls below don't call the debug_XXX functions.
+  ScopedDisableDebugCalls disable;
+
+  for (const auto& header : list_) {
+    VerifyAndFree(debug, header, debug.GetPointer(header));
+  }
+  list_.clear();
+}
diff --git a/libc/bionic/debug_backtrace.h b/libc/malloc_debug/FreeTrackData.h
similarity index 60%
copy from libc/bionic/debug_backtrace.h
copy to libc/malloc_debug/FreeTrackData.h
index adc9d1d..5888a0e 100644
--- a/libc/bionic/debug_backtrace.h
+++ b/libc/malloc_debug/FreeTrackData.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,15 +26,40 @@
  * SUCH DAMAGE.
  */
 
-#ifndef DEBUG_BACKTRACE_H
-#define DEBUG_BACKTRACE_H
+#ifndef DEBUG_MALLOC_FREETRACKDATA_H
+#define DEBUG_MALLOC_FREETRACKDATA_H
 
 #include <stdint.h>
-#include <sys/cdefs.h>
+#include <pthread.h>
 
-__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);
+#include <deque>
+#include <vector>
 
-#endif /* DEBUG_BACKTRACE_H */
+#include <private/bionic_macros.h>
+
+// Forward declarations.
+struct Header;
+class DebugData;
+struct Config;
+
+class FreeTrackData {
+ public:
+  FreeTrackData(const Config& config);
+  virtual ~FreeTrackData() = default;
+
+  void Add(DebugData& debug, const Header* header);
+
+  void VerifyAll(DebugData& debug);
+
+ private:
+  void LogFreeError(DebugData& debug, const Header* header, const uint8_t* pointer);
+  void VerifyAndFree(DebugData& debug, const Header* header, const void* pointer);
+
+  pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
+  std::deque<const Header*> list_;
+  std::vector<uint8_t> cmp_mem_;
+
+  DISALLOW_COPY_AND_ASSIGN(FreeTrackData);
+};
+
+#endif // DEBUG_MALLOC_FREETRACKDATA_H
diff --git a/libc/malloc_debug/GuardData.cpp b/libc/malloc_debug/GuardData.cpp
new file mode 100644
index 0000000..4ba5253
--- /dev/null
+++ b/libc/malloc_debug/GuardData.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include <vector>
+
+#include "backtrace.h"
+#include "Config.h"
+#include "debug_disable.h"
+#include "debug_log.h"
+#include "DebugData.h"
+#include "malloc_debug.h"
+#include "GuardData.h"
+
+GuardData::GuardData(int init_value, size_t num_bytes) {
+  // Create a buffer for fast comparisons of the front guard.
+  cmp_mem_.resize(num_bytes);
+  memset(cmp_mem_.data(), init_value, cmp_mem_.size());
+}
+
+void GuardData::LogFailure(const Header* header, const void* pointer, const void* data) {
+  ScopedDisableDebugCalls disable;
+
+  error_log(LOG_DIVIDER);
+  error_log("+++ ALLOCATION %p SIZE %zu HAS A CORRUPTED %s GUARD", pointer,
+            header->real_size(), GetTypeName());
+
+  // Log all of the failing bytes.
+  const uint8_t* expected = cmp_mem_.data();
+  int pointer_idx = reinterpret_cast<uintptr_t>(data) - reinterpret_cast<uintptr_t>(pointer);
+  const uint8_t* real = reinterpret_cast<const uint8_t*>(data);
+  for (size_t i = 0; i < cmp_mem_.size(); i++, pointer_idx++) {
+    if (real[i] != expected[i]) {
+      error_log("  pointer[%d] = 0x%02x (expected 0x%02x)", pointer_idx, real[i], expected[i]);
+    }
+  }
+
+  error_log("Backtrace at time of failure:");
+  std::vector<uintptr_t> frames(64);
+  size_t frame_num = backtrace_get(frames.data(), frames.size());
+  frames.resize(frame_num);
+  backtrace_log(frames.data(), frames.size());
+  error_log(LOG_DIVIDER);
+}
+
+FrontGuardData::FrontGuardData(const Config& config, size_t* offset)
+   : GuardData(config.front_guard_value, config.front_guard_bytes) {
+  // Create a buffer for fast comparisons of the front guard.
+  cmp_mem_.resize(config.front_guard_bytes);
+  memset(cmp_mem_.data(), config.front_guard_value, cmp_mem_.size());
+  // Assumes that front_bytes is a multiple of sizeof(uintptr_t).
+  offset_ = *offset;
+  *offset += config.front_guard_bytes;
+}
+
+bool FrontGuardData::Valid(DebugData& debug, const Header* header) {
+  return GuardData::Valid(debug.GetFrontGuard(header));
+}
+
+void FrontGuardData::LogFailure(DebugData& debug, const Header* header) {
+  GuardData::LogFailure(header, debug.GetPointer(header), debug.GetFrontGuard(header));
+}
+
+RearGuardData::RearGuardData(const Config& config)
+    : GuardData(config.rear_guard_value, config.rear_guard_bytes) {
+}
+
+bool RearGuardData::Valid(DebugData& debug, const Header* header) {
+  return GuardData::Valid(debug.GetRearGuard(header));
+}
+
+void RearGuardData::LogFailure(DebugData& debug, const Header* header) {
+  GuardData::LogFailure(header, debug.GetPointer(header), debug.GetRearGuard(header));
+}
diff --git a/libc/malloc_debug/GuardData.h b/libc/malloc_debug/GuardData.h
new file mode 100644
index 0000000..4de2702
--- /dev/null
+++ b/libc/malloc_debug/GuardData.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 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 DEBUG_MALLOC_GUARDDATA_H
+#define DEBUG_MALLOC_GUARDDATA_H
+
+#include <stdint.h>
+#include <string.h>
+
+#include <vector>
+
+#include <private/bionic_macros.h>
+
+// Forward declarations.
+class DebugData;
+struct Header;
+struct Config;
+
+class GuardData {
+ public:
+  GuardData(int init_value, size_t num_bytes);
+  virtual ~GuardData() = default;
+
+  bool Valid(void* data) { return memcmp(data, cmp_mem_.data(), cmp_mem_.size()) == 0; }
+
+  void LogFailure(const Header* header, const void* pointer, const void* data);
+
+ protected:
+  std::vector<uint8_t> cmp_mem_;
+
+  virtual const char* GetTypeName() = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(GuardData);
+};
+
+class FrontGuardData : public GuardData {
+ public:
+  FrontGuardData(const Config& config, size_t* offset);
+  virtual ~FrontGuardData() = default;
+
+  bool Valid(DebugData& debug, const Header* header);
+
+  void LogFailure(DebugData& debug, const Header* header);
+
+  size_t offset() { return offset_; }
+
+ private:
+  const char* GetTypeName() override { return "FRONT"; }
+
+  size_t offset_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(FrontGuardData);
+};
+
+class RearGuardData : public GuardData {
+ public:
+  RearGuardData(const Config& config);
+  virtual ~RearGuardData() = default;
+
+  bool Valid(DebugData& debug, const Header* header);
+
+  void LogFailure(DebugData& debug, const Header* header);
+
+ private:
+  const char* GetTypeName() override { return "REAR"; }
+
+  DISALLOW_COPY_AND_ASSIGN(RearGuardData);
+};
+
+#endif // DEBUG_MALLOC_GUARDDATA_H
diff --git a/libc/malloc_debug/MapData.cpp b/libc/malloc_debug/MapData.cpp
new file mode 100644
index 0000000..ce2b999
--- /dev/null
+++ b/libc/malloc_debug/MapData.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <ctype.h>
+#include <elf.h>
+#include <inttypes.h>
+#include <link.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <vector>
+
+#include "debug_disable.h"
+#include "MapData.h"
+
+// Format of /proc/<PID>/maps:
+//   6f000000-6f01e000 rwxp 00000000 00:0c 16389419   /system/lib/libcomposer.so
+static MapEntry* parse_line(char* line) {
+  uintptr_t start;
+  uintptr_t end;
+  uintptr_t offset;
+  char permissions[4];
+  int name_pos;
+  if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n", &start,
+             &end, permissions, &offset, &name_pos) < 2) {
+    return nullptr;
+  }
+
+  const char* name = line + name_pos;
+  size_t name_len = strlen(name);
+  if (name_len && name[name_len - 1] == '\n') {
+    name_len -= 1;
+  }
+
+  MapEntry* entry = new MapEntry(start, end, offset, name, name_len);
+  if (permissions[0] != 'r') {
+    // Any unreadable map will just get a zero load base.
+    entry->load_base = 0;
+    entry->load_base_read = true;
+  }
+  return entry;
+}
+
+template<typename T>
+static inline bool get_val(MapEntry* entry, uintptr_t addr, T* store) {
+  if (addr < entry->start || addr + sizeof(T) > entry->end) {
+    return false;
+  }
+  // Make sure the address is aligned properly.
+  if (addr & (sizeof(T)-1)) {
+    return false;
+  }
+  *store = *reinterpret_cast<T*>(addr);
+  return true;
+}
+
+static void read_loadbase(MapEntry* entry) {
+  entry->load_base = 0;
+  entry->load_base_read = true;
+  uintptr_t addr = entry->start;
+  ElfW(Ehdr) ehdr;
+  if (!get_val<ElfW(Half)>(entry, addr + offsetof(ElfW(Ehdr), e_phnum), &ehdr.e_phnum)) {
+    return;
+  }
+  if (!get_val<ElfW(Off)>(entry, addr + offsetof(ElfW(Ehdr), e_phoff), &ehdr.e_phoff)) {
+    return;
+  }
+  addr += ehdr.e_phoff;
+  for (size_t i = 0; i < ehdr.e_phnum; i++) {
+    ElfW(Phdr) phdr;
+    if (!get_val<ElfW(Word)>(entry, addr + offsetof(ElfW(Phdr), p_type), &phdr.p_type)) {
+      return;
+    }
+    if (!get_val<ElfW(Off)>(entry, addr + offsetof(ElfW(Phdr), p_offset), &phdr.p_offset)) {
+      return;
+    }
+    if (phdr.p_type == PT_LOAD && phdr.p_offset == entry->offset) {
+      if (!get_val<ElfW(Addr)>(entry, addr + offsetof(ElfW(Phdr), p_vaddr), &phdr.p_vaddr)) {
+        return;
+      }
+      entry->load_base = phdr.p_vaddr;
+      return;
+    }
+    addr += sizeof(phdr);
+  }
+}
+
+bool MapData::Initialize() {
+  ScopedDisableDebugCalls disable;
+
+  FILE* fp = fopen("/proc/self/maps", "re");
+  if (fp == nullptr) {
+    return false;
+  }
+
+  std::vector<char> buffer(1024);
+  while (fgets(buffer.data(), buffer.size(), fp) != nullptr) {
+    MapEntry* entry = parse_line(buffer.data());
+    if (entry == nullptr) {
+      return false;
+    }
+    entries_.push_back(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;
+  }
+  entries_.clear();
+}
+
+// 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;
+    }
+  }
+  if (rel_pc) {
+    *rel_pc = pc;
+  }
+  return nullptr;
+}
diff --git a/libc/bionic/debug_mapinfo.h b/libc/malloc_debug/MapData.h
similarity index 68%
rename from libc/bionic/debug_mapinfo.h
rename to libc/malloc_debug/MapData.h
index af7d05d..d5f315ab 100644
--- a/libc/bionic/debug_mapinfo.h
+++ b/libc/malloc_debug/MapData.h
@@ -26,23 +26,42 @@
  * SUCH DAMAGE.
  */
 
-#ifndef DEBUG_MAPINFO_H
-#define DEBUG_MAPINFO_H
+#ifndef DEBUG_MALLOC_MAPDATA_H
+#define DEBUG_MALLOC_MAPDATA_H
 
 #include <sys/cdefs.h>
 
-struct mapinfo_t {
-  struct mapinfo_t* next;
+#include <string>
+#include <vector>
+
+#include <private/bionic_macros.h>
+
+struct MapEntry {
+  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) {}
+
   uintptr_t start;
   uintptr_t end;
   uintptr_t offset;
   uintptr_t load_base;
-  bool load_base_read;
-  char name[];
+  bool load_base_read = false;
+  std::string name;
 };
 
-__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, uintptr_t pc, uintptr_t* rel_pc);
+class MapData {
+ public:
+  static MapData* Create();
+  ~MapData();
 
-#endif /* DEBUG_MAPINFO_H */
+  const MapEntry* find(uintptr_t pc, uintptr_t* rel_pc = nullptr);
+
+ private:
+  MapData() = default;
+  bool Initialize();
+
+  std::vector<MapEntry*> entries_;
+
+  DISALLOW_COPY_AND_ASSIGN(MapData);
+};
+
+#endif  // DEBUG_MALLOC_MAPDATA_H
diff --git a/libc/malloc_debug/TrackData.cpp b/libc/malloc_debug/TrackData.cpp
new file mode 100644
index 0000000..e5260a9
--- /dev/null
+++ b/libc/malloc_debug/TrackData.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <algorithm>
+#include <vector>
+
+#include <private/ScopedPthreadMutexLocker.h>
+
+#include "backtrace.h"
+#include "BacktraceData.h"
+#include "Config.h"
+#include "DebugData.h"
+#include "debug_disable.h"
+#include "debug_log.h"
+#include "malloc_debug.h"
+#include "TrackData.h"
+
+void TrackData::GetList(std::vector<Header*>* list) {
+  ScopedDisableDebugCalls disable;
+
+  for (const auto& header : headers_) {
+    list->push_back(header);
+  }
+
+  // Sort by the size of the allocation.
+  std::sort(list->begin(), list->end(), [](Header* a, Header* b) {
+    if (a->size == b->size) return a < b;
+    return a->size > b->size;
+  });
+}
+
+void TrackData::Add(Header* header, bool backtrace_found) {
+  ScopedDisableDebugCalls disable;
+
+  pthread_mutex_lock(&mutex_);
+  if (backtrace_found) {
+    total_backtrace_allocs_++;
+  }
+  headers_.insert(header);
+  pthread_mutex_unlock(&mutex_);
+}
+
+void TrackData::Remove(Header* header, bool backtrace_found) {
+  ScopedDisableDebugCalls disable;
+
+  pthread_mutex_lock(&mutex_);
+  headers_.erase(header);
+  if (backtrace_found) {
+    total_backtrace_allocs_--;
+  }
+  pthread_mutex_unlock(&mutex_);
+}
+
+void TrackData::DisplayLeaks(DebugData& debug) {
+  ScopedDisableDebugCalls disable;
+
+  std::vector<Header*> list;
+  GetList(&list);
+
+  size_t track_count = 0;
+  for (const auto& header : list) {
+    error_log("+++ %s leaked block of size %zu at %p (leak %zu of %zu)", getprogname(),
+              header->real_size(), debug.GetPointer(header), ++track_count, list.size());
+    if (debug.config().options & BACKTRACE) {
+      BacktraceHeader* back_header = debug.GetAllocBacktrace(header);
+      if (back_header->num_frames > 0) {
+        error_log("Backtrace at time of allocation:");
+        backtrace_log(&back_header->frames[0], back_header->num_frames);
+      }
+    }
+    g_dispatch->free(header->orig_pointer);
+  }
+}
+
+void TrackData::GetInfo(DebugData& debug, uint8_t** info, size_t* overall_size,
+                        size_t* info_size, size_t* total_memory, size_t* backtrace_size) {
+  ScopedPthreadMutexLocker scoped(&mutex_);
+
+  if (headers_.size() == 0 || total_backtrace_allocs_ == 0) {
+    return;
+  }
+
+  *backtrace_size = debug.config().backtrace_frames;
+  *info_size = sizeof(size_t) * 2 + sizeof(uintptr_t) * *backtrace_size;
+  *info = reinterpret_cast<uint8_t*>(g_dispatch->calloc(*info_size, total_backtrace_allocs_));
+  if (*info == nullptr) {
+    return;
+  }
+  *overall_size = *info_size * total_backtrace_allocs_;
+
+  std::vector<Header*> list;
+  GetList(&list);
+
+  uint8_t* data = *info;
+  for (const auto& header : list) {
+    BacktraceHeader* back_header = debug.GetAllocBacktrace(header);
+    if (back_header->num_frames > 0) {
+      memcpy(data, &header->size, sizeof(size_t));
+      memcpy(&data[sizeof(size_t)], &back_header->num_frames, sizeof(size_t));
+      memcpy(&data[2 * sizeof(size_t)], &back_header->frames[0],
+            back_header->num_frames * sizeof(uintptr_t));
+
+      *total_memory += header->real_size();
+
+      data += *info_size;
+    }
+  }
+}
diff --git a/libc/malloc_debug/TrackData.h b/libc/malloc_debug/TrackData.h
new file mode 100644
index 0000000..45e6892
--- /dev/null
+++ b/libc/malloc_debug/TrackData.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2015 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 DEBUG_MALLOC_TRACKDATA_H
+#define DEBUG_MALLOC_TRACKDATA_H
+
+#include <stdint.h>
+#include <pthread.h>
+
+#include <vector>
+#include <unordered_set>
+
+#include <private/bionic_macros.h>
+
+// Forward declarations.
+struct Header;
+struct Config;
+class DebugData;
+
+class TrackData {
+ public:
+  TrackData() = default;
+  virtual ~TrackData() = default;
+
+  void GetList(std::vector<Header*>* list);
+
+  void Add(Header* header, bool backtrace_found);
+
+  void Remove(Header* header, bool backtrace_found);
+
+  void GetInfo(DebugData& debug, uint8_t** info, size_t* overall_size,
+               size_t* info_size, size_t* total_memory, size_t* backtrace_size);
+
+  void DisplayLeaks(DebugData& debug);
+
+ private:
+  pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
+  std::unordered_set<Header*> headers_;
+  size_t total_backtrace_allocs_ = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(TrackData);
+};
+
+#endif // DEBUG_MALLOC_TRACKDATA_H
diff --git a/libc/bionic/debug_backtrace.cpp b/libc/malloc_debug/backtrace.cpp
similarity index 62%
rename from libc/bionic/debug_backtrace.cpp
rename to libc/malloc_debug/backtrace.cpp
index de3ce08..00290fd 100644
--- a/libc/bionic/debug_backtrace.cpp
+++ b/libc/malloc_debug/backtrace.cpp
@@ -26,18 +26,20 @@
  * SUCH DAMAGE.
  */
 
-#include "debug_backtrace.h"
-
 #include <dlfcn.h>
+#include <errno.h>
 #include <inttypes.h>
 #include <malloc.h>
+#include <pthread.h>
+#include <string.h>
+#include <sys/types.h>
 #include <unistd.h>
 #include <unwind.h>
-#include <sys/types.h>
 
-#include "debug_mapinfo.h"
-#include "malloc_debug_disable.h"
-#include "private/libc_logging.h"
+#include "backtrace.h"
+#include "debug_disable.h"
+#include "debug_log.h"
+#include "MapData.h"
 
 #if defined(__LP64__)
 #define PAD_PTR "016" PRIxPTR
@@ -49,29 +51,42 @@
 
 extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
 
-static mapinfo_t* g_map_info = NULL;
+static MapData* g_map_data = nullptr;
+static const MapEntry* g_current_code_map = nullptr;
 
-__LIBC_HIDDEN__ void backtrace_startup() {
-  ScopedDisableDebugCalls disable;
+static _Unwind_Reason_Code find_current_map(__unwind_context* context, void*) {
+  uintptr_t ip = _Unwind_GetIP(context);
 
-  g_map_info = mapinfo_create(getpid());
+  if (ip == 0) {
+    return _URC_END_OF_STACK;
+  }
+  g_current_code_map = g_map_data->find(ip);
+  return _URC_END_OF_STACK;
 }
 
-__LIBC_HIDDEN__ void backtrace_shutdown() {
+void backtrace_startup() {
   ScopedDisableDebugCalls disable;
 
-  mapinfo_destroy(g_map_info);
+  g_map_data = MapData::Create();
+  if (g_map_data) {
+    _Unwind_Backtrace(find_current_map, nullptr);
+  }
+}
+
+void backtrace_shutdown() {
+  ScopedDisableDebugCalls disable;
+
+  delete g_map_data;
+  g_map_data = nullptr;
 }
 
 struct stack_crawl_state_t {
   uintptr_t* frames;
   size_t frame_count;
-  size_t max_depth;
-  bool have_skipped_self;
+  size_t cur_frame = 0;
 
-  stack_crawl_state_t(uintptr_t* frames, size_t max_depth)
-      : frames(frames), frame_count(0), max_depth(max_depth), have_skipped_self(false) {
-  }
+  stack_crawl_state_t(uintptr_t* frames, size_t frame_count)
+      : frames(frames), frame_count(frame_count) {}
 };
 
 static _Unwind_Reason_Code trace_function(__unwind_context* context, void* arg) {
@@ -79,12 +94,6 @@
 
   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;
-  }
-
   // The instruction pointer is pointing at the instruction after the return
   // call on all architectures.
   // Modify the pc to point at the real function.
@@ -115,61 +124,56 @@
     // so subtract 1 to estimate where the instruction lives.
     ip--;
 #endif
+
+    // Do not record the frames that fall in our own shared library.
+    if (g_current_code_map && (ip >= g_current_code_map->start) && ip < g_current_code_map->end) {
+      return _URC_NO_REASON;
+    }
   }
 
-  state->frames[state->frame_count++] = ip;
-  return (state->frame_count >= state->max_depth) ? _URC_END_OF_STACK : _URC_NO_REASON;
+  state->frames[state->cur_frame++] = ip;
+  return (state->cur_frame >= state->frame_count) ? _URC_END_OF_STACK : _URC_NO_REASON;
 }
 
-__LIBC_HIDDEN__ int get_backtrace(uintptr_t* frames, size_t max_depth) {
+size_t backtrace_get(uintptr_t* frames, size_t frame_count) {
   ScopedDisableDebugCalls disable;
 
-  stack_crawl_state_t state(frames, max_depth);
+  stack_crawl_state_t state(frames, frame_count);
   _Unwind_Backtrace(trace_function, &state);
-  return state.frame_count;
+  return state.cur_frame;
 }
 
-__LIBC_HIDDEN__ void log_backtrace(uintptr_t* frames, size_t frame_count) {
+void backtrace_log(uintptr_t* frames, size_t frame_count) {
   ScopedDisableDebugCalls disable;
 
-  uintptr_t self_bt[16];
-  if (frames == NULL) {
-    frame_count = get_backtrace(self_bt, 16);
-    frames = self_bt;
-  }
-
-  __libc_format_log(ANDROID_LOG_ERROR, "libc",
-                    "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
-
-  for (size_t i = 0 ; i < frame_count; ++i) {
+  for (size_t frame_num = 0; frame_num < frame_count; frame_num++) {
     uintptr_t offset = 0;
-    const char* symbol = NULL;
+    const char* symbol = nullptr;
 
     Dl_info info;
-    if (dladdr((void*) frames[i], &info) != 0) {
+    if (dladdr(reinterpret_cast<void*>(frames[frame_num]), &info) != 0) {
       offset = reinterpret_cast<uintptr_t>(info.dli_saddr);
       symbol = info.dli_sname;
     }
 
     uintptr_t rel_pc = offset;
-    const mapinfo_t* mi = (g_map_info != NULL) ? mapinfo_find(g_map_info, frames[i], &rel_pc) : NULL;
-    const char* soname = (mi != NULL) ? mi->name : info.dli_fname;
-    if (soname == NULL) {
+    const MapEntry* entry = nullptr;
+    if (g_map_data) {
+      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>";
     }
-    if (symbol != NULL) {
-      char* demangled_symbol = __cxa_demangle(symbol, NULL, NULL, NULL);
-      const char* best_name = (demangled_symbol != NULL) ? demangled_symbol : symbol;
+    if (symbol != nullptr) {
+      char* demangled_symbol = __cxa_demangle(symbol, nullptr, nullptr, nullptr);
+      const char* best_name = (demangled_symbol != nullptr) ? demangled_symbol : symbol;
 
-      __libc_format_log(ANDROID_LOG_ERROR, "libc",
-                        "          #%02zd  pc %" PAD_PTR "  %s (%s+%" PRIuPTR ")",
-                        i, rel_pc, soname, best_name, frames[i] - offset);
-
+      error_log("          #%02zd  pc %" PAD_PTR "  %s (%s+%" PRIuPTR ")",
+                frame_num, rel_pc, soname, best_name, frames[frame_num] - offset);
       free(demangled_symbol);
     } else {
-      __libc_format_log(ANDROID_LOG_ERROR, "libc",
-                        "          #%02zd  pc %" PAD_PTR "  %s",
-                        i, rel_pc, soname);
+      error_log("          #%02zd  pc %" PAD_PTR "  %s", frame_num, rel_pc, soname);
     }
   }
 }
diff --git a/libc/bionic/malloc_debug_backtrace.h b/libc/malloc_debug/backtrace.h
similarity index 81%
rename from libc/bionic/malloc_debug_backtrace.h
rename to libc/malloc_debug/backtrace.h
index 774548b..513a649 100644
--- a/libc/bionic/malloc_debug_backtrace.h
+++ b/libc/malloc_debug/backtrace.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2013 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -29,9 +29,12 @@
 #ifndef MALLOC_DEBUG_BACKTRACE_H
 #define MALLOC_DEBUG_BACKTRACE_H
 
-extern bool g_backtrace_enabled;
+#include <stdint.h>
+#include <sys/cdefs.h>
 
-#define GET_BACKTRACE(bt, depth) \
-  (g_backtrace_enabled ? get_backtrace(bt, depth) : 0)
+void backtrace_startup();
+void backtrace_shutdown();
+size_t backtrace_get(uintptr_t* frames, size_t frame_count);
+void backtrace_log(uintptr_t* frames, size_t frame_count);
 
-#endif  // MALLOC_DEBUG_BACKTRACE_H
+#endif // MALLOC_DEBUG_BACKTRACE_H
diff --git a/libc/bionic/malloc_debug_backtrace.h b/libc/malloc_debug/debug_disable.cpp
similarity index 60%
copy from libc/bionic/malloc_debug_backtrace.h
copy to libc/malloc_debug/debug_disable.cpp
index 774548b..af0264b 100644
--- a/libc/bionic/malloc_debug_backtrace.h
+++ b/libc/malloc_debug/debug_disable.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,12 +26,41 @@
  * SUCH DAMAGE.
  */
 
-#ifndef MALLOC_DEBUG_BACKTRACE_H
-#define MALLOC_DEBUG_BACKTRACE_H
+#include <pthread.h>
 
-extern bool g_backtrace_enabled;
+#include "DebugData.h"
+#include "debug_disable.h"
+#include "debug_log.h"
 
-#define GET_BACKTRACE(bt, depth) \
-  (g_backtrace_enabled ? get_backtrace(bt, depth) : 0)
+extern DebugData* g_debug;
+pthread_key_t g_disable_key;
 
-#endif  // MALLOC_DEBUG_BACKTRACE_H
+bool DebugCallsDisabled() {
+  if (g_debug == nullptr || pthread_getspecific(g_disable_key) != nullptr) {
+    return true;
+  }
+  return false;
+}
+
+bool DebugDisableInitialize() {
+  int error = pthread_key_create(&g_disable_key, nullptr);
+  if (error != 0) {
+    error_log("pthread_key_create failed: %s", strerror(error));
+    return false;
+  }
+  pthread_setspecific(g_disable_key, nullptr);
+
+  return true;
+}
+
+void DebugDisableFinalize() {
+  pthread_key_delete(g_disable_key);
+}
+
+void DebugDisableSet(bool disable) {
+  if (disable) {
+    pthread_setspecific(g_disable_key, reinterpret_cast<void*>(1));
+  } else {
+    pthread_setspecific(g_disable_key, nullptr);
+  }
+}
diff --git a/libc/bionic/malloc_debug_disable.h b/libc/malloc_debug/debug_disable.h
similarity index 84%
rename from libc/bionic/malloc_debug_disable.h
rename to libc/malloc_debug/debug_disable.h
index 9503128..9edb4df 100644
--- a/libc/bionic/malloc_debug_disable.h
+++ b/libc/malloc_debug/debug_disable.h
@@ -29,29 +29,29 @@
 #ifndef MALLOC_DEBUG_DISABLE_H
 #define MALLOC_DEBUG_DISABLE_H
 
-#include <pthread.h>
+#include <sys/cdefs.h>
 
-#include "private/bionic_macros.h"
+#include <private/bionic_macros.h>
 
 // =============================================================================
 // Used to disable the debug allocation calls.
 // =============================================================================
-extern pthread_key_t g_debug_calls_disabled;
+bool DebugDisableInitialize();
+void DebugDisableFinalize();
 
-static inline bool DebugCallsDisabled() {
-  return pthread_getspecific(g_debug_calls_disabled) != NULL;
-}
+bool DebugCallsDisabled();
+void DebugDisableSet(bool disable);
 
 class ScopedDisableDebugCalls {
  public:
   ScopedDisableDebugCalls() : disabled_(DebugCallsDisabled()) {
     if (!disabled_) {
-      pthread_setspecific(g_debug_calls_disabled, reinterpret_cast<const void*>(1));
+      DebugDisableSet(true);
     }
   }
   ~ScopedDisableDebugCalls() {
     if (!disabled_) {
-      pthread_setspecific(g_debug_calls_disabled, NULL);
+      DebugDisableSet(false);
     }
   }
 
diff --git a/libc/bionic/malloc_debug_backtrace.h b/libc/malloc_debug/debug_log.h
similarity index 65%
copy from libc/bionic/malloc_debug_backtrace.h
copy to libc/malloc_debug/debug_log.h
index 774548b..67f584d 100644
--- a/libc/bionic/malloc_debug_backtrace.h
+++ b/libc/malloc_debug/debug_log.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,12 +26,19 @@
  * SUCH DAMAGE.
  */
 
-#ifndef MALLOC_DEBUG_BACKTRACE_H
-#define MALLOC_DEBUG_BACKTRACE_H
+#ifndef MALLOC_DEBUG_LOG_H
+#define MALLOC_DEBUG_LOG_H
 
-extern bool g_backtrace_enabled;
+#include <private/libc_logging.h>
 
-#define GET_BACKTRACE(bt, depth) \
-  (g_backtrace_enabled ? get_backtrace(bt, depth) : 0)
+// =============================================================================
+// log functions
+// =============================================================================
+#define debug_log(format, ...)  \
+    __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 info_log(format, ...)  \
+    __libc_format_log(ANDROID_LOG_INFO, "malloc_debug", (format), ##__VA_ARGS__ )
 
-#endif  // MALLOC_DEBUG_BACKTRACE_H
+#endif  // MALLOC_DEBUG_LOG_H
diff --git a/libc/malloc_debug/exported32.map b/libc/malloc_debug/exported32.map
new file mode 100644
index 0000000..aef8818
--- /dev/null
+++ b/libc/malloc_debug/exported32.map
@@ -0,0 +1,20 @@
+LIBC_MALLOC_DEBUG {
+  global:
+    debug_calloc;
+    debug_finalize;
+    debug_free;
+    debug_free_malloc_leak_info;
+    debug_get_malloc_leak_info;
+    debug_initialize;
+    debug_mallinfo;
+    debug_malloc;
+    debug_malloc_usable_size;
+    debug_memalign;
+    debug_posix_memalign;
+    debug_pvalloc;
+    debug_realloc;
+    debug_valloc;
+
+  local:
+    *;
+};
diff --git a/libc/malloc_debug/exported64.map b/libc/malloc_debug/exported64.map
new file mode 100644
index 0000000..70fd581
--- /dev/null
+++ b/libc/malloc_debug/exported64.map
@@ -0,0 +1,18 @@
+LIBC_MALLOC_DEBUG {
+  global:
+    debug_calloc;
+    debug_finalize;
+    debug_free;
+    debug_free_malloc_leak_info;
+    debug_get_malloc_leak_info;
+    debug_initialize;
+    debug_mallinfo;
+    debug_malloc;
+    debug_malloc_usable_size;
+    debug_memalign;
+    debug_posix_memalign;
+    debug_realloc;
+
+  local:
+    *;
+};
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
new file mode 100644
index 0000000..f55d488
--- /dev/null
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <malloc.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <private/bionic_malloc_dispatch.h>
+
+#include "backtrace.h"
+#include "DebugData.h"
+#include "debug_disable.h"
+#include "debug_log.h"
+#include "malloc_debug.h"
+
+// ------------------------------------------------------------------------
+// Global Data
+// ------------------------------------------------------------------------
+DebugData* g_debug;
+
+int* g_malloc_zygote_child;
+
+const MallocDispatch* g_dispatch;
+// ------------------------------------------------------------------------
+
+// ------------------------------------------------------------------------
+// Use C style prototypes for all exported functions. This makes it easy
+// to do dlsym lookups during libc initialization when malloc debug
+// is enabled.
+// ------------------------------------------------------------------------
+__BEGIN_DECLS
+
+bool debug_initialize(const MallocDispatch* malloc_dispatch, int* malloc_zygote_child);
+void debug_finalize();
+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);
+void debug_free_malloc_leak_info(uint8_t* info);
+size_t debug_malloc_usable_size(void* pointer);
+void* debug_malloc(size_t size);
+void debug_free(void* pointer);
+void* debug_memalign(size_t alignment, size_t bytes);
+void* debug_realloc(void* pointer, size_t bytes);
+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);
+
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+void* debug_pvalloc(size_t bytes);
+void* debug_valloc(size_t size);
+#endif
+
+__END_DECLS
+// ------------------------------------------------------------------------
+
+static void LogTagError(const Header* header, const void* pointer, const char* name) {
+  ScopedDisableDebugCalls disable;
+
+  error_log(LOG_DIVIDER);
+  error_log("+++ ALLOCATION %p HAS INVALID TAG %" PRIx32 " (%s)", pointer, header->tag, name);
+  error_log("Backtrace at time of failure:");
+  std::vector<uintptr_t> frames(64);
+  size_t frame_num = backtrace_get(frames.data(), frames.size());
+  frames.resize(frame_num);
+  backtrace_log(frames.data(), frames.size());
+  error_log(LOG_DIVIDER);
+}
+
+static void* InitHeader(Header* header, void* orig_pointer, size_t size) {
+  header->tag = DEBUG_TAG;
+  header->orig_pointer = orig_pointer;
+  header->size = size;
+  if (*g_malloc_zygote_child) {
+    header->set_zygote();
+  }
+  header->usable_size = g_dispatch->malloc_usable_size(orig_pointer);
+  if (header->usable_size == 0) {
+    g_dispatch->free(orig_pointer);
+    return nullptr;
+  }
+  header->usable_size -= g_debug->pointer_offset() +
+      reinterpret_cast<uintptr_t>(orig_pointer) - reinterpret_cast<uintptr_t>(header);
+
+  if (g_debug->config().options & FRONT_GUARD) {
+    uint8_t* guard = g_debug->GetFrontGuard(header);
+    memset(guard, g_debug->config().front_guard_value, g_debug->config().front_guard_bytes);
+  }
+
+  if (g_debug->config().options & REAR_GUARD) {
+    uint8_t* guard = g_debug->GetRearGuard(header);
+    memset(guard, g_debug->config().rear_guard_value, g_debug->config().rear_guard_bytes);
+    // If the rear guard is enabled, set the usable size to the exact size
+    // of the allocation.
+    header->usable_size = header->real_size();
+  }
+
+  bool backtrace_found = false;
+  if (g_debug->config().options & BACKTRACE) {
+    BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
+    if (g_debug->backtrace->enabled()) {
+      back_header->num_frames = backtrace_get(&back_header->frames[0],
+                                              g_debug->config().backtrace_frames);
+      backtrace_found = back_header->num_frames > 0;
+    } else {
+      back_header->num_frames = 0;
+    }
+    back_header = g_debug->GetFreeBacktrace(header);
+    back_header->num_frames = 0;
+  }
+
+  if (g_debug->config().options & TRACK_ALLOCS) {
+    g_debug->track->Add(header, backtrace_found);
+  }
+
+  return g_debug->GetPointer(header);
+}
+
+bool debug_initialize(const MallocDispatch* malloc_dispatch, int* malloc_zygote_child) {
+  if (malloc_zygote_child == nullptr) {
+    return false;
+  }
+  g_malloc_zygote_child = malloc_zygote_child;
+
+  g_dispatch = malloc_dispatch;
+
+  if (!DebugDisableInitialize()) {
+    return false;
+  }
+
+  DebugData* debug = new DebugData();
+  if (!debug->Initialize()) {
+    delete debug;
+    DebugDisableFinalize();
+    return false;
+  }
+  g_debug = debug;
+
+  // Always enable the backtrace code since we will use it in a number
+  // of different error cases.
+  backtrace_startup();
+
+  return true;
+}
+
+void debug_finalize() {
+  if (g_debug == nullptr) {
+    return;
+  }
+
+  if (g_debug->config().options & FREE_TRACK) {
+    g_debug->free_track->VerifyAll(*g_debug);
+  }
+
+  if (g_debug->config().options & LEAK_TRACK) {
+    g_debug->track->DisplayLeaks(*g_debug);
+  }
+
+  backtrace_shutdown();
+
+  DebugDisableSet(true);
+
+  delete g_debug;
+  g_debug = nullptr;
+
+  DebugDisableFinalize();
+}
+
+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) {
+  ScopedDisableDebugCalls disable;
+
+  // Verify the arguments.
+  if (info == nullptr || overall_size == nullptr || info_size == NULL ||
+      total_memory == nullptr || backtrace_size == nullptr) {
+    error_log("get_malloc_leak_info: At least one invalid parameter.");
+    return;
+  }
+
+  *info = nullptr;
+  *overall_size = 0;
+  *info_size = 0;
+  *total_memory = 0;
+  *backtrace_size = 0;
+
+  if (!(g_debug->config().options & BACKTRACE)) {
+    error_log("get_malloc_leak_info: Allocations not being tracked, to enable "
+              "set the option 'backtrace'.");
+    return;
+  }
+
+  g_debug->track->GetInfo(*g_debug, info, overall_size, info_size, total_memory, backtrace_size);
+}
+
+void debug_free_malloc_leak_info(uint8_t* info) {
+  g_dispatch->free(info);
+}
+
+size_t debug_malloc_usable_size(void* pointer) {
+  if (DebugCallsDisabled() || !g_debug->need_header() || pointer == nullptr) {
+    return g_dispatch->malloc_usable_size(pointer);
+  }
+
+  Header* header = g_debug->GetHeader(pointer);
+  if (header->tag != DEBUG_TAG) {
+    LogTagError(header, pointer, "malloc_usable_size");
+    return 0;
+  }
+
+  return header->usable_size;
+}
+
+void* debug_malloc(size_t size) {
+  if (DebugCallsDisabled()) {
+    return g_dispatch->malloc(size);
+  }
+
+  size_t real_size = size + g_debug->extra_bytes();
+  if (real_size < size) {
+    // Overflow.
+    errno = ENOMEM;
+    return nullptr;
+  }
+
+  void* pointer;
+  if (g_debug->need_header()) {
+    if (size > Header::max_size()) {
+      errno = ENOMEM;
+      return nullptr;
+    }
+
+    Header* header = reinterpret_cast<Header*>(g_dispatch->memalign(sizeof(uintptr_t), real_size));
+    if (header == nullptr) {
+      return nullptr;
+    }
+    pointer = InitHeader(header, header, size);
+  } else {
+    pointer = g_dispatch->malloc(real_size);
+  }
+
+  if (pointer != nullptr && g_debug->config().options & FILL_ON_ALLOC) {
+    size_t bytes = debug_malloc_usable_size(pointer);
+    size_t fill_bytes = g_debug->config().fill_on_alloc_bytes;
+    bytes = (bytes < fill_bytes) ? bytes : fill_bytes;
+    memset(pointer, g_debug->config().fill_alloc_value, bytes);
+  }
+  return pointer;
+}
+
+void debug_free(void* pointer) {
+  if (DebugCallsDisabled() || pointer == nullptr) {
+    return g_dispatch->free(pointer);
+  }
+
+  void* free_pointer = pointer;
+  size_t bytes;
+  if (g_debug->need_header()) {
+    Header* header = g_debug->GetHeader(pointer);
+    if (header->tag != DEBUG_TAG) {
+      LogTagError(header, pointer, "free");
+      return;
+    }
+    free_pointer = header->orig_pointer;
+
+    if (g_debug->config().options & FRONT_GUARD) {
+      if (!g_debug->front_guard->Valid(*g_debug, header)) {
+        g_debug->front_guard->LogFailure(*g_debug, header);
+      }
+    }
+    if (g_debug->config().options & REAR_GUARD) {
+      if (!g_debug->rear_guard->Valid(*g_debug, header)) {
+        g_debug->rear_guard->LogFailure(*g_debug, header);
+      }
+    }
+
+    if (g_debug->config().options & TRACK_ALLOCS) {
+      bool backtrace_found = false;
+      if (g_debug->config().options & BACKTRACE) {
+        BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
+        backtrace_found = back_header->num_frames > 0;
+      }
+      g_debug->track->Remove(header, backtrace_found);
+    }
+
+    if (g_debug->config().options & FREE_TRACK) {
+      // Only log the free backtrace if we are using the free track feature.
+      if ((g_debug->config().options & BACKTRACE) && g_debug->backtrace->enabled()) {
+        BacktraceHeader* back_header = g_debug->GetFreeBacktrace(header);
+        back_header->num_frames = backtrace_get(&back_header->frames[0],
+                                                g_debug->config().backtrace_frames);
+      }
+
+      g_debug->free_track->Add(*g_debug, header);
+
+      // Do not free this pointer just yet.
+      free_pointer = nullptr;
+    }
+
+    bytes = header->usable_size;
+  } else {
+    bytes = g_dispatch->malloc_usable_size(pointer);
+  }
+
+  if (g_debug->config().options & FILL_ON_FREE) {
+    size_t fill_bytes = g_debug->config().fill_on_free_bytes;
+    bytes = (bytes < fill_bytes) ? bytes : fill_bytes;
+    memset(pointer, g_debug->config().fill_free_value, bytes);
+  }
+
+  g_dispatch->free(free_pointer);
+}
+
+void* debug_memalign(size_t alignment, size_t bytes) {
+  if (DebugCallsDisabled()) {
+    return g_dispatch->memalign(alignment, bytes);
+  }
+
+  void* pointer;
+  if (g_debug->need_header()) {
+    if (bytes > Header::max_size()) {
+      errno = ENOMEM;
+      return nullptr;
+    }
+
+    // Make the alignment a power of two.
+    if (!powerof2(alignment)) {
+      alignment = BIONIC_ROUND_UP_POWER_OF_2(alignment);
+    }
+    // Force the alignment to at least sizeof(uintptr_t) to guarantee
+    // that the header is aligned properly.
+    if (alignment < sizeof(uintptr_t)) {
+      alignment = sizeof(uintptr_t);
+    }
+
+    // We don't have any idea what the natural alignment of
+    // the underlying native allocator is, so we always need to
+    // over allocate.
+    size_t real_size = alignment + bytes + g_debug->extra_bytes();
+    if (real_size < bytes) {
+      // Overflow.
+      errno = ENOMEM;
+      return nullptr;
+    }
+
+    pointer = g_dispatch->malloc(real_size);
+    if (pointer == nullptr) {
+      return nullptr;
+    }
+
+    uintptr_t value = reinterpret_cast<uintptr_t>(pointer) + g_debug->pointer_offset();
+    // Now align the pointer.
+    value += (-value % alignment);
+
+    Header* header = g_debug->GetHeader(reinterpret_cast<void*>(value));
+    pointer = InitHeader(header, pointer, bytes);
+  } else {
+    size_t real_size = bytes + g_debug->extra_bytes();
+    if (real_size < bytes) {
+      // Overflow.
+      errno = ENOMEM;
+      return nullptr;
+    }
+    pointer = g_dispatch->memalign(alignment, real_size);
+  }
+
+  if (pointer != nullptr && g_debug->config().options & FILL_ON_ALLOC) {
+    size_t bytes = debug_malloc_usable_size(pointer);
+    size_t fill_bytes = g_debug->config().fill_on_alloc_bytes;
+    bytes = (bytes < fill_bytes) ? bytes : fill_bytes;
+    memset(pointer, g_debug->config().fill_alloc_value, bytes);
+  }
+  return pointer;
+}
+
+void* debug_realloc(void* pointer, size_t bytes) {
+  if (DebugCallsDisabled()) {
+    return g_dispatch->realloc(pointer, bytes);
+  }
+
+  if (pointer == nullptr) {
+    return debug_malloc(bytes);
+  }
+
+  if (bytes == 0) {
+    debug_free(pointer);
+    return nullptr;
+  }
+
+  size_t real_size = bytes;
+  if (g_debug->config().options & EXPAND_ALLOC) {
+    real_size += g_debug->config().expand_alloc_bytes;
+    if (real_size < bytes) {
+      // Overflow.
+      errno = ENOMEM;
+      return nullptr;
+    }
+  }
+
+  void* new_pointer;
+  size_t prev_size;
+  if (g_debug->need_header()) {
+    if (bytes > Header::max_size()) {
+      errno = ENOMEM;
+      return nullptr;
+    }
+
+    Header* header = g_debug->GetHeader(pointer);
+    if (header->tag != DEBUG_TAG) {
+      LogTagError(header, pointer, "realloc");
+      return nullptr;
+    }
+
+    // Same size, do nothing.
+    if (real_size == header->real_size()) {
+      return pointer;
+    }
+
+    // Allocation is shrinking.
+    if (real_size < header->usable_size) {
+      header->size = real_size;
+      if (*g_malloc_zygote_child) {
+        header->set_zygote();
+      }
+      if (g_debug->config().options & REAR_GUARD) {
+        // Don't bother allocating a smaller pointer in this case, simply
+        // change the header usable_size and reset the rear guard.
+        header->usable_size = header->real_size();
+        memset(g_debug->GetRearGuard(header), g_debug->config().rear_guard_value,
+               g_debug->config().rear_guard_bytes);
+      }
+      return pointer;
+    }
+
+    // Allocate the new size.
+    new_pointer = debug_malloc(bytes);
+    if (new_pointer == nullptr) {
+      errno = ENOMEM;
+      return nullptr;
+    }
+
+    prev_size = header->usable_size;
+    memcpy(new_pointer, pointer, prev_size);
+    debug_free(pointer);
+  } else {
+    prev_size = g_dispatch->malloc_usable_size(pointer);
+    new_pointer = g_dispatch->realloc(pointer, real_size);
+    if (new_pointer == nullptr) {
+      return nullptr;
+    }
+  }
+
+  if (g_debug->config().options & FILL_ON_ALLOC) {
+    size_t bytes = debug_malloc_usable_size(new_pointer);
+    if (bytes > g_debug->config().fill_on_alloc_bytes) {
+      bytes = g_debug->config().fill_on_alloc_bytes;
+    }
+    if (bytes > prev_size) {
+      memset(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(new_pointer) + prev_size),
+             g_debug->config().fill_alloc_value, bytes - prev_size);
+    }
+  }
+
+  return new_pointer;
+}
+
+void* debug_calloc(size_t nmemb, size_t bytes) {
+  if (DebugCallsDisabled()) {
+    return g_dispatch->calloc(nmemb, bytes);
+  }
+
+  size_t real_size = nmemb * bytes + g_debug->extra_bytes();
+  if (real_size < bytes || real_size < nmemb) {
+    // Overflow.
+    errno = ENOMEM;
+    return nullptr;
+  }
+
+  if (g_debug->need_header()) {
+    // The above check will guarantee the multiply will not overflow.
+    if (bytes * nmemb > Header::max_size()) {
+      errno = ENOMEM;
+      return nullptr;
+    }
+
+    // Need to guarantee the alignment of the header.
+    Header* header = reinterpret_cast<Header*>(g_dispatch->memalign(sizeof(uintptr_t), real_size));
+    if (header == nullptr) {
+      return nullptr;
+    }
+    memset(header, 0, g_dispatch->malloc_usable_size(header));
+    return InitHeader(header, header, nmemb * bytes);
+  } else {
+    return g_dispatch->calloc(1, real_size);
+  }
+}
+
+struct mallinfo debug_mallinfo() {
+  return g_dispatch->mallinfo();
+}
+
+int debug_posix_memalign(void** memptr, size_t alignment, size_t size) {
+  if (DebugCallsDisabled()) {
+    return g_dispatch->posix_memalign(memptr, alignment, size);
+  }
+
+  if (!powerof2(alignment)) {
+    return EINVAL;
+  }
+  int saved_errno = errno;
+  *memptr = debug_memalign(alignment, size);
+  errno = saved_errno;
+  return (*memptr != nullptr) ? 0 : ENOMEM;
+}
+
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+void* debug_pvalloc(size_t bytes) {
+  if (DebugCallsDisabled()) {
+    return g_dispatch->pvalloc(bytes);
+  }
+
+  size_t pagesize = getpagesize();
+  size_t size = BIONIC_ALIGN(bytes, pagesize);
+  if (size < bytes) {
+    // Overflow
+    errno = ENOMEM;
+    return nullptr;
+  }
+  return debug_memalign(pagesize, size);
+}
+
+void* debug_valloc(size_t size) {
+  if (DebugCallsDisabled()) {
+    return g_dispatch->valloc(size);
+  }
+  return debug_memalign(getpagesize(), size);
+}
+#endif
diff --git a/libc/malloc_debug/malloc_debug.h b/libc/malloc_debug/malloc_debug.h
new file mode 100644
index 0000000..4a15f77
--- /dev/null
+++ b/libc/malloc_debug/malloc_debug.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 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_H
+#define MALLOC_DEBUG_H
+
+#include <stdint.h>
+
+#include <private/bionic_malloc_dispatch.h>
+
+// Allocations that require a header include a variable length header.
+// This is the order that data structures will be found. If an optional
+// part of the header does not exist, the other parts of the header
+// will still be in this order.
+//   Header          (Required)
+//   BacktraceHeader (Optional: For the allocation backtrace)
+//   BacktraceHeader (Optional: For the free backtrace)
+//   uint8_t data    (Optional: Front guard, will be a multiple of sizeof(uintptr_t))
+//   allocation data
+//   uint8_t data    (Optional: End guard)
+//
+// If backtracing is enabled, then both BacktraceHeaders will be present.
+//
+// In the initialization function, offsets into the header will be set
+// for each different header location. The offsets are always from the
+// beginning of the Header section.
+struct Header {
+  uint32_t tag;
+  void* orig_pointer;
+  size_t size;
+  size_t usable_size;
+  size_t real_size() const { return size & ~(1U << 31); }
+  void set_zygote() { size |= 1U << 31; }
+  static size_t max_size() { return (1U << 31) - 1; }
+} __attribute__((packed));
+
+struct TrackHeader {
+  Header* prev = nullptr;
+  Header* next = nullptr;
+} __attribute__((packed));
+
+struct BacktraceHeader {
+  size_t num_frames;
+  uintptr_t frames[0];
+} __attribute__((packed));
+
+constexpr uint32_t DEBUG_TAG = 0x1ee7d00d;
+constexpr char LOG_DIVIDER[] = "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***";
+constexpr size_t FREE_TRACK_MEM_BUFFER_SIZE = 4096;
+
+extern const MallocDispatch* g_dispatch;
+
+#endif // MALLOC_DEBUG_H
diff --git a/libc/malloc_debug/tests/backtrace_fake.cpp b/libc/malloc_debug/tests/backtrace_fake.cpp
new file mode 100644
index 0000000..32da696
--- /dev/null
+++ b/libc/malloc_debug/tests/backtrace_fake.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <deque>
+#include <vector>
+#include <utility>
+
+#include "backtrace.h"
+#include "backtrace_fake.h"
+#include "debug_log.h"
+
+static std::deque<std::vector<uintptr_t>> g_fake_backtrace;
+
+void backtrace_fake_clear_all() {
+  g_fake_backtrace.clear();
+}
+
+void backtrace_fake_add(const std::vector<uintptr_t>& ips) {
+  g_fake_backtrace.push_back(ips);
+}
+
+void backtrace_startup() {
+}
+
+void backtrace_shutdown() {
+}
+
+size_t backtrace_get(uintptr_t* frames, size_t frame_num) {
+  if (frame_num == 0 || g_fake_backtrace.size() == 0) {
+    return 0;
+  }
+
+  size_t ips_size = g_fake_backtrace[0].size();
+  size_t total_frames = (frame_num < ips_size) ? frame_num : ips_size;
+  memcpy(frames, g_fake_backtrace[0].data(), sizeof(uintptr_t) * total_frames);
+  g_fake_backtrace.pop_front();
+  return total_frames;
+}
+
+void backtrace_log(uintptr_t* frames, size_t frame_count) {
+  for (size_t i = 0; i < frame_count; i++) {
+    error_log("  #%02zd pc %p", i, reinterpret_cast<void*>(frames[i]));
+  }
+}
diff --git a/libc/malloc_debug/tests/backtrace_fake.h b/libc/malloc_debug/tests/backtrace_fake.h
new file mode 100644
index 0000000..f2aa7a0
--- /dev/null
+++ b/libc/malloc_debug/tests/backtrace_fake.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MALLOC_DEBUG_TESTS_BACKTRACE_FAKE_H
+#define MALLOC_DEBUG_TESTS_BACKTRACE_FAKE_H
+
+#include <stdint.h>
+
+#include <vector>
+
+void backtrace_fake_clear_all();
+void backtrace_fake_add(const std::vector<uintptr_t>& ips);
+
+#endif // MALLOC_DEBUG_TESTS_BACKTRACE_FAKE_H
diff --git a/libc/malloc_debug/tests/libc_fake.cpp b/libc/malloc_debug/tests/libc_fake.cpp
new file mode 100644
index 0000000..45ace4d
--- /dev/null
+++ b/libc/malloc_debug/tests/libc_fake.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern "C" const char* getprogname() {
+  return "malloc_testing";
+}
diff --git a/libc/malloc_debug/tests/log_fake.cpp b/libc/malloc_debug/tests/log_fake.cpp
new file mode 100644
index 0000000..7350ea0
--- /dev/null
+++ b/libc/malloc_debug/tests/log_fake.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <log/log.h>
+#include <log/logger.h>
+
+// Forward declarations.
+class Backtrace;
+struct EventTagMap;
+struct AndroidLogEntry;
+
+std::string g_fake_log_buf;
+
+std::string g_fake_log_print;
+
+void resetLogs() {
+  g_fake_log_buf = "";
+  g_fake_log_print = "";
+}
+
+std::string getFakeLogBuf() {
+  return g_fake_log_buf;
+}
+
+std::string getFakeLogPrint() {
+  return g_fake_log_print;
+}
+
+extern "C" int __libc_format_log(int priority, const char* tag, const char* format, ...) {
+  g_fake_log_print += std::to_string(priority) + ' ';
+  g_fake_log_print += tag;
+  g_fake_log_print += ' ';
+
+  va_list ap;
+  va_start(ap, format);
+  android::base::StringAppendV(&g_fake_log_print, format, ap);
+  va_end(ap);
+
+  g_fake_log_print += '\n';
+
+  return 0;
+}
+
+extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
+  g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
+  g_fake_log_buf += tag;
+  g_fake_log_buf += ' ';
+  g_fake_log_buf += msg;
+  return 1;
+}
+
+extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+  g_fake_log_print += std::to_string(prio) + ' ';
+  g_fake_log_print += tag;
+  g_fake_log_print += ' ';
+
+  va_list ap;
+  va_start(ap, fmt);
+  android::base::StringAppendV(&g_fake_log_print, fmt, ap);
+  va_end(ap);
+
+  g_fake_log_print += '\n';
+
+  return 1;
+}
+
+extern "C" log_id_t android_name_to_log_id(const char*) {
+  return LOG_ID_SYSTEM;
+}
+
+extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+  errno = EACCES;
+  return nullptr;
+}
+
+extern "C" int android_logger_list_read(struct logger_list*, struct log_msg*) {
+  return 0;
+}
+
+extern "C" EventTagMap* android_openEventTagMap(const char*) {
+  return nullptr;
+}
+
+extern "C" int android_log_processBinaryLogBuffer(
+    struct logger_entry*,
+    AndroidLogEntry*, const EventTagMap*, char*, int) {
+  return 0;
+}
+
+extern "C" void android_logger_list_free(struct logger_list*) {
+}
diff --git a/libc/malloc_debug/tests/log_fake.h b/libc/malloc_debug/tests/log_fake.h
new file mode 100644
index 0000000..1ea72f8
--- /dev/null
+++ b/libc/malloc_debug/tests/log_fake.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MALLOC_DEBUG_TESTS_LOG_FAKE_H
+#define MALLOC_DEBUG_TESTS_LOG_FAKE_H
+
+#include <string>
+
+void resetLogs();
+std::string getFakeLogBuf();
+std::string getFakeLogPrint();
+
+#endif // MALLOC_DEBUG_TESTS_LOG_FAKE_H
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
new file mode 100644
index 0000000..ad25948
--- /dev/null
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <limits.h>
+
+#include <memory>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "Config.h"
+
+#include "log_fake.h"
+
+extern "C" int property_set(const char*, const char*);
+
+class MallocDebugConfigTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    resetLogs();
+  }
+
+  void TearDown() override {
+  }
+
+  std::unique_ptr<Config> config;
+
+  bool InitConfig(const char* property_value) {
+    config.reset(new Config);
+    property_set("libc.debug.malloc.options", property_value);
+    return config->SetFromProperties();
+  }
+};
+
+std::string usage_string(
+  "6 malloc_debug malloc debug options usage:\n"
+  "6 malloc_debug \n"
+  "6 malloc_debug   front_guard[=XX]\n"
+  "6 malloc_debug     Enables a front guard on all allocations. If XX is set\n"
+  "6 malloc_debug     it sets the number of bytes in the guard. The default is\n"
+  "6 malloc_debug     32 bytes.\n"
+  "6 malloc_debug \n"
+  "6 malloc_debug   rear_guard[=XX]\n"
+  "6 malloc_debug     Enables a rear guard on all allocations. If XX is set\n"
+  "6 malloc_debug     it sets the number of bytes in the guard. The default is\n"
+  "6 malloc_debug     32 bytes.\n"
+  "6 malloc_debug \n"
+  "6 malloc_debug   guard[=XX]\n"
+  "6 malloc_debug     Enables both a front guard and a rear guard on all allocations.\n"
+  "6 malloc_debug     If XX is set it sets the number of bytes in both guards.\n"
+  "6 malloc_debug     The default is 32 bytes.\n"
+  "6 malloc_debug \n"
+  "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     The default is 16 frames.\n"
+  "6 malloc_debug \n"
+  "6 malloc_debug   backtrace_enable_on_signal[=XX]\n"
+  "6 malloc_debug     Enable capturing the backtrace at the point of allocation.\n"
+  "6 malloc_debug     The backtrace capture is not enabled until the process\n"
+  "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.\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"
+  "6 malloc_debug     allocation. The default is to fill the entire allocation.\n"
+  "6 malloc_debug \n"
+  "6 malloc_debug   fill_on_free[=XX]\n"
+  "6 malloc_debug     On free, fill with the value 0xef. If XX is set it will\n"
+  "6 malloc_debug     only fill up to XX bytes of the allocation. The default is to\n"
+  "6 malloc_debug     fill the entire allocation.\n"
+  "6 malloc_debug \n"
+  "6 malloc_debug   fill[=XX]\n"
+  "6 malloc_debug     On both first allocation free, fill with the value 0xeb on\n"
+  "6 malloc_debug     first allocation and the value 0xef. If XX is set, only fill\n"
+  "6 malloc_debug     up to XX bytes. The default is to fill the entire allocation.\n"
+  "6 malloc_debug \n"
+  "6 malloc_debug   expand_alloc[=XX]\n"
+  "6 malloc_debug     Allocate an extra number of bytes for every allocation call.\n"
+  "6 malloc_debug     If XX is set, that is the number of bytes to expand the\n"
+  "6 malloc_debug     allocation by. The default is 16 bytes.\n"
+  "6 malloc_debug \n"
+  "6 malloc_debug   free_track[=XX]\n"
+  "6 malloc_debug     When a pointer is freed, do not free the memory right away.\n"
+  "6 malloc_debug     Instead, keep XX of these allocations around and then verify\n"
+  "6 malloc_debug     that they have not been modified when the total number of freed\n"
+  "6 malloc_debug     allocations exceeds the XX amount. When the program terminates,\n"
+  "6 malloc_debug     the rest of these allocations are verified.\n"
+  "6 malloc_debug     The default is to record 100 allocations.\n"
+  "6 malloc_debug \n"
+  "6 malloc_debug   leak_track\n"
+  "6 malloc_debug     Enable the leak tracking of memory allocations.\n"
+);
+
+TEST_F(MallocDebugConfigTest, unknown_option) {
+
+  ASSERT_FALSE(InitConfig("unknown_option"));
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg("6 malloc_debug malloc_testing: unknown option unknown_option\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, unparseable_number) {
+  ASSERT_FALSE(InitConfig("backtrace=XXX"));
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg("6 malloc_debug malloc_testing: bad value for option 'backtrace'\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, illegal_value_zero) {
+  ASSERT_FALSE(InitConfig("backtrace=0"));
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg("6 malloc_debug malloc_testing: bad value for option 'backtrace', value must be > 0: 0\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, no_space) {
+  ASSERT_FALSE(InitConfig("backtrace=10front_guard"));
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg(
+      "6 malloc_debug malloc_testing: bad value for option 'backtrace', "
+      "non space found after option: front_guard\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, illegal_value_negative) {
+  ASSERT_FALSE(InitConfig("backtrace=-1"));
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg("6 malloc_debug malloc_testing: bad value for option 'backtrace', value must be > 0: -1\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, overflow) {
+  ASSERT_FALSE(InitConfig("backtrace=99999999999999999999"));
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg(
+      "6 malloc_debug malloc_testing: bad value for option 'backtrace': "
+      "Math result not representable\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, set_value_error) {
+  ASSERT_FALSE(InitConfig("leak_track=12"));
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg(
+      "6 malloc_debug malloc_testing: value set for option 'leak_track' "
+      "which does not take a value\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, space_before_equal) {
+  ASSERT_TRUE(InitConfig("backtrace  =10"));
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+  ASSERT_EQ(10U, config->backtrace_frames);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, space_after_equal) {
+  ASSERT_TRUE(InitConfig("backtrace=  10"));
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+  ASSERT_EQ(10U, config->backtrace_frames);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, extra_space) {
+  ASSERT_TRUE(InitConfig("   backtrace=64   "));
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+  ASSERT_EQ(64U, config->backtrace_frames);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, multiple_options) {
+  ASSERT_TRUE(InitConfig("  backtrace=64   front_guard=24"));
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS | FRONT_GUARD, config->options);
+  ASSERT_EQ(64U, config->backtrace_frames);
+  ASSERT_EQ(24U, config->front_guard_bytes);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, front_guard) {
+  ASSERT_TRUE(InitConfig("front_guard=24"));
+  ASSERT_EQ(FRONT_GUARD, config->options);
+  ASSERT_EQ(24U, config->front_guard_bytes);
+
+  ASSERT_TRUE(InitConfig("front_guard"));
+  ASSERT_EQ(FRONT_GUARD, config->options);
+  ASSERT_EQ(32U, config->front_guard_bytes);
+
+  ASSERT_TRUE(InitConfig("front_guard=39"));
+  ASSERT_EQ(FRONT_GUARD, config->options);
+  ASSERT_EQ(40U, config->front_guard_bytes);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, rear_guard) {
+  ASSERT_TRUE(InitConfig("rear_guard=50"));
+  ASSERT_EQ(REAR_GUARD, config->options);
+  ASSERT_EQ(50U, config->rear_guard_bytes);
+
+  ASSERT_TRUE(InitConfig("rear_guard"));
+  ASSERT_EQ(REAR_GUARD, config->options);
+  ASSERT_EQ(32U, config->rear_guard_bytes);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, guard) {
+  ASSERT_TRUE(InitConfig("guard=32"));
+  ASSERT_EQ(FRONT_GUARD | REAR_GUARD, config->options);
+  ASSERT_EQ(32U, config->front_guard_bytes);
+  ASSERT_EQ(32U, config->rear_guard_bytes);
+
+  ASSERT_TRUE(InitConfig("guard"));
+  ASSERT_EQ(FRONT_GUARD | REAR_GUARD, config->options);
+  ASSERT_EQ(32U, config->front_guard_bytes);
+  ASSERT_EQ(32U, config->rear_guard_bytes);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, backtrace) {
+  ASSERT_TRUE(InitConfig("backtrace=64"));
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+  ASSERT_EQ(64U, config->backtrace_frames);
+
+  ASSERT_TRUE(InitConfig("backtrace"));
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+  ASSERT_EQ(16U, config->backtrace_frames);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, backtrace_enable_on_signal) {
+  ASSERT_TRUE(InitConfig("backtrace_enable_on_signal=64"));
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+  ASSERT_EQ(64U, config->backtrace_frames);
+
+  ASSERT_TRUE(InitConfig("backtrace_enable_on_signal"));
+  ASSERT_EQ(BACKTRACE | TRACK_ALLOCS, config->options);
+  ASSERT_EQ(16U, config->backtrace_frames);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, fill_on_alloc) {
+  ASSERT_TRUE(InitConfig("fill_on_alloc=64"));
+  ASSERT_EQ(FILL_ON_ALLOC, config->options);
+  ASSERT_EQ(64U, config->fill_on_alloc_bytes);
+
+  ASSERT_TRUE(InitConfig("fill_on_alloc"));
+  ASSERT_EQ(FILL_ON_ALLOC, config->options);
+  ASSERT_EQ(SIZE_MAX, config->fill_on_alloc_bytes);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, fill_on_free) {
+  ASSERT_TRUE(InitConfig("fill_on_free=64"));
+  ASSERT_EQ(FILL_ON_FREE, config->options);
+  ASSERT_EQ(64U, config->fill_on_free_bytes);
+
+  ASSERT_TRUE(InitConfig("fill_on_free"));
+  ASSERT_EQ(FILL_ON_FREE, config->options);
+  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, fill) {
+  ASSERT_TRUE(InitConfig("fill=64"));
+  ASSERT_EQ(FILL_ON_ALLOC | FILL_ON_FREE, config->options);
+  ASSERT_EQ(64U, config->fill_on_alloc_bytes);
+  ASSERT_EQ(64U, config->fill_on_free_bytes);
+
+  ASSERT_TRUE(InitConfig("fill"));
+  ASSERT_EQ(FILL_ON_ALLOC | FILL_ON_FREE, config->options);
+  ASSERT_EQ(SIZE_MAX, config->fill_on_alloc_bytes);
+  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, expand_alloc) {
+  ASSERT_TRUE(InitConfig("expand_alloc=1234"));
+  ASSERT_EQ(EXPAND_ALLOC, config->options);
+  ASSERT_EQ(1234U, config->expand_alloc_bytes);
+
+  ASSERT_TRUE(InitConfig("expand_alloc"));
+  ASSERT_EQ(EXPAND_ALLOC, config->options);
+  ASSERT_EQ(16U, config->expand_alloc_bytes);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, free_track) {
+  ASSERT_TRUE(InitConfig("free_track=1234"));
+  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
+  ASSERT_EQ(1234U, config->free_track_allocations);
+  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
+
+  ASSERT_TRUE(InitConfig("free_track"));
+  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
+  ASSERT_EQ(100U, config->free_track_allocations);
+  ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, free_track_and_fill_on_free) {
+  ASSERT_TRUE(InitConfig("free_track=1234 fill_on_free=32"));
+  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
+  ASSERT_EQ(1234U, config->free_track_allocations);
+  ASSERT_EQ(32U, config->fill_on_free_bytes);
+
+  ASSERT_TRUE(InitConfig("free_track fill_on_free=60"));
+  ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options);
+  ASSERT_EQ(100U, config->free_track_allocations);
+  ASSERT_EQ(60U, config->fill_on_free_bytes);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, leak_track) {
+  ASSERT_TRUE(InitConfig("leak_track"));
+  ASSERT_EQ(LEAK_TRACK | TRACK_ALLOCS, config->options);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, leak_track_fail) {
+  ASSERT_FALSE(InitConfig("leak_track=100"));
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string log_msg(
+      "6 malloc_debug malloc_testing: value set for option 'leak_track' "
+      "which does not take a value\n");
+  ASSERT_STREQ((log_msg + usage_string).c_str(), 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
new file mode 100644
index 0000000..0e8ca68
--- /dev/null
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -0,0 +1,1391 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <malloc.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <vector>
+#include <utility>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#include <private/bionic_macros.h>
+#include <private/bionic_malloc_dispatch.h>
+
+#include "malloc_debug.h"
+
+#include "log_fake.h"
+#include "backtrace_fake.h"
+
+__BEGIN_DECLS
+
+int property_set(const char*, const char*);
+bool debug_initialize(const MallocDispatch*, int*);
+void debug_finalize();
+
+void* debug_malloc(size_t);
+void debug_free(void*);
+void* debug_calloc(size_t, size_t);
+void* debug_realloc(void*, size_t);
+int debug_posix_memalign(void**, size_t, size_t);
+void* debug_memalign(size_t, size_t);
+size_t debug_malloc_usable_size(void*);
+void debug_get_malloc_leak_info(uint8_t**, size_t*, size_t*, size_t*, size_t*);
+void debug_free_malloc_leak_info(uint8_t*);
+
+struct mallinfo debug_mallinfo();
+
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+void* debug_pvalloc(size_t);
+void* debug_valloc(size_t);
+#endif
+
+__END_DECLS
+
+constexpr char DIVIDER[] =
+    "6 malloc_debug *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n";
+
+constexpr uint32_t TRACK_HEADER = 0x2;
+constexpr uint32_t BACKTRACE_HEADER = 0x4;
+
+static size_t get_tag_offset(uint32_t flags = 0, size_t backtrace_frames = 0) {
+  size_t offset = BIONIC_ALIGN(sizeof(Header), sizeof(uintptr_t));
+  if (flags & TRACK_HEADER) {
+    offset += BIONIC_ALIGN(sizeof(TrackHeader), sizeof(uintptr_t));
+  }
+  if (flags & BACKTRACE_HEADER) {
+    offset += BIONIC_ALIGN(sizeof(BacktraceHeader) + sizeof(uintptr_t) * backtrace_frames - 1, sizeof(uintptr_t));
+    offset += BIONIC_ALIGN(sizeof(BacktraceHeader) + sizeof(uintptr_t) * backtrace_frames - 1, sizeof(uintptr_t));
+  }
+  return offset;
+}
+
+class MallocDebugTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    initialized = false;
+    resetLogs();
+    backtrace_fake_clear_all();
+  }
+
+  void TearDown() override {
+    if (initialized) {
+      debug_finalize();
+    }
+  }
+
+  void Init(const char* property_value) {
+    property_set("libc.debug.malloc.options", property_value);
+    zygote = 0;
+    ASSERT_TRUE(debug_initialize(&dispatch, &zygote));
+    initialized = true;
+  }
+
+  bool initialized;
+
+  int zygote;
+
+  static MallocDispatch dispatch;
+};
+
+MallocDispatch MallocDebugTest::dispatch = {
+  .calloc = calloc,
+  .free = free,
+  .mallinfo = mallinfo,
+  .malloc = malloc,
+  .malloc_usable_size = malloc_usable_size,
+  .memalign = memalign,
+  .posix_memalign = posix_memalign,
+  .realloc = realloc,
+};
+
+void VerifyAllocCalls() {
+  size_t alloc_size = 1024;
+
+  // Verify debug_malloc.
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(alloc_size));
+  ASSERT_TRUE(pointer != nullptr);
+  for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) {
+    ASSERT_EQ(0xeb, pointer[i]);
+  }
+  debug_free(pointer);
+
+  // Verify debug_calloc.
+  pointer = reinterpret_cast<uint8_t*>(debug_calloc(1, alloc_size));
+  ASSERT_TRUE(pointer != nullptr);
+  for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) {
+    ASSERT_EQ(0, pointer[i]) << "Failed at byte " << i;
+  }
+  debug_free(pointer);
+
+  pointer = reinterpret_cast<uint8_t*>(debug_memalign(128, alloc_size));
+  ASSERT_TRUE(pointer != nullptr);
+  for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) {
+    ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i;
+  }
+  debug_free(pointer);
+
+  pointer = reinterpret_cast<uint8_t*>(debug_realloc(nullptr, alloc_size));
+  ASSERT_TRUE(pointer != nullptr);
+  for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) {
+    ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i;
+  }
+  memset(pointer, 0xff, alloc_size);
+  // Increase the size, verify the extra length is initialized to 0xeb,
+  // but the rest is 0xff.
+  pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, alloc_size * 2));
+  ASSERT_TRUE(pointer != nullptr);
+  for (size_t i = 0; i < alloc_size; i++) {
+    ASSERT_EQ(0xff, pointer[i]) << "Failed at byte " << i;
+  }
+  for (size_t i = alloc_size; i < debug_malloc_usable_size(pointer); i++) {
+    ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i;
+  }
+  memset(pointer, 0xff, debug_malloc_usable_size(pointer));
+  // Shrink the size and verify nothing changes.
+  pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, alloc_size));
+  ASSERT_TRUE(pointer != nullptr);
+  for (size_t i = 0; i < debug_malloc_usable_size(pointer); i++) {
+    ASSERT_EQ(0xff, pointer[i]) << "Failed at byte " << i;
+  }
+  // This should free the pointer.
+  pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 0));
+  ASSERT_TRUE(pointer == nullptr);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, fill_generic) {
+  Init("fill");
+  VerifyAllocCalls();
+}
+
+TEST_F(MallocDebugTest, fill_on_alloc_generic) {
+  Init("fill_on_alloc");
+  VerifyAllocCalls();
+}
+
+TEST_F(MallocDebugTest, fill_on_alloc_partial) {
+  Init("fill_on_alloc=25");
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  for (size_t i = 0; i < 25; i++) {
+    ASSERT_EQ(0xeb, pointer[i]) << "Failed at byte " << i;
+  }
+  debug_free(pointer);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, fill_on_free) {
+  Init("fill_on_free free_track");
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  size_t usable_size = debug_malloc_usable_size(pointer);
+  memset(pointer, 0, usable_size);
+  debug_free(pointer);
+
+  for (size_t i = 0; i < usable_size; i++) {
+    ASSERT_EQ(0xef, pointer[i]) << "Failed at byte " << i;
+  }
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, fill_on_free_partial) {
+  Init("fill_on_free=30 free_track");
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  size_t usable_size = debug_malloc_usable_size(pointer);
+  memset(pointer, 0, usable_size);
+  debug_free(pointer);
+
+  for (size_t i = 0; i < 30; i++) {
+    ASSERT_EQ(0xef, pointer[i]) << "Failed to fill on free at byte " << i;
+  }
+  for (size_t i = 30; i < usable_size; i++) {
+    ASSERT_EQ(0, pointer[i]) << "Filled too much on byte " << i;
+  }
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, free_track_partial) {
+  Init("fill_on_free=30 free_track");
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  size_t usable_size = debug_malloc_usable_size(pointer);
+  memset(pointer, 0, usable_size);
+  debug_free(pointer);
+
+  for (size_t i = 0; i < 30; i++) {
+    ASSERT_EQ(0xef, pointer[i]) << "Failed to fill on free at byte " << i;
+  }
+  for (size_t i = 30; i < usable_size; i++) {
+    ASSERT_EQ(0, pointer[i]) << "Filled too much on byte " << i;
+  }
+
+  debug_finalize();
+  initialized = false;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, all_options) {
+  Init("guard backtrace fill expand_alloc free_track leak_track");
+  VerifyAllocCalls();
+}
+
+TEST_F(MallocDebugTest, expand_alloc) {
+  Init("expand_alloc=1024");
+
+  void* pointer = debug_malloc(10);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_LE(1034U, debug_malloc_usable_size(pointer));
+  debug_free(pointer);
+
+  pointer = debug_calloc(1, 20);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_LE(1044U, debug_malloc_usable_size(pointer));
+  debug_free(pointer);
+
+  pointer = debug_memalign(128, 15);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_LE(1039U, debug_malloc_usable_size(pointer));
+  debug_free(pointer);
+
+  pointer = debug_realloc(nullptr, 30);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_LE(1054U, debug_malloc_usable_size(pointer));
+  pointer = debug_realloc(pointer, 100);
+  ASSERT_LE(1124U, debug_malloc_usable_size(pointer));
+  debug_free(pointer);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, front_guard) {
+  Init("front_guard=32");
+
+  // Create a buffer for doing comparisons.
+  std::vector<uint8_t> buffer(32);
+  memset(buffer.data(), 0xaa, buffer.size());
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0);
+  memset(pointer, 0xff, 100);
+  debug_free(pointer);
+
+  // Loop through a bunch alignments.
+  for (size_t alignment = 1; alignment <= 256; alignment++) {
+    pointer = reinterpret_cast<uint8_t*>(debug_memalign(alignment, 100));
+    ASSERT_TRUE(pointer != nullptr);
+    ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0);
+    size_t alignment_mask = alignment - 1;
+    if (!powerof2(alignment)) {
+      alignment_mask = BIONIC_ROUND_UP_POWER_OF_2(alignment) - 1;
+    }
+    ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(pointer) & alignment_mask);
+    memset(pointer, 0xff, 100);
+    debug_free(pointer);
+  }
+
+  pointer = reinterpret_cast<uint8_t*>(debug_calloc(1, 100));
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0);
+  for (size_t i = 0; i < 100; i++) {
+    ASSERT_EQ(0, pointer[i]) << "debug_calloc non-zero byte at " << i;
+  }
+  debug_free(pointer);
+
+  pointer = reinterpret_cast<uint8_t*>(debug_realloc(nullptr, 100));
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0);
+  memset(pointer, 0xff, 100);
+  pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 200));
+  ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0);
+  memset(pointer, 0xff, 200);
+  pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 0));
+  ASSERT_TRUE(pointer == nullptr);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, realloc_memalign_memory) {
+  Init("rear_guard");
+
+  void* pointer = debug_memalign(1024, 100);
+  ASSERT_TRUE(pointer != nullptr);
+  memset(pointer, 0, 100);
+
+  pointer = debug_realloc(pointer, 1024);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_EQ(1024U, debug_malloc_usable_size(pointer));
+  memset(pointer, 0, 1024);
+  debug_free(pointer);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, front_guard_corrupted) {
+  Init("front_guard=32");
+
+  backtrace_fake_add(std::vector<uintptr_t> {0x1, 0x2, 0x3});
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  pointer[-32] = 0x00;
+  pointer[-15] = 0x02;
+  debug_free(pointer);
+
+  std::string expected_log(DIVIDER);
+  expected_log += android::base::StringPrintf(
+      "6 malloc_debug +++ ALLOCATION %p SIZE 100 HAS A CORRUPTED FRONT GUARD\n", pointer);
+  expected_log += "6 malloc_debug   pointer[-32] = 0x00 (expected 0xaa)\n";
+  expected_log += "6 malloc_debug   pointer[-15] = 0x02 (expected 0xaa)\n";
+  expected_log += "6 malloc_debug Backtrace at time of failure:\n";
+  expected_log += "6 malloc_debug   #00 pc 0x1\n";
+  expected_log += "6 malloc_debug   #01 pc 0x2\n";
+  expected_log += "6 malloc_debug   #02 pc 0x3\n";
+  expected_log += DIVIDER;
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, rear_guard) {
+  Init("rear_guard=32");
+
+  // Create a buffer for doing comparisons.
+  std::vector<uint8_t> buffer(32);
+  memset(buffer.data(), 0xbb, buffer.size());
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_EQ(100U, debug_malloc_usable_size(pointer));
+  ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0);
+  memset(pointer, 0xff, 100);
+  debug_free(pointer);
+
+  // Loop through a bunch alignments.
+  for (size_t alignment = 1; alignment <= 256; alignment++) {
+    pointer = reinterpret_cast<uint8_t*>(debug_memalign(alignment, 100));
+    ASSERT_TRUE(pointer != nullptr);
+    ASSERT_EQ(100U, debug_malloc_usable_size(pointer));
+    ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0);
+    size_t alignment_mask = alignment - 1;
+    if (!powerof2(alignment)) {
+      alignment_mask = BIONIC_ROUND_UP_POWER_OF_2(alignment) - 1;
+    }
+    ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(pointer) & alignment_mask)
+        << "Failed at alignment " << alignment << " mask " << alignment_mask;
+    memset(pointer, 0xff, 100);
+    debug_free(pointer);
+  }
+
+  pointer = reinterpret_cast<uint8_t*>(debug_calloc(1, 100));
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_EQ(100U, debug_malloc_usable_size(pointer));
+  ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0);
+  for (size_t i = 0; i < 100; i++) {
+    ASSERT_EQ(0, pointer[i]) << "debug_calloc non-zero byte at " << i;
+  }
+  debug_free(pointer);
+
+  pointer = reinterpret_cast<uint8_t*>(debug_realloc(nullptr, 100));
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0);
+  memset(pointer, 0xff, 100);
+  pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 200));
+  ASSERT_TRUE(memcmp(buffer.data(), &pointer[200], buffer.size()) == 0);
+  for (size_t i = 0; i < 100; i++) {
+    ASSERT_EQ(0xff, pointer[i]) << "debug_realloc not copied byte at " << i;
+  }
+  memset(pointer, 0xff, 200);
+  pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 0));
+  ASSERT_TRUE(pointer == nullptr);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, rear_guard_corrupted) {
+  Init("rear_guard=32");
+
+  backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200, 0x300});
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  pointer[130] = 0xbf;
+  pointer[131] = 0x00;
+  debug_free(pointer);
+
+  std::string expected_log(DIVIDER);
+  expected_log += android::base::StringPrintf(
+      "6 malloc_debug +++ ALLOCATION %p SIZE 100 HAS A CORRUPTED REAR GUARD\n", pointer);
+  expected_log += "6 malloc_debug   pointer[130] = 0xbf (expected 0xbb)\n";
+  expected_log += "6 malloc_debug   pointer[131] = 0x00 (expected 0xbb)\n";
+  expected_log += "6 malloc_debug Backtrace at time of failure:\n";
+  expected_log += "6 malloc_debug   #00 pc 0x100\n";
+  expected_log += "6 malloc_debug   #01 pc 0x200\n";
+  expected_log += "6 malloc_debug   #02 pc 0x300\n";
+  expected_log += DIVIDER;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, rear_guard_corrupted_after_realloc_shrink) {
+  Init("rear_guard=32");
+
+  backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200, 0x300});
+
+  void* pointer = debug_malloc(200);
+  ASSERT_TRUE(pointer != nullptr);
+  memset(pointer, 0, 200);
+
+  uint8_t* pointer_shrink = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 100));
+  pointer_shrink[130] = 0xbf;
+  pointer_shrink[131] = 0x00;
+  debug_free(pointer);
+
+  // When shrinking sizes, the same pointer should be returned.
+  ASSERT_EQ(pointer, pointer_shrink);
+
+  std::string expected_log(DIVIDER);
+  expected_log += android::base::StringPrintf(
+      "6 malloc_debug +++ ALLOCATION %p SIZE 100 HAS A CORRUPTED REAR GUARD\n", pointer);
+  expected_log += "6 malloc_debug   pointer[130] = 0xbf (expected 0xbb)\n";
+  expected_log += "6 malloc_debug   pointer[131] = 0x00 (expected 0xbb)\n";
+  expected_log += "6 malloc_debug Backtrace at time of failure:\n";
+  expected_log += "6 malloc_debug   #00 pc 0x100\n";
+  expected_log += "6 malloc_debug   #01 pc 0x200\n";
+  expected_log += "6 malloc_debug   #02 pc 0x300\n";
+  expected_log += DIVIDER;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, tag_corrupted) {
+  Init("rear_guard=32");
+
+  backtrace_fake_add(std::vector<uintptr_t> {0xa, 0xb, 0xc});
+
+  backtrace_fake_add(std::vector<uintptr_t> {0xaa, 0xbb, 0xcc});
+
+  backtrace_fake_add(std::vector<uintptr_t> {0xaaa, 0xbbb, 0xccc});
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  uint8_t saved = pointer[-get_tag_offset()];
+  pointer[-get_tag_offset()] = 0x00;
+  ASSERT_EQ(0U, debug_malloc_usable_size(pointer));
+  ASSERT_TRUE(debug_realloc(pointer, 200) == nullptr);
+  debug_free(pointer);
+
+  // Fix the pointer and really free it.
+  pointer[-get_tag_offset()] = saved;
+  debug_free(pointer);
+
+  std::string expected_log(DIVIDER);
+  expected_log += android::base::StringPrintf(
+      "6 malloc_debug +++ ALLOCATION %p HAS INVALID TAG 1ee7d000 (malloc_usable_size)\n",
+      pointer);
+  expected_log += "6 malloc_debug Backtrace at time of failure:\n";
+  expected_log += "6 malloc_debug   #00 pc 0xa\n";
+  expected_log += "6 malloc_debug   #01 pc 0xb\n";
+  expected_log += "6 malloc_debug   #02 pc 0xc\n";
+  expected_log += DIVIDER;
+
+  expected_log += DIVIDER;
+  expected_log += android::base::StringPrintf(
+      "6 malloc_debug +++ ALLOCATION %p HAS INVALID TAG 1ee7d000 (realloc)\n",
+      pointer);
+  expected_log += "6 malloc_debug Backtrace at time of failure:\n";
+  expected_log += "6 malloc_debug   #00 pc 0xaa\n";
+  expected_log += "6 malloc_debug   #01 pc 0xbb\n";
+  expected_log += "6 malloc_debug   #02 pc 0xcc\n";
+  expected_log += DIVIDER;
+
+  expected_log += DIVIDER;
+  expected_log += android::base::StringPrintf(
+      "6 malloc_debug +++ ALLOCATION %p HAS INVALID TAG 1ee7d000 (free)\n",
+      pointer);
+  expected_log += "6 malloc_debug Backtrace at time of failure:\n";
+  expected_log += "6 malloc_debug   #00 pc 0xaaa\n";
+  expected_log += "6 malloc_debug   #01 pc 0xbbb\n";
+  expected_log += "6 malloc_debug   #02 pc 0xccc\n";
+  expected_log += DIVIDER;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, leak_track_no_frees) {
+  Init("leak_track");
+
+  void* pointer1 = debug_malloc(200);
+  ASSERT_TRUE(pointer1 != nullptr);
+  memset(pointer1, 0, 200);
+
+  void* pointer2 = debug_malloc(128);
+  ASSERT_TRUE(pointer2 != nullptr);
+  memset(pointer2, 0, 128);
+
+  void* pointer3 = debug_malloc(1024);
+  ASSERT_TRUE(pointer3 != nullptr);
+  memset(pointer3, 0, 1024);
+
+  debug_finalize();
+  initialized = false;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string 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 += android::base::StringPrintf(
+        "6 malloc_debug +++ malloc_testing leaked block of size 200 at %p (leak 2 of 3)\n",
+      pointer1);
+  expected_log += android::base::StringPrintf(
+        "6 malloc_debug +++ malloc_testing leaked block of size 128 at %p (leak 3 of 3)\n",
+      pointer2);
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, leak_track_no_frees_with_backtrace) {
+  Init("leak_track backtrace");
+
+  backtrace_fake_add(std::vector<uintptr_t> {0x1000, 0x2000, 0x3000});
+
+  void* pointer1 = debug_malloc(100);
+  ASSERT_TRUE(pointer1 != nullptr);
+  memset(pointer1, 0, 100);
+
+  backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000, 0xc000, 0xd000});
+
+  void* pointer2 = debug_malloc(128);
+  ASSERT_TRUE(pointer2 != nullptr);
+  memset(pointer2, 0, 128);
+
+  backtrace_fake_add(std::vector<uintptr_t> {0xfe000, 0xde000, 0xce000, 0xbe000, 0xae000});
+
+  void* pointer3 = debug_malloc(1024);
+  ASSERT_TRUE(pointer3 != nullptr);
+  memset(pointer3, 0, 1024);
+
+  debug_finalize();
+  initialized = false;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string 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";
+  expected_log += "6 malloc_debug   #00 pc 0xfe000\n";
+  expected_log += "6 malloc_debug   #01 pc 0xde000\n";
+  expected_log += "6 malloc_debug   #02 pc 0xce000\n";
+  expected_log += "6 malloc_debug   #03 pc 0xbe000\n";
+  expected_log += "6 malloc_debug   #04 pc 0xae000\n";
+
+  expected_log += android::base::StringPrintf(
+      "6 malloc_debug +++ malloc_testing leaked block of size 128 at %p (leak 2 of 3)\n",
+      pointer2);
+  expected_log += "6 malloc_debug Backtrace at time of allocation:\n";
+  expected_log += "6 malloc_debug   #00 pc 0xa000\n";
+  expected_log += "6 malloc_debug   #01 pc 0xb000\n";
+  expected_log += "6 malloc_debug   #02 pc 0xc000\n";
+  expected_log += "6 malloc_debug   #03 pc 0xd000\n";
+
+  expected_log += android::base::StringPrintf(
+      "6 malloc_debug +++ malloc_testing leaked block of size 100 at %p (leak 3 of 3)\n",
+      pointer1);
+  expected_log += "6 malloc_debug Backtrace at time of allocation:\n";
+  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());
+}
+
+TEST_F(MallocDebugTest, leak_track_frees) {
+  Init("leak_track");
+
+  void* pointer1 = debug_malloc(390);
+  ASSERT_TRUE(pointer1 != nullptr);
+  memset(pointer1, 0, 390);
+  debug_free(pointer1);
+
+  pointer1 = debug_malloc(100);
+  ASSERT_TRUE(pointer1 != nullptr);
+  memset(pointer1, 0, 100);
+
+  void* pointer2 = debug_malloc(250);
+  ASSERT_TRUE(pointer2 != nullptr);
+  memset(pointer2, 0, 250);
+  debug_free(pointer2);
+
+  pointer2 = debug_malloc(450);
+  ASSERT_TRUE(pointer2 != nullptr);
+  memset(pointer2, 0, 450);
+
+  void* pointer3 = debug_malloc(999);
+  ASSERT_TRUE(pointer3 != nullptr);
+  memset(pointer3, 0, 999);
+  debug_free(pointer2);
+
+  debug_finalize();
+  initialized = false;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string expected_log = android::base::StringPrintf(
+      "6 malloc_debug +++ malloc_testing leaked block of size 999 at %p (leak 1 of 2)\n",
+      pointer3);
+  expected_log += android::base::StringPrintf(
+      "6 malloc_debug +++ malloc_testing leaked block of size 100 at %p (leak 2 of 2)\n",
+      pointer1);
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, free_track) {
+  Init("free_track=5");
+
+  void* pointers[10];
+  for (size_t i = 0; i < sizeof(pointers) / sizeof(void*); i++) {
+    pointers[i] = debug_malloc(100 + i);
+    ASSERT_TRUE(pointers[i] != nullptr);
+    memset(pointers[i], 0, 100 + i);
+    debug_free(pointers[i]);
+  }
+
+  // Large allocations (> 4096) to verify large allocation checks.
+  void* pointer = debug_malloc(8192);
+  ASSERT_TRUE(pointer != nullptr);
+  memset(pointer, 0, 8192);
+  debug_free(pointer);
+
+  pointer = debug_malloc(9000);
+  ASSERT_TRUE(pointer != nullptr);
+  memset(pointer, 0, 9000);
+  debug_free(pointer);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, free_track_use_after_free) {
+  Init("free_track=5");
+
+  uint8_t* pointers[5];
+  for (size_t i = 0; i < sizeof(pointers) / sizeof(void*); i++) {
+    pointers[i] = reinterpret_cast<uint8_t*>(debug_malloc(100 + i));
+    ASSERT_TRUE(pointers[i] != nullptr);
+    memset(pointers[i], 0, 100 + i);
+    debug_free(pointers[i]);
+  }
+
+  // Stomp on the data.
+  pointers[0][20] = 0xaf;
+  pointers[0][99] = 0x12;
+
+  pointers[3][3] = 0x34;
+
+  // Large allocations (> 4096) to verify large allocation checks.
+  uint8_t* pointer1_large = reinterpret_cast<uint8_t*>(debug_malloc(8192));
+  ASSERT_TRUE(pointer1_large != nullptr);
+  memset(pointer1_large, 0, 8192);
+  debug_free(pointer1_large);
+
+  pointer1_large[4095] = 0x90;
+  pointer1_large[4100] = 0x56;
+  pointer1_large[8191] = 0x89;
+
+  uint8_t* pointer2_large = reinterpret_cast<uint8_t*>(debug_malloc(9000));
+  ASSERT_TRUE(pointer2_large != nullptr);
+  memset(pointer2_large, 0, 9000);
+  debug_free(pointer2_large);
+
+  pointer2_large[8200] = 0x78;
+
+  // Do a bunch of alloc and free to verify the above frees are checked.
+  for (size_t i = 0; i < 10; i++) {
+    void* flush_pointer = debug_malloc(100+i);
+    ASSERT_TRUE(flush_pointer != nullptr);
+    memset(flush_pointer, 0, 100 + i);
+    debug_free(flush_pointer);
+  }
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string expected_log(DIVIDER);
+  expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointers[0]);
+  expected_log += "6 malloc_debug   pointer[20] = 0xaf (expected 0xef)\n";
+  expected_log += "6 malloc_debug   pointer[99] = 0x12 (expected 0xef)\n";
+  expected_log += DIVIDER;
+  expected_log += DIVIDER;
+  expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointers[3]);
+  expected_log += "6 malloc_debug   pointer[3] = 0x34 (expected 0xef)\n";
+  expected_log += DIVIDER;
+  expected_log += DIVIDER;
+  expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer1_large);
+  expected_log += "6 malloc_debug   pointer[4095] = 0x90 (expected 0xef)\n";
+  expected_log += "6 malloc_debug   pointer[4100] = 0x56 (expected 0xef)\n";
+  expected_log += "6 malloc_debug   pointer[8191] = 0x89 (expected 0xef)\n";
+  expected_log += DIVIDER;
+  expected_log += DIVIDER;
+  expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer2_large);
+  expected_log += "6 malloc_debug   pointer[8200] = 0x78 (expected 0xef)\n";
+  expected_log += DIVIDER;
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, free_track_use_after_free_finalize) {
+  Init("free_track=100");
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
+  ASSERT_TRUE(pointer != nullptr);
+  memset(pointer, 0, 100);
+  debug_free(pointer);
+
+  pointer[56] = 0x91;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+
+  debug_finalize();
+  initialized = false;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string expected_log(DIVIDER);
+  expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer);
+  expected_log += "6 malloc_debug   pointer[56] = 0x91 (expected 0xef)\n";
+  expected_log += DIVIDER;
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, free_track_use_after_free_with_backtrace) {
+  Init("free_track=100 backtrace");
+
+  // Alloc backtrace.
+  backtrace_fake_add(std::vector<uintptr_t> {0xf0, 0xe, 0xd});
+  // Free backtrace.
+  backtrace_fake_add(std::vector<uintptr_t> {0xfa, 0xeb, 0xdc});
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(200));
+  ASSERT_TRUE(pointer != nullptr);
+  memset(pointer, 0, 200);
+  debug_free(pointer);
+
+  pointer[101] = 0xab;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+
+  debug_finalize();
+  initialized = false;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string expected_log(DIVIDER);
+  expected_log += android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p USED AFTER FREE\n", pointer);
+  expected_log += "6 malloc_debug   pointer[101] = 0xab (expected 0xef)\n";
+  expected_log += "6 malloc_debug Backtrace at time of free:\n";
+  expected_log += "6 malloc_debug   #00 pc 0xfa\n";
+  expected_log += "6 malloc_debug   #01 pc 0xeb\n";
+  expected_log += "6 malloc_debug   #02 pc 0xdc\n";
+  expected_log += DIVIDER;
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, get_malloc_leak_info_invalid) {
+  Init("fill");
+
+  uint8_t* info;
+  size_t overall_size;
+  size_t info_size;
+  size_t total_memory;
+  size_t backtrace_size;
+
+  std::string expected_log("6 malloc_debug get_malloc_leak_info: At least one invalid parameter.\n");
+
+  debug_get_malloc_leak_info(nullptr, &overall_size, &info_size, &total_memory, &backtrace_size);
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+
+  resetLogs();
+  debug_get_malloc_leak_info(&info, nullptr, &info_size, &total_memory, &backtrace_size);
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+
+  resetLogs();
+  debug_get_malloc_leak_info(&info, &overall_size, nullptr, &total_memory, &backtrace_size);
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+
+  resetLogs();
+  debug_get_malloc_leak_info(&info, &overall_size, &info_size, nullptr, &backtrace_size);
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+
+  resetLogs();
+  debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, nullptr);
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, get_malloc_leak_info_not_enabled) {
+  Init("fill");
+
+  uint8_t* info;
+  size_t overall_size;
+  size_t info_size;
+  size_t total_memory;
+  size_t backtrace_size;
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
+  std::string expected_log(
+      "6 malloc_debug get_malloc_leak_info: Allocations not being tracked, to enable "
+      "set the option 'backtrace'.\n");
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+struct InfoEntry {
+  size_t size;
+  size_t num_frames;
+  uintptr_t frames[0];
+} __attribute__((packed));
+
+TEST_F(MallocDebugTest, get_malloc_leak_info_empty) {
+  Init("backtrace");
+
+  uint8_t* info;
+  size_t overall_size;
+  size_t info_size;
+  size_t total_memory;
+  size_t backtrace_size;
+
+  debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
+  ASSERT_TRUE(info == nullptr);
+  ASSERT_EQ(0U, overall_size);
+  ASSERT_EQ(0U, info_size);
+  ASSERT_EQ(0U, total_memory);
+  ASSERT_EQ(0U, backtrace_size);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, get_malloc_leak_info_single) {
+  Init("backtrace");
+
+  // Create the expected info buffer.
+  size_t individual_size = 2 * sizeof(size_t) + 16 * sizeof(uintptr_t);
+  std::vector<uint8_t> expected_info(individual_size);
+  memset(expected_info.data(), 0, individual_size);
+
+  InfoEntry* entry = reinterpret_cast<InfoEntry*>(expected_info.data());
+  entry->size = 200;
+  entry->num_frames = 3;
+  entry->frames[0] = 0xf;
+  entry->frames[1] = 0xe;
+  entry->frames[2] = 0xd;
+
+  backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd});
+
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(entry->size));
+  ASSERT_TRUE(pointer != nullptr);
+  memset(pointer, 0, entry->size);
+
+  uint8_t* info;
+  size_t overall_size;
+  size_t info_size;
+  size_t total_memory;
+  size_t backtrace_size;
+
+  debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(individual_size, overall_size);
+  ASSERT_EQ(individual_size, info_size);
+  ASSERT_EQ(200U, total_memory);
+  ASSERT_EQ(16U, backtrace_size);
+  ASSERT_TRUE(memcmp(expected_info.data(), info, overall_size) == 0);
+
+  debug_free_malloc_leak_info(info);
+
+  debug_free(pointer);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, get_malloc_leak_info_multi) {
+  Init("backtrace=16");
+
+  // Create the expected info buffer.
+  size_t individual_size = 2 * sizeof(size_t) + 16 * sizeof(uintptr_t);
+  std::vector<uint8_t> expected_info(individual_size * 3);
+  memset(expected_info.data(), 0, individual_size * 3);
+
+  InfoEntry* entry0 = reinterpret_cast<InfoEntry*>(expected_info.data());
+  InfoEntry* entry1 = reinterpret_cast<InfoEntry*>(
+      reinterpret_cast<uintptr_t>(entry0) + individual_size);
+  InfoEntry* entry2 = reinterpret_cast<InfoEntry*>(
+      reinterpret_cast<uintptr_t>(entry1) + individual_size);
+
+  // These values will be in the reverse order that we create.
+  entry2->size = 500;
+  entry2->num_frames = 4;
+  entry2->frames[0] = 0xf;
+  entry2->frames[1] = 0xe;
+  entry2->frames[2] = 0xd;
+  entry2->frames[3] = 0xc;
+
+  backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd, 0xc});
+
+  uint8_t* pointers[3];
+
+  pointers[0] = reinterpret_cast<uint8_t*>(debug_malloc(entry2->size));
+  ASSERT_TRUE(pointers[0] != nullptr);
+  memset(pointers[0], 0, entry2->size);
+
+  entry1->size = 4100;
+  entry1->num_frames = 16;
+  for (size_t i = 0; i < 16; i++) {
+    entry1->frames[i] = 0xbc000 + i;
+  }
+
+  backtrace_fake_add(
+      std::vector<uintptr_t> {0xbc000, 0xbc001, 0xbc002, 0xbc003, 0xbc004, 0xbc005,
+                              0xbc006, 0xbc007, 0xbc008, 0xbc009, 0xbc00a, 0xbc00b,
+                              0xbc00c, 0xbc00d, 0xbc00e, 0xbc00f, 0xffff});
+
+  pointers[1] = reinterpret_cast<uint8_t*>(debug_malloc(entry1->size));
+  ASSERT_TRUE(pointers[1] != nullptr);
+  memset(pointers[1], 0, entry1->size);
+
+  entry0->size = 9000;
+  entry0->num_frames = 1;
+
+  entry0->frames[0] = 0x104;
+  backtrace_fake_add(std::vector<uintptr_t> {0x104});
+
+  pointers[2] = reinterpret_cast<uint8_t*>(debug_malloc(entry0->size));
+  ASSERT_TRUE(pointers[2] != nullptr);
+  memset(pointers[2], 0, entry0->size);
+
+  uint8_t* info;
+  size_t overall_size;
+  size_t info_size;
+  size_t total_memory;
+  size_t backtrace_size;
+
+  debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(individual_size * 3, overall_size);
+  ASSERT_EQ(individual_size, info_size);
+  ASSERT_EQ(500U + 4100U + 9000U, total_memory);
+  ASSERT_EQ(16U, backtrace_size);
+  ASSERT_TRUE(memcmp(expected_info.data(), info, overall_size) == 0);
+
+  debug_free_malloc_leak_info(info);
+
+  debug_free(pointers[0]);
+  debug_free(pointers[1]);
+  debug_free(pointers[2]);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, get_malloc_leak_info_multi_skip_empty_backtrace) {
+  Init("backtrace=16");
+
+  // Create the expected info buffer.
+  size_t individual_size = 2 * sizeof(size_t) + 16 * sizeof(uintptr_t);
+  std::vector<uint8_t> expected_info(individual_size * 2);
+  memset(expected_info.data(), 0, individual_size * 2);
+
+  InfoEntry* entry0 = reinterpret_cast<InfoEntry*>(expected_info.data());
+  InfoEntry* entry1 = reinterpret_cast<InfoEntry*>(
+      reinterpret_cast<uintptr_t>(entry0) + individual_size);
+
+  // These values will be in the reverse order that we create.
+  entry1->size = 500;
+  entry1->num_frames = 4;
+  entry1->frames[0] = 0xf;
+  entry1->frames[1] = 0xe;
+  entry1->frames[2] = 0xd;
+  entry1->frames[3] = 0xc;
+
+  backtrace_fake_add(std::vector<uintptr_t> {0xf, 0xe, 0xd, 0xc});
+
+  uint8_t* pointers[3];
+
+  pointers[0] = reinterpret_cast<uint8_t*>(debug_malloc(entry1->size));
+  ASSERT_TRUE(pointers[0] != nullptr);
+  memset(pointers[0], 0, entry1->size);
+
+  entry0->size = 4100;
+  entry0->num_frames = 16;
+  for (size_t i = 0; i < 16; i++) {
+    entry0->frames[i] = 0xbc000 + i;
+  }
+
+  backtrace_fake_add(
+      std::vector<uintptr_t> {0xbc000, 0xbc001, 0xbc002, 0xbc003, 0xbc004, 0xbc005,
+                              0xbc006, 0xbc007, 0xbc008, 0xbc009, 0xbc00a, 0xbc00b,
+                              0xbc00c, 0xbc00d, 0xbc00e, 0xbc00f, 0xffff});
+
+  pointers[1] = reinterpret_cast<uint8_t*>(debug_malloc(entry0->size));
+  ASSERT_TRUE(pointers[1] != nullptr);
+  memset(pointers[1], 0, entry0->size);
+
+  pointers[2] = reinterpret_cast<uint8_t*>(debug_malloc(10000));
+  ASSERT_TRUE(pointers[2] != nullptr);
+  memset(pointers[2], 0, 10000);
+
+  uint8_t* info;
+  size_t overall_size;
+  size_t info_size;
+  size_t total_memory;
+  size_t backtrace_size;
+
+  debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(individual_size * 2, overall_size);
+  ASSERT_EQ(individual_size, info_size);
+  ASSERT_EQ(500U + 4100U, total_memory);
+  ASSERT_EQ(16U, backtrace_size);
+  ASSERT_TRUE(memcmp(expected_info.data(), info, overall_size) == 0);
+
+  debug_free_malloc_leak_info(info);
+
+  debug_free(pointers[0]);
+  debug_free(pointers[1]);
+  debug_free(pointers[2]);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, realloc_usable_size) {
+  Init("front_guard");
+
+  // Verify that if the usable size > size of alloc, that realloc
+  // copies the bytes in the usable size not just the size.
+  // This assumes that an allocation of size 1 returns usable size > 1.
+  // If this isn't true, this test is not going to do anything.
+  uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(1));
+  ASSERT_TRUE(pointer != nullptr);
+  size_t usable_size = debug_malloc_usable_size(pointer);
+  memset(pointer, 0xaa, usable_size);
+  pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, usable_size + 10));
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_LE(usable_size + 10, debug_malloc_usable_size(pointer));
+  for (size_t i = 0; i < usable_size; i++) {
+    ASSERT_EQ(0xaa, pointer[i]) << "Failed compare at byte " << i;
+  }
+  debug_free(pointer);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, backtrace_enable_on_signal) {
+  Init("backtrace_enable_on_signal=20");
+
+  size_t individual_size = 2 * sizeof(size_t) + 20 * sizeof(uintptr_t);
+
+  backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
+  backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200, 0x300, 0x400});
+  backtrace_fake_add(std::vector<uintptr_t> {0x500, 0xa00, 0xb00});
+
+  // First allocation should not actually attempt to get the backtrace.
+  void* pointer = debug_malloc(10);
+  ASSERT_TRUE(pointer != nullptr);
+
+  uint8_t* info;
+  size_t overall_size;
+  size_t info_size;
+  size_t total_memory;
+  size_t backtrace_size;
+
+  debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
+  ASSERT_TRUE(info == nullptr);
+  ASSERT_EQ(0U, overall_size);
+  ASSERT_EQ(0U, info_size);
+  ASSERT_EQ(0U, total_memory);
+  ASSERT_EQ(0U, backtrace_size);
+  debug_free(pointer);
+
+  debug_free_malloc_leak_info(info);
+
+  // Send the signal to enable.
+  ASSERT_TRUE(kill(getpid(), SIGRTMIN + 10) == 0);
+  sleep(1);
+
+  pointer = debug_malloc(100);
+  ASSERT_TRUE(pointer != nullptr);
+
+  debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
+  ASSERT_TRUE(info != nullptr);
+  ASSERT_EQ(individual_size, overall_size);
+  ASSERT_EQ(individual_size, info_size);
+  ASSERT_EQ(100U, total_memory);
+  ASSERT_EQ(20U, backtrace_size);
+  uintptr_t* ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t)]);
+  ASSERT_EQ(0xbc000U, ips[0]);
+  ASSERT_EQ(0xecd00U, ips[1]);
+  ASSERT_EQ(0x12000U, ips[2]);
+  for (size_t i = 3; i < 20; i++) {
+    ASSERT_EQ(0U, ips[i]);
+  }
+
+  debug_free(pointer);
+
+  debug_free_malloc_leak_info(info);
+
+  // Send the signal to disable.
+  ASSERT_TRUE(kill(getpid(), SIGRTMIN + 10) == 0);
+  sleep(1);
+
+  pointer = debug_malloc(200);
+  ASSERT_TRUE(pointer != nullptr);
+
+  debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
+  ASSERT_TRUE(info == nullptr);
+  ASSERT_EQ(0U, overall_size);
+  ASSERT_EQ(0U, info_size);
+  ASSERT_EQ(0U, total_memory);
+  ASSERT_EQ(0U, backtrace_size);
+
+  debug_free(pointer);
+
+  debug_free_malloc_leak_info(info);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  std::string expected_log = android::base::StringPrintf(
+      "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to enable backtracing.\n",
+      SIGRTMIN + 10, getpid());
+  ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, overflow) {
+  Init("guard fill_on_free");
+
+  void* pointer = debug_malloc(SIZE_MAX);
+  ASSERT_TRUE(pointer == nullptr);
+  ASSERT_EQ(ENOMEM, errno);
+
+  pointer = debug_calloc(1, SIZE_MAX);
+  ASSERT_TRUE(pointer == nullptr);
+  ASSERT_EQ(ENOMEM, errno);
+
+  pointer = debug_calloc(SIZE_MAX, 1);
+  ASSERT_TRUE(pointer == nullptr);
+  ASSERT_EQ(ENOMEM, errno);
+
+  pointer = debug_calloc(SIZE_MAX/100, 100);
+  ASSERT_TRUE(pointer == nullptr);
+  ASSERT_EQ(ENOMEM, errno);
+
+  pointer = debug_calloc(100, SIZE_MAX/100);
+  ASSERT_TRUE(pointer == nullptr);
+  ASSERT_EQ(ENOMEM, errno);
+
+  pointer = debug_realloc(nullptr, SIZE_MAX);
+  ASSERT_TRUE(pointer == nullptr);
+  ASSERT_EQ(ENOMEM, errno);
+
+  pointer = debug_malloc(100);
+  ASSERT_TRUE(pointer != nullptr);
+  memset(pointer, 0xd0, 100);
+
+  void* realloc_pointer = debug_realloc(pointer, SIZE_MAX);
+  ASSERT_TRUE(realloc_pointer == nullptr);
+  // Verify the pointer was not freed.
+  for (size_t i = 0; i < 100; i++) {
+    ASSERT_EQ(0xd0, reinterpret_cast<uint8_t*>(pointer)[i]) << "Failed checking byte " << i;
+  }
+  debug_free(pointer);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+static void VerifyZygoteSet(size_t memory_bytes) {
+  size_t expected_info_size = 2 * sizeof(size_t) + 16 * sizeof(uintptr_t);
+  std::vector<uint8_t> expected_info(expected_info_size);
+  memset(expected_info.data(), 0, expected_info_size);
+  InfoEntry* entry = reinterpret_cast<InfoEntry*>(expected_info.data());
+  entry->size = memory_bytes | (1U << 31);
+  entry->num_frames = 1;
+  entry->frames[0] = 0x1;
+
+  uint8_t* info;
+  size_t overall_size;
+  size_t info_size;
+  size_t total_memory;
+  size_t backtrace_size;
+
+  debug_get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size);
+  ASSERT_EQ(expected_info_size, overall_size);
+  ASSERT_EQ(expected_info_size, info_size);
+  ASSERT_EQ(memory_bytes, total_memory);
+  ASSERT_EQ(16U, backtrace_size);
+  ASSERT_TRUE(memcmp(info, expected_info.data(), expected_info_size) == 0);
+
+  debug_free_malloc_leak_info(info);
+}
+
+TEST_F(MallocDebugTest, zygote_set) {
+  // Set all of the options.
+  Init("guard fill backtrace leak_track free_track=2");
+
+  zygote = 1;
+
+  backtrace_fake_add(std::vector<uintptr_t> {0x1});
+
+  void* pointer = debug_malloc(100);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_EQ(100U, debug_malloc_usable_size(pointer));
+  memset(pointer, 0, 100);
+  VerifyZygoteSet(100);
+  debug_free(pointer);
+
+  backtrace_fake_add(std::vector<uintptr_t> {0x1});
+  pointer = debug_calloc(10, 20);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_EQ(200U, debug_malloc_usable_size(pointer));
+  VerifyZygoteSet(200);
+  debug_free(pointer);
+
+  backtrace_fake_add(std::vector<uintptr_t> {0x1});
+  pointer = debug_memalign(128, 300);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_EQ(300U, debug_malloc_usable_size(pointer));
+  memset(pointer, 0, 300);
+  VerifyZygoteSet(300);
+  debug_free(pointer);
+
+  backtrace_fake_add(std::vector<uintptr_t> {0x1});
+  pointer = debug_malloc(500);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_EQ(500U, debug_malloc_usable_size(pointer));
+  memset(pointer, 0, 500);
+  VerifyZygoteSet(500);
+
+  backtrace_fake_add(std::vector<uintptr_t> {0x1});
+  pointer = debug_realloc(pointer, 300);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_EQ(300U, debug_malloc_usable_size(pointer));
+  VerifyZygoteSet(300);
+  debug_free(pointer);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, max_size) {
+  Init("guard");
+
+  void* pointer = debug_malloc(1U << 31);
+  ASSERT_TRUE(pointer == nullptr);
+
+  pointer = debug_calloc(1, 1U << 31);
+  ASSERT_TRUE(pointer == nullptr);
+
+  pointer = debug_calloc(1U << 31, 1);
+  ASSERT_TRUE(pointer == nullptr);
+
+  pointer = debug_memalign(16, 1U << 31);
+  ASSERT_TRUE(pointer == nullptr);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, debug_mallinfo) {
+  Init("guard");
+
+  void* pointer = debug_malloc(150);
+  ASSERT_TRUE(pointer != nullptr);
+
+  struct mallinfo mi = debug_mallinfo();
+  EXPECT_NE(0U, mi.uordblks);
+
+  debug_free(pointer);
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, debug_posix_memalign) {
+  Init("guard");
+
+  void* pointer;
+  ASSERT_EQ(0, debug_posix_memalign(&pointer, 32, 300));
+  ASSERT_TRUE(pointer != nullptr);
+  debug_free(pointer);
+
+  ASSERT_EQ(EINVAL, debug_posix_memalign(&pointer, 11, 300));
+
+  ASSERT_EQ(ENOMEM, debug_posix_memalign(&pointer, 16, SIZE_MAX));
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
+TEST_F(MallocDebugTest, debug_pvalloc) {
+  Init("guard");
+
+  size_t pagesize = getpagesize();
+  void* pointer = debug_pvalloc(1);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_EQ(pagesize, debug_malloc_usable_size(pointer));
+  uintptr_t value = reinterpret_cast<uintptr_t>(pointer) & (pagesize - 1);
+  ASSERT_EQ(0U, value);
+  debug_free(pointer);
+}
+
+TEST_F(MallocDebugTest, debug_valloc) {
+  Init("guard");
+
+  size_t pagesize = getpagesize();
+  void* pointer = debug_valloc(100);
+  ASSERT_TRUE(pointer != nullptr);
+  ASSERT_EQ(100U, debug_malloc_usable_size(pointer));
+  uintptr_t value = reinterpret_cast<uintptr_t>(pointer) & (pagesize - 1);
+  ASSERT_EQ(0U, value);
+  debug_free(pointer);
+}
+#endif
diff --git a/libc/malloc_debug/tests/property_fake.cpp b/libc/malloc_debug/tests/property_fake.cpp
new file mode 100644
index 0000000..d9f0ad8
--- /dev/null
+++ b/libc/malloc_debug/tests/property_fake.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#include <string>
+#include <unordered_map>
+
+#include <sys/system_properties.h>
+
+std::unordered_map<std::string, std::string> g_properties;
+
+extern "C" int property_set(const char* name, const char* value) {
+  if (g_properties.count(name) != 0) {
+    g_properties.erase(name);
+  }
+  g_properties[name] = value;
+  return 0;
+}
+
+extern "C" int property_get(const char* key, char* value, const char* default_value) {
+  if (g_properties.count(key) == 0) {
+    if (default_value == nullptr) {
+      return 0;
+    }
+    strncpy(value, default_value, PROP_VALUE_MAX-1);
+  } else {
+    strncpy(value, g_properties[key].c_str(), PROP_VALUE_MAX-1);
+  }
+  value[PROP_VALUE_MAX-1] = '\0';
+  return strlen(value);
+}
+
+extern "C" int __system_property_get(const char* key, char* value) {
+  if (g_properties.count(key) == 0) {
+    return 0;
+  } else {
+    strncpy(value, g_properties[key].c_str(), PROP_VALUE_MAX-1);
+  }
+  value[PROP_VALUE_MAX-1] = '\0';
+  return strlen(value);
+}
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index a671d77..c802e3a 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -36,7 +36,7 @@
 struct libc_globals {
   vdso_entry vdso[VDSO_END];
   long setjmp_cookie;
-  MallocDebug malloc_dispatch;
+  MallocDispatch malloc_dispatch;
 };
 
 __LIBC_HIDDEN__ extern WriteProtected<libc_globals> __libc_globals;
diff --git a/libc/private/bionic_malloc_dispatch.h b/libc/private/bionic_malloc_dispatch.h
index 34fb898..5dcd37a 100644
--- a/libc/private/bionic_malloc_dispatch.h
+++ b/libc/private/bionic_malloc_dispatch.h
@@ -30,38 +30,36 @@
 #define _PRIVATE_BIONIC_MALLOC_DISPATCH_H
 
 #include <stddef.h>
-#include "private/bionic_config.h"
+#include <private/bionic_config.h>
 
-/* Entry in malloc dispatch table. */
-typedef void* (*MallocDebugCalloc)(size_t, size_t);
-typedef void (*MallocDebugFree)(void*);
-typedef struct mallinfo (*MallocDebugMallinfo)();
-typedef void* (*MallocDebugMalloc)(size_t);
-typedef size_t (*MallocDebugMallocUsableSize)(const void*);
-typedef void* (*MallocDebugMemalign)(size_t, size_t);
-typedef int (*MallocDebugPosixMemalign)(void**, size_t, size_t);
+// Entry in malloc dispatch table.
+typedef void* (*MallocCalloc)(size_t, size_t);
+typedef void (*MallocFree)(void*);
+typedef struct mallinfo (*MallocMallinfo)();
+typedef void* (*MallocMalloc)(size_t);
+typedef size_t (*MallocMallocUsableSize)(const void*);
+typedef void* (*MallocMemalign)(size_t, size_t);
+typedef int (*MallocPosixMemalign)(void**, size_t, size_t);
+typedef void* (*MallocRealloc)(void*, size_t);
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-typedef void* (*MallocDebugPvalloc)(size_t);
-#endif
-typedef void* (*MallocDebugRealloc)(void*, size_t);
-#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-typedef void* (*MallocDebugValloc)(size_t);
+typedef void* (*MallocPvalloc)(size_t);
+typedef void* (*MallocValloc)(size_t);
 #endif
 
-struct MallocDebug {
-  MallocDebugCalloc calloc;
-  MallocDebugFree free;
-  MallocDebugMallinfo mallinfo;
-  MallocDebugMalloc malloc;
-  MallocDebugMallocUsableSize malloc_usable_size;
-  MallocDebugMemalign memalign;
-  MallocDebugPosixMemalign posix_memalign;
+struct MallocDispatch {
+  MallocCalloc calloc;
+  MallocFree free;
+  MallocMallinfo mallinfo;
+  MallocMalloc malloc;
+  MallocMallocUsableSize malloc_usable_size;
+  MallocMemalign memalign;
+  MallocPosixMemalign posix_memalign;
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-  MallocDebugPvalloc pvalloc;
+  MallocPvalloc pvalloc;
 #endif
-  MallocDebugRealloc realloc;
+  MallocRealloc realloc;
 #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
-  MallocDebugValloc valloc;
+  MallocValloc valloc;
 #endif
 } __attribute__((aligned(32)));