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 c84a2a2601)
This commit is contained in:
Christopher Ferris 2018-06-13 10:46:34 -07:00
parent 2583b0adb2
commit 2e1a40a203
14 changed files with 110 additions and 34 deletions

View File

@ -39,6 +39,7 @@
// allocations that are currently in use. // allocations that are currently in use.
// free_malloc_leak_info: Frees the data allocated by the call to // free_malloc_leak_info: Frees the data allocated by the call to
// get_malloc_leak_info. // get_malloc_leak_info.
// write_malloc_leak_info: Writes the leak info data to a file.
#include <pthread.h> #include <pthread.h>
@ -211,6 +212,7 @@ enum FunctionEnum : uint8_t {
FUNC_GET_MALLOC_LEAK_INFO, FUNC_GET_MALLOC_LEAK_INFO,
FUNC_FREE_MALLOC_LEAK_INFO, FUNC_FREE_MALLOC_LEAK_INFO,
FUNC_MALLOC_BACKTRACE, FUNC_MALLOC_BACKTRACE,
FUNC_WRITE_LEAK_INFO,
FUNC_LAST, FUNC_LAST,
}; };
static void* g_functions[FUNC_LAST]; static void* g_functions[FUNC_LAST];
@ -219,6 +221,7 @@ typedef void (*finalize_func_t)();
typedef bool (*init_func_t)(const MallocDispatch*, int*, const char*); 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 (*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 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); typedef ssize_t (*malloc_backtrace_func_t)(void*, uintptr_t*, size_t);
// ============================================================================= // =============================================================================
@ -260,6 +263,26 @@ extern "C" void free_malloc_leak_info(uint8_t* info) {
reinterpret_cast<free_malloc_leak_info_func_t>(func)(info); 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> template<typename FunctionType>
@ -392,6 +415,7 @@ static void* LoadSharedLibrary(const char* shared_lib, const char* prefix, Mallo
"get_malloc_leak_info", "get_malloc_leak_info",
"free_malloc_leak_info", "free_malloc_leak_info",
"malloc_backtrace", "malloc_backtrace",
"write_malloc_leak_info",
}; };
for (size_t i = 0; i < FUNC_LAST; i++) { for (size_t i = 0; i < FUNC_LAST; i++) {
char symbol[128]; char symbol[128];

View File

@ -1634,6 +1634,7 @@ LIBC_PRIVATE {
tkill; # arm x86 mips tkill; # arm x86 mips
wait3; # arm x86 mips wait3; # arm x86 mips
wcswcs; # arm x86 mips wcswcs; # arm x86 mips
write_malloc_leak_info;
} LIBC_P; } LIBC_P;
LIBC_DEPRECATED { LIBC_DEPRECATED {

View File

@ -1352,6 +1352,7 @@ LIBC_PRIVATE {
free_malloc_leak_info; free_malloc_leak_info;
get_malloc_leak_info; get_malloc_leak_info;
gMallocLeakZygoteChild; gMallocLeakZygoteChild;
write_malloc_leak_info;
} LIBC_P; } LIBC_P;
LIBC_DEPRECATED { LIBC_DEPRECATED {

View File

@ -1660,6 +1660,7 @@ LIBC_PRIVATE {
tkill; # arm x86 mips tkill; # arm x86 mips
wait3; # arm x86 mips wait3; # arm x86 mips
wcswcs; # arm x86 mips wcswcs; # arm x86 mips
write_malloc_leak_info;
} LIBC_P; } LIBC_P;
LIBC_DEPRECATED { LIBC_DEPRECATED {

View File

@ -1475,6 +1475,7 @@ LIBC_PRIVATE {
tkill; # arm x86 mips tkill; # arm x86 mips
wait3; # arm x86 mips wait3; # arm x86 mips
wcswcs; # arm x86 mips wcswcs; # arm x86 mips
write_malloc_leak_info;
} LIBC_P; } LIBC_P;
LIBC_DEPRECATED { LIBC_DEPRECATED {

View File

@ -1352,6 +1352,7 @@ LIBC_PRIVATE {
free_malloc_leak_info; free_malloc_leak_info;
get_malloc_leak_info; get_malloc_leak_info;
gMallocLeakZygoteChild; gMallocLeakZygoteChild;
write_malloc_leak_info;
} LIBC_P; } LIBC_P;
LIBC_DEPRECATED { LIBC_DEPRECATED {

View File

@ -1474,6 +1474,7 @@ LIBC_PRIVATE {
tkill; # arm x86 mips tkill; # arm x86 mips
wait3; # arm x86 mips wait3; # arm x86 mips
wcswcs; # arm x86 mips wcswcs; # arm x86 mips
write_malloc_leak_info;
} LIBC_P; } LIBC_P;
LIBC_DEPRECATED { LIBC_DEPRECATED {

View File

@ -1352,6 +1352,7 @@ LIBC_PRIVATE {
free_malloc_leak_info; free_malloc_leak_info;
get_malloc_leak_info; get_malloc_leak_info;
gMallocLeakZygoteChild; gMallocLeakZygoteChild;
write_malloc_leak_info;
} LIBC_P; } LIBC_P;
LIBC_DEPRECATED { LIBC_DEPRECATED {

View File

@ -139,5 +139,6 @@ cc_test {
"-Wall", "-Wall",
"-Werror", "-Werror",
"-Wno-error=format-zero-length", "-Wno-error=format-zero-length",
"-O0",
], ],
} }

View File

@ -161,7 +161,7 @@ on the signal will be backtrace\_dump\_prefix.**PID**.txt. The filename chosen
when the program exits will be backtrace\_dump\_prefix.**PID**.exit.txt. when the program exits will be backtrace\_dump\_prefix.**PID**.exit.txt.
### backtrace\_full ### 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 that is extra thorough and can unwind through Java frames. This will run
slower than the normal backtracing function. slower than the normal backtracing function.
@ -495,15 +495,32 @@ The final section is the map data for the process:
The map data is simply the output of /proc/PID/maps. This data can be used to The map data is simply the output of /proc/PID/maps. This data can be used to
decode the frames in the backtraces. 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 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 z 0 sz 400 num 1 bt 0000a230 0000b500
While v1.1: While v1.2:
z 0 sz 400 num 1 bt a230 b500 z 0 sz 400 num 1 bt a230 b500

View File

@ -21,6 +21,7 @@ LIBC_MALLOC_DEBUG {
debug_pvalloc; debug_pvalloc;
debug_realloc; debug_realloc;
debug_valloc; debug_valloc;
debug_write_malloc_leak_info;
local: local:
*; *;

View File

@ -19,6 +19,7 @@ LIBC_MALLOC_DEBUG {
debug_memalign; debug_memalign;
debug_posix_memalign; debug_posix_memalign;
debug_realloc; debug_realloc;
debug_write_malloc_leak_info;
local: local:
*; *;

View File

@ -38,6 +38,7 @@
#include <vector> #include <vector>
#include <android-base/file.h> #include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h> #include <android-base/stringprintf.h>
#include <private/bionic_malloc_dispatch.h> #include <private/bionic_malloc_dispatch.h>
@ -69,9 +70,10 @@ __BEGIN_DECLS
bool debug_initialize(const MallocDispatch* malloc_dispatch, int* malloc_zygote_child, bool debug_initialize(const MallocDispatch* malloc_dispatch, int* malloc_zygote_child,
const char* options); const char* options);
void debug_finalize(); 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, 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); 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); ssize_t debug_malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count);
void debug_free_malloc_leak_info(uint8_t* info); void debug_free_malloc_leak_info(uint8_t* info);
size_t debug_malloc_usable_size(void* pointer); size_t debug_malloc_usable_size(void* pointer);
@ -813,28 +815,11 @@ void* debug_valloc(size_t size) {
static std::mutex g_dump_lock; static std::mutex g_dump_lock;
bool debug_dump_heap(const char* file_name) { static void write_dump(FILE* fp) {
ScopedDisableDebugCalls disable; fprintf(fp, "Android Native Heap Dump v1.2\n\n");
std::lock_guard<std::mutex> guard(g_dump_lock); std::string fingerprint = android::base::GetProperty("ro.build.fingerprint", "unknown");
fprintf(fp, "Build fingerprint: '%s'\n\n", fingerprint.c_str());
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");
PointerData::DumpLiveToFile(fp); PointerData::DumpLiveToFile(fp);
@ -846,6 +831,33 @@ bool debug_dump_heap(const char* file_name) {
fprintf(fp, "%s", content.c_str()); fprintf(fp, "%s", content.c_str());
} }
fprintf(fp, "END\n"); 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; 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);
}

View File

@ -1250,10 +1250,15 @@ static std::string SanitizeHeapData(const std::string& data) {
continue; 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; return sanitized;
} }
@ -1312,7 +1317,9 @@ void MallocDebugTest::BacktraceDumpOnSignal(bool trigger_with_alloc) {
std::string sanitized(SanitizeHeapData(actual)); std::string sanitized(SanitizeHeapData(actual));
std::string expected = std::string expected =
R"(Android Native Heap Dump v1.1 R"(Android Native Heap Dump v1.2
Build fingerprint: ''
Total memory: 405 Total memory: 405
Allocation records: 6 Allocation records: 6
@ -1377,7 +1384,9 @@ TEST_F(MallocDebugTest, backtrace_dump_on_exit) {
std::string sanitized(SanitizeHeapData(actual)); std::string sanitized(SanitizeHeapData(actual));
std::string expected = std::string expected =
R"(Android Native Heap Dump v1.1 R"(Android Native Heap Dump v1.2
Build fingerprint: ''
Total memory: 1200 Total memory: 1200
Allocation records: 3 Allocation records: 3
@ -1426,7 +1435,9 @@ TEST_F(MallocDebugTest, backtrace_dump_on_exit_shared_backtrace) {
std::string sanitized(SanitizeHeapData(actual)); std::string sanitized(SanitizeHeapData(actual));
std::string expected = std::string expected =
R"(Android Native Heap Dump v1.1 R"(Android Native Heap Dump v1.2
Build fingerprint: ''
Total memory: 1000 Total memory: 1000
Allocation records: 2 Allocation records: 2
@ -1482,7 +1493,9 @@ TEST_F(MallocDebugTest, backtrace_full_dump_on_exit) {
std::string sanitized(SanitizeHeapData(actual)); std::string sanitized(SanitizeHeapData(actual));
std::string expected = std::string expected =
R"(Android Native Heap Dump v1.1 R"(Android Native Heap Dump v1.2
Build fingerprint: ''
Total memory: 1200 Total memory: 1200
Allocation records: 3 Allocation records: 3