summaryrefslogtreecommitdiff
path: root/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
diff options
context:
space:
mode:
authorChristopher Ferris <cferris@google.com>2017-08-04 13:04:04 -0700
committerChristopher Ferris <cferris@google.com>2017-09-05 15:57:00 -0700
commit602b88ccddfafdefec023e7599d49c811ad3214f (patch)
tree11730f3f0cbc308860c4e45d6f0310c7f2083041 /libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
parente0e2798f11ca082fe0a796f167f9006c51dc3304 (diff)
Provide method to dump backtrace heap data.
For non-zygote spawned processes, we might want to dump the backtrace data. Provide a method to send a signal to a process and then dump the data to a file. Adds a method to dump the backtrace data on exit. Update documentation and explain format of heap dump data. Test: Ran unit tests, enabled new options and used them. Change-Id: Ie2fa706694160731afe02c1382b037d06df1d069
Diffstat (limited to 'libc/malloc_debug/tests/malloc_debug_unit_tests.cpp')
-rw-r--r--libc/malloc_debug/tests/malloc_debug_unit_tests.cpp231
1 files changed, 221 insertions, 10 deletions
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 4fdba2ef9..37d805751 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -32,6 +32,7 @@
#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <private/bionic_macros.h>
#include <private/bionic_malloc_dispatch.h>
@@ -82,6 +83,8 @@ static size_t get_tag_offset(uint32_t flags = 0, size_t backtrace_frames = 0) {
static constexpr const char RECORD_ALLOCS_FILE[] = "/data/local/tmp/record_allocs.txt";
+static constexpr const char BACKTRACE_DUMP_PREFIX[] = "/data/local/tmp/backtrace_heap";
+
class MallocDebugTest : public ::testing::Test {
protected:
void SetUp() override {
@@ -104,6 +107,8 @@ class MallocDebugTest : public ::testing::Test {
initialized = true;
}
+ void BacktraceDumpOnSignal(bool trigger_with_alloc);
+
bool initialized;
int zygote;
@@ -132,7 +137,7 @@ MallocDispatch MallocDebugTest::dispatch = {
mallopt,
};
-void VerifyAllocCalls() {
+void VerifyAllocCalls(bool backtrace_enabled) {
size_t alloc_size = 1024;
// Verify debug_malloc.
@@ -186,17 +191,23 @@ void VerifyAllocCalls() {
ASSERT_TRUE(pointer == nullptr);
ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
+ std::string expected_log;
+ if (backtrace_enabled) {
+ 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, fill_generic) {
Init("fill");
- VerifyAllocCalls();
+ VerifyAllocCalls(false);
}
TEST_F(MallocDebugTest, fill_on_alloc_generic) {
Init("fill_on_alloc");
- VerifyAllocCalls();
+ VerifyAllocCalls(false);
}
TEST_F(MallocDebugTest, fill_on_alloc_partial) {
@@ -275,7 +286,7 @@ TEST_F(MallocDebugTest, free_track_partial) {
TEST_F(MallocDebugTest, all_options) {
Init("guard backtrace fill expand_alloc free_track leak_track");
- VerifyAllocCalls();
+ VerifyAllocCalls(true);
}
TEST_F(MallocDebugTest, expand_alloc) {
@@ -624,6 +635,9 @@ TEST_F(MallocDebugTest, leak_track_no_frees_with_backtrace) {
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());
+ expected_log += android::base::StringPrintf(
"6 malloc_debug +++ malloc_testing leaked block of size 1024 at %p (leak 1 of 3)\n",
pointer3);
expected_log += "6 malloc_debug Backtrace at time of allocation:\n";
@@ -649,7 +663,6 @@ TEST_F(MallocDebugTest, leak_track_no_frees_with_backtrace) {
expected_log += "6 malloc_debug #00 pc 0x1000\n";
expected_log += "6 malloc_debug #01 pc 0x2000\n";
expected_log += "6 malloc_debug #02 pc 0x3000\n";
-
ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
}
@@ -1022,7 +1035,10 @@ TEST_F(MallocDebugTest, get_malloc_leak_info_empty) {
ASSERT_EQ(0U, backtrace_size);
ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().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, get_malloc_leak_info_single) {
@@ -1065,7 +1081,10 @@ TEST_F(MallocDebugTest, get_malloc_leak_info_single) {
debug_free(pointer);
ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().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, get_malloc_leak_info_multi) {
@@ -1144,7 +1163,10 @@ TEST_F(MallocDebugTest, get_malloc_leak_info_multi) {
debug_free(pointers[2]);
ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().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, get_malloc_leak_info_multi_skip_empty_backtrace) {
@@ -1215,6 +1237,185 @@ TEST_F(MallocDebugTest, get_malloc_leak_info_multi_skip_empty_backtrace) {
debug_free(pointers[2]);
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());
+}
+
+static std::string SanitizeHeapData(const std::string& data) {
+ // Remove the map data since it's not consistent.
+ std::string sanitized;
+ bool skip_map_data = false;
+ bool map_data_found = false;
+ for (auto& line : android::base::Split(data, "\n")) {
+ if (skip_map_data) {
+ if (line == "END") {
+ if (map_data_found) {
+ sanitized += "MAP_DATA\n";
+ map_data_found = false;
+ }
+ skip_map_data = false;
+ } else {
+ map_data_found = true;
+ continue;
+ }
+ }
+ if (line == "MAPS") {
+ skip_map_data = true;
+ }
+ sanitized += line + '\n';
+ }
+ return sanitized;
+}
+
+void MallocDebugTest::BacktraceDumpOnSignal(bool trigger_with_alloc) {
+ Init("backtrace=4");
+
+ 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});
+
+ backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000});
+ backtrace_fake_add(std::vector<uintptr_t> {0xa100, 0xb200});
+ backtrace_fake_add(std::vector<uintptr_t> {0xa300, 0xb300});
+
+ std::vector<void*> pointers;
+ zygote = 1;
+ pointers.push_back(debug_malloc(100));
+ ASSERT_TRUE(pointers.back() != nullptr);
+ pointers.push_back(debug_malloc(40));
+ ASSERT_TRUE(pointers.back() != nullptr);
+ pointers.push_back(debug_malloc(200));
+ ASSERT_TRUE(pointers.back() != nullptr);
+
+ zygote = 0;
+ pointers.push_back(debug_malloc(10));
+ ASSERT_TRUE(pointers.back() != nullptr);
+ pointers.push_back(debug_malloc(50));
+ ASSERT_TRUE(pointers.back() != nullptr);
+ pointers.push_back(debug_malloc(5));
+ ASSERT_TRUE(pointers.back() != nullptr);
+
+ // Dump all of the data accumulated so far.
+ ASSERT_TRUE(kill(getpid(), SIGRTMAX - 17) == 0);
+ sleep(1);
+
+ // This triggers the dumping.
+ if (trigger_with_alloc) {
+ pointers.push_back(debug_malloc(23));
+ ASSERT_TRUE(pointers.back() != nullptr);
+ } else {
+ debug_free(pointers.back());
+ pointers.pop_back();
+ }
+
+ for (auto* pointer : pointers) {
+ debug_free(pointer);
+ }
+
+ // Read all of the contents.
+ std::string actual;
+ std::string name = android::base::StringPrintf("%s.%d.txt", BACKTRACE_DUMP_PREFIX, getpid());
+ 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: 405\n"
+ "Allocation records: 6\n"
+ "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 1 sz 200 num 1 bt 0000000000000500 0000000000000600\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 1 sz 200 num 1 bt 00000500 00000600\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());
+ std::string expected_log = android::base::StringPrintf(
+ "4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the backtrace.\n",
+ SIGRTMAX - 17, getpid());
+ expected_log += android::base::StringPrintf(
+ "6 malloc_debug Dumping to file: /data/local/tmp/backtrace_heap.%d.txt\n\n", getpid());
+ ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugTest, backtrace_dump_on_signal_by_malloc) {
+ BacktraceDumpOnSignal(true);
+}
+
+TEST_F(MallocDebugTest, backtrace_dump_on_signal_by_free) {
+ BacktraceDumpOnSignal(false);
+}
+
+TEST_F(MallocDebugTest, backtrace_dump_on_exit) {
+ 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});
+ backtrace_fake_add(std::vector<uintptr_t> {0xa000, 0xb000, 0xc000});
+
+ std::vector<void*> pointers;
+ pointers.push_back(debug_malloc(300));
+ pointers.push_back(debug_malloc(400));
+ pointers.push_back(debug_malloc(500));
+
+ // Call the exit function manually.
+ debug_finalize();
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+ ASSERT_EQ(pid, 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: 1200\n"
+ "Allocation records: 3\n"
+ "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"
+#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"
+#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());
}
@@ -1317,6 +1518,9 @@ TEST_F(MallocDebugTest, backtrace_enable_on_signal) {
std::string expected_log = android::base::StringPrintf(
"4 malloc_debug malloc_testing: Run: 'kill -%d %d' to enable backtracing.\n",
SIGRTMAX - 19, getpid());
+ 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());
}
@@ -1439,7 +1643,10 @@ TEST_F(MallocDebugTest, zygote_set) {
debug_free(pointer);
ASSERT_STREQ("", getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().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, max_size) {
@@ -1596,6 +1803,7 @@ void VerifyRecordAllocs() {
// Read all of the contents.
std::string actual;
ASSERT_TRUE(android::base::ReadFileToString(RECORD_ALLOCS_FILE, &actual));
+ ASSERT_EQ(0, unlink(RECORD_ALLOCS_FILE));
ASSERT_STREQ(expected.c_str(), actual.c_str());
@@ -1653,6 +1861,7 @@ TEST_F(MallocDebugTest, record_allocs_max) {
// Read all of the contents.
std::string actual;
ASSERT_TRUE(android::base::ReadFileToString(RECORD_ALLOCS_FILE, &actual));
+ ASSERT_EQ(0, unlink(RECORD_ALLOCS_FILE));
ASSERT_STREQ(expected.c_str(), actual.c_str());
@@ -1694,6 +1903,7 @@ TEST_F(MallocDebugTest, record_allocs_thread_done) {
// Read all of the contents.
std::string actual;
ASSERT_TRUE(android::base::ReadFileToString(RECORD_ALLOCS_FILE, &actual));
+ ASSERT_EQ(0, unlink(RECORD_ALLOCS_FILE));
ASSERT_STREQ(expected.c_str(), actual.c_str());
@@ -1748,6 +1958,7 @@ TEST_F(MallocDebugTest, record_allocs_file_name_fail) {
expected += android::base::StringPrintf("%d: free %p\n", getpid(), pointer);
ASSERT_TRUE(android::base::ReadFileToString(RECORD_ALLOCS_FILE, &actual));
+ ASSERT_EQ(0, unlink(RECORD_ALLOCS_FILE));
ASSERT_STREQ(expected.c_str(), actual.c_str());
ASSERT_STREQ("", getFakeLogBuf().c_str());