diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp index 1ea4ac1a3..40a0023a8 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 @@ -211,6 +212,7 @@ enum FunctionEnum : uint8_t { 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 void (*finalize_func_t)(); 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 @@ extern "C" void free_malloc_leak_info(uint8_t* info) { reinterpret_cast(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(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 @@ -392,6 +415,7 @@ static void* LoadSharedLibrary(const char* shared_lib, const char* prefix, Mallo "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 159d4a0a3..18099ddd5 100644 --- a/libc/libc.arm.map +++ b/libc/libc.arm.map @@ -1634,6 +1634,7 @@ LIBC_PRIVATE { 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 7702f9ebe..f37641ef9 100644 --- a/libc/libc.arm64.map +++ b/libc/libc.arm64.map @@ -1352,6 +1352,7 @@ LIBC_PRIVATE { 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 ebe209893..21966b3e7 100644 --- a/libc/libc.map.txt +++ b/libc/libc.map.txt @@ -1660,6 +1660,7 @@ LIBC_PRIVATE { 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 d04cb4f45..0bc6fce07 100644 --- a/libc/libc.mips.map +++ b/libc/libc.mips.map @@ -1475,6 +1475,7 @@ LIBC_PRIVATE { 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 7702f9ebe..f37641ef9 100644 --- a/libc/libc.mips64.map +++ b/libc/libc.mips64.map @@ -1352,6 +1352,7 @@ LIBC_PRIVATE { 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 2e10bbb28..14ee151ce 100644 --- a/libc/libc.x86.map +++ b/libc/libc.x86.map @@ -1474,6 +1474,7 @@ LIBC_PRIVATE { 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 7702f9ebe..f37641ef9 100644 --- a/libc/libc.x86_64.map +++ b/libc/libc.x86_64.map @@ -1352,6 +1352,7 @@ LIBC_PRIVATE { 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 a4ff5d412..0cb4ccad3 100644 --- a/libc/malloc_debug/Android.bp +++ b/libc/malloc_debug/Android.bp @@ -139,5 +139,6 @@ cc_test { "-Wall", "-Werror", "-Wno-error=format-zero-length", + "-O0", ], } diff --git a/libc/malloc_debug/README.md b/libc/malloc_debug/README.md index c427d5f19..c4ee72295 100644 --- a/libc/malloc_debug/README.md +++ b/libc/malloc_debug/README.md @@ -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. ### 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 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 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 78a6990aa..2f590d091 100644 --- a/libc/malloc_debug/exported32.map +++ b/libc/malloc_debug/exported32.map @@ -21,6 +21,7 @@ LIBC_MALLOC_DEBUG { 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 2bfc38b82..08d36a5ca 100644 --- a/libc/malloc_debug/exported64.map +++ b/libc/malloc_debug/exported64.map @@ -19,6 +19,7 @@ LIBC_MALLOC_DEBUG { 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 836c33b86..1e7086c7d 100644 --- a/libc/malloc_debug/malloc_debug.cpp +++ b/libc/malloc_debug/malloc_debug.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -69,9 +70,10 @@ __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_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 @@ void* debug_valloc(size_t size) { 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 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 @@ bool debug_dump_heap(const char* file_name) { 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 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 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 0663f6a46..cd6d2c28e 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 @@ static std::string SanitizeHeapData(const std::string& data) { 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 @@ void MallocDebugTest::BacktraceDumpOnSignal(bool trigger_with_alloc) { 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 @@ TEST_F(MallocDebugTest, backtrace_dump_on_exit) { 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 @@ TEST_F(MallocDebugTest, backtrace_dump_on_exit_shared_backtrace) { 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 @@ TEST_F(MallocDebugTest, backtrace_full_dump_on_exit) { 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