Change heap dump format slightly.
Bump the version from v1.1 to v1.2 and add a build fingerprint line.
Update the heap dump documentation to match the new format and reflect
what made it in P and what made it in Q.
Update the unit tests for this change.
Add -O0 to unit test code to make it easier to debug.
Add an external function that can be used by the framework code
so that there is only one way to dump the heap.
Bug: 110095681
Test: Ran unit tests.
Test: Did a dump of a real process and verified fingerprint.
Test: Did a dump of a process without malloc debug enabled.
Change-Id: I769a476cbeaf4c85c5d75bd6d6385f0e3add948c
Merged-In: I769a476cbeaf4c85c5d75bd6d6385f0e3add948c
(cherry picked from commit c84a2a2601a4112ca6e43a33defb989c1da8c2f4)
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index 1ea4ac1..40a0023 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -39,6 +39,7 @@
// allocations that are currently in use.
// free_malloc_leak_info: Frees the data allocated by the call to
// get_malloc_leak_info.
+// write_malloc_leak_info: Writes the leak info data to a file.
#include <pthread.h>
@@ -211,6 +212,7 @@
FUNC_GET_MALLOC_LEAK_INFO,
FUNC_FREE_MALLOC_LEAK_INFO,
FUNC_MALLOC_BACKTRACE,
+ FUNC_WRITE_LEAK_INFO,
FUNC_LAST,
};
static void* g_functions[FUNC_LAST];
@@ -219,6 +221,7 @@
typedef bool (*init_func_t)(const MallocDispatch*, int*, const char*);
typedef void (*get_malloc_leak_info_func_t)(uint8_t**, size_t*, size_t*, size_t*, size_t*);
typedef void (*free_malloc_leak_info_func_t)(uint8_t*);
+typedef bool (*write_malloc_leak_info_func_t)(FILE*);
typedef ssize_t (*malloc_backtrace_func_t)(void*, uintptr_t*, size_t);
// =============================================================================
@@ -260,6 +263,26 @@
reinterpret_cast<free_malloc_leak_info_func_t>(func)(info);
}
+extern "C" void write_malloc_leak_info(FILE* fp) {
+ if (fp == nullptr) {
+ error_log("write_malloc_leak_info called with a nullptr");
+ return;
+ }
+
+ void* func = g_functions[FUNC_WRITE_LEAK_INFO];
+ bool written = false;
+ if (func != nullptr) {
+ written = reinterpret_cast<write_malloc_leak_info_func_t>(func)(fp);
+ }
+
+ if (!written) {
+ 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");
+ }
+}
+
// =============================================================================
template<typename FunctionType>
@@ -392,6 +415,7 @@
"get_malloc_leak_info",
"free_malloc_leak_info",
"malloc_backtrace",
+ "write_malloc_leak_info",
};
for (size_t i = 0; i < FUNC_LAST; i++) {
char symbol[128];
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 159d4a0..18099dd 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1634,6 +1634,7 @@
tkill; # arm x86 mips
wait3; # arm x86 mips
wcswcs; # arm x86 mips
+ write_malloc_leak_info;
} LIBC_P;
LIBC_DEPRECATED {
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index 7702f9e..f37641e 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1352,6 +1352,7 @@
free_malloc_leak_info;
get_malloc_leak_info;
gMallocLeakZygoteChild;
+ write_malloc_leak_info;
} LIBC_P;
LIBC_DEPRECATED {
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index ebe2098..21966b3 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1660,6 +1660,7 @@
tkill; # arm x86 mips
wait3; # arm x86 mips
wcswcs; # arm x86 mips
+ write_malloc_leak_info;
} LIBC_P;
LIBC_DEPRECATED {
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index d04cb4f..0bc6fce 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1475,6 +1475,7 @@
tkill; # arm x86 mips
wait3; # arm x86 mips
wcswcs; # arm x86 mips
+ write_malloc_leak_info;
} LIBC_P;
LIBC_DEPRECATED {
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index 7702f9e..f37641e 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1352,6 +1352,7 @@
free_malloc_leak_info;
get_malloc_leak_info;
gMallocLeakZygoteChild;
+ write_malloc_leak_info;
} LIBC_P;
LIBC_DEPRECATED {
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 2e10bbb..14ee151 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1474,6 +1474,7 @@
tkill; # arm x86 mips
wait3; # arm x86 mips
wcswcs; # arm x86 mips
+ write_malloc_leak_info;
} LIBC_P;
LIBC_DEPRECATED {
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index 7702f9e..f37641e 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1352,6 +1352,7 @@
free_malloc_leak_info;
get_malloc_leak_info;
gMallocLeakZygoteChild;
+ write_malloc_leak_info;
} LIBC_P;
LIBC_DEPRECATED {
diff --git a/libc/malloc_debug/Android.bp b/libc/malloc_debug/Android.bp
index a4ff5d4..0cb4cca 100644
--- a/libc/malloc_debug/Android.bp
+++ b/libc/malloc_debug/Android.bp
@@ -139,5 +139,6 @@
"-Wall",
"-Werror",
"-Wno-error=format-zero-length",
+ "-O0",
],
}
diff --git a/libc/malloc_debug/README.md b/libc/malloc_debug/README.md
index c427d5f..c4ee722 100644
--- a/libc/malloc_debug/README.md
+++ b/libc/malloc_debug/README.md
@@ -161,7 +161,7 @@
when the program exits will be backtrace\_dump\_prefix.**PID**.exit.txt.
### backtrace\_full
-As of P, any time that a backtrace is gathered, a different algorithm is used
+As of Q, any time that a backtrace is gathered, a different algorithm is used
that is extra thorough and can unwind through Java frames. This will run
slower than the normal backtracing function.
@@ -495,15 +495,32 @@
The map data is simply the output of /proc/PID/maps. This data can be used to
decode the frames in the backtraces.
-As of Android P, there is a new version of this file. The new header is:
+There are now multiple versions of the file:
+
+Android P produces version v1.1 of the heap dump.
Android Native Heap Dump v1.1
-The new version no longer 0 pads the backtrace addresses. In v1.0:
+The only difference between v1.0 and v1.1 is that the NUM\_ALLOCATIONS
+value is always accurate in v1.1. A previous version of malloc debug set
+NUM\_ALLOCATIONS to an incorrect value. For heap dump v1.0, the
+NUM\_ALLOCATIONS value should be treated as always 1 no matter what is
+actually present.
+
+Android Q introduces v1.2 of the heap dump. The new header looks like this:
+
+ Android Native Heap Dump v1.2
+
+ Build fingerprint: 'google/taimen/taimen:8.1.0/OPM2.171026.006.C1/4769658:user/release-keys'
+
+The new line fingerprint line is the contents of the ro.build.fingerprint
+property.
+
+The new version no longer 0 pads the backtrace addresses. In v1.0/v1.1:
z 0 sz 400 num 1 bt 0000a230 0000b500
-While v1.1:
+While v1.2:
z 0 sz 400 num 1 bt a230 b500
diff --git a/libc/malloc_debug/exported32.map b/libc/malloc_debug/exported32.map
index 78a6990..2f590d0 100644
--- a/libc/malloc_debug/exported32.map
+++ b/libc/malloc_debug/exported32.map
@@ -21,6 +21,7 @@
debug_pvalloc;
debug_realloc;
debug_valloc;
+ debug_write_malloc_leak_info;
local:
*;
diff --git a/libc/malloc_debug/exported64.map b/libc/malloc_debug/exported64.map
index 2bfc38b..08d36a5 100644
--- a/libc/malloc_debug/exported64.map
+++ b/libc/malloc_debug/exported64.map
@@ -19,6 +19,7 @@
debug_memalign;
debug_posix_memalign;
debug_realloc;
+ debug_write_malloc_leak_info;
local:
*;
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index 836c33b..1e7086c 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -38,6 +38,7 @@
#include <vector>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <private/bionic_malloc_dispatch.h>
@@ -69,9 +70,10 @@
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_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);
+bool debug_write_malloc_leak_info(FILE* fp);
ssize_t debug_malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
void debug_free_malloc_leak_info(uint8_t* info);
size_t debug_malloc_usable_size(void* pointer);
@@ -813,28 +815,11 @@
static std::mutex g_dump_lock;
-bool debug_dump_heap(const char* file_name) {
- ScopedDisableDebugCalls disable;
+static void write_dump(FILE* fp) {
+ fprintf(fp, "Android Native Heap Dump v1.2\n\n");
- 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.1\n\n");
+ std::string fingerprint = android::base::GetProperty("ro.build.fingerprint", "unknown");
+ fprintf(fp, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
PointerData::DumpLiveToFile(fp);
@@ -846,6 +831,33 @@
fprintf(fp, "%s", content.c_str());
}
fprintf(fp, "END\n");
- fclose(fp);
+}
+
+bool debug_write_malloc_leak_info(FILE* fp) {
+ ScopedDisableDebugCalls disable;
+
+ std::lock_guard<std::mutex> guard(g_dump_lock);
+
+ if (!(g_debug->config().options() & BACKTRACE)) {
+ return false;
+ }
+
+ write_dump(fp);
return true;
}
+
+void 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;
+ }
+
+ error_log("Dumping to file: %s\n", file_name);
+ write_dump(fp);
+ fclose(fp);
+}
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index 0663f6a..cd6d2c2 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -1250,10 +1250,15 @@
continue;
}
}
- if (line == "MAPS") {
- skip_map_data = true;
+
+ if (android::base::StartsWith(line, "Build fingerprint:")) {
+ sanitized += "Build fingerprint: ''\n";
+ } else {
+ if (line == "MAPS") {
+ skip_map_data = true;
+ }
+ sanitized += line + '\n';
}
- sanitized += line + '\n';
}
return sanitized;
}
@@ -1312,7 +1317,9 @@
std::string sanitized(SanitizeHeapData(actual));
std::string expected =
-R"(Android Native Heap Dump v1.1
+R"(Android Native Heap Dump v1.2
+
+Build fingerprint: ''
Total memory: 405
Allocation records: 6
@@ -1377,7 +1384,9 @@
std::string sanitized(SanitizeHeapData(actual));
std::string expected =
-R"(Android Native Heap Dump v1.1
+R"(Android Native Heap Dump v1.2
+
+Build fingerprint: ''
Total memory: 1200
Allocation records: 3
@@ -1426,7 +1435,9 @@
std::string sanitized(SanitizeHeapData(actual));
std::string expected =
-R"(Android Native Heap Dump v1.1
+R"(Android Native Heap Dump v1.2
+
+Build fingerprint: ''
Total memory: 1000
Allocation records: 2
@@ -1482,7 +1493,9 @@
std::string sanitized(SanitizeHeapData(actual));
std::string expected =
-R"(Android Native Heap Dump v1.1
+R"(Android Native Heap Dump v1.2
+
+Build fingerprint: ''
Total memory: 1200
Allocation records: 3