From 30659fd243d3996e0bb70be92469c65117cd465d Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Mon, 15 Apr 2019 19:01:08 -0700 Subject: [PATCH] Move all leak info functions to android_mallopt. Bug: 130028357 Test: malloc_hooks unit tests. Test: Enable backtrace for mediaserver, run dumpsys media.player -m Test: Enable backtrace for calendar, run am dumpheap -n Change-Id: I6774e28ccd9b3f2310127a5b39ccd15fe696a787 Merged-In: I6774e28ccd9b3f2310127a5b39ccd15fe696a787 (cherry picked from commit 3aadc5e80a5e2cf6b6760ed90d528709223bb449) --- libc/bionic/malloc_common.cpp | 9 +-- libc/bionic/malloc_common_dynamic.cpp | 59 +++++++++++-------- libc/libc.map.txt | 7 +-- .../malloc_hooks/tests/malloc_hooks_tests.cpp | 21 +++---- libc/private/bionic_malloc.h | 36 +++++++++++ 5 files changed, 83 insertions(+), 49 deletions(-) diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp index 3f1bcc36d..60ee138c7 100644 --- a/libc/bionic/malloc_common.cpp +++ b/libc/bionic/malloc_common.cpp @@ -32,14 +32,7 @@ // calls and add special debugging code to attempt to catch allocation // errors. All of the debugging code is implemented in a separate shared // library that is only loaded when the property "libc.debug.malloc.options" -// is set to a non-zero value. There are two functions exported to -// allow ddms, or other external users to get information from the debug -// allocation. -// get_malloc_leak_info: Returns information about all of the known native -// 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. +// is set to a non-zero value. #include #include diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp index 64f9f6f7b..599ac6a58 100644 --- a/libc/bionic/malloc_common_dynamic.cpp +++ b/libc/bionic/malloc_common_dynamic.cpp @@ -409,39 +409,29 @@ __LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals) { // ============================================================================= // Functions to support dumping of native heap allocations using malloc debug. // ============================================================================= - -// Retrieve native heap information. -// -// "*info" is set to a buffer we allocate -// "*overall_size" is set to the size of the "info" buffer -// "*info_size" is set to the size of a single entry -// "*total_memory" is set to the sum of all allocations we're tracking; does -// not include heap overhead -// "*backtrace_size" is set to the maximum number of entries in the back trace -extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overall_size, - size_t* info_size, size_t* total_memory, size_t* backtrace_size) { +bool GetMallocLeakInfo(android_mallopt_leak_info_t* leak_info) { void* func = gFunctions[FUNC_GET_MALLOC_LEAK_INFO]; if (func == nullptr) { - return; + errno = ENOTSUP; + return false; } - reinterpret_cast(func)(info, overall_size, info_size, total_memory, - backtrace_size); + reinterpret_cast(func)( + &leak_info->buffer, &leak_info->overall_size, &leak_info->info_size, + &leak_info->total_memory, &leak_info->backtrace_size); + return true; } -extern "C" void free_malloc_leak_info(uint8_t* info) { +bool FreeMallocLeakInfo(android_mallopt_leak_info_t* leak_info) { void* func = gFunctions[FUNC_FREE_MALLOC_LEAK_INFO]; if (func == nullptr) { - return; + errno = ENOTSUP; + return false; } - reinterpret_cast(func)(info); + reinterpret_cast(func)(leak_info->buffer); + return true; } -extern "C" void write_malloc_leak_info(FILE* fp) { - if (fp == nullptr) { - error_log("write_malloc_leak_info called with a nullptr"); - return; - } - +bool WriteMallocLeakInfo(FILE* fp) { void* func = gFunctions[FUNC_WRITE_LEAK_INFO]; bool written = false; if (func != nullptr) { @@ -453,7 +443,9 @@ extern "C" void write_malloc_leak_info(FILE* fp) { fprintf(fp, "# adb shell stop\n"); fprintf(fp, "# adb shell setprop libc.debug.malloc.options backtrace\n"); fprintf(fp, "# adb shell start\n"); + errno = ENOTSUP; } + return written; } // ============================================================================= @@ -484,6 +476,27 @@ extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) { if (opcode == M_SET_ALLOCATION_LIMIT_BYTES) { return LimitEnable(arg, arg_size); } + if (opcode == M_WRITE_MALLOC_LEAK_INFO_TO_FILE) { + if (arg == nullptr || arg_size != sizeof(FILE*)) { + errno = EINVAL; + return false; + } + return WriteMallocLeakInfo(reinterpret_cast(arg)); + } + if (opcode == M_GET_MALLOC_LEAK_INFO) { + if (arg == nullptr || arg_size != sizeof(android_mallopt_leak_info_t)) { + errno = EINVAL; + return false; + } + return GetMallocLeakInfo(reinterpret_cast(arg)); + } + if (opcode == M_FREE_MALLOC_LEAK_INFO) { + if (arg == nullptr || arg_size != sizeof(android_mallopt_leak_info_t)) { + errno = EINVAL; + return false; + } + return FreeMallocLeakInfo(reinterpret_cast(arg)); + } return HeapprofdMallopt(opcode, arg, arg_size); } // ============================================================================= diff --git a/libc/libc.map.txt b/libc/libc.map.txt index 55ca9dcfb..f0fccf553 100644 --- a/libc/libc.map.txt +++ b/libc/libc.map.txt @@ -1473,15 +1473,10 @@ LIBC_Q { # introduced=Q malloc_enable; # apex malloc_iterate; # apex - # Used by libmediautils - write_malloc_leak_info; # apex - free_malloc_leak_info; # apex - get_malloc_leak_info; # apex - # Used by libandroid_net android_getaddrinfofornet; # apex - # Used by libandroid_runtime and libmedia + # Used by libandroid_runtime, libmedia and libmediautils android_mallopt; # apex } LIBC_P; diff --git a/libc/malloc_hooks/tests/malloc_hooks_tests.cpp b/libc/malloc_hooks/tests/malloc_hooks_tests.cpp index 0d23a6a8f..86e20ea90 100644 --- a/libc/malloc_hooks/tests/malloc_hooks_tests.cpp +++ b/libc/malloc_hooks/tests/malloc_hooks_tests.cpp @@ -38,6 +38,7 @@ #include +#include #include #include @@ -197,19 +198,15 @@ TEST_F(MallocHooksTest, extended_functions) { } TEST_F(MallocHooksTest, DISABLED_extended_functions) { - uint8_t* info = nullptr; - size_t overall_size = 100; - size_t info_size = 200; - size_t total_memory = 300; - size_t backtrace_size = 400; - get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); - EXPECT_EQ(nullptr, info); - EXPECT_EQ(0U, overall_size); - EXPECT_EQ(0U, info_size); - EXPECT_EQ(0U, total_memory); - EXPECT_EQ(0U, backtrace_size); + android_mallopt_leak_info_t leak_info; + ASSERT_TRUE(android_mallopt(M_GET_MALLOC_LEAK_INFO, &leak_info, sizeof(leak_info))); + EXPECT_EQ(nullptr, leak_info.buffer); + EXPECT_EQ(0U, leak_info.overall_size); + EXPECT_EQ(0U, leak_info.info_size); + EXPECT_EQ(0U, leak_info.total_memory); + EXPECT_EQ(0U, leak_info.backtrace_size); - free_malloc_leak_info(info); + ASSERT_TRUE(android_mallopt(M_FREE_MALLOC_LEAK_INFO, &leak_info, sizeof(leak_info))); malloc_enable(); malloc_disable(); diff --git a/libc/private/bionic_malloc.h b/libc/private/bionic_malloc.h index e8a6f6ef5..9c602eadf 100644 --- a/libc/private/bionic_malloc.h +++ b/libc/private/bionic_malloc.h @@ -30,6 +30,22 @@ #include +// Structures for android_mallopt. + +typedef struct { + // Pointer to the buffer allocated by a call to M_GET_MALLOC_LEAK_INFO. + uint8_t* buffer; + // The size of the "info" buffer. + size_t overall_size; + // The size of a single entry. + size_t info_size; + // The sum of all allocations that have been tracked. Does not include + // any heap overhead. + size_t total_memory; + // The maximum number of backtrace entries. + size_t backtrace_size; +} android_mallopt_leak_info_t; + // Opcodes for android_mallopt. enum { @@ -48,6 +64,26 @@ enum { // Called after the zygote forks to indicate this is a child. M_SET_ZYGOTE_CHILD = 4, #define M_SET_ZYGOTE_CHILD M_SET_ZYGOTE_CHILD + + // Options to dump backtraces of allocations. These options only + // work when malloc debug has been enabled. + + // Writes the backtrace information of all current allocations to a file. + // NOTE: arg_size has to be sizeof(FILE*) because FILE is an opaque type. + // arg = FILE* + // arg_size = sizeof(FILE*) + M_WRITE_MALLOC_LEAK_INFO_TO_FILE = 5, +#define M_WRITE_MALLOC_LEAK_INFO_TO_FILE M_WRITE_MALLOC_LEAK_INFO_TO_FILE + // Get information about the backtraces of all + // arg = android_mallopt_leak_info_t* + // arg_size = sizeof(android_mallopt_leak_info_t) + M_GET_MALLOC_LEAK_INFO = 6, +#define M_GET_MALLOC_LEAK_INFO M_GET_MALLOC_LEAK_INFO + // Free the memory allocated and returned by M_GET_MALLOC_LEAK_INFO. + // arg = android_mallopt_leak_info_t* + // arg_size = sizeof(android_mallopt_leak_info_t) + M_FREE_MALLOC_LEAK_INFO = 7, +#define M_FREE_MALLOC_LEAK_INFO M_FREE_MALLOC_LEAK_INFO }; // Manipulates bionic-specific handling of memory allocation APIs such as