Malloc debug rewrite.
The major components of the rewrite:
- Completely remove the qemu shared library code. Nobody was using it
and it appears to have broken at some point.
- Adds the ability to enable/disable different options independently.
- Adds a new option that can enable the backtrace on alloc/free when
a process gets a specific signal.
- Adds a new way to enable malloc debug. If a special property is
set, and the process has an environment variable set, then debug
malloc will be enabled. This allows something that might be
a derivative of app_process to be started with an environment variable
being enabled.
- get_malloc_leak_info() used to return one element for each pointer that
had the exact same backtrace. The new version returns information for
every one of the pointers with same backtrace. It turns out ddms already
automatically coalesces these, so the old method simply hid the fact
that there where multiple pointers with the same amount of backtrace.
- Moved all of the malloc debug specific code into the library.
Nothing related to the malloc debug data structures remains in libc.
- Removed the calls to the debug malloc cleanup routine. Instead, I
added an atexit call with the debug malloc cleanup routine. This gets
around most problems related to the timing of doing the cleanup.
The new properties and environment variables:
libc.debug.malloc.options
Set by option name (such as "backtrace"). Setting this to a bad value
will cause a usage statement to be printed to the log.
libc.debug.malloc.program
Same as before. If this is set, then only the program named will
be launched with malloc debug enabled. This is not a complete match,
but if any part of the property is in the program name, malloc debug is
enabled.
libc.debug.malloc.env_enabled
If set, then malloc debug is only enabled if the running process has the
environment variable LIBC_DEBUG_MALLOC_ENABLE set.
Bug: 19145921
Change-Id: I7b0e58cc85cc6d4118173fe1f8627a391b64c0d7
diff --git a/libc/malloc_debug/Android.mk b/libc/malloc_debug/Android.mk
new file mode 100644
index 0000000..f0f4056
--- /dev/null
+++ b/libc/malloc_debug/Android.mk
@@ -0,0 +1,74 @@
+LOCAL_PATH := $(call my-dir)
+
+libc_malloc_debug_src_files := \
+ BacktraceData.cpp \
+ Config.cpp \
+ DebugData.cpp \
+ debug_disable.cpp \
+ FreeTrackData.cpp \
+ GuardData.cpp \
+ malloc_debug.cpp \
+ TrackData.cpp \
+
+# ==============================================================
+# libc_malloc_debug.so
+# ==============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libc_malloc_debug
+
+LOCAL_SRC_FILES := \
+ $(libc_malloc_debug_src_files) \
+ backtrace.cpp \
+ MapData.cpp \
+
+LOCAL_CXX_STL := none
+
+# Only need this for arm since libc++ uses its own unwind code that
+# doesn't mix with the other default unwind code.
+LOCAL_STATIC_LIBRARIES_arm := libunwind_llvm
+
+LOCAL_STATIC_LIBRARIES += libc++abi libc++_static libc_logging
+LOCAL_LDFLAGS_32 := -Wl,--version-script,$(LOCAL_PATH)/exported32.map
+LOCAL_LDFLAGS_64 := -Wl,--version-script,$(LOCAL_PATH)/exported64.map
+LOCAL_ALLOW_UNDEFINED_SYMBOLS := true
+LOCAL_C_INCLUDES += bionic/libc
+
+LOCAL_SANITIZE := never
+LOCAL_NATIVE_COVERAGE := false
+
+LOCAL_CFLAGS := \
+ -Wall \
+ -Werror \
+ -fno-stack-protector \
+
+include $(BUILD_SHARED_LIBRARY)
+
+# ==============================================================
+# Unit Tests
+# ==============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := malloc_debug_unit_tests
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_SRC_FILES := \
+ tests/backtrace_fake.cpp \
+ tests/log_fake.cpp \
+ tests/libc_fake.cpp \
+ tests/property_fake.cpp \
+ tests/malloc_debug_config_tests.cpp \
+ tests/malloc_debug_unit_tests.cpp \
+ $(libc_malloc_debug_src_files) \
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/tests
+LOCAL_C_INCLUDES += bionic/libc
+
+LOCAL_SHARED_LIBRARIES := libbase
+
+LOCAL_CFLAGS := \
+ -Wall \
+ -Werror \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libc/malloc_debug/BacktraceData.cpp b/libc/malloc_debug/BacktraceData.cpp
new file mode 100644
index 0000000..9f39068
--- /dev/null
+++ b/libc/malloc_debug/BacktraceData.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <private/bionic_macros.h>
+
+#include "BacktraceData.h"
+#include "Config.h"
+#include "DebugData.h"
+#include "debug_log.h"
+#include "malloc_debug.h"
+
+BacktraceData::BacktraceData(const Config& config, size_t* offset) {
+ size_t hdr_len = sizeof(BacktraceHeader) + sizeof(uintptr_t) * config.backtrace_frames - 1;
+ alloc_offset_ = *offset;
+ *offset += BIONIC_ALIGN(hdr_len, sizeof(uintptr_t));
+ free_offset_ = *offset;
+ *offset += BIONIC_ALIGN(hdr_len, sizeof(uintptr_t));
+}
+
+static BacktraceData* g_backtrace_data = nullptr;
+
+static void EnableToggle(int, siginfo_t*, void*) {
+ if (g_backtrace_data->enabled()) {
+ g_backtrace_data->set_enabled(false);
+ } else {
+ g_backtrace_data->set_enabled(true);
+ }
+}
+
+bool BacktraceData::Initialize(const Config& config) {
+ enabled_ = config.backtrace_enabled;
+ if (config.backtrace_enable_on_signal) {
+ g_backtrace_data = this;
+
+ struct sigaction enable_act;
+ memset(&enable_act, 0, sizeof(enable_act));
+
+ enable_act.sa_sigaction = EnableToggle;
+ enable_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ sigemptyset(&enable_act.sa_mask);
+ if (sigaction(config.backtrace_signal, &enable_act, nullptr) != 0) {
+ error_log("Unable to set up backtrace signal enable function: %s", strerror(errno));
+ return false;
+ }
+ info_log("%s: Run: 'kill -%d %d' to enable backtracing.", getprogname(),
+ config.backtrace_signal, getpid());
+ }
+ return true;
+}
diff --git a/libc/malloc_debug/BacktraceData.h b/libc/malloc_debug/BacktraceData.h
new file mode 100644
index 0000000..10daec7
--- /dev/null
+++ b/libc/malloc_debug/BacktraceData.h
@@ -0,0 +1,61 @@
+/*
+ * 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_BACKTRACEDATA_H
+#define DEBUG_MALLOC_BACKTRACEDATA_H
+
+#include <stdint.h>
+
+#include <private/bionic_macros.h>
+
+// Forward declarations.
+struct Config;
+
+class BacktraceData {
+ public:
+ BacktraceData(const Config& config, size_t* offset);
+ virtual ~BacktraceData() = default;
+
+ bool Initialize(const Config& config);
+
+ inline size_t alloc_offset() { return alloc_offset_; }
+ inline size_t free_offset() { return free_offset_; }
+
+ bool enabled() { return enabled_; }
+ void set_enabled(bool enabled) { enabled_ = enabled; }
+
+ private:
+ size_t alloc_offset_ = 0;
+ size_t free_offset_ = 0;
+
+ volatile bool enabled_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(BacktraceData);
+};
+
+#endif // DEBUG_MALLOC_BACKTRACEDATA_H
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
new file mode 100644
index 0000000..e27076d
--- /dev/null
+++ b/libc/malloc_debug/Config.cpp
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/cdefs.h>
+
+#include <string>
+#include <vector>
+
+#include <sys/system_properties.h>
+
+#include <private/bionic_macros.h>
+
+#include "Config.h"
+#include "debug_log.h"
+
+struct Feature {
+ std::string name;
+ size_t default_value = 0;
+
+ uint64_t option = 0;
+ size_t* value = nullptr;
+ bool* config = nullptr;
+ // If set to true, then all of the options following are set on until
+ // for which the combo_option value is set.
+ bool combo_option = false;
+};
+
+class PropertyParser {
+ public:
+ PropertyParser(const char* property) : cur_(property) {}
+
+ bool Get(std::string* property, size_t* value, bool* value_set);
+
+ bool Done() { return done_; }
+
+ void LogUsage();
+
+ static constexpr uint8_t DEFAULT_FILL_ALLOC_VALUE = 0xeb;
+ static constexpr uint8_t DEFAULT_FILL_FREE_VALUE = 0xef;
+
+ static constexpr uint8_t DEFAULT_FRONT_GUARD_VALUE = 0xaa;
+ static constexpr uint8_t DEFAULT_REAR_GUARD_VALUE = 0xbb;
+
+ private:
+ const char* cur_ = nullptr;
+
+ bool done_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(PropertyParser);
+};
+
+bool PropertyParser::Get(std::string* property, size_t* value, bool* value_set) {
+ // Process each property name we can find.
+ while (isspace(*cur_))
+ ++cur_;
+
+ if (*cur_ == '\0') {
+ done_ = true;
+ return false;
+ }
+
+ const char* property_start = cur_;
+ while (!isspace(*cur_) && *cur_ != '=' && *cur_ != '\0')
+ ++cur_;
+
+ *property = std::string(property_start, cur_ - property_start);
+
+ // Skip any spaces after the name.
+ while (isspace(*cur_) && *cur_ != '=' && *cur_ != '\0')
+ ++cur_;
+
+ if (*cur_ == '=') {
+ ++cur_;
+ errno = 0;
+ *value_set = true;
+ char* end;
+ long read_value = strtol(cur_, const_cast<char**>(&end), 10);
+ if (errno != 0) {
+ error_log("%s: bad value for option '%s': %s", getprogname(), property->c_str(),
+ strerror(errno));
+ return false;
+ }
+ if (cur_ == end || (!isspace(*end) && *end != '\0')) {
+ if (cur_ == end) {
+ error_log("%s: bad value for option '%s'", getprogname(), property->c_str());
+ } else {
+ error_log("%s: bad value for option '%s', non space found after option: %s",
+ getprogname(), property->c_str(), end);
+ }
+ return false;
+ } else if (read_value <= 0) {
+ error_log("%s: bad value for option '%s', value must be > 0: %ld",
+ getprogname(), property->c_str(), read_value);
+ return false;
+ }
+ *value = static_cast<size_t>(read_value);
+ cur_ = end;
+ } else {
+ *value_set = false;
+ }
+ return true;
+}
+
+void PropertyParser::LogUsage() {
+ error_log("malloc debug options usage:");
+ error_log("");
+ error_log(" front_guard[=XX]");
+ error_log(" Enables a front guard on all allocations. If XX is set");
+ error_log(" it sets the number of bytes in the guard. The default is");
+ error_log(" 32 bytes.");
+ error_log("");
+ error_log(" rear_guard[=XX]");
+ error_log(" Enables a rear guard on all allocations. If XX is set");
+ error_log(" it sets the number of bytes in the guard. The default is");
+ error_log(" 32 bytes.");
+ error_log("");
+ error_log(" guard[=XX]");
+ error_log(" Enables both a front guard and a rear guard on all allocations.");
+ error_log(" If XX is set it sets the number of bytes in both guards.");
+ error_log(" The default is 32 bytes.");
+ error_log("");
+ error_log(" backtrace[=XX]");
+ error_log(" Enable capturing the backtrace at the point of allocation.");
+ error_log(" If XX is set it sets the number of backtrace frames.");
+ error_log(" The default is 16 frames.");
+ error_log("");
+ error_log(" backtrace_enable_on_signal[=XX]");
+ error_log(" Enable capturing the backtrace at the point of allocation.");
+ error_log(" The backtrace capture is not enabled until the process");
+ error_log(" receives a signal. If XX is set it sets the number of backtrace");
+ error_log(" frames. The default is 16 frames.");
+ error_log("");
+ error_log(" fill_on_alloc[=XX]");
+ error_log(" On first allocation, fill with the value 0x%02x.", DEFAULT_FILL_ALLOC_VALUE);
+ error_log(" If XX is set it will only fill up to XX bytes of the");
+ error_log(" allocation. The default is to fill the entire allocation.");
+ error_log("");
+ error_log(" fill_on_free[=XX]");
+ error_log(" On free, fill with the value 0x%02x. If XX is set it will",
+ DEFAULT_FILL_FREE_VALUE);
+ error_log(" only fill up to XX bytes of the allocation. The default is to");
+ error_log(" fill the entire allocation.");
+ error_log("");
+ error_log(" fill[=XX]");
+ error_log(" On both first allocation free, fill with the value 0x%02x on",
+ DEFAULT_FILL_ALLOC_VALUE);
+ error_log(" first allocation and the value 0x%02x. If XX is set, only fill",
+ DEFAULT_FILL_FREE_VALUE);
+ error_log(" up to XX bytes. The default is to fill the entire allocation.");
+ error_log("");
+ error_log(" expand_alloc[=XX]");
+ error_log(" Allocate an extra number of bytes for every allocation call.");
+ error_log(" If XX is set, that is the number of bytes to expand the");
+ error_log(" allocation by. The default is 16 bytes.");
+ error_log("");
+ error_log(" free_track[=XX]");
+ error_log(" When a pointer is freed, do not free the memory right away.");
+ error_log(" Instead, keep XX of these allocations around and then verify");
+ error_log(" that they have not been modified when the total number of freed");
+ error_log(" allocations exceeds the XX amount. When the program terminates,");
+ error_log(" the rest of these allocations are verified.");
+ error_log(" The default is to record 100 allocations.");
+ error_log("");
+ error_log(" leak_track");
+ error_log(" Enable the leak tracking of memory allocations.");
+}
+
+static bool SetFeature(const Feature& feature, size_t value, bool value_set) {
+ if (feature.config) {
+ *feature.config = true;
+ }
+ if (feature.value != nullptr) {
+ if (value_set) {
+ *feature.value = value;
+ } else {
+ *feature.value = feature.default_value;
+ }
+ } else if (value_set) {
+ error_log("%s: value set for option '%s' which does not take a value",
+ getprogname(), feature.name.c_str());
+ return false;
+ }
+ return true;
+}
+
+// This function is designed to be called once. A second call will not
+// reset all variables.
+bool Config::SetFromProperties() {
+ char property_str[PROP_VALUE_MAX];
+ memset(property_str, 0, sizeof(property_str));
+ if (!__system_property_get("libc.debug.malloc.options", property_str)) {
+ return false;
+ }
+
+ // Initialize a few default values.
+ fill_alloc_value = PropertyParser::DEFAULT_FILL_ALLOC_VALUE;
+ fill_free_value = PropertyParser::DEFAULT_FILL_FREE_VALUE;
+ front_guard_value = PropertyParser::DEFAULT_FRONT_GUARD_VALUE;
+ rear_guard_value = PropertyParser::DEFAULT_REAR_GUARD_VALUE;
+ backtrace_signal = SIGRTMIN + 10;
+
+ // Parse the options are of the format:
+ // option_name or option_name=XX
+
+ // Supported features:
+ const Feature features[] = {
+ { .name="guard", .default_value=32, .option=0, .combo_option=true },
+ // Enable front guard. Value is the size of the guard.
+ { .name="front_guard", .default_value=32, .option=FRONT_GUARD,
+ .value=&this->front_guard_bytes, .combo_option=true },
+ // Enable end guard. Value is the size of the guard.
+ { .name="rear_guard", .default_value=32, .option=REAR_GUARD,
+ .value=&this->rear_guard_bytes, .combo_option=true },
+
+ // Enable logging the backtrace on allocation. Value is the total
+ // number of frames to log.
+ { .name="backtrace", .default_value=16, .option=BACKTRACE | TRACK_ALLOCS,
+ .value=&this->backtrace_frames, .config=&this->backtrace_enabled },
+ // Enable gathering backtrace values on a signal.
+ { .name="backtrace_enable_on_signal", .default_value=16, .option=BACKTRACE | TRACK_ALLOCS,
+ .value=&this->backtrace_frames, .config=&this->backtrace_enable_on_signal },
+
+ { .name="fill", .default_value=SIZE_MAX, .option=0, .combo_option=true },
+ // Fill the allocation with an arbitrary pattern on allocation.
+ // Value is the number of bytes of the allocation to fill
+ // (default entire allocation).
+ { .name="fill_on_alloc", .default_value=SIZE_MAX, .option=FILL_ON_ALLOC,
+ .value=&this->fill_on_alloc_bytes, .combo_option=true },
+ // Fill the allocation with an arbitrary pattern on free.
+ // Value is the number of bytes of the allocation to fill
+ // (default entire allocation).
+ { .name="fill_on_free", .default_value=SIZE_MAX, .option=FILL_ON_FREE,
+ .value=&this->fill_on_free_bytes, .combo_option=true },
+
+ // Expand the size of every alloc by this number bytes. Value is
+ // the total number of bytes to expand every allocation by.
+ { .name="expand_alloc", .default_value=16, .option=EXPAND_ALLOC,
+ .value=&this->expand_alloc_bytes, },
+
+ // Keep track of the freed allocations and verify at a later date
+ // that they have not been used. Turning this on, also turns on
+ // fill on free.
+ { .name="free_track", .default_value=100, .option=FREE_TRACK | FILL_ON_FREE,
+ .value=&this->free_track_allocations, },
+
+ // Enable printing leaked allocations.
+ { .name="leak_track", .option=LEAK_TRACK | TRACK_ALLOCS },
+ };
+
+ // Process each property name we can find.
+ std::string property;
+ size_t value;
+ bool value_set;
+ PropertyParser parser(property_str);
+ bool found = false;
+ bool valid = true;
+ while (valid && parser.Get(&property, &value, &value_set)) {
+ for (size_t i = 0; i < sizeof(features)/sizeof(Feature); i++) {
+ if (property == features[i].name) {
+ if (features[i].option == 0 && features[i].combo_option) {
+ i++;
+ for (; i < sizeof(features)/sizeof(Feature) && features[i].combo_option; i++) {
+ if (!SetFeature(features[i], value, value_set)) {
+ valid = false;
+ break;
+ }
+ options |= features[i].option;
+ }
+ if (!valid) {
+ break;
+ }
+ } else {
+ if (!SetFeature(features[i], value, value_set)) {
+ valid = false;
+ break;
+ }
+ options |= features[i].option;
+ }
+ found = true;
+ break;
+ }
+ }
+ if (valid && !found) {
+ error_log("%s: unknown option %s", getprogname(), property.c_str());
+ valid = false;
+ break;
+ }
+ }
+
+ valid = valid && parser.Done();
+
+ if (valid) {
+ // It's necessary to align the front guard to sizeof(uintptr_t) to
+ // make sure that the header is aligned properly.
+ if (options & FRONT_GUARD) {
+ front_guard_bytes = BIONIC_ALIGN(front_guard_bytes, sizeof(uintptr_t));
+ }
+
+ // This situation can occur if the free_track option is specified and
+ // the fill_on_free option is not. In this case, indicate the whole
+ // allocation should be filled.
+ if ((options & FILL_ON_FREE) && fill_on_free_bytes == 0) {
+ fill_on_free_bytes = SIZE_MAX;
+ }
+ } else {
+ parser.LogUsage();
+ }
+
+ return valid;
+}
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
new file mode 100644
index 0000000..4b91e2b
--- /dev/null
+++ b/libc/malloc_debug/Config.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_DEBUG_CONFIG_H
+#define MALLOC_DEBUG_CONFIG_H
+
+#include <stdint.h>
+
+constexpr uint64_t FRONT_GUARD = 0x1;
+constexpr uint64_t REAR_GUARD = 0x2;
+constexpr uint64_t BACKTRACE = 0x4;
+constexpr uint64_t FILL_ON_ALLOC = 0x8;
+constexpr uint64_t FILL_ON_FREE = 0x10;
+constexpr uint64_t EXPAND_ALLOC = 0x20;
+constexpr uint64_t FREE_TRACK = 0x40;
+constexpr uint64_t TRACK_ALLOCS = 0x80;
+constexpr uint64_t LEAK_TRACK = 0x100;
+
+// If only one or more of these options is set, then no special header is needed.
+constexpr uint64_t NO_HEADER_OPTIONS = FILL_ON_ALLOC | FILL_ON_FREE | EXPAND_ALLOC;
+
+struct Config {
+ bool SetFromProperties();
+
+ size_t front_guard_bytes = 0;
+ size_t rear_guard_bytes = 0;
+
+ bool backtrace_enable_on_signal = false;
+ int backtrace_signal = 0;
+ bool backtrace_enabled = false;
+ size_t backtrace_frames = 0;
+
+ size_t fill_on_alloc_bytes = 0;
+ size_t fill_on_free_bytes = 0;
+
+ size_t expand_alloc_bytes = 0;
+
+ size_t free_track_allocations = 0;
+
+ uint64_t options = 0;
+ uint8_t fill_alloc_value;
+ uint8_t fill_free_value;
+ uint8_t front_guard_value;
+ uint8_t rear_guard_value;
+};
+
+#endif // MALLOC_DEBUG_CONFIG_H
diff --git a/libc/malloc_debug/DebugData.cpp b/libc/malloc_debug/DebugData.cpp
new file mode 100644
index 0000000..bf2a0f5
--- /dev/null
+++ b/libc/malloc_debug/DebugData.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+
+#include "BacktraceData.h"
+#include "Config.h"
+#include "DebugData.h"
+#include "debug_disable.h"
+#include "FreeTrackData.h"
+#include "GuardData.h"
+#include "malloc_debug.h"
+#include "TrackData.h"
+
+bool DebugData::Initialize() {
+ if (!config_.SetFromProperties()) {
+ return false;
+ }
+
+ // Check to see if the options that require a header are enabled.
+ if ((config_.options & ~(NO_HEADER_OPTIONS)) != 0) {
+ need_header_ = true;
+
+ // Initialize all of the static header offsets.
+ pointer_offset_ = BIONIC_ALIGN(sizeof(Header), sizeof(uintptr_t));
+
+ if (config_.options & BACKTRACE) {
+ backtrace.reset(new BacktraceData(config_, &pointer_offset_));
+ if (!backtrace->Initialize(config_)) {
+ return false;
+ }
+ }
+
+ if (config_.options & FRONT_GUARD) {
+ front_guard.reset(new FrontGuardData(config_, &pointer_offset_));
+ }
+
+ extra_bytes_ = pointer_offset_;
+
+ // Initialize all of the non-header data.
+ if (config_.options & REAR_GUARD) {
+ rear_guard.reset(new RearGuardData(config_));
+ extra_bytes_ += config_.rear_guard_bytes;
+ }
+
+ if (config_.options & FREE_TRACK) {
+ free_track.reset(new FreeTrackData(config_));
+ }
+
+ if (config_.options & TRACK_ALLOCS) {
+ track.reset(new TrackData());
+ }
+ }
+
+ if (config_.options & EXPAND_ALLOC) {
+ extra_bytes_ += config_.expand_alloc_bytes;
+ }
+ return true;
+}
diff --git a/libc/malloc_debug/DebugData.h b/libc/malloc_debug/DebugData.h
new file mode 100644
index 0000000..e023c51
--- /dev/null
+++ b/libc/malloc_debug/DebugData.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef DEBUG_MALLOC_DEBUGDATA_H
+#define DEBUG_MALLOC_DEBUGDATA_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include <private/bionic_macros.h>
+
+#include "BacktraceData.h"
+#include "Config.h"
+#include "FreeTrackData.h"
+#include "GuardData.h"
+#include "malloc_debug.h"
+#include "TrackData.h"
+
+class DebugData {
+ public:
+ DebugData() = default;
+ ~DebugData() = default;
+
+ bool Initialize();
+
+ static bool Disabled();
+
+ inline void* GetPointer(const Header* header) {
+ uintptr_t value = reinterpret_cast<uintptr_t>(header);
+ return reinterpret_cast<void*>(value + pointer_offset_);
+ }
+
+ Header* GetHeader(const void* pointer) {
+ uintptr_t value = reinterpret_cast<uintptr_t>(pointer);
+ return reinterpret_cast<Header*>(value - pointer_offset_);
+ }
+
+ BacktraceHeader* GetAllocBacktrace(const Header* header) {
+ uintptr_t value = reinterpret_cast<uintptr_t>(header);
+ return reinterpret_cast<BacktraceHeader*>(value + backtrace->alloc_offset());
+ }
+
+ BacktraceHeader* GetFreeBacktrace(const Header* header) {
+ uintptr_t value = reinterpret_cast<uintptr_t>(header);
+ return reinterpret_cast<BacktraceHeader*>(value + backtrace->free_offset());
+ }
+
+ uint8_t* GetFrontGuard(const Header* header) {
+ uintptr_t value = reinterpret_cast<uintptr_t>(header);
+ return reinterpret_cast<uint8_t*>(value + front_guard->offset());
+ }
+
+ uint8_t* GetRearGuard(const Header* header) {
+ uintptr_t value = reinterpret_cast<uintptr_t>(GetPointer(header));
+ return reinterpret_cast<uint8_t*>(value + header->real_size());
+ }
+
+ const Config& config() { return config_; }
+ size_t pointer_offset() { return pointer_offset_; }
+ bool need_header() { return need_header_; }
+ size_t extra_bytes() { return extra_bytes_; }
+
+ std::unique_ptr<BacktraceData> backtrace;
+ std::unique_ptr<TrackData> track;
+ std::unique_ptr<FrontGuardData> front_guard;
+ std::unique_ptr<RearGuardData> rear_guard;
+ std::unique_ptr<FreeTrackData> free_track;
+
+ private:
+ size_t extra_bytes_ = 0;
+
+ size_t pointer_offset_ = 0;
+ bool need_header_ = false;
+
+ Config config_;
+
+ DISALLOW_COPY_AND_ASSIGN(DebugData);
+};
+
+#endif // MALLOC_DEBUG_DEBUGDATA_H
diff --git a/libc/malloc_debug/FreeTrackData.cpp b/libc/malloc_debug/FreeTrackData.cpp
new file mode 100644
index 0000000..3466861
--- /dev/null
+++ b/libc/malloc_debug/FreeTrackData.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+
+#include "backtrace.h"
+#include "Config.h"
+#include "DebugData.h"
+#include "debug_disable.h"
+#include "debug_log.h"
+#include "FreeTrackData.h"
+#include "malloc_debug.h"
+
+FreeTrackData::FreeTrackData(const Config& config) {
+ cmp_mem_.resize(4096);
+ memset(cmp_mem_.data(), config.fill_free_value, cmp_mem_.size());
+}
+
+void FreeTrackData::LogFreeError(DebugData& debug, const Header* header,
+ const uint8_t* pointer) {
+ ScopedDisableDebugCalls disable;
+
+ error_log(LOG_DIVIDER);
+ error_log("+++ ALLOCATION %p USED AFTER FREE", pointer);
+ uint8_t fill_free_value = debug.config().fill_free_value;
+ for (size_t i = 0; i < header->usable_size; i++) {
+ if (pointer[i] != fill_free_value) {
+ error_log(" pointer[%zu] = 0x%02x (expected 0x%02x)", i, pointer[i], fill_free_value);
+ }
+ }
+ if (debug.config().options & BACKTRACE) {
+ BacktraceHeader* back_header = debug.GetFreeBacktrace(header);
+ if (back_header->num_frames > 0) {
+ error_log("Backtrace at time of free:");
+ backtrace_log(&back_header->frames[0], back_header->num_frames);
+ }
+ }
+ error_log(LOG_DIVIDER);
+}
+
+void FreeTrackData::VerifyAndFree(DebugData& debug, const Header* header,
+ const void* pointer) {
+ const uint8_t* memory = reinterpret_cast<const uint8_t*>(pointer);
+ size_t bytes = header->usable_size;
+ bytes = (bytes < debug.config().fill_on_free_bytes) ? bytes : debug.config().fill_on_free_bytes;
+ while (bytes > 0) {
+ size_t bytes_to_cmp = (bytes < cmp_mem_.size()) ? bytes : cmp_mem_.size();
+ if (memcmp(memory, cmp_mem_.data(), bytes_to_cmp) != 0) {
+ LogFreeError(debug, header, reinterpret_cast<const uint8_t*>(pointer));
+ break;
+ }
+ bytes -= bytes_to_cmp;
+ memory = &memory[bytes_to_cmp];
+ }
+ g_dispatch->free(header->orig_pointer);
+}
+
+void FreeTrackData::Add(DebugData& debug, const Header* header) {
+ // Make sure the stl calls below don't call the debug_XXX functions.
+ ScopedDisableDebugCalls disable;
+
+ pthread_mutex_lock(&mutex_);
+ if (list_.size() == debug.config().free_track_allocations) {
+ VerifyAndFree(debug, list_.back(), debug.GetPointer(list_.back()));
+ list_.pop_back();
+ }
+
+ list_.push_front(header);
+
+ pthread_mutex_unlock(&mutex_);
+}
+
+void FreeTrackData::VerifyAll(DebugData& debug) {
+ // Make sure the stl calls below don't call the debug_XXX functions.
+ ScopedDisableDebugCalls disable;
+
+ for (const auto& header : list_) {
+ VerifyAndFree(debug, header, debug.GetPointer(header));
+ }
+ list_.clear();
+}
diff --git a/libc/malloc_debug/FreeTrackData.h b/libc/malloc_debug/FreeTrackData.h
new file mode 100644
index 0000000..5888a0e
--- /dev/null
+++ b/libc/malloc_debug/FreeTrackData.h
@@ -0,0 +1,65 @@
+/*
+ * 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_FREETRACKDATA_H
+#define DEBUG_MALLOC_FREETRACKDATA_H
+
+#include <stdint.h>
+#include <pthread.h>
+
+#include <deque>
+#include <vector>
+
+#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/malloc_debug/MapData.h b/libc/malloc_debug/MapData.h
new file mode 100644
index 0000000..d5f315ab
--- /dev/null
+++ b/libc/malloc_debug/MapData.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef DEBUG_MALLOC_MAPDATA_H
+#define DEBUG_MALLOC_MAPDATA_H
+
+#include <sys/cdefs.h>
+
+#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 = false;
+ std::string name;
+};
+
+class MapData {
+ public:
+ static MapData* Create();
+ ~MapData();
+
+ 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/malloc_debug/backtrace.cpp b/libc/malloc_debug/backtrace.cpp
new file mode 100644
index 0000000..00290fd
--- /dev/null
+++ b/libc/malloc_debug/backtrace.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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 <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 "backtrace.h"
+#include "debug_disable.h"
+#include "debug_log.h"
+#include "MapData.h"
+
+#if defined(__LP64__)
+#define PAD_PTR "016" PRIxPTR
+#else
+#define PAD_PTR "08" PRIxPTR
+#endif
+
+typedef struct _Unwind_Context __unwind_context;
+
+extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
+
+static MapData* g_map_data = nullptr;
+static const MapEntry* g_current_code_map = nullptr;
+
+static _Unwind_Reason_Code find_current_map(__unwind_context* context, void*) {
+ uintptr_t ip = _Unwind_GetIP(context);
+
+ if (ip == 0) {
+ return _URC_END_OF_STACK;
+ }
+ g_current_code_map = g_map_data->find(ip);
+ return _URC_END_OF_STACK;
+}
+
+void backtrace_startup() {
+ ScopedDisableDebugCalls disable;
+
+ g_map_data = MapData::Create();
+ if (g_map_data) {
+ _Unwind_Backtrace(find_current_map, nullptr);
+ }
+}
+
+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 cur_frame = 0;
+
+ 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) {
+ stack_crawl_state_t* state = static_cast<stack_crawl_state_t*>(arg);
+
+ uintptr_t ip = _Unwind_GetIP(context);
+
+ // The instruction pointer is pointing at the instruction after the return
+ // call on all architectures.
+ // Modify the pc to point at the real function.
+ if (ip != 0) {
+#if defined(__arm__)
+ // If the ip is suspiciously low, do nothing to avoid a segfault trying
+ // to access this memory.
+ if (ip >= 4096) {
+ // Check bits [15:11] of the first halfword assuming the instruction
+ // is 32 bits long. If the bits are any of these values, then our
+ // assumption was correct:
+ // b11101
+ // b11110
+ // b11111
+ // Otherwise, this is a 16 bit instruction.
+ uint16_t value = (*reinterpret_cast<uint16_t*>(ip - 2)) >> 11;
+ if (value == 0x1f || value == 0x1e || value == 0x1d) {
+ ip -= 4;
+ } else {
+ ip -= 2;
+ }
+ }
+#elif defined(__aarch64__)
+ // All instructions are 4 bytes long, skip back one instruction.
+ ip -= 4;
+#elif defined(__i386__) || defined(__x86_64__)
+ // It's difficult to decode exactly where the previous instruction is,
+ // 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->cur_frame++] = ip;
+ return (state->cur_frame >= state->frame_count) ? _URC_END_OF_STACK : _URC_NO_REASON;
+}
+
+size_t backtrace_get(uintptr_t* frames, size_t frame_count) {
+ ScopedDisableDebugCalls disable;
+
+ stack_crawl_state_t state(frames, frame_count);
+ _Unwind_Backtrace(trace_function, &state);
+ return state.cur_frame;
+}
+
+void backtrace_log(uintptr_t* frames, size_t frame_count) {
+ ScopedDisableDebugCalls disable;
+
+ for (size_t frame_num = 0; frame_num < frame_count; frame_num++) {
+ uintptr_t offset = 0;
+ const char* symbol = nullptr;
+
+ Dl_info info;
+ 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 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 != nullptr) {
+ char* demangled_symbol = __cxa_demangle(symbol, nullptr, nullptr, nullptr);
+ const char* best_name = (demangled_symbol != nullptr) ? demangled_symbol : symbol;
+
+ error_log(" #%02zd pc %" PAD_PTR " %s (%s+%" PRIuPTR ")",
+ frame_num, rel_pc, soname, best_name, frames[frame_num] - offset);
+ free(demangled_symbol);
+ } else {
+ error_log(" #%02zd pc %" PAD_PTR " %s", frame_num, rel_pc, soname);
+ }
+ }
+}
diff --git a/libc/malloc_debug/backtrace.h b/libc/malloc_debug/backtrace.h
new file mode 100644
index 0000000..513a649
--- /dev/null
+++ b/libc/malloc_debug/backtrace.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_DEBUG_BACKTRACE_H
+#define MALLOC_DEBUG_BACKTRACE_H
+
+#include <stdint.h>
+#include <sys/cdefs.h>
+
+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
diff --git a/libc/malloc_debug/debug_disable.cpp b/libc/malloc_debug/debug_disable.cpp
new file mode 100644
index 0000000..af0264b
--- /dev/null
+++ b/libc/malloc_debug/debug_disable.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 "DebugData.h"
+#include "debug_disable.h"
+#include "debug_log.h"
+
+extern DebugData* g_debug;
+pthread_key_t g_disable_key;
+
+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/malloc_debug/debug_disable.h b/libc/malloc_debug/debug_disable.h
new file mode 100644
index 0000000..9edb4df
--- /dev/null
+++ b/libc/malloc_debug/debug_disable.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef MALLOC_DEBUG_DISABLE_H
+#define MALLOC_DEBUG_DISABLE_H
+
+#include <sys/cdefs.h>
+
+#include <private/bionic_macros.h>
+
+// =============================================================================
+// Used to disable the debug allocation calls.
+// =============================================================================
+bool DebugDisableInitialize();
+void DebugDisableFinalize();
+
+bool DebugCallsDisabled();
+void DebugDisableSet(bool disable);
+
+class ScopedDisableDebugCalls {
+ public:
+ ScopedDisableDebugCalls() : disabled_(DebugCallsDisabled()) {
+ if (!disabled_) {
+ DebugDisableSet(true);
+ }
+ }
+ ~ScopedDisableDebugCalls() {
+ if (!disabled_) {
+ DebugDisableSet(false);
+ }
+ }
+
+ private:
+ bool disabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDisableDebugCalls);
+};
+
+#endif // MALLOC_DEBUG_DISABLE_H
diff --git a/libc/malloc_debug/debug_log.h b/libc/malloc_debug/debug_log.h
new file mode 100644
index 0000000..67f584d
--- /dev/null
+++ b/libc/malloc_debug/debug_log.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef MALLOC_DEBUG_LOG_H
+#define MALLOC_DEBUG_LOG_H
+
+#include <private/libc_logging.h>
+
+// =============================================================================
+// 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_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);
+}