Merge "Add support for signal dumping log stats." into main am: 74ab77df47
Original change: https://android-review.googlesource.com/c/platform/bionic/+/2848756 Change-Id: I2d34e9dcee4145c99e9de877063d2e024e663bb4 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
ce22c7a43c
10 changed files with 206 additions and 3 deletions
|
@ -61,6 +61,7 @@ cc_library {
|
|||
"DebugData.cpp",
|
||||
"debug_disable.cpp",
|
||||
"GuardData.cpp",
|
||||
"LogAllocatorStats.cpp",
|
||||
"malloc_debug.cpp",
|
||||
"PointerData.cpp",
|
||||
"RecordData.cpp",
|
||||
|
|
|
@ -204,6 +204,10 @@ const std::unordered_map<std::string, Config::OptionInfo> Config::kOptions = {
|
|||
"check_unreachable_on_signal",
|
||||
{CHECK_UNREACHABLE_ON_SIGNAL, &Config::VerifyValueEmpty},
|
||||
},
|
||||
{
|
||||
"log_allocator_stats_on_signal",
|
||||
{LOG_ALLOCATOR_STATS_ON_SIGNAL, &Config::VerifyValueEmpty},
|
||||
},
|
||||
};
|
||||
|
||||
bool Config::ParseValue(const std::string& option, const std::string& value, size_t min_value,
|
||||
|
@ -467,6 +471,7 @@ bool Config::Init(const char* options_str) {
|
|||
backtrace_min_size_bytes_ = 0;
|
||||
backtrace_max_size_bytes_ = SIZE_MAX;
|
||||
check_unreachable_signal_ = SIGRTMAX - 16;
|
||||
log_allocator_stats_signal_ = SIGRTMAX - 15;
|
||||
|
||||
// Process each option name we can find.
|
||||
std::string option;
|
||||
|
|
|
@ -48,6 +48,7 @@ constexpr uint64_t ABORT_ON_ERROR = 0x800;
|
|||
constexpr uint64_t VERBOSE = 0x1000;
|
||||
constexpr uint64_t CHECK_UNREACHABLE_ON_SIGNAL = 0x2000;
|
||||
constexpr uint64_t BACKTRACE_SPECIFIC_SIZES = 0x4000;
|
||||
constexpr uint64_t LOG_ALLOCATOR_STATS_ON_SIGNAL = 0x8000;
|
||||
|
||||
// In order to guarantee posix compliance, set the minimum alignment
|
||||
// to 8 bytes for 32 bit systems and 16 bytes for 64 bit systems.
|
||||
|
@ -100,6 +101,8 @@ class Config {
|
|||
|
||||
int check_unreachable_signal() const { return check_unreachable_signal_; }
|
||||
|
||||
int log_allocator_stats_signal() const { return log_allocator_stats_signal_; }
|
||||
|
||||
private:
|
||||
struct OptionInfo {
|
||||
uint64_t option;
|
||||
|
@ -175,4 +178,5 @@ class Config {
|
|||
uint8_t rear_guard_value_;
|
||||
|
||||
int check_unreachable_signal_ = 0;
|
||||
int log_allocator_stats_signal_ = 0;
|
||||
};
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "Config.h"
|
||||
#include "DebugData.h"
|
||||
#include "GuardData.h"
|
||||
#include "LogAllocatorStats.h"
|
||||
#include "PointerData.h"
|
||||
#include "debug_disable.h"
|
||||
#include "malloc_debug.h"
|
||||
|
@ -75,6 +76,13 @@ bool DebugData::Initialize(const char* options) {
|
|||
if (config_.options() & EXPAND_ALLOC) {
|
||||
extra_bytes_ += config_.expand_alloc_bytes();
|
||||
}
|
||||
|
||||
if (config_.options() & LOG_ALLOCATOR_STATS_ON_SIGNAL) {
|
||||
if (!LogAllocatorStats::Initialize(config_)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
73
libc/malloc_debug/LogAllocatorStats.cpp
Normal file
73
libc/malloc_debug/LogAllocatorStats.cpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Config.h"
|
||||
#include "LogAllocatorStats.h"
|
||||
#include "debug_log.h"
|
||||
|
||||
namespace LogAllocatorStats {
|
||||
|
||||
static std::atomic_bool g_call_mallopt = {};
|
||||
|
||||
static void CallMalloptLogStats(int, struct siginfo*, void*) {
|
||||
g_call_mallopt = true;
|
||||
}
|
||||
|
||||
void CheckIfShouldLog() {
|
||||
bool expected = true;
|
||||
if (g_call_mallopt.compare_exchange_strong(expected, false)) {
|
||||
info_log("Logging allocator stats...");
|
||||
if (mallopt(M_LOG_STATS, 0) == 0) {
|
||||
error_log("mallopt(M_LOG_STATS, 0) call failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Initialize(const Config& config) {
|
||||
struct sigaction64 log_stats_act = {};
|
||||
log_stats_act.sa_sigaction = CallMalloptLogStats;
|
||||
log_stats_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
|
||||
if (sigaction64(config.log_allocator_stats_signal(), &log_stats_act, nullptr) != 0) {
|
||||
error_log("Unable to set up log allocator stats signal function: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.options() & VERBOSE) {
|
||||
info_log("%s: Run: 'kill -%d %d' to log allocator stats.", getprogname(),
|
||||
config.log_allocator_stats_signal(), getpid());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace LogAllocatorStats
|
40
libc/malloc_debug/LogAllocatorStats.h
Normal file
40
libc/malloc_debug/LogAllocatorStats.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// Forward declarations
|
||||
class ConfigData;
|
||||
|
||||
namespace LogAllocatorStats {
|
||||
|
||||
bool Initialize(const Config& config);
|
||||
|
||||
void CheckIfShouldLog();
|
||||
|
||||
} // namespace LogAllocatorStats
|
|
@ -340,6 +340,16 @@ Example leak error found in the log:
|
|||
04-15 12:35:33.305 7412 7412 E malloc_debug: #02 pc 000a9e38 /system/lib/libc++.so
|
||||
04-15 12:35:33.305 7412 7412 E malloc_debug: #03 pc 000a28a8 /system/lib/libc++.so
|
||||
|
||||
### log\_allocator\_stats\_on\_signal
|
||||
As of Android V, this option will trigger a call to:
|
||||
|
||||
mallopt(M_LOG_STATS, 0);
|
||||
|
||||
When a process receives the signal SIGRTMAX - 15 (which is 49 on Android
|
||||
devices). The mallopt call is not async safe and is not called from the
|
||||
signal handler directly. Instead, the next time any allocation call occurs,
|
||||
the mallopt is called.
|
||||
|
||||
### record\_allocs[=TOTAL\_ENTRIES]
|
||||
Keep track of every allocation/free made on every thread and dump them
|
||||
to a file when the signal SIGRTMAX - 18 (which is 46 on Android devices)
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
|
||||
#include "Config.h"
|
||||
#include "DebugData.h"
|
||||
#include "LogAllocatorStats.h"
|
||||
#include "Unreachable.h"
|
||||
#include "UnwindBacktrace.h"
|
||||
#include "backtrace.h"
|
||||
|
@ -517,11 +518,15 @@ size_t debug_malloc_usable_size(void* pointer) {
|
|||
}
|
||||
|
||||
static TimedResult InternalMalloc(size_t size) {
|
||||
if ((g_debug->config().options() & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset()) {
|
||||
uint64_t options = g_debug->config().options();
|
||||
if ((options & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset()) {
|
||||
debug_dump_heap(android::base::StringPrintf(
|
||||
"%s.%d.txt", g_debug->config().backtrace_dump_prefix().c_str(), getpid())
|
||||
.c_str());
|
||||
}
|
||||
if (options & LOG_ALLOCATOR_STATS_ON_SIGNAL) {
|
||||
LogAllocatorStats::CheckIfShouldLog();
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
size = 1;
|
||||
|
@ -593,11 +598,15 @@ void* debug_malloc(size_t size) {
|
|||
}
|
||||
|
||||
static TimedResult InternalFree(void* pointer) {
|
||||
if ((g_debug->config().options() & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset()) {
|
||||
uint64_t options = g_debug->config().options();
|
||||
if ((options & BACKTRACE) && g_debug->pointer->ShouldDumpAndReset()) {
|
||||
debug_dump_heap(android::base::StringPrintf(
|
||||
"%s.%d.txt", g_debug->config().backtrace_dump_prefix().c_str(), getpid())
|
||||
.c_str());
|
||||
}
|
||||
if (options & LOG_ALLOCATOR_STATS_ON_SIGNAL) {
|
||||
LogAllocatorStats::CheckIfShouldLog();
|
||||
}
|
||||
|
||||
void* free_pointer = pointer;
|
||||
size_t bytes;
|
||||
|
|
|
@ -824,6 +824,24 @@ TEST_F(MallocDebugConfigTest, trigger_check_unreachable_on_signal_fail) {
|
|||
ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
|
||||
}
|
||||
|
||||
TEST_F(MallocDebugConfigTest, log_allocator_stats_on_signal) {
|
||||
ASSERT_TRUE(InitConfig("log_allocator_stats_on_signal")) << getFakeLogPrint();
|
||||
ASSERT_EQ(LOG_ALLOCATOR_STATS_ON_SIGNAL, config->options());
|
||||
|
||||
ASSERT_STREQ("", getFakeLogBuf().c_str());
|
||||
ASSERT_STREQ("", getFakeLogPrint().c_str());
|
||||
}
|
||||
|
||||
TEST_F(MallocDebugConfigTest, trigger_log_allocator_stats_on_signal_fail) {
|
||||
ASSERT_FALSE(InitConfig("log_allocator_stats_on_signal=200")) << getFakeLogPrint();
|
||||
|
||||
ASSERT_STREQ("", getFakeLogBuf().c_str());
|
||||
std::string log_msg(
|
||||
"6 malloc_debug malloc_testing: value set for option 'log_allocator_stats_on_signal' "
|
||||
"which does not take a value\n");
|
||||
ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
|
||||
}
|
||||
|
||||
TEST_F(MallocDebugConfigTest, size) {
|
||||
ASSERT_TRUE(InitConfig("backtrace_size=37")) << getFakeLogPrint();
|
||||
ASSERT_EQ(BACKTRACE_SPECIFIC_SIZES, config->options());
|
||||
|
|
|
@ -266,6 +266,9 @@ void VerifyAllocCalls(bool all_options) {
|
|||
expected_log += android::base::StringPrintf(
|
||||
"4 malloc_debug malloc_testing: Run: 'kill -%d %d' to dump the allocation records.\n",
|
||||
SIGRTMAX - 18, getpid());
|
||||
expected_log += android::base::StringPrintf(
|
||||
"4 malloc_debug malloc_testing: Run: 'kill -%d %d' to log allocator stats.\n",
|
||||
SIGRTMAX - 15, getpid());
|
||||
expected_log += android::base::StringPrintf(
|
||||
"4 malloc_debug malloc_testing: Run: 'kill -%d %d' to check for unreachable memory.\n",
|
||||
SIGRTMAX - 16, getpid());
|
||||
|
@ -348,6 +351,16 @@ TEST_F(MallocDebugTest, verbose_check_unreachable_on_signal) {
|
|||
ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
|
||||
}
|
||||
|
||||
TEST_F(MallocDebugTest, verbose_log_allocator_stats_on_signal) {
|
||||
Init("verbose log_allocator_stats_on_signal");
|
||||
|
||||
std::string expected_log = android::base::StringPrintf(
|
||||
"4 malloc_debug malloc_testing: Run: 'kill -%d %d' to log allocator stats.\n", SIGRTMAX - 15,
|
||||
getpid());
|
||||
expected_log += "4 malloc_debug malloc_testing: malloc debug enabled\n";
|
||||
ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
|
||||
}
|
||||
|
||||
TEST_F(MallocDebugTest, fill_on_free) {
|
||||
Init("fill_on_free free_track free_track_backtrace_num_frames=0");
|
||||
|
||||
|
@ -411,7 +424,8 @@ TEST_F(MallocDebugTest, free_track_partial) {
|
|||
TEST_F(MallocDebugTest, all_options) {
|
||||
Init(
|
||||
"guard backtrace backtrace_enable_on_signal fill expand_alloc free_track leak_track "
|
||||
"record_allocs verify_pointers abort_on_error verbose check_unreachable_on_signal");
|
||||
"record_allocs verify_pointers abort_on_error verbose check_unreachable_on_signal "
|
||||
"log_allocator_stats_on_signal");
|
||||
VerifyAllocCalls(true);
|
||||
}
|
||||
|
||||
|
@ -2781,6 +2795,27 @@ TEST_F(MallocDebugTest, check_unreachable_on_signal) {
|
|||
getFakeLogPrint().c_str());
|
||||
}
|
||||
|
||||
TEST_F(MallocDebugTest, log_allocator_stats_on_signal) {
|
||||
Init("log_allocator_stats_on_signal");
|
||||
|
||||
ASSERT_TRUE(kill(getpid(), SIGRTMAX - 15) == 0);
|
||||
sleep(1);
|
||||
|
||||
// The first unreachable check will pass.
|
||||
void* pointer = debug_malloc(110);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
debug_free(pointer);
|
||||
|
||||
ASSERT_STREQ("", getFakeLogBuf().c_str());
|
||||
if (!running_with_hwasan()) {
|
||||
// Do an exact match because the mallopt should not fail in normal operation.
|
||||
ASSERT_STREQ("4 malloc_debug Logging allocator stats...\n", getFakeLogPrint().c_str());
|
||||
} else {
|
||||
// mallopt fails with hwasan, so just verify that the message is present.
|
||||
ASSERT_MATCH(getFakeLogPrint(), "4 malloc_debug Logging allocator stats...\\n");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MallocDebugTest, backtrace_only_some_sizes_with_backtrace_size) {
|
||||
Init("leak_track backtrace backtrace_size=120");
|
||||
|
||||
|
|
Loading…
Reference in a new issue