1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/performance_manager/policies/termination_target_policy.h"
#include <algorithm>
#include <cstdint>
#include "chrome/browser/performance_manager/policies/discard_eligibility_policy.h"
#include "components/performance_manager/public/graph/frame_node.h"
#include "content/public/common/process_type.h"
namespace performance_manager {
// Private memory footprint threshold above which a process is considered large.
constexpr uint64_t kLargeProcessFootprintThresholdKb = 100 * 1024; // 100 MiB
namespace {
struct PotentialTerminationTarget {
raw_ptr<const ProcessNode> process = nullptr;
uint64_t private_footprint_kb = 0;
bool can_discard = true;
// Last time a page in the process was visible. `base::TimeTicks::Max()` if
// currently visible. `base::TimeTicks()` if there is no visible page in the
// process.
base::TimeTicks last_visible_time;
// Returns true if `this` is a better termination target than `other`.
bool operator<(const PotentialTerminationTarget& other) const {
if (is_large() != other.is_large()) {
return is_large();
}
if (can_discard != other.can_discard) {
return can_discard;
}
return last_visible_time < other.last_visible_time;
}
bool is_large() const {
return private_footprint_kb > kLargeProcessFootprintThresholdKb;
}
};
} // namespace
TerminationTargetPolicy::TerminationTargetPolicy(
std::unique_ptr<TerminationTargetSetter> termination_target_setter)
: termination_target_setter_(std::move(termination_target_setter)) {}
TerminationTargetPolicy::~TerminationTargetPolicy() {
if (current_termination_target_) {
termination_target_setter_->SetTerminationTarget(nullptr);
}
}
void TerminationTargetPolicy::OnPassedToGraph(Graph* graph) {
graph->AddSystemNodeObserver(this);
graph->AddPageNodeObserver(this);
graph->AddProcessNodeObserver(this);
}
void TerminationTargetPolicy::OnTakenFromGraph(Graph* graph) {
graph->RemoveProcessNodeObserver(this);
graph->RemovePageNodeObserver(this);
graph->RemoveSystemNodeObserver(this);
}
void TerminationTargetPolicy::OnIsVisibleChanged(const PageNode* page_node) {
UpdateTerminationTarget();
}
void TerminationTargetPolicy::OnPageNodeRemoved(const PageNode* page_node) {
UpdateTerminationTarget();
}
void TerminationTargetPolicy::OnBeforeProcessNodeRemoved(
const ProcessNode* process_node) {
if (process_node == current_termination_target_) {
UpdateTerminationTarget(/*process_being_deleted=*/process_node);
}
}
void TerminationTargetPolicy::OnProcessLifetimeChange(
const ProcessNode* process_node) {
if (process_node == current_termination_target_) {
UpdateTerminationTarget(process_node);
}
}
void TerminationTargetPolicy::OnProcessMemoryMetricsAvailable(
const SystemNode* system_node) {
UpdateTerminationTarget();
}
void TerminationTargetPolicy::UpdateTerminationTarget(
const ProcessNode* process_being_deleted) {
std::vector<PotentialTerminationTarget> targets;
auto process_nodes = GetOwningGraph()->GetAllProcessNodes();
targets.reserve(process_nodes.size());
policies::DiscardEligibilityPolicy* eligibility_policy =
policies::DiscardEligibilityPolicy::GetFromGraph(GetOwningGraph());
for (const ProcessNode* process_node : process_nodes) {
if (process_node->GetProcessType() != content::PROCESS_TYPE_RENDERER) {
continue;
}
if (process_node == process_being_deleted) {
continue;
}
if (!process_node->GetProcess().IsValid()) {
continue;
}
targets.emplace_back();
PotentialTerminationTarget& target = targets.back();
target.process = process_node;
target.private_footprint_kb = process_node->GetPrivateFootprintKb();
for (const FrameNode* frame_node : process_node->GetFrameNodes()) {
const PageNode* page_node = frame_node->GetPageNode();
if (eligibility_policy->CanDiscard(
page_node,
policies::DiscardEligibilityPolicy::DiscardReason::URGENT,
/*minimum_time_in_background=*/base::TimeDelta()) !=
policies::CanDiscardResult::kEligible) {
target.can_discard = false;
}
if (page_node->IsVisible()) {
target.last_visible_time = base::TimeTicks::Max();
} else {
target.last_visible_time = std::max(
target.last_visible_time, page_node->GetLastVisibilityChangeTime());
}
}
}
auto min_element_it = std::min_element(targets.begin(), targets.end());
if (min_element_it != targets.end()) {
if (min_element_it->process != current_termination_target_) {
termination_target_setter_->SetTerminationTarget(min_element_it->process);
current_termination_target_ = min_element_it->process;
}
} else if (current_termination_target_) {
termination_target_setter_->SetTerminationTarget(nullptr);
current_termination_target_ = nullptr;
}
}
} // namespace performance_manager
|