summaryrefslogtreecommitdiff
path: root/libc/malloc_debug/malloc_debug.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/malloc_debug.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/malloc_debug.cpp')
-rw-r--r--libc/malloc_debug/malloc_debug.cpp93
1 files changed, 90 insertions, 3 deletions
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 014d3855e..d890a1ccd 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 @@ __BEGIN_DECLS
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 @@ static void* InitHeader(Header* header, void* orig_pointer, size_t size) {
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 @@ static void* InitHeader(Header* header, void* orig_pointer, size_t size) {
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 @@ void debug_finalize() {
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 @@ size_t debug_malloc_usable_size(void* 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());
+ }
+
if (size == 0) {
size = 1;
}
@@ -341,6 +359,13 @@ void* debug_malloc(size_t size) {
}
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 @@ void* debug_realloc(void* pointer, size_t bytes) {
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 @@ void* debug_valloc(size_t size) {
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;
+}