Merge "Make sem_wait able to return errno EINTR for sdk > 23."
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 734d86b..ab48fb8 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..4c2c7f3
--- /dev/null
+++ b/libc/malloc_debug/Android.mk
@@ -0,0 +1,76 @@
+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
+
+# -Wno-error=format-zero-length needed for gcc to compile.
+LOCAL_CFLAGS := \
+ -Wall \
+ -Werror \
+ -fno-stack-protector \
+ -Wno-error=format-zero-length \
+
+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..76901e2
--- /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 {
+ Feature(std::string name, size_t default_value, uint64_t option, size_t* value,
+ bool* config, bool combo_option)
+ : name(name), default_value(default_value), option(option), value(value),
+ config(config), combo_option(combo_option) {}
+ 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[] = {
+ Feature("guard", 32, 0, nullptr, nullptr, true),
+ // Enable front guard. Value is the size of the guard.
+ Feature("front_guard", 32, FRONT_GUARD, &this->front_guard_bytes, nullptr, true),
+ // Enable end guard. Value is the size of the guard.
+ Feature("rear_guard", 32, REAR_GUARD, &this->rear_guard_bytes, nullptr, true),
+
+ // Enable logging the backtrace on allocation. Value is the total
+ // number of frames to log.
+ Feature("backtrace", 16, BACKTRACE | TRACK_ALLOCS, &this->backtrace_frames,
+ &this->backtrace_enabled, false),
+ // Enable gathering backtrace values on a signal.
+ Feature("backtrace_enable_on_signal", 16, BACKTRACE | TRACK_ALLOCS, &this->backtrace_frames,
+ &this->backtrace_enable_on_signal, false),
+
+ Feature("fill", SIZE_MAX, 0, nullptr, nullptr, true),
+ // Fill the allocation with an arbitrary pattern on allocation.
+ // Value is the number of bytes of the allocation to fill
+ // (default entire allocation).
+ Feature("fill_on_alloc", SIZE_MAX, FILL_ON_ALLOC, &this->fill_on_alloc_bytes,
+ nullptr, true),
+ // Fill the allocation with an arbitrary pattern on free.
+ // Value is the number of bytes of the allocation to fill
+ // (default entire allocation).
+ Feature("fill_on_free", SIZE_MAX, FILL_ON_FREE, &this->fill_on_free_bytes, nullptr, true),
+
+ // Expand the size of every alloc by this number bytes. Value is
+ // the total number of bytes to expand every allocation by.
+ Feature ("expand_alloc", 16, EXPAND_ALLOC, &this->expand_alloc_bytes, nullptr, false),
+
+ // 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.
+ Feature("free_track", 100, FREE_TRACK | FILL_ON_FREE, &this->free_track_allocations,
+ nullptr, false),
+
+ // Enable printing leaked allocations.
+ Feature("leak_track", 0, LEAK_TRACK | TRACK_ALLOCS, nullptr, nullptr, false),
+ };
+
+ // 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)));