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
|
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_SESSIONS_SESSION_RESTORE_STATS_COLLECTOR_H_
#define CHROME_BROWSER_SESSIONS_SESSION_RESTORE_STATS_COLLECTOR_H_
#include <map>
#include "base/callback_list.h"
#include "base/time/tick_clock.h"
#include "chrome/browser/sessions/session_restore.h"
#include "chrome/browser/sessions/session_restore_delegate.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
namespace content {
class NavigationController;
class RenderWidgetHost;
}
// SessionRestoreStatsCollector observes SessionRestore events ands records UMA
// accordingly.
//
// A SessionRestoreStatsCollector is tied to an instance of a session restore,
// currently being instantianted and owned by the TabLoader. It has two main
// phases to its life:
//
// 1. The session restore is active and ongoing (the TabLoader is still
// scheduling tabs for loading). This phases ends when there are no
// non-deferred tabs left to be loaded. During this phases statistics are
// gathered in a structure before being emitted as UMA metrics at the end of
// this phase. At this point the TabLoader ceases to exist and destroys it's
// reference to the SessionRestoreStatsCollector.
// 2. If any tabs have been deferred the SessionRestoreStatsCollector continues
// tracking deferred tabs. This continues to observe the tabs to see which
// (if any) of the deferred tabs are subsequently forced to be loaded by the
// user. Since such tabs may exist until the end of the browsers life the
// statistics are emitted immediately, or risk being lost entirely. When
// there are no longer deferred tabs to track the
// SessionRestoreStatsCollector will destroy itself.
//
// TODO(chrisha): Many of these metrics don't make sense to collect in the
// presence of an unavailable network, or when tabs are closed during loading.
// Rethink the collection in these cases.
class SessionRestoreStatsCollector
: public content::NotificationObserver,
public base::RefCounted<SessionRestoreStatsCollector> {
public:
// Houses all of the statistics gathered by the SessionRestoreStatsCollector
// while the underlying TabLoader is active. These statistics are all reported
// at once via the reporting delegate.
struct TabLoaderStats {
// Constructor that initializes everything to zero.
TabLoaderStats();
// The number of tabs involved in all overlapping session restores being
// tracked by this SessionRestoreStatsCollector. This corresponds to the
// "SessionRestore.TabCount" metric and one bucket of the
// "SessionRestore.TabActions" histogram. If any tabs were deferred it also
// corresponds to the "SessionRestore.TabCount.MemoryPressure.Total"
// histogram.
size_t tab_count;
// The number of restored tabs that were deferred. Corresponds to the
// "SessionRestore.TabCount.MemoryPressure.Deferred" histogram.
size_t tabs_deferred;
// The number of tabs whose loading was automatically started because they
// are active or explicitly caused to be loaded by the TabLoader. This
// corresponds to one bucket of the "SessionRestore.TabActions" histogram
// and the "SessionRestore.TabCount.MemoryPressure.LoadStarted".
size_t tabs_load_started;
// The number of tabs loaded automatically because they are active, or
// explicitly caused to be loaded by the TabLoader. This corresponds to one
// bucket of the "SessionRestore.TabActions" histogram, and the
// "SessionRestore.TabCount.MemoryPressure.Loaded" histogram.
size_t tabs_loaded;
// The time elapsed between |restore_started| and reception of the first
// NOTIFICATION_LOAD_STOP event for any of the active tabs involved in the
// session restore. If this is zero it is because it has not been
// recorded (all visible tabs were closed before they finished loading, or
// the user switched to an already loaded tab before a visible session
// restore tab finished loading). Corresponds to
// "SessionRestore.ForegroundTabFirstLoaded" and its _XX variants.
base::TimeDelta foreground_tab_first_loaded;
// The time elapsed between |restore_started| and reception of the first
// NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE event for any of
// the tabs involved in the session restore. If this is zero it is because
// it has not been recorded (all visible tabs were closed or switched away
// from before they were painted). Corresponds to
// "SessionRestore.ForegroundTabFirstPaint3" and its _XX variants.
base::TimeDelta foreground_tab_first_paint;
// The time taken for all non-deferred tabs to be loaded. This corresponds
// to the "SessionRestore.AllTabsLoaded" metric and its _XX variants
// (vaguely named for historical reasons, as it predates the concept of
// deferred tabs).
base::TimeDelta non_deferred_tabs_loaded;
// The maximum number of tabs loading in parallel. This corresponds to the
// "SessionRestore.ParallelTabLoads" metric.
size_t parallel_tab_loads;
};
// The StatsReportingDelegate is responsible for delivering statistics
// reported by the SessionRestoreStatsCollector.
class StatsReportingDelegate;
// An implementation of StatsReportingDelegate for reporting via UMA.
class UmaStatsReportingDelegate;
// Constructs a SessionRestoreStatsCollector.
SessionRestoreStatsCollector(
const base::TimeTicks& restore_started,
scoped_ptr<StatsReportingDelegate> reporting_delegate);
// Adds new tabs to the list of tracked tabs.
void TrackTabs(const std::vector<SessionRestoreDelegate::RestoredTab>& tabs);
// Called to indicate that the loading of a tab has been deferred by session
// restore.
void DeferTab(content::NavigationController* tab);
// Exposed for unittesting.
const TabLoaderStats& tab_loader_stats() const { return tab_loader_stats_; }
private:
friend class TestSessionRestoreStatsCollector;
friend class base::RefCounted<SessionRestoreStatsCollector>;
enum TabLoadingState { TAB_IS_NOT_LOADING, TAB_IS_LOADING, TAB_IS_LOADED };
// State that is tracked for a tab while it is being observed.
struct TabState {
explicit TabState(content::NavigationController* controller);
// The NavigationController associated with the tab. This is the primary
// index for it and is never null.
content::NavigationController* controller;
// Set to true if the tab has been deferred by the TabLoader.
bool is_deferred;
// The current loading state of the tab.
TabLoadingState loading_state;
};
// Maps a NavigationController to its state. This is the primary map and
// physically houses the state.
using NavigationControllerMap =
std::map<content::NavigationController*, TabState>;
~SessionRestoreStatsCollector() override;
// NotificationObserver method. This is the workhorse of the class and drives
// all state transitions.
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override;
// Called when a tab is no longer tracked. This is called by the 'Observe'
// notification callback. Takes care of unregistering all observers and
// removing the tab from all internal data structures.
void RemoveTab(content::NavigationController* tab);
// Registers for relevant notifications for a tab and inserts the tab into
// to tabs_tracked_ map. Return a pointer to the newly created TabState.
TabState* RegisterForNotifications(content::NavigationController* tab);
// Returns the tab state, nullptr if not found.
TabState* GetTabState(content::NavigationController* tab);
TabState* GetTabState(content::RenderWidgetHost* tab);
// Marks a tab as loading.
void MarkTabAsLoading(TabState* tab_state);
// Checks to see if the SessionRestoreStatsCollector has finished collecting,
// and if so, releases the self reference to the shared pointer.
void ReleaseIfDoneTracking();
// Testing seam for configuring the tick clock in use.
void set_tick_clock(scoped_ptr<base::TickClock> tick_clock) {
tick_clock_ = tick_clock.Pass();
}
// Has ReleaseIfDoneTracking determined that there are no non-deferred tabs to
// track?
bool done_tracking_non_deferred_tabs_;
// Has the time for foreground tab load been recorded?
bool got_first_foreground_load_;
// Has the time for foreground tab paint been recorded?
bool got_first_paint_;
// The time the restore process started.
const base::TimeTicks restore_started_;
// List of tracked tabs, mapped to their TabState.
NavigationControllerMap tabs_tracked_;
// Counts the number of non-deferred tabs that the
// SessionRestoreStatsCollector is waiting to see load.
size_t waiting_for_load_tab_count_;
// Counts the current number of actively loading tabs.
size_t loading_tab_count_;
// Counts the current number of deferred tabs.
size_t deferred_tab_count_;
// Notification registrar.
content::NotificationRegistrar registrar_;
// Statistics gathered regarding the TabLoader.
TabLoaderStats tab_loader_stats_;
// The source of ticks used for taking timing information. This is
// configurable as a testing seam. Defaults to using base::DefaultTickClock,
// which in turn uses base::TimeTicks.
scoped_ptr<base::TickClock> tick_clock_;
// The reporting delegate used to report gathered statistics.
scoped_ptr<StatsReportingDelegate> reporting_delegate_;
// For keeping SessionRestoreStatsCollector alive while it is still working
// even if no TabLoader references it. The object only lives on if it still
// has deferred tabs remaining from an interrupted session restore.
scoped_refptr<SessionRestoreStatsCollector> this_retainer_;
DISALLOW_COPY_AND_ASSIGN(SessionRestoreStatsCollector);
};
// An abstract reporting delegate is used as a testing seam.
class SessionRestoreStatsCollector::StatsReportingDelegate {
public:
StatsReportingDelegate() {}
virtual ~StatsReportingDelegate() {}
// Called when TabLoader has completed its work.
virtual void ReportTabLoaderStats(const TabLoaderStats& tab_loader_stats) = 0;
// Called when a tab has been deferred.
virtual void ReportTabDeferred() = 0;
// Called when a deferred tab has been loaded.
virtual void ReportDeferredTabLoaded() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(StatsReportingDelegate);
};
// The default reporting delegate, which reports statistics via UMA.
class SessionRestoreStatsCollector::UmaStatsReportingDelegate
: public StatsReportingDelegate {
public:
UmaStatsReportingDelegate();
~UmaStatsReportingDelegate() override {}
// StatsReportingDelegate:
void ReportTabLoaderStats(const TabLoaderStats& tab_loader_stats) override;
void ReportTabDeferred() override;
void ReportDeferredTabLoaded() override;
private:
// Has ReportTabDeferred been called?
bool got_report_tab_deferred_;
DISALLOW_COPY_AND_ASSIGN(UmaStatsReportingDelegate);
};
#endif // CHROME_BROWSER_SESSIONS_SESSION_RESTORE_STATS_COLLECTOR_H_
|