Merge "Add option to force memunreachable check."
This commit is contained in:
commit
bcd8f15e27
9 changed files with 291 additions and 25 deletions
|
@ -64,6 +64,7 @@ cc_library {
|
|||
"malloc_debug.cpp",
|
||||
"PointerData.cpp",
|
||||
"RecordData.cpp",
|
||||
"Unreachable.cpp",
|
||||
"UnwindBacktrace.cpp",
|
||||
],
|
||||
|
||||
|
@ -73,6 +74,7 @@ cc_library {
|
|||
"libasync_safe",
|
||||
"libbase",
|
||||
"libc_malloc_debug_backtrace",
|
||||
"libmemunreachable",
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
|
|
|
@ -70,17 +70,21 @@ static constexpr const char DEFAULT_RECORD_ALLOCS_FILE[] = "/data/local/tmp/reco
|
|||
|
||||
const std::unordered_map<std::string, Config::OptionInfo> Config::kOptions = {
|
||||
{
|
||||
"guard", {FRONT_GUARD | REAR_GUARD | TRACK_ALLOCS, &Config::SetGuard},
|
||||
"guard",
|
||||
{FRONT_GUARD | REAR_GUARD | TRACK_ALLOCS, &Config::SetGuard},
|
||||
},
|
||||
{
|
||||
"front_guard", {FRONT_GUARD | TRACK_ALLOCS, &Config::SetFrontGuard},
|
||||
"front_guard",
|
||||
{FRONT_GUARD | TRACK_ALLOCS, &Config::SetFrontGuard},
|
||||
},
|
||||
{
|
||||
"rear_guard", {REAR_GUARD | TRACK_ALLOCS, &Config::SetRearGuard},
|
||||
"rear_guard",
|
||||
{REAR_GUARD | TRACK_ALLOCS, &Config::SetRearGuard},
|
||||
},
|
||||
|
||||
{
|
||||
"backtrace", {BACKTRACE | TRACK_ALLOCS, &Config::SetBacktrace},
|
||||
"backtrace",
|
||||
{BACKTRACE | TRACK_ALLOCS, &Config::SetBacktrace},
|
||||
},
|
||||
{
|
||||
"backtrace_enable_on_signal",
|
||||
|
@ -88,55 +92,74 @@ const std::unordered_map<std::string, Config::OptionInfo> Config::kOptions = {
|
|||
},
|
||||
|
||||
{
|
||||
"backtrace_dump_on_exit", {0, &Config::SetBacktraceDumpOnExit},
|
||||
"backtrace_dump_on_exit",
|
||||
{0, &Config::SetBacktraceDumpOnExit},
|
||||
},
|
||||
{
|
||||
"backtrace_dump_prefix", {0, &Config::SetBacktraceDumpPrefix},
|
||||
"backtrace_dump_prefix",
|
||||
{0, &Config::SetBacktraceDumpPrefix},
|
||||
},
|
||||
{
|
||||
"backtrace_full", {BACKTRACE_FULL, &Config::VerifyValueEmpty},
|
||||
"backtrace_full",
|
||||
{BACKTRACE_FULL, &Config::VerifyValueEmpty},
|
||||
},
|
||||
|
||||
{
|
||||
"fill", {FILL_ON_ALLOC | FILL_ON_FREE, &Config::SetFill},
|
||||
"fill",
|
||||
{FILL_ON_ALLOC | FILL_ON_FREE, &Config::SetFill},
|
||||
},
|
||||
{
|
||||
"fill_on_alloc", {FILL_ON_ALLOC, &Config::SetFillOnAlloc},
|
||||
"fill_on_alloc",
|
||||
{FILL_ON_ALLOC, &Config::SetFillOnAlloc},
|
||||
},
|
||||
{
|
||||
"fill_on_free", {FILL_ON_FREE, &Config::SetFillOnFree},
|
||||
"fill_on_free",
|
||||
{FILL_ON_FREE, &Config::SetFillOnFree},
|
||||
},
|
||||
|
||||
{
|
||||
"expand_alloc", {EXPAND_ALLOC, &Config::SetExpandAlloc},
|
||||
"expand_alloc",
|
||||
{EXPAND_ALLOC, &Config::SetExpandAlloc},
|
||||
},
|
||||
|
||||
{
|
||||
"free_track", {FREE_TRACK | FILL_ON_FREE | TRACK_ALLOCS, &Config::SetFreeTrack},
|
||||
"free_track",
|
||||
{FREE_TRACK | FILL_ON_FREE | TRACK_ALLOCS, &Config::SetFreeTrack},
|
||||
},
|
||||
{
|
||||
"free_track_backtrace_num_frames", {0, &Config::SetFreeTrackBacktraceNumFrames},
|
||||
"free_track_backtrace_num_frames",
|
||||
{0, &Config::SetFreeTrackBacktraceNumFrames},
|
||||
},
|
||||
|
||||
{
|
||||
"leak_track", {LEAK_TRACK | TRACK_ALLOCS, &Config::VerifyValueEmpty},
|
||||
"leak_track",
|
||||
{LEAK_TRACK | TRACK_ALLOCS, &Config::VerifyValueEmpty},
|
||||
},
|
||||
|
||||
{
|
||||
"record_allocs", {RECORD_ALLOCS, &Config::SetRecordAllocs},
|
||||
"record_allocs",
|
||||
{RECORD_ALLOCS, &Config::SetRecordAllocs},
|
||||
},
|
||||
{
|
||||
"record_allocs_file", {0, &Config::SetRecordAllocsFile},
|
||||
"record_allocs_file",
|
||||
{0, &Config::SetRecordAllocsFile},
|
||||
},
|
||||
|
||||
{
|
||||
"verify_pointers", {TRACK_ALLOCS, &Config::VerifyValueEmpty},
|
||||
"verify_pointers",
|
||||
{TRACK_ALLOCS, &Config::VerifyValueEmpty},
|
||||
},
|
||||
{
|
||||
"abort_on_error", {ABORT_ON_ERROR, &Config::VerifyValueEmpty},
|
||||
"abort_on_error",
|
||||
{ABORT_ON_ERROR, &Config::VerifyValueEmpty},
|
||||
},
|
||||
{
|
||||
"verbose", {VERBOSE, &Config::VerifyValueEmpty},
|
||||
"verbose",
|
||||
{VERBOSE, &Config::VerifyValueEmpty},
|
||||
},
|
||||
{
|
||||
"check_unreachable_on_signal",
|
||||
{CHECK_UNREACHABLE_ON_SIGNAL, &Config::VerifyValueEmpty},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -380,6 +403,7 @@ bool Config::Init(const char* options_str) {
|
|||
backtrace_enabled_ = false;
|
||||
backtrace_dump_on_exit_ = false;
|
||||
backtrace_dump_prefix_ = DEFAULT_BACKTRACE_DUMP_PREFIX;
|
||||
check_unreachable_signal_ = SIGRTMAX - 16;
|
||||
|
||||
// Process each option name we can find.
|
||||
std::string option;
|
||||
|
|
|
@ -46,6 +46,7 @@ constexpr uint64_t RECORD_ALLOCS = 0x200;
|
|||
constexpr uint64_t BACKTRACE_FULL = 0x400;
|
||||
constexpr uint64_t ABORT_ON_ERROR = 0x800;
|
||||
constexpr uint64_t VERBOSE = 0x1000;
|
||||
constexpr uint64_t CHECK_UNREACHABLE_ON_SIGNAL = 0x2000;
|
||||
|
||||
// In order to guarantee posix compliance, set the minimum alignment
|
||||
// to 8 bytes for 32 bit systems and 16 bytes for 64 bit systems.
|
||||
|
@ -93,6 +94,8 @@ class Config {
|
|||
size_t record_allocs_num_entries() const { return record_allocs_num_entries_; }
|
||||
const std::string& record_allocs_file() const { return record_allocs_file_; }
|
||||
|
||||
int check_unreachable_signal() const { return check_unreachable_signal_; }
|
||||
|
||||
private:
|
||||
struct OptionInfo {
|
||||
uint64_t option;
|
||||
|
@ -160,4 +163,6 @@ class Config {
|
|||
uint8_t fill_free_value_;
|
||||
uint8_t front_guard_value_;
|
||||
uint8_t rear_guard_value_;
|
||||
|
||||
int check_unreachable_signal_ = 0;
|
||||
};
|
||||
|
|
|
@ -114,7 +114,7 @@ the backtrace and information about the original allocation. After that, this
|
|||
option will not add a special header.
|
||||
|
||||
As of P, this option will also enable dumping backtrace heap data to a
|
||||
file when the process receives the signal SIGRTMAX - 17 ( which is 47 on most
|
||||
file when the process receives the signal SIGRTMAX - 17 ( which is 47 on
|
||||
Android devices). The format of this dumped data is the same format as
|
||||
that dumped when running am dumpheap -n. The default is to dump this data
|
||||
to the file /data/local/tmp/backtrace\_heap.**PID**.txt. This is useful when
|
||||
|
@ -127,7 +127,7 @@ malloc/free occurs.
|
|||
### backtrace\_enable\_on\_signal[=MAX\_FRAMES]
|
||||
Enable capturing the backtrace of each allocation site. If the
|
||||
backtrace capture is toggled when the process receives the signal
|
||||
SIGRTMAX - 19 (which is 45 on most Android devices). When this
|
||||
SIGRTMAX - 19 (which is 45 on Android devices). When this
|
||||
option is used alone, backtrace capture starts out disabled until the signal
|
||||
is received. If both this option and the backtrace option are set, then
|
||||
backtrace capture is enabled until the signal is received.
|
||||
|
@ -165,6 +165,29 @@ 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.
|
||||
|
||||
### check\_unreachable\_on\_signal
|
||||
As of Android U, this option will trigger a check for unreachable memory
|
||||
in a process. Specifically, if the signal SIGRTMAX - 16 (which is 48 on
|
||||
Android devices). The best way to see the exact signal being used is to
|
||||
enable the verbose option then look at the log for the message:
|
||||
|
||||
Run: 'kill -48 <PID>' to check for unreachable memory.
|
||||
|
||||
When the signal is received, the actual unreachable check only triggers
|
||||
on the next allocation that happens in the process (malloc/free, etc).
|
||||
|
||||
If a process is not doing any allocations, it can be forced to trigger when
|
||||
running:
|
||||
|
||||
debuggerd -b <PID>
|
||||
|
||||
**NOTE**: The unreachable check can fail for protected processes, so it
|
||||
might be necessary to run:
|
||||
|
||||
setenforce 0
|
||||
|
||||
To get the unreachable data.
|
||||
|
||||
### fill\_on\_alloc[=MAX\_FILLED\_BYTES]
|
||||
Any allocation routine, other than calloc, will result in the allocation being
|
||||
filled with the value 0xeb. When doing a realloc to a larger size, the bytes
|
||||
|
@ -270,7 +293,7 @@ Example leak error found in the log:
|
|||
|
||||
### 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 most Android devices)
|
||||
to a file when the signal SIGRTMAX - 18 (which is 46 on Android devices)
|
||||
is received.
|
||||
|
||||
If TOTAL\_ENTRIES is set, then it indicates the total number of
|
||||
|
|
80
libc/malloc_debug/Unreachable.cpp
Normal file
80
libc/malloc_debug/Unreachable.cpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright (C) 2022 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 <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
|
||||
#include <memunreachable/memunreachable.h>
|
||||
#include <platform/bionic/macros.h>
|
||||
|
||||
#include "Config.h"
|
||||
#include "Unreachable.h"
|
||||
#include "debug_log.h"
|
||||
|
||||
std::atomic_bool Unreachable::do_check_;
|
||||
|
||||
static void EnableUnreachableCheck(int, struct siginfo*, void*) {
|
||||
Unreachable::EnableCheck();
|
||||
}
|
||||
|
||||
void Unreachable::CheckIfRequested(const Config& config) {
|
||||
if ((config.options() & CHECK_UNREACHABLE_ON_SIGNAL) && do_check_.exchange(false)) {
|
||||
info_log("Starting to check for unreachable memory.");
|
||||
if (!LogUnreachableMemory(false, 100)) {
|
||||
error_log("Unreachable check failed, run setenforce 0 and try again.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Unreachable::Initialize(const Config& config) {
|
||||
if (!(config.options() & CHECK_UNREACHABLE_ON_SIGNAL)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct sigaction64 unreachable_act = {};
|
||||
unreachable_act.sa_sigaction = EnableUnreachableCheck;
|
||||
unreachable_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
|
||||
if (sigaction64(config.check_unreachable_signal(), &unreachable_act, nullptr) != 0) {
|
||||
error_log("Unable to set up check unreachable signal function: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.options() & VERBOSE) {
|
||||
info_log("%s: Run: 'kill -%d %d' to check for unreachable memory.", getprogname(),
|
||||
config.check_unreachable_signal(), getpid());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
49
libc/malloc_debug/Unreachable.h
Normal file
49
libc/malloc_debug/Unreachable.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (C) 2022 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
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
// Forward declarations
|
||||
class ConfigData;
|
||||
|
||||
class Unreachable {
|
||||
public:
|
||||
static bool Initialize(const Config& config);
|
||||
static void CheckIfRequested(const Config& config);
|
||||
|
||||
static void EnableCheck() { do_check_ = true; }
|
||||
|
||||
private:
|
||||
static std::atomic_bool do_check_;
|
||||
|
||||
BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(Unreachable);
|
||||
};
|
|
@ -53,11 +53,12 @@
|
|||
|
||||
#include "Config.h"
|
||||
#include "DebugData.h"
|
||||
#include "Unreachable.h"
|
||||
#include "UnwindBacktrace.h"
|
||||
#include "backtrace.h"
|
||||
#include "debug_disable.h"
|
||||
#include "debug_log.h"
|
||||
#include "malloc_debug.h"
|
||||
#include "UnwindBacktrace.h"
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Global Data
|
||||
|
@ -315,7 +316,7 @@ bool debug_initialize(const MallocDispatch* malloc_dispatch, bool* zygote_child,
|
|||
}
|
||||
|
||||
DebugData* debug = new DebugData();
|
||||
if (!debug->Initialize(options)) {
|
||||
if (!debug->Initialize(options) || !Unreachable::Initialize(debug->config())) {
|
||||
delete debug;
|
||||
DebugDisableFinalize();
|
||||
return false;
|
||||
|
@ -402,6 +403,8 @@ void debug_free_malloc_leak_info(uint8_t* info) {
|
|||
}
|
||||
|
||||
size_t debug_malloc_usable_size(void* pointer) {
|
||||
Unreachable::CheckIfRequested(g_debug->config());
|
||||
|
||||
if (DebugCallsDisabled() || pointer == nullptr) {
|
||||
return g_dispatch->malloc_usable_size(pointer);
|
||||
}
|
||||
|
@ -467,6 +470,8 @@ static void* InternalMalloc(size_t size) {
|
|||
}
|
||||
|
||||
void* debug_malloc(size_t size) {
|
||||
Unreachable::CheckIfRequested(g_debug->config());
|
||||
|
||||
if (DebugCallsDisabled()) {
|
||||
return g_dispatch->malloc(size);
|
||||
}
|
||||
|
@ -544,6 +549,8 @@ static void InternalFree(void* pointer) {
|
|||
}
|
||||
|
||||
void debug_free(void* pointer) {
|
||||
Unreachable::CheckIfRequested(g_debug->config());
|
||||
|
||||
if (DebugCallsDisabled() || pointer == nullptr) {
|
||||
return g_dispatch->free(pointer);
|
||||
}
|
||||
|
@ -563,6 +570,8 @@ void debug_free(void* pointer) {
|
|||
}
|
||||
|
||||
void* debug_memalign(size_t alignment, size_t bytes) {
|
||||
Unreachable::CheckIfRequested(g_debug->config());
|
||||
|
||||
if (DebugCallsDisabled()) {
|
||||
return g_dispatch->memalign(alignment, bytes);
|
||||
}
|
||||
|
@ -643,6 +652,8 @@ void* debug_memalign(size_t alignment, size_t bytes) {
|
|||
}
|
||||
|
||||
void* debug_realloc(void* pointer, size_t bytes) {
|
||||
Unreachable::CheckIfRequested(g_debug->config());
|
||||
|
||||
if (DebugCallsDisabled()) {
|
||||
return g_dispatch->realloc(pointer, bytes);
|
||||
}
|
||||
|
@ -763,6 +774,8 @@ void* debug_realloc(void* pointer, size_t bytes) {
|
|||
}
|
||||
|
||||
void* debug_calloc(size_t nmemb, size_t bytes) {
|
||||
Unreachable::CheckIfRequested(g_debug->config());
|
||||
|
||||
if (DebugCallsDisabled()) {
|
||||
return g_dispatch->calloc(nmemb, bytes);
|
||||
}
|
||||
|
@ -862,6 +875,8 @@ int debug_malloc_info(int options, FILE* fp) {
|
|||
}
|
||||
|
||||
void* debug_aligned_alloc(size_t alignment, size_t size) {
|
||||
Unreachable::CheckIfRequested(g_debug->config());
|
||||
|
||||
if (DebugCallsDisabled()) {
|
||||
return g_dispatch->aligned_alloc(alignment, size);
|
||||
}
|
||||
|
@ -873,6 +888,8 @@ void* debug_aligned_alloc(size_t alignment, size_t size) {
|
|||
}
|
||||
|
||||
int debug_posix_memalign(void** memptr, size_t alignment, size_t size) {
|
||||
Unreachable::CheckIfRequested(g_debug->config());
|
||||
|
||||
if (DebugCallsDisabled()) {
|
||||
return g_dispatch->posix_memalign(memptr, alignment, size);
|
||||
}
|
||||
|
@ -934,6 +951,8 @@ ssize_t debug_malloc_backtrace(void* pointer, uintptr_t* frames, size_t max_fram
|
|||
|
||||
#if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
|
||||
void* debug_pvalloc(size_t bytes) {
|
||||
Unreachable::CheckIfRequested(g_debug->config());
|
||||
|
||||
if (DebugCallsDisabled()) {
|
||||
return g_dispatch->pvalloc(bytes);
|
||||
}
|
||||
|
@ -949,6 +968,8 @@ void* debug_pvalloc(size_t bytes) {
|
|||
}
|
||||
|
||||
void* debug_valloc(size_t size) {
|
||||
Unreachable::CheckIfRequested(g_debug->config());
|
||||
|
||||
if (DebugCallsDisabled()) {
|
||||
return g_dispatch->valloc(size);
|
||||
}
|
||||
|
|
|
@ -761,3 +761,21 @@ TEST_F(MallocDebugConfigTest, trigger_verbose_fail) {
|
|||
"which does not take a value\n");
|
||||
ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
|
||||
}
|
||||
|
||||
TEST_F(MallocDebugConfigTest, check_unreachable_on_signal) {
|
||||
ASSERT_TRUE(InitConfig("check_unreachable_on_signal")) << getFakeLogPrint();
|
||||
ASSERT_EQ(CHECK_UNREACHABLE_ON_SIGNAL, config->options());
|
||||
|
||||
ASSERT_STREQ("", getFakeLogBuf().c_str());
|
||||
ASSERT_STREQ("", getFakeLogPrint().c_str());
|
||||
}
|
||||
|
||||
TEST_F(MallocDebugConfigTest, trigger_check_unreachable_on_signal_fail) {
|
||||
ASSERT_FALSE(InitConfig("check_unreachable_on_signal=200")) << getFakeLogPrint();
|
||||
|
||||
ASSERT_STREQ("", getFakeLogBuf().c_str());
|
||||
std::string log_msg(
|
||||
"6 malloc_debug malloc_testing: value set for option 'check_unreachable_on_signal' "
|
||||
"which does not take a value\n");
|
||||
ASSERT_STREQ((log_msg + usage_string).c_str(), getFakeLogPrint().c_str());
|
||||
}
|
||||
|
|
|
@ -227,6 +227,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 check for unreachable memory.\n",
|
||||
SIGRTMAX - 16, getpid());
|
||||
}
|
||||
expected_log += "4 malloc_debug malloc_testing: malloc debug enabled\n";
|
||||
ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
|
||||
|
@ -296,6 +299,16 @@ TEST_F(MallocDebugTest, verbose_record_allocs) {
|
|||
ASSERT_STREQ(expected_log.c_str(), getFakeLogPrint().c_str());
|
||||
}
|
||||
|
||||
TEST_F(MallocDebugTest, verbose_check_unreachable_on_signal) {
|
||||
Init("verbose check_unreachable_on_signal");
|
||||
|
||||
std::string expected_log = android::base::StringPrintf(
|
||||
"4 malloc_debug malloc_testing: Run: 'kill -%d %d' to check for unreachable memory.\n",
|
||||
SIGRTMAX - 16, 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");
|
||||
|
||||
|
@ -359,7 +372,7 @@ 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");
|
||||
"record_allocs verify_pointers abort_on_error verbose check_unreachable_on_signal");
|
||||
VerifyAllocCalls(true);
|
||||
}
|
||||
|
||||
|
@ -2695,3 +2708,34 @@ TEST_F(MallocDebugTest, dump_heap) {
|
|||
std::string expected_log = std::string("6 malloc_debug Dumping to file: ") + tf.path + "\n\n";
|
||||
ASSERT_EQ(expected_log, getFakeLogPrint());
|
||||
}
|
||||
|
||||
extern "C" bool LogUnreachableMemory(bool, size_t) {
|
||||
static bool return_value = false;
|
||||
return_value = !return_value;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
TEST_F(MallocDebugTest, check_unreachable_on_signal) {
|
||||
Init("check_unreachable_on_signal");
|
||||
|
||||
ASSERT_TRUE(kill(getpid(), SIGRTMAX - 16) == 0);
|
||||
sleep(1);
|
||||
|
||||
// The first unreachable check will pass.
|
||||
void* pointer = debug_malloc(110);
|
||||
ASSERT_TRUE(pointer != nullptr);
|
||||
|
||||
ASSERT_TRUE(kill(getpid(), SIGRTMAX - 16) == 0);
|
||||
sleep(1);
|
||||
|
||||
// The second unreachable check will fail.
|
||||
debug_free(pointer);
|
||||
|
||||
ASSERT_STREQ("", getFakeLogBuf().c_str());
|
||||
std::string expected_log = "4 malloc_debug Starting to check for unreachable memory.\n";
|
||||
ASSERT_STREQ(
|
||||
"4 malloc_debug Starting to check for unreachable memory.\n"
|
||||
"4 malloc_debug Starting to check for unreachable memory.\n"
|
||||
"6 malloc_debug Unreachable check failed, run setenforce 0 and try again.\n",
|
||||
getFakeLogPrint().c_str());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue