#include "base/profiler/thread_group_profiler.h"
#include <map>
#include <memory>
#include "base/functional/bind.h"
#include "base/functional/function_ref.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/profiler/periodic_sampling_scheduler.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/profiler/stack_sampling_profiler_test_util.h"
#include "base/profiler/thread_group_profiler_client.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/gtest_util.h"
#include "base/test/task_environment.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace base {
namespace internal {
class WorkerThread;
}
namespace {
constexpr int kSamplesPerProfile = 20;
constexpr TimeDelta kSamplingInterval = Milliseconds(100);
constexpr TimeDelta kTimeToNextCollection = Hours(1);
}
class MockPeriodicSamplingScheduler : public PeriodicSamplingScheduler {
public:
explicit MockPeriodicSamplingScheduler(TimeDelta time_to_next_collection)
: PeriodicSamplingScheduler(Seconds(30), 0.02, TimeTicks::Now()),
time_to_next_collection_(time_to_next_collection) {}
TimeDelta GetTimeToNextCollection() override {
return time_to_next_collection_;
}
protected:
TimeDelta time_to_next_collection_;
};
class MockProfileBuilder : public ProfileBuilder {
public:
explicit MockProfileBuilder(OnceClosure completed_callback)
: completed_callback_(std::move(completed_callback)) {}
void OnProfileCompleted(TimeDelta profile_duration,
TimeDelta sampling_period) override {
std::move(completed_callback_).Run();
}
ModuleCache* GetModuleCache() override { return &module_cache_; }
MOCK_METHOD2(OnSampleCompleted,
void(std::vector<Frame> frames, TimeTicks sample_timestamp));
protected:
ModuleCache module_cache_;
OnceClosure completed_callback_;
};
class MockThreadGroupProfilerClient : public ThreadGroupProfilerClient {
public:
MockThreadGroupProfilerClient() = default;
StackSamplingProfiler::SamplingParams GetSamplingParams() override {
return {.samples_per_profile = kSamplesPerProfile,
.sampling_interval = kSamplingInterval};
}
std::unique_ptr<ProfileBuilder> CreateProfileBuilder(
OnceClosure callback) override {
return std::make_unique<MockProfileBuilder>(std::move(callback));
}
bool IsProfilerEnabledForCurrentProcess() override { return true; }
bool IsSingleProcess(const CommandLine& command_line) override {
return false;
}
StackSamplingProfiler::UnwindersFactory GetUnwindersFactory() override {
return CreateCoreUnwindersFactoryForTesting(nullptr);
}
};
class MockProfiler : public ThreadGroupProfiler::Profiler {
public:
MockProfiler(std::map<PlatformThreadId, MockProfiler*>& sampling_profilers,
int& sampling_profilers_created,
PlatformThreadId target_thread_id,
const StackSamplingProfiler::SamplingParams& params,
std::unique_ptr<ProfileBuilder> profile_builder)
: sampling_profilers_(sampling_profilers),
sampling_profilers_created_(sampling_profilers_created),
target_thread_id_(target_thread_id),
sampling_params_(params),
profile_builder_(std::move(profile_builder)) {
EXPECT_EQ(sampling_profilers_->count(target_thread_id_), 0);
(*sampling_profilers_)[target_thread_id_] = this;
++*sampling_profilers_created_;
EXPECT_CALL(*this, Start());
}
~MockProfiler() override { sampling_profilers_->erase(target_thread_id_); }
MOCK_METHOD(void, Start, (), (override));
const StackSamplingProfiler::SamplingParams& sampling_params() const {
return sampling_params_;
}
void CompleteProfiling() {
profile_builder_->OnProfileCompleted(TimeDelta(), TimeDelta());
}
private:
raw_ref<std::map<PlatformThreadId, MockProfiler*>> sampling_profilers_;
raw_ref<int> sampling_profilers_created_;
PlatformThreadId target_thread_id_;
const StackSamplingProfiler::SamplingParams sampling_params_;
std::unique_ptr<ProfileBuilder> profile_builder_;
};
ThreadGroupProfiler::ProfilerFactory GetMockProfilerFactory(
std::map<PlatformThreadId, MockProfiler*>& sampling_profilers,
int& sampling_profilers_created) {
return BindRepeating(BindLambdaForTesting(
[&sampling_profilers, &sampling_profilers_created](
SamplingProfilerThreadToken thread_token,
const StackSamplingProfiler::SamplingParams& params,
std::unique_ptr<ProfileBuilder> profile_builder,
StackSamplingProfiler::UnwindersFactory unwinder_factory)
-> std::unique_ptr<ThreadGroupProfiler::Profiler> {
return std::make_unique<MockProfiler>(
sampling_profilers, sampling_profilers_created, thread_token.id,
params, std::move(profile_builder));
}));
}
class ThreadGroupProfilerTest : public testing::Test {
public:
void SetUp() override {
ThreadGroupProfiler::SetClient(
std::make_unique<MockThreadGroupProfilerClient>());
profiler_ = std::make_unique<ThreadGroupProfiler>(
ThreadPool::CreateSequencedTaskRunner({MayBlock()}),
0,
std::make_unique<MockPeriodicSamplingScheduler>(kTimeToNextCollection),
GetMockProfilerFactory(sampling_profilers_,
sampling_profilers_created_));
}
void TearDown() override {
task_environment_.reset();
if (!shutdown_started_) {
profiler_->Shutdown();
}
ThreadGroupProfiler::SetClient(nullptr);
}
protected:
class FakeWorkerThread : public Thread {
public:
FakeWorkerThread(internal::WorkerThread* fake_pointer,
test::TaskEnvironment& task_environment)
: Thread("FakeWorkerThread"),
fake_pointer_(fake_pointer),
task_environment_(task_environment) {
Start();
}
~FakeWorkerThread() override { Stop(); }
void RunOnThread(FunctionRef<void(internal::WorkerThread*)> callable) {
WaitableEvent callable_completed{
WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED};
RunOnThreadAsync(callable, callable_completed);
callable_completed.Wait();
task_environment_->RunUntilIdle();
}
void RunOnThreadAsync(FunctionRef<void(internal::WorkerThread*)> callable,
WaitableEvent& callable_completed) {
task_runner()->PostTask(
FROM_HERE, BindLambdaForTesting([callable, &callable_completed,
fake_pointer = fake_pointer_] {
callable(fake_pointer);
callable_completed.Signal();
}));
}
private:
raw_ptr<internal::WorkerThread> const fake_pointer_;
raw_ref<test::TaskEnvironment> task_environment_;
};
std::unique_ptr<FakeWorkerThread> CreateFakeWorkerThread() {
return std::make_unique<FakeWorkerThread>(
reinterpret_cast<internal::WorkerThread*>(next_worker_thread_id_++),
*task_environment_);
}
void InitiateNextCollection() {
task_environment_->FastForwardBy(time_to_next_collection_);
task_environment_->RunUntilIdle();
time_to_next_collection_ = kTimeToNextCollection;
}
void AdvanceBySamples(int samples) {
const TimeDelta samples_duration = kSamplingInterval * samples;
task_environment_->FastForwardBy(samples_duration);
task_environment_->RunUntilIdle();
time_to_next_collection_ -= samples_duration;
}
void AdvanceToEndOfCollection() {
const TimeDelta duration_already_advanced =
kTimeToNextCollection - time_to_next_collection_;
const TimeDelta collection_duration =
kSamplingInterval * kSamplesPerProfile;
const TimeDelta duration_to_end_of_collection =
collection_duration - duration_already_advanced;
task_environment_->FastForwardBy(duration_to_end_of_collection);
task_environment_->RunUntilIdle();
time_to_next_collection_ -= duration_to_end_of_collection;
}
void CompleteProfiling(MockProfiler* profiler) {
profiler->CompleteProfiling();
task_environment_->RunUntilIdle();
}
std::optional<test::TaskEnvironment> task_environment_{
std::in_place, test::TaskEnvironment::TimeSource::MOCK_TIME,
test::TaskEnvironment::ThreadPoolExecutionMode::ASYNC};
std::map<PlatformThreadId, MockProfiler*> sampling_profilers_;
int sampling_profilers_created_ = 0;
std::unique_ptr<ThreadGroupProfiler> profiler_;
ModuleCache module_cache_;
int next_worker_thread_id_ = 1;
TimeDelta time_to_next_collection_ = kTimeToNextCollection;
bool shutdown_started_ = false;
};
TEST_F(ThreadGroupProfilerTest, Construction) {
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest, CollectionInactive_WorkerInactiveLifecycle) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadExiting(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest, CollectionInactive_WorkerActiveLifecycle) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadIdle(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadExiting(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_NoWorkers) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_WorkerExited) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadExiting(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_InactiveWorker) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_NewlyInactiveWorker) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadIdle(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_ActiveWorker) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
InitiateNextCollection();
EXPECT_EQ(sampling_profilers_.size(), 1);
ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
sampling_profilers_.end());
MockProfiler* const sampling_profiler =
sampling_profilers_[worker->GetThreadId()];
EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
kSamplesPerProfile);
EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
kSamplingInterval);
}
TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_ReactivatedWorker) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadIdle(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
InitiateNextCollection();
EXPECT_EQ(sampling_profilers_.size(), 1);
ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
sampling_profilers_.end());
MockProfiler* const sampling_profiler =
sampling_profilers_[worker->GetThreadId()];
EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
kSamplesPerProfile);
EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
kSamplingInterval);
}
TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_MultipleWorkers) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
std::unique_ptr<FakeWorkerThread> worker2 = CreateFakeWorkerThread();
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
worker2->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
worker2->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
InitiateNextCollection();
EXPECT_EQ(sampling_profilers_.size(), 2);
ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
sampling_profilers_.end());
ASSERT_TRUE(sampling_profilers_.find(worker2->GetThreadId()) !=
sampling_profilers_.end());
MockProfiler* const sampling_profiler =
sampling_profilers_[worker->GetThreadId()];
EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
kSamplesPerProfile);
EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
kSamplingInterval);
MockProfiler* const sampling_profiler2 =
sampling_profilers_[worker2->GetThreadId()];
EXPECT_EQ(sampling_profiler2->sampling_params().samples_per_profile,
kSamplesPerProfile);
EXPECT_EQ(sampling_profiler2->sampling_params().sampling_interval,
kSamplingInterval);
}
TEST_F(ThreadGroupProfilerTest, CollectionBecomesActive_WorkerBecomesActive) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
sampling_profilers_.end());
MockProfiler* const sampling_profiler =
sampling_profilers_[worker->GetThreadId()];
EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
kSamplesPerProfile);
EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
kSamplingInterval);
}
TEST_F(ThreadGroupProfilerTest, CollectionActive_WorkerInactiveLifecycle) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadExiting(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest, CollectionActive_WorkerActiveStartsProfiling) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
sampling_profilers_.end());
MockProfiler* const sampling_profiler =
sampling_profilers_[worker->GetThreadId()];
EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
kSamplesPerProfile);
EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
kSamplingInterval);
}
TEST_F(ThreadGroupProfilerTest,
CollectionActive_WorkerReactivatedContinuesExistingProfiling) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadIdle(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
EXPECT_EQ(sampling_profilers_created_, 1);
}
TEST_F(ThreadGroupProfilerTest,
CollectionActive_WorkerActiveToIdleContinuesProfiling) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadIdle(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
EXPECT_EQ(sampling_profilers_created_, 1);
ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
sampling_profilers_.end());
}
TEST_F(ThreadGroupProfilerTest,
CollectionActive_WorkerActiveToExitStopsProfiling) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadExiting(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest,
CollectionActive_CollectionContinuesOnWorkerExit) {
std::unique_ptr<FakeWorkerThread> worker_to_exit = CreateFakeWorkerThread();
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
worker_to_exit->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker_to_exit->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
worker_to_exit->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadExiting(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
}
TEST_F(ThreadGroupProfilerTest,
CollectionActive_WorkerProfilesOnlyUntilEndOfCollection) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
AdvanceBySamples(5);
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
sampling_profilers_.end());
MockProfiler* const sampling_profiler =
sampling_profilers_[worker->GetThreadId()];
EXPECT_EQ(sampling_profiler->sampling_params().samples_per_profile,
kSamplesPerProfile - 5);
EXPECT_EQ(sampling_profiler->sampling_params().sampling_interval,
kSamplingInterval);
}
TEST_F(ThreadGroupProfilerTest,
CollectionActive_WorkerDoesNotProfileNearEndOfCollection) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
AdvanceBySamples(kSamplesPerProfile - 5);
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest, CollectionEnded_NoWorkers) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
AdvanceToEndOfCollection();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest,
CollectionEnded_ActiveWorker_FinishesBeforeCollection) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
AdvanceBySamples(kSamplesPerProfile - 1);
EXPECT_EQ(sampling_profilers_.size(), 1);
ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
sampling_profilers_.end());
CompleteProfiling(sampling_profilers_[worker->GetThreadId()]);
EXPECT_TRUE(sampling_profilers_.empty());
AdvanceToEndOfCollection();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadIdle(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest,
CollectionEnded_ActiveWorker_FinishesAfterCollection) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
InitiateNextCollection();
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_EQ(sampling_profilers_.size(), 1);
AdvanceToEndOfCollection();
EXPECT_EQ(sampling_profilers_.size(), 1);
EXPECT_EQ(sampling_profilers_.size(), 1);
ASSERT_TRUE(sampling_profilers_.find(worker->GetThreadId()) !=
sampling_profilers_.end());
CompleteProfiling(sampling_profilers_[worker->GetThreadId()]);
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadIdle(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
worker->RunOnThread([this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
});
EXPECT_TRUE(sampling_profilers_.empty());
}
TEST_F(ThreadGroupProfilerTest, PostTaskRunnerShutdown) {
std::unique_ptr<FakeWorkerThread> worker = CreateFakeWorkerThread();
task_environment_.reset();
WaitableEvent on_thread_call_completed{
WaitableEvent::ResetPolicy::AUTOMATIC,
WaitableEvent::InitialState::NOT_SIGNALED};
auto worker_started = [this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadStarted(worker_thread);
};
worker->RunOnThreadAsync(worker_started, on_thread_call_completed);
on_thread_call_completed.Wait();
EXPECT_TRUE(sampling_profilers_.empty());
auto worker_active = [this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadActive(worker_thread);
};
worker->RunOnThreadAsync(worker_active, on_thread_call_completed);
on_thread_call_completed.Wait();
EXPECT_TRUE(sampling_profilers_.empty());
auto worker_idle = [this](internal::WorkerThread* worker_thread) {
profiler_->OnWorkerThreadIdle(worker_thread);
};
worker->RunOnThreadAsync(worker_idle, on_thread_call_completed);
on_thread_call_completed.Wait();
EXPECT_TRUE(sampling_profilers_.empty());
auto worker_exit =
[profiler = profiler_.get()](internal::WorkerThread* worker_thread) {
profiler->OnWorkerThreadExiting(worker_thread);
};
worker->RunOnThreadAsync(worker_exit, on_thread_call_completed);
profiler_->Shutdown();
shutdown_started_ = true;
on_thread_call_completed.Wait();
EXPECT_TRUE(sampling_profilers_.empty());
}
}