1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CHROME_BROWSER_PERFORMANCE_MANAGER_OBSERVERS_ISOLATION_CONTEXT_METRICS_H_
6 #define CHROME_BROWSER_PERFORMANCE_MANAGER_OBSERVERS_ISOLATION_CONTEXT_METRICS_H_
7 
8 #include <unordered_map>
9 
10 #include "base/containers/small_map.h"
11 #include "base/macros.h"
12 #include "base/timer/timer.h"
13 #include "components/performance_manager/public/graph/frame_node.h"
14 #include "components/performance_manager/public/graph/graph.h"
15 #include "components/performance_manager/public/graph/process_node.h"
16 
17 namespace performance_manager {
18 
19 // A graph observer that tracks various metrics related to the isolation context
20 // of frames and pages:
21 //
22 // (1) How common it is for frames to be hosted in a same process that don't
23 //     actually need to be hosted together (they are not in the same site
24 //     instance and thus can't synchronously script each other). This is to
25 //     inform value of work on Blink Isolates.
26 // (2) How common it is for pages to be in browsing instances with other pages,
27 //     as opposed to in browsing instances on their own. This is for estimating
28 //     the impact of extending freezing logic to entire browsing instances.
29 class IsolationContextMetrics : public FrameNode::ObserverDefaultImpl,
30                                 public GraphOwned,
31                                 public ProcessNode::ObserverDefaultImpl {
32  public:
33   IsolationContextMetrics();
34   ~IsolationContextMetrics() override;
35 
36   // Starts the timer for periodic reporting.
37   void StartTimer();
38 
39  protected:
40   // Helper struct that implements storage for ProcessData.
41   friend struct IsolationContextMetricsProcessDataImpl;
42 
43   // Periodic reporting interval. This would ideally be triggered by UMA
44   // collection and not on its own timer, but UMA collection lives on the UI
45   // thread. This wants to be something on the same order of magnitude as UMA
46   // collection but not so fast as to cause pointless wakeups.
47   static constexpr base::TimeDelta kReportingInterval =
48       base::TimeDelta::FromMinutes(5);
49 
50   // This histogram records the cumulative amount of time a process spends
51   // hosting only frames from distinct site instances, versus hosting more than
52   // one frame from the same site instance. See ProcessDataState for details.
53   static const char kProcessDataByTimeHistogramName[];
54   // This histogram records the number of processes that ever only host frames
55   // from distinct site instances, versus those that ever host more than one
56   // frame from the same site instance. See ProcessDataState for details.
57   static const char kProcessDataByProcessHistogramName[];
58   // This histogram records the number of frames in a renderer over time.
59   static const char kFramesPerRendererByTimeHistogram[];
60   // This histogram records the number of site instances in a renderer over
61   // time.
62   static const char kSiteInstancesPerRendererByTimeHistogram[];
63 
64   // Tracks the number of distinct site instances being hosted per process.
65   struct ProcessData {
66     ProcessData();
67     ~ProcessData();
68 
69     // Factories/accessors for node attached data.
70     static ProcessData* Get(const ProcessNode* process_node);
71     static ProcessData* GetOrCreate(const ProcessNode* process_node);
72 
73     // A map between site instance ID and the number of frames with that site
74     // instance in the process. This is typically small for most processes, but
75     // can go to O(100s) for power users hence the use of small_map.
76     base::small_map<std::unordered_map<int32_t, int>> site_instance_frame_count;
77     // The number of frames in this process.
78     int frame_count = 0;
79     // The number of site instances with multiple frames in this process.
80     // Basically, this counts the number of entries in
81     // |site_instance_frame_count| that are > 1.
82     int multi_frame_site_instance_count = 0;
83     // Whether or not this process has *ever* hosted multiple frames.
84     bool has_hosted_multiple_frames = false;
85     // Whether or not this process has *ever* hosted multiple frames in the same
86     // site instance. This goes to true if |multi_frame_site_instance_count| is
87     // ever greater than 0.
88     bool has_hosted_multiple_frames_with_same_site_instance = false;
89     // The last time data related to this process was reported to the histogram.
90     // This happens on a timer or on state changes. This is initialized to the
91     // time of the struct creation.
92     base::TimeTicks last_reported;
93   };
94 
95   // A state that can be calculated from a ProcessData.
96   enum class ProcessDataState {
97     kUndefined = -1,  // This value is never reported, but used in logic.
98     kAllFramesHaveDistinctSiteInstances = 0,
99     kSomeFramesHaveSameSiteInstance = 1,
100     kOnlyOneFrameExists = 2,
101     // Must be maintained as the max value.
102     kMaxValue = kOnlyOneFrameExists
103   };
104 
105   // FrameNodeObserver implementation:
106   void OnFrameNodeAdded(const FrameNode* frame_node) override;
107   void OnBeforeFrameNodeRemoved(const FrameNode* frame_node) override;
108 
109   // GraphOwned implementation:
110   void OnPassedToGraph(Graph* graph) override;
111   void OnTakenFromGraph(Graph* graph) override;
112 
113   // ProcessNodeObserver implementation:
114   void OnBeforeProcessNodeRemoved(const ProcessNode* process_node) override;
115 
116   // (Un)registers the various node observer flavors of this object with the
117   // graph. These are invoked by OnPassedToGraph and OnTakenFromGraph, but
118   // hoisted to their own functions for testing.
119   void RegisterObservers(Graph* graph);
120   void UnregisterObservers(Graph* graph);
121 
122   // Returns the state associated with a ProcessData.
123   static ProcessDataState GetProcessDataState(const ProcessData* process_data);
124 
125   // Reports data associated with a ProcessData.
126   static void ReportProcessData(ProcessData* process_data,
127                                 ProcessDataState state,
128                                 base::TimeTicks now);
129 
130   // Reports all process data.
131   void ReportAllProcessData(base::TimeTicks now);
132 
133   // Adds or removes a frame from the relevant process data. The |delta| should
134   // be +/- 1.
135   void ChangeFrameCount(const FrameNode* frame_node, int delta);
136 
137   // This is virtual in order to provide a testing seam.
138   virtual void OnReportingTimerFired();
139 
140   // The graph to which this object belongs.
141   Graph* graph_ = nullptr;
142 
143   // Timer that is used to periodically flush metrics. This ensures that they
144   // are mostly up to date in the event of a catastrophic browser crash. We
145   // could do this on the same schedule as UMA itself by being a MetricsProvider
146   // but that is UI thread bound, and would require all sorts of hassles for us
147   // to build.
148   // TODO(chrisha): Migrate away if metrics team provides a convenient API.
149   // https://crbug.com/961468
150   base::RepeatingTimer reporting_timer_;
151 
152  private:
153   DISALLOW_COPY_AND_ASSIGN(IsolationContextMetrics);
154 };
155 
156 }  // namespace performance_manager
157 
158 #endif  // CHROME_BROWSER_PERFORMANCE_MANAGER_OBSERVERS_ISOLATION_CONTEXT_METRICS_H_
159