| // Copyright 2018 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "components/metrics/call_stack_profile_builder.h" | 
 |  | 
 | #include <memory> | 
 |  | 
 | #include "base/files/file_path.h" | 
 | #include "base/functional/callback.h" | 
 | #include "base/profiler/module_cache.h" | 
 | #include "base/profiler/stack_sampling_profiler_test_util.h" | 
 | #include "base/test/bind.h" | 
 | #include "base/test/mock_callback.h" | 
 | #include "base/time/time.h" | 
 | #include "build/build_config.h" | 
 | #include "components/metrics/call_stack_profile_params.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 | #include "third_party/metrics_proto/sampled_profile.pb.h" | 
 |  | 
 | namespace metrics { | 
 |  | 
 | namespace { | 
 |  | 
 | constexpr CallStackProfileParams kProfileParams = { | 
 |     CallStackProfileParams::Process::kBrowser, | 
 |     CallStackProfileParams::Thread::kMain, | 
 |     CallStackProfileParams::Trigger::kProcessStartup}; | 
 |  | 
 | class TestingCallStackProfileBuilder : public CallStackProfileBuilder { | 
 |  public: | 
 |   TestingCallStackProfileBuilder( | 
 |       const CallStackProfileParams& profile_params, | 
 |       const WorkIdRecorder* work_id_recorder = nullptr, | 
 |       base::OnceClosure completed_callback = base::OnceClosure()); | 
 |  | 
 |   ~TestingCallStackProfileBuilder() override; | 
 |  | 
 |   base::TimeTicks test_profile_start_time() const { | 
 |     return test_profile_start_time_; | 
 |   } | 
 |  | 
 |   const SampledProfile& test_sampled_profile() const { | 
 |     return test_sampled_profile_; | 
 |   } | 
 |  | 
 |  protected: | 
 |   // Overridden for testing. | 
 |   void PassProfilesToMetricsProvider(base::TimeTicks profile_start_time, | 
 |                                      SampledProfile sampled_profile) override; | 
 |  | 
 |  private: | 
 |   // The start time and completed profile. | 
 |   base::TimeTicks test_profile_start_time_; | 
 |   SampledProfile test_sampled_profile_; | 
 | }; | 
 |  | 
 | TestingCallStackProfileBuilder::TestingCallStackProfileBuilder( | 
 |     const CallStackProfileParams& profile_params, | 
 |     const WorkIdRecorder* work_id_recorder, | 
 |     base::OnceClosure completed_callback) | 
 |     : CallStackProfileBuilder(profile_params, | 
 |                               work_id_recorder, | 
 |                               std::move(completed_callback)) {} | 
 |  | 
 | TestingCallStackProfileBuilder::~TestingCallStackProfileBuilder() = default; | 
 |  | 
 | void TestingCallStackProfileBuilder::PassProfilesToMetricsProvider( | 
 |     base::TimeTicks profile_start_time, | 
 |     SampledProfile sampled_profile) { | 
 |   test_profile_start_time_ = profile_start_time; | 
 |   test_sampled_profile_ = std::move(sampled_profile); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | TEST(CallStackProfileBuilderTest, ProfilingCompleted) { | 
 |   // Set up a mock completed callback which will be run once. | 
 |   base::MockCallback<base::OnceClosure> mock_closure; | 
 |   EXPECT_CALL(mock_closure, Run()).Times(1); | 
 |  | 
 |   auto profile_builder = std::make_unique<TestingCallStackProfileBuilder>( | 
 |       kProfileParams, nullptr, mock_closure.Get()); | 
 |   base::MetadataRecorder metadata_recorder; | 
 |  | 
 | #if BUILDFLAG(IS_WIN) | 
 |   uint64_t module_md5 = 0x46C3E4166659AC02ULL; | 
 |   base::FilePath module_path(L"c:\\some\\path\\to\\chrome.exe"); | 
 | #else | 
 |   uint64_t module_md5 = 0x554838A8451AC36CULL; | 
 |   base::FilePath module_path("/some/path/to/chrome"); | 
 | #endif | 
 |  | 
 |   const uintptr_t module_base_address1 = 0x1000; | 
 |   base::TestModule module1(module_base_address1); | 
 |   module1.set_id("1"); | 
 |   module1.set_debug_basename(module_path); | 
 |   base::Frame frame1 = {module_base_address1 + 0x10, &module1}; | 
 |  | 
 |   const uintptr_t module_base_address2 = 0x1100; | 
 |   base::TestModule module2(module_base_address2); | 
 |   module2.set_id("2"); | 
 |   module2.set_debug_basename(module_path); | 
 |   base::Frame frame2 = {module_base_address2 + 0x10, &module2}; | 
 |  | 
 |   const uintptr_t module_base_address3 = 0x1010; | 
 |   base::TestModule module3(module_base_address3); | 
 |   module3.set_id("3"); | 
 |   module3.set_debug_basename(module_path); | 
 |   base::Frame frame3 = {module_base_address3 + 0x10, &module3}; | 
 |  | 
 |   std::vector<base::Frame> frames1 = {frame1, frame2}; | 
 |   std::vector<base::Frame> frames2 = {frame3}; | 
 |  | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted(frames1, base::TimeTicks()); | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted(frames2, base::TimeTicks()); | 
 |   profile_builder->OnProfileCompleted(base::Milliseconds(500), | 
 |                                       base::Milliseconds(100)); | 
 |  | 
 |   const SampledProfile& proto = profile_builder->test_sampled_profile(); | 
 |  | 
 |   ASSERT_TRUE(proto.has_process()); | 
 |   ASSERT_EQ(BROWSER_PROCESS, proto.process()); | 
 |   ASSERT_TRUE(proto.has_thread()); | 
 |   ASSERT_EQ(MAIN_THREAD, proto.thread()); | 
 |   ASSERT_TRUE(proto.has_trigger_event()); | 
 |   ASSERT_EQ(SampledProfile::PROCESS_STARTUP, proto.trigger_event()); | 
 |  | 
 |   ASSERT_TRUE(proto.has_call_stack_profile()); | 
 |   const CallStackProfile& profile = proto.call_stack_profile(); | 
 |  | 
 |   ASSERT_EQ(2, profile.stack_size()); | 
 |   ASSERT_EQ(2, profile.stack(0).frame_size()); | 
 |   ASSERT_TRUE(profile.stack(0).frame(0).has_module_id_index()); | 
 |   EXPECT_EQ(0, profile.stack(0).frame(0).module_id_index()); | 
 |   ASSERT_TRUE(profile.stack(0).frame(1).has_module_id_index()); | 
 |   EXPECT_EQ(1, profile.stack(0).frame(1).module_id_index()); | 
 |   ASSERT_EQ(1, profile.stack(1).frame_size()); | 
 |   ASSERT_TRUE(profile.stack(1).frame(0).has_module_id_index()); | 
 |   EXPECT_EQ(2, profile.stack(1).frame(0).module_id_index()); | 
 |  | 
 |   ASSERT_EQ(3, profile.module_id().size()); | 
 |   ASSERT_TRUE(profile.module_id(0).has_build_id()); | 
 |   EXPECT_EQ("1", profile.module_id(0).build_id()); | 
 |   ASSERT_TRUE(profile.module_id(0).has_name_md5_prefix()); | 
 |   EXPECT_EQ(module_md5, profile.module_id(0).name_md5_prefix()); | 
 |   ASSERT_TRUE(profile.module_id(1).has_build_id()); | 
 |   EXPECT_EQ("2", profile.module_id(1).build_id()); | 
 |   ASSERT_TRUE(profile.module_id(1).has_name_md5_prefix()); | 
 |   EXPECT_EQ(module_md5, profile.module_id(1).name_md5_prefix()); | 
 |   ASSERT_TRUE(profile.module_id(2).has_build_id()); | 
 |   EXPECT_EQ("3", profile.module_id(2).build_id()); | 
 |   ASSERT_TRUE(profile.module_id(2).has_name_md5_prefix()); | 
 |   EXPECT_EQ(module_md5, profile.module_id(2).name_md5_prefix()); | 
 |  | 
 |   ASSERT_EQ(2, profile.stack_sample_size()); | 
 |   EXPECT_EQ(0, profile.stack_sample(0).stack_index()); | 
 |   EXPECT_FALSE(profile.stack_sample(0).has_continued_work()); | 
 |   EXPECT_FALSE(profile.stack_sample(0).has_weight()); | 
 |   EXPECT_EQ(1, profile.stack_sample(1).stack_index()); | 
 |   EXPECT_FALSE(profile.stack_sample(1).has_continued_work()); | 
 |   EXPECT_FALSE(profile.stack_sample(1).has_weight()); | 
 |  | 
 |   ASSERT_TRUE(profile.has_profile_duration_ms()); | 
 |   EXPECT_EQ(500, profile.profile_duration_ms()); | 
 |   ASSERT_TRUE(profile.has_sampling_period_ms()); | 
 |   EXPECT_EQ(100, profile.sampling_period_ms()); | 
 | } | 
 |  | 
 | TEST(CallStackProfileBuilderTest, CustomWeightsAndCounts) { | 
 |   auto profile_builder = | 
 |       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams); | 
 |  | 
 |   base::TestModule module1; | 
 |   base::Frame frame1 = {0x10, &module1}; | 
 |   std::vector<base::Frame> frames = {frame1}; | 
 |  | 
 |   profile_builder->OnSampleCompleted(frames, base::TimeTicks(), 42, 3); | 
 |   profile_builder->OnSampleCompleted(frames, base::TimeTicks(), 1, 1); | 
 |   profile_builder->OnSampleCompleted(frames, base::TimeTicks()); | 
 |   profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta()); | 
 |  | 
 |   const SampledProfile& proto = profile_builder->test_sampled_profile(); | 
 |  | 
 |   ASSERT_TRUE(proto.has_call_stack_profile()); | 
 |   const CallStackProfile& profile = proto.call_stack_profile(); | 
 |   ASSERT_EQ(3, profile.stack_sample_size()); | 
 |   EXPECT_TRUE(profile.stack_sample(0).has_weight()); | 
 |   EXPECT_TRUE(profile.stack_sample(0).has_count()); | 
 |   EXPECT_EQ(42, profile.stack_sample(0).weight()); | 
 |   EXPECT_EQ(3, profile.stack_sample(0).count()); | 
 |   EXPECT_FALSE(profile.stack_sample(1).has_weight()); | 
 |   EXPECT_FALSE(profile.stack_sample(1).has_count()); | 
 |   EXPECT_FALSE(profile.stack_sample(2).has_weight()); | 
 |   EXPECT_FALSE(profile.stack_sample(2).has_count()); | 
 | } | 
 |  | 
 | TEST(CallStackProfileBuilderTest, StacksDeduped) { | 
 |   auto profile_builder = | 
 |       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams); | 
 |   base::MetadataRecorder metadata_recorder; | 
 |  | 
 |   base::TestModule module1; | 
 |   base::Frame frame1 = {0x10, &module1}; | 
 |  | 
 |   base::TestModule module2; | 
 |   base::Frame frame2 = {0x20, &module2}; | 
 |  | 
 |   std::vector<base::Frame> frames = {frame1, frame2}; | 
 |  | 
 |   // Two stacks are completed with the same frames therefore they are deduped | 
 |   // to one. | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted(frames, base::TimeTicks()); | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted(frames, base::TimeTicks()); | 
 |  | 
 |   profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta()); | 
 |  | 
 |   const SampledProfile& proto = profile_builder->test_sampled_profile(); | 
 |  | 
 |   ASSERT_TRUE(proto.has_process()); | 
 |   ASSERT_EQ(BROWSER_PROCESS, proto.process()); | 
 |   ASSERT_TRUE(proto.has_thread()); | 
 |   ASSERT_EQ(MAIN_THREAD, proto.thread()); | 
 |   ASSERT_TRUE(proto.has_trigger_event()); | 
 |   ASSERT_EQ(SampledProfile::PROCESS_STARTUP, proto.trigger_event()); | 
 |  | 
 |   ASSERT_TRUE(proto.has_call_stack_profile()); | 
 |   const CallStackProfile& profile = proto.call_stack_profile(); | 
 |   ASSERT_EQ(1, profile.stack_size()); | 
 |   ASSERT_EQ(2, profile.stack_sample_size()); | 
 |   EXPECT_EQ(0, profile.stack_sample(0).stack_index()); | 
 |   EXPECT_EQ(0, profile.stack_sample(1).stack_index()); | 
 | } | 
 |  | 
 | TEST(CallStackProfileBuilderTest, StacksNotDeduped) { | 
 |   auto profile_builder = | 
 |       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams); | 
 |   base::MetadataRecorder metadata_recorder; | 
 |  | 
 |   base::TestModule module1; | 
 |   base::Frame frame1 = {0x10, &module1}; | 
 |  | 
 |   base::TestModule module2; | 
 |   base::Frame frame2 = {0x20, &module2}; | 
 |  | 
 |   std::vector<base::Frame> frames1 = {frame1}; | 
 |   std::vector<base::Frame> frames2 = {frame2}; | 
 |  | 
 |   // Two stacks are completed with the different frames therefore not deduped. | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted(frames1, base::TimeTicks()); | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted(frames2, base::TimeTicks()); | 
 |  | 
 |   profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta()); | 
 |  | 
 |   const SampledProfile& proto = profile_builder->test_sampled_profile(); | 
 |  | 
 |   ASSERT_TRUE(proto.has_process()); | 
 |   ASSERT_EQ(BROWSER_PROCESS, proto.process()); | 
 |   ASSERT_TRUE(proto.has_thread()); | 
 |   ASSERT_EQ(MAIN_THREAD, proto.thread()); | 
 |   ASSERT_TRUE(proto.has_trigger_event()); | 
 |   ASSERT_EQ(SampledProfile::PROCESS_STARTUP, proto.trigger_event()); | 
 |  | 
 |   ASSERT_TRUE(proto.has_call_stack_profile()); | 
 |   const CallStackProfile& profile = proto.call_stack_profile(); | 
 |   ASSERT_EQ(2, profile.stack_size()); | 
 |   ASSERT_EQ(2, profile.stack_sample_size()); | 
 |   EXPECT_EQ(0, profile.stack_sample(0).stack_index()); | 
 |   EXPECT_EQ(1, profile.stack_sample(1).stack_index()); | 
 | } | 
 |  | 
 | TEST(CallStackProfileBuilderTest, Modules) { | 
 |   auto profile_builder = | 
 |       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams); | 
 |   base::MetadataRecorder metadata_recorder; | 
 |  | 
 |   // A frame with no module. | 
 |   base::Frame frame1 = {0x1010, nullptr}; | 
 |  | 
 |   const uintptr_t module_base_address2 = 0x1100; | 
 | #if BUILDFLAG(IS_WIN) | 
 |   uint64_t module_md5 = 0x46C3E4166659AC02ULL; | 
 |   base::FilePath module_path(L"c:\\some\\path\\to\\chrome.exe"); | 
 | #else | 
 |   uint64_t module_md5 = 0x554838A8451AC36CULL; | 
 |   base::FilePath module_path("/some/path/to/chrome"); | 
 | #endif | 
 |   base::TestModule module2(module_base_address2); | 
 |   module2.set_id("2"); | 
 |   module2.set_debug_basename(module_path); | 
 |   base::Frame frame2 = {module_base_address2 + 0x10, &module2}; | 
 |  | 
 |   std::vector<base::Frame> frames = {frame1, frame2}; | 
 |  | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted(frames, base::TimeTicks()); | 
 |   profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta()); | 
 |  | 
 |   const SampledProfile& proto = profile_builder->test_sampled_profile(); | 
 |  | 
 |   ASSERT_TRUE(proto.has_call_stack_profile()); | 
 |   const CallStackProfile& profile = proto.call_stack_profile(); | 
 |  | 
 |   ASSERT_EQ(1, profile.stack_sample_size()); | 
 |   EXPECT_EQ(0, profile.stack_sample(0).stack_index()); | 
 |  | 
 |   ASSERT_EQ(1, profile.stack_size()); | 
 |   ASSERT_EQ(2, profile.stack(0).frame_size()); | 
 |  | 
 |   ASSERT_FALSE(profile.stack(0).frame(0).has_module_id_index()); | 
 |   ASSERT_FALSE(profile.stack(0).frame(0).has_address()); | 
 |  | 
 |   ASSERT_TRUE(profile.stack(0).frame(1).has_module_id_index()); | 
 |   EXPECT_EQ(0, profile.stack(0).frame(1).module_id_index()); | 
 |   ASSERT_TRUE(profile.stack(0).frame(1).has_address()); | 
 |   EXPECT_EQ(0x10ULL, profile.stack(0).frame(1).address()); | 
 |  | 
 |   ASSERT_EQ(1, profile.module_id().size()); | 
 |   ASSERT_TRUE(profile.module_id(0).has_build_id()); | 
 |   EXPECT_EQ("2", profile.module_id(0).build_id()); | 
 |   ASSERT_TRUE(profile.module_id(0).has_name_md5_prefix()); | 
 |   EXPECT_EQ(module_md5, profile.module_id(0).name_md5_prefix()); | 
 | } | 
 |  | 
 | TEST(CallStackProfileBuilderTest, DedupModules) { | 
 |   auto profile_builder = | 
 |       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams); | 
 |   base::MetadataRecorder metadata_recorder; | 
 |  | 
 |   const uintptr_t module_base_address = 0x1000; | 
 |  | 
 | #if BUILDFLAG(IS_WIN) | 
 |   uint64_t module_md5 = 0x46C3E4166659AC02ULL; | 
 |   base::FilePath module_path(L"c:\\some\\path\\to\\chrome.exe"); | 
 | #else | 
 |   uint64_t module_md5 = 0x554838A8451AC36CULL; | 
 |   base::FilePath module_path("/some/path/to/chrome"); | 
 | #endif | 
 |  | 
 |   base::TestModule module(module_base_address); | 
 |   module.set_id("1"); | 
 |   module.set_debug_basename(module_path); | 
 |   base::Frame frame1 = {module_base_address + 0x10, &module}; | 
 |   base::Frame frame2 = {module_base_address + 0x20, &module}; | 
 |  | 
 |   std::vector<base::Frame> frames = {frame1, frame2}; | 
 |  | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted(frames, base::TimeTicks()); | 
 |   profile_builder->OnProfileCompleted(base::TimeDelta(), base::TimeDelta()); | 
 |  | 
 |   const SampledProfile& proto = profile_builder->test_sampled_profile(); | 
 |  | 
 |   ASSERT_TRUE(proto.has_call_stack_profile()); | 
 |   const CallStackProfile& profile = proto.call_stack_profile(); | 
 |  | 
 |   ASSERT_EQ(1, profile.stack_sample_size()); | 
 |   EXPECT_EQ(0, profile.stack_sample(0).stack_index()); | 
 |  | 
 |   ASSERT_EQ(1, profile.stack_size()); | 
 |   ASSERT_EQ(2, profile.stack(0).frame_size()); | 
 |  | 
 |   // The two frames share the same module, which should be deduped in the | 
 |   // output. | 
 |   ASSERT_TRUE(profile.stack(0).frame(0).has_module_id_index()); | 
 |   EXPECT_EQ(0, profile.stack(0).frame(0).module_id_index()); | 
 |   ASSERT_TRUE(profile.stack(0).frame(0).has_address()); | 
 |   EXPECT_EQ(0x10ULL, profile.stack(0).frame(0).address()); | 
 |  | 
 |   ASSERT_TRUE(profile.stack(0).frame(1).has_module_id_index()); | 
 |   EXPECT_EQ(0, profile.stack(0).frame(1).module_id_index()); | 
 |   ASSERT_TRUE(profile.stack(0).frame(1).has_address()); | 
 |   EXPECT_EQ(0x20ULL, profile.stack(0).frame(1).address()); | 
 |  | 
 |   ASSERT_EQ(1, profile.module_id().size()); | 
 |   ASSERT_TRUE(profile.module_id(0).has_build_id()); | 
 |   EXPECT_EQ("1", profile.module_id(0).build_id()); | 
 |   ASSERT_TRUE(profile.module_id(0).has_name_md5_prefix()); | 
 |   EXPECT_EQ(module_md5, profile.module_id(0).name_md5_prefix()); | 
 | } | 
 |  | 
 | TEST(CallStackProfileBuilderTest, WorkIds) { | 
 |   class TestWorkIdRecorder : public WorkIdRecorder { | 
 |    public: | 
 |     unsigned int RecordWorkId() const override { return current_id; } | 
 |  | 
 |     unsigned int current_id = 0; | 
 |   }; | 
 |  | 
 |   TestWorkIdRecorder work_id_recorder; | 
 |   auto profile_builder = std::make_unique<TestingCallStackProfileBuilder>( | 
 |       kProfileParams, &work_id_recorder); | 
 |   base::MetadataRecorder metadata_recorder; | 
 |  | 
 |   base::TestModule module; | 
 |   base::Frame frame = {0x10, &module}; | 
 |  | 
 |   // Id 0 means the message loop hasn't been started yet, so the sample should | 
 |   // not have continued_work set. | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted({frame}, base::TimeTicks()); | 
 |  | 
 |   // The second sample with the same id should have continued_work set. | 
 |   work_id_recorder.current_id = 1; | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted({frame}, base::TimeTicks()); | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted({frame}, base::TimeTicks()); | 
 |  | 
 |   // Ids are in general non-contiguous across multiple samples. | 
 |   work_id_recorder.current_id = 10; | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted({frame}, base::TimeTicks()); | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted({frame}, base::TimeTicks()); | 
 |  | 
 |   profile_builder->OnProfileCompleted(base::Milliseconds(500), | 
 |                                       base::Milliseconds(100)); | 
 |  | 
 |   const SampledProfile& proto = profile_builder->test_sampled_profile(); | 
 |  | 
 |   ASSERT_TRUE(proto.has_call_stack_profile()); | 
 |   const CallStackProfile& profile = proto.call_stack_profile(); | 
 |  | 
 |   ASSERT_EQ(5, profile.stack_sample_size()); | 
 |   EXPECT_FALSE(profile.stack_sample(0).has_continued_work()); | 
 |   EXPECT_FALSE(profile.stack_sample(1).has_continued_work()); | 
 |   EXPECT_TRUE(profile.stack_sample(2).continued_work()); | 
 |   EXPECT_FALSE(profile.stack_sample(3).has_continued_work()); | 
 |   EXPECT_TRUE(profile.stack_sample(4).continued_work()); | 
 | } | 
 |  | 
 | TEST(CallStackProfileBuilderTest, ProfileStartTime) { | 
 |   auto profile_builder = | 
 |       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams); | 
 |  | 
 |   base::TestModule module; | 
 |   const base::Frame frame = {0x10, &module}; | 
 |   const base::TimeTicks first_sample_time = base::TimeTicks::UnixEpoch(); | 
 |  | 
 |   profile_builder->OnSampleCompleted({frame}, first_sample_time); | 
 |   profile_builder->OnSampleCompleted({frame}, | 
 |                                      first_sample_time + base::Seconds(1)); | 
 |   profile_builder->OnProfileCompleted(base::Seconds(1), base::Seconds(1)); | 
 |  | 
 |   EXPECT_EQ(first_sample_time, profile_builder->test_profile_start_time()); | 
 | } | 
 |  | 
 | // A basic test of RecordMetadata at the level of the | 
 | // CallStackProfileBuilder. The underlying implementation in | 
 | // CallStackProfileMetadata is tested independently. | 
 | TEST(CallStackProfileBuilderTest, RecordMetadata) { | 
 |   base::MetadataRecorder metadata_recorder; | 
 |   auto profile_builder = | 
 |       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams, nullptr); | 
 |  | 
 |   base::TestModule module; | 
 |   base::Frame frame = {0x10, &module}; | 
 |  | 
 |   metadata_recorder.Set(100, absl::nullopt, absl::nullopt, 10); | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted({frame}, base::TimeTicks()); | 
 |  | 
 |   profile_builder->OnProfileCompleted(base::Milliseconds(500), | 
 |                                       base::Milliseconds(100)); | 
 |  | 
 |   const SampledProfile& proto = profile_builder->test_sampled_profile(); | 
 |  | 
 |   ASSERT_TRUE(proto.has_call_stack_profile()); | 
 |   const CallStackProfile& profile = proto.call_stack_profile(); | 
 |  | 
 |   ASSERT_EQ(1, profile.metadata_name_hash_size()); | 
 |   EXPECT_EQ(100u, profile.metadata_name_hash(0)); | 
 |  | 
 |   ASSERT_EQ(1, profile.stack_sample_size()); | 
 |  | 
 |   auto sample = profile.stack_sample(0); | 
 |   ASSERT_EQ(1, sample.metadata_size()); | 
 |   EXPECT_EQ(0, sample.metadata(0).name_hash_index()); | 
 |   EXPECT_FALSE(sample.metadata(0).has_key()); | 
 |   EXPECT_EQ(10, sample.metadata(0).value()); | 
 | } | 
 |  | 
 | // A basic test of ApplyMetadataRetrospectively at the level of the | 
 | // CallStackProfileBuilder. The underlying implementation in | 
 | // CallStackProfileMetadata is tested independently. | 
 | TEST(CallStackProfileBuilderTest, ApplyMetadataRetrospectively_Basic) { | 
 |   base::MetadataRecorder metadata_recorder; | 
 |   auto profile_builder = | 
 |       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams, nullptr); | 
 |  | 
 |   base::TestModule module; | 
 |   base::Frame frame = {0x10, &module}; | 
 |   base::TimeTicks profile_start_time = base::TimeTicks::UnixEpoch(); | 
 |   base::TimeDelta sample_time_delta = base::Seconds(1); | 
 |  | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted({frame}, profile_start_time); | 
 |  | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted({frame}, | 
 |                                      profile_start_time + sample_time_delta); | 
 |  | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted( | 
 |       {frame}, profile_start_time + 2 * sample_time_delta); | 
 |  | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted( | 
 |       {frame}, profile_start_time + 3 * sample_time_delta); | 
 |  | 
 |   // Apply the metadata from the second through third samples. | 
 |   profile_builder->ApplyMetadataRetrospectively( | 
 |       profile_start_time + sample_time_delta, | 
 |       profile_start_time + sample_time_delta * 2, | 
 |       base::MetadataRecorder::Item(3, 30, absl::nullopt, 300)); | 
 |  | 
 |   profile_builder->OnProfileCompleted(3 * sample_time_delta, sample_time_delta); | 
 |  | 
 |   const SampledProfile& proto = profile_builder->test_sampled_profile(); | 
 |  | 
 |   ASSERT_TRUE(proto.has_call_stack_profile()); | 
 |   const CallStackProfile& profile = proto.call_stack_profile(); | 
 |  | 
 |   ASSERT_EQ(1, profile.metadata_name_hash_size()); | 
 |   EXPECT_EQ(3u, profile.metadata_name_hash(0)); | 
 |  | 
 |   EXPECT_EQ(4, profile.stack_sample_size()); | 
 |  | 
 |   EXPECT_EQ(0, profile.stack_sample(0).metadata_size()); | 
 |  | 
 |   ASSERT_EQ(1, profile.stack_sample(1).metadata_size()); | 
 |   EXPECT_EQ(0, profile.stack_sample(1).metadata(0).name_hash_index()); | 
 |   EXPECT_EQ(30, profile.stack_sample(1).metadata(0).key()); | 
 |   EXPECT_EQ(300, profile.stack_sample(1).metadata(0).value()); | 
 |  | 
 |   EXPECT_EQ(0, profile.stack_sample(2).metadata_size()); | 
 |  | 
 |   ASSERT_EQ(1, profile.stack_sample(3).metadata_size()); | 
 |   EXPECT_EQ(0, profile.stack_sample(3).metadata(0).name_hash_index()); | 
 |   EXPECT_EQ(30, profile.stack_sample(3).metadata(0).key()); | 
 |   EXPECT_FALSE(profile.stack_sample(3).metadata(0).has_value()); | 
 | } | 
 |  | 
 | // Checks that ApplyMetadataRetrospectively doesn't apply metadata if the | 
 | // requested start time is before the profile start time. | 
 | TEST(CallStackProfileBuilderTest, | 
 |      ApplyMetadataRetrospectively_BeforeStartTime) { | 
 |   base::MetadataRecorder metadata_recorder; | 
 |   auto profile_builder = | 
 |       std::make_unique<TestingCallStackProfileBuilder>(kProfileParams, nullptr); | 
 |  | 
 |   base::TestModule module; | 
 |   base::Frame frame = {0x10, &module}; | 
 |   base::TimeTicks profile_start_time = base::TimeTicks::UnixEpoch(); | 
 |   base::TimeDelta sample_time_delta = base::Seconds(1); | 
 |  | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted({frame}, profile_start_time); | 
 |  | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted({frame}, | 
 |                                      profile_start_time + sample_time_delta); | 
 |  | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted( | 
 |       {frame}, profile_start_time + 2 * sample_time_delta); | 
 |  | 
 |   profile_builder->RecordMetadata(base::MetadataRecorder::MetadataProvider( | 
 |       &metadata_recorder, base::PlatformThread::CurrentId())); | 
 |   profile_builder->OnSampleCompleted( | 
 |       {frame}, profile_start_time + 3 * sample_time_delta); | 
 |  | 
 |   profile_builder->ApplyMetadataRetrospectively( | 
 |       profile_start_time - base::Microseconds(1), | 
 |       profile_start_time + sample_time_delta, | 
 |       base::MetadataRecorder::Item(3, 30, absl::nullopt, 300)); | 
 |  | 
 |   profile_builder->OnProfileCompleted(3 * sample_time_delta, sample_time_delta); | 
 |  | 
 |   const SampledProfile& proto = profile_builder->test_sampled_profile(); | 
 |  | 
 |   ASSERT_TRUE(proto.has_call_stack_profile()); | 
 |   const CallStackProfile& profile = proto.call_stack_profile(); | 
 |  | 
 |   EXPECT_EQ(0, profile.metadata_name_hash_size()); | 
 |   EXPECT_EQ(4, profile.stack_sample_size()); | 
 |  | 
 |   for (const CallStackProfile::StackSample& sample : profile.stack_sample()) | 
 |     EXPECT_EQ(0, sample.metadata_size()); | 
 | } | 
 |  | 
 | }  // namespace metrics |