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
|
// 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 COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_CPU_MEASUREMENT_MONITOR_H_
#define COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_CPU_MEASUREMENT_MONITOR_H_
#include <map>
#include <optional>
#include <set>
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/values.h"
#include "components/performance_manager/public/graph/frame_node.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/graph/process_node.h"
#include "components/performance_manager/public/graph/worker_node.h"
#include "components/performance_manager/public/resource_attribution/cpu_measurement_delegate.h"
#include "components/performance_manager/public/resource_attribution/origin_in_browsing_instance_context.h"
#include "components/performance_manager/public/resource_attribution/query_results.h"
#include "components/performance_manager/public/resource_attribution/resource_contexts.h"
#include "components/performance_manager/resource_attribution/graph_change.h"
#include "components/performance_manager/resource_attribution/performance_manager_aliases.h"
#include "components/performance_manager/resource_attribution/query_params.h"
namespace resource_attribution {
class ScopedCPUTimeResult;
// Periodically collect CPU usage from process nodes.
//
// Note: this can't store measurements in NodeAttachedData because the final CPU
// measurement for a node may not arrive until after it's removed from the
// graph. So this is not a decorator as defined in
// components/performance_manager/README.md
class CPUMeasurementMonitor
: public FrameNodeObserver,
public PageNodeObserver,
public ProcessNodeObserver,
public WorkerNodeObserver,
public performance_manager::NodeDataDescriberDefaultImpl {
public:
CPUMeasurementMonitor();
~CPUMeasurementMonitor() override;
CPUMeasurementMonitor(const CPUMeasurementMonitor& other) = delete;
CPUMeasurementMonitor& operator=(const CPUMeasurementMonitor&) = delete;
// The given `factory` will be used to create a CPUMeasurementDelegate for
// each ProcessNode to be measured.
void SetDelegateFactoryForTesting(CPUMeasurementDelegate::Factory* factory);
// Starts monitoring CPU usage for all renderer ProcessNode's in `graph`.
void StartMonitoring(Graph* graph);
// Stops monitoring the graph.
void StopMonitoring();
// Returns true if currently monitoring.
bool IsMonitoring() const;
// Creates an empty list in `dead_context_results_` to store results from
// deleted nodes for `query_id`.
void RepeatingQueryStarted(internal::QueryId query_id);
// Removes all `dead_context_results_` that are waiting for `query_id`.
void RepeatingQueryStopped(internal::QueryId query_id);
// Returns true if `dead_context_results_` contains `query_id`.
bool IsTrackingQueryForTesting(internal::QueryId query_id) const;
// Returns the total number of ResourceContexts tracked in
// `dead_context_results_`. Contexts can be tracked more than once.
size_t GetDeadContextCountForTesting() const;
// Updates the CPU measurements for each ProcessNode being tracked and returns
// the estimated CPU usage of each frame and worker in those processes, and
// all other resource contexts containing them. Each QueryResults object will
// contain a CPUTimeResult. `query_id` is the ID of the
// ScopedResourceUsageQuery that made the request, or nullopt for a one-shot
// query.
//
// When consecutive measurements for the same `query_id` contain a
// CPUTimeResult for a given context, the cumulative CPU usage isn't reset
// between the two measurements, even if the context was transiently dead (a
// dead OriginInBrowsingInstanceContext can be revived). However, if a
// measurement doesn't contain a CPUTimeResult for a context, the next
// measurement may not include CPU usage that was previously reported for that
// context.
QueryResultMap UpdateAndGetCPUMeasurements(
std::optional<internal::QueryId> query_id = std::nullopt);
// Logs metrics on CPUMeasurementMonitor's memory usage to UMA.
void RecordMemoryMetrics();
// FrameNode::Observer:
void OnBeforeFrameNodeAdded(
const FrameNode* frame_node,
const FrameNode* pending_parent_frame_node,
const PageNode* pending_page_node,
const ProcessNode* pending_process_node,
const FrameNode* pending_parent_or_outer_document_or_embedder) override;
void OnBeforeFrameNodeRemoved(const FrameNode* frame_node) override;
void OnOriginChanged(
const FrameNode* frame_node,
const std::optional<url::Origin>& previous_value) override;
// PageNode::Observer:
void OnPageNodeAdded(const PageNode* page_node) override;
void OnBeforePageNodeRemoved(const PageNode* page_node) override;
// ProcessNode::Observer:
void OnProcessNodeAdded(const ProcessNode* process_node) override;
void OnProcessLifetimeChange(const ProcessNode* process_node) override;
void OnBeforeProcessNodeRemoved(const ProcessNode* process_node) override;
void OnPriorityChanged(const ProcessNode* process_node,
base::TaskPriority previous_value) override;
// WorkerNode::Observer:
void OnBeforeWorkerNodeAdded(
const WorkerNode* worker_node,
const ProcessNode* pending_process_node) override;
void OnBeforeWorkerNodeRemoved(const WorkerNode* worker_node) override;
void OnBeforeClientFrameAdded(const WorkerNode* worker_node,
const FrameNode* client_frame_node) override;
void OnBeforeClientFrameRemoved(const WorkerNode* worker_node,
const FrameNode* client_frame_node) override;
void OnBeforeClientWorkerAdded(const WorkerNode* worker_node,
const WorkerNode* client_worker_node) override;
void OnBeforeClientWorkerRemoved(
const WorkerNode* worker_node,
const WorkerNode* client_worker_node) override;
// NodeDataDescriber:
base::Value::Dict DescribeFrameNodeData(const FrameNode* node) const override;
base::Value::Dict DescribePageNodeData(const PageNode* node) const override;
base::Value::Dict DescribeProcessNodeData(
const ProcessNode* node) const override;
base::Value::Dict DescribeWorkerNodeData(
const WorkerNode* node) const override;
private:
friend class CPUMeasurementMonitorTest;
friend class ScopedCPUTimeResult;
// Creates a CPUMeasurementData to track the CPU usage of `process_node`.
void MonitorCPUUsage(const ProcessNode* process_node);
// Updates the CPU measurement for all ProcessNodes being monitored using
// MeasureAndDistributeCPUUsage(). Adds the estimated CPU usage of each frame
// and worker since the last time the process was measured to a
// ScopedCPUTimeResult.
void UpdateAllCPUMeasurements();
// Updates the CPU measurement for `process_node` using
// MeasureAndDistributeCPUUsage(). Adds the estimated CPU usage of each frame
// and worker since the last time the process was measured to a
// ScopedCPUTimeResult. `graph_change` is the event that triggered the
// measurement or NoGraphChange if it wasn't triggered due to a graph change.
void UpdateCPUMeasurements(const ProcessNode* process_node,
GraphChange graph_change = NoGraphChange());
// Retrieves the `CPUTimeResult` for `context`. If no result exists, returns a
// reference to a null pointer, which the caller must initialize.
scoped_refptr<ScopedCPUTimeResult>& GetResultPtr(
const ResourceContext& context);
// Adds the new measurements in `measurement_deltas` to ScopedCPUTimeResults.
// `graph_change` is the event that triggered the measurement or NoGraphChange
// if it wasn't triggered due to a graph change.
void ApplyMeasurementDeltas(
const std::map<ResourceContext, CPUTimeResult>& measurement_deltas,
GraphChange graph_change = NoGraphChange());
// Adds the measurement in `delta` to the result for `context`. The start time
// of `delta` must follow the end time of the result. Used for adding
// successive measurements of process, frame and worker contexts, so the
// algorithm in the metadata for the result should match that of `delta`.
// There may be gaps between deltas, such as if a process died and was
// restarted.
void ApplySequentialDelta(const ResourceContext& context,
const CPUTimeResult& delta);
// Adds the measurement in `delta` to the result for `context`. Delta may
// start before the result or end after it. Used for adding frame and worker
// measurements to page contexts, since the frames and workers can be added in
// any order.
void ApplyOverlappingDelta(const ResourceContext& context,
const CPUTimeResult& delta);
// Moves `result_ptr` into `dead_context_results_`. `result_ptr` is passed by
// move to ensure the caller drops its reference to the live result.
void SaveFinalMeasurement(scoped_refptr<ScopedCPUTimeResult>&& result_ptr);
// Returns all `OriginInBrowsingInstanceContext`s associated with live frame
// or worker contexts.
std::set<OriginInBrowsingInstanceContext>
GetLiveOriginInBrowsingInstanceContexts();
// Measures the CPU usage of `process_node`, calculates the change in CPU
// usage over the period (`last_measurement_time_` ... now], and allocates
// the results to frames and workers in the process using
// SplitResourceAmongFramesAndWorkers(). The new CPU usage in this
// measurement is added to `measurement_deltas`.
static void MeasureAndDistributeCPUUsage(
const ProcessNode* process_node,
GraphChange graph_change,
std::map<ResourceContext, CPUTimeResult>& measurement_deltas);
SEQUENCE_CHECKER(sequence_checker_);
// A map from `OriginInBrowsingInstanceContext`s to the estimated CPU usage of
// each, updated whenever UpdateCPUMeasurements() is called. An
// `OriginInBrowsingInstanceContext` is considered live as long as it's in
// this map. Results for other context types are held in NodeInlineData in
// live PM nodes.
std::map<OriginInBrowsingInstanceContext, scoped_refptr<ScopedCPUTimeResult>>
origin_results_ GUARDED_BY_CONTEXT(sequence_checker_);
// A map of non-owning pointers to all `ScopedCPUTimeResult` instances
// associated with `OriginInBrowsingInstanceContext`.
std::map<OriginInBrowsingInstanceContext, raw_ptr<ScopedCPUTimeResult>>
weak_origin_results_ GUARDED_BY_CONTEXT(sequence_checker_);
// CPU time results for dead contexts retained by ScopedResourceUsageQuery.
//
// TODO(crbug.com/333112603): Not every ScopedResourceUsageQuery wants results
// for each context. Currently all results are reported to every query, and
// QueryScheduler filters out the ones the query doesn't need. For efficiency
// CPUMeasurementMonitor should only report the results the query needs.
struct DeadContextResults {
DeadContextResults();
~DeadContextResults();
// Move-only.
DeadContextResults(DeadContextResults&&);
DeadContextResults& operator=(DeadContextResults&&);
DeadContextResults(const DeadContextResults&) = delete;
DeadContextResults& operator=(const DeadContextResults&) = delete;
// Results for dead contexts to report in the next measurement for this
// query.
//
// When a context dies, its result is added to the `to_report` set of all
// live queries.
std::set<scoped_refptr<ScopedCPUTimeResult>> to_report;
// Results kept alive until the next measurement for this query, in case the
// associated context is revived. If a context is revived while this set has
// a reference to its last result, GetResultPtr() will retrieve it instead
// of creating a new one.
//
// When a measurement for a query contains a result for a dead
// `OriginInBrowsingInstanceContext`, the result is kept in the `kept_alive`
// set of the query until the next measurement. This ensures that when
// consecutive measurements for a query contain a result for a given
// context, the cumulative CPU usage isn't reset between the two
// measurements, even if the context was transiently dead.
std::set<scoped_refptr<ScopedCPUTimeResult>> kept_alive;
};
std::map<internal::QueryId, DeadContextResults> dead_context_results_
GUARDED_BY_CONTEXT(sequence_checker_);
// Factory that creates CPUMeasurementDelegate objects for each ProcessNode
// being measured.
raw_ptr<CPUMeasurementDelegate::Factory> delegate_factory_
GUARDED_BY_CONTEXT(sequence_checker_);
// Graph being monitored. This will be only be set if StartMonitoring() was
// called and StopMonitoring() was not.
raw_ptr<Graph> graph_ GUARDED_BY_CONTEXT(sequence_checker_) = nullptr;
};
// Ref-counted class which holds a `ResourceContext` and `CPUTimeResult`.
//
// If the context is an `OriginInBrowsingInstanceContext`, the constructor and
// destructor maintain a non-owning pointer to `this` in `weak_origin_results_`,
// allowing GetOriginInBrowsingInstanceResultPtr() to reuse a result that is
// still referenced by `dead_context_results_`.
class ScopedCPUTimeResult : public base::RefCounted<ScopedCPUTimeResult> {
public:
ScopedCPUTimeResult(CPUMeasurementMonitor* monitor,
const ResourceContext& context,
const CPUTimeResult& result);
ScopedCPUTimeResult(const ScopedCPUTimeResult&) = delete;
ScopedCPUTimeResult& operator=(const ScopedCPUTimeResult&) = delete;
CPUTimeResult& result() { return result_; }
const ResourceContext& context() const { return context_; }
size_t EstimateMemoryUsage() const;
private:
friend class base::RefCounted<ScopedCPUTimeResult>;
~ScopedCPUTimeResult();
const raw_ptr<CPUMeasurementMonitor> monitor_;
const ResourceContext context_;
CPUTimeResult result_;
};
} // namespace resource_attribution
#endif // COMPONENTS_PERFORMANCE_MANAGER_RESOURCE_ATTRIBUTION_CPU_MEASUREMENT_MONITOR_H_
|