Add support for using the new unwinder.
This adds a new option backtrace_full, when it is set, then it will use
libunwindstack.
Modify the dump to file data to dump the extra information from libunwindstack.
Along with the new dump file format, change the version to v1.1.
Updated document for new format of file data.
Add unit tests for the new functionality.
Bug: 74361929
Test: Ran unit tests.
Change-Id: I40fff795f5346bba7b9d7fde2e04f269ff4eb7f1
diff --git a/libc/malloc_debug/tests/backtrace_fake.cpp b/libc/malloc_debug/tests/backtrace_fake.cpp
index db542e5..ad16c02 100644
--- a/libc/malloc_debug/tests/backtrace_fake.cpp
+++ b/libc/malloc_debug/tests/backtrace_fake.cpp
@@ -20,6 +20,8 @@
#include <vector>
#include <utility>
+#include <unwindstack/LocalUnwinder.h>
+
#include "backtrace.h"
#include "backtrace_fake.h"
#include "debug_log.h"
@@ -57,3 +59,31 @@
error_log(" #%02zd pc %p", i, reinterpret_cast<void*>(frames[i]));
}
}
+
+static std::deque<std::vector<unwindstack::LocalFrameData>> g_fake_local_frame_data;
+
+void BacktraceUnwindFakeClearAll() {
+ g_fake_local_frame_data.clear();
+}
+
+void BacktraceUnwindFake(const std::vector<unwindstack::LocalFrameData>& frames) {
+ g_fake_local_frame_data.push_back(frames);
+}
+
+bool Unwind(std::vector<uintptr_t>* frames, std::vector<unwindstack::LocalFrameData>* info, size_t) {
+ if (g_fake_local_frame_data.empty()) {
+ return false;
+ }
+
+ *info = g_fake_local_frame_data.front();
+ g_fake_local_frame_data.pop_front();
+ frames->clear();
+ for (const auto& frame : *info) {
+ frames->push_back(frame.pc);
+ }
+
+ return true;
+}
+
+void UnwindLog(const std::vector<unwindstack::LocalFrameData>& /*frame_info*/) {
+}
diff --git a/libc/malloc_debug/tests/backtrace_fake.h b/libc/malloc_debug/tests/backtrace_fake.h
index f2aa7a0..a9ee97d 100644
--- a/libc/malloc_debug/tests/backtrace_fake.h
+++ b/libc/malloc_debug/tests/backtrace_fake.h
@@ -21,7 +21,12 @@
#include <vector>
+#include <unwindstack/LocalUnwinder.h>
+
void backtrace_fake_clear_all();
void backtrace_fake_add(const std::vector<uintptr_t>& ips);
+void BacktraceUnwindFakeClearAll();
+void BacktraceUnwindFake(const std::vector<unwindstack::LocalFrameData>& frames);
+
#endif // MALLOC_DEBUG_TESTS_BACKTRACE_FAKE_H
diff --git a/libc/malloc_debug/tests/log_fake.cpp b/libc/malloc_debug/tests/log_fake.cpp
index fb64e3e..9a23ac7 100644
--- a/libc/malloc_debug/tests/log_fake.cpp
+++ b/libc/malloc_debug/tests/log_fake.cpp
@@ -45,9 +45,7 @@
}
extern "C" int async_safe_format_log(int priority, const char* tag, const char* format, ...) {
- g_fake_log_print += std::to_string(priority) + ' ';
- g_fake_log_print += tag;
- g_fake_log_print += ' ';
+ g_fake_log_print += std::to_string(priority) + ' ' + tag + ' ';
va_list ap;
va_start(ap, format);
@@ -59,6 +57,12 @@
return 0;
}
+extern "C" int async_safe_write_log(int priority, const char* tag, const char* msg) {
+ g_fake_log_print += std::to_string(priority) + ' ' + tag + ' ' + msg + '\n';
+
+ return 0;
+}
+
extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
g_fake_log_buf += tag;
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index 4603535..2a6e399 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -83,6 +83,11 @@
"6 malloc_debug backtrace_dump_prefix.<PID>.final.txt.\n"
"6 malloc_debug The default is false.\n"
"6 malloc_debug \n"
+ "6 malloc_debug backtrace_full\n"
+ "6 malloc_debug Any time a backtrace is acquired, use an unwinder that can\n"
+ "6 malloc_debug display Java stack frames. This unwinder can run slower than\n"
+ "6 malloc_debug normal unwinder.\n"
+ "6 malloc_debug \n"
"6 malloc_debug fill_on_alloc[=XX]\n"
"6 malloc_debug On first allocation, fill with the value 0xeb.\n"
"6 malloc_debug If XX is set it will only fill up to XX bytes of the\n"
@@ -413,6 +418,24 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
+TEST_F(MallocDebugConfigTest, backtrace_full) {
+ ASSERT_TRUE(InitConfig("backtrace_full")) << getFakeLogPrint();
+ ASSERT_EQ(BACKTRACE_FULL, config->options());
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(MallocDebugConfigTest, backtrace_full_fail) {
+ ASSERT_FALSE(InitConfig("backtrace_full=200")) << getFakeLogPrint();
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ std::string log_msg(
+ "6 malloc_debug malloc_testing: value set for option 'backtrace_full' "
+ "which does not take a value\n");
+ ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
+}
+
TEST_F(MallocDebugConfigTest, fill_on_alloc) {
ASSERT_TRUE(InitConfig("fill_on_alloc=64")) << getFakeLogPrint();
ASSERT_EQ(FILL_ON_ALLOC, 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 1504d06..0663f6a 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -19,6 +19,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
+#include <sys/mman.h>
#include <sys/param.h>
#include <sys/types.h>
#include <unistd.h>
@@ -1311,30 +1312,23 @@
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 50 num 1 bt 000000000000a100 000000000000b200\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 50 num 1 bt 0000a100 0000b200\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"
- "END\n\n";
+R"(Android Native Heap Dump v1.1
+
+Total memory: 405
+Allocation records: 6
+Backtrace size: 4
+
+z 0 sz 50 num 1 bt a100 b200
+z 0 sz 10 num 1 bt a000 b000
+z 0 sz 5 num 1 bt a300 b300
+z 1 sz 200 num 1 bt 500 600
+z 1 sz 100 num 1 bt 100 200
+z 1 sz 40 num 1 bt 300 400
+MAPS
+MAP_DATA
+END
+
+)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1383,24 +1377,20 @@
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 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 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";
+R"(Android Native Heap Dump v1.1
+
+Total memory: 1200
+Allocation records: 3
+Backtrace size: 4
+
+z 0 sz 500 num 1 bt a000 b000 c000
+z 0 sz 400 num 1 bt a000 b000
+z 0 sz 300 num 1 bt 100 200
+MAPS
+MAP_DATA
+END
+
+)";
ASSERT_STREQ(expected.c_str(), sanitized.c_str()) << "Actual data: \n" << actual;
ASSERT_STREQ("", getFakeLogBuf().c_str());
@@ -1436,28 +1426,84 @@
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"
- "END\n\n";
+R"(Android Native Heap Dump v1.1
+
+Total memory: 1000
+Allocation records: 2
+Backtrace size: 4
+
+z 0 sz 400 num 1 bt a000 b000 c000
+z 0 sz 300 num 2 bt 100 200
+MAPS
+MAP_DATA
+END
+
+)";
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_full_dump_on_exit) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ Init("backtrace=4 backtrace_full backtrace_dump_on_exit");
+ BacktraceUnwindFake(
+ std::vector<unwindstack::LocalFrameData>{{nullptr, 0x1100, 0x100, "fake1", 10},
+ {nullptr, 0x1200, 0x200, "fake2", 20}});
+ unwindstack::MapInfo map_info{0x10000, 0x20000, 0, PROT_READ | PROT_EXEC, "/data/fake.so"};
+ BacktraceUnwindFake(
+ std::vector<unwindstack::LocalFrameData>{{&map_info, 0x1a000, 0xa000, "level1", 0},
+ {&map_info, 0x1b000, 0xb000, "level2", 10}});
+ BacktraceUnwindFake(
+ std::vector<unwindstack::LocalFrameData>{{nullptr, 0x1a000, 0xa000, "func1", 0},
+ {nullptr, 0x1b000, 0xb000, "func2", 10},
+ {nullptr, 0x1c000, 0xc000, "", 30}});
+
+ 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, 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 =
+R"(Android Native Heap Dump v1.1
+
+Total memory: 1200
+Allocation records: 3
+Backtrace size: 4
+
+z 0 sz 500 num 1 bt 1a000 1b000 1c000
+ bt_info {"" a000 "func1" 0} {"" b000 "func2" a} {"" c000 "" 0}
+z 0 sz 400 num 1 bt 1a000 1b000
+ bt_info {"/data/fake.so" a000 "level1" 0} {"/data/fake.so" b000 "level2" a}
+z 0 sz 300 num 1 bt 1100 1200
+ bt_info {"" 100 "fake1" a} {"" 200 "fake2" 14}
+MAPS
+MAP_DATA
+END
+
+)";
+ 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, realloc_usable_size) {
Init("front_guard");
@@ -2286,7 +2332,8 @@
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 backtrace_str("6 malloc_debug Backtrace at time of failure:\n");
+ 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;