#include "base/trace_event/memory_dump_manager.h"
#include <stdint.h>
#include <memory>
#include <string_view>
#include <utility>
#include <vector>
#include "base/allocator/buildflags.h"
#include "base/at_exit.h"
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/run_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/task_environment.h"
#include "base/test/test_future.h"
#include "base/test/test_io_thread.h"
#include "base/test/trace_test_utils.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "base/trace_event/memory_dump_manager_test_utils.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "base/trace_event/memory_dump_scheduler.h"
#include "base/trace_event/memory_infra_background_allowlist.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_log.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::AtMost;
using testing::Between;
using testing::Return;
namespace base::trace_event {
MATCHER(IsDetailedDump, "") {
return arg.level_of_detail == MemoryDumpLevelOfDetail::kDetailed;
}
MATCHER(IsLightDump, "") {
return arg.level_of_detail == MemoryDumpLevelOfDetail::kLight;
}
MATCHER(IsDeterministicDump, "") {
return arg.determinism == MemoryDumpDeterminism::kForceGc;
}
MATCHER(IsNotDeterministicDump, "") {
return arg.determinism == MemoryDumpDeterminism::kNone;
}
namespace {
constexpr char kMDPName[] = "TestDumpProvider";
constexpr char kAllowlistedMDPName[] = "AllowlistedTestDumpProvider";
constexpr std::string_view kTestMDPAllowlist[] = {kAllowlistedMDPName};
void RegisterDumpProvider(
MemoryDumpProvider* mdp,
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
const MemoryDumpProvider::Options& options,
const char* name = kMDPName) {
MemoryDumpManager* mdm = MemoryDumpManager::GetInstance();
mdm->set_dumper_registrations_ignored_for_testing(false);
mdm->RegisterDumpProvider(mdp, name, std::move(task_runner), options);
mdm->set_dumper_registrations_ignored_for_testing(true);
}
void RegisterDumpProvider(
MemoryDumpProvider* mdp,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
RegisterDumpProvider(mdp, task_runner, MemoryDumpProvider::Options());
}
void RegisterDumpProviderWithSequencedTaskRunner(
MemoryDumpProvider* mdp,
scoped_refptr<base::SequencedTaskRunner> task_runner,
const MemoryDumpProvider::Options& options) {
MemoryDumpManager* mdm = MemoryDumpManager::GetInstance();
mdm->set_dumper_registrations_ignored_for_testing(false);
mdm->RegisterDumpProviderWithSequencedTaskRunner(mdp, kMDPName, task_runner,
options);
mdm->set_dumper_registrations_ignored_for_testing(true);
}
void PostTaskAndWait(const Location& from_here,
SequencedTaskRunner* task_runner,
base::OnceClosure task) {
base::WaitableEvent event(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
task_runner->PostTask(from_here, std::move(task));
task_runner->PostTask(FROM_HERE, base::BindOnce(&WaitableEvent::Signal,
base::Unretained(&event)));
event.Wait();
}
class MockMemoryDumpProvider : public MemoryDumpProvider {
public:
MOCK_METHOD0(Destructor, void());
MOCK_METHOD2(OnMemoryDump,
bool(const MemoryDumpArgs& args, ProcessMemoryDump* pmd));
MockMemoryDumpProvider() {
ON_CALL(*this, OnMemoryDump(_, _))
.WillByDefault([](const MemoryDumpArgs&,
ProcessMemoryDump* pmd) -> bool { return true; });
}
~MockMemoryDumpProvider() override {
if (enable_mock_destructor) {
Destructor();
}
}
bool enable_mock_destructor = false;
};
class TestSequencedTaskRunner : public SequencedTaskRunner {
public:
TestSequencedTaskRunner() = default;
void set_enabled(bool value) { enabled_ = value; }
unsigned no_of_post_tasks() const { return num_of_post_tasks_; }
bool PostNonNestableDelayedTask(const Location& from_here,
OnceClosure task,
TimeDelta delay) override {
NOTREACHED();
}
bool PostDelayedTask(const Location& from_here,
OnceClosure task,
TimeDelta delay) override {
num_of_post_tasks_++;
if (enabled_) {
return task_runner_->PostDelayedTask(from_here, std::move(task), delay);
}
return false;
}
bool RunsTasksInCurrentSequence() const override {
return task_runner_->RunsTasksInCurrentSequence();
}
private:
~TestSequencedTaskRunner() override = default;
const scoped_refptr<SequencedTaskRunner> task_runner_ =
ThreadPool::CreateSequencedTaskRunner({});
bool enabled_ = true;
unsigned num_of_post_tasks_ = 0;
};
}
class MemoryDumpManagerTest : public testing::Test {
public:
explicit MemoryDumpManagerTest(bool is_coordinator = false)
: is_coordinator_(is_coordinator) {}
MemoryDumpManagerTest(const MemoryDumpManagerTest&) = delete;
MemoryDumpManagerTest& operator=(const MemoryDumpManagerTest&) = delete;
void SetUp() override {
mdm_ = MemoryDumpManager::CreateInstanceForTesting();
ASSERT_EQ(mdm_.get(), MemoryDumpManager::GetInstance());
InitializeMemoryDumpManagerForInProcessTesting(is_coordinator_);
task_environment_ = std::make_unique<test::TaskEnvironment>();
tracing_environment_ = std::make_unique<test::TracingEnvironment>();
}
void TearDown() override {
task_environment_.reset();
mdm_.reset();
tracing_environment_.reset();
}
protected:
ProcessMemoryDumpOutcome RequestProcessDumpAndWait(
MemoryDumpType dump_type,
MemoryDumpLevelOfDetail level_of_detail,
MemoryDumpDeterminism determinism) {
static uint64_t test_guid = 1;
test_guid++;
MemoryDumpRequestArgs request_args{test_guid, dump_type, level_of_detail,
determinism};
test::TestFuture<ProcessMemoryDumpOutcome, uint64_t,
std::unique_ptr<ProcessMemoryDump>>
future;
mdm_->CreateProcessDump(request_args, future.GetSequenceBoundCallback());
CHECK(future.Wait());
EXPECT_EQ(future.Get<uint64_t>(), test_guid);
return future.Get<ProcessMemoryDumpOutcome>();
}
void EnableForTracing() {
mdm_->SetupForTracing(TraceConfig::MemoryDumpConfig());
}
void EnableForTracingWithTraceConfig(const std::string& trace_config_string) {
TraceConfig trace_config(trace_config_string);
mdm_->SetupForTracing(trace_config.memory_dump_config());
}
void DisableTracing() { mdm_->TeardownForTracing(); }
int GetMaxConsecutiveFailuresCount() const {
return MemoryDumpManager::kMaxConsecutiveFailuresCount;
}
const MemoryDumpProvider::Options kDefaultOptions;
std::unique_ptr<MemoryDumpManager> mdm_;
private:
ShadowingAtExitManager at_exit_manager_;
std::unique_ptr<test::TaskEnvironment> task_environment_;
std::unique_ptr<test::TracingEnvironment> tracing_environment_;
const bool is_coordinator_;
};
class MemoryDumpManagerTestAsCoordinator : public MemoryDumpManagerTest {
public:
MemoryDumpManagerTestAsCoordinator() : MemoryDumpManagerTest(true) {}
MemoryDumpManagerTestAsCoordinator(
const MemoryDumpManagerTestAsCoordinator&) = delete;
MemoryDumpManagerTestAsCoordinator& operator=(
const MemoryDumpManagerTestAsCoordinator&) = delete;
};
TEST_F(MemoryDumpManagerTest, SingleDumper) {
MockMemoryDumpProvider mdp;
RegisterDumpProvider(&mdp, SingleThreadTaskRunner::GetCurrentDefault());
EnableForTracing();
EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(3);
for (int i = 0; i < 3; ++i) {
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
}
DisableTracing();
mdm_->UnregisterDumpProvider(&mdp);
EnableForTracing();
EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
for (int i = 0; i < 3; ++i) {
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
}
DisableTracing();
}
TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgs) {
MockMemoryDumpProvider mdp;
RegisterDumpProvider(&mdp, SingleThreadTaskRunner::GetCurrentDefault());
EnableForTracing();
EXPECT_CALL(mdp, OnMemoryDump(IsDetailedDump(), _));
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
DisableTracing();
mdm_->UnregisterDumpProvider(&mdp);
RegisterDumpProvider(&mdp, SingleThreadTaskRunner::GetCurrentDefault());
EnableForTracing();
EXPECT_CALL(mdp, OnMemoryDump(IsLightDump(), _));
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kLight,
MemoryDumpDeterminism::kNone));
DisableTracing();
mdm_->UnregisterDumpProvider(&mdp);
}
TEST_F(MemoryDumpManagerTest, CheckMemoryDumpArgsDeterministic) {
MockMemoryDumpProvider mdp;
RegisterDumpProvider(&mdp, SingleThreadTaskRunner::GetCurrentDefault());
EnableForTracing();
EXPECT_CALL(mdp, OnMemoryDump(IsDeterministicDump(), _));
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kForceGc));
DisableTracing();
mdm_->UnregisterDumpProvider(&mdp);
RegisterDumpProvider(&mdp, SingleThreadTaskRunner::GetCurrentDefault());
EnableForTracing();
EXPECT_CALL(mdp, OnMemoryDump(IsNotDeterministicDump(), _));
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kLight,
MemoryDumpDeterminism::kNone));
DisableTracing();
mdm_->UnregisterDumpProvider(&mdp);
}
TEST_F(MemoryDumpManagerTest, MultipleDumpers) {
MockMemoryDumpProvider mdp1;
MockMemoryDumpProvider mdp2;
RegisterDumpProvider(&mdp1, SingleThreadTaskRunner::GetCurrentDefault());
EnableForTracing();
EXPECT_CALL(mdp1, OnMemoryDump(_, _));
EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(0);
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
DisableTracing();
mdm_->UnregisterDumpProvider(&mdp1);
RegisterDumpProvider(&mdp2, nullptr);
EnableForTracing();
EXPECT_CALL(mdp1, OnMemoryDump(_, _)).Times(0);
EXPECT_CALL(mdp2, OnMemoryDump(_, _));
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
DisableTracing();
RegisterDumpProvider(&mdp1, nullptr);
EnableForTracing();
EXPECT_CALL(mdp1, OnMemoryDump(_, _));
EXPECT_CALL(mdp2, OnMemoryDump(_, _));
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
DisableTracing();
}
#if BUILDFLAG(IS_IOS)
#define MAYBE_RegistrationConsistency DISABLED_RegistrationConsistency
#else
#define MAYBE_RegistrationConsistency RegistrationConsistency
#endif
TEST_F(MemoryDumpManagerTest, MAYBE_RegistrationConsistency) {
MockMemoryDumpProvider mdp;
RegisterDumpProvider(&mdp, SingleThreadTaskRunner::GetCurrentDefault());
{
EXPECT_CALL(mdp, OnMemoryDump(_, _));
EnableForTracing();
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
DisableTracing();
}
mdm_->UnregisterDumpProvider(&mdp);
{
EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
EnableForTracing();
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
DisableTracing();
}
RegisterDumpProvider(&mdp, SingleThreadTaskRunner::GetCurrentDefault());
mdm_->UnregisterDumpProvider(&mdp);
{
EXPECT_CALL(mdp, OnMemoryDump(_, _)).Times(0);
EnableForTracing();
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
DisableTracing();
}
RegisterDumpProvider(&mdp, SingleThreadTaskRunner::GetCurrentDefault());
mdm_->UnregisterDumpProvider(&mdp);
RegisterDumpProvider(&mdp, SingleThreadTaskRunner::GetCurrentDefault());
{
EXPECT_CALL(mdp, OnMemoryDump(_, _));
EnableForTracing();
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
DisableTracing();
}
}
TEST_F(MemoryDumpManagerTest, RespectTaskRunnerAffinity) {
const uint32_t kNumInitialThreads = 8;
std::vector<std::unique_ptr<Thread>> threads;
std::vector<std::unique_ptr<MockMemoryDumpProvider>> mdps;
for (uint32_t i = kNumInitialThreads; i > 0; --i) {
threads.push_back(std::make_unique<Thread>("test thread"));
auto* thread = threads.back().get();
thread->Start();
scoped_refptr<SingleThreadTaskRunner> task_runner = thread->task_runner();
mdps.push_back(std::make_unique<MockMemoryDumpProvider>());
auto* mdp = mdps.back().get();
RegisterDumpProvider(mdp, task_runner, kDefaultOptions);
EXPECT_CALL(*mdp, OnMemoryDump(_, _))
.Times(i)
.WillRepeatedly(
[task_runner](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
EXPECT_TRUE(task_runner->RunsTasksInCurrentSequence());
return true;
});
}
EnableForTracing();
while (!threads.empty()) {
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
{
RunLoop run_loop;
OnceClosure unregistration =
BindOnce(&MemoryDumpManager::UnregisterDumpProvider,
Unretained(mdm_.get()), Unretained(mdps.back().get()));
threads.back()->task_runner()->PostTaskAndReply(
FROM_HERE, std::move(unregistration), run_loop.QuitClosure());
run_loop.Run();
}
mdps.pop_back();
threads.back()->Stop();
threads.pop_back();
}
DisableTracing();
}
TEST_F(MemoryDumpManagerTest, PostTaskForSequencedTaskRunner) {
std::vector<MockMemoryDumpProvider> mdps(3);
scoped_refptr<TestSequencedTaskRunner> task_runner1(
MakeRefCounted<TestSequencedTaskRunner>());
scoped_refptr<TestSequencedTaskRunner> task_runner2(
MakeRefCounted<TestSequencedTaskRunner>());
RegisterDumpProviderWithSequencedTaskRunner(&mdps[0], task_runner1,
kDefaultOptions);
RegisterDumpProviderWithSequencedTaskRunner(&mdps[1], task_runner2,
kDefaultOptions);
RegisterDumpProviderWithSequencedTaskRunner(&mdps[2], task_runner2,
kDefaultOptions);
EXPECT_CALL(mdps[0], OnMemoryDump(_, _)).Times(0);
EXPECT_CALL(mdps[1], OnMemoryDump(_, _)).Times(2);
EXPECT_CALL(mdps[2], OnMemoryDump(_, _)).Times(2);
EnableForTracing();
task_runner1->set_enabled(false);
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
EXPECT_EQ(1u, task_runner1->no_of_post_tasks());
EXPECT_EQ(1u, task_runner2->no_of_post_tasks());
task_runner1->set_enabled(true);
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
EXPECT_EQ(2u, task_runner1->no_of_post_tasks());
EXPECT_EQ(2u, task_runner2->no_of_post_tasks());
DisableTracing();
}
TEST_F(MemoryDumpManagerTest, DisableFailingDumpers) {
MockMemoryDumpProvider mdp1;
MockMemoryDumpProvider mdp2;
RegisterDumpProvider(&mdp1, nullptr);
RegisterDumpProvider(&mdp2, nullptr);
EnableForTracing();
EXPECT_CALL(mdp1, OnMemoryDump(_, _))
.Times(GetMaxConsecutiveFailuresCount())
.WillRepeatedly(Return(false));
EXPECT_CALL(mdp2, OnMemoryDump(_, _))
.WillOnce(Return(false))
.WillOnce(Return(true))
.WillOnce(Return(false))
.WillOnce(Return(false))
.WillOnce(Return(true))
.WillOnce(Return(false));
const int kNumDumps = 2 * GetMaxConsecutiveFailuresCount();
for (int i = 0; i < kNumDumps; i++) {
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
}
DisableTracing();
}
TEST_F(MemoryDumpManagerTest, RegisterDumperWhileDumping) {
MockMemoryDumpProvider mdp1;
MockMemoryDumpProvider mdp2;
RegisterDumpProvider(&mdp1, nullptr);
EnableForTracing();
EXPECT_CALL(mdp1, OnMemoryDump(_, _))
.Times(4)
.WillOnce(Return(true))
.WillOnce([&mdp2](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
RegisterDumpProvider(&mdp2, nullptr);
return true;
})
.WillRepeatedly(Return(true));
EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(Between(2, 3));
for (int i = 0; i < 4; i++) {
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
}
DisableTracing();
}
TEST_F(MemoryDumpManagerTest, UnregisterDumperWhileDumping) {
MockMemoryDumpProvider mdp1;
MockMemoryDumpProvider mdp2;
RegisterDumpProvider(&mdp1, SingleThreadTaskRunner::GetCurrentDefault(),
kDefaultOptions);
RegisterDumpProvider(&mdp2, SingleThreadTaskRunner::GetCurrentDefault(),
kDefaultOptions);
EnableForTracing();
EXPECT_CALL(mdp1, OnMemoryDump(_, _))
.Times(4)
.WillOnce(Return(true))
.WillOnce([&mdp2](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
MemoryDumpManager::GetInstance()->UnregisterDumpProvider(&mdp2);
return true;
})
.WillRepeatedly(Return(true));
EXPECT_CALL(mdp2, OnMemoryDump(_, _)).Times(Between(1, 2));
for (int i = 0; i < 4; i++) {
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
}
DisableTracing();
}
TEST_F(MemoryDumpManagerTest, UnregisterDumperFromThreadWhileDumping) {
std::vector<std::unique_ptr<TestIOThread>> threads;
std::vector<std::unique_ptr<MockMemoryDumpProvider>> mdps;
for (int i = 0; i < 2; i++) {
threads.push_back(std::make_unique<TestIOThread>(TestIOThread::kAutoStart));
mdps.push_back(std::make_unique<MockMemoryDumpProvider>());
RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(),
kDefaultOptions);
}
int on_memory_dump_call_count = 0;
for (const std::unique_ptr<MockMemoryDumpProvider>& mdp : mdps) {
int other_idx = (mdps.front() == mdp);
scoped_refptr<SingleThreadTaskRunner> other_runner =
threads[other_idx]->task_runner();
MockMemoryDumpProvider* other_mdp = mdps[other_idx].get();
auto on_dump = [this, other_runner, other_mdp, &on_memory_dump_call_count](
const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
PostTaskAndWait(FROM_HERE, other_runner.get(),
base::BindOnce(&MemoryDumpManager::UnregisterDumpProvider,
base::Unretained(&*mdm_), other_mdp));
on_memory_dump_call_count++;
return true;
};
EXPECT_CALL(*mdp, OnMemoryDump(_, _)).Times(AtMost(1)).WillOnce(on_dump);
}
EnableForTracing();
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
ASSERT_EQ(1, on_memory_dump_call_count);
DisableTracing();
}
TEST_F(MemoryDumpManagerTest, TearDownThreadWhileDumping) {
std::vector<std::unique_ptr<TestIOThread>> threads;
std::vector<std::unique_ptr<MockMemoryDumpProvider>> mdps;
for (int i = 0; i < 2; i++) {
threads.push_back(std::make_unique<TestIOThread>(TestIOThread::kAutoStart));
mdps.push_back(std::make_unique<MockMemoryDumpProvider>());
RegisterDumpProvider(mdps.back().get(), threads.back()->task_runner(),
kDefaultOptions);
}
int on_memory_dump_call_count = 0;
for (const std::unique_ptr<MockMemoryDumpProvider>& mdp : mdps) {
int other_idx = (mdps.front() == mdp);
TestIOThread* other_thread = threads[other_idx].get();
scoped_refptr<SequencedTaskRunner> main_runner =
SequencedTaskRunner::GetCurrentDefault();
auto on_dump = [other_thread, main_runner, &on_memory_dump_call_count](
const MemoryDumpArgs& args, ProcessMemoryDump* pmd) {
PostTaskAndWait(
FROM_HERE, main_runner.get(),
base::BindOnce(&TestIOThread::Stop, base::Unretained(other_thread)));
on_memory_dump_call_count++;
return true;
};
EXPECT_CALL(*mdp, OnMemoryDump(_, _)).Times(AtMost(1)).WillOnce(on_dump);
}
EnableForTracing();
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
ASSERT_EQ(1, on_memory_dump_call_count);
DisableTracing();
}
TEST_F(MemoryDumpManagerTest, TriggerDumpWithoutTracing) {
MockMemoryDumpProvider mdp;
RegisterDumpProvider(&mdp, nullptr);
EXPECT_CALL(mdp, OnMemoryDump(_, _));
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
}
TEST_F(MemoryDumpManagerTest, BackgroundAllowlisting) {
SetDumpProviderAllowlistForTesting(kTestMDPAllowlist);
MockMemoryDumpProvider backgroundMdp;
RegisterDumpProvider(&backgroundMdp, nullptr, kDefaultOptions,
kAllowlistedMDPName);
EnableForTracing();
EXPECT_CALL(backgroundMdp, OnMemoryDump(_, _)).Times(1);
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kSummaryOnly,
MemoryDumpLevelOfDetail::kBackground,
MemoryDumpDeterminism::kNone));
DisableTracing();
}
TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoon) {
static const int kNumProviders = 3;
int dtor_count = 0;
std::vector<std::unique_ptr<MemoryDumpProvider>> mdps;
for (int i = 0; i < kNumProviders; ++i) {
auto mdp = std::make_unique<MockMemoryDumpProvider>();
mdp->enable_mock_destructor = true;
EXPECT_CALL(*mdp, Destructor()).WillOnce([&dtor_count] { dtor_count++; });
RegisterDumpProvider(mdp.get(), nullptr, kDefaultOptions);
mdps.push_back(std::move(mdp));
}
while (!mdps.empty()) {
mdm_->UnregisterAndDeleteDumpProviderSoon(std::move(mdps.back()));
mdps.pop_back();
}
ASSERT_EQ(kNumProviders, dtor_count);
}
TEST_F(MemoryDumpManagerTest, UnregisterAndDeleteDumpProviderSoonDuringDump) {
auto mdp = std::make_unique<MockMemoryDumpProvider>();
mdp->enable_mock_destructor = true;
RegisterDumpProvider(mdp.get(), nullptr, kDefaultOptions);
base::PlatformThreadRef thread_ref;
auto self_unregister_from_another_thread =
[&mdp, &thread_ref](const MemoryDumpArgs&, ProcessMemoryDump*) -> bool {
thread_ref = PlatformThread::CurrentRef();
TestIOThread thread_for_unregistration(TestIOThread::kAutoStart);
PostTaskAndWait(
FROM_HERE, thread_for_unregistration.task_runner().get(),
base::BindOnce(&MemoryDumpManager::UnregisterAndDeleteDumpProviderSoon,
base::Unretained(MemoryDumpManager::GetInstance()),
std::move(mdp)));
thread_for_unregistration.Stop();
return true;
};
EXPECT_CALL(*mdp, OnMemoryDump(_, _))
.Times(1)
.WillOnce(self_unregister_from_another_thread);
EXPECT_CALL(*mdp, Destructor()).Times(1).WillOnce([&thread_ref] {
EXPECT_EQ(thread_ref, PlatformThread::CurrentRef());
});
EnableForTracing();
for (int i = 0; i < 2; ++i) {
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
}
DisableTracing();
}
class SimpleMockMemoryDumpProvider : public MemoryDumpProvider {
public:
explicit SimpleMockMemoryDumpProvider(int expected_num_dump_calls)
: expected_num_dump_calls_(expected_num_dump_calls) {}
~SimpleMockMemoryDumpProvider() override {
EXPECT_EQ(expected_num_dump_calls_, num_dump_calls_);
}
bool OnMemoryDump(const MemoryDumpArgs& args,
ProcessMemoryDump* pmd) override {
++num_dump_calls_;
return true;
}
private:
int expected_num_dump_calls_;
int num_dump_calls_ = 0;
};
TEST_F(MemoryDumpManagerTest, NoStackOverflowWithTooManyMDPs) {
SetDumpProviderAllowlistForTesting(kTestMDPAllowlist);
int kMDPCount = 1000;
std::vector<std::unique_ptr<SimpleMockMemoryDumpProvider>> mdps;
for (int i = 0; i < kMDPCount; ++i) {
mdps.push_back(std::make_unique<SimpleMockMemoryDumpProvider>(1));
RegisterDumpProvider(mdps.back().get(), nullptr);
}
for (int i = 0; i < kMDPCount; ++i) {
mdps.push_back(std::make_unique<SimpleMockMemoryDumpProvider>(3));
RegisterDumpProvider(mdps.back().get(), nullptr, kDefaultOptions,
kAllowlistedMDPName);
}
auto stopped_thread = std::make_unique<Thread>("test thread");
stopped_thread->Start();
for (int i = 0; i < kMDPCount; ++i) {
mdps.push_back(std::make_unique<SimpleMockMemoryDumpProvider>(0));
RegisterDumpProvider(mdps.back().get(), stopped_thread->task_runner(),
kDefaultOptions, kAllowlistedMDPName);
}
stopped_thread->Stop();
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kDetailed,
MemoryDumpDeterminism::kNone));
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kExplicitlyTriggered,
MemoryDumpLevelOfDetail::kBackground,
MemoryDumpDeterminism::kNone));
EXPECT_EQ(ProcessMemoryDumpOutcome::kSuccess,
RequestProcessDumpAndWait(MemoryDumpType::kSummaryOnly,
MemoryDumpLevelOfDetail::kBackground,
MemoryDumpDeterminism::kNone));
}
}