From 9f5e41017d769b5c999559a4cbea3fa68904544c Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Wed, 25 Jan 2023 01:54:28 +0000 Subject: [PATCH] Make each render process provide its private memory footprint for browser process. Because of the "hidepid=2" mount option for /proc on Android, browser process cannot open /proc/{render process pid}/maps and status, i.e. no such file or directory. So add PrivateMemoryFootprintProvider, which uses MemoryUsageMonitor, to third_party/blink/renderer/controller and provides the calculated private memory footprint for browser process by using render_host.mojom. Bug: 1393283 Change-Id: Ibb00262afc2ed0552e3f0c2ec5fb7449878b93c9 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4124620 Reviewed-by: Arthur Sonzogni Reviewed-by: Daniel Cheng Reviewed-by: Kinuko Yasuda Commit-Queue: Takashi Sakamoto Cr-Commit-Position: refs/heads/main@{#1096563} --- ..._level_memory_pressure_signal_generator.cc | 19 +++++-- .../renderer_host/render_process_host_impl.cc | 7 +++ .../renderer_host/render_process_host_impl.h | 13 +++++ content/common/renderer_host.mojom | 18 +++++++ content/public/test/render_view_test.cc | 5 ++ content/renderer/render_thread_impl.cc | 7 +++ content/renderer/render_thread_impl.h | 5 ++ .../renderer/renderer_blink_platform_impl.cc | 9 ++++ .../renderer/renderer_blink_platform_impl.h | 4 ++ third_party/blink/public/platform/platform.h | 6 +++ .../blink/renderer/controller/BUILD.gn | 2 + .../renderer/controller/blink_initializer.cc | 7 +++ .../private_memory_footprint_provider.cc | 44 ++++++++++++++++ .../private_memory_footprint_provider.h | 52 +++++++++++++++++++ 14 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 third_party/blink/renderer/controller/private_memory_footprint_provider.cc create mode 100644 third_party/blink/renderer/controller/private_memory_footprint_provider.h diff --git a/content/browser/memory_pressure/user_level_memory_pressure_signal_generator.cc b/content/browser/memory_pressure/user_level_memory_pressure_signal_generator.cc index 7a58b8967645f..f820ccb48a27e 100644 --- a/content/browser/memory_pressure/user_level_memory_pressure_signal_generator.cc +++ b/content/browser/memory_pressure/user_level_memory_pressure_signal_generator.cc @@ -3,7 +3,6 @@ // found in the LICENSE file. #include "content/browser/memory_pressure/user_level_memory_pressure_signal_generator.h" -#include "base/task/sequenced_task_runner.h" #if BUILDFLAG(IS_ANDROID) #include @@ -199,6 +198,13 @@ uint64_t UserLevelMemoryPressureSignalGenerator:: add_process_private_footprint(base::Process::Current()); // Measure private memory footprints of GPU process and Utility processes. + // Since GPU process uses the same user id as the browser process (android), + // the browser process can measure the GPU's private memory footprint. + // However, regarding the utility processes, their user ids are different. + // So because of the hidepid=2 mount option, the browser process cannot + // measure the private memory footprints of the utility processes. + // TODO(crbug.com/1393283): measure the private memory footprints of + // the utility processes correctly. for (content::BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) { add_process_private_footprint(iter.GetData().GetProcess()); } @@ -224,7 +230,14 @@ uint64_t UserLevelMemoryPressureSignalGenerator:: continue; } - total_private_footprint_bytes += GetPrivateFootprint(process).value_or(0); + // Because of the "hidepid=2" mount option for /proc on Android, + // the browser process cannot open /proc/{render process pid}/maps and + // status, i.e. no such file or directory. So each renderer process + // provides its private memory footprint for the browser process and + // the browser process gets the (cached) value via RenderProcessHostImpl. + total_private_footprint_bytes += + static_cast(host) + ->GetPrivateMemoryFootprint(); } return total_private_footprint_bytes; } @@ -267,7 +280,7 @@ void UserLevelMemoryPressureSignalGenerator::NotifyMemoryPressure() { namespace { -// TODO(crbug.com/1393282): if this feature is approved, refactor the duplicate +// TODO(crbug.com/1393283): if this feature is approved, refactor the duplicate // code under //third_party/blink/renderer/controller. If not approved, // remove the code as soon as possible. absl::optional CalculateProcessMemoryFootprint( diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 0009a87d3063f..0f4588f2d8fd8 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -4888,6 +4888,13 @@ void RenderProcessHostImpl::RecordUserMetricsAction(const std::string& action) { base::RecordComputedAction(action); } +#if BUILDFLAG(IS_ANDROID) +void RenderProcessHostImpl::SetPrivateMemoryFootprint( + uint64_t private_memory_footprint_bytes) { + private_memory_footprint_bytes_ = private_memory_footprint_bytes; +} +#endif + void RenderProcessHostImpl::UpdateProcessPriorityInputs() { int32_t new_visible_widgets_count = 0; unsigned int new_frame_depth = kMaxFrameDepthForPriority; diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h index 153f3e02cf800..1a17076187072 100644 --- a/content/browser/renderer_host/render_process_host_impl.h +++ b/content/browser/renderer_host/render_process_host_impl.h @@ -744,6 +744,10 @@ class CONTENT_EXPORT RenderProcessHostImpl // Notifies the renderer process of memory pressure level. void NotifyMemoryPressureToRenderer( base::MemoryPressureListener::MemoryPressureLevel level); + + uint64_t GetPrivateMemoryFootprint() const { + return private_memory_footprint_bytes_; + } #endif protected: @@ -826,6 +830,10 @@ class CONTENT_EXPORT RenderProcessHostImpl BrowserHistogramCallback callback) override; void SuddenTerminationChanged(bool enabled) override; void RecordUserMetricsAction(const std::string& action) override; +#if BUILDFLAG(IS_ANDROID) + void SetPrivateMemoryFootprint( + uint64_t private_memory_footprint_bytes) override; +#endif void CreateEmbeddedFrameSinkProvider( mojo::PendingReceiver receiver); @@ -1237,6 +1245,11 @@ class CONTENT_EXPORT RenderProcessHostImpl scoped_refptr pepper_renderer_connection_; #endif +#if BUILDFLAG(IS_ANDROID) + // The private memory footprint of the render process. + uint64_t private_memory_footprint_bytes_ = 0u; +#endif + // IOThreadHostImpl owns some IO-thread state associated with this // RenderProcessHostImpl. This is mainly to allow various IPCs from the // renderer to be handled on the IO thread without a hop to the UI thread. diff --git a/content/common/renderer_host.mojom b/content/common/renderer_host.mojom index 0311dc64231e9..a908b856f5f8f 100644 --- a/content/common/renderer_host.mojom +++ b/content/common/renderer_host.mojom @@ -21,4 +21,22 @@ interface RendererHost { // Sends a string to be recorded by UserMetrics. RecordUserMetricsAction(string action); + + // Provides this render process' private memory footprint to the browser. + // Used by the UserLevelMemoryPressureSignalGenerator. + // + // In case of compromised renderer process: + // - Reporting small values make it less likely for a memory pressure signal + // to be sent. This isn't a big deal, it only increase the changes the OS to + // start killing the renderer process. + // - Reporting large values makes Chrome to dispatch memory pressure + // signal needlessly. There is a 10 minutes wait in between them. It is not + // particularly worrying. + // + // TODO(https://crbug.com/1393283): if the + // UserLevelMemoryPressureSignalGenerator is approved, investigate a + // possibility to gather data inside the browser process, via e.g. + // getProcessMemoryInfo. + [EnableIf=is_android] + SetPrivateMemoryFootprint(uint64 private_memory_footprint_bytes); }; diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc index f0021eb9f253b..628b6952d970c 100644 --- a/content/public/test/render_view_test.cc +++ b/content/public/test/render_view_test.cc @@ -228,6 +228,11 @@ class RendererBlinkPlatformImplTestOverrideImpl // Get rid of the dependency to the sandbox, which is not available in // RenderViewTest. blink::WebSandboxSupport* GetSandboxSupport() override { return nullptr; } + +#if BUILDFLAG(IS_ANDROID) + void SetPrivateMemoryFootprint( + uint64_t private_memory_footprint_bytes) override {} +#endif }; class RenderFrameWasShownWaiter : public RenderFrameObserver { diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index 59b0db6fe97c0..2fb8a5e9b9f18 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -1878,4 +1878,11 @@ std::unique_ptr RenderThreadImpl::CreateMediaCodecFactory( #endif } +#if BUILDFLAG(IS_ANDROID) +void RenderThreadImpl::SetPrivateMemoryFootprint( + uint64_t private_memory_footprint_bytes) { + GetRendererHost()->SetPrivateMemoryFootprint(private_memory_footprint_bytes); +} +#endif + } // namespace content diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h index d4cbe57cc7835..4c9e160e336b3 100644 --- a/content/renderer/render_thread_impl.h +++ b/content/renderer/render_thread_impl.h @@ -386,6 +386,11 @@ class CONTENT_EXPORT RenderThreadImpl run_loop_start_time_ = run_loop_start_time; } +#if BUILDFLAG(IS_ANDROID) + // Provide private memory footprint for browser process. + void SetPrivateMemoryFootprint(uint64_t private_memory_footprint_bytes); +#endif + private: friend class RenderThreadImplBrowserTest; friend class AgentSchedulingGroup; diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc index b8e31584d4365..a0811daead7bc 100644 --- a/content/renderer/renderer_blink_platform_impl.cc +++ b/content/renderer/renderer_blink_platform_impl.cc @@ -1054,4 +1054,13 @@ RendererBlinkPlatformImpl::VideoFrameCompositorTaskRunner() { return compositor_task_runner; } +#if BUILDFLAG(IS_ANDROID) +void RendererBlinkPlatformImpl::SetPrivateMemoryFootprint( + uint64_t private_memory_footprint_bytes) { + auto* render_thread = RenderThreadImpl::current(); + CHECK(render_thread); + render_thread->SetPrivateMemoryFootprint(private_memory_footprint_bytes); +} +#endif + } // namespace content diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h index cd7d964c2a488..f9b61ff32e3e7 100644 --- a/content/renderer/renderer_blink_platform_impl.h +++ b/content/renderer/renderer_blink_platform_impl.h @@ -230,6 +230,10 @@ class CONTENT_EXPORT RendererBlinkPlatformImpl : public BlinkPlatformImpl { override; scoped_refptr VideoFrameCompositorTaskRunner() override; +#if BUILDFLAG(IS_ANDROID) + void SetPrivateMemoryFootprint( + uint64_t private_memory_footprint_bytes) override; +#endif // Tells this platform that the renderer is locked to a site (i.e., a scheme // plus eTLD+1, such as https://google.com), or to a more specific origin. diff --git a/third_party/blink/public/platform/platform.h b/third_party/blink/public/platform/platform.h index 81103f13c8000..b2de9ae25b7ac 100644 --- a/third_party/blink/public/platform/platform.h +++ b/third_party/blink/public/platform/platform.h @@ -793,6 +793,12 @@ class BLINK_PLATFORM_EXPORT Platform { return attribution_reporting::mojom::OsSupport::kDisabled; } +#if BUILDFLAG(IS_ANDROID) + // User Level Memory Pressure Signal Generator ------------------ + virtual void SetPrivateMemoryFootprint( + uint64_t private_memory_footprint_bytes) {} +#endif + private: static void InitializeMainThreadCommon( Platform* platform, diff --git a/third_party/blink/renderer/controller/BUILD.gn b/third_party/blink/renderer/controller/BUILD.gn index bb3dc58a360cf..2cf3784f871e2 100644 --- a/third_party/blink/renderer/controller/BUILD.gn +++ b/third_party/blink/renderer/controller/BUILD.gn @@ -74,6 +74,8 @@ component("controller") { "memory_usage_monitor_posix.h", "oom_intervention_impl.cc", "oom_intervention_impl.h", + "private_memory_footprint_provider.cc", + "private_memory_footprint_provider.h", ] public_deps = [ "//third_party/blink/public/mojom:memory_usage_monitor_linux_mojo_bindings_blink" ] } diff --git a/third_party/blink/renderer/controller/blink_initializer.cc b/third_party/blink/renderer/controller/blink_initializer.cc index 52094e5c351db..51eed8765dfd5 100644 --- a/third_party/blink/renderer/controller/blink_initializer.cc +++ b/third_party/blink/renderer/controller/blink_initializer.cc @@ -70,6 +70,7 @@ #if BUILDFLAG(IS_ANDROID) #include "third_party/blink/renderer/controller/crash_memory_metrics_reporter_impl.h" #include "third_party/blink/renderer/controller/oom_intervention_impl.h" +#include "third_party/blink/renderer/controller/private_memory_footprint_provider.h" #endif #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) @@ -248,6 +249,12 @@ void BlinkInitializer::RegisterMemoryWatchers() { // navigation. HighestPmfReporter::Initialize(main_thread_task_runner); #endif + +#if BUILDFLAG(IS_ANDROID) + // Initialize PrivateMemoryFootprintProvider to start providing the value + // for the browser process. + PrivateMemoryFootprintProvider::Initialize(main_thread_task_runner); +#endif } void BlinkInitializer::InitLocalFrame(LocalFrame& frame) const { diff --git a/third_party/blink/renderer/controller/private_memory_footprint_provider.cc b/third_party/blink/renderer/controller/private_memory_footprint_provider.cc new file mode 100644 index 0000000000000..7c200476fa4cc --- /dev/null +++ b/third_party/blink/renderer/controller/private_memory_footprint_provider.cc @@ -0,0 +1,44 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/controller/private_memory_footprint_provider.h" + +#include "base/task/task_runner.h" +#include "base/time/default_tick_clock.h" +#include "third_party/blink/public/platform/platform.h" + +namespace blink { + +void PrivateMemoryFootprintProvider::Initialize( + scoped_refptr task_runner) { + DEFINE_STATIC_LOCAL(PrivateMemoryFootprintProvider, provider, + (std::move(task_runner))); + (void)provider; +} + +PrivateMemoryFootprintProvider::PrivateMemoryFootprintProvider( + scoped_refptr task_runner) + : PrivateMemoryFootprintProvider(std::move(task_runner), + base::DefaultTickClock::GetInstance()) {} + +PrivateMemoryFootprintProvider::PrivateMemoryFootprintProvider( + scoped_refptr task_runner, + const base::TickClock* clock) + : task_runner_(std::move(task_runner)), clock_(clock) { + MemoryUsageMonitor::Instance().AddObserver(this); +} + +void PrivateMemoryFootprintProvider::OnMemoryPing(MemoryUsage usage) { + DCHECK(IsMainThread()); + SetPrivateMemoryFootprint( + static_cast(usage.private_footprint_bytes)); +} + +void PrivateMemoryFootprintProvider::SetPrivateMemoryFootprint( + uint64_t private_memory_footprint_bytes) { + Platform::Current()->SetPrivateMemoryFootprint( + private_memory_footprint_bytes); +} + +} // namespace blink diff --git a/third_party/blink/renderer/controller/private_memory_footprint_provider.h b/third_party/blink/renderer/controller/private_memory_footprint_provider.h new file mode 100644 index 0000000000000..c956af5a7d6b4 --- /dev/null +++ b/third_party/blink/renderer/controller/private_memory_footprint_provider.h @@ -0,0 +1,52 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CONTROLLER_PRIVATE_MEMORY_FOOTPRINT_PROVIDER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CONTROLLER_PRIVATE_MEMORY_FOOTPRINT_PROVIDER_H_ + +#include "base/time/time.h" +#include "third_party/blink/renderer/controller/controller_export.h" +#include "third_party/blink/renderer/controller/memory_usage_monitor.h" +#include "third_party/blink/renderer/platform/timer.h" +#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" + +namespace base { +class SingleThreadTaskRunner; +class TickClock; +} // namespace base + +namespace blink { + +// Provides this renderer process' private memory footprint for browser process. +class CONTROLLER_EXPORT PrivateMemoryFootprintProvider + : public MemoryUsageMonitor::Observer { + USING_FAST_MALLOC(PrivateMemoryFootprintProvider); + + public: + // Initializes the shared instance. Has no effect if called more than once. + static void Initialize( + scoped_refptr task_runner); + + private: + explicit PrivateMemoryFootprintProvider( + scoped_refptr task_runner); + + PrivateMemoryFootprintProvider( + scoped_refptr task_runner_for_testing, + const base::TickClock* clock); + + // MemoryUsageMonitor::Observer: + void OnMemoryPing(MemoryUsage) override; + + // Use mojom to provide private memory footprint for this RendererHost in + // the browser process. + void SetPrivateMemoryFootprint(uint64_t private_memory_footprint_bytes); + + scoped_refptr task_runner_; + const base::TickClock* clock_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CONTROLLER_PMF_REPORTER_H_