diff options
author | Christopher Ferris <cferris@google.com> | 2017-08-04 13:04:04 -0700 |
---|---|---|
committer | Christopher Ferris <cferris@google.com> | 2017-09-05 15:57:00 -0700 |
commit | 602b88ccddfafdefec023e7599d49c811ad3214f (patch) | |
tree | 11730f3f0cbc308860c4e45d6f0310c7f2083041 /libc/malloc_debug/tests/malloc_debug_unit_tests.cpp | |
parent | e0e2798f11ca082fe0a796f167f9006c51dc3304 (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.cpp | 231 |
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()); |