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 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
|
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PERFORMANCE_MANAGER_FREEZING_FREEZING_POLICY_H_
#define COMPONENTS_PERFORMANCE_MANAGER_FREEZING_FREEZING_POLICY_H_
#include <deque>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string_view>
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/rand_util.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/performance_manager/freezing/freezer.h"
#include "components/performance_manager/public/decorators/page_live_state_decorator.h"
#include "components/performance_manager/public/freezing/cannot_freeze_reason.h"
#include "components/performance_manager/public/freezing/freezing.h"
#include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/graph/graph_registered.h"
#include "components/performance_manager/public/graph/node_data_describer.h"
#include "components/performance_manager/public/graph/page_node.h"
#include "components/performance_manager/public/resource_attribution/cpu_proportion_tracker.h"
#include "components/performance_manager/public/resource_attribution/queries.h"
#include "content/public/browser/browsing_instance_id.h"
#include "url/origin.h"
namespace performance_manager {
// Freezes sets of connected pages when no page in the set is opted-out and:
// - All pages have at least one freezing vote, or,
// - A group of same-origin and same-browsing-instance frames/workers associated
// with the set of connected pages used a lot of CPU in the background and
// Battery Saver is active.
//
// Pages are connected if they host frames from the same browsing instance. For
// example:
// - Page A hosts frames from browsing instance 1
// - Page B hosts frames from browsing instances 1 and 2
// - Page C hosts frames from browsing instance 2
// - Page D hosts frames from browsing instance 3
// The sets of connected pages are {A, B, C} and {D}.
//
// The `CannotFreezeReason` enum lists conditions that opt-out a page from
// freezing.
class FreezingPolicy : public PageNodeObserver,
public FrameNodeObserver,
public PageLiveStateObserver,
public resource_attribution::QueryResultObserver,
public GraphOwnedAndRegistered<FreezingPolicy>,
public NodeDataDescriberDefaultImpl {
public:
enum class FreezingType {
// Freezing via the voting system exposed to embedders.
kVoting,
// Freezing of CPU-intensive background tabs when Battery Saver is active.
kBatterySaver,
// Freezing of tabs which aren't in the set of most recently used, to
// scale to infinite tabs.
kInfiniteTabs,
};
using FreezingTypeSet = base::
EnumSet<FreezingType, FreezingType::kVoting, FreezingType::kInfiniteTabs>;
explicit FreezingPolicy(
std::unique_ptr<freezing::Discarder> discarder,
std::unique_ptr<freezing::OptOutChecker> opt_out_checker = nullptr);
~FreezingPolicy() override;
FreezingPolicy(const FreezingPolicy&) = delete;
FreezingPolicy& operator=(const FreezingPolicy&) = delete;
void SetFreezerForTesting(std::unique_ptr<Freezer> freezer) {
freezer_ = std::move(freezer);
}
// Invoked freezing on battery saver is enabled or disabled.
void ToggleFreezingOnBatterySaverMode(bool is_enabled);
// Add or remove a freezing vote for `page_node`. A browsing instance is
// frozen if all its pages have a freezing vote and none have a
// `CannotFreezeReason`.
void AddFreezeVote(PageNode* page_node);
void RemoveFreezeVote(PageNode* page_node);
// Returns details about whether a page can be frozen.
freezing::CanFreezeDetails GetCanFreezeDetails(const PageNode* page_node);
// Returns a set of `CannotFreezeReason`s applicable to `freezing_type`.
static freezing::CannotFreezeReasonSet CannotFreezeReasonsForType(
FreezingPolicy::FreezingType type);
private:
FRIEND_TEST_ALL_PREFIXES(FreezingPolicyBatterySaverTest,
RecordFreezingEligibilityUKMForPageStatic);
class CanFreezePerTypeTracker;
// Freezing related state for a page.
struct PageFreezingState;
// Freezing related state for a browsing instance.
struct BrowsingInstanceState {
BrowsingInstanceState();
~BrowsingInstanceState();
// Returns true if all pages in this browsing instance are frozen.
bool AllPagesFrozen() const;
// Pages that have frames in this browsing instance (typically only 1 page,
// but may contain an unbounded amount of pages connected via opener
// relationship).
base::flat_set<const PageNode*> pages;
// Highest CPU measurement for a group of same-origin frames/workers
// associated with this browsing instance, over the last measurement period.
// (1.0 = 100% of 1 core)
std::optional<double> highest_cpu_current_interval;
// Highest CPU measurement for a group of same-origin frames/workers
// associated within this browsing instance, over any past measurement
// period during which no `CannotFreezeReason` associated with
// `FreezingType::kBatterySaver` was applicable. (1.0 = 100% of 1 core)
double highest_cpu_without_battery_saver_cannot_freeze = 0.0;
// `CannotFreezeReason`s applicable to this browsing instance at any point
// since the last CPU measurement.
freezing::CannotFreezeReasonSet
cannot_freeze_reasons_since_last_cpu_measurement;
// First per-origin Private Memory Footprint measurement taken after this
// browsing instance became frozen. Empty if not all pages in this browsing
// instance are frozen.
base::flat_map<url::Origin, uint64_t> per_origin_pmf_after_freezing_kb;
};
// Returns pages connected to `page`, including `page` itself. See
// meta-comment above this class for a definition of "connected".
base::flat_set<raw_ptr<const PageNode>> GetConnectedPages(
const PageNode* page);
// Returns browsing instance id(s) for `page`.
base::flat_set<content::BrowsingInstanceId> GetBrowsingInstances(
const PageNode* page) const;
// Returns the `PageFreezingState` for `page`, creating it if necessary.
PageFreezingState& GetFreezingState(const PageNode* page_node) const;
// Update frozen state for all pages connected to `page`. Connected pages
// (including `page_node`) are added to `connected_pages_out` if not nullptr.
void UpdateFrozenState(
const PageNode* page_node,
base::TimeTicks now = base::TimeTicks::Now(),
base::flat_set<raw_ptr<const PageNode>>* connected_pages_out = nullptr);
// Helper to add or remove a `CannotFreezeReason` for `page_node`.
void OnCannotFreezeReasonChange(const PageNode* page_node,
bool add,
freezing::CannotFreezeReason reason);
// Returns the union of `CannotFreezeReason`s applicable to pages associated
// with `browsing_instance_state`.
freezing::CannotFreezeReasonSet GetCannotFreezeReasons(
const BrowsingInstanceState& browsing_instance_state);
// GraphOwned implementation:
void OnPassedToGraph(Graph* graph) override;
void OnTakenFromGraph(Graph* graph) override;
// PageNodeObserver implementation:
void OnPageNodeAdded(const PageNode* page_node) override;
void OnBeforePageNodeRemoved(const PageNode* page_node) override;
void OnTypeChanged(const PageNode* page_node,
PageType previous_type) override;
void OnIsVisibleChanged(const PageNode* page_node) override;
void OnIsAudibleChanged(const PageNode* page_node) override;
void OnPageLifecycleStateChanged(const PageNode* page_node) override;
void OnPageHasFreezingOriginTrialOptOutChanged(
const PageNode* page_node) override;
void OnPageIsHoldingWebLockChanged(const PageNode* page_node) override;
void OnPageIsHoldingBlockingIndexedDBLockChanged(
const PageNode* page_node) override;
void OnPageUsesWebRTCChanged(const PageNode* page_node) override;
void OnPageNotificationPermissionStatusChange(
const PageNode* page_node,
std::optional<blink::mojom::PermissionStatus> previous_status) override;
void OnMainFrameUrlChanged(const PageNode* page_node) override;
void OnLoadingStateChanged(const PageNode* page_node,
PageNode::LoadingState previous_state) override;
// FrameNodeObserver implementation:
void OnFrameNodeAdded(const FrameNode* frame_node) override;
void OnFrameNodeRemoved(
const FrameNode* frame_node,
const FrameNode* previous_parent_frame_node,
const PageNode* previous_page_node,
const ProcessNode* previous_process_node,
const FrameNode* previous_parent_or_outer_document_or_embedder) override;
void OnIsAudibleChanged(const FrameNode* frame_node) override;
// PageLiveStateObserver:
void OnIsConnectedToUSBDeviceChanged(const PageNode* page_node) override;
void OnIsConnectedToBluetoothDeviceChanged(
const PageNode* page_node) override;
void OnIsConnectedToHidDeviceChanged(const PageNode* page_node) override;
void OnIsConnectedToSerialPortChanged(const PageNode* page_node) override;
void OnIsCapturingVideoChanged(const PageNode* page_node) override;
void OnIsCapturingAudioChanged(const PageNode* page_node) override;
void OnIsBeingMirroredChanged(const PageNode* page_node) override;
void OnIsCapturingWindowChanged(const PageNode* page_node) override;
void OnIsCapturingDisplayChanged(const PageNode* page_node) override;
// NodeDataDescriber:
base::Value::Dict DescribePageNodeData(const PageNode* node) const override;
// resource_attribution::QueryResultObserver:
void OnResourceUsageUpdated(
const resource_attribution::QueryResultMap& results) override;
// Discards browsing instances for which memory usage has significantly
// increased since they were frozen upon receiving a memory measurement.
void DiscardFrozenPagesWithGrowingMemoryOnMemoryMeasurement(
const resource_attribution::QueryResultMap& results);
// Updates the frozen state of all browsing instances upon receiving a CPU
// measurement.
void UpdateFrozenStateOnCPUMeasurement(
const resource_attribution::QueryResultMap& results);
// Invoked by the OptOutChecker when the opt-out policy for
// `browser_context_id` changes.
void OnOptOutPolicyChanged(std::string_view browser_context_id);
// Removes the last page from the most recently used list if needed, to keep
// its size below the limit.
void MaybePopFromMostRecentlyUsedList();
// Checks that the size of the most recently used list respects the limit.
void CheckMostRecentlyUsedListSize();
// Starts a timer to manage periodic unfreezing of a tab frozen for
// `FreezingContext::kInfiniteTabs`. The timer is scheduled to invoke
// OnPeriodicUnfreezeTimer() at the next time when the tab must be unfrozen or
// re-frozen.
void StartPeriodicUnfreezeTimer(const PageNode* page_node,
base::TimeTicks now);
// Method invoked when when it's time to unfreeze or re-freeze a tab frozen
// for `FreezingContext::kInfiniteTabs`.
void OnPeriodicUnfreezeTimer(const PageNode* page);
// Records freezing eligibility UKM for all pages.
void RecordFreezingEligibilityUKM();
// Records freezing eligibility UKM for a page. Virtual for testing.
virtual void RecordFreezingEligibilityUKMForPage(
ukm::SourceId source_id,
double highest_cpu_current_interval,
double highest_cpu_without_battery_saver_cannot_freeze,
freezing::CannotFreezeReasonSet battery_saver_cannot_freeze_reasons);
// Records freezing eligibility UKM for a page. Static implementation.
//
// Note: The virtual method RecordFreezingEligibilityUKMForPage() and the
// static method RecordFreezingEligibilityUKMForPageStatic() are separate to
// facilitate testing the code that produces the inputs for the UKM event and
// the code that records the UKM event based on these inputs separately.
static void RecordFreezingEligibilityUKMForPageStatic(
ukm::SourceId source_id,
double highest_cpu_current_interval,
double highest_cpu_without_battery_saver_cannot_freeze,
freezing::CannotFreezeReasonSet battery_saver_cannot_freeze_reasons);
// Returns a random periodic unfreeze phase. Can be overridden in test to
// eliminate randomness.
virtual base::TimeTicks GenerateRandomPeriodicUnfreezePhase() const;
// Used to freeze pages.
std::unique_ptr<Freezer> freezer_;
// Used to discard pages.
std::unique_ptr<freezing::Discarder> discarder_;
// Used to check whether pages are opted out of freezing by the embedder.
std::unique_ptr<freezing::OptOutChecker> opt_out_checker_;
// State of each browsing instance.
std::map<content::BrowsingInstanceId, BrowsingInstanceState>
browsing_instance_states_;
// Whether Battery Saver is currently active.
bool is_battery_saver_active_ = false;
// Measures cumulative CPU usage per group of frames/workers that belong to
// the same [browsing instance, origin]. Engaged when the
// "CPUMeasurementInFreezingPolicy" feature is enabled.
std::optional<resource_attribution::ScopedResourceUsageQuery>
resource_usage_query_;
// Manages observation of `resource_usage_query_` by `this`.
resource_attribution::ScopedQueryObservation
resource_usage_query_observation_{this};
// Calculates the proportion of CPU used by a group of frames/workers that
// belong to the same [browsing instance, origin] over an interval, based on
// cumulative measurements from `resource_usage_query_`.
resource_attribution::CPUProportionTracker cpu_proportion_tracker_;
// Used to subsample the emission of UKM events.
base::MetricsSubSampler metrics_subsampler_;
// List of most recently used hidden tabs. A tab becomes the most recently
// used when it transitions from visible to hidden, or when it's created in a
// hidden state.
std::deque<raw_ptr<const PageNode>> most_recently_used_;
// Number of visible tabs.
int num_visible_tabs_ = 0;
base::WeakPtrFactory<FreezingPolicy> weak_factory_{this};
};
} // namespace performance_manager
#endif // COMPONENTS_PERFORMANCE_MANAGER_FREEZING_FREEZING_POLICY_H_
|