aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2018-03-07 13:38:48 -0800
committerChristopher Ferris <cferris@google.com>2018-04-03 13:13:53 -0700
commit8b70a0266d42297e9b38e6209588eb3621843e95 (patch)
treed73242853197da6f154149580fc3afa35f13d84d
parent622a36923ec2778dec0f195fec5cdc2056e65fca (diff)
downloadbionic-8b70a0266d42297e9b38e6209588eb3621843e95.tar.gz
Refactor malloc debug.
Changes - Refactor the code so that only guards require creating a special header for every pointer allocated. - Store only a single copy of every backtrace. This saves memory so that turning on the backtrace option doesn't result in 10X memory usage. - Added new option track_allocs that only verifies pointers are valid for free/malloc_usable_size/realloc. - Remove suffix from test names. - Add the TRACK_ALLOCS options to all guard options. - Add new option verify_pointers that is a lightweight way to verify pointers that are passed to allocation routines. - Do auto-formatting of the code. - Updated documentation for all of these changes. Bug: 74361929 Test: Ran unit tests. Test: Ran libmemunreachable unit tests. Test: Ran an app with backtrace enabled. Change-Id: I3246c48ae4f9811f64622d90d0a9b4d9d818702c (cherry picked from commit 4da2503d70dc4bc1444454876e3794b69227d90d)
-rw-r--r--libc/malloc_debug/Android.bp14
-rw-r--r--libc/malloc_debug/BacktraceData.cpp85
-rw-r--r--libc/malloc_debug/BacktraceData.h68
-rw-r--r--libc/malloc_debug/Config.cpp121
-rw-r--r--libc/malloc_debug/Config.h2
-rw-r--r--libc/malloc_debug/DebugData.cpp47
-rw-r--r--libc/malloc_debug/DebugData.h23
-rw-r--r--libc/malloc_debug/FreeTrackData.cpp129
-rw-r--r--libc/malloc_debug/FreeTrackData.h76
-rw-r--r--libc/malloc_debug/GuardData.cpp15
-rw-r--r--libc/malloc_debug/MapData.cpp12
-rw-r--r--libc/malloc_debug/MapData.h7
-rw-r--r--libc/malloc_debug/PointerData.cpp553
-rw-r--r--libc/malloc_debug/PointerData.h183
-rw-r--r--libc/malloc_debug/README.md47
-rw-r--r--libc/malloc_debug/RecordData.cpp37
-rw-r--r--libc/malloc_debug/RecordData.h2
-rw-r--r--libc/malloc_debug/TrackData.cpp183
-rw-r--r--libc/malloc_debug/TrackData.h76
-rw-r--r--libc/malloc_debug/backtrace.cpp17
-rw-r--r--libc/malloc_debug/debug_log.h15
-rw-r--r--libc/malloc_debug/malloc_debug.cpp463
-rw-r--r--libc/malloc_debug/malloc_debug.h7
-rw-r--r--libc/malloc_debug/tests/malloc_debug_config_tests.cpp52
-rw-r--r--libc/malloc_debug/tests/malloc_debug_unit_tests.cpp481
25 files changed, 1548 insertions, 1167 deletions
diff --git a/libc/malloc_debug/Android.bp b/libc/malloc_debug/Android.bp
index ede24315f..899987c42 100644
--- a/libc/malloc_debug/Android.bp
+++ b/libc/malloc_debug/Android.bp
@@ -42,15 +42,13 @@ cc_library {
name: "libc_malloc_debug",
srcs: [
- "BacktraceData.cpp",
"Config.cpp",
"DebugData.cpp",
"debug_disable.cpp",
- "FreeTrackData.cpp",
"GuardData.cpp",
"malloc_debug.cpp",
+ "PointerData.cpp",
"RecordData.cpp",
- "TrackData.cpp",
],
stl: "libc++_static",
@@ -91,6 +89,7 @@ cc_library {
"-Werror",
"-fno-stack-protector",
"-Wno-error=format-zero-length",
+ "-Wthread-safety",
],
}
@@ -98,16 +97,7 @@ cc_library {
// Unit Tests
// ==============================================================
cc_test {
-
name: "malloc_debug_unit_tests",
- multilib: {
- lib32: {
- suffix: "32",
- },
- lib64: {
- suffix: "64",
- },
- },
srcs: [
"tests/backtrace_fake.cpp",
diff --git a/libc/malloc_debug/BacktraceData.cpp b/libc/malloc_debug/BacktraceData.cpp
deleted file mode 100644
index 57d8f2afe..000000000
--- a/libc/malloc_debug/BacktraceData.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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"
-
-static void ToggleBacktraceEnable(int, siginfo_t*, void*) {
- g_debug->backtrace->ToggleBacktraceEnabled();
-}
-
-static void EnableDump(int, siginfo_t*, void*) {
- g_debug->backtrace->EnableDumping();
-}
-
-BacktraceData::BacktraceData(DebugData* debug_data, const Config& config, size_t* offset)
- : OptionData(debug_data) {
- size_t hdr_len = sizeof(BacktraceHeader) + sizeof(uintptr_t) * config.backtrace_frames();
- alloc_offset_ = *offset;
- *offset += __BIONIC_ALIGN(hdr_len, MINIMUM_ALIGNMENT_BYTES);
-}
-
-bool BacktraceData::Initialize(const Config& config) {
- enabled_ = config.backtrace_enabled();
- if (config.backtrace_enable_on_signal()) {
- struct sigaction64 enable_act = {};
- enable_act.sa_sigaction = ToggleBacktraceEnable;
- enable_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
- if (sigaction64(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());
- }
-
- struct sigaction64 act = {};
- act.sa_sigaction = EnableDump;
- act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
- if (sigaction64(config.backtrace_dump_signal(), &act, nullptr) != 0) {
- error_log("Unable to set up backtrace dump signal function: %s", strerror(errno));
- return false;
- }
- info_log("%s: Run: 'kill -%d %d' to dump the backtrace.", getprogname(),
- config.backtrace_dump_signal(), getpid());
-
- dump_ = false;
- return true;
-}
diff --git a/libc/malloc_debug/BacktraceData.h b/libc/malloc_debug/BacktraceData.h
deleted file mode 100644
index 3c153484d..000000000
--- a/libc/malloc_debug/BacktraceData.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <stdint.h>
-
-#include <atomic>
-
-#include <private/bionic_macros.h>
-
-#include "OptionData.h"
-
-// Forward declarations.
-class Config;
-
-class BacktraceData : public OptionData {
- public:
- BacktraceData(DebugData* debug_data, const Config& config, size_t* offset);
- virtual ~BacktraceData() = default;
-
- bool Initialize(const Config& config);
-
- inline size_t alloc_offset() { return alloc_offset_; }
-
- bool ShouldBacktrace() { return enabled_ == 1; }
- void ToggleBacktraceEnabled() { enabled_.fetch_xor(1); }
-
- void EnableDumping() { dump_ = true; }
- bool ShouldDumpAndReset() {
- bool expected = true;
- return dump_.compare_exchange_strong(expected, false);
- }
-
- private:
- size_t alloc_offset_ = 0;
-
- std::atomic_uint8_t enabled_;
-
- std::atomic_bool dump_;
-
- DISALLOW_COPY_AND_ASSIGN(BacktraceData);
-};
diff --git a/libc/malloc_debug/Config.cpp b/libc/malloc_debug/Config.cpp
index 3cecf9b57..2c94fe81f 100644
--- a/libc/malloc_debug/Config.cpp
+++ b/libc/malloc_debug/Config.cpp
@@ -69,65 +69,70 @@ static constexpr size_t MAX_RECORD_ALLOCS = 50000000;
static constexpr const char DEFAULT_RECORD_ALLOCS_FILE[] = "/data/local/tmp/record_allocs.txt";
const std::unordered_map<std::string, Config::OptionInfo> Config::kOptions = {
- {"guard",
- {FRONT_GUARD | REAR_GUARD, &Config::SetGuard},
+ {
+ "guard", {FRONT_GUARD | REAR_GUARD | TRACK_ALLOCS, &Config::SetGuard},
},
- {"front_guard",
- {FRONT_GUARD, &Config::SetFrontGuard},
+ {
+ "front_guard", {FRONT_GUARD | TRACK_ALLOCS, &Config::SetFrontGuard},
},
- {"rear_guard",
- {REAR_GUARD, &Config::SetRearGuard},
+ {
+ "rear_guard", {REAR_GUARD | TRACK_ALLOCS, &Config::SetRearGuard},
},
- {"backtrace",
- {BACKTRACE | TRACK_ALLOCS, &Config::SetBacktrace},
+ {
+ "backtrace", {BACKTRACE | TRACK_ALLOCS, &Config::SetBacktrace},
},
- {"backtrace_enable_on_signal",
- {BACKTRACE | TRACK_ALLOCS, &Config::SetBacktraceEnableOnSignal},
+ {
+ "backtrace_enable_on_signal",
+ {BACKTRACE | TRACK_ALLOCS, &Config::SetBacktraceEnableOnSignal},
},
- {"backtrace_dump_on_exit",
- {0, &Config::SetBacktraceDumpOnExit},
+ {
+ "backtrace_dump_on_exit", {0, &Config::SetBacktraceDumpOnExit},
},
- {"backtrace_dump_prefix",
- {0, &Config::SetBacktraceDumpPrefix},
+ {
+ "backtrace_dump_prefix", {0, &Config::SetBacktraceDumpPrefix},
},
- {"fill",
- {FILL_ON_ALLOC | FILL_ON_FREE, &Config::SetFill},
+ {
+ "fill", {FILL_ON_ALLOC | FILL_ON_FREE, &Config::SetFill},
},
- {"fill_on_alloc",
- {FILL_ON_ALLOC, &Config::SetFillOnAlloc},
+ {
+ "fill_on_alloc", {FILL_ON_ALLOC, &Config::SetFillOnAlloc},
},
- {"fill_on_free",
- {FILL_ON_FREE, &Config::SetFillOnFree},
+ {
+ "fill_on_free", {FILL_ON_FREE, &Config::SetFillOnFree},
},
- {"expand_alloc",
- {EXPAND_ALLOC, &Config::SetExpandAlloc},
+ {
+ "expand_alloc", {EXPAND_ALLOC, &Config::SetExpandAlloc},
},
- {"free_track",
- {FREE_TRACK | FILL_ON_FREE, &Config::SetFreeTrack},
+ {
+ "free_track", {FREE_TRACK | FILL_ON_FREE | TRACK_ALLOCS, &Config::SetFreeTrack},
},
- {"free_track_backtrace_num_frames",
- {0, &Config::SetFreeTrackBacktraceNumFrames},
+ {
+ "free_track_backtrace_num_frames", {0, &Config::SetFreeTrackBacktraceNumFrames},
},
- {"leak_track",
- {LEAK_TRACK | TRACK_ALLOCS, &Config::VerifyValueEmpty},
+ {
+ "leak_track", {LEAK_TRACK | TRACK_ALLOCS, &Config::VerifyValueEmpty},
},
- {"record_allocs",
- {RECORD_ALLOCS, &Config::SetRecordAllocs},
+ {
+ "record_allocs", {RECORD_ALLOCS, &Config::SetRecordAllocs},
},
- {"record_allocs_file",
- {0, &Config::SetRecordAllocsFile},
+ {
+ "record_allocs_file", {0, &Config::SetRecordAllocsFile},
+ },
+
+ {
+ "verify_pointers", {TRACK_ALLOCS, &Config::VerifyValueEmpty},
},
};
-bool Config::ParseValue(const std::string& option, const std::string& value,
- size_t min_value, size_t max_value, size_t* parsed_value) const {
+bool Config::ParseValue(const std::string& option, const std::string& value, size_t min_value,
+ size_t max_value, size_t* parsed_value) const {
assert(!value.empty());
// Parse the value into a size_t value.
@@ -135,8 +140,7 @@ bool Config::ParseValue(const std::string& option, const std::string& value,
char* end;
long long_value = strtol(value.c_str(), &end, 10);
if (errno != 0) {
- error_log("%s: bad value for option '%s': %s", getprogname(), option.c_str(),
- strerror(errno));
+ error_log("%s: bad value for option '%s': %s", getprogname(), option.c_str(), strerror(errno));
return false;
}
if (end == value.c_str()) {
@@ -144,24 +148,24 @@ bool Config::ParseValue(const std::string& option, const std::string& value,
return false;
}
if (static_cast<size_t>(end - value.c_str()) != value.size()) {
- error_log("%s: bad value for option '%s', non space found after option: %s",
- getprogname(), option.c_str(), end);
+ error_log("%s: bad value for option '%s', non space found after option: %s", getprogname(),
+ option.c_str(), end);
return false;
}
if (long_value < 0) {
- error_log("%s: bad value for option '%s', value cannot be negative: %ld",
- getprogname(), option.c_str(), long_value);
+ error_log("%s: bad value for option '%s', value cannot be negative: %ld", getprogname(),
+ option.c_str(), long_value);
return false;
}
if (static_cast<size_t>(long_value) < min_value) {
- error_log("%s: bad value for option '%s', value must be >= %zu: %ld",
- getprogname(), option.c_str(), min_value, long_value);
+ error_log("%s: bad value for option '%s', value must be >= %zu: %ld", getprogname(),
+ option.c_str(), min_value, long_value);
return false;
}
if (static_cast<size_t>(long_value) > max_value) {
- error_log("%s: bad value for option '%s', value must be <= %zu: %ld",
- getprogname(), option.c_str(), max_value, long_value);
+ error_log("%s: bad value for option '%s', value must be <= %zu: %ld", getprogname(),
+ option.c_str(), max_value, long_value);
return false;
}
*parsed_value = static_cast<size_t>(long_value);
@@ -261,7 +265,6 @@ bool Config::SetBacktraceDumpPrefix(const std::string&, const std::string& value
return true;
}
-
bool Config::SetExpandAlloc(const std::string& option, const std::string& value) {
return ParseValue(option, value, DEFAULT_EXPAND_BYTES, 1, MAX_EXPAND_BYTES, &expand_alloc_bytes_);
}
@@ -305,14 +308,13 @@ bool Config::SetRecordAllocsFile(const std::string&, const std::string& value) {
bool Config::VerifyValueEmpty(const std::string& option, const std::string& value) {
if (!value.empty()) {
// This is not valid.
- error_log("%s: value set for option '%s' which does not take a value",
- getprogname(), option.c_str());
+ error_log("%s: value set for option '%s' which does not take a value", getprogname(),
+ option.c_str());
return false;
}
return true;
}
-
void Config::LogUsage() const {
error_log("malloc debug options usage:");
error_log("");
@@ -329,8 +331,8 @@ void Config::LogUsage() const {
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 %zu bytes, the max bytes is %zu.",
- DEFAULT_GUARD_BYTES, MAX_GUARD_BYTES);
+ error_log(" The default is %zu bytes, the max bytes is %zu.", DEFAULT_GUARD_BYTES,
+ MAX_GUARD_BYTES);
error_log("");
error_log(" backtrace[=XX]");
error_log(" Enable capturing the backtrace at the point of allocation.");
@@ -419,13 +421,16 @@ void Config::LogUsage() const {
error_log(" This option only has meaning if the record_allocs options has been specified.");
error_log(" This is the name of the file to which recording information will be dumped.");
error_log(" The default is %s.", DEFAULT_RECORD_ALLOCS_FILE);
+ error_log("");
+ error_log(" verify_pointers");
+ error_log(" A lightweight way to verify that free/malloc_usable_size/realloc");
+ error_log(" are passed valid pointers.");
}
bool Config::GetOption(const char** options_str, std::string* option, std::string* value) {
const char* cur = *options_str;
// Process each property name we can find.
- while (isspace(*cur))
- ++cur;
+ while (isspace(*cur)) ++cur;
if (*cur == '\0') {
*options_str = cur;
@@ -433,25 +438,21 @@ bool Config::GetOption(const char** options_str, std::string* option, std::strin
}
const char* start = cur;
- while (!isspace(*cur) && *cur != '=' && *cur != '\0')
- ++cur;
+ while (!isspace(*cur) && *cur != '=' && *cur != '\0') ++cur;
*option = std::string(start, cur - start);
// Skip any spaces after the name.
- while (isspace(*cur))
- ++cur;
+ while (isspace(*cur)) ++cur;
value->clear();
if (*cur == '=') {
++cur;
// Skip the space after the equal.
- while (isspace(*cur))
- ++cur;
+ while (isspace(*cur)) ++cur;
start = cur;
- while (!isspace(*cur) && *cur != '\0')
- ++cur;
+ while (!isspace(*cur) && *cur != '\0') ++cur;
if (cur != start) {
*value = std::string(start, cur - start);
diff --git a/libc/malloc_debug/Config.h b/libc/malloc_debug/Config.h
index a310e09e0..3bcef0bc8 100644
--- a/libc/malloc_debug/Config.h
+++ b/libc/malloc_debug/Config.h
@@ -53,7 +53,7 @@ constexpr size_t MINIMUM_ALIGNMENT_BYTES = 8;
#endif
// If one or more of these options is set, then a special header is needed.
-constexpr uint64_t HEADER_OPTIONS = FRONT_GUARD | REAR_GUARD | BACKTRACE | FREE_TRACK | LEAK_TRACK;
+constexpr uint64_t HEADER_OPTIONS = FRONT_GUARD | REAR_GUARD;
class Config {
public:
diff --git a/libc/malloc_debug/DebugData.cpp b/libc/malloc_debug/DebugData.cpp
index 76f8fbb1e..44c4a10d9 100644
--- a/libc/malloc_debug/DebugData.cpp
+++ b/libc/malloc_debug/DebugData.cpp
@@ -28,14 +28,12 @@
#include <stdint.h>
-#include "BacktraceData.h"
#include "Config.h"
#include "DebugData.h"
-#include "debug_disable.h"
-#include "FreeTrackData.h"
#include "GuardData.h"
+#include "PointerData.h"
+#include "debug_disable.h"
#include "malloc_debug.h"
-#include "TrackData.h"
bool DebugData::Initialize(const char* options) {
if (!config_.Init(options)) {
@@ -44,18 +42,9 @@ bool DebugData::Initialize(const char* options) {
// Check to see if the options that require a header are enabled.
if (config_.options() & HEADER_OPTIONS) {
- need_header_ = true;
-
// Initialize all of the static header offsets.
pointer_offset_ = __BIONIC_ALIGN(sizeof(Header), MINIMUM_ALIGNMENT_BYTES);
- if (config_.options() & BACKTRACE) {
- backtrace.reset(new BacktraceData(this, config_, &pointer_offset_));
- if (!backtrace->Initialize(config_)) {
- return false;
- }
- }
-
if (config_.options() & FRONT_GUARD) {
front_guard.reset(new FrontGuardData(this, config_, &pointer_offset_));
}
@@ -67,13 +56,12 @@ bool DebugData::Initialize(const char* options) {
rear_guard.reset(new RearGuardData(this, config_));
extra_bytes_ += config_.rear_guard_bytes();
}
+ }
- if (config_.options() & FREE_TRACK) {
- free_track.reset(new FreeTrackData(this, config_));
- }
-
- if (config_.options() & TRACK_ALLOCS) {
- track.reset(new TrackData(this));
+ if (TrackPointers()) {
+ pointer.reset(new PointerData(this));
+ if (!pointer->Initialize(config_)) {
+ return false;
}
}
@@ -91,28 +79,19 @@ bool DebugData::Initialize(const char* options) {
}
void DebugData::PrepareFork() {
- if (track != nullptr) {
- track->PrepareFork();
- }
- if (free_track != nullptr) {
- free_track->PrepareFork();
+ if (pointer != nullptr) {
+ pointer->PrepareFork();
}
}
void DebugData::PostForkParent() {
- if (track != nullptr) {
- track->PostForkParent();
- }
- if (free_track != nullptr) {
- free_track->PostForkParent();
+ if (pointer != nullptr) {
+ pointer->PostForkParent();
}
}
void DebugData::PostForkChild() {
- if (track != nullptr) {
- track->PostForkChild();
- }
- if (free_track != nullptr) {
- free_track->PostForkChild();
+ if (pointer != nullptr) {
+ pointer->PostForkChild();
}
}
diff --git a/libc/malloc_debug/DebugData.h b/libc/malloc_debug/DebugData.h
index 9aece0b80..f7cf8abd1 100644
--- a/libc/malloc_debug/DebugData.h
+++ b/libc/malloc_debug/DebugData.h
@@ -35,13 +35,11 @@
#include <private/bionic_macros.h>
-#include "BacktraceData.h"
#include "Config.h"
-#include "FreeTrackData.h"
#include "GuardData.h"
-#include "malloc_debug.h"
+#include "PointerData.h"
#include "RecordData.h"
-#include "TrackData.h"
+#include "malloc_debug.h"
class DebugData {
public:
@@ -62,11 +60,6 @@ class DebugData {
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());
- }
-
uint8_t* GetFrontGuard(const Header* header) {
uintptr_t value = reinterpret_cast<uintptr_t>(header);
return reinterpret_cast<uint8_t*>(value + front_guard->offset());
@@ -74,30 +67,30 @@ class DebugData {
uint8_t* GetRearGuard(const Header* header) {
uintptr_t value = reinterpret_cast<uintptr_t>(GetPointer(header));
- return reinterpret_cast<uint8_t*>(value + header->real_size());
+ return reinterpret_cast<uint8_t*>(value + header->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_; }
+ bool TrackPointers() { return config_.options() & TRACK_ALLOCS; }
+
+ bool HeaderEnabled() { return config_.options() & HEADER_OPTIONS; }
+
void PrepareFork();
void PostForkParent();
void PostForkChild();
- std::unique_ptr<BacktraceData> backtrace;
- std::unique_ptr<TrackData> track;
std::unique_ptr<FrontGuardData> front_guard;
+ std::unique_ptr<PointerData> pointer;
std::unique_ptr<RearGuardData> rear_guard;
- std::unique_ptr<FreeTrackData> free_track;
std::unique_ptr<RecordData> record;
private:
size_t extra_bytes_ = 0;
size_t pointer_offset_ = 0;
- bool need_header_ = false;
Config config_;
diff --git a/libc/malloc_debug/FreeTrackData.cpp b/libc/malloc_debug/FreeTrackData.cpp
deleted file mode 100644
index e8e7a6774..000000000
--- a/libc/malloc_debug/FreeTrackData.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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(DebugData* debug, const Config& config)
- : OptionData(debug), backtrace_num_frames_(config.free_track_backtrace_num_frames()) {
- cmp_mem_.resize(4096);
- memset(cmp_mem_.data(), config.fill_free_value(), cmp_mem_.size());
-}
-
-void FreeTrackData::LogFreeError(const Header* header, const uint8_t* pointer) {
- 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(" allocation[%zu] = 0x%02x (expected 0x%02x)", i, pointer[i], fill_free_value);
- }
- }
- auto back_iter = backtraces_.find(header);
- if (back_iter != backtraces_.end()) {
- const BacktraceHeader* back_header = back_iter->second;
- error_log("Backtrace at time of free:");
- backtrace_log(&back_header->frames[0], back_header->num_frames);
- }
- error_log(LOG_DIVIDER);
-}
-
-void FreeTrackData::VerifyAndFree(const Header* header) {
- const void* pointer = debug_->GetPointer(header);
- if (header->tag != DEBUG_FREE_TAG) {
- error_log(LOG_DIVIDER);
- error_log("+++ ALLOCATION %p HAS CORRUPTED HEADER TAG 0x%x AFTER FREE", pointer, header->tag);
- error_log(LOG_DIVIDER);
- } else {
- 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(header, reinterpret_cast<const uint8_t*>(pointer));
- break;
- }
- bytes -= bytes_to_cmp;
- memory = &memory[bytes_to_cmp];
- }
- }
-
- auto back_iter = backtraces_.find(header);
- if (back_iter != backtraces_.end()) {
- g_dispatch->free(reinterpret_cast<void*>(back_iter->second));
- backtraces_.erase(header);
- }
- g_dispatch->free(header->orig_pointer);
-}
-
-void FreeTrackData::Add(const Header* header) {
- pthread_mutex_lock(&mutex_);
- if (list_.size() == debug_->config().free_track_allocations()) {
- const Header* old_header = list_.back();
- VerifyAndFree(old_header);
- list_.pop_back();
- }
-
- if (backtrace_num_frames_ > 0) {
- BacktraceHeader* back_header = reinterpret_cast<BacktraceHeader*>(
- g_dispatch->malloc(sizeof(BacktraceHeader) + backtrace_num_frames_ * sizeof(uintptr_t)));
- if (back_header) {
- back_header->num_frames = backtrace_get(&back_header->frames[0], backtrace_num_frames_);
- backtraces_[header] = back_header;
- }
- }
- list_.push_front(header);
-
- pthread_mutex_unlock(&mutex_);
-}
-
-void FreeTrackData::VerifyAll() {
- for (const auto& header : list_) {
- VerifyAndFree(header);
- }
- list_.clear();
-}
-
-void FreeTrackData::LogBacktrace(const Header* header) {
- auto back_iter = backtraces_.find(header);
- if (back_iter == backtraces_.end()) {
- return;
- }
-
- error_log("Backtrace of original free:");
- backtrace_log(&back_iter->second->frames[0], back_iter->second->num_frames);
-}
diff --git a/libc/malloc_debug/FreeTrackData.h b/libc/malloc_debug/FreeTrackData.h
deleted file mode 100644
index bd3497d5e..000000000
--- a/libc/malloc_debug/FreeTrackData.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <pthread.h>
-
-#include <deque>
-#include <unordered_map>
-#include <vector>
-
-#include <private/bionic_macros.h>
-
-#include "OptionData.h"
-
-// Forward declarations.
-struct BacktraceHeader;
-class Config;
-class DebugData;
-struct Header;
-
-class FreeTrackData : public OptionData {
- public:
- FreeTrackData(DebugData* debug_data, const Config& config);
- virtual ~FreeTrackData() = default;
-
- void Add(const Header* header);
-
- void VerifyAll();
-
- void LogBacktrace(const Header* header);
-
- void PrepareFork() { pthread_mutex_lock(&mutex_); }
-
- void PostForkParent() { pthread_mutex_unlock(&mutex_); }
-
- void PostForkChild() { pthread_mutex_init(&mutex_, NULL); }
-
- private:
- void LogFreeError(const Header* header, const uint8_t* pointer);
- void VerifyAndFree(const Header* header);
-
- pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
- std::deque<const Header*> list_;
- std::vector<uint8_t> cmp_mem_;
- std::unordered_map<const Header*, BacktraceHeader*> backtraces_;
- size_t backtrace_num_frames_;
-
- DISALLOW_COPY_AND_ASSIGN(FreeTrackData);
-};
diff --git a/libc/malloc_debug/GuardData.cpp b/libc/malloc_debug/GuardData.cpp
index d6cef85c1..f9a2dcaa7 100644
--- a/libc/malloc_debug/GuardData.cpp
+++ b/libc/malloc_debug/GuardData.cpp
@@ -31,13 +31,13 @@
#include <vector>
-#include "backtrace.h"
#include "Config.h"
+#include "DebugData.h"
+#include "GuardData.h"
+#include "backtrace.h"
#include "debug_disable.h"
#include "debug_log.h"
-#include "DebugData.h"
#include "malloc_debug.h"
-#include "GuardData.h"
GuardData::GuardData(DebugData* debug_data, int init_value, size_t num_bytes)
: OptionData(debug_data) {
@@ -48,8 +48,8 @@ GuardData::GuardData(DebugData* debug_data, int init_value, size_t num_bytes)
void GuardData::LogFailure(const Header* header, const void* pointer, const void* data) {
error_log(LOG_DIVIDER);
- error_log("+++ ALLOCATION %p SIZE %zu HAS A CORRUPTED %s GUARD", pointer,
- header->real_size(), GetTypeName());
+ error_log("+++ ALLOCATION %p SIZE %zu HAS A CORRUPTED %s GUARD", pointer, header->size,
+ GetTypeName());
// Log all of the failing bytes.
const uint8_t* expected = cmp_mem_.data();
@@ -70,7 +70,7 @@ void GuardData::LogFailure(const Header* header, const void* pointer, const void
}
FrontGuardData::FrontGuardData(DebugData* debug_data, const Config& config, size_t* offset)
- : GuardData(debug_data, config.front_guard_value(), config.front_guard_bytes()) {
+ : GuardData(debug_data, 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());
@@ -88,8 +88,7 @@ void FrontGuardData::LogFailure(const Header* header) {
}
RearGuardData::RearGuardData(DebugData* debug_data, const Config& config)
- : GuardData(debug_data, config.rear_guard_value(), config.rear_guard_bytes()) {
-}
+ : GuardData(debug_data, config.rear_guard_value(), config.rear_guard_bytes()) {}
bool RearGuardData::Valid(const Header* header) {
return GuardData::Valid(debug_->GetRearGuard(header));
diff --git a/libc/malloc_debug/MapData.cpp b/libc/malloc_debug/MapData.cpp
index d57017e34..060425e23 100644
--- a/libc/malloc_debug/MapData.cpp
+++ b/libc/malloc_debug/MapData.cpp
@@ -31,8 +31,8 @@
#include <inttypes.h>
#include <link.h>
#include <stdio.h>
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
#include <vector>
@@ -46,8 +46,8 @@ static MapEntry* parse_line(char* line) {
uintptr_t offset;
char permissions[5];
int name_pos;
- if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n", &start,
- &end, permissions, &offset, &name_pos) < 2) {
+ if (sscanf(line, "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n", &start, &end,
+ permissions, &offset, &name_pos) < 2) {
return nullptr;
}
@@ -66,13 +66,13 @@ static MapEntry* parse_line(char* line) {
return entry;
}
-template<typename T>
+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)) {
+ if (addr & (sizeof(T) - 1)) {
return false;
}
*store = *reinterpret_cast<T*>(addr);
@@ -157,7 +157,7 @@ const MapEntry* MapData::find(uintptr_t pc, uintptr_t* rel_pc) {
return nullptr;
}
- MapEntry *entry = *it;
+ MapEntry* entry = *it;
if (!entry->load_base_read) {
read_loadbase(entry);
}
diff --git a/libc/malloc_debug/MapData.h b/libc/malloc_debug/MapData.h
index a71f96d0d..d8398bde3 100644
--- a/libc/malloc_debug/MapData.h
+++ b/libc/malloc_debug/MapData.h
@@ -31,8 +31,8 @@
#include <sys/cdefs.h>
#include <mutex>
-#include <string>
#include <set>
+#include <string>
#include <private/bionic_macros.h>
@@ -50,12 +50,9 @@ struct MapEntry {
std::string name;
};
-
// Ordering comparator that returns equivalence for overlapping entries
struct compare_entries {
- bool operator()(const MapEntry* a, const MapEntry* b) const {
- return a->end <= b->start;
- }
+ bool operator()(const MapEntry* a, const MapEntry* b) const { return a->end <= b->start; }
};
class MapData {
diff --git a/libc/malloc_debug/PointerData.cpp b/libc/malloc_debug/PointerData.cpp
new file mode 100644
index 000000000..f811a5eb4
--- /dev/null
+++ b/libc/malloc_debug/PointerData.cpp
@@ -0,0 +1,553 @@
+/*
+ * 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 <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
+#include <private/bionic_macros.h>
+
+#include "Config.h"
+#include "DebugData.h"
+#include "PointerData.h"
+#include "backtrace.h"
+#include "debug_log.h"
+#include "malloc_debug.h"
+
+std::atomic_uint8_t PointerData::backtrace_enabled_;
+std::atomic_bool PointerData::backtrace_dump_;
+
+std::mutex PointerData::pointer_mutex_;
+std::unordered_map<uintptr_t, PointerInfoType> PointerData::pointers_ GUARDED_BY(
+ PointerData::pointer_mutex_);
+
+std::mutex PointerData::frame_mutex_;
+std::unordered_map<FrameKeyType, size_t> PointerData::key_to_index_ GUARDED_BY(
+ PointerData::frame_mutex_);
+std::unordered_map<size_t, FrameInfoType> PointerData::frames_ GUARDED_BY(PointerData::frame_mutex_);
+constexpr size_t kBacktraceEmptyIndex = 1;
+size_t PointerData::cur_hash_index_ GUARDED_BY(PointerData::frame_mutex_);
+
+std::mutex PointerData::free_pointer_mutex_;
+std::deque<FreePointerInfoType> PointerData::free_pointers_ GUARDED_BY(
+ PointerData::free_pointer_mutex_);
+
+// Buffer to use for comparison.
+static constexpr size_t kCompareBufferSize = 512 * 1024;
+static std::vector<uint8_t> g_cmp_mem(0);
+
+static void ToggleBacktraceEnable(int, siginfo_t*, void*) {
+ g_debug->pointer->ToggleBacktraceEnabled();
+}
+
+static void EnableDump(int, siginfo_t*, void*) {
+ g_debug->pointer->EnableDumping();
+}
+
+PointerData::PointerData(DebugData* debug_data) : OptionData(debug_data) {}
+
+bool PointerData::Initialize(const Config& config) NO_THREAD_SAFETY_ANALYSIS {
+ pointers_.clear();
+ key_to_index_.clear();
+ frames_.clear();
+ free_pointers_.clear();
+ // A hash index of kBacktraceEmptyIndex indicates that we tried to get
+ // a backtrace, but there was nothing recorded.
+ cur_hash_index_ = kBacktraceEmptyIndex + 1;
+
+ backtrace_enabled_ = config.backtrace_enabled();
+ if (config.backtrace_enable_on_signal()) {
+ struct sigaction64 enable_act = {};
+ enable_act.sa_sigaction = ToggleBacktraceEnable;
+ enable_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ if (sigaction64(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());
+ }
+
+ if (config.options() & BACKTRACE) {
+ struct sigaction64 act = {};
+ act.sa_sigaction = EnableDump;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ if (sigaction64(config.backtrace_dump_signal(), &act, nullptr) != 0) {
+ error_log("Unable to set up backtrace dump signal function: %s", strerror(errno));
+ return false;
+ }
+ info_log("%s: Run: 'kill -%d %d' to dump the backtrace.", getprogname(),
+ config.backtrace_dump_signal(), getpid());
+ }
+
+ backtrace_dump_ = false;
+
+ if (config.options() & FREE_TRACK) {
+ g_cmp_mem.resize(kCompareBufferSize, config.fill_free_value());
+ }
+ return true;
+}
+
+size_t PointerData::AddBacktrace(size_t num_frames) {
+ std::vector<uintptr_t> frames(num_frames);
+ num_frames = backtrace_get(frames.data(), frames.size());
+ if (num_frames == 0) {
+ return kBacktraceEmptyIndex;
+ }
+
+ FrameKeyType key{.num_frames = num_frames, .frames = frames.data()};
+ size_t hash_index;
+ std::lock_guard<std::mutex> frame_guard(frame_mutex_);
+ auto entry = key_to_index_.find(key);
+ if (entry == key_to_index_.end()) {
+ frames.resize(num_frames);
+ hash_index = cur_hash_index_++;
+ key.frames = frames.data();
+ key_to_index_.emplace(key, hash_index);
+
+ frames_.emplace(hash_index, FrameInfoType{.references = 1, .frames = std::move(frames)});
+ } else {
+ hash_index = entry->second;
+ FrameInfoType* frame_info = &frames_[hash_index];
+ frame_info->references++;
+ }
+ return hash_index;
+}
+
+void PointerData::RemoveBacktrace(size_t hash_index) {
+ if (hash_index <= kBacktraceEmptyIndex) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> frame_guard(frame_mutex_);
+ auto frame_entry = frames_.find(hash_index);
+ if (frame_entry == frames_.end()) {
+ error_log("hash_index %zu does not have matching frame data.", hash_index);
+ return;
+ }
+ FrameInfoType* frame_info = &frame_entry->second;
+ if (--frame_info->references == 0) {
+ FrameKeyType key{.num_frames = frame_info->frames.size(), .frames = frame_info->frames.data()};
+ key_to_index_.erase(key);
+ frames_.erase(hash_index);
+ }
+}
+
+void PointerData::Add(const void* ptr, size_t pointer_size) {
+ uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
+ size_t hash_index = 0;
+ if (backtrace_enabled_) {
+ hash_index = AddBacktrace(g_debug->config().backtrace_frames());
+ }
+
+ std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
+ pointers_[pointer] = PointerInfoType{PointerInfoType::GetEncodedSize(pointer_size), hash_index};
+}
+
+void PointerData::Remove(const void* ptr) {
+ uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
+ size_t hash_index;
+ {
+ std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
+ auto entry = pointers_.find(pointer);
+ if (entry == pointers_.end()) {
+ // Error.
+ error_log("No tracked pointer found for 0x%" PRIxPTR, pointer);
+ return;
+ }
+ hash_index = entry->second.hash_index;
+ pointers_.erase(pointer);
+ }
+
+ RemoveBacktrace(hash_index);
+}
+
+size_t PointerData::GetFrames(const void* ptr, uintptr_t* frames, size_t max_frames) {
+ uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
+ size_t hash_index;
+ {
+ std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
+ auto entry = pointers_.find(pointer);
+ if (entry == pointers_.end()) {
+ return 0;
+ }
+ hash_index = entry->second.hash_index;
+ }
+
+ if (hash_index <= kBacktraceEmptyIndex) {
+ return 0;
+ }
+
+ std::lock_guard<std::mutex> frame_guard(frame_mutex_);
+ auto frame_entry = frames_.find(hash_index);
+ if (frame_entry == frames_.end()) {
+ return 0;
+ }
+ FrameInfoType* frame_info = &frame_entry->second;
+ if (max_frames > frame_info->frames.size()) {
+ max_frames = frame_info->frames.size();
+ }
+ memcpy(frames, &frame_info->frames[0], max_frames * sizeof(uintptr_t));
+
+ return max_frames;
+}
+
+void PointerData::LogFreeError(const FreePointerInfoType& info, size_t usable_size) {
+ error_log(LOG_DIVIDER);
+ uint8_t* memory = reinterpret_cast<uint8_t*>(info.pointer);
+ error_log("+++ ALLOCATION %p USED AFTER FREE", memory);
+ uint8_t fill_free_value = g_debug->config().fill_free_value();
+ for (size_t i = 0; i < usable_size; i++) {
+ if (memory[i] != fill_free_value) {
+ error_log(" allocation[%zu] = 0x%02x (expected 0x%02x)", i, memory[i], fill_free_value);
+ }
+ }
+
+ if (info.hash_index > kBacktraceEmptyIndex) {
+ std::lock_guard<std::mutex> frame_guard(frame_mutex_);
+ auto frame_entry = frames_.find(info.hash_index);
+ if (frame_entry != frames_.end()) {
+ FrameInfoType* frame_info = &frame_entry->second;
+ error_log("Backtrace at time of free:");
+ backtrace_log(frame_info->frames.data(), frame_info->frames.size());
+ }
+ }
+
+ error_log(LOG_DIVIDER);
+}
+
+void PointerData::VerifyFreedPointer(const FreePointerInfoType& info) {
+ size_t usable_size;
+ if (g_debug->HeaderEnabled()) {
+ // Check to see if the tag data has been damaged.
+ Header* header = g_debug->GetHeader(reinterpret_cast<const void*>(info.pointer));
+ if (header->tag != DEBUG_FREE_TAG) {
+ error_log(LOG_DIVIDER);
+ error_log("+++ ALLOCATION 0x%" PRIxPTR " HAS CORRUPTED HEADER TAG 0x%x AFTER FREE",
+ info.pointer, header->tag);
+ error_log(LOG_DIVIDER);
+
+ // Stop processing here, it is impossible to tell how the header
+ // may have been damaged.
+ return;
+ }
+ usable_size = header->usable_size;
+ } else {
+ usable_size = g_dispatch->malloc_usable_size(reinterpret_cast<const void*>(info.pointer));
+ }
+
+ size_t bytes = (usable_size < g_debug->config().fill_on_free_bytes())
+ ? usable_size
+ : g_debug->config().fill_on_free_bytes();
+ const uint8_t* memory = reinterpret_cast<const uint8_t*>(info.pointer);
+ while (bytes > 0) {
+ size_t bytes_to_cmp = (bytes < g_cmp_mem.size()) ? bytes : g_cmp_mem.size();
+ if (memcmp(memory, g_cmp_mem.data(), bytes_to_cmp) != 0) {
+ LogFreeError(info, usable_size);
+ }
+ bytes -= bytes_to_cmp;
+ memory = &memory[bytes_to_cmp];
+ }
+}
+
+void* PointerData::AddFreed(const void* ptr) {
+ uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
+
+ size_t hash_index = 0;
+ size_t num_frames = g_debug->config().free_track_backtrace_num_frames();
+ if (num_frames) {
+ hash_index = AddBacktrace(num_frames);
+ }
+
+ void* last = nullptr;
+ std::lock_guard<std::mutex> freed_guard(free_pointer_mutex_);
+ if (free_pointers_.size() == g_debug->config().free_track_allocations()) {
+ FreePointerInfoType info(free_pointers_.front());
+ free_pointers_.pop_front();
+ VerifyFreedPointer(info);
+ RemoveBacktrace(info.hash_index);
+ last = reinterpret_cast<void*>(info.pointer);
+ }
+
+ free_pointers_.emplace_back(FreePointerInfoType{pointer, hash_index});
+ return last;
+}
+
+void PointerData::LogFreeBacktrace(const void* ptr) {
+ size_t hash_index = 0;
+ {
+ uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
+ std::lock_guard<std::mutex> freed_guard(free_pointer_mutex_);
+ for (const auto& info : free_pointers_) {
+ if (info.pointer == pointer) {
+ hash_index = info.hash_index;
+ break;
+ }
+ }
+ }
+
+ if (hash_index <= kBacktraceEmptyIndex) {
+ return;
+ }
+
+ std::lock_guard<std::mutex> frame_guard(frame_mutex_);
+ auto frame_entry = frames_.find(hash_index);
+ if (frame_entry == frames_.end()) {
+ error_log("Freed pointer hash_index %zu does not have matching frame data.", hash_index);
+ return;
+ }
+ FrameInfoType* frame_info = &frame_entry->second;
+ error_log("Backtrace of original free:");
+ backtrace_log(frame_info->frames.data(), frame_info->frames.size());
+}
+
+void PointerData::VerifyAllFreed() {
+ std::lock_guard<std::mutex> freed_guard(free_pointer_mutex_);
+ for (auto& free_info : free_pointers_) {
+ VerifyFreedPointer(free_info);
+ }
+}
+
+void PointerData::GetList(std::vector<ListInfoType>* list, bool only_with_backtrace)
+ REQUIRES(pointer_mutex_, frame_mutex_) {
+ for (const auto& entry : pointers_) {
+ FrameInfoType* frame_info = nullptr;
+ size_t hash_index = entry.second.hash_index;
+ if (hash_index > kBacktraceEmptyIndex) {
+ frame_info = &frames_[hash_index];
+ if (frame_info->references == 0) {
+ // Somehow wound up with a pointer with a valid hash_index, but
+ // no frame data. This should not be possible since adding a pointer
+ // occurs after the hash_index and frame data have been added.
+ // When removing a pointer, the pointer is deleted before the frame
+ // data.
+ frames_.erase(hash_index);
+ error_log("Pointer 0x%" PRIxPTR " hash_index %zu does not exist.", entry.first, hash_index);
+ frame_info = nullptr;
+ }
+ }
+ if (hash_index == 0 && only_with_backtrace) {
+ continue;
+ }
+
+ list->emplace_back(ListInfoType{entry.first, 1, entry.second.RealSize(),
+ entry.second.ZygoteChildAlloc(), frame_info});
+ }
+
+ // Sort by the size of the allocation.
+ std::sort(list->begin(), list->end(), [](const ListInfoType& a, const ListInfoType& b) {
+ // Put zygote child allocations first.
+ bool a_zygote_child_alloc = a.zygote_child_alloc;
+ bool b_zygote_child_alloc = b.zygote_child_alloc;
+ if (a_zygote_child_alloc && !b_zygote_child_alloc) {
+ return false;
+ }
+ if (!a_zygote_child_alloc && b_zygote_child_alloc) {
+ return true;
+ }
+
+ // Sort by size, descending order.
+ if (a.size != b.size) return a.size > b.size;
+
+ // Put pointers with no backtrace last.
+ FrameInfoType* a_frame = a.frame_info;
+ FrameInfoType* b_frame = b.frame_info;
+ if (a_frame == nullptr && b_frame != nullptr) {
+ return false;
+ }
+ if (a_frame != nullptr && b_frame == nullptr) {
+ return true;
+ }
+ // Put the pointers with longest backtrace first.
+ if (a_frame->frames.size() != b_frame->frames.size()) {
+ return a_frame->frames.size() > b_frame->frames.size();
+ }
+
+ // Last sort by pointer.
+ return a.pointer < b.pointer;
+ });
+}
+
+void PointerData::GetUniqueList(std::vector<ListInfoType>* list, bool only_with_backtrace)
+ REQUIRES(pointer_mutex_, frame_mutex_) {
+ GetList(list, only_with_backtrace);
+
+ // Remove duplicates of size/backtraces.
+ for (auto iter = list->begin(); iter != list->end();) {
+ auto dup_iter = iter + 1;
+ bool zygote_child_alloc = iter->zygote_child_alloc;
+ size_t size = iter->size;
+ FrameInfoType* frame_info = iter->frame_info;
+ for (; dup_iter != list->end(); ++dup_iter) {
+ if (zygote_child_alloc != dup_iter->zygote_child_alloc || size != dup_iter->size ||
+ frame_info != dup_iter->frame_info) {
+ break;
+ }
+ iter->num_allocations++;
+ }
+ iter = list->erase(iter + 1, dup_iter);
+ }
+}
+
+void PointerData::LogLeaks() {
+ std::vector<ListInfoType> list;
+
+ std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
+ std::lock_guard<std::mutex> frame_guard(frame_mutex_);
+ GetList(&list, false);
+
+ size_t track_count = 0;
+ for (const auto& list_info : list) {
+ error_log("+++ %s leaked block of size %zu at 0x%" PRIxPTR " (leak %zu of %zu)", getprogname(),
+ list_info.size, list_info.pointer, ++track_count, list.size());
+ if (list_info.frame_info != nullptr) {
+ error_log("Backtrace at time of allocation:");
+ backtrace_log(list_info.frame_info->frames.data(), list_info.frame_info->frames.size());
+ }
+ // Do not bother to free the pointers, we are about to exit any way.
+ }
+}
+
+void PointerData::GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size,
+ size_t* total_memory, size_t* backtrace_size) {
+ std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
+ std::lock_guard<std::mutex> frame_guard(frame_mutex_);
+
+ if (pointers_.empty()) {
+ return;
+ }
+
+ std::vector<ListInfoType> list;
+ GetUniqueList(&list, true);
+ if (list.empty()) {
+ return;
+ }
+
+ *backtrace_size = g_debug->config().backtrace_frames();
+ *info_size = sizeof(size_t) * 2 + sizeof(uintptr_t) * *backtrace_size;
+ *overall_size = *info_size * list.size();
+ *info = reinterpret_cast<uint8_t*>(g_dispatch->calloc(*info_size, list.size()));
+ if (*info == nullptr) {
+ return;
+ }
+
+ uint8_t* data = *info;
+ *total_memory = 0;
+ for (const auto& list_info : list) {
+ FrameInfoType* frame_info = list_info.frame_info;
+ *total_memory += list_info.size * list_info.num_allocations;
+ size_t allocation_size =
+ PointerInfoType::GetEncodedSize(list_info.zygote_child_alloc, list_info.size);
+ memcpy(data, &allocation_size, sizeof(size_t));
+ memcpy(&data[sizeof(size_t)], &list_info.num_allocations, sizeof(size_t));
+ if (frame_info != nullptr) {
+ memcpy(&data[2 * sizeof(size_t)], frame_info->frames.data(),
+ frame_info->frames.size() * sizeof(uintptr_t));
+ }
+ data += *info_size;
+ }
+}
+
+bool PointerData::Exists(const void* ptr) {
+ uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
+ std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
+ return pointers_.count(pointer) != 0;
+}
+
+void PointerData::DumpLiveToFile(FILE* fp) {
+ std::vector<ListInfoType> list;
+
+ std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
+ std::lock_guard<std::mutex> frame_guard(frame_mutex_);
+ GetUniqueList(&list, false);
+
+ size_t total_memory = 0;
+ for (const auto& info : list) {
+ total_memory += info.size * info.num_allocations;
+ }
+
+ fprintf(fp, "Total memory: %zu\n", total_memory);
+ fprintf(fp, "Allocation records: %zd\n", list.size());
+ fprintf(fp, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
+ fprintf(fp, "\n");
+
+ for (const auto& info : list) {
+ fprintf(fp, "z %d sz %8zu num %zu bt", (info.zygote_child_alloc) ? 1 : 0, info.size,
+ info.num_allocations);
+ FrameInfoType* frame_info = info.frame_info;
+ if (frame_info != nullptr) {
+ for (size_t i = 0; i < frame_info->frames.size(); i++) {
+ if (frame_info->frames[i] == 0) {
+ break;
+ }
+#if defined(__LP64__)
+ fprintf(fp, " %016" PRIxPTR, frame_info->frames[i]);
+#else
+ fprintf(fp, " %08" PRIxPTR, frame_info->frames[i]);
+#endif
+ }
+ }
+ fprintf(fp, "\n");
+ }
+}
+
+void PointerData::PrepareFork() NO_THREAD_SAFETY_ANALYSIS {
+ pointer_mutex_.lock();
+ frame_mutex_.lock();
+ free_pointer_mutex_.lock();
+}
+
+void PointerData::PostForkParent() NO_THREAD_SAFETY_ANALYSIS {
+ frame_mutex_.unlock();
+ pointer_mutex_.unlock();
+ free_pointer_mutex_.unlock();
+}
+
+void PointerData::PostForkChild() __attribute__((no_thread_safety_analysis)) {
+ // Make sure that any potential mutexes have been released and are back
+ // to an initial state.
+ frame_mutex_.try_lock();
+ frame_mutex_.unlock();
+ pointer_mutex_.try_lock();
+ pointer_mutex_.unlock();
+ free_pointer_mutex_.try_lock();
+ free_pointer_mutex_.unlock();
+}
diff --git a/libc/malloc_debug/PointerData.h b/libc/malloc_debug/PointerData.h
new file mode 100644
index 000000000..7a0b3b89e
--- /dev/null
+++ b/libc/malloc_debug/PointerData.h
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <atomic>
+#include <deque>
+#include <mutex>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <private/bionic_macros.h>
+
+#include "OptionData.h"
+
+extern int* g_malloc_zygote_child;
+
+// Forward declarations.
+class Config;
+
+struct FrameKeyType {
+ size_t num_frames;
+ uintptr_t* frames;
+
+ bool operator==(const FrameKeyType& comp) const {
+ if (num_frames != comp.num_frames) return false;
+ for (size_t i = 0; i < num_frames; i++) {
+ if (frames[i] != comp.frames[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+namespace std {
+template <>
+struct hash<FrameKeyType> {
+ std::size_t operator()(const FrameKeyType& key) const {
+ std::size_t cur_hash = key.frames[0];
+ // Limit the number of frames to speed up hashing.
+ size_t max_frames = (key.num_frames > 5) ? 5 : key.num_frames;
+ for (size_t i = 1; i < max_frames; i++) {
+ cur_hash ^= key.frames[i];
+ }
+ return cur_hash;
+ }
+};
+}; // namespace std
+
+struct FrameInfoType {
+ size_t references = 0;
+ std::vector<uintptr_t> frames;
+};
+
+struct PointerInfoType {
+ size_t size;
+ size_t hash_index;
+ size_t RealSize() const { return size & ~(1U << 31); }
+ bool ZygoteChildAlloc() const { return size & (1U << 31); }
+ static size_t GetEncodedSize(size_t size) { return GetEncodedSize(*g_malloc_zygote_child, size); }
+ static size_t GetEncodedSize(bool child_alloc, size_t size) {
+ return size | ((child_alloc) ? (1U << 31) : 0);
+ }
+ static size_t MaxSize() { return (1U << 31) - 1; }
+};
+
+struct FreePointerInfoType {
+ uintptr_t pointer;
+ size_t hash_index;
+};
+
+struct ListInfoType {
+ uintptr_t pointer;
+ size_t num_allocations;
+ size_t size;
+ bool zygote_child_alloc;
+ FrameInfoType* frame_info;
+};
+
+class PointerData : public OptionData {
+ public:
+ PointerData(DebugData* debug_data);
+ virtual ~PointerData() = default;
+
+ bool Initialize(const Config& config);
+
+ inline size_t alloc_offset() { return alloc_offset_; }
+
+ bool ShouldBacktrace() { return backtrace_enabled_ == 1; }
+ void ToggleBacktraceEnabled() { backtrace_enabled_.fetch_xor(1); }
+
+ void EnableDumping() { backtrace_dump_ = true; }
+ bool ShouldDumpAndReset() {
+ bool expected = true;
+ return backtrace_dump_.compare_exchange_strong(expected, false);
+ }
+
+ void PrepareFork();
+ void PostForkParent();
+ void PostForkChild();
+
+ static void GetList(std::vector<ListInfoType>* list, bool only_with_backtrace);
+ static void GetUniqueList(std::vector<ListInfoType>* list, bool only_with_backtrace);
+
+ static size_t AddBacktrace(size_t num_frames);
+ static void RemoveBacktrace(size_t hash_index);
+
+ static void Add(const void* pointer, size_t size);
+ static void Remove(const void* pointer);
+
+ typedef std::unordered_map<uintptr_t, PointerInfoType>::iterator iterator;
+ static iterator begin() { return pointers_.begin(); }
+ static iterator end() { return pointers_.end(); }
+
+ static void* AddFreed(const void* pointer);
+ static void LogFreeError(const FreePointerInfoType& info, size_t usable_size);
+ static void LogFreeBacktrace(const void* ptr);
+ static void VerifyFreedPointer(const FreePointerInfoType& info);
+ static void VerifyAllFreed();
+
+ static void LogLeaks();
+ static void DumpLiveToFile(FILE* fp);
+
+ static void GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory,
+ size_t* backtrace_size);
+
+ static size_t GetFrames(const void* pointer, uintptr_t* frames, size_t max_frames);
+
+ static bool Exists(const void* pointer);
+
+ private:
+ static std::string GetHashString(uintptr_t* frames, size_t num_frames);
+
+ size_t alloc_offset_ = 0;
+ std::vector<uint8_t> cmp_mem_;
+
+ static std::atomic_uint8_t backtrace_enabled_;
+
+ static std::atomic_bool backtrace_dump_;
+
+ static std::mutex pointer_mutex_;
+ static std::unordered_map<uintptr_t, PointerInfoType> pointers_;
+
+ static std::mutex frame_mutex_;
+ static std::unordered_map<FrameKeyType, size_t> key_to_index_;
+ static std::unordered_map<size_t, FrameInfoType> frames_;
+ static size_t cur_hash_index_;
+
+ static std::mutex free_pointer_mutex_;
+ static std::deque<FreePointerInfoType> free_pointers_;
+
+ DISALLOW_COPY_AND_ASSIGN(PointerData);
+};
diff --git a/libc/malloc_debug/README.md b/libc/malloc_debug/README.md
index 69d064824..59ab6ad7b 100644
--- a/libc/malloc_debug/README.md
+++ b/libc/malloc_debug/README.md
@@ -33,6 +33,13 @@ On 32 bit systems, these two deprecated functions are also replaced:
Any errors detected by the library are reported in the log.
+NOTE: There is a small behavioral change beginning in P for realloc.
+Before, a realloc from one size to a smaller size would not update the
+backtrace related to the allocation. Starting in P, every single realloc
+call changes the backtrace for the pointer no matter whether the pointer
+returned has changed or not.
+
+
Controlling Malloc Debug Behavior
---------------------------------
Malloc debug is controlled by individual options. Each option can be enabled
@@ -106,8 +113,9 @@ If MAX\_FRAMES is present, it indicates the maximum number of frames to
capture in a backtrace. The default is 16 frames, the maximumum value
this can be set to is 256.
-This option adds a special header to all allocations that contains the
-backtrace and information about the original allocation.
+Before P, this option adds a special header to all allocations that contains
+the backtrace and information about the original allocation. After that, this
+option will not add a special header.
As of P, this option will also enable dumping backtrace heap data to a
file when the process receives the signal SIGRTMAX - 17 ( which is 47 on most
@@ -132,8 +140,9 @@ If MAX\_FRAMES is present, it indicates the maximum number of frames to
capture in a backtrace. The default is 16 frames, the maximumum value
this can be set to is 256.
-This option adds a special header to all allocations that contains the
-backtrace and information about the original allocation.
+Before P, this option adds a special header to all allocations that contains
+the backtrace and information about the original allocation. After that, this
+option will not add a special header.
### backtrace\_dump\_on\_exit
As of P, when the backtrace option has been enabled, this causes the backtrace
@@ -201,8 +210,9 @@ If ALLOCATION\_COUNT is present, it indicates the total number of allocations
in the list. The default is to record 100 freed allocations, the max
allocations to record is 16384.
-This option adds a special header to all allocations that contains
-information about the original allocation.
+Before P, this option adds a special header to all allocations that contains
+the backtrace and information about the original allocation. After that, this
+option will not add a special header.
Example error:
@@ -238,8 +248,9 @@ then the log will include the backtrace of the leaked allocations. This
option is not useful when enabled globally because a lot of programs do not
free everything before the program terminates.
-This option adds a special header to all allocations that contains
-information about the original allocation.
+Before P, this option adds a special header to all allocations that contains
+the backtrace and information about the original allocation. After that, this
+option will not add a special header.
Example leak error found in the log:
@@ -362,6 +373,26 @@ will be placed.
**NOTE**: This option is not available until the O release of Android.
+### verify\_pointers
+Track all live allocations to determine if a pointer is used that does not
+exist. This option is a lightweight way to verify that all
+free/malloc\_usable\_size/realloc calls are passed valid pointers.
+
+Example error:
+
+ 04-15 12:00:31.304 7412 7412 E malloc_debug: +++ ALLOCATION 0x12345678 UNKNOWN POINTER (free)
+ 04-15 12:00:31.305 7412 7412 E malloc_debug: Backtrace at time of failure:
+ 04-15 12:00:31.305 7412 7412 E malloc_debug: #00 pc 00029310 /system/lib/libc.so
+ 04-15 12:00:31.305 7412 7412 E malloc_debug: #01 pc 00021438 /system/lib/libc.so (newlocale+160)
+ 04-15 12:00:31.305 7412 7412 E malloc_debug: #02 pc 000a9e38 /system/lib/libc++.so
+ 04-15 12:00:31.305 7412 7412 E malloc_debug: #03 pc 000a28a8 /system/lib/libc++.so
+
+Where the name of the function varies depending on the function that called
+with a bad pointer. Only three functions do this checking: free,
+malloc\_usable\_size, realloc.
+
+**NOTE**: This option is not available until the P release of Android.
+
Additional Errors
-----------------
There are a few other error messages that might appear in the log.
diff --git a/libc/malloc_debug/RecordData.cpp b/libc/malloc_debug/RecordData.cpp
index 8e9c6716f..aea25133e 100644
--- a/libc/malloc_debug/RecordData.cpp
+++ b/libc/malloc_debug/RecordData.cpp
@@ -40,10 +40,10 @@
#include <android-base/stringprintf.h>
#include "Config.h"
-#include "debug_disable.h"
-#include "debug_log.h"
#include "DebugData.h"
#include "RecordData.h"
+#include "debug_disable.h"
+#include "debug_log.h"
RecordEntry::RecordEntry() : tid_(gettid()) {
}
@@ -52,52 +52,45 @@ std::string ThreadCompleteEntry::GetString() const {
return android::base::StringPrintf("%d: thread_done 0x0\n", tid_);
}
-AllocEntry::AllocEntry(void* pointer) : pointer_(pointer) {
-}
+AllocEntry::AllocEntry(void* pointer) : pointer_(pointer) {}
-MallocEntry::MallocEntry(void* pointer, size_t size) : AllocEntry(pointer), size_(size) {
-}
+MallocEntry::MallocEntry(void* pointer, size_t size) : AllocEntry(pointer), size_(size) {}
std::string MallocEntry::GetString() const {
return android::base::StringPrintf("%d: malloc %p %zu\n", tid_, pointer_, size_);
}
-FreeEntry::FreeEntry(void* pointer) : AllocEntry(pointer) {
-}
+FreeEntry::FreeEntry(void* pointer) : AllocEntry(pointer) {}
std::string FreeEntry::GetString() const {
return android::base::StringPrintf("%d: free %p\n", tid_, pointer_);
}
CallocEntry::CallocEntry(void* pointer, size_t nmemb, size_t size)
- : MallocEntry(pointer, size), nmemb_(nmemb) {
-}
+ : MallocEntry(pointer, size), nmemb_(nmemb) {}
std::string CallocEntry::GetString() const {
return android::base::StringPrintf("%d: calloc %p %zu %zu\n", tid_, pointer_, nmemb_, size_);
}
ReallocEntry::ReallocEntry(void* pointer, size_t size, void* old_pointer)
- : MallocEntry(pointer, size), old_pointer_(old_pointer) {
-}
+ : MallocEntry(pointer, size), old_pointer_(old_pointer) {}
std::string ReallocEntry::GetString() const {
- return android::base::StringPrintf("%d: realloc %p %p %zu\n", tid_, pointer_,
- old_pointer_, size_);
+ return android::base::StringPrintf("%d: realloc %p %p %zu\n", tid_, pointer_, old_pointer_, size_);
}
// aligned_alloc, posix_memalign, memalign, pvalloc, valloc all recorded with this class.
MemalignEntry::MemalignEntry(void* pointer, size_t size, size_t alignment)
- : MallocEntry(pointer, size), alignment_(alignment) {
-}
+ : MallocEntry(pointer, size), alignment_(alignment) {}
std::string MemalignEntry::GetString() const {
- return android::base::StringPrintf("%d: memalign %p %zu %zu\n", tid_, pointer_,
- alignment_, size_);
+ return android::base::StringPrintf("%d: memalign %p %zu %zu\n", tid_, pointer_, alignment_, size_);
}
struct ThreadData {
- ThreadData(RecordData* record_data, ThreadCompleteEntry* entry) : record_data(record_data), entry(entry) {}
+ ThreadData(RecordData* record_data, ThreadCompleteEntry* entry)
+ : record_data(record_data), entry(entry) {}
RecordData* record_data;
ThreadCompleteEntry* entry;
size_t count = 0;
@@ -141,8 +134,8 @@ void RecordData::Dump() {
last_entry_index = num_entries_;
}
- int dump_fd = open(dump_file_.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
- 0755);
+ int dump_fd =
+ open(dump_file_.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, 0755);
if (dump_fd != -1) {
for (size_t i = 0; i < last_entry_index; i++) {
std::string line = entries_[i]->GetString();
@@ -201,7 +194,7 @@ bool RecordData::Initialize(const Config& config) {
}
RecordData::~RecordData() {
- delete [] entries_;
+ delete[] entries_;
pthread_key_delete(key_);
}
diff --git a/libc/malloc_debug/RecordData.h b/libc/malloc_debug/RecordData.h
index 021a2993a..3e5ca029e 100644
--- a/libc/malloc_debug/RecordData.h
+++ b/libc/malloc_debug/RecordData.h
@@ -28,8 +28,8 @@
#pragma once
-#include <stdint.h>
#include <pthread.h>
+#include <stdint.h>
#include <unistd.h>
#include <atomic>
diff --git a/libc/malloc_debug/TrackData.cpp b/libc/malloc_debug/TrackData.cpp
deleted file mode 100644
index 4266aa282..000000000
--- a/libc/malloc_debug/TrackData.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * 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"
-
-TrackData::TrackData(DebugData* debug_data) : OptionData(debug_data) {
-}
-
-void TrackData::GetList(std::vector<const Header*>* list) {
- for (const auto& header : headers_) {
- list->push_back(header);
- }
-
- // Sort by the size of the allocation.
- std::sort(list->begin(), list->end(), [](const Header* a, const Header* b) {
- if (a->size == b->size) return a < b;
- return a->size > b->size;
- });
-}
-
-void TrackData::GetListBySizeThenBacktrace(std::vector<const Header*>* list, size_t* total_memory) {
- if (!(debug_->config().options() & BACKTRACE)) {
- return;
- }
-
- *total_memory = 0;
- for (const auto& header : headers_) {
- list->push_back(header);
- *total_memory += header->real_size();
- }
-
- // Put all zygote allocations first by size and backtrace.
- // Then all zygote child allocation by size and backtrace.
- std::sort(list->begin(), list->end(), [&](const Header* a, const Header* b) {
- if (a->zygote_child_alloc() && !b->zygote_child_alloc()) {
- return false;
- } else if (!a->zygote_child_alloc() && b->zygote_child_alloc()) {
- return true;
- }
- if (a->real_size() != b->real_size()) return a->real_size() < b->real_size();
- // If the size is the same, compare backtrace elements.
- BacktraceHeader* a_back = debug_->GetAllocBacktrace(a);
- BacktraceHeader* b_back = debug_->GetAllocBacktrace(b);
- for (size_t i = 0; i < a_back->num_frames; i++) {
- if (i > b_back->num_frames) {
- // All frames equal up to this point, but a has more frames available.
- return false;
- }
- if (a_back->frames[i] < b_back->frames[i]) {
- return false;
- } else if (a_back->frames[i] > b_back->frames[i]) {
- return true;
- }
- }
- if (a_back->num_frames < b_back->num_frames) {
- // All frames equal up to this point, but b has more frames available.
- return true;
- }
- return false;
- });
-
-}
-
-void TrackData::Add(const Header* header, bool backtrace_found) {
- pthread_mutex_lock(&mutex_);
- if (backtrace_found) {
- total_backtrace_allocs_++;
- }
- headers_.insert(header);
- pthread_mutex_unlock(&mutex_);
-}
-
-void TrackData::Remove(const Header* header, bool backtrace_found) {
- pthread_mutex_lock(&mutex_);
- headers_.erase(header);
- if (backtrace_found) {
- total_backtrace_allocs_--;
- }
- pthread_mutex_unlock(&mutex_);
-}
-
-bool TrackData::Contains(const Header* header) {
- pthread_mutex_lock(&mutex_);
- bool found = headers_.count(header);
- pthread_mutex_unlock(&mutex_);
- return found;
-}
-
-void TrackData::DisplayLeaks() {
- std::vector<const 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(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<const Header*> list;
- GetList(&list);
-
- uint8_t* data = *info;
- size_t num_allocations = 1;
- 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)], &num_allocations, 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
deleted file mode 100644
index 7201c49ec..000000000
--- a/libc/malloc_debug/TrackData.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.
- */
-
-#pragma once
-
-#include <stdint.h>
-#include <pthread.h>
-
-#include <vector>
-#include <unordered_set>
-
-#include <private/bionic_macros.h>
-
-#include "OptionData.h"
-
-// Forward declarations.
-struct Header;
-class Config;
-class DebugData;
-
-class TrackData : public OptionData {
- public:
- explicit TrackData(DebugData* debug_data);
- virtual ~TrackData() = default;
-
- void GetList(std::vector<const Header*>* list);
-
- void GetListBySizeThenBacktrace(std::vector<const Header*>* list, size_t* total_memory);
-
- void Add(const Header* header, bool backtrace_found);
-
- void Remove(const Header* header, bool backtrace_found);
-
- bool Contains(const Header *header);
-
- void GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size,
- size_t* total_memory, size_t* backtrace_size);
-
- void DisplayLeaks();
-
- void PrepareFork() { pthread_mutex_lock(&mutex_); }
- void PostForkParent() { pthread_mutex_unlock(&mutex_); }
- void PostForkChild() { pthread_mutex_init(&mutex_, NULL); }
-
- private:
- pthread_mutex_t mutex_ = PTHREAD_MUTEX_INITIALIZER;
- std::unordered_set<const Header*> headers_;
- size_t total_backtrace_allocs_ = 0;
-
- DISALLOW_COPY_AND_ASSIGN(TrackData);
-};
diff --git a/libc/malloc_debug/backtrace.cpp b/libc/malloc_debug/backtrace.cpp
index 2443ba103..cd8c3340c 100644
--- a/libc/malloc_debug/backtrace.cpp
+++ b/libc/malloc_debug/backtrace.cpp
@@ -38,9 +38,9 @@
#include <demangle.h>
+#include "MapData.h"
#include "backtrace.h"
#include "debug_log.h"
-#include "MapData.h"
#if defined(__LP64__)
#define PAD_PTR "016" PRIxPTR
@@ -69,8 +69,7 @@ void backtrace_startup() {
_Unwind_Backtrace(find_current_map, nullptr);
}
-void backtrace_shutdown() {
-}
+void backtrace_shutdown() {}
struct stack_crawl_state_t {
uintptr_t* frames;
@@ -165,13 +164,13 @@ std::string backtrace_string(const uintptr_t* frames, size_t frame_count) {
char buf[1024];
if (symbol != nullptr) {
- async_safe_format_buffer(
- buf, sizeof(buf), " #%02zd pc %" PAD_PTR " %s%s (%s+%" PRIuPTR ")\n", frame_num,
- rel_pc, soname, offset_buf, demangle(symbol).c_str(), frames[frame_num] - offset);
+ async_safe_format_buffer(buf, sizeof(buf),
+ " #%02zd pc %" PAD_PTR " %s%s (%s+%" PRIuPTR ")\n",
+ frame_num, rel_pc, soname, offset_buf, demangle(symbol).c_str(),
+ frames[frame_num] - offset);
} else {
- async_safe_format_buffer(
- buf, sizeof(buf), " #%02zd pc %" PAD_PTR " %s%s\n", frame_num, rel_pc, soname,
- offset_buf);
+ async_safe_format_buffer(buf, sizeof(buf), " #%02zd pc %" PAD_PTR " %s%s\n",
+ frame_num, rel_pc, soname, offset_buf);
}
str += buf;
}
diff --git a/libc/malloc_debug/debug_log.h b/libc/malloc_debug/debug_log.h
index 54dd221ec..d2257df58 100644
--- a/libc/malloc_debug/debug_log.h
+++ b/libc/malloc_debug/debug_log.h
@@ -33,11 +33,10 @@
// =============================================================================
// log functions
// =============================================================================
-#define debug_log(format, ...) \
- async_safe_format_log(ANDROID_LOG_DEBUG, "malloc_debug", (format), ##__VA_ARGS__ )
-#define error_log(format, ...) \
- async_safe_format_log(ANDROID_LOG_ERROR, "malloc_debug", (format), ##__VA_ARGS__ )
-#define error_log_string(str) \
- async_safe_write_log(ANDROID_LOG_ERROR, "malloc_debug", (str))
-#define info_log(format, ...) \
- async_safe_format_log(ANDROID_LOG_INFO, "malloc_debug", (format), ##__VA_ARGS__ )
+#define debug_log(format, ...) \
+ async_safe_format_log(ANDROID_LOG_DEBUG, "malloc_debug", (format), ##__VA_ARGS__)
+#define error_log(format, ...) \
+ async_safe_format_log(ANDROID_LOG_ERROR, "malloc_debug", (format), ##__VA_ARGS__)
+#define error_log_string(str) async_safe_write_log(ANDROID_LOG_ERROR, "malloc_debug", (str))
+#define info_log(format, ...) \
+ async_safe_format_log(ANDROID_LOG_INFO, "malloc_debug", (format), ##__VA_ARGS__)
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index ecfbd7112..6f841cab5 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -41,9 +41,9 @@
#include <android-base/stringprintf.h>
#include <private/bionic_malloc_dispatch.h>
-#include "backtrace.h"
#include "Config.h"
#include "DebugData.h"
+#include "backtrace.h"
#include "debug_disable.h"
#include "debug_log.h"
#include "malloc_debug.h"
@@ -66,12 +66,11 @@ const MallocDispatch* g_dispatch;
__BEGIN_DECLS
bool debug_initialize(const MallocDispatch* malloc_dispatch, int* malloc_zygote_child,
- const char* options);
+ const char* options);
void debug_finalize();
bool debug_dump_heap(const char* file_name);
-void debug_get_malloc_leak_info(
- uint8_t** info, size_t* overall_size, size_t* info_size, size_t* total_memory,
- size_t* backtrace_size);
+void debug_get_malloc_leak_info(uint8_t** info, size_t* overall_size, size_t* info_size,
+ size_t* total_memory, size_t* backtrace_size);
ssize_t debug_malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
void debug_free_malloc_leak_info(uint8_t* info);
size_t debug_malloc_usable_size(void* pointer);
@@ -85,7 +84,7 @@ struct mallinfo debug_mallinfo();
int debug_mallopt(int param, int value);
int debug_posix_memalign(void** memptr, size_t alignment, size_t size);
int debug_iterate(uintptr_t base, size_t size,
- void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
+ void (*callback)(uintptr_t base, size_t size, void* arg), void* arg);
void debug_malloc_disable();
void debug_malloc_enable();
@@ -99,59 +98,92 @@ __END_DECLS
static void InitAtfork() {
static pthread_once_t atfork_init = PTHREAD_ONCE_INIT;
- pthread_once(&atfork_init, [](){
+ pthread_once(&atfork_init, []() {
pthread_atfork(
- [](){
+ []() {
if (g_debug != nullptr) {
g_debug->PrepareFork();
}
},
- [](){
+ []() {
if (g_debug != nullptr) {
g_debug->PostForkParent();
}
},
- [](){
+ []() {
if (g_debug != nullptr) {
g_debug->PostForkChild();
}
- }
- );
+ });
});
}
-static void LogTagError(const Header* header, const void* pointer, const char* name) {
+static void LogError(const void* pointer, const char* error_str) {
error_log(LOG_DIVIDER);
- if (header->tag == DEBUG_FREE_TAG) {
- error_log("+++ ALLOCATION %p USED AFTER FREE (%s)", pointer, name);
- if (g_debug->config().options() & FREE_TRACK) {
- g_debug->free_track->LogBacktrace(header);
- }
+ error_log("+++ ALLOCATION %p %s", pointer, error_str);
+
+ // If we are tracking already freed pointers, check to see if this is
+ // one so we can print extra information.
+ if (g_debug->config().options() & FREE_TRACK) {
+ PointerData::LogFreeBacktrace(pointer);
+ }
+
+ std::vector<uintptr_t> frames(128);
+ size_t num_frames = backtrace_get(frames.data(), frames.size());
+ if (num_frames == 0) {
+ error_log("Backtrace failed to get any frames.");
} else {
- error_log("+++ ALLOCATION %p HAS INVALID TAG %" PRIx32 " (%s)", pointer, header->tag, name);
+ error_log("Backtrace at time of failure:");
+ backtrace_log(frames.data(), num_frames);
}
- 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 bool VerifyPointer(const void* pointer, const char* function_name) {
+ if (g_debug->HeaderEnabled()) {
+ Header* header = g_debug->GetHeader(pointer);
+ if (header->tag != DEBUG_TAG) {
+ std::string error_str;
+ if (header->tag == DEBUG_FREE_TAG) {
+ error_str = std::string("USED AFTER FREE (") + function_name + ")";
+ } else {
+ error_str = android::base::StringPrintf("HAS INVALID TAG %" PRIx32 " (%s)", header->tag,
+ function_name);
+ }
+ LogError(pointer, error_str.c_str());
+ return false;
+ }
+ }
+
+ if (g_debug->TrackPointers()) {
+ if (!PointerData::Exists(pointer)) {
+ std::string error_str(std::string("UNKNOWN POINTER (") + function_name + ")");
+ LogError(pointer, error_str.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
+static size_t InternalMallocUsableSize(void* pointer) {
+ if (g_debug->HeaderEnabled()) {
+ return g_debug->GetHeader(pointer)->usable_size;
+ } else {
+ return g_dispatch->malloc_usable_size(pointer);
+ }
+}
+
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_child_alloc();
- }
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>(header) - reinterpret_cast<uintptr_t>(orig_pointer);
+ header->usable_size -= g_debug->pointer_offset() + reinterpret_cast<uintptr_t>(header) -
+ reinterpret_cast<uintptr_t>(orig_pointer);
if (g_debug->config().options() & FRONT_GUARD) {
uint8_t* guard = g_debug->GetFrontGuard(header);
@@ -163,30 +195,14 @@ static void* InitHeader(Header* header, void* orig_pointer, size_t size) {
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->ShouldBacktrace()) {
- 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;
- }
- }
-
- if (g_debug->config().options() & TRACK_ALLOCS) {
- g_debug->track->Add(header, backtrace_found);
+ header->usable_size = header->size;
}
return g_debug->GetPointer(header);
}
bool debug_initialize(const MallocDispatch* malloc_dispatch, int* malloc_zygote_child,
- const char* options) {
+ const char* options) {
if (malloc_zygote_child == nullptr || options == nullptr) {
return false;
}
@@ -222,18 +238,19 @@ void debug_finalize() {
}
if (g_debug->config().options() & FREE_TRACK) {
- g_debug->free_track->VerifyAll();
+ PointerData::VerifyAllFreed();
}
if (g_debug->config().options() & LEAK_TRACK) {
- g_debug->track->DisplayLeaks();
+ PointerData::LogLeaks();
}
if ((g_debug->config().options() & BACKTRACE) && g_debug->config().backtrace_dump_on_exit()) {
ScopedDisableDebugCalls disable;
- debug_dump_heap(
- android::base::StringPrintf("%s.%d.exit.txt",
- g_debug->config().backtrace_dump_prefix().c_str(), getpid()).c_str());
+ debug_dump_heap(android::base::StringPrintf("%s.%d.exit.txt",
+ g_debug->config().backtrace_dump_prefix().c_str(),
+ getpid())
+ .c_str());
}
DebugDisableSet(true);
@@ -246,13 +263,13 @@ void debug_finalize() {
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) {
+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) {
+ 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;
}
@@ -264,47 +281,37 @@ void debug_get_malloc_leak_info(uint8_t** info, size_t* overall_size,
*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'.");
+ error_log(
+ "get_malloc_leak_info: Allocations not being tracked, to enable "
+ "set the option 'backtrace'.");
return;
}
- g_debug->track->GetInfo(info, overall_size, info_size, total_memory, backtrace_size);
+ PointerData::GetInfo(info, overall_size, info_size, total_memory, backtrace_size);
}
void debug_free_malloc_leak_info(uint8_t* info) {
g_dispatch->free(info);
}
-static size_t internal_malloc_usable_size(void* pointer) {
- if (g_debug->need_header()) {
- Header* header = g_debug->GetHeader(pointer);
- if (header->tag != DEBUG_TAG) {
- LogTagError(header, pointer, "malloc_usable_size");
- return 0;
- }
-
- return header->usable_size;
- } else {
- return g_dispatch->malloc_usable_size(pointer);
- }
-}
-
size_t debug_malloc_usable_size(void* pointer) {
if (DebugCallsDisabled() || pointer == nullptr) {
return g_dispatch->malloc_usable_size(pointer);
}
ScopedDisableDebugCalls disable;
- return internal_malloc_usable_size(pointer);
+ if (!VerifyPointer(pointer, "malloc_usable_size")) {
+ return 0;
+ }
+
+ return InternalMallocUsableSize(pointer);
}
-static void *internal_malloc(size_t size) {
- if ((g_debug->config().options() & BACKTRACE) && g_debug->backtrace->ShouldDumpAndReset()) {
- debug_dump_heap(
- android::base::StringPrintf("%s.%d.txt",
- g_debug->config().backtrace_dump_prefix().c_str(),
- getpid()).c_str());
+static void* InternalMalloc(size_t size) {
+ if ((g_debug->config().options() & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset()) {
+ debug_dump_heap(android::base::StringPrintf(
+ "%s.%d.txt", g_debug->config().backtrace_dump_prefix().c_str(), getpid())
+ .c_str());
}
if (size == 0) {
@@ -318,15 +325,15 @@ static void *internal_malloc(size_t size) {
return nullptr;
}
- void* pointer;
- if (g_debug->need_header()) {
- if (size > Header::max_size()) {
- errno = ENOMEM;
- return nullptr;
- }
+ if (size > PointerInfoType::MaxSize()) {
+ errno = ENOMEM;
+ return nullptr;
+ }
- Header* header = reinterpret_cast<Header*>(
- g_dispatch->memalign(MINIMUM_ALIGNMENT_BYTES, real_size));
+ void* pointer;
+ if (g_debug->HeaderEnabled()) {
+ Header* header =
+ reinterpret_cast<Header*>(g_dispatch->memalign(MINIMUM_ALIGNMENT_BYTES, real_size));
if (header == nullptr) {
return nullptr;
}
@@ -335,11 +342,17 @@ static void *internal_malloc(size_t size) {
pointer = g_dispatch->malloc(real_size);
}
- if (pointer != nullptr && g_debug->config().options() & FILL_ON_ALLOC) {
- size_t bytes = internal_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);
+ if (pointer != nullptr) {
+ if (g_debug->TrackPointers()) {
+ PointerData::Add(pointer, size);
+ }
+
+ if (g_debug->config().options() & FILL_ON_ALLOC) {
+ size_t bytes = InternalMallocUsableSize(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;
}
@@ -350,7 +363,7 @@ void* debug_malloc(size_t size) {
}
ScopedDisableDebugCalls disable;
- void* pointer = internal_malloc(size);
+ void* pointer = InternalMalloc(size);
if (g_debug->config().options() & RECORD_ALLOCS) {
g_debug->record->AddEntry(new MallocEntry(pointer, size));
@@ -359,23 +372,18 @@ void* debug_malloc(size_t size) {
return pointer;
}
-static void internal_free(void* pointer) {
- if ((g_debug->config().options() & BACKTRACE) && g_debug->backtrace->ShouldDumpAndReset()) {
- debug_dump_heap(
- android::base::StringPrintf("%s.%d.txt",
- g_debug->config().backtrace_dump_prefix().c_str(),
- getpid()).c_str());
+static void InternalFree(void* pointer) {
+ if ((g_debug->config().options() & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset()) {
+ debug_dump_heap(android::base::StringPrintf(
+ "%s.%d.txt", g_debug->config().backtrace_dump_prefix().c_str(), getpid())
+ .c_str());
}
void* free_pointer = pointer;
size_t bytes;
Header* header;
- if (g_debug->need_header()) {
+ if (g_debug->HeaderEnabled()) {
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) {
@@ -389,14 +397,6 @@ static void internal_free(void* pointer) {
}
}
- 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);
- }
header->tag = DEBUG_FREE_TAG;
bytes = header->usable_size;
@@ -410,13 +410,23 @@ static void internal_free(void* pointer) {
memset(pointer, g_debug->config().fill_free_value(), bytes);
}
+ if (g_debug->TrackPointers()) {
+ PointerData::Remove(pointer);
+ }
+
if (g_debug->config().options() & FREE_TRACK) {
// Do not add the allocation until we are done modifying the pointer
// itself. This avoids a race if a lot of threads are all doing
// frees at the same time and we wind up trying to really free this
// pointer from another thread, while still trying to free it in
// this function.
- g_debug->free_track->Add(header);
+ pointer = PointerData::AddFreed(pointer);
+ if (pointer != nullptr) {
+ if (g_debug->HeaderEnabled()) {
+ pointer = g_debug->GetHeader(pointer)->orig_pointer;
+ }
+ g_dispatch->free(pointer);
+ }
} else {
g_dispatch->free(free_pointer);
}
@@ -432,7 +442,11 @@ void debug_free(void* pointer) {
g_debug->record->AddEntry(new FreeEntry(pointer));
}
- internal_free(pointer);
+ if (!VerifyPointer(pointer, "free")) {
+ return;
+ }
+
+ InternalFree(pointer);
}
void* debug_memalign(size_t alignment, size_t bytes) {
@@ -445,13 +459,13 @@ void* debug_memalign(size_t alignment, size_t bytes) {
bytes = 1;
}
- void* pointer;
- if (g_debug->need_header()) {
- if (bytes > Header::max_size()) {
- errno = ENOMEM;
- return nullptr;
- }
+ if (bytes > PointerInfoType::MaxSize()) {
+ errno = ENOMEM;
+ return nullptr;
+ }
+ void* pointer;
+ if (g_debug->HeaderEnabled()) {
// Make the alignment a power of two.
if (!powerof2(alignment)) {
alignment = BIONIC_ROUND_UP_POWER_OF_2(alignment);
@@ -493,15 +507,21 @@ void* debug_memalign(size_t alignment, size_t bytes) {
pointer = g_dispatch->memalign(alignment, real_size);
}
- if (pointer != nullptr && g_debug->config().options() & FILL_ON_ALLOC) {
- size_t bytes = internal_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);
- }
+ if (pointer != nullptr) {
+ if (g_debug->TrackPointers()) {
+ PointerData::Add(pointer, bytes);
+ }
- if (g_debug->config().options() & RECORD_ALLOCS) {
- g_debug->record->AddEntry(new MemalignEntry(pointer, bytes, alignment));
+ if (g_debug->config().options() & FILL_ON_ALLOC) {
+ size_t bytes = InternalMallocUsableSize(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);
+ }
+
+ if (g_debug->config().options() & RECORD_ALLOCS) {
+ g_debug->record->AddEntry(new MemalignEntry(pointer, bytes, alignment));
+ }
}
return pointer;
@@ -514,19 +534,23 @@ void* debug_realloc(void* pointer, size_t bytes) {
ScopedDisableDebugCalls disable;
if (pointer == nullptr) {
- pointer = internal_malloc(bytes);
+ pointer = InternalMalloc(bytes);
if (g_debug->config().options() & RECORD_ALLOCS) {
g_debug->record->AddEntry(new ReallocEntry(pointer, bytes, nullptr));
}
return pointer;
}
+ if (!VerifyPointer(pointer, "realloc")) {
+ return nullptr;
+ }
+
if (bytes == 0) {
if (g_debug->config().options() & RECORD_ALLOCS) {
g_debug->record->AddEntry(new ReallocEntry(nullptr, bytes, pointer));
}
- internal_free(pointer);
+ InternalFree(pointer);
return nullptr;
}
@@ -540,45 +564,45 @@ void* debug_realloc(void* pointer, size_t bytes) {
}
}
+ if (bytes > PointerInfoType::MaxSize()) {
+ 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;
- }
-
+ if (g_debug->HeaderEnabled()) {
// Same size, do nothing.
- if (real_size == header->real_size()) {
- // Do not bother recording, this is essentially a nop.
+ Header* header = g_debug->GetHeader(pointer);
+ if (real_size == header->size) {
+ if (g_debug->TrackPointers()) {
+ // Remove and re-add so that the backtrace is updated.
+ PointerData::Remove(pointer);
+ PointerData::Add(pointer, 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_child_alloc();
- }
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();
+ header->usable_size = header->size;
memset(g_debug->GetRearGuard(header), g_debug->config().rear_guard_value(),
g_debug->config().rear_guard_bytes());
}
- // Do not bother recording, this is essentially a nop.
+ if (g_debug->TrackPointers()) {
+ // Remove and re-add so that the backtrace is updated.
+ PointerData::Remove(pointer);
+ PointerData::Add(pointer, real_size);
+ }
return pointer;
}
// Allocate the new size.
- new_pointer = internal_malloc(bytes);
+ new_pointer = InternalMalloc(bytes);
if (new_pointer == nullptr) {
errno = ENOMEM;
return nullptr;
@@ -586,17 +610,25 @@ void* debug_realloc(void* pointer, size_t bytes) {
prev_size = header->usable_size;
memcpy(new_pointer, pointer, prev_size);
- internal_free(pointer);
+ InternalFree(pointer);
} else {
+ if (g_debug->TrackPointers()) {
+ PointerData::Remove(pointer);
+ }
+
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->TrackPointers()) {
+ PointerData::Add(new_pointer, real_size);
+ }
}
if (g_debug->config().options() & FILL_ON_ALLOC) {
- size_t bytes = internal_malloc_usable_size(new_pointer);
+ size_t bytes = InternalMallocUsableSize(new_pointer);
if (bytes > g_debug->config().fill_on_alloc_bytes()) {
bytes = g_debug->config().fill_on_alloc_bytes();
}
@@ -637,17 +669,16 @@ void* debug_calloc(size_t nmemb, size_t bytes) {
return nullptr;
}
- void* pointer;
- if (g_debug->need_header()) {
- // The above check will guarantee the multiply will not overflow.
- if (size > Header::max_size()) {
- errno = ENOMEM;
- return nullptr;
- }
+ if (real_size > PointerInfoType::MaxSize()) {
+ errno = ENOMEM;
+ return nullptr;
+ }
+ void* pointer;
+ if (g_debug->HeaderEnabled()) {
// Need to guarantee the alignment of the header.
- Header* header = reinterpret_cast<Header*>(
- g_dispatch->memalign(MINIMUM_ALIGNMENT_BYTES, real_size));
+ Header* header =
+ reinterpret_cast<Header*>(g_dispatch->memalign(MINIMUM_ALIGNMENT_BYTES, real_size));
if (header == nullptr) {
return nullptr;
}
@@ -656,9 +687,14 @@ void* debug_calloc(size_t nmemb, size_t bytes) {
} else {
pointer = g_dispatch->calloc(1, real_size);
}
+
if (g_debug->config().options() & RECORD_ALLOCS) {
g_debug->record->AddEntry(new CallocEntry(pointer, bytes, nmemb));
}
+
+ if (pointer != nullptr && g_debug->TrackPointers()) {
+ PointerData::Add(pointer, size);
+ }
return pointer;
}
@@ -695,81 +731,45 @@ int debug_posix_memalign(void** memptr, size_t alignment, size_t size) {
return (*memptr != nullptr) ? 0 : ENOMEM;
}
-int debug_iterate(uintptr_t base, size_t size,
- void (*callback)(uintptr_t base, size_t size, void* arg), void* arg) {
- // Can't allocate, malloc is disabled
- // Manual capture of the arguments to pass to the lambda below as void* arg
- struct iterate_ctx {
- decltype(callback) callback;
- decltype(arg) arg;
- } ctx = { callback, arg };
-
- return g_dispatch->iterate(base, size,
- [](uintptr_t base, size_t size, void* arg) {
- const iterate_ctx* ctx = reinterpret_cast<iterate_ctx*>(arg);
- const void* pointer = reinterpret_cast<void*>(base);
- if (g_debug->need_header()) {
- const Header* header = reinterpret_cast<const Header*>(pointer);
- if (g_debug->config().options() & TRACK_ALLOCS) {
- if (g_debug->track->Contains(header)) {
- // Return just the body of the allocation if we're sure the header exists
- ctx->callback(reinterpret_cast<uintptr_t>(g_debug->GetPointer(header)),
- header->usable_size, ctx->arg);
- return;
- }
- }
- }
- // Fall back to returning the whole allocation
- ctx->callback(base, size, ctx->arg);
- }, &ctx);
+int debug_iterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_t, void*),
+ void* arg) {
+ if (g_debug->TrackPointers()) {
+ // Since malloc is disabled, don't bother acquiring any locks.
+ for (auto it = PointerData::begin(); it != PointerData::end(); ++it) {
+ callback(it->first, InternalMallocUsableSize(reinterpret_cast<void*>(it->first)), arg);
+ }
+ return 0;
+ }
+
+ // An option that adds a header will add pointer tracking, so no need to
+ // check if headers are enabled.
+ return g_dispatch->iterate(base, size, callback, arg);
}
void debug_malloc_disable() {
g_dispatch->malloc_disable();
- if (g_debug->track) {
- g_debug->track->PrepareFork();
+ if (g_debug->pointer) {
+ g_debug->pointer->PrepareFork();
}
}
void debug_malloc_enable() {
- if (g_debug->track) {
- g_debug->track->PostForkParent();
+ if (g_debug->pointer) {
+ g_debug->pointer->PostForkParent();
}
g_dispatch->malloc_enable();
}
-ssize_t debug_malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count) {
+ssize_t debug_malloc_backtrace(void* pointer, uintptr_t* frames, size_t max_frames) {
if (DebugCallsDisabled() || pointer == nullptr) {
return 0;
}
ScopedDisableDebugCalls disable;
- if (g_debug->need_header()) {
- Header* header;
- if (g_debug->config().options() & TRACK_ALLOCS) {
- header = g_debug->GetHeader(pointer);
- if (!g_debug->track->Contains(header)) {
- return 0;
- }
- } else {
- header = reinterpret_cast<Header*>(pointer);
- }
- if (header->tag != DEBUG_TAG) {
- return 0;
- }
- if (g_debug->config().options() & BACKTRACE) {
- BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
- if (back_header->num_frames > 0) {
- if (frame_count > back_header->num_frames) {
- frame_count = back_header->num_frames;
- }
- memcpy(frames, &back_header->frames[0], frame_count * sizeof(uintptr_t));
- return frame_count;
- }
- }
+ if (!(g_debug->config().options() & BACKTRACE)) {
+ return 0;
}
-
- return 0;
+ return PointerData::GetFrames(pointer, frames, max_frames);
}
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
@@ -821,30 +821,7 @@ bool debug_dump_heap(const char* file_name) {
fprintf(fp, "Android Native Heap Dump v1.0\n\n");
- std::vector<const Header*> list;
- size_t total_memory;
- g_debug->track->GetListBySizeThenBacktrace(&list, &total_memory);
- fprintf(fp, "Total memory: %zu\n", total_memory);
- fprintf(fp, "Allocation records: %zd\n", list.size());
- fprintf(fp, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
- fprintf(fp, "\n");
-
- for (const auto& header : list) {
- const BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
- fprintf(fp, "z %d sz %8zu num 1 bt", (header->zygote_child_alloc()) ? 1 : 0,
- header->real_size());
- for (size_t i = 0; i < back_header->num_frames; i++) {
- if (back_header->frames[i] == 0) {
- break;
- }
-#ifdef __LP64__
- fprintf(fp, " %016" PRIxPTR, back_header->frames[i]);
-#else
- fprintf(fp, " %08" PRIxPTR, back_header->frames[i]);
-#endif
- }
- fprintf(fp, "\n");
- }
+ PointerData::DumpLiveToFile(fp);
fprintf(fp, "MAPS\n");
std::string content;
diff --git a/libc/malloc_debug/malloc_debug.h b/libc/malloc_debug/malloc_debug.h
index 18a6c2c7f..ac210e246 100644
--- a/libc/malloc_debug/malloc_debug.h
+++ b/libc/malloc_debug/malloc_debug.h
@@ -37,13 +37,10 @@
// 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)
// uint8_t data (Optional: Front guard, will be a multiple of MINIMUM_ALIGNMENT_BYTES)
// 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.
@@ -52,10 +49,6 @@ struct Header {
void* orig_pointer;
size_t size;
size_t usable_size;
- size_t real_size() const { return size & ~(1U << 31); }
- void set_zygote_child_alloc() { size |= 1U << 31; }
- bool zygote_child_alloc() const { return size & (1U << 31); }
- static size_t max_size() { return (1U << 31) - 1; }
} __attribute__((packed));
struct BacktraceHeader {
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index ee8fe0691..460353583 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -135,6 +135,10 @@ std::string usage_string(
"6 malloc_debug This option only has meaning if the record_allocs options has been specified.\n"
"6 malloc_debug This is the name of the file to which recording information will be dumped.\n"
"6 malloc_debug The default is /data/local/tmp/record_allocs.txt.\n"
+ "6 malloc_debug \n"
+ "6 malloc_debug verify_pointers\n"
+ "6 malloc_debug A lightweight way to verify that free/malloc_usable_size/realloc\n"
+ "6 malloc_debug are passed valid pointers.\n"
);
TEST_F(MallocDebugConfigTest, unknown_option) {
@@ -250,15 +254,15 @@ TEST_F(MallocDebugConfigTest, multiple_options) {
TEST_F(MallocDebugConfigTest, front_guard) {
ASSERT_TRUE(InitConfig("front_guard=48")) << getFakeLogPrint();
- ASSERT_EQ(FRONT_GUARD, config->options());
+ ASSERT_EQ(FRONT_GUARD | TRACK_ALLOCS, config->options());
ASSERT_EQ(48U, config->front_guard_bytes());
ASSERT_TRUE(InitConfig("front_guard")) << getFakeLogPrint();
- ASSERT_EQ(FRONT_GUARD, config->options());
+ ASSERT_EQ(FRONT_GUARD | TRACK_ALLOCS, config->options());
ASSERT_EQ(32U, config->front_guard_bytes());
ASSERT_TRUE(InitConfig("front_guard=39")) << getFakeLogPrint();
- ASSERT_EQ(FRONT_GUARD, config->options());
+ ASSERT_EQ(FRONT_GUARD | TRACK_ALLOCS, config->options());
#if defined(__LP64__)
ASSERT_EQ(48U, config->front_guard_bytes());
#else
@@ -266,7 +270,7 @@ TEST_F(MallocDebugConfigTest, front_guard) {
#endif
ASSERT_TRUE(InitConfig("front_guard=41")) << getFakeLogPrint();
- ASSERT_EQ(FRONT_GUARD, config->options());
+ ASSERT_EQ(FRONT_GUARD | TRACK_ALLOCS, config->options());
ASSERT_EQ(48U, config->front_guard_bytes());
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -275,11 +279,11 @@ TEST_F(MallocDebugConfigTest, front_guard) {
TEST_F(MallocDebugConfigTest, rear_guard) {
ASSERT_TRUE(InitConfig("rear_guard=50")) << getFakeLogPrint();
- ASSERT_EQ(REAR_GUARD, config->options());
+ ASSERT_EQ(REAR_GUARD | TRACK_ALLOCS, config->options());
ASSERT_EQ(50U, config->rear_guard_bytes());
ASSERT_TRUE(InitConfig("rear_guard")) << getFakeLogPrint();
- ASSERT_EQ(REAR_GUARD, config->options());
+ ASSERT_EQ(REAR_GUARD | TRACK_ALLOCS, config->options());
ASSERT_EQ(32U, config->rear_guard_bytes());
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -288,12 +292,12 @@ TEST_F(MallocDebugConfigTest, rear_guard) {
TEST_F(MallocDebugConfigTest, guard) {
ASSERT_TRUE(InitConfig("guard=32")) << getFakeLogPrint();
- ASSERT_EQ(FRONT_GUARD | REAR_GUARD, config->options());
+ ASSERT_EQ(FRONT_GUARD | REAR_GUARD | TRACK_ALLOCS, config->options());
ASSERT_EQ(32U, config->front_guard_bytes());
ASSERT_EQ(32U, config->rear_guard_bytes());
ASSERT_TRUE(InitConfig("guard")) << getFakeLogPrint();
- ASSERT_EQ(FRONT_GUARD | REAR_GUARD, config->options());
+ ASSERT_EQ(FRONT_GUARD | REAR_GUARD | TRACK_ALLOCS, config->options());
ASSERT_EQ(32U, config->front_guard_bytes());
ASSERT_EQ(32U, config->rear_guard_bytes());
@@ -465,13 +469,13 @@ TEST_F(MallocDebugConfigTest, expand_alloc) {
TEST_F(MallocDebugConfigTest, free_track) {
ASSERT_TRUE(InitConfig("free_track=1234")) << getFakeLogPrint();
- ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+ ASSERT_EQ(FREE_TRACK | FILL_ON_FREE | TRACK_ALLOCS, config->options());
ASSERT_EQ(1234U, config->free_track_allocations());
ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes());
ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
ASSERT_TRUE(InitConfig("free_track")) << getFakeLogPrint();
- ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+ ASSERT_EQ(FREE_TRACK | FILL_ON_FREE | TRACK_ALLOCS, config->options());
ASSERT_EQ(100U, config->free_track_allocations());
ASSERT_EQ(SIZE_MAX, config->fill_on_free_bytes());
ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
@@ -482,20 +486,20 @@ TEST_F(MallocDebugConfigTest, free_track) {
TEST_F(MallocDebugConfigTest, free_track_and_fill_on_free) {
ASSERT_TRUE(InitConfig("free_track=1234 fill_on_free=32")) << getFakeLogPrint();
- ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+ ASSERT_EQ(FREE_TRACK | FILL_ON_FREE | TRACK_ALLOCS, config->options());
ASSERT_EQ(1234U, config->free_track_allocations());
ASSERT_EQ(32U, config->fill_on_free_bytes());
ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
ASSERT_TRUE(InitConfig("free_track fill_on_free=60")) << getFakeLogPrint();
- ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+ ASSERT_EQ(FREE_TRACK | FILL_ON_FREE | TRACK_ALLOCS, config->options());
ASSERT_EQ(100U, config->free_track_allocations());
ASSERT_EQ(60U, config->fill_on_free_bytes());
ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
// Now reverse the arguments.
ASSERT_TRUE(InitConfig("fill_on_free=32 free_track=1234")) << getFakeLogPrint();
- ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+ ASSERT_EQ(FREE_TRACK | FILL_ON_FREE | TRACK_ALLOCS, config->options());
ASSERT_EQ(1234U, config->free_track_allocations());
ASSERT_EQ(32U, config->fill_on_free_bytes());
ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
@@ -530,11 +534,11 @@ TEST_F(MallocDebugConfigTest, free_track_backtrace_num_frames_zero) {
TEST_F(MallocDebugConfigTest, free_track_backtrace_num_frames_and_free_track) {
ASSERT_TRUE(InitConfig("free_track free_track_backtrace_num_frames=123")) << getFakeLogPrint();
- ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+ ASSERT_EQ(FREE_TRACK | FILL_ON_FREE | TRACK_ALLOCS, config->options());
ASSERT_EQ(123U, config->free_track_backtrace_num_frames());
ASSERT_TRUE(InitConfig("free_track free_track_backtrace_num_frames")) << getFakeLogPrint();
- ASSERT_EQ(FREE_TRACK | FILL_ON_FREE, config->options());
+ ASSERT_EQ(FREE_TRACK | FILL_ON_FREE | TRACK_ALLOCS, config->options());
ASSERT_EQ(16U, config->free_track_backtrace_num_frames());
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -559,6 +563,24 @@ TEST_F(MallocDebugConfigTest, leak_track_fail) {
ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
}
+TEST_F(MallocDebugConfigTest, verify_pointers) {
+ ASSERT_TRUE(InitConfig("verify_pointers")) << getFakeLogPrint();
+ ASSERT_EQ(TRACK_ALLOCS, config->options());
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, verify_pointers_fail) {
+ ASSERT_FALSE(InitConfig("verify_pointers=200")) << getFakeLogPrint();
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg(
+ "6 malloc_debug malloc_testing: value set for option 'verify_pointers' "
+ "which does not take a value\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
TEST_F(MallocDebugConfigTest, record_allocs) {
ASSERT_TRUE(InitConfig("record_allocs=1234")) << getFakeLogPrint();
ASSERT_EQ(RECORD_ALLOCS, config->options());
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 0e4a7d85f..8b2818872 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -72,15 +72,8 @@ __END_DECLS
constexpr char DIVIDER[] =
"6 malloc_debug *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n";
-constexpr uint32_t BACKTRACE_HEADER = 0x1;
-
-static size_t get_tag_offset(uint32_t flags = 0, size_t backtrace_frames = 0) {
- size_t offset = __BIONIC_ALIGN(sizeof(Header), MINIMUM_ALIGNMENT_BYTES);
- if (flags & BACKTRACE_HEADER) {
- offset += __BIONIC_ALIGN(sizeof(BacktraceHeader) + sizeof(uintptr_t) * backtrace_frames,
- MINIMUM_ALIGNMENT_BYTES);
- }
- return offset;
+static size_t get_tag_offset() {
+ return __BIONIC_ALIGN(sizeof(Header), MINIMUM_ALIGNMENT_BYTES);
}
static constexpr const char RECORD_ALLOCS_FILE[] = "/data/local/tmp/record_allocs.txt";
@@ -111,6 +104,10 @@ class MallocDebugTest : public ::testing::Test {
void BacktraceDumpOnSignal(bool trigger_with_alloc);
+ static size_t GetInfoEntrySize(size_t max_frames) {
+ return 2 * sizeof(size_t) + max_frames * sizeof(uintptr_t);
+ }
+
bool initialized;
int zygote;
@@ -140,6 +137,16 @@ MallocDispatch MallocDebugTest::dispatch = {
aligned_alloc,
};
+std::string ShowDiffs(uint8_t* a, uint8_t* b, size_t size) {
+ std::string diff;
+ for (size_t i = 0; i < size; i++) {
+ if (a[i] != b[i]) {
+ diff += android::base::StringPrintf("Byte %zu: 0x%x 0x%x\n", i, a[i], b[i]);
+ }
+ }
+ return diff;
+}
+
void VerifyAllocCalls(bool backtrace_enabled) {
size_t alloc_size = 1024;
@@ -335,7 +342,8 @@ TEST_F(MallocDebugTest, front_guard) {
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);
+ ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0)
+ << ShowDiffs(buffer.data(), &pointer[-buffer.size()], buffer.size());
memset(pointer, 0xff, 100);
debug_free(pointer);
@@ -343,7 +351,8 @@ TEST_F(MallocDebugTest, front_guard) {
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);
+ ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0)
+ << ShowDiffs(buffer.data(), &pointer[-buffer.size()], buffer.size());
size_t alignment_mask = alignment - 1;
if (!powerof2(alignment)) {
alignment_mask = BIONIC_ROUND_UP_POWER_OF_2(alignment) - 1;
@@ -355,7 +364,8 @@ TEST_F(MallocDebugTest, front_guard) {
pointer = reinterpret_cast<uint8_t*>(debug_calloc(1, 100));
ASSERT_TRUE(pointer != nullptr);
- ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0);
+ ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0)
+ << ShowDiffs(buffer.data(), &pointer[-buffer.size()], buffer.size());
for (size_t i = 0; i < 100; i++) {
ASSERT_EQ(0, pointer[i]) << "debug_calloc non-zero byte at " << i;
}
@@ -363,10 +373,12 @@ TEST_F(MallocDebugTest, front_guard) {
pointer = reinterpret_cast<uint8_t*>(debug_realloc(nullptr, 100));
ASSERT_TRUE(pointer != nullptr);
- ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0);
+ ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0)
+ << ShowDiffs(buffer.data(), &pointer[-buffer.size()], buffer.size());
memset(pointer, 0xff, 100);
pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 200));
- ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0);
+ ASSERT_TRUE(memcmp(buffer.data(), &pointer[-buffer.size()], buffer.size()) == 0)
+ << ShowDiffs(buffer.data(), &pointer[-buffer.size()], buffer.size());
memset(pointer, 0xff, 200);
pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 0));
ASSERT_TRUE(pointer == nullptr);
@@ -427,7 +439,8 @@ TEST_F(MallocDebugTest, rear_guard) {
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);
+ ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0)
+ << ShowDiffs(buffer.data(), &pointer[100], buffer.size());
memset(pointer, 0xff, 100);
debug_free(pointer);
@@ -436,7 +449,8 @@ TEST_F(MallocDebugTest, rear_guard) {
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);
+ ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0)
+ << ShowDiffs(buffer.data(), &pointer[100], buffer.size());
size_t alignment_mask = alignment - 1;
if (!powerof2(alignment)) {
alignment_mask = BIONIC_ROUND_UP_POWER_OF_2(alignment) - 1;
@@ -450,7 +464,8 @@ TEST_F(MallocDebugTest, rear_guard) {
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);
+ ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0)
+ << ShowDiffs(buffer.data(), &pointer[100], buffer.size());
for (size_t i = 0; i < 100; i++) {
ASSERT_EQ(0, pointer[i]) << "debug_calloc non-zero byte at " << i;
}
@@ -458,10 +473,12 @@ TEST_F(MallocDebugTest, rear_guard) {
pointer = reinterpret_cast<uint8_t*>(debug_realloc(nullptr, 100));
ASSERT_TRUE(pointer != nullptr);
- ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0);
+ ASSERT_TRUE(memcmp(buffer.data(), &pointer[100], buffer.size()) == 0)
+ << ShowDiffs(buffer.data(), &pointer[100], buffer.size());
memset(pointer, 0xff, 100);
pointer = reinterpret_cast<uint8_t*>(debug_realloc(pointer, 200));
- ASSERT_TRUE(memcmp(buffer.data(), &pointer[200], buffer.size()) == 0);
+ ASSERT_TRUE(memcmp(buffer.data(), &pointer[200], buffer.size()) == 0)
+ << ShowDiffs(buffer.data(), &pointer[200], buffer.size());
for (size_t i = 0; i < 100; i++) {
ASSERT_EQ(0xff, pointer[i]) << "debug_realloc not copied byte at " << i;
}
@@ -829,7 +846,7 @@ TEST_F(MallocDebugTest, free_track_use_after_free_finalize) {
}
TEST_F(MallocDebugTest, free_track_use_after_free_with_backtrace) {
- Init("free_track=100");
+ Init("free_track=100 rear_guard");
// Free backtrace.
backtrace_fake_add(std::vector<uintptr_t> {0xfa, 0xeb, 0xdc});
@@ -860,7 +877,7 @@ TEST_F(MallocDebugTest, free_track_use_after_free_with_backtrace) {
}
TEST_F(MallocDebugTest, free_track_use_after_free_call_realloc) {
- Init("free_track=100");
+ Init("free_track=100 rear_guard");
// Free backtrace.
backtrace_fake_add(std::vector<uintptr_t> {0xfa, 0xeb, 0xdc});
@@ -894,7 +911,7 @@ TEST_F(MallocDebugTest, free_track_use_after_free_call_realloc) {
}
TEST_F(MallocDebugTest, free_track_use_after_free_call_free) {
- Init("free_track=100");
+ Init("free_track=100 rear_guard");
// Free backtrace.
backtrace_fake_add(std::vector<uintptr_t> {0xfa, 0xeb, 0xdc});
@@ -926,7 +943,7 @@ TEST_F(MallocDebugTest, free_track_use_after_free_call_free) {
}
TEST_F(MallocDebugTest, free_track_header_tag_corrupted) {
- Init("free_track=100 free_track_backtrace_num_frames=0");
+ Init("free_track=100 free_track_backtrace_num_frames=0 rear_guard");
uint8_t* pointer = reinterpret_cast<uint8_t*>(debug_malloc(100));
ASSERT_TRUE(pointer != nullptr);
@@ -1053,7 +1070,7 @@ 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);
+ size_t individual_size = GetInfoEntrySize(16);
std::vector<uint8_t> expected_info(individual_size);
memset(expected_info.data(), 0, individual_size);
@@ -1082,7 +1099,8 @@ TEST_F(MallocDebugTest, get_malloc_leak_info_single) {
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);
+ ASSERT_TRUE(memcmp(expected_info.data(), info, overall_size) == 0)
+ << ShowDiffs(expected_info.data(), info, overall_size);
debug_free_malloc_leak_info(info);
@@ -1099,7 +1117,7 @@ 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);
+ size_t individual_size = GetInfoEntrySize(16);
std::vector<uint8_t> expected_info(individual_size * 3);
memset(expected_info.data(), 0, individual_size * 3);
@@ -1162,7 +1180,8 @@ TEST_F(MallocDebugTest, get_malloc_leak_info_multi) {
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);
+ ASSERT_TRUE(memcmp(expected_info.data(), info, overall_size) == 0)
+ << ShowDiffs(expected_info.data(), info, overall_size);
debug_free_malloc_leak_info(info);
@@ -1177,52 +1196,13 @@ TEST_F(MallocDebugTest, get_malloc_leak_info_multi) {
ASSERT_STREQ(expected_log.c_str(), 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_allocations = 1;
- 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_allocations = 1;
- 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);
+TEST_F(MallocDebugTest, get_malloc_backtrace_with_header) {
+ Init("backtrace=16 guard");
- pointers[2] = reinterpret_cast<uint8_t*>(debug_malloc(10000));
- ASSERT_TRUE(pointers[2] != nullptr);
- memset(pointers[2], 0, 10000);
+ void* pointer = debug_malloc(100);
+ ASSERT_TRUE(pointer != nullptr);
+ memset(pointer, 0, 100);
+ EXPECT_EQ(100U, debug_malloc_usable_size(pointer));
uint8_t* info;
size_t overall_size;
@@ -1231,18 +1211,18 @@ TEST_F(MallocDebugTest, get_malloc_leak_info_multi_skip_empty_backtrace) {
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);
-
+ EXPECT_TRUE(info != nullptr);
+ EXPECT_EQ(GetInfoEntrySize(16), overall_size);
+ EXPECT_EQ(GetInfoEntrySize(16), info_size);
+ EXPECT_EQ(100U, total_memory);
+ EXPECT_EQ(16U, backtrace_size);
debug_free_malloc_leak_info(info);
- debug_free(pointers[0]);
- debug_free(pointers[1]);
- debug_free(pointers[2]);
+ debug_free(pointer);
+
+ // There should be no pointers that have leaked.
+ debug_finalize();
+ initialized = false;
ASSERT_STREQ("", getFakeLogBuf().c_str());
std::string expected_log = android::base::StringPrintf(
@@ -1338,19 +1318,19 @@ void MallocDebugTest::BacktraceDumpOnSignal(bool trigger_with_alloc) {
"Backtrace size: 4\n"
"\n"
#if defined(__LP64__)
- "z 0 sz 5 num 1 bt 000000000000a300 000000000000b300\n"
- "z 0 sz 10 num 1 bt 000000000000a000 000000000000b000\n"
"z 0 sz 50 num 1 bt 000000000000a100 000000000000b200\n"
- "z 1 sz 40 num 1 bt 0000000000000300 0000000000000400\n"
- "z 1 sz 100 num 1 bt 0000000000000100 0000000000000200\n"
+ "z 0 sz 10 num 1 bt 000000000000a000 000000000000b000\n"
+ "z 0 sz 5 num 1 bt 000000000000a300 000000000000b300\n"
"z 1 sz 200 num 1 bt 0000000000000500 0000000000000600\n"
+ "z 1 sz 100 num 1 bt 0000000000000100 0000000000000200\n"
+ "z 1 sz 40 num 1 bt 0000000000000300 0000000000000400\n"
#else
- "z 0 sz 5 num 1 bt 0000a300 0000b300\n"
- "z 0 sz 10 num 1 bt 0000a000 0000b000\n"
"z 0 sz 50 num 1 bt 0000a100 0000b200\n"
- "z 1 sz 40 num 1 bt 00000300 00000400\n"
- "z 1 sz 100 num 1 bt 00000100 00000200\n"
+ "z 0 sz 10 num 1 bt 0000a000 0000b000\n"
+ "z 0 sz 5 num 1 bt 0000a300 0000b300\n"
"z 1 sz 200 num 1 bt 00000500 00000600\n"
+ "z 1 sz 100 num 1 bt 00000100 00000200\n"
+ "z 1 sz 40 num 1 bt 00000300 00000400\n"
#endif
"MAPS\n"
"MAP_DATA\n"
@@ -1410,13 +1390,64 @@ TEST_F(MallocDebugTest, backtrace_dump_on_exit) {
"Backtrace size: 4\n"
"\n"
#if defined(__LP64__)
- "z 0 sz 300 num 1 bt 0000000000000100 0000000000000200\n"
- "z 0 sz 400 num 1 bt 000000000000a000 000000000000b000\n"
"z 0 sz 500 num 1 bt 000000000000a000 000000000000b000 000000000000c000\n"
+ "z 0 sz 400 num 1 bt 000000000000a000 000000000000b000\n"
+ "z 0 sz 300 num 1 bt 0000000000000100 0000000000000200\n"
#else
- "z 0 sz 300 num 1 bt 00000100 00000200\n"
- "z 0 sz 400 num 1 bt 0000a000 0000b000\n"
"z 0 sz 500 num 1 bt 0000a000 0000b000 0000c000\n"
+ "z 0 sz 400 num 1 bt 0000a000 0000b000\n"
+ "z 0 sz 300 num 1 bt 00000100 00000200\n"
+#endif
+ "MAPS\n"
+ "MAP_DATA\n"
+ "END\n\n";
+ ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, backtrace_dump_on_exit_shared_backtrace) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ Init("backtrace=4 backtrace_dump_on_exit");
+ backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200});
+ backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000, 0xc000});
+ backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200});
+
+ std::vector<void*> pointers;
+ pointers.push_back(debug_malloc(300));
+ pointers.push_back(debug_malloc(400));
+ pointers.push_back(debug_malloc(300));
+
+ // Call the exit function manually.
+ debug_finalize();
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+ ASSERT_EQ(pid, TEMP_FAILURE_RETRY(waitpid(pid, nullptr, 0)));
+
+ // Read all of the contents.
+ std::string actual;
+ std::string name = android::base::StringPrintf("%s.%d.exit.txt", BACKTRACE_DUMP_PREFIX, pid);
+ ASSERT_TRUE(android::base::ReadFileToString(name, &actual));
+ ASSERT_EQ(0, unlink(name.c_str()));
+
+ std::string sanitized(SanitizeHeapData(actual));
+
+ std::string expected =
+ "Android Native Heap Dump v1.0\n"
+ "\n"
+ "Total memory: 1000\n"
+ "Allocation records: 2\n"
+ "Backtrace size: 4\n"
+ "\n"
+#if defined(__LP64__)
+ "z 0 sz 400 num 1 bt 000000000000a000 000000000000b000 000000000000c000\n"
+ "z 0 sz 300 num 2 bt 0000000000000100 0000000000000200\n"
+#else
+ "z 0 sz 400 num 1 bt 0000a000 0000b000 0000c000\n"
+ "z 0 sz 300 num 2 bt 00000100 00000200\n"
#endif
"MAPS\n"
"MAP_DATA\n"
@@ -1427,6 +1458,7 @@ TEST_F(MallocDebugTest, backtrace_dump_on_exit) {
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
+
TEST_F(MallocDebugTest, realloc_usable_size) {
Init("front_guard");
@@ -1453,7 +1485,7 @@ TEST_F(MallocDebugTest, realloc_usable_size) {
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);
+ size_t individual_size = GetInfoEntrySize(20);
backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200, 0x300, 0x400});
@@ -1532,6 +1564,198 @@ TEST_F(MallocDebugTest, backtrace_enable_on_signal) {
ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
}
+TEST_F(MallocDebugTest, backtrace_same_stack) {
+ Init("backtrace=4");
+
+ size_t individual_size = GetInfoEntrySize(4);
+
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
+
+ void* pointers[4];
+ pointers[0] = debug_malloc(10);
+ ASSERT_TRUE(pointers[0] != nullptr);
+ pointers[1] = debug_malloc(10);
+ ASSERT_TRUE(pointers[1] != nullptr);
+ pointers[2] = debug_malloc(10);
+ ASSERT_TRUE(pointers[2] != nullptr);
+ pointers[3] = debug_malloc(100);
+ ASSERT_TRUE(pointers[3] != 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(individual_size * 2, overall_size);
+ ASSERT_EQ(individual_size, info_size);
+ EXPECT_EQ(130U, total_memory);
+ EXPECT_EQ(4U, backtrace_size);
+ EXPECT_EQ(100U, *reinterpret_cast<size_t*>(&info[0]));
+ EXPECT_EQ(1U, *reinterpret_cast<size_t*>(&info[sizeof(size_t)]));
+ uintptr_t* ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t)]);
+ EXPECT_EQ(0xbc000U, ips[0]);
+ EXPECT_EQ(0xecd00U, ips[1]);
+ EXPECT_EQ(0x12000U, ips[2]);
+
+ EXPECT_EQ(10U, *reinterpret_cast<size_t*>(&info[individual_size]));
+ EXPECT_EQ(3U, *reinterpret_cast<size_t*>(&info[sizeof(size_t) + individual_size]));
+ ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t) + individual_size]);
+ EXPECT_EQ(0xbc000U, ips[0]);
+ EXPECT_EQ(0xecd00U, ips[1]);
+ EXPECT_EQ(0x12000U, ips[2]);
+
+ debug_free_malloc_leak_info(info);
+
+ debug_free(pointers[0]);
+ debug_free(pointers[1]);
+ debug_free(pointers[2]);
+ debug_free(pointers[3]);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string expected_log = android::base::StringPrintf(
+ "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+ SIGRTMAX - 17, getpid());
+ ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, backtrace_same_stack_zygote) {
+ Init("backtrace=4");
+
+ size_t individual_size = GetInfoEntrySize(4);
+
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000});
+
+ zygote = 1;
+
+ void* pointers[4];
+ pointers[0] = debug_malloc(100);
+ ASSERT_TRUE(pointers[0] != nullptr);
+ pointers[1] = debug_malloc(100);
+ ASSERT_TRUE(pointers[1] != nullptr);
+ pointers[2] = debug_malloc(100);
+ ASSERT_TRUE(pointers[2] != nullptr);
+ pointers[3] = debug_malloc(100);
+ ASSERT_TRUE(pointers[3] != 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(individual_size * 2, overall_size);
+ EXPECT_EQ(individual_size, info_size);
+ EXPECT_EQ(400U, total_memory);
+ EXPECT_EQ(4U, backtrace_size);
+
+ EXPECT_EQ(0x80000064U, *reinterpret_cast<size_t*>(&info[0]));
+ EXPECT_EQ(3U, *reinterpret_cast<size_t*>(&info[sizeof(size_t)]));
+ uintptr_t* ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t)]);
+ EXPECT_EQ(0xbc000U, ips[0]);
+ EXPECT_EQ(0xecd00U, ips[1]);
+ EXPECT_EQ(0x12000U, ips[2]);
+
+ EXPECT_EQ(0x80000064U, *reinterpret_cast<size_t*>(&info[individual_size]));
+ EXPECT_EQ(1U, *reinterpret_cast<size_t*>(&info[sizeof(size_t) + individual_size]));
+ ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t) + individual_size]);
+ EXPECT_EQ(0xbc000U, ips[0]);
+ EXPECT_EQ(0U, ips[1]);
+
+ debug_free_malloc_leak_info(info);
+
+ debug_free(pointers[0]);
+ debug_free(pointers[1]);
+ debug_free(pointers[2]);
+ debug_free(pointers[3]);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string expected_log = android::base::StringPrintf(
+ "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+ SIGRTMAX - 17, getpid());
+ ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, backtrace_same_stack_mix_zygote) {
+ Init("backtrace=4");
+
+ size_t individual_size = GetInfoEntrySize(4);
+
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000, 0xecd00, 0x12000});
+ backtrace_fake_add(std::vector<uintptr_t> {0xbc000});
+
+ zygote = 1;
+ void* pointers[4];
+ pointers[0] = debug_malloc(40);
+ ASSERT_TRUE(pointers[0] != nullptr);
+ pointers[1] = debug_malloc(40);
+ ASSERT_TRUE(pointers[1] != nullptr);
+
+ zygote = 0;
+ pointers[2] = debug_malloc(40);
+ ASSERT_TRUE(pointers[2] != nullptr);
+ pointers[3] = debug_malloc(100);
+ ASSERT_TRUE(pointers[3] != 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(individual_size * 3, overall_size);
+ ASSERT_EQ(individual_size, info_size);
+ EXPECT_EQ(220U, total_memory);
+ EXPECT_EQ(4U, backtrace_size);
+
+ EXPECT_EQ(100U, *reinterpret_cast<size_t*>(&info[0]));
+ EXPECT_EQ(1U, *reinterpret_cast<size_t*>(&info[sizeof(size_t)]));
+ uintptr_t* ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t)]);
+ EXPECT_EQ(0xbc000U, ips[0]);
+ EXPECT_EQ(0U, ips[1]);
+
+ EXPECT_EQ(40U, *reinterpret_cast<size_t*>(&info[individual_size]));
+ EXPECT_EQ(1U, *reinterpret_cast<size_t*>(&info[sizeof(size_t) + individual_size]));
+ ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t) + individual_size]);
+ EXPECT_EQ(0xbc000U, ips[0]);
+ EXPECT_EQ(0xecd00U, ips[1]);
+ EXPECT_EQ(0x12000U, ips[2]);
+
+ EXPECT_EQ(0x80000028U, *reinterpret_cast<size_t*>(&info[2 * individual_size]));
+ EXPECT_EQ(2U, *reinterpret_cast<size_t*>(&info[sizeof(size_t) + 2 * individual_size]));
+ ips = reinterpret_cast<uintptr_t*>(&info[2 * sizeof(size_t) + 2 * individual_size]);
+ EXPECT_EQ(0xbc000U, ips[0]);
+ EXPECT_EQ(0xecd00U, ips[1]);
+ EXPECT_EQ(0x12000U, ips[2]);
+
+ debug_free_malloc_leak_info(info);
+
+ debug_free(pointers[0]);
+ debug_free(pointers[1]);
+ debug_free(pointers[2]);
+ debug_free(pointers[3]);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string expected_log = android::base::StringPrintf(
+ "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+ SIGRTMAX - 17, getpid());
+ ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
TEST_F(MallocDebugTest, overflow) {
Init("guard fill_on_free");
@@ -1601,7 +1825,8 @@ static void VerifyZygoteSet(size_t memory_bytes) {
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);
+ ASSERT_TRUE(memcmp(info, expected_info.data(), expected_info_size) == 0)
+ << ShowDiffs(info, expected_info.data(), expected_info_size);
debug_free_malloc_leak_info(info);
}
@@ -1619,6 +1844,7 @@ TEST_F(MallocDebugTest, zygote_set) {
ASSERT_EQ(100U, debug_malloc_usable_size(pointer));
memset(pointer, 0, 100);
VerifyZygoteSet(100);
+ ASSERT_FALSE(HasFatalFailure());
debug_free(pointer);
backtrace_fake_add(std::vector<uintptr_t> {0x1});
@@ -1626,6 +1852,7 @@ TEST_F(MallocDebugTest, zygote_set) {
ASSERT_TRUE(pointer != nullptr);
ASSERT_EQ(200U, debug_malloc_usable_size(pointer));
VerifyZygoteSet(200);
+ ASSERT_FALSE(HasFatalFailure());
debug_free(pointer);
backtrace_fake_add(std::vector<uintptr_t> {0x1});
@@ -1634,6 +1861,7 @@ TEST_F(MallocDebugTest, zygote_set) {
ASSERT_EQ(300U, debug_malloc_usable_size(pointer));
memset(pointer, 0, 300);
VerifyZygoteSet(300);
+ ASSERT_FALSE(HasFatalFailure());
debug_free(pointer);
backtrace_fake_add(std::vector<uintptr_t> {0x1});
@@ -1642,12 +1870,14 @@ TEST_F(MallocDebugTest, zygote_set) {
ASSERT_EQ(500U, debug_malloc_usable_size(pointer));
memset(pointer, 0, 500);
VerifyZygoteSet(500);
+ ASSERT_FALSE(HasFatalFailure());
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);
+ ASSERT_FALSE(HasFatalFailure());
debug_free(pointer);
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1984,3 +2214,62 @@ TEST_F(MallocDebugTest, record_allocs_file_name_fail) {
RECORD_ALLOCS_FILE);
ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
}
+
+TEST_F(MallocDebugTest, verify_pointers) {
+ Init("verify_pointers");
+
+ void* pointer = debug_malloc(10);
+ memset(pointer, 0, 10);
+ debug_free(pointer);
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+
+ debug_free(pointer);
+ ASSERT_EQ(0U, debug_malloc_usable_size(pointer));
+ ASSERT_EQ(nullptr, debug_realloc(pointer, 1000));
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string free_pointer_str(
+ android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p UNKNOWN POINTER (free)\n",
+ pointer));
+ std::string usable_pointer_str(
+ android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p UNKNOWN POINTER (malloc_usable_size)\n",
+ pointer));
+ std::string realloc_pointer_str(
+ android::base::StringPrintf("6 malloc_debug +++ ALLOCATION %p UNKNOWN POINTER (realloc)\n",
+ pointer));
+ std::string backtrace_str("6 malloc_debug Backtrace failed to get any frames.\n");
+
+ std::string expected_log(DIVIDER + free_pointer_str + backtrace_str + DIVIDER);
+ expected_log += DIVIDER + usable_pointer_str + backtrace_str + DIVIDER;
+ expected_log += DIVIDER + realloc_pointer_str + backtrace_str + DIVIDER;
+ ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+
+ resetLogs();
+
+ backtrace_fake_add(std::vector<uintptr_t> {0x100, 0x200});
+ backtrace_fake_add(std::vector<uintptr_t> {0x300, 0x400});
+ backtrace_fake_add(std::vector<uintptr_t> {0x500, 0x600});
+ debug_free(pointer);
+ ASSERT_EQ(0U, debug_malloc_usable_size(pointer));
+ ASSERT_EQ(nullptr, debug_realloc(pointer, 1000));
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ expected_log = DIVIDER + free_pointer_str;
+ 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 += DIVIDER;
+ expected_log += DIVIDER + usable_pointer_str;
+ expected_log += "6 malloc_debug Backtrace at time of failure:\n";
+ expected_log += "6 malloc_debug #00 pc 0x300\n";
+ expected_log += "6 malloc_debug #01 pc 0x400\n";
+ expected_log += DIVIDER;
+ expected_log += DIVIDER + realloc_pointer_str;
+ expected_log += "6 malloc_debug Backtrace at time of failure:\n";
+ expected_log += "6 malloc_debug #00 pc 0x500\n";
+ expected_log += "6 malloc_debug #01 pc 0x600\n";
+ expected_log += DIVIDER;
+ ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}