| // Copyright 2019 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/metrics/perf/heap_collector.h" |
| |
| #include <memory> |
| #include <string> |
| #include <unordered_map> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/allocator/allocator_extension.h" |
| #include "base/files/file.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/macros.h" |
| #include "base/sampling_heap_profiler/poisson_allocation_sampler.h" |
| #include "base/sampling_heap_profiler/sampling_heap_profiler.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "base/test/test_simple_task_runner.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/test/base/test_browser_window.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/services/heap_profiling/public/cpp/settings.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/metrics_proto/sampled_profile.pb.h" |
| |
| namespace metrics { |
| |
| namespace { |
| |
| // Returns a sample PerfDataProto for a heap profile. |
| PerfDataProto GetSampleHeapPerfDataProto() { |
| PerfDataProto proto; |
| |
| // Add file attributes. |
| PerfDataProto_PerfFileAttr* file_attr = proto.add_file_attrs(); |
| PerfDataProto_PerfEventAttr* event_attr = file_attr->mutable_attr(); |
| event_attr->set_type(1); // PERF_TYPE_SOFTWARE |
| event_attr->set_size(96); // PERF_ATTR_SIZE_VER3 |
| event_attr->set_config(9); // PERF_COUNT_SW_DUMMY |
| event_attr->set_sample_id_all(true); |
| event_attr->set_sample_period(111222); |
| event_attr->set_sample_type(1 /*PERF_SAMPLE_IP*/ | 2 /*PERF_SAMPLE_TID*/ | |
| 32 /*PERF_SAMPLE_CALLCHAIN*/ | |
| 64 /*PERF_SAMPLE_ID*/ | |
| 256 /*PERF_SAMPLE_PERIOD*/); |
| event_attr->set_mmap(true); |
| file_attr->add_ids(0); |
| |
| PerfDataProto_PerfEventType* event_type = proto.add_event_types(); |
| event_type->set_id(9); // PERF_COUNT_SW_DUMMY |
| |
| file_attr = proto.add_file_attrs(); |
| event_attr = file_attr->mutable_attr(); |
| event_attr->set_type(1); // PERF_TYPE_SOFTWARE |
| event_attr->set_size(96); // PERF_ATTR_SIZE_VER3 |
| event_attr->set_config(9); // PERF_COUNT_SW_DUMMY |
| event_attr->set_sample_id_all(true); |
| event_attr->set_sample_period(111222); |
| event_attr->set_sample_type(1 /*PERF_SAMPLE_IP*/ | 2 /*PERF_SAMPLE_TID*/ | |
| 32 /*PERF_SAMPLE_CALLCHAIN*/ | |
| 64 /*PERF_SAMPLE_ID*/ | |
| 256 /*PERF_SAMPLE_PERIOD*/); |
| file_attr->add_ids(1); |
| |
| event_type = proto.add_event_types(); |
| event_type->set_id(9); // PERF_COUNT_SW_DUMMY |
| |
| // Add MMAP event. |
| PerfDataProto_PerfEvent* event = proto.add_events(); |
| PerfDataProto_EventHeader* header = event->mutable_header(); |
| header->set_type(1); // PERF_RECORD_MMAP |
| header->set_misc(0); |
| header->set_size(0); |
| |
| PerfDataProto_MMapEvent* mmap = event->mutable_mmap_event(); |
| mmap->set_pid(3456); |
| mmap->set_tid(3456); |
| mmap->set_start(0x617aa770f000); |
| mmap->set_len(0x617ab0689000 - 0x617aa770f000); |
| mmap->set_pgoff(16); |
| |
| PerfDataProto_SampleInfo* sample_info = mmap->mutable_sample_info(); |
| sample_info->set_pid(3456); |
| sample_info->set_tid(3456); |
| sample_info->set_id(0); |
| |
| // Add Sample events. |
| event = proto.add_events(); |
| header = event->mutable_header(); |
| header->set_type(9); // PERF_RECORD_SAMPLE |
| header->set_misc(2); // PERF_RECORD_MISC_USER |
| header->set_size(0); |
| |
| double scale = 1 / (1 - exp(-(1024.00 / 2.00) / 111222.00)); |
| |
| PerfDataProto_SampleEvent* sample = event->mutable_sample_event(); |
| sample->set_ip(0x617aae951c31); |
| sample->set_pid(3456); |
| sample->set_tid(3456); |
| sample->set_id(0); |
| sample->set_period(2 * scale); |
| sample->add_callchain(static_cast<uint64_t>(-512)); // PERF_CONTEXT_USER |
| sample->add_callchain(0x617aae951c31); |
| sample->add_callchain(0x617aae95062e); |
| |
| event = proto.add_events(); |
| header = event->mutable_header(); |
| header->set_type(9); // PERF_RECORD_SAMPLE |
| header->set_misc(2); // PERF_RECORD_MISC_USER |
| header->set_size(0); |
| |
| sample = event->mutable_sample_event(); |
| sample->set_ip(0x617aae951c31); |
| sample->set_pid(3456); |
| sample->set_tid(3456); |
| sample->set_id(1); |
| sample->set_period(1024 * scale); |
| sample->add_callchain(static_cast<uint64_t>(-512)); // PERF_CONTEXT_USER |
| sample->add_callchain(0x617aae951c31); |
| sample->add_callchain(0x617aae95062e); |
| |
| return proto; |
| } |
| |
| // Allows access to some private methods for testing. |
| class TestHeapCollector : public HeapCollector { |
| public: |
| TestHeapCollector() : HeapCollector(HeapCollectionMode::kTcmalloc) {} |
| explicit TestHeapCollector(HeapCollectionMode mode) : HeapCollector(mode) {} |
| |
| using HeapCollector::collection_params; |
| using HeapCollector::CollectProfile; |
| using HeapCollector::DumpProfileToTempFile; |
| using HeapCollector::IsEnabled; |
| using HeapCollector::MakeQuipperCommand; |
| using HeapCollector::Mode; |
| using HeapCollector::ParseAndSaveProfile; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(TestHeapCollector); |
| }; |
| |
| size_t HeapSamplingPeriod(const TestHeapCollector& collector) { |
| CHECK_EQ(collector.Mode(), HeapCollectionMode::kTcmalloc) |
| << "Reading heap sampling period works only with tcmalloc sampling"; |
| size_t sampling_period; |
| CHECK(base::allocator::GetNumericProperty("tcmalloc.sampling_period_bytes", |
| &sampling_period)) |
| << "Failed to read heap sampling period"; |
| return sampling_period; |
| } |
| |
| } // namespace |
| |
| class HeapCollectorTest : public testing::Test { |
| public: |
| HeapCollectorTest() {} |
| |
| // Opens a new browser window, which can be incognito, and returns a unique |
| // handle for it. |
| size_t OpenBrowserWindow(bool incognito) { |
| auto browser_window = std::make_unique<TestBrowserWindow>(); |
| Profile* browser_profile = |
| incognito ? profile_->GetOffTheRecordProfile() : profile_.get(); |
| Browser::CreateParams params(browser_profile, true); |
| params.type = Browser::TYPE_TABBED; |
| params.window = browser_window.get(); |
| auto browser = std::make_unique<Browser>(params); |
| |
| size_t handle = next_browser_id++; |
| open_browsers_[handle] = |
| std::make_pair(std::move(browser_window), std::move(browser)); |
| return handle; |
| } |
| |
| // Closes the browser window with the given handle. |
| void CloseBrowserWindow(size_t handle) { |
| auto it = open_browsers_.find(handle); |
| ASSERT_FALSE(it == open_browsers_.end()); |
| open_browsers_.erase(it); |
| } |
| |
| void SetUp() override { |
| // Instantiate a testing profile. |
| TestingProfile::Builder profile_builder; |
| profile_ = profile_builder.Build(); |
| } |
| |
| void MakeHeapCollector(HeapCollectionMode mode) { |
| heap_collector_ = std::make_unique<TestHeapCollector>(mode); |
| // HeapCollector requires the user to be logged in. |
| heap_collector_->OnUserLoggedIn(); |
| } |
| |
| void TearDown() override { |
| heap_collector_.reset(); |
| open_browsers_.clear(); |
| profile_.reset(); |
| } |
| |
| protected: |
| // Needed to pass PrerenderManager's DCHECKs. |
| content::TestBrowserThreadBundle test_browser_thread_bundle_; |
| |
| // The associated testing browser profile. |
| std::unique_ptr<TestingProfile> profile_; |
| |
| // Keep track of the open browsers and accompanying windows. |
| std::unordered_map< |
| size_t, |
| std::pair<std::unique_ptr<TestBrowserWindow>, std::unique_ptr<Browser>>> |
| open_browsers_; |
| static size_t next_browser_id; |
| |
| std::unique_ptr<TestHeapCollector> heap_collector_; |
| |
| DISALLOW_COPY_AND_ASSIGN(HeapCollectorTest); |
| }; |
| |
| size_t HeapCollectorTest::next_browser_id = 1; |
| |
| TEST_F(HeapCollectorTest, CheckSetup_Tcmalloc) { |
| MakeHeapCollector(HeapCollectionMode::kTcmalloc); |
| heap_collector_->Init(); |
| |
| // No profiles are cached on start. |
| std::vector<SampledProfile> stored_profiles; |
| EXPECT_FALSE(heap_collector_->GetSampledProfiles(&stored_profiles)); |
| EXPECT_TRUE(stored_profiles.empty()); |
| |
| // Heap sampling is enabled when no incognito window is open. |
| size_t sampling_period = HeapSamplingPeriod(*heap_collector_); |
| EXPECT_GT(sampling_period, 0u); |
| } |
| |
| TEST_F(HeapCollectorTest, CheckSetup_ShimLayer) { |
| MakeHeapCollector(HeapCollectionMode::kShimLayer); |
| heap_collector_->Init(); |
| |
| // No profiles are cached on start. |
| std::vector<SampledProfile> stored_profiles; |
| EXPECT_FALSE(heap_collector_->GetSampledProfiles(&stored_profiles)); |
| EXPECT_TRUE(stored_profiles.empty()); |
| |
| EXPECT_TRUE(heap_collector_->IsEnabled()); |
| } |
| |
| TEST_F(HeapCollectorTest, IncognitoWindowDisablesSamplingOnInit_Tcmalloc) { |
| MakeHeapCollector(HeapCollectionMode::kTcmalloc); |
| OpenBrowserWindow(/*incognito=*/true); |
| heap_collector_->Init(); |
| |
| // Heap sampling is disabled when an incognito session is active. |
| size_t sampling_period = HeapSamplingPeriod(*heap_collector_); |
| EXPECT_EQ(sampling_period, 0u); |
| } |
| |
| TEST_F(HeapCollectorTest, IncognitoWindowDisablesSamplingOnInit_ShimLayer) { |
| MakeHeapCollector(HeapCollectionMode::kShimLayer); |
| OpenBrowserWindow(/*incognito=*/true); |
| heap_collector_->Init(); |
| |
| // Heap sampling is disabled when an incognito session is active. |
| EXPECT_FALSE(heap_collector_->IsEnabled()); |
| } |
| |
| TEST_F(HeapCollectorTest, IncognitoWindowPausesSampling_Tcmalloc) { |
| MakeHeapCollector(HeapCollectionMode::kTcmalloc); |
| heap_collector_->Init(); |
| |
| // Heap sampling is enabled. |
| size_t sampling_period = HeapSamplingPeriod(*heap_collector_); |
| EXPECT_GT(sampling_period, 0u); |
| |
| // Opening an incognito window disables sampling. |
| auto win1 = OpenBrowserWindow(/*incognito=*/true); |
| sampling_period = HeapSamplingPeriod(*heap_collector_); |
| EXPECT_EQ(sampling_period, 0u); |
| |
| // Opening a regular window doesn't resume sampling. |
| OpenBrowserWindow(/*incognito=*/false); |
| // Heap sampling is still disabled. |
| sampling_period = HeapSamplingPeriod(*heap_collector_); |
| EXPECT_EQ(sampling_period, 0u); |
| |
| // Open another incognito window and close the first one. |
| auto win3 = OpenBrowserWindow(/*incognito=*/true); |
| CloseBrowserWindow(win1); |
| // Heap sampling is still disabled. |
| sampling_period = HeapSamplingPeriod(*heap_collector_); |
| EXPECT_EQ(sampling_period, 0u); |
| |
| // Closing the last incognito window resumes heap sampling. |
| CloseBrowserWindow(win3); |
| // Heap sampling is enabled. |
| sampling_period = HeapSamplingPeriod(*heap_collector_); |
| EXPECT_GT(sampling_period, 0u); |
| } |
| |
| TEST_F(HeapCollectorTest, IncognitoWindowPausesSampling_ShimLayer) { |
| MakeHeapCollector(HeapCollectionMode::kShimLayer); |
| heap_collector_->Init(); |
| |
| // Heap sampling is enabled. |
| EXPECT_TRUE(heap_collector_->IsEnabled()); |
| |
| // Opening an incognito window disables sampling and doesn't crash the test. |
| auto win1 = OpenBrowserWindow(/*incognito=*/true); |
| EXPECT_FALSE(heap_collector_->IsEnabled()); |
| |
| // Open also a regular window. Sampling still disabled. |
| OpenBrowserWindow(/*incognito=*/false); |
| EXPECT_FALSE(heap_collector_->IsEnabled()); |
| |
| // Open another incognito window and close the first one. |
| auto win3 = OpenBrowserWindow(/*incognito=*/true); |
| CloseBrowserWindow(win1); |
| EXPECT_FALSE(heap_collector_->IsEnabled()); |
| |
| // Closing the last incognito window resumes heap sampling, without |
| // crashing the test. |
| CloseBrowserWindow(win3); |
| EXPECT_TRUE(heap_collector_->IsEnabled()); |
| } |
| |
| TEST_F(HeapCollectorTest, DumpProfileToTempFile_Tcmalloc) { |
| MakeHeapCollector(HeapCollectionMode::kTcmalloc); |
| base::Optional<base::FilePath> got_path = |
| heap_collector_->DumpProfileToTempFile(); |
| // Check that we got a path. |
| ASSERT_TRUE(got_path); |
| // Check that the file is readable and not empty. |
| base::File temp(got_path.value(), |
| base::File::FLAG_OPEN | base::File::FLAG_READ); |
| ASSERT_TRUE(temp.IsValid()); |
| EXPECT_GT(temp.GetLength(), 0); |
| temp.Close(); |
| // We must be able to remove the temp file. |
| ASSERT_TRUE(base::DeleteFile(got_path.value(), false)); |
| } |
| |
| TEST_F(HeapCollectorTest, DumpProfileToTempFile_ShimLayer) { |
| MakeHeapCollector(HeapCollectionMode::kShimLayer); |
| base::Optional<base::FilePath> got_path = |
| heap_collector_->DumpProfileToTempFile(); |
| // Check that we got a path. |
| ASSERT_TRUE(got_path); |
| // Check that the file is readable and not empty. |
| base::File temp(got_path.value(), |
| base::File::FLAG_OPEN | base::File::FLAG_READ); |
| ASSERT_TRUE(temp.IsValid()); |
| EXPECT_GT(temp.GetLength(), 0); |
| temp.Close(); |
| // We must be able to remove the temp file. |
| ASSERT_TRUE(base::DeleteFile(got_path.value(), false)); |
| } |
| |
| TEST_F(HeapCollectorTest, MakeQuipperCommand) { |
| const base::FilePath kTempProfile( |
| FILE_PATH_LITERAL("/tmp/MakeQuipperCommand.test")); |
| base::CommandLine got = heap_collector_->MakeQuipperCommand(kTempProfile); |
| |
| // Check that we got the correct two switch names. |
| ASSERT_EQ(got.GetSwitches().size(), 2u); |
| EXPECT_TRUE(got.HasSwitch("input_heap_profile")); |
| EXPECT_TRUE(got.HasSwitch("pid")); |
| |
| // Check that we got the correct program name and switch values. |
| EXPECT_EQ(got.GetProgram().value(), "/usr/bin/quipper"); |
| EXPECT_EQ(got.GetSwitchValuePath("input_heap_profile"), kTempProfile); |
| EXPECT_EQ(got.GetSwitchValueASCII("pid"), |
| std::to_string(base::GetCurrentProcId())); |
| } |
| |
| TEST_F(HeapCollectorTest, ParseAndSaveProfile_Tcmalloc) { |
| MakeHeapCollector(HeapCollectionMode::kTcmalloc); |
| // Write a sample perf data proto to a temp file. |
| const base::FilePath kTempProfile( |
| FILE_PATH_LITERAL("/tmp/ParseAndSaveProfile.test")); |
| PerfDataProto heap_proto = GetSampleHeapPerfDataProto(); |
| std::string serialized_proto = heap_proto.SerializeAsString(); |
| |
| base::File temp(kTempProfile, base::File::FLAG_CREATE_ALWAYS | |
| base::File::FLAG_READ | |
| base::File::FLAG_WRITE); |
| EXPECT_TRUE(temp.created()); |
| EXPECT_TRUE(temp.IsValid()); |
| int res = temp.WriteAtCurrentPos(serialized_proto.c_str(), |
| serialized_proto.length()); |
| EXPECT_EQ(res, static_cast<int>(serialized_proto.length())); |
| temp.Close(); |
| |
| // Create a command line that copies the input file to the output. |
| base::CommandLine::StringVector argv; |
| argv.push_back("cat"); |
| argv.push_back(kTempProfile.value()); |
| base::CommandLine cat(argv); |
| |
| // Run the command. |
| auto sampled_profile = std::make_unique<SampledProfile>(); |
| sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION); |
| heap_collector_->ParseAndSaveProfile(cat, kTempProfile, |
| std::move(sampled_profile)); |
| |
| // Check that the profile was cached. |
| std::vector<SampledProfile> stored_profiles; |
| EXPECT_TRUE(heap_collector_->GetSampledProfiles(&stored_profiles)); |
| ASSERT_EQ(1U, stored_profiles.size()); |
| |
| const SampledProfile& profile = stored_profiles[0]; |
| EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event()); |
| EXPECT_TRUE(profile.has_ms_after_boot()); |
| EXPECT_TRUE(profile.has_ms_after_login()); |
| |
| ASSERT_TRUE(profile.has_perf_data()); |
| EXPECT_EQ(serialized_proto, profile.perf_data().SerializeAsString()); |
| |
| // Check that the temp profile file is removed after pending tasks complete. |
| heap_collector_->Deactivate(); |
| test_browser_thread_bundle_.RunUntilIdle(); |
| temp = |
| base::File(kTempProfile, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| ASSERT_FALSE(temp.IsValid()); |
| } |
| |
| TEST_F(HeapCollectorTest, ParseAndSaveProfile_ShimLayer) { |
| MakeHeapCollector(HeapCollectionMode::kShimLayer); |
| // Write a sample perf data proto to a temp file. |
| const base::FilePath kTempProfile( |
| FILE_PATH_LITERAL("/tmp/ParseAndSaveProfile.test")); |
| PerfDataProto heap_proto = GetSampleHeapPerfDataProto(); |
| std::string serialized_proto = heap_proto.SerializeAsString(); |
| |
| base::File temp(kTempProfile, base::File::FLAG_CREATE_ALWAYS | |
| base::File::FLAG_READ | |
| base::File::FLAG_WRITE); |
| EXPECT_TRUE(temp.created()); |
| EXPECT_TRUE(temp.IsValid()); |
| int res = temp.WriteAtCurrentPos(serialized_proto.c_str(), |
| serialized_proto.length()); |
| EXPECT_EQ(res, static_cast<int>(serialized_proto.length())); |
| temp.Close(); |
| |
| // Create a command line that copies the input file to the output. |
| base::CommandLine::StringVector argv; |
| argv.push_back("cat"); |
| argv.push_back(kTempProfile.value()); |
| base::CommandLine cat(argv); |
| |
| // Run the command. |
| auto sampled_profile = std::make_unique<SampledProfile>(); |
| sampled_profile->set_trigger_event(SampledProfile::PERIODIC_COLLECTION); |
| heap_collector_->ParseAndSaveProfile(cat, kTempProfile, |
| std::move(sampled_profile)); |
| |
| // Check that the profile was cached. |
| std::vector<SampledProfile> stored_profiles; |
| EXPECT_TRUE(heap_collector_->GetSampledProfiles(&stored_profiles)); |
| ASSERT_EQ(1U, stored_profiles.size()); |
| |
| const SampledProfile& profile = stored_profiles[0]; |
| EXPECT_EQ(SampledProfile::PERIODIC_COLLECTION, profile.trigger_event()); |
| EXPECT_TRUE(profile.has_ms_after_boot()); |
| EXPECT_TRUE(profile.has_ms_after_login()); |
| |
| ASSERT_TRUE(profile.has_perf_data()); |
| EXPECT_EQ(serialized_proto, profile.perf_data().SerializeAsString()); |
| |
| // Check that the temp profile file is removed after pending tasks complete. |
| heap_collector_->Deactivate(); |
| test_browser_thread_bundle_.RunUntilIdle(); |
| temp = |
| base::File(kTempProfile, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| ASSERT_FALSE(temp.IsValid()); |
| } |
| |
| class HeapCollectorCollectionParamsTest : public testing::Test { |
| public: |
| HeapCollectorCollectionParamsTest() |
| : task_runner_(base::MakeRefCounted<base::TestSimpleTaskRunner>()), |
| task_runner_handle_(task_runner_) {} |
| |
| private: |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner_; |
| base::ThreadTaskRunnerHandle task_runner_handle_; |
| |
| DISALLOW_COPY_AND_ASSIGN(HeapCollectorCollectionParamsTest); |
| }; |
| |
| TEST_F(HeapCollectorCollectionParamsTest, ParametersOverride_Tcmalloc) { |
| std::map<std::string, std::string> params; |
| params.insert(std::make_pair("SamplingIntervalBytes", "800000")); |
| params.insert(std::make_pair("PeriodicCollectionIntervalMs", "3600000")); |
| params.insert(std::make_pair("ResumeFromSuspend::SamplingFactor", "1")); |
| params.insert(std::make_pair("ResumeFromSuspend::MaxDelaySec", "10")); |
| params.insert(std::make_pair("RestoreSession::SamplingFactor", "2")); |
| params.insert(std::make_pair("RestoreSession::MaxDelaySec", "20")); |
| |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitAndEnableFeatureWithParameters( |
| heap_profiling::kOOPHeapProfilingFeature, params); |
| |
| TestHeapCollector heap_collector(HeapCollectionMode::kTcmalloc); |
| const auto& parsed_params = heap_collector.collection_params(); |
| |
| // Not initialized yet: |
| size_t sampling_period = HeapSamplingPeriod(heap_collector); |
| EXPECT_NE(800000u, sampling_period); |
| EXPECT_NE(base::TimeDelta::FromHours(1), parsed_params.periodic_interval); |
| EXPECT_NE(1, parsed_params.resume_from_suspend.sampling_factor); |
| EXPECT_NE(base::TimeDelta::FromSeconds(10), |
| parsed_params.resume_from_suspend.max_collection_delay); |
| EXPECT_NE(2, parsed_params.restore_session.sampling_factor); |
| EXPECT_NE(base::TimeDelta::FromSeconds(20), |
| parsed_params.restore_session.max_collection_delay); |
| |
| heap_collector.Init(); |
| |
| sampling_period = HeapSamplingPeriod(heap_collector); |
| EXPECT_EQ(800000u, sampling_period); |
| EXPECT_EQ(base::TimeDelta::FromHours(1), parsed_params.periodic_interval); |
| EXPECT_EQ(1, parsed_params.resume_from_suspend.sampling_factor); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(10), |
| parsed_params.resume_from_suspend.max_collection_delay); |
| EXPECT_EQ(2, parsed_params.restore_session.sampling_factor); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(20), |
| parsed_params.restore_session.max_collection_delay); |
| } |
| |
| TEST_F(HeapCollectorCollectionParamsTest, ParametersOverride_ShimLayer) { |
| std::map<std::string, std::string> params; |
| params.insert(std::make_pair("SamplingIntervalBytes", "800000")); |
| params.insert(std::make_pair("PeriodicCollectionIntervalMs", "3600000")); |
| params.insert(std::make_pair("ResumeFromSuspend::SamplingFactor", "1")); |
| params.insert(std::make_pair("ResumeFromSuspend::MaxDelaySec", "10")); |
| params.insert(std::make_pair("RestoreSession::SamplingFactor", "2")); |
| params.insert(std::make_pair("RestoreSession::MaxDelaySec", "20")); |
| |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitAndEnableFeatureWithParameters( |
| heap_profiling::kOOPHeapProfilingFeature, params); |
| |
| TestHeapCollector heap_collector(HeapCollectionMode::kShimLayer); |
| const auto& parsed_params = heap_collector.collection_params(); |
| |
| // Not initialized yet: |
| EXPECT_NE(base::TimeDelta::FromHours(1), parsed_params.periodic_interval); |
| EXPECT_NE(1, parsed_params.resume_from_suspend.sampling_factor); |
| EXPECT_NE(base::TimeDelta::FromSeconds(10), |
| parsed_params.resume_from_suspend.max_collection_delay); |
| EXPECT_NE(2, parsed_params.restore_session.sampling_factor); |
| EXPECT_NE(base::TimeDelta::FromSeconds(20), |
| parsed_params.restore_session.max_collection_delay); |
| |
| heap_collector.Init(); |
| |
| EXPECT_EQ(base::TimeDelta::FromHours(1), parsed_params.periodic_interval); |
| EXPECT_EQ(1, parsed_params.resume_from_suspend.sampling_factor); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(10), |
| parsed_params.resume_from_suspend.max_collection_delay); |
| EXPECT_EQ(2, parsed_params.restore_session.sampling_factor); |
| EXPECT_EQ(base::TimeDelta::FromSeconds(20), |
| parsed_params.restore_session.max_collection_delay); |
| } |
| |
| namespace { |
| |
| std::vector<base::SamplingHeapProfiler::Sample> SamplingHeapProfilerSamples() { |
| // Generate a sample using the SamplingHeapProfiler collector. Then, duplicate |
| // and customize their values. |
| base::SamplingHeapProfiler::Init(); |
| auto* collector = base::SamplingHeapProfiler::Get(); |
| collector->Start(); |
| |
| auto* sampler = base::PoissonAllocationSampler::Get(); |
| sampler->SuppressRandomnessForTest(true); |
| sampler->SetSamplingInterval(1000000); |
| // Generate and remove a dummy sample, because the first sample is potentially |
| // ignored by the SHP profiler. |
| sampler->RecordAlloc(reinterpret_cast<void*>(1), 1000000, |
| base::PoissonAllocationSampler::AllocatorType::kMalloc, |
| nullptr); |
| sampler->RecordFree(reinterpret_cast<void*>(1)); |
| // Generate a second sample that we are going to retrieve. |
| sampler->RecordAlloc(reinterpret_cast<void*>(2), 1000000, |
| base::PoissonAllocationSampler::AllocatorType::kMalloc, |
| nullptr); |
| |
| auto samples = collector->GetSamples(0); |
| EXPECT_EQ(1lu, samples.size()); |
| sampler->RecordFree(reinterpret_cast<void*>(2)); |
| collector->Stop(); |
| |
| // Customize the sample. |
| auto& sample1 = samples[0]; |
| sample1.size = 100; |
| sample1.total = 1000; |
| sample1.stack = {reinterpret_cast<void*>(0x10000), |
| reinterpret_cast<void*>(0x10100), |
| reinterpret_cast<void*>(0x10200)}; |
| |
| samples.emplace_back(samples.back()); |
| EXPECT_EQ(2lu, samples.size()); |
| auto& sample2 = samples[1]; |
| sample2.size = 200; |
| sample2.total = 2000; |
| sample2.stack = { |
| reinterpret_cast<void*>(0x20000), reinterpret_cast<void*>(0x20100), |
| reinterpret_cast<void*>(0x20200), reinterpret_cast<void*>(0x20300)}; |
| |
| samples.emplace_back(samples.back()); |
| EXPECT_EQ(3lu, samples.size()); |
| auto& sample3 = samples[2]; |
| sample3.size = 300; |
| sample3.total = 3000; |
| sample3.stack = {reinterpret_cast<void*>(0x30000), |
| reinterpret_cast<void*>(0x30100), |
| reinterpret_cast<void*>(0x30200)}; |
| |
| return samples; |
| } |
| |
| std::string GetProcMaps() { |
| return R"text(304acba71000-304acba72000 ---p 00000000 00:00 0 |
| 304acba72000-304acd86a000 rw-p 00000000 00:00 0 |
| 304acd86a000-304acd86b000 ---p 00000000 00:00 0 |
| 304acd86b000-304acd88a000 rw-p 00000000 00:00 0 |
| 304acd88a000-304acd88b000 ---p 00000000 00:00 0 |
| 304acd88b000-304acd8aa000 rw-p 00000000 00:00 0 |
| 5ffa92db8000-5ffa93d15000 r--p 00000000 b3:03 71780 /opt/google/chrome/chrome |
| 5ffa93d15000-5ffa93d16000 r--p 00f5d000 b3:03 71780 /opt/google/chrome/chrome |
| 5ffa93d16000-5ffa93d17000 r--p 00f5e000 b3:03 71780 /opt/google/chrome/chrome |
| 5ffa93d17000-5ffa9d176000 r-xp 00f5f000 b3:03 71780 /opt/google/chrome/chrome |
| 5ffa9d176000-5ffa9d1ca000 rw-p 0a3be000 b3:03 71780 /opt/google/chrome/chrome |
| 5ffa9d1ca000-5ffa9d8ff000 r--p 0a412000 b3:03 71780 /opt/google/chrome/chrome |
| 5ffa9d8ff000-5ffa9db6c000 rw-p 00000000 00:00 0 |
| 7f9c9e11c000-7f9c9e11d000 ---p 00000000 00:00 0 |
| 7f9c9e11d000-7f9c9e91d000 rw-p 00000000 00:00 0 [stack:1843] |
| 7f9c9e91d000-7f9c9e91e000 ---p 00000000 00:00 0 |
| 7f9c9e91e000-7f9c9f11e000 rw-p 00000000 00:00 0 |
| 7f9ca0d47000-7f9ca0d4c000 r-xp 00000000 b3:03 46090 /lib64/libnss_dns-2.27.so |
| 7f9ca0d4c000-7f9ca0f4b000 ---p 00005000 b3:03 46090 /lib64/libnss_dns-2.27.so |
| 7f9ca0f4b000-7f9ca0f4c000 r--p 00004000 b3:03 46090 /lib64/libnss_dns-2.27.so |
| 7f9ca0f4c000-7f9ca0f4d000 rw-p 00005000 b3:03 46090 /lib64/libnss_dns-2.27.so |
| 7f9ca0f4d000-7f9ca114d000 rw-s 00000000 00:13 16382 /dev/shm/.com.google.Chrome.nohIdv (deleted) |
| 7f9ca26a0000-7f9ca26a1000 ---p 00000000 00:00 0 |
| 7f9ca26a1000-7f9ca2ea1000 rw-p 00000000 00:00 0 [stack:1796] |
| 7f9ca2ea1000-7f9ca2f31000 r-xp 00000000 b3:03 46120 /lib64/libpcre.so.1.2.9 |
| 7f9ca2f31000-7f9ca2f32000 r--p 0008f000 b3:03 46120 /lib64/libpcre.so.1.2.9 |
| 7f9ca2f32000-7f9ca2f33000 rw-p 00090000 b3:03 46120 /lib64/libpcre.so.1.2.9 |
| 7f9ca2f33000-7f9ca302d000 r-xp 00000000 b3:03 40207 /usr/lib64/libglib-2.0.so.0.5200.3 |
| 7f9ca302e000-7f9ca302f000 r--p 000fa000 b3:03 40207 /usr/lib64/libglib-2.0.so.0.5200.3 |
| 7f9ca4687000-7f9ca4887000 rw-s 00000000 00:13 12699 /dev/shm/.com.google.Chrome.KWz8dN (deleted) |
| 7f9ca4887000-7f9ca4888000 ---p 00000000 00:00 0 |
| 7f9ca4888000-7f9ca5088000 rw-p 00000000 00:00 0 [stack:1423] |
| 7f9ca52c8000-7f9ca52c9000 ---p 00000000 00:00 0 |
| 7f9ca52c9000-7f9ca5ac9000 rw-p 00000000 00:00 0 [stack:1411] |
| 7f9ca5aec000-7f9ca5b66000 r--p 00000000 b3:03 39083 /usr/share/fonts/roboto/Roboto-Light.ttf |
| 7f9ca5b66000-7f9ca5d66000 rw-s 00000000 00:13 10081 /dev/shm/.com.google.Chrome.MLuvgs (deleted) |
| )text"; |
| } |
| |
| } // namespace |
| |
| TEST(HeapCollectorShimLayerTest, WriteHeapProfileToFile_InvalidProcMaps) { |
| base::FilePath temp_path; |
| ASSERT_TRUE(base::CreateTemporaryFile(&temp_path)); |
| base::File temp(temp_path, base::File::FLAG_CREATE_ALWAYS | |
| base::File::FLAG_READ | |
| base::File::FLAG_WRITE); |
| ASSERT_TRUE(temp.created()); |
| ASSERT_TRUE(temp.IsValid()); |
| |
| auto samples = SamplingHeapProfilerSamples(); |
| std::string proc_maps = "Bogus proc maps\n"; |
| EXPECT_FALSE(internal::WriteHeapProfileToFile(&temp, samples, proc_maps)); |
| temp.Close(); |
| base::DeleteFile(temp_path, false); |
| } |
| |
| TEST(HeapCollectorShimLayerTest, WriteHeapProfileToFile) { |
| base::FilePath temp_path; |
| ASSERT_TRUE(base::CreateTemporaryFile(&temp_path)); |
| base::File temp(temp_path, base::File::FLAG_CREATE_ALWAYS | |
| base::File::FLAG_READ | |
| base::File::FLAG_WRITE); |
| ASSERT_TRUE(temp.created()); |
| ASSERT_TRUE(temp.IsValid()); |
| |
| auto samples = SamplingHeapProfilerSamples(); |
| auto proc_maps = GetProcMaps(); |
| EXPECT_TRUE(internal::WriteHeapProfileToFile(&temp, samples, proc_maps)); |
| temp.Close(); |
| |
| std::string got; |
| EXPECT_TRUE(base::ReadFileToString(temp_path, &got)); |
| |
| std::string want = R"text(heap profile: 3: 6000 [3: 6000] @ heap_v2/1 |
| 1: 1000 [1: 1000] @ 0x10000 0x10100 0x10200 |
| 1: 2000 [1: 2000] @ 0x20000 0x20100 0x20200 0x20300 |
| 1: 3000 [1: 3000] @ 0x30000 0x30100 0x30200 |
| |
| MAPPED_LIBRARIES: |
| 304acba71000-304acba72000 ---p 00000000 00:00 0 |
| 304acba72000-304acd86a000 rw-p 00000000 00:00 0 |
| 304acd86a000-304acd86b000 ---p 00000000 00:00 0 |
| 304acd86b000-304acd88a000 rw-p 00000000 00:00 0 |
| 304acd88a000-304acd88b000 ---p 00000000 00:00 0 |
| 304acd88b000-304acd8aa000 rw-p 00000000 00:00 0 |
| 5ffa92db8000-5ffa93d15000 r--p 00000000 00:00 0 /opt/google/chrome/chrome |
| 5ffa93d15000-5ffa93d16000 r--p 00f5d000 00:00 0 /opt/google/chrome/chrome |
| 5ffa93d16000-5ffa93d17000 r--p 00f5e000 00:00 0 /opt/google/chrome/chrome |
| 5ffa93d17000-5ffa9d176000 r-xp 00f5f000 00:00 0 /opt/google/chrome/chrome |
| 5ffa9d176000-5ffa9d1ca000 rw-p 0a3be000 00:00 0 /opt/google/chrome/chrome |
| 5ffa9d1ca000-5ffa9d8ff000 r--p 0a412000 00:00 0 /opt/google/chrome/chrome |
| 5ffa9d8ff000-5ffa9db6c000 rw-p 00000000 00:00 0 |
| 7f9c9e11c000-7f9c9e11d000 ---p 00000000 00:00 0 |
| 7f9c9e11d000-7f9c9e91d000 rw-p 00000000 00:00 0 [stack:1843] |
| 7f9c9e91d000-7f9c9e91e000 ---p 00000000 00:00 0 |
| 7f9c9e91e000-7f9c9f11e000 rw-p 00000000 00:00 0 |
| 7f9ca0d47000-7f9ca0d4c000 r-xp 00000000 00:00 0 /lib64/libnss_dns-2.27.so |
| 7f9ca0d4c000-7f9ca0f4b000 ---p 00005000 00:00 0 /lib64/libnss_dns-2.27.so |
| 7f9ca0f4b000-7f9ca0f4c000 r--p 00004000 00:00 0 /lib64/libnss_dns-2.27.so |
| 7f9ca0f4c000-7f9ca0f4d000 rw-p 00005000 00:00 0 /lib64/libnss_dns-2.27.so |
| 7f9ca0f4d000-7f9ca114d000 rw-- 00000000 00:00 0 /dev/shm/.com.google.Chrome.nohIdv (deleted) |
| 7f9ca26a0000-7f9ca26a1000 ---p 00000000 00:00 0 |
| 7f9ca26a1000-7f9ca2ea1000 rw-p 00000000 00:00 0 [stack:1796] |
| 7f9ca2ea1000-7f9ca2f31000 r-xp 00000000 00:00 0 /lib64/libpcre.so.1.2.9 |
| 7f9ca2f31000-7f9ca2f32000 r--p 0008f000 00:00 0 /lib64/libpcre.so.1.2.9 |
| 7f9ca2f32000-7f9ca2f33000 rw-p 00090000 00:00 0 /lib64/libpcre.so.1.2.9 |
| 7f9ca2f33000-7f9ca302d000 r-xp 00000000 00:00 0 /usr/lib64/libglib-2.0.so.0.5200.3 |
| 7f9ca302e000-7f9ca302f000 r--p 000fa000 00:00 0 /usr/lib64/libglib-2.0.so.0.5200.3 |
| 7f9ca4687000-7f9ca4887000 rw-- 00000000 00:00 0 /dev/shm/.com.google.Chrome.KWz8dN (deleted) |
| 7f9ca4887000-7f9ca4888000 ---p 00000000 00:00 0 |
| 7f9ca4888000-7f9ca5088000 rw-p 00000000 00:00 0 [stack:1423] |
| 7f9ca52c8000-7f9ca52c9000 ---p 00000000 00:00 0 |
| 7f9ca52c9000-7f9ca5ac9000 rw-p 00000000 00:00 0 [stack:1411] |
| 7f9ca5aec000-7f9ca5b66000 r--p 00000000 00:00 0 /usr/share/fonts/roboto/Roboto-Light.ttf |
| 7f9ca5b66000-7f9ca5d66000 rw-- 00000000 00:00 0 /dev/shm/.com.google.Chrome.MLuvgs (deleted) |
| )text"; |
| |
| EXPECT_EQ(want, got); |
| base::DeleteFile(temp_path, false); |
| } |
| |
| } // namespace metrics |