Skip to content

Commit

Permalink
Report full page process list in ReportPageProcessesPolicy
Browse files Browse the repository at this point in the history
The page process list is sent to the resource manager daemon
(resourced). The page process list is used to estimate the memory usage
of the background and protected processes. Based on the Chrome memory
usage, resourced determines when to release memory from Chrome or VMs or
container. When there are a lot of background processes in Ash Chrome,
resourced avoids killing perceptible apps in VMs or container.

If the last page node event is within the miminum report interval, start
a timer to ensure the page process list is reported after the last page
node event.

Deprecate crosapi ReportBackgroundProcesses.

Bug: b/304679315
Change-Id: Id84c12ba5ea933470b5dffda3885083e98bb349e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4951554
Reviewed-by: Patrick Monette <pmonette@chromium.org>
Reviewed-by: Brian Geffon <bgeffon@chromium.org>
Reviewed-by: Erik Chen <erikchen@chromium.org>
Commit-Queue: Vovo Yang <vovoy@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1212560}
  • Loading branch information
Vovo Yang authored and Chromium LUCI CQ committed Oct 20, 2023
1 parent 60f22f4 commit 237386e
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 79 deletions.
21 changes: 17 additions & 4 deletions chrome/browser/ash/crosapi/resource_manager_ash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,26 @@ void ResourceManagerAsh::AddMemoryPressureObserver(
observers_.Add(std::move(remote));
}

void ResourceManagerAsh::ReportBackgroundProcesses(
void ResourceManagerAsh::DEPRECATED_ReportBackgroundProcesses(
const std::vector<int32_t>& pids) {
NOTREACHED();
}

void ResourceManagerAsh::ReportPageProcesses(
std::vector<mojom::PageProcessPtr> page_processes) {
ash::ResourcedClient* client = ash::ResourcedClient::Get();
if (client) {
client->ReportBackgroundProcesses(ash::ResourcedClient::Component::kLacros,
pids);
if (!client) {
return;
}

std::vector<ash::ResourcedClient::Process> processes;
for (auto& page_process : page_processes) {
processes.emplace_back(page_process->pid, page_process->host_protected_page,
page_process->host_visible_page);
}

client->ReportBrowserProcesses(ash::ResourcedClient::Component::kLacros,
processes);
}

} // namespace crosapi
5 changes: 4 additions & 1 deletion chrome/browser/ash/crosapi/resource_manager_ash.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ class ResourceManagerAsh : public mojom::ResourceManager,
// crosapi::mojom::ResourceManager:
void AddMemoryPressureObserver(
mojo::PendingRemote<mojom::MemoryPressureObserver> observer) override;
void ReportBackgroundProcesses(const std::vector<int32_t>& pids) override;
void DEPRECATED_ReportBackgroundProcesses(
const std::vector<int32_t>& pids) override;
void ReportPageProcesses(
std::vector<mojom::PageProcessPtr> processes) override;

private:
// Support any number of connections.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ class PageNodeSortProxy {
is_visible_(is_visible),
is_protected_(is_protected),
last_visible_(last_visible) {}

const PageNode* page_node() const { return page_node_; }
bool is_protected() const { return is_protected_; }
bool is_visible() const { return is_visible_; }

// Returns true if the rhs is more important.
bool operator<(const PageNodeSortProxy& rhs) const {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

#include "chrome/browser/performance_manager/policies/report_page_processes_policy.h"

#include <algorithm>
#include <set>

#include "base/functional/bind.h"
#include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/graph_operations.h"
#include "components/performance_manager/public/graph/process_node.h"
#include "content/public/browser/browser_thread.h"

Expand Down Expand Up @@ -36,14 +39,22 @@ namespace {
// https://source.chromium.org/chromiumos/chromiumos/codesearch/+/main:src/platform2/resourced/src/memory.rs;drc=a76ccbdab134a54a6b3314a6b78722b9b3fab6d1;l=506
constexpr base::TimeDelta kReportProcessesMinimalInterval = base::Seconds(3);

void ReportBackgroundProcessesOnUIThread(
const std::vector<base::ProcessId>& pids) {
void ReportPageProcessesOnUIThread(
const std::vector<ReportPageProcessesPolicy::PageProcess>& page_processes) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
ash::ResourcedClient* client = ash::ResourcedClient::Get();
if (client) {
client->ReportBackgroundProcesses(ash::ResourcedClient::Component::kAsh,
pids);
if (!client) {
return;
}

std::vector<ash::ResourcedClient::Process> processes;
for (const auto& page_process : page_processes) {
processes.emplace_back(page_process.pid, page_process.host_protected_page,
page_process.host_visible_page);
}

client->ReportBrowserProcesses(ash::ResourcedClient::Component::kAsh,
processes);
#endif // BUILDFLAG(IS_CHROMEOS_ASH)

#if BUILDFLAG(IS_CHROMEOS_LACROS)
Expand All @@ -59,20 +70,37 @@ void ReportBackgroundProcessesOnUIThread(
service->GetInterfaceVersion<crosapi::mojom::ResourceManager>();
if (resource_manager_version <
int{crosapi::mojom::ResourceManager::MethodMinVersions::
kReportBackgroundProcessesMinVersion}) {
kReportPageProcessesMinVersion}) {
LOG(WARNING) << "Resource Manager version " << resource_manager_version
<< " does not support reporting background processes.";
<< " does not support reporting page processes.";
return;
}

service->GetRemote<crosapi::mojom::ResourceManager>()
->ReportBackgroundProcesses(pids);
std::vector<crosapi::mojom::PageProcessPtr> processes;

for (const auto& page_process : page_processes) {
crosapi::mojom::PageProcessPtr process = crosapi::mojom::PageProcess::New();
process->pid = page_process.pid;
process->host_protected_page = page_process.host_protected_page;
process->host_visible_page = page_process.host_visible_page;
processes.push_back(std::move(process));
}

service->GetRemote<crosapi::mojom::ResourceManager>()->ReportPageProcesses(
std::move(processes));
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
}

} // namespace

ReportPageProcessesPolicy::ReportPageProcessesPolicy() = default;
ReportPageProcessesPolicy::ReportPageProcessesPolicy()
: delayed_report_timer_(
FROM_HERE,
kReportProcessesMinimalInterval,
base::BindRepeating(
&ReportPageProcessesPolicy::HandlePageNodeEventsDelayed,
base::Unretained(this))) {}

ReportPageProcessesPolicy::~ReportPageProcessesPolicy() = default;

void ReportPageProcessesPolicy::OnPassedToGraph(Graph* graph) {
Expand Down Expand Up @@ -106,21 +134,35 @@ void ReportPageProcessesPolicy::OnTypeChanged(const PageNode* page_node,
}

void ReportPageProcessesPolicy::HandlePageNodeEventsThrottled() {
base::TimeTicks now = base::TimeTicks::Now();
if (delayed_report_timer_.IsRunning()) {
// This event happened too soon after the last report. The updated process
// list will be sent after the minimal interval period.
has_delayed_events_ = true;
return;
}

HandlePageNodeEvents();

if (now - last_report_ > kReportProcessesMinimalInterval) {
last_report_ = now;
// Start the throttling timer. Any event that happens while it is running will
// not cause a report until the minimum interval has passed.
delayed_report_timer_.Reset();
}

void ReportPageProcessesPolicy::HandlePageNodeEventsDelayed() {
if (has_delayed_events_) {
HandlePageNodeEvents();
}
}

void ReportPageProcessesPolicy::HandlePageNodeEvents() {
has_delayed_events_ = false;

PageDiscardingHelper* discarding_helper =
PageDiscardingHelper::GetFromGraph(graph_);

std::vector<const PageNode*> page_nodes = graph_->GetAllPageNodes();

std::vector<PageNodeSortProxy> background_candidates;
std::vector<PageNodeSortProxy> candidates;

for (const auto* page_node : page_nodes) {
PageDiscardingHelper::CanDiscardResult can_discard_result =
Expand All @@ -131,48 +173,52 @@ void ReportPageProcessesPolicy::HandlePageNodeEvents() {
bool is_protected = (can_discard_result ==
PageDiscardingHelper::CanDiscardResult::kProtected);
bool is_visible = page_node->IsVisible();
if (!is_marked && !is_protected) {
background_candidates.emplace_back(
page_node, is_marked, is_visible, is_protected,
page_node->GetTimeSinceLastVisibilityChange());
}
candidates.emplace_back(page_node, is_marked, is_visible, is_protected,
page_node->GetTimeSinceLastVisibilityChange());
}

std::vector<base::ProcessId> background_pids =
GetUniquePids(background_candidates);
ReportBackgroundProcesses(std::move(background_pids));
// Sorts with descending importance.
std::sort(candidates.begin(), candidates.end(),
[](const PageNodeSortProxy& lhs, const PageNodeSortProxy& rhs) {
return rhs < lhs;
});

ListPageProcesses(candidates);
}

std::vector<base::ProcessId> ReportPageProcessesPolicy::GetUniquePids(
void ReportPageProcessesPolicy::ListPageProcesses(
const std::vector<PageNodeSortProxy>& candidates) {
std::vector<base::ProcessId> pids;

std::set<base::ProcessId> pid_set;

for (const auto& candidate : candidates) {
const FrameNode* main_frame_node =
candidate.page_node()->GetMainFrameNode();
if (!main_frame_node) {
continue;
std::vector<PageProcess> page_processes;

for (auto candidate : candidates) {
base::flat_set<const ProcessNode*> processes =
GraphOperations::GetAssociatedProcessNodes(candidate.page_node());
for (auto* process : processes) {
base::ProcessId pid = process->GetProcessId();
if (pid == base::kNullProcessId) {
continue;
}
if (!pid_set.insert(pid).second) {
// If the process is already in a more important page node, skip. For
// example, if both a non-protected page node and a protected page node
// contain a process ID, the page process with this process ID is marked
// as protected.
continue;
}
page_processes.emplace_back(pid, candidate.is_protected(),
candidate.is_visible());
}
base::ProcessId pid = main_frame_node->GetProcessNode()->GetProcessId();

if (pid == base::kNullProcessId || pid_set.find(pid) != pid_set.end()) {
continue;
}

pids.push_back(pid);
pid_set.insert(pid);
}

return pids;
ReportPageProcesses(std::move(page_processes));
}

void ReportPageProcessesPolicy::ReportBackgroundProcesses(
std::vector<base::ProcessId> pids) {
void ReportPageProcessesPolicy::ReportPageProcesses(
std::vector<PageProcess> processes) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&ReportBackgroundProcessesOnUIThread, std::move(pids)));
base::BindOnce(&ReportPageProcessesOnUIThread, std::move(processes)));
}

} // namespace performance_manager::policies
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "base/process/process_handle.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/performance_manager/policies/page_discarding_helper.h"
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/graph/page_node.h"
Expand All @@ -25,6 +26,18 @@ namespace performance_manager::policies {
class ReportPageProcessesPolicy : public GraphOwned,
public PageNode::ObserverDefaultImpl {
public:
struct PageProcess {
PageProcess(base::ProcessId pid,
bool host_protected_page,
bool host_visible_page)
: pid(pid),
host_protected_page(host_protected_page),
host_visible_page(host_visible_page) {}
base::ProcessId pid;
bool host_protected_page;
bool host_visible_page;
};

ReportPageProcessesPolicy();
~ReportPageProcessesPolicy() override;
ReportPageProcessesPolicy(const ReportPageProcessesPolicy& other) = delete;
Expand All @@ -46,23 +59,30 @@ class ReportPageProcessesPolicy : public GraphOwned,
// These members are protected for testing.
void HandlePageNodeEvents();

// Reports the background process list to the resource manager daemon
// (resourced). Based on the background process list, resourced determines
// when to release memory from Chrome or VMs or containers.
// Reports the page process list to the resource manager daemon (resourced).
// Based on the process list, resourced determines when to release memory
// from Chrome or VMs or containers.
//
// It's virtual for testing.
virtual void ReportBackgroundProcesses(std::vector<base::ProcessId> pids);
virtual void ReportPageProcesses(std::vector<PageProcess> processes);

private:
// ReportPageProcessesPolicy is active when receiving page node events.
void HandlePageNodeEventsThrottled();

// Returns a vector of pids of the main frame renderer process of the
// |candidates| (the child frame renderer processes are ignored). The order of
// the pids is corresponding to the order of the |candidates|.
std::vector<base::ProcessId> GetUniquePids(
const std::vector<PageNodeSortProxy>& candidates);
// Called by |delayed_report_timer_|.
void HandlePageNodeEventsDelayed();

// List the processes associated with the page nodes in |candidates|.
// The input |candidates| should be sorted with descending importance.
void ListPageProcesses(const std::vector<PageNodeSortProxy>& candidates);

// Indicates if there is an unhandled page node event.
bool has_delayed_events_;

base::TimeTicks last_report_ = base::TimeTicks::Now();
// Delay the reporting if it's less than the minimum interval since last
// reporting.
base::RetainingOneShotTimer delayed_report_timer_;

raw_ptr<Graph> graph_ = nullptr;

Expand Down

0 comments on commit 237386e

Please sign in to comment.