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
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 014d385..d890a1c 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -34,8 +34,11 @@
#include <sys/param.h>
#include <unistd.h>
+#include <mutex>
#include <vector>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
#include <private/bionic_malloc_dispatch.h>
#include "backtrace.h"
@@ -65,6 +68,7 @@
bool debug_initialize(const MallocDispatch* malloc_dispatch, int* malloc_zygote_child,
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);
@@ -138,7 +142,7 @@
header->orig_pointer = orig_pointer;
header->size = size;
if (*g_malloc_zygote_child) {
- header->set_zygote();
+ header->set_zygote_child_alloc();
}
header->usable_size = g_dispatch->malloc_usable_size(orig_pointer);
if (header->usable_size == 0) {
@@ -164,7 +168,7 @@
bool backtrace_found = false;
if (g_debug->config().options() & BACKTRACE) {
BacktraceHeader* back_header = g_debug->GetAllocBacktrace(header);
- if (g_debug->backtrace->enabled()) {
+ 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;
@@ -224,6 +228,13 @@
g_debug->track->DisplayLeaks();
}
+ 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());
+ }
+
DebugDisableSet(true);
backtrace_shutdown();
@@ -288,6 +299,13 @@
}
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());
+ }
+
if (size == 0) {
size = 1;
}
@@ -341,6 +359,13 @@
}
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());
+ }
+
void* free_pointer = pointer;
size_t bytes;
Header* header;
@@ -538,7 +563,7 @@
if (real_size < header->usable_size) {
header->size = real_size;
if (*g_malloc_zygote_child) {
- header->set_zygote();
+ header->set_zygote_child_alloc();
}
if (g_debug->config().options() & REAR_GUARD) {
// Don't bother allocating a smaller pointer in this case, simply
@@ -758,3 +783,65 @@
return debug_memalign(getpagesize(), size);
}
#endif
+
+static std::mutex g_dump_lock;
+
+bool debug_dump_heap(const char* file_name) {
+ ScopedDisableDebugCalls disable;
+
+ std::lock_guard<std::mutex> guard(g_dump_lock);
+
+ FILE* fp = fopen(file_name, "w+e");
+ if (fp == nullptr) {
+ error_log("Unable to create file: %s", file_name);
+ return false;
+ }
+ error_log("Dumping to file: %s\n", file_name);
+
+ if (!(g_debug->config().options() & BACKTRACE)) {
+ fprintf(fp, "Native heap dump not available. To enable, run these commands (requires root):\n");
+ fprintf(fp, "# adb shell stop\n");
+ fprintf(fp, "# adb shell setprop libc.debug.malloc.options backtrace\n");
+ fprintf(fp, "# adb shell start\n");
+ fclose(fp);
+ return false;
+ }
+
+ 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");
+ }
+
+ fprintf(fp, "MAPS\n");
+ std::string content;
+ if (!android::base::ReadFileToString("/proc/self/maps", &content)) {
+ fprintf(fp, "Could not open /proc/self/maps\n");
+ } else {
+ fprintf(fp, "%s", content.c_str());
+ }
+ fprintf(fp, "END\n");
+ fclose(fp);
+ return true;
+}