#include "base/profiler/stack_sampling_profiler.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <array>
#include <cstdlib>
#include <memory>
#include <set>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/metrics_hashes.h"
#include "base/notimplemented.h"
#include "base/profiler/profiler_buildflags.h"
#include "base/profiler/sample_metadata.h"
#include "base/profiler/stack_sampler.h"
#include "base/profiler/stack_sampling_profiler_test_util.h"
#include "base/profiler/unwinder.h"
#include "base/run_loop.h"
#include "base/scoped_native_library.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/threading/simple_thread.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#include <intrin.h>
#include <malloc.h>
#else
#include <alloca.h>
#endif
#if (BUILDFLAG(IS_WIN) && defined(ARCH_CPU_X86_64)) || (BUILDFLAG(IS_MAC)) || \
(BUILDFLAG(IS_IOS) && defined(ARCH_CPU_64_BITS)) || \
(BUILDFLAG(IS_ANDROID) && \
(BUILDFLAG(ENABLE_ARM_CFI_TABLE) || defined(ARCH_CPU_ARM64))) || \
(BUILDFLAG(IS_CHROMEOS) && \
(defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64)) && \
!defined(MEMORY_SANITIZER))
#define STACK_SAMPLING_PROFILER_SUPPORTED 1
#endif
namespace base {
#if defined(STACK_SAMPLING_PROFILER_SUPPORTED)
#define PROFILER_TEST_F(TestClass, TestName) TEST_F(TestClass, TestName)
#else
#define PROFILER_TEST_F(TestClass, TestName) \
TEST_F(TestClass, DISABLED_##TestName)
#endif
using SamplingParams = StackSamplingProfiler::SamplingParams;
namespace {
struct RetrospectiveMetadata {
TimeTicks period_start;
TimeTicks period_end;
MetadataRecorder::Item item;
};
struct Profile {
std::vector<std::vector<Frame>> samples;
int record_metadata_count;
std::vector<RetrospectiveMetadata> retrospective_metadata;
std::vector<MetadataRecorder::Item> profile_metadata;
TimeDelta profile_duration;
TimeDelta sampling_period;
};
using ProfileCompletedCallback = OnceCallback<void(Profile)>;
class TestProfileBuilder : public ProfileBuilder {
public:
TestProfileBuilder(ModuleCache* module_cache,
ProfileCompletedCallback callback);
TestProfileBuilder(const TestProfileBuilder&) = delete;
TestProfileBuilder& operator=(const TestProfileBuilder&) = delete;
~TestProfileBuilder() override;
ModuleCache* GetModuleCache() override;
void RecordMetadata(
const MetadataRecorder::MetadataProvider& metadata_provider) override;
void ApplyMetadataRetrospectively(
TimeTicks period_start,
TimeTicks period_end,
const MetadataRecorder::Item& item) override;
void AddProfileMetadata(const MetadataRecorder::Item& item) override;
void OnSampleCompleted(std::vector<Frame> sample,
TimeTicks sample_timestamp) override;
void OnProfileCompleted(TimeDelta profile_duration,
TimeDelta sampling_period) override;
private:
raw_ptr<ModuleCache> module_cache_;
std::vector<std::vector<Frame>> samples_;
int record_metadata_count_ = 0;
std::vector<RetrospectiveMetadata> retrospective_metadata_;
std::vector<MetadataRecorder::Item> profile_metadata_;
ProfileCompletedCallback callback_;
};
TestProfileBuilder::TestProfileBuilder(ModuleCache* module_cache,
ProfileCompletedCallback callback)
: module_cache_(module_cache), callback_(std::move(callback)) {}
TestProfileBuilder::~TestProfileBuilder() = default;
ModuleCache* TestProfileBuilder::GetModuleCache() {
return module_cache_;
}
void TestProfileBuilder::RecordMetadata(
const MetadataRecorder::MetadataProvider& metadata_provider) {
++record_metadata_count_;
}
void TestProfileBuilder::ApplyMetadataRetrospectively(
TimeTicks period_start,
TimeTicks period_end,
const MetadataRecorder::Item& item) {
retrospective_metadata_.push_back(
RetrospectiveMetadata{period_start, period_end, item});
}
void TestProfileBuilder::AddProfileMetadata(
const MetadataRecorder::Item& item) {
profile_metadata_.push_back(item);
}
void TestProfileBuilder::OnSampleCompleted(std::vector<Frame> sample,
TimeTicks sample_timestamp) {
samples_.push_back(std::move(sample));
}
void TestProfileBuilder::OnProfileCompleted(TimeDelta profile_duration,
TimeDelta sampling_period) {
std::move(callback_).Run(Profile{samples_, record_metadata_count_,
retrospective_metadata_, profile_metadata_,
profile_duration, sampling_period});
}
void SynchronousUnloadNativeLibrary(NativeLibrary library) {
UnloadNativeLibrary(library);
#if BUILDFLAG(IS_WIN)
uintptr_t module_base_address = reinterpret_cast<uintptr_t>(library);
HMODULE module_handle;
while (::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
reinterpret_cast<LPCTSTR>(module_base_address),
&module_handle) ||
::GetLastError() != ERROR_MOD_NOT_FOUND) {
PlatformThread::Sleep(Milliseconds(1));
}
#elif BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS)
#else
NOTIMPLEMENTED();
#endif
}
void WithTargetThread(ProfileCallback profile_callback) {
UnwindScenario scenario(BindRepeating(&CallWithPlainFunction));
WithTargetThread(&scenario, std::move(profile_callback));
}
struct TestProfilerInfo {
TestProfilerInfo(SamplingProfilerThreadToken thread_token,
const SamplingParams& params,
ModuleCache* module_cache,
StackSamplerTestDelegate* delegate = nullptr)
: completed(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED),
profiler(thread_token,
params,
std::make_unique<TestProfileBuilder>(
module_cache,
BindLambdaForTesting([this](Profile result_profile) {
profile = std::move(result_profile);
completed.Signal();
})),
CreateCoreUnwindersFactoryForTesting(module_cache),
RepeatingClosure(),
delegate) {}
TestProfilerInfo(const TestProfilerInfo&) = delete;
TestProfilerInfo& operator=(const TestProfilerInfo&) = delete;
Profile profile;
WaitableEvent completed;
StackSamplingProfiler profiler;
};
std::vector<std::vector<Frame>> CaptureSamples(const SamplingParams& params,
TimeDelta profiler_wait_time,
ModuleCache* module_cache) {
std::vector<std::vector<Frame>> samples;
WithTargetThread(BindLambdaForTesting(
[&](SamplingProfilerThreadToken target_thread_token) {
TestProfilerInfo info(target_thread_token, params, module_cache);
info.profiler.Start();
info.completed.TimedWait(profiler_wait_time);
info.profiler.Stop();
info.completed.Wait();
samples = std::move(info.profile.samples);
}));
return samples;
}
size_t WaitForSamplingComplete(
const std::vector<std::unique_ptr<TestProfilerInfo>>& infos) {
std::vector<WaitableEvent*> sampling_completed_rawptrs(infos.size());
std::ranges::transform(infos, sampling_completed_rawptrs.begin(),
[](const std::unique_ptr<TestProfilerInfo>& info) {
return &info.get()->completed;
});
return WaitableEvent::WaitMany(sampling_completed_rawptrs);
}
TimeDelta AVeryLongTimeDelta() {
return Days(1);
}
void TestLibraryUnload(bool wait_until_unloaded, ModuleCache* module_cache) {
class StackCopiedSignaler : public StackSamplerTestDelegate {
public:
StackCopiedSignaler(WaitableEvent* stack_copied,
WaitableEvent* start_stack_walk,
bool wait_to_walk_stack)
: stack_copied_(stack_copied),
start_stack_walk_(start_stack_walk),
wait_to_walk_stack_(wait_to_walk_stack) {}
void OnPreStackWalk() override {
stack_copied_->Signal();
if (wait_to_walk_stack_) {
start_stack_walk_->Wait();
}
}
private:
const raw_ptr<WaitableEvent> stack_copied_;
const raw_ptr<WaitableEvent> start_stack_walk_;
const bool wait_to_walk_stack_;
};
SamplingParams params;
params.sampling_interval = Milliseconds(0);
params.samples_per_profile = 1;
NativeLibrary other_library = LoadOtherLibrary();
UnwindScenario scenario(BindRepeating(
&CallThroughOtherLibrary, UnsafeDanglingUntriaged(other_library)));
UnwindScenario::SampleEvents events;
TargetThread target_thread(
BindLambdaForTesting([&] { scenario.Execute(&events); }));
target_thread.Start();
events.ready_for_sample.Wait();
WaitableEvent sampling_thread_completed(
WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
Profile profile;
WaitableEvent stack_copied(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
WaitableEvent start_stack_walk(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
StackCopiedSignaler test_delegate(&stack_copied, &start_stack_walk,
wait_until_unloaded);
StackSamplingProfiler profiler(
target_thread.thread_token(), params,
std::make_unique<TestProfileBuilder>(
module_cache,
BindLambdaForTesting(
[&profile, &sampling_thread_completed](Profile result_profile) {
profile = std::move(result_profile);
sampling_thread_completed.Signal();
})),
CreateCoreUnwindersFactoryForTesting(module_cache), RepeatingClosure(),
&test_delegate);
profiler.Start();
stack_copied.Wait();
events.sample_finished.Signal();
target_thread.Join();
if (wait_until_unloaded) {
SynchronousUnloadNativeLibrary(other_library);
} else {
UnloadNativeLibrary(other_library);
}
start_stack_walk.Signal();
sampling_thread_completed.Wait();
ASSERT_EQ(1u, profile.samples.size());
const std::vector<Frame>& sample = profile.samples[0];
if (wait_until_unloaded) {
EXPECT_EQ(nullptr, sample.back().module)
<< "Stack:\n"
<< FormatSampleForDiagnosticOutput(sample);
ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange()});
ExpectStackDoesNotContain(sample,
{scenario.GetSetupFunctionAddressRange(),
scenario.GetOuterFunctionAddressRange()});
} else {
if (!sample.back().module) {
ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange()});
ExpectStackDoesNotContain(sample,
{scenario.GetSetupFunctionAddressRange(),
scenario.GetOuterFunctionAddressRange()});
return;
}
ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
scenario.GetSetupFunctionAddressRange(),
scenario.GetOuterFunctionAddressRange()});
}
}
class StackSamplingProfilerTest : public testing::Test {
public:
StackSamplingProfilerTest() = default;
void SetUp() override {
StackSamplingProfiler::TestPeer::DisableIdleShutdown();
}
void TearDown() override {
StackSamplingProfiler::TestPeer::Reset();
}
protected:
ModuleCache* module_cache() { return &module_cache_; }
private:
ModuleCache module_cache_;
base::test::TaskEnvironment task_environment_;
};
}
#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) || \
(defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_ANDROID)) || \
(BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
#define MAYBE_Basic DISABLED_Basic
#else
#define MAYBE_Basic Basic
#endif
PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_Basic) {
UnwindScenario scenario(BindRepeating(&CallWithPlainFunction));
const std::vector<Frame>& sample = SampleScenario(&scenario, module_cache());
for (const auto& frame : sample) {
EXPECT_NE(nullptr, frame.module);
}
ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
scenario.GetSetupFunctionAddressRange(),
scenario.GetOuterFunctionAddressRange()});
}
class TestAuxUnwinder : public Unwinder {
public:
TestAuxUnwinder(const Frame& frame_to_report,
base::RepeatingClosure add_initial_modules_callback)
: frame_to_report_(frame_to_report),
add_initial_modules_callback_(std::move(add_initial_modules_callback)) {
}
TestAuxUnwinder(const TestAuxUnwinder&) = delete;
TestAuxUnwinder& operator=(const TestAuxUnwinder&) = delete;
void InitializeModules() override {
if (add_initial_modules_callback_) {
add_initial_modules_callback_.Run();
}
}
bool CanUnwindFrom(const Frame& current_frame) const override { return true; }
UnwindResult TryUnwind(UnwinderStateCapture* capture_state,
RegisterContext* thread_context,
uintptr_t stack_top,
std::vector<Frame>* stack) override {
stack->push_back(frame_to_report_);
return UnwindResult::kAborted;
}
private:
const Frame frame_to_report_;
base::RepeatingClosure add_initial_modules_callback_;
};
#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) || \
BUILDFLAG(IS_ANDROID) || \
(BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
#define MAYBE_Alloca DISABLED_Alloca
#else
#define MAYBE_Alloca Alloca
#endif
PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_Alloca) {
UnwindScenario scenario(BindRepeating(&CallWithAlloca));
const std::vector<Frame>& sample = SampleScenario(&scenario, module_cache());
ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
scenario.GetSetupFunctionAddressRange(),
scenario.GetOuterFunctionAddressRange()});
}
#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) || \
BUILDFLAG(IS_IOS) || \
(BUILDFLAG(IS_ANDROID) && BUILDFLAG(EXCLUDE_UNWIND_TABLES)) || \
(BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)) || \
(BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
#define MAYBE_OtherLibrary DISABLED_OtherLibrary
#else
#define MAYBE_OtherLibrary OtherLibrary
#endif
PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_OtherLibrary) {
ScopedNativeLibrary other_library(LoadOtherLibrary());
UnwindScenario scenario(
BindRepeating(&CallThroughOtherLibrary, Unretained(other_library.get())));
const std::vector<Frame>& sample = SampleScenario(&scenario, module_cache());
ExpectStackContains(sample, {scenario.GetWaitForSampleAddressRange(),
scenario.GetSetupFunctionAddressRange(),
scenario.GetOuterFunctionAddressRange()});
}
#if BUILDFLAG(IS_APPLE) || \
(BUILDFLAG(IS_ANDROID) && BUILDFLAG(EXCLUDE_UNWIND_TABLES)) || \
(BUILDFLAG(IS_ANDROID) && defined(ADDRESS_SANITIZER)) || \
(BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
#define MAYBE_UnloadingLibrary DISABLED_UnloadingLibrary
#else
#define MAYBE_UnloadingLibrary UnloadingLibrary
#endif
PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_UnloadingLibrary) {
TestLibraryUnload(false, module_cache());
}
#if (defined(ADDRESS_SANITIZER) && BUILDFLAG(IS_APPLE)) || \
BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) || \
(BUILDFLAG(IS_CHROMEOS) && !BUILDFLAG(IS_CHROMEOS_DEVICE))
#define MAYBE_UnloadedLibrary DISABLED_UnloadedLibrary
#else
#define MAYBE_UnloadedLibrary UnloadedLibrary
#endif
PROFILER_TEST_F(StackSamplingProfilerTest, MAYBE_UnloadedLibrary) {
TestLibraryUnload(true, module_cache());
}
PROFILER_TEST_F(StackSamplingProfilerTest, StopWithoutStarting) {
WithTargetThread(BindLambdaForTesting(
[this](SamplingProfilerThreadToken target_thread_token) {
SamplingParams params;
params.sampling_interval = Milliseconds(0);
params.samples_per_profile = 1;
Profile profile;
WaitableEvent sampling_completed(
WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
StackSamplingProfiler profiler(
target_thread_token, params,
std::make_unique<TestProfileBuilder>(
module_cache(),
BindLambdaForTesting(
[&profile, &sampling_completed](Profile result_profile) {
profile = std::move(result_profile);
sampling_completed.Signal();
})),
CreateCoreUnwindersFactoryForTesting(module_cache()));
profiler.Stop();
EXPECT_FALSE(sampling_completed.IsSignaled());
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest, StopSafely) {
class SampleRecordedCounter : public StackSamplerTestDelegate {
public:
SampleRecordedCounter() = default;
void OnPreStackWalk() override {
AutoLock lock(lock_);
++count_;
}
size_t Get() {
AutoLock lock(lock_);
return count_;
}
private:
Lock lock_;
size_t count_ = 0;
};
WithTargetThread(
BindLambdaForTesting([](SamplingProfilerThreadToken target_thread_token) {
std::array<SamplingParams, 2> params;
params[0].initial_delay = Milliseconds(10);
params[0].sampling_interval = Milliseconds(1);
params[0].samples_per_profile = 100000;
params[1].initial_delay = Milliseconds(10);
params[1].sampling_interval = Milliseconds(1);
params[1].samples_per_profile = 100000;
std::array<SampleRecordedCounter, std::size(params)> samples_recorded;
ModuleCache module_cache1, module_cache2;
TestProfilerInfo profiler_info0(target_thread_token, params[0],
&module_cache1, &samples_recorded[0]);
TestProfilerInfo profiler_info1(target_thread_token, params[1],
&module_cache2, &samples_recorded[1]);
profiler_info0.profiler.Start();
profiler_info1.profiler.Start();
while (samples_recorded[0].Get() == 0 ||
samples_recorded[1].Get() == 0) {
PlatformThread::Sleep(Milliseconds(1));
}
profiler_info0.profiler.Stop();
profiler_info0.completed.Wait();
size_t count0 = samples_recorded[0].Get();
size_t count1 = samples_recorded[1].Get();
while (samples_recorded[1].Get() < count1 + 2) {
PlatformThread::Sleep(Milliseconds(1));
}
EXPECT_EQ(count0, samples_recorded[0].Get());
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest, StopDuringInitialDelay) {
SamplingParams params;
params.initial_delay = Seconds(60);
std::vector<std::vector<Frame>> samples =
CaptureSamples(params, Milliseconds(0), module_cache());
EXPECT_TRUE(samples.empty());
}
PROFILER_TEST_F(StackSamplingProfilerTest, StopDuringInterSampleInterval) {
class SampleRecordedEvent : public StackSamplerTestDelegate {
public:
SampleRecordedEvent()
: sample_recorded_(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED) {}
void OnPreStackWalk() override { sample_recorded_.Signal(); }
void WaitForSample() { sample_recorded_.Wait(); }
private:
WaitableEvent sample_recorded_;
};
WithTargetThread(BindLambdaForTesting(
[this](SamplingProfilerThreadToken target_thread_token) {
SamplingParams params;
params.sampling_interval = AVeryLongTimeDelta();
params.samples_per_profile = 2;
SampleRecordedEvent samples_recorded;
TestProfilerInfo profiler_info(target_thread_token, params,
module_cache(), &samples_recorded);
profiler_info.profiler.Start();
samples_recorded.WaitForSample();
profiler_info.profiler.Stop();
profiler_info.completed.Wait();
EXPECT_EQ(1u, profiler_info.profile.samples.size());
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest, GetNextSampleTime_NormalExecution) {
const auto& GetNextSampleTime =
StackSamplingProfiler::TestPeer::GetNextSampleTime;
const TimeTicks scheduled_current_sample_time = TimeTicks::UnixEpoch();
const TimeDelta sampling_interval = Milliseconds(10);
EXPECT_EQ(scheduled_current_sample_time + sampling_interval,
GetNextSampleTime(scheduled_current_sample_time, sampling_interval,
scheduled_current_sample_time));
EXPECT_EQ(scheduled_current_sample_time + sampling_interval,
GetNextSampleTime(
scheduled_current_sample_time, sampling_interval,
scheduled_current_sample_time + 0.4 * sampling_interval));
EXPECT_EQ(scheduled_current_sample_time + sampling_interval,
GetNextSampleTime(
scheduled_current_sample_time, sampling_interval,
scheduled_current_sample_time - 0.4 * sampling_interval));
}
PROFILER_TEST_F(StackSamplingProfilerTest, GetNextSampleTime_DelayedExecution) {
const auto& GetNextSampleTime =
StackSamplingProfiler::TestPeer::GetNextSampleTime;
const TimeTicks scheduled_current_sample_time = TimeTicks::UnixEpoch();
const TimeDelta sampling_interval = Milliseconds(10);
EXPECT_EQ(scheduled_current_sample_time + 2 * sampling_interval,
GetNextSampleTime(
scheduled_current_sample_time, sampling_interval,
scheduled_current_sample_time + 0.6 * sampling_interval));
EXPECT_EQ(scheduled_current_sample_time + 2 * sampling_interval,
GetNextSampleTime(
scheduled_current_sample_time, sampling_interval,
scheduled_current_sample_time + 1.0 * sampling_interval));
EXPECT_EQ(scheduled_current_sample_time + 2 * sampling_interval,
GetNextSampleTime(
scheduled_current_sample_time, sampling_interval,
scheduled_current_sample_time + 1.4 * sampling_interval));
EXPECT_EQ(scheduled_current_sample_time + 11 * sampling_interval,
GetNextSampleTime(
scheduled_current_sample_time, sampling_interval,
scheduled_current_sample_time + 9.6 * sampling_interval));
EXPECT_EQ(scheduled_current_sample_time + 11 * sampling_interval,
GetNextSampleTime(
scheduled_current_sample_time, sampling_interval,
scheduled_current_sample_time + 10.0 * sampling_interval));
EXPECT_EQ(scheduled_current_sample_time + 11 * sampling_interval,
GetNextSampleTime(
scheduled_current_sample_time, sampling_interval,
scheduled_current_sample_time + 10.4 * sampling_interval));
}
PROFILER_TEST_F(StackSamplingProfilerTest, DestroyProfilerWhileProfiling) {
SamplingParams params;
params.sampling_interval = Milliseconds(10);
Profile profile;
WithTargetThread(BindLambdaForTesting(
[&, this](SamplingProfilerThreadToken target_thread_token) {
std::unique_ptr<StackSamplingProfiler> profiler;
auto profile_builder = std::make_unique<TestProfileBuilder>(
module_cache(),
BindLambdaForTesting([&profile](Profile result_profile) {
profile = std::move(result_profile);
}));
profiler = std::make_unique<StackSamplingProfiler>(
target_thread_token, params, std::move(profile_builder),
CreateCoreUnwindersFactoryForTesting(module_cache()));
profiler->Start();
profiler.reset();
PlatformThread::Sleep(Milliseconds(50));
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest, CanRunMultipleProfilers) {
SamplingParams params;
params.sampling_interval = Milliseconds(0);
params.samples_per_profile = 1;
std::vector<std::vector<Frame>> samples =
CaptureSamples(params, AVeryLongTimeDelta(), module_cache());
ASSERT_EQ(1u, samples.size());
samples = CaptureSamples(params, AVeryLongTimeDelta(), module_cache());
ASSERT_EQ(1u, samples.size());
}
PROFILER_TEST_F(StackSamplingProfilerTest, MultipleStart) {
WithTargetThread(
BindLambdaForTesting([](SamplingProfilerThreadToken target_thread_token) {
SamplingParams params1;
params1.initial_delay = AVeryLongTimeDelta();
params1.samples_per_profile = 1;
ModuleCache module_cache1;
TestProfilerInfo profiler_info1(target_thread_token, params1,
&module_cache1);
SamplingParams params2;
params2.sampling_interval = Milliseconds(1);
params2.samples_per_profile = 1;
ModuleCache module_cache2;
TestProfilerInfo profiler_info2(target_thread_token, params2,
&module_cache2);
profiler_info1.profiler.Start();
profiler_info2.profiler.Start();
profiler_info2.completed.Wait();
EXPECT_EQ(1u, profiler_info2.profile.samples.size());
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest, ProfileGeneralInfo) {
WithTargetThread(BindLambdaForTesting(
[this](SamplingProfilerThreadToken target_thread_token) {
SamplingParams params;
params.sampling_interval = Milliseconds(1);
params.samples_per_profile = 3;
TestProfilerInfo profiler_info(target_thread_token, params,
module_cache());
profiler_info.profiler.Start();
profiler_info.completed.Wait();
EXPECT_EQ(3u, profiler_info.profile.samples.size());
EXPECT_GT(profiler_info.profile.profile_duration,
profiler_info.profile.sampling_period * 3);
EXPECT_EQ(Milliseconds(1), profiler_info.profile.sampling_period);
EXPECT_EQ(3, profiler_info.profile.record_metadata_count);
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest, SamplerIdleShutdown) {
SamplingParams params;
params.sampling_interval = Milliseconds(0);
params.samples_per_profile = 1;
std::vector<std::vector<Frame>> samples =
CaptureSamples(params, AVeryLongTimeDelta(), module_cache());
ASSERT_EQ(1u, samples.size());
ASSERT_TRUE(StackSamplingProfiler::TestPeer::IsSamplingThreadRunning());
StackSamplingProfiler::TestPeer::PerformSamplingThreadIdleShutdown(false);
while (StackSamplingProfiler::TestPeer::IsSamplingThreadRunning()) {
PlatformThread::Sleep(Milliseconds(1));
}
}
PROFILER_TEST_F(StackSamplingProfilerTest,
WillRestartSamplerAfterIdleShutdown) {
SamplingParams params;
params.sampling_interval = Milliseconds(0);
params.samples_per_profile = 1;
std::vector<std::vector<Frame>> samples =
CaptureSamples(params, AVeryLongTimeDelta(), module_cache());
ASSERT_EQ(1u, samples.size());
ASSERT_TRUE(StackSamplingProfiler::TestPeer::IsSamplingThreadRunning());
StackSamplingProfiler::TestPeer::PerformSamplingThreadIdleShutdown(false);
samples = CaptureSamples(params, AVeryLongTimeDelta(), module_cache());
ASSERT_EQ(1u, samples.size());
EXPECT_TRUE(StackSamplingProfiler::TestPeer::IsSamplingThreadRunning());
}
PROFILER_TEST_F(StackSamplingProfilerTest, StopAfterIdleShutdown) {
WithTargetThread(BindLambdaForTesting(
[this](SamplingProfilerThreadToken target_thread_token) {
SamplingParams params;
params.sampling_interval = Milliseconds(1);
params.samples_per_profile = 1;
TestProfilerInfo profiler_info(target_thread_token, params,
module_cache());
profiler_info.profiler.Start();
profiler_info.completed.Wait();
ASSERT_TRUE(StackSamplingProfiler::TestPeer::IsSamplingThreadRunning());
StackSamplingProfiler::TestPeer::PerformSamplingThreadIdleShutdown(
false);
profiler_info.profiler.Stop();
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest,
ProfileBeforeAndAfterSamplingThreadRunning) {
WithTargetThread(
BindLambdaForTesting([](SamplingProfilerThreadToken target_thread_token) {
ModuleCache module_cache1;
ModuleCache module_cache2;
std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos;
profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
target_thread_token,
SamplingParams{AVeryLongTimeDelta(),
1,
Milliseconds(1)},
&module_cache1));
profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
target_thread_token,
SamplingParams{Milliseconds(0),
1,
Milliseconds(1)},
&module_cache2));
EXPECT_FALSE(
StackSamplingProfiler::TestPeer::IsSamplingThreadRunning());
profiler_infos[0]->profiler.Start();
EXPECT_TRUE(StackSamplingProfiler::TestPeer::IsSamplingThreadRunning());
profiler_infos[1]->profiler.Start();
size_t completed_profiler = WaitForSamplingComplete(profiler_infos);
EXPECT_EQ(1U, completed_profiler);
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest, IdleShutdownAbort) {
WithTargetThread(BindLambdaForTesting(
[this](SamplingProfilerThreadToken target_thread_token) {
SamplingParams params;
params.sampling_interval = Milliseconds(1);
params.samples_per_profile = 1;
TestProfilerInfo profiler_info(target_thread_token, params,
module_cache());
profiler_info.profiler.Start();
profiler_info.completed.Wait();
EXPECT_EQ(1u, profiler_info.profile.samples.size());
StackSamplingProfiler::TestPeer::PerformSamplingThreadIdleShutdown(
true);
PlatformThread::Sleep(Milliseconds(200));
EXPECT_TRUE(StackSamplingProfiler::TestPeer::IsSamplingThreadRunning());
TestProfilerInfo another_info(target_thread_token, params,
module_cache());
another_info.profiler.Start();
another_info.completed.Wait();
EXPECT_EQ(1u, another_info.profile.samples.size());
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest, ConcurrentProfiling_InSync) {
WithTargetThread(
BindLambdaForTesting([](SamplingProfilerThreadToken target_thread_token) {
ModuleCache module_cache1;
ModuleCache module_cache2;
std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos;
profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
target_thread_token,
SamplingParams{Milliseconds(10),
9,
Milliseconds(1)},
&module_cache1));
profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
target_thread_token,
SamplingParams{Milliseconds(11),
8,
Milliseconds(1)},
&module_cache2));
profiler_infos[0]->profiler.Start();
profiler_infos[1]->profiler.Start();
size_t completed_profiler = WaitForSamplingComplete(profiler_infos);
size_t other_profiler = 1 - completed_profiler;
profiler_infos[other_profiler]->completed.Wait();
EXPECT_EQ(9u, profiler_infos[0]->profile.samples.size());
EXPECT_EQ(8u, profiler_infos[1]->profile.samples.size());
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest, ConcurrentProfiling_Mixed) {
WithTargetThread(
BindLambdaForTesting([](SamplingProfilerThreadToken target_thread_token) {
std::vector<ModuleCache> module_caches(3);
std::vector<std::unique_ptr<TestProfilerInfo>> profiler_infos;
profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
target_thread_token,
SamplingParams{Milliseconds(8),
10,
Milliseconds(4)},
&module_caches[0]));
profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
target_thread_token,
SamplingParams{Milliseconds(9),
10,
Milliseconds(3)},
&module_caches[1]));
profiler_infos.push_back(std::make_unique<TestProfilerInfo>(
target_thread_token,
SamplingParams{Milliseconds(10),
10,
Milliseconds(2)},
&module_caches[2]));
for (auto& i : profiler_infos) {
i->profiler.Start();
}
size_t completed_profiler = WaitForSamplingComplete(profiler_infos);
EXPECT_EQ(10u,
profiler_infos[completed_profiler]->profile.samples.size());
for (auto& i : profiler_infos) {
i->profiler.Stop();
}
for (auto& i : profiler_infos) {
i.reset();
}
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest, MultipleSampledThreads) {
UnwindScenario scenario1(BindRepeating(&CallWithPlainFunction));
UnwindScenario::SampleEvents events1;
TargetThread target_thread1(
BindLambdaForTesting([&] { scenario1.Execute(&events1); }));
target_thread1.Start();
events1.ready_for_sample.Wait();
UnwindScenario scenario2(BindRepeating(&CallWithPlainFunction));
UnwindScenario::SampleEvents events2;
TargetThread target_thread2(
BindLambdaForTesting([&] { scenario2.Execute(&events2); }));
target_thread2.Start();
events2.ready_for_sample.Wait();
SamplingParams params1, params2;
params1.initial_delay = Milliseconds(10);
params1.sampling_interval = Milliseconds(1);
params1.samples_per_profile = 9;
params2.initial_delay = Milliseconds(10);
params2.sampling_interval = Milliseconds(1);
params2.samples_per_profile = 8;
Profile profile1, profile2;
ModuleCache module_cache1, module_cache2;
WaitableEvent sampling_thread_completed1(
WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
StackSamplingProfiler profiler1(
target_thread1.thread_token(), params1,
std::make_unique<TestProfileBuilder>(
&module_cache1,
BindLambdaForTesting(
[&profile1, &sampling_thread_completed1](Profile result_profile) {
profile1 = std::move(result_profile);
sampling_thread_completed1.Signal();
})),
CreateCoreUnwindersFactoryForTesting(&module_cache1));
WaitableEvent sampling_thread_completed2(
WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
StackSamplingProfiler profiler2(
target_thread2.thread_token(), params2,
std::make_unique<TestProfileBuilder>(
&module_cache2,
BindLambdaForTesting(
[&profile2, &sampling_thread_completed2](Profile result_profile) {
profile2 = std::move(result_profile);
sampling_thread_completed2.Signal();
})),
CreateCoreUnwindersFactoryForTesting(&module_cache2));
profiler1.Start();
profiler2.Start();
sampling_thread_completed1.Wait();
sampling_thread_completed2.Wait();
EXPECT_EQ(9u, profile1.samples.size());
EXPECT_EQ(8u, profile2.samples.size());
events1.sample_finished.Signal();
events2.sample_finished.Signal();
target_thread1.Join();
target_thread2.Join();
}
class ProfilerThread : public SimpleThread {
public:
ProfilerThread(const std::string& name,
SamplingProfilerThreadToken thread_token,
const SamplingParams& params,
ModuleCache* module_cache)
: SimpleThread(name, Options()),
run_(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED),
completed_(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED),
profiler_(thread_token,
params,
std::make_unique<TestProfileBuilder>(
module_cache,
BindLambdaForTesting([this](Profile result_profile) {
profile_ = std::move(result_profile);
completed_.Signal();
})),
CreateCoreUnwindersFactoryForTesting(module_cache)) {}
void Run() override {
run_.Wait();
profiler_.Start();
}
void Go() { run_.Signal(); }
void Wait() { completed_.Wait(); }
Profile& profile() { return profile_; }
private:
WaitableEvent run_;
Profile profile_;
WaitableEvent completed_;
StackSamplingProfiler profiler_;
};
PROFILER_TEST_F(StackSamplingProfilerTest, MultipleProfilerThreads) {
WithTargetThread(
BindLambdaForTesting([](SamplingProfilerThreadToken target_thread_token) {
SamplingParams params1, params2;
params1.initial_delay = Milliseconds(10);
params1.sampling_interval = Milliseconds(1);
params1.samples_per_profile = 9;
params2.initial_delay = Milliseconds(10);
params2.sampling_interval = Milliseconds(1);
params2.samples_per_profile = 8;
ModuleCache module_cache1;
ProfilerThread profiler_thread1("profiler1", target_thread_token,
params1, &module_cache1);
ModuleCache module_cache2;
ProfilerThread profiler_thread2("profiler2", target_thread_token,
params2, &module_cache2);
profiler_thread1.Start();
profiler_thread2.Start();
PlatformThread::Sleep(Milliseconds(10));
profiler_thread1.Go();
profiler_thread2.Go();
profiler_thread1.Wait();
profiler_thread2.Wait();
EXPECT_EQ(9u, profiler_thread1.profile().samples.size());
EXPECT_EQ(8u, profiler_thread2.profile().samples.size());
profiler_thread1.Join();
profiler_thread2.Join();
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest, AddAuxUnwinder_BeforeStart) {
SamplingParams params;
params.sampling_interval = Milliseconds(0);
params.samples_per_profile = 1;
UnwindScenario scenario(BindRepeating(&CallWithPlainFunction));
int add_initial_modules_invocation_count = 0;
const auto add_initial_modules_callback =
[&add_initial_modules_invocation_count] {
++add_initial_modules_invocation_count;
};
Profile profile;
WithTargetThread(
&scenario,
BindLambdaForTesting(
[&](SamplingProfilerThreadToken target_thread_token) {
WaitableEvent sampling_thread_completed(
WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
StackSamplingProfiler profiler(
target_thread_token, params,
std::make_unique<TestProfileBuilder>(
module_cache(),
BindLambdaForTesting([&profile, &sampling_thread_completed](
Profile result_profile) {
profile = std::move(result_profile);
sampling_thread_completed.Signal();
})),
CreateCoreUnwindersFactoryForTesting(module_cache()));
profiler.AddAuxUnwinder(std::make_unique<TestAuxUnwinder>(
Frame(23, nullptr),
BindLambdaForTesting(add_initial_modules_callback)));
profiler.Start();
sampling_thread_completed.Wait();
}));
ASSERT_EQ(1, add_initial_modules_invocation_count);
ASSERT_EQ(1u, profile.samples.size());
const std::vector<Frame>& frames = profile.samples[0];
ASSERT_EQ(2u, frames.size());
EXPECT_EQ(23u, frames[1].instruction_pointer);
EXPECT_EQ(nullptr, frames[1].module);
}
PROFILER_TEST_F(StackSamplingProfilerTest, AddAuxUnwinder_AfterStart) {
SamplingParams params;
params.sampling_interval = Milliseconds(10);
params.samples_per_profile = 2;
UnwindScenario scenario(BindRepeating(&CallWithPlainFunction));
int add_initial_modules_invocation_count = 0;
const auto add_initial_modules_callback =
[&add_initial_modules_invocation_count] {
++add_initial_modules_invocation_count;
};
Profile profile;
WithTargetThread(
&scenario,
BindLambdaForTesting(
[&](SamplingProfilerThreadToken target_thread_token) {
WaitableEvent sampling_thread_completed(
WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
StackSamplingProfiler profiler(
target_thread_token, params,
std::make_unique<TestProfileBuilder>(
module_cache(),
BindLambdaForTesting([&profile, &sampling_thread_completed](
Profile result_profile) {
profile = std::move(result_profile);
sampling_thread_completed.Signal();
})),
CreateCoreUnwindersFactoryForTesting(module_cache()));
profiler.Start();
profiler.AddAuxUnwinder(std::make_unique<TestAuxUnwinder>(
Frame(23, nullptr),
BindLambdaForTesting(add_initial_modules_callback)));
sampling_thread_completed.Wait();
}));
ASSERT_EQ(1, add_initial_modules_invocation_count);
ASSERT_EQ(2u, profile.samples.size());
const std::vector<Frame>& frames = profile.samples[1];
ASSERT_EQ(2u, frames.size());
EXPECT_EQ(23u, frames[1].instruction_pointer);
EXPECT_EQ(nullptr, frames[1].module);
}
PROFILER_TEST_F(StackSamplingProfilerTest, AddAuxUnwinder_AfterStop) {
SamplingParams params;
params.sampling_interval = Milliseconds(0);
params.samples_per_profile = 1;
UnwindScenario scenario(BindRepeating(&CallWithPlainFunction));
Profile profile;
WithTargetThread(
&scenario,
BindLambdaForTesting(
[&](SamplingProfilerThreadToken target_thread_token) {
WaitableEvent sampling_thread_completed(
WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
StackSamplingProfiler profiler(
target_thread_token, params,
std::make_unique<TestProfileBuilder>(
module_cache(),
BindLambdaForTesting([&profile, &sampling_thread_completed](
Profile result_profile) {
profile = std::move(result_profile);
sampling_thread_completed.Signal();
})),
CreateCoreUnwindersFactoryForTesting(module_cache()));
profiler.Start();
profiler.Stop();
profiler.AddAuxUnwinder(std::make_unique<TestAuxUnwinder>(
Frame(23, nullptr), base::RepeatingClosure()));
sampling_thread_completed.Wait();
}));
}
PROFILER_TEST_F(StackSamplingProfilerTest,
ApplyMetadataToPastSamples_PassedToProfileBuilder) {
class PostSampleInvoker : public StackSamplerTestDelegate {
public:
explicit PostSampleInvoker(RepeatingClosure post_sample_closure)
: post_sample_closure_(std::move(post_sample_closure)) {}
void OnPreStackWalk() override { post_sample_closure_.Run(); }
private:
RepeatingClosure post_sample_closure_;
};
class SynchronizedSampleTimes {
public:
void AddNow() {
AutoLock lock(lock_);
times_.push_back(TimeTicks::Now());
}
std::vector<TimeTicks> GetTimes() {
AutoLock lock(lock_);
return times_;
}
private:
Lock lock_;
std::vector<TimeTicks> times_;
};
SamplingParams params;
params.sampling_interval = Milliseconds(10);
params.samples_per_profile = 10000;
UnwindScenario scenario(BindRepeating(&CallWithPlainFunction));
std::vector<TimeTicks> sample_times;
Profile profile;
WithTargetThread(
&scenario,
BindLambdaForTesting(
[&](SamplingProfilerThreadToken target_thread_token) {
SynchronizedSampleTimes synchronized_sample_times;
WaitableEvent sample_seen(WaitableEvent::ResetPolicy::AUTOMATIC);
PostSampleInvoker post_sample_invoker(BindLambdaForTesting([&] {
synchronized_sample_times.AddNow();
sample_seen.Signal();
}));
StackSamplingProfiler profiler(
target_thread_token, params,
std::make_unique<TestProfileBuilder>(
module_cache(),
BindLambdaForTesting([&profile](Profile result_profile) {
profile = std::move(result_profile);
})),
CreateCoreUnwindersFactoryForTesting(module_cache()),
RepeatingClosure(), &post_sample_invoker);
profiler.Start();
for (int i = 0; i < 5; ++i) {
sample_seen.Wait();
}
sample_times = synchronized_sample_times.GetTimes();
ApplyMetadataToPastSamples(sample_times[1], sample_times[3],
"TestMetadata1", 10,
base::SampleMetadataScope::kProcess);
ApplyMetadataToPastSamples(sample_times[2], sample_times[4],
"TestMetadata2", 100, 11,
base::SampleMetadataScope::kProcess);
profiler.Stop();
}));
ASSERT_EQ(2u, profile.retrospective_metadata.size());
const RetrospectiveMetadata& metadata1 = profile.retrospective_metadata[0];
EXPECT_EQ(sample_times[1], metadata1.period_start);
EXPECT_EQ(sample_times[3], metadata1.period_end);
EXPECT_EQ(HashMetricName("TestMetadata1"), metadata1.item.name_hash);
EXPECT_FALSE(metadata1.item.key.has_value());
EXPECT_EQ(10, metadata1.item.value);
const RetrospectiveMetadata& metadata2 = profile.retrospective_metadata[1];
EXPECT_EQ(sample_times[2], metadata2.period_start);
EXPECT_EQ(sample_times[4], metadata2.period_end);
EXPECT_EQ(HashMetricName("TestMetadata2"), metadata2.item.name_hash);
ASSERT_TRUE(metadata2.item.key.has_value());
EXPECT_EQ(100, *metadata2.item.key);
EXPECT_EQ(11, metadata2.item.value);
}
PROFILER_TEST_F(
StackSamplingProfilerTest,
ApplyMetadataToPastSamples_PassedToProfileBuilder_MultipleCollections) {
SamplingParams params;
params.sampling_interval = Milliseconds(10);
params.samples_per_profile = 10000;
ModuleCache module_cache1, module_cache2;
WaitableEvent profiler1_started;
WaitableEvent profiler2_started;
WaitableEvent profiler1_metadata_applied;
WaitableEvent profiler2_metadata_applied;
Profile profile1;
WaitableEvent sampling_completed1;
TargetThread target_thread1(BindLambdaForTesting([&] {
StackSamplingProfiler profiler1(
target_thread1.thread_token(), params,
std::make_unique<TestProfileBuilder>(
&module_cache1, BindLambdaForTesting([&](Profile result_profile) {
profile1 = std::move(result_profile);
sampling_completed1.Signal();
})),
CreateCoreUnwindersFactoryForTesting(&module_cache1),
RepeatingClosure());
profiler1.Start();
profiler1_started.Signal();
profiler2_started.Wait();
ApplyMetadataToPastSamples(TimeTicks(), TimeTicks::Now(), "TestMetadata1",
10, 10, SampleMetadataScope::kThread);
profiler1_metadata_applied.Signal();
profiler2_metadata_applied.Wait();
profiler1.Stop();
}));
target_thread1.Start();
Profile profile2;
WaitableEvent sampling_completed2;
TargetThread target_thread2(BindLambdaForTesting([&] {
StackSamplingProfiler profiler2(
target_thread2.thread_token(), params,
std::make_unique<TestProfileBuilder>(
&module_cache2, BindLambdaForTesting([&](Profile result_profile) {
profile2 = std::move(result_profile);
sampling_completed2.Signal();
})),
CreateCoreUnwindersFactoryForTesting(&module_cache2),
RepeatingClosure());
profiler2.Start();
profiler2_started.Signal();
profiler1_started.Wait();
ApplyMetadataToPastSamples(TimeTicks(), TimeTicks::Now(), "TestMetadata2",
20, 20, SampleMetadataScope::kThread);
profiler2_metadata_applied.Signal();
profiler1_metadata_applied.Wait();
profiler2.Stop();
}));
target_thread2.Start();
target_thread1.Join();
target_thread2.Join();
sampling_completed1.Wait();
sampling_completed2.Wait();
ASSERT_EQ(1u, profile1.retrospective_metadata.size());
ASSERT_EQ(1u, profile2.retrospective_metadata.size());
{
const RetrospectiveMetadata& metadata1 = profile1.retrospective_metadata[0];
EXPECT_EQ(HashMetricName("TestMetadata1"), metadata1.item.name_hash);
ASSERT_TRUE(metadata1.item.key.has_value());
EXPECT_EQ(10, *metadata1.item.key);
EXPECT_EQ(10, metadata1.item.value);
}
{
const RetrospectiveMetadata& metadata2 = profile2.retrospective_metadata[0];
EXPECT_EQ(HashMetricName("TestMetadata2"), metadata2.item.name_hash);
ASSERT_TRUE(metadata2.item.key.has_value());
EXPECT_EQ(20, *metadata2.item.key);
EXPECT_EQ(20, metadata2.item.value);
}
}
PROFILER_TEST_F(StackSamplingProfilerTest,
AddProfileMetadata_PassedToProfileBuilder) {
class PostSampleInvoker : public StackSamplerTestDelegate {
public:
explicit PostSampleInvoker(RepeatingClosure post_sample_closure)
: post_sample_closure_(std::move(post_sample_closure)) {}
void OnPreStackWalk() override { post_sample_closure_.Run(); }
private:
RepeatingClosure post_sample_closure_;
};
SamplingParams params;
params.sampling_interval = Milliseconds(10);
params.samples_per_profile = 10000;
UnwindScenario scenario(BindRepeating(&CallWithPlainFunction));
Profile profile;
WithTargetThread(
&scenario,
BindLambdaForTesting(
[&](SamplingProfilerThreadToken target_thread_token) {
WaitableEvent sample_seen(WaitableEvent::ResetPolicy::AUTOMATIC);
PostSampleInvoker post_sample_invoker(
BindLambdaForTesting([&] { sample_seen.Signal(); }));
StackSamplingProfiler profiler(
target_thread_token, params,
std::make_unique<TestProfileBuilder>(
module_cache(),
BindLambdaForTesting([&profile](Profile result_profile) {
profile = std::move(result_profile);
})),
CreateCoreUnwindersFactoryForTesting(module_cache()),
RepeatingClosure(), &post_sample_invoker);
profiler.Start();
sample_seen.Wait();
AddProfileMetadata("TestMetadata", 1, 2,
SampleMetadataScope::kProcess);
profiler.Stop();
}));
ASSERT_EQ(1u, profile.profile_metadata.size());
const MetadataRecorder::Item& item = profile.profile_metadata[0];
EXPECT_EQ(HashMetricName("TestMetadata"), item.name_hash);
EXPECT_EQ(1, *item.key);
EXPECT_EQ(2, item.value);
}
PROFILER_TEST_F(StackSamplingProfilerTest,
AddProfileMetadata_PassedToProfileBuilder_MultipleCollections) {
SamplingParams params;
params.sampling_interval = Milliseconds(10);
params.samples_per_profile = 10000;
ModuleCache module_cache1, module_cache2;
WaitableEvent profiler1_started;
WaitableEvent profiler2_started;
WaitableEvent profiler1_metadata_applied;
WaitableEvent profiler2_metadata_applied;
Profile profile1;
WaitableEvent sampling_completed1;
TargetThread target_thread1(BindLambdaForTesting([&] {
StackSamplingProfiler profiler1(
target_thread1.thread_token(), params,
std::make_unique<TestProfileBuilder>(
&module_cache1, BindLambdaForTesting([&](Profile result_profile) {
profile1 = std::move(result_profile);
sampling_completed1.Signal();
})),
CreateCoreUnwindersFactoryForTesting(&module_cache1),
RepeatingClosure());
profiler1.Start();
profiler1_started.Signal();
profiler2_started.Wait();
AddProfileMetadata("TestMetadata1", 1, 2, SampleMetadataScope::kThread);
profiler1_metadata_applied.Signal();
profiler2_metadata_applied.Wait();
profiler1.Stop();
}));
target_thread1.Start();
Profile profile2;
WaitableEvent sampling_completed2;
TargetThread target_thread2(BindLambdaForTesting([&] {
StackSamplingProfiler profiler2(
target_thread2.thread_token(), params,
std::make_unique<TestProfileBuilder>(
&module_cache2, BindLambdaForTesting([&](Profile result_profile) {
profile2 = std::move(result_profile);
sampling_completed2.Signal();
})),
CreateCoreUnwindersFactoryForTesting(&module_cache2),
RepeatingClosure());
profiler2.Start();
profiler2_started.Signal();
profiler1_started.Wait();
AddProfileMetadata("TestMetadata2", 11, 12, SampleMetadataScope::kThread);
profiler2_metadata_applied.Signal();
profiler1_metadata_applied.Wait();
profiler2.Stop();
}));
target_thread2.Start();
target_thread1.Join();
target_thread2.Join();
sampling_completed1.Wait();
sampling_completed2.Wait();
ASSERT_EQ(1u, profile1.profile_metadata.size());
ASSERT_EQ(1u, profile2.profile_metadata.size());
{
const MetadataRecorder::Item& item = profile1.profile_metadata[0];
EXPECT_EQ(HashMetricName("TestMetadata1"), item.name_hash);
ASSERT_TRUE(item.key.has_value());
EXPECT_EQ(1, *item.key);
EXPECT_EQ(2, item.value);
}
{
const MetadataRecorder::Item& item = profile2.profile_metadata[0];
EXPECT_EQ(HashMetricName("TestMetadata2"), item.name_hash);
ASSERT_TRUE(item.key.has_value());
EXPECT_EQ(11, *item.key);
EXPECT_EQ(12, item.value);
}
}
}