| // Copyright 2017 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/profiling_host/background_profiling_triggers.h" |
| |
| #include <set> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/time/time.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" |
| #include "chrome/browser/profiling_host/profiling_process_host.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chrome/test/base/testing_profile_manager.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "services/resource_coordinator/public/cpp/memory_instrumentation/coordinator.h" |
| #include "services/resource_coordinator/public/mojom/memory_instrumentation/memory_instrumentation.mojom.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using GlobalMemoryDump = memory_instrumentation::GlobalMemoryDump; |
| using GlobalMemoryDumpPtr = memory_instrumentation::mojom::GlobalMemoryDumpPtr; |
| using ProcessMemoryDumpPtr = |
| memory_instrumentation::mojom::ProcessMemoryDumpPtr; |
| using OSMemDumpPtr = memory_instrumentation::mojom::OSMemDumpPtr; |
| using ProcessType = memory_instrumentation::mojom::ProcessType; |
| |
| namespace heap_profiling { |
| namespace { |
| |
| constexpr uint32_t kProcessMallocTriggerKb = 2 * 1024 * 1024; // 2 Gig |
| |
| OSMemDumpPtr GetFakeOSMemDump(uint32_t resident_set_kb, |
| uint32_t private_footprint_kb, |
| uint32_t shared_footprint_kb) { |
| return memory_instrumentation::mojom::OSMemDump::New( |
| resident_set_kb, private_footprint_kb, shared_footprint_kb |
| #if defined(OS_LINUX) || defined(OS_ANDROID) |
| , |
| 0 |
| #endif |
| ); |
| } |
| |
| void PopulateMetrics(GlobalMemoryDumpPtr* global_dump, |
| base::ProcessId pid, |
| ProcessType process_type, |
| uint32_t resident_set_kb, |
| uint32_t private_memory_kb, |
| uint32_t shared_footprint_kb) { |
| ProcessMemoryDumpPtr pmd( |
| memory_instrumentation::mojom::ProcessMemoryDump::New()); |
| pmd->pid = pid; |
| pmd->process_type = process_type; |
| pmd->os_dump = |
| GetFakeOSMemDump(resident_set_kb, private_memory_kb, shared_footprint_kb); |
| (*global_dump)->process_dumps.push_back(std::move(pmd)); |
| } |
| |
| } // namespace |
| |
| class FakeBackgroundProfilingTriggers : public BackgroundProfilingTriggers { |
| public: |
| explicit FakeBackgroundProfilingTriggers(ProfilingProcessHost* host) |
| : BackgroundProfilingTriggers(host), |
| was_report_triggered_(false), |
| should_trigger_control_report_(false) {} |
| |
| using BackgroundProfilingTriggers::OnReceivedMemoryDump; |
| |
| void Reset() { |
| should_trigger_control_report_ = false; |
| was_report_triggered_ = false; |
| pmf_at_last_upload_.clear(); |
| } |
| bool WasReportTriggered() const { return was_report_triggered_; } |
| |
| bool ShouldTriggerControlReport(int content_process_type) const override { |
| return should_trigger_control_report_; |
| } |
| void SetControlTrigger(bool trigger_control_report) { |
| should_trigger_control_report_ = trigger_control_report; |
| } |
| |
| private: |
| void TriggerMemoryReport(std::string trigger_name) override { |
| was_report_triggered_ = true; |
| } |
| |
| bool was_report_triggered_; |
| bool should_trigger_control_report_; |
| }; |
| |
| class BackgroundProfilingTriggersTest : public testing::Test { |
| public: |
| BackgroundProfilingTriggersTest() |
| : testing_profile_manager_(TestingBrowserProcess::GetGlobal()), |
| triggers_(&host_), |
| is_metrics_enabled_(true) {} |
| |
| void SetUp() override { |
| ASSERT_TRUE(testing_profile_manager_.SetUp()); |
| ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting( |
| &is_metrics_enabled_); |
| for (base::ProcessId i = 1; i <= 10; ++i) |
| profiled_pids_.push_back(i); |
| } |
| |
| void TearDown() override { |
| ChromeMetricsServiceAccessor::SetMetricsAndCrashReportingForTesting( |
| nullptr); |
| } |
| |
| protected: |
| content::TestBrowserThreadBundle thread_bundle_; |
| TestingProfileManager testing_profile_manager_; |
| |
| ProfilingProcessHost host_; |
| FakeBackgroundProfilingTriggers triggers_; |
| std::vector<base::ProcessId> profiled_pids_; |
| |
| bool is_metrics_enabled_; |
| }; |
| |
| // Ensures: |
| // * robust to empty memory dumps. |
| // * does not trigger if below a size threshold. |
| TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_EmptyCases) { |
| GlobalMemoryDumpPtr dump_empty( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| triggers_.OnReceivedMemoryDump( |
| profiled_pids_, true, GlobalMemoryDump::MoveFrom(std::move(dump_empty))); |
| EXPECT_FALSE(triggers_.WasReportTriggered()); |
| triggers_.Reset(); |
| |
| GlobalMemoryDumpPtr dump_browser( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| PopulateMetrics(&dump_browser, 1, ProcessType::BROWSER, 1, 1, 1); |
| triggers_.OnReceivedMemoryDump( |
| profiled_pids_, true, |
| GlobalMemoryDump::MoveFrom(std::move(dump_browser))); |
| EXPECT_FALSE(triggers_.WasReportTriggered()); |
| triggers_.Reset(); |
| |
| GlobalMemoryDumpPtr dump_gpu( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| PopulateMetrics(&dump_gpu, 1, ProcessType::GPU, 1, 1, 1); |
| triggers_.OnReceivedMemoryDump( |
| profiled_pids_, true, GlobalMemoryDump::MoveFrom(std::move(dump_gpu))); |
| EXPECT_FALSE(triggers_.WasReportTriggered()); |
| triggers_.Reset(); |
| |
| GlobalMemoryDumpPtr dump_renderer( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| PopulateMetrics(&dump_renderer, 1, ProcessType::RENDERER, 1, 1, 1); |
| triggers_.OnReceivedMemoryDump( |
| profiled_pids_, true, |
| GlobalMemoryDump::MoveFrom(std::move(dump_renderer))); |
| EXPECT_FALSE(triggers_.WasReportTriggered()); |
| triggers_.Reset(); |
| |
| GlobalMemoryDumpPtr dump_other( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| PopulateMetrics(&dump_other, 1, ProcessType::OTHER, 1, 1, 1); |
| triggers_.OnReceivedMemoryDump( |
| profiled_pids_, true, GlobalMemoryDump::MoveFrom(std::move(dump_other))); |
| EXPECT_FALSE(triggers_.WasReportTriggered()); |
| triggers_.Reset(); |
| } |
| |
| TEST_F(BackgroundProfilingTriggersTest, OnReceivedMemoryDump_ProfiledPids) { |
| GlobalMemoryDumpPtr dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| PopulateMetrics(&dump, 1, ProcessType::RENDERER, 1, 1, 1); |
| PopulateMetrics(&dump, 2, ProcessType::OTHER, kProcessMallocTriggerKb, |
| kProcessMallocTriggerKb, kProcessMallocTriggerKb); |
| |
| // Small processes and OTHER type processes don't trigger. |
| triggers_.OnReceivedMemoryDump(profiled_pids_, true, |
| GlobalMemoryDump::MoveFrom(std::move(dump))); |
| EXPECT_FALSE(triggers_.WasReportTriggered()); |
| |
| // Ensure each process type triggers. |
| triggers_.Reset(); |
| dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); |
| PopulateMetrics(&dump, 1, ProcessType::BROWSER, kProcessMallocTriggerKb, |
| kProcessMallocTriggerKb, kProcessMallocTriggerKb); |
| triggers_.OnReceivedMemoryDump(profiled_pids_, true, |
| GlobalMemoryDump::MoveFrom(std::move(dump))); |
| EXPECT_TRUE(triggers_.WasReportTriggered()); |
| |
| triggers_.Reset(); |
| dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); |
| PopulateMetrics(&dump, 1, ProcessType::RENDERER, kProcessMallocTriggerKb, |
| kProcessMallocTriggerKb, kProcessMallocTriggerKb); |
| triggers_.OnReceivedMemoryDump(profiled_pids_, true, |
| GlobalMemoryDump::MoveFrom(std::move(dump))); |
| EXPECT_TRUE(triggers_.WasReportTriggered()); |
| |
| triggers_.Reset(); |
| dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); |
| PopulateMetrics(&dump, 1, ProcessType::GPU, kProcessMallocTriggerKb, |
| kProcessMallocTriggerKb, kProcessMallocTriggerKb); |
| triggers_.OnReceivedMemoryDump(profiled_pids_, true, |
| GlobalMemoryDump::MoveFrom(std::move(dump))); |
| EXPECT_TRUE(triggers_.WasReportTriggered()); |
| |
| // Ensure control trigger work on browser process, no matter memory usage. |
| triggers_.Reset(); |
| triggers_.SetControlTrigger(true); |
| dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); |
| PopulateMetrics(&dump, 1, ProcessType::BROWSER, 1, 1, 1); |
| triggers_.OnReceivedMemoryDump(profiled_pids_, true, |
| GlobalMemoryDump::MoveFrom(std::move(dump))); |
| EXPECT_TRUE(triggers_.WasReportTriggered()); |
| } |
| |
| TEST_F(BackgroundProfilingTriggersTest, HighWaterMark) { |
| GlobalMemoryDumpPtr dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| PopulateMetrics(&dump, 1, ProcessType::BROWSER, kProcessMallocTriggerKb, |
| kProcessMallocTriggerKb, kProcessMallocTriggerKb); |
| triggers_.OnReceivedMemoryDump(profiled_pids_, true, |
| GlobalMemoryDump::MoveFrom(std::move(dump))); |
| EXPECT_TRUE(triggers_.WasReportTriggered()); |
| triggers_.Reset(); |
| |
| // A small increase in memory should not trigger another report. |
| dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); |
| uint32_t small_increase = kProcessMallocTriggerKb + 10 * 1024; |
| PopulateMetrics(&dump, 1, ProcessType::BROWSER, small_increase, |
| small_increase, small_increase); |
| EXPECT_FALSE(triggers_.WasReportTriggered()); |
| |
| // But a large increase should trigger another report. |
| dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); |
| uint32_t large_increase = kProcessMallocTriggerKb + 1000 * 1024; |
| PopulateMetrics(&dump, 1, ProcessType::BROWSER, large_increase, |
| large_increase, large_increase); |
| EXPECT_FALSE(triggers_.WasReportTriggered()); |
| } |
| |
| // Non-profiled processes don't trigger. |
| TEST_F(BackgroundProfilingTriggersTest, OnlyProfiledProcessesTrigger) { |
| GlobalMemoryDumpPtr dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| PopulateMetrics(&dump, 101, ProcessType::BROWSER, kProcessMallocTriggerKb, |
| kProcessMallocTriggerKb, kProcessMallocTriggerKb); |
| triggers_.OnReceivedMemoryDump(profiled_pids_, true, |
| GlobalMemoryDump::MoveFrom(std::move(dump))); |
| EXPECT_FALSE(triggers_.WasReportTriggered()); |
| |
| dump = memory_instrumentation::mojom::GlobalMemoryDump::New(); |
| PopulateMetrics(&dump, 1, ProcessType::BROWSER, kProcessMallocTriggerKb, |
| kProcessMallocTriggerKb, kProcessMallocTriggerKb); |
| triggers_.OnReceivedMemoryDump(profiled_pids_, true, |
| GlobalMemoryDump::MoveFrom(std::move(dump))); |
| EXPECT_TRUE(triggers_.WasReportTriggered()); |
| } |
| |
| // Ensure IsAllowedToUpload() respects metrics collection settings. |
| TEST_F(BackgroundProfilingTriggersTest, IsAllowedToUpload_Metrics) { |
| EXPECT_TRUE(triggers_.IsAllowedToUpload()); |
| |
| is_metrics_enabled_ = false; |
| EXPECT_FALSE(triggers_.IsAllowedToUpload()); |
| } |
| |
| } // namespace heap_profiling |