1 // Copyright 2015 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.
5 #include "chrome/browser/sessions/session_restore_stats_collector.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/stringprintf.h"
11 #include "content/public/browser/notification_service.h"
12 #include "content/public/browser/notification_types.h"
13 #include "content/public/browser/render_widget_host_view.h"
14 #include "content/public/browser/web_contents.h"
16 using content::NavigationController
;
17 using content::RenderWidgetHost
;
18 using content::WebContents
;
21 void SessionRestoreStatsCollector::TrackTabs(
22 const std::vector
<SessionRestoreDelegate::RestoredTab
>& tabs
,
23 const base::TimeTicks
& restore_started
,
25 if (!shared_collector_
)
26 shared_collector_
= new SessionRestoreStatsCollector(restore_started
);
28 shared_collector_
->AddTabs(tabs
, active_only
);
31 SessionRestoreStatsCollector::SessionRestoreStatsCollector(
32 const base::TimeTicks
& restore_started
)
33 : got_first_foreground_load_(false),
34 got_first_paint_(false),
35 restore_started_(restore_started
),
37 max_parallel_tab_loads_(0) {
38 this_retainer_
= this;
40 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE
,
41 content::NotificationService::AllSources());
44 SessionRestoreStatsCollector::~SessionRestoreStatsCollector() {
45 DCHECK((got_first_paint_
|| render_widget_hosts_to_paint_
.empty()) &&
46 tabs_tracked_
.empty() && render_widget_hosts_loading_
.empty());
47 DCHECK(shared_collector_
== this);
48 shared_collector_
= nullptr;
51 void SessionRestoreStatsCollector::Observe(
53 const content::NotificationSource
& source
,
54 const content::NotificationDetails
& details
) {
56 case content::NOTIFICATION_LOAD_START
: {
57 // Add this render_widget_host to the set of those we're waiting for
58 // paints on. We want to only record stats for paints that occur after
59 // a load has finished.
60 NavigationController
* tab
=
61 content::Source
<NavigationController
>(source
).ptr();
62 RenderWidgetHost
* render_widget_host
= GetRenderWidgetHost(tab
);
63 DCHECK(render_widget_host
);
64 render_widget_hosts_loading_
.insert(render_widget_host
);
67 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED
: {
68 WebContents
* web_contents
= content::Source
<WebContents
>(source
).ptr();
69 RemoveTab(&web_contents
->GetController());
72 case content::NOTIFICATION_LOAD_STOP
: {
73 NavigationController
* tab
=
74 content::Source
<NavigationController
>(source
).ptr();
75 RenderWidgetHost
* render_widget_host
= GetRenderWidgetHost(tab
);
76 render_widget_hosts_to_paint_
.insert(render_widget_host
);
78 if (!got_first_foreground_load_
&& render_widget_host
&&
79 render_widget_host
->GetView() &&
80 render_widget_host
->GetView()->IsShowing()) {
81 got_first_foreground_load_
= true;
82 base::TimeDelta time_to_load
=
83 base::TimeTicks::Now() - restore_started_
;
84 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstLoaded",
86 base::TimeDelta::FromMilliseconds(10),
87 base::TimeDelta::FromSeconds(100), 100);
88 // Record a time for the number of tabs, to help track down
90 std::string time_for_count
= base::StringPrintf(
91 "SessionRestore.ForegroundTabFirstLoaded_%d", tab_count_
);
92 base::HistogramBase
* counter_for_count
=
93 base::Histogram::FactoryTimeGet(
94 time_for_count
, base::TimeDelta::FromMilliseconds(10),
95 base::TimeDelta::FromSeconds(100), 100,
96 base::Histogram::kUmaTargetedHistogramFlag
);
97 counter_for_count
->AddTime(time_to_load
);
101 case content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE
: {
102 RenderWidgetHost
* render_widget_host
=
103 content::Source
<RenderWidgetHost
>(source
).ptr();
104 if (!got_first_paint_
&& render_widget_host
->GetView() &&
105 render_widget_host
->GetView()->IsShowing()) {
106 if (render_widget_hosts_to_paint_
.find(render_widget_host
) !=
107 render_widget_hosts_to_paint_
.end()) {
108 // Got a paint for one of our renderers, so record time.
109 got_first_paint_
= true;
110 base::TimeDelta time_to_paint
=
111 base::TimeTicks::Now() - restore_started_
;
112 // TODO(danduong): to remove this with 467680, to make sure we
113 // don't forget to clean this up.
114 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstPaint",
116 base::TimeDelta::FromMilliseconds(10),
117 base::TimeDelta::FromSeconds(100), 100);
118 // Record a time for the number of tabs, to help track down
120 std::string time_for_count
= base::StringPrintf(
121 "SessionRestore.ForegroundTabFirstPaint_%d", tab_count_
);
122 base::HistogramBase
* counter_for_count
=
123 base::Histogram::FactoryTimeGet(
124 time_for_count
, base::TimeDelta::FromMilliseconds(10),
125 base::TimeDelta::FromSeconds(100), 100,
126 base::Histogram::kUmaTargetedHistogramFlag
);
127 counter_for_count
->AddTime(time_to_paint
);
128 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstPaint2",
130 base::TimeDelta::FromMilliseconds(100),
131 base::TimeDelta::FromMinutes(16), 50);
132 // Record a time for the number of tabs, to help track down
134 std::string time_for_count2
= base::StringPrintf(
135 "SessionRestore.ForegroundTabFirstPaint2_%d", tab_count_
);
136 base::HistogramBase
* counter_for_count2
=
137 base::Histogram::FactoryTimeGet(
138 time_for_count2
, base::TimeDelta::FromMilliseconds(100),
139 base::TimeDelta::FromMinutes(16), 50,
140 base::Histogram::kUmaTargetedHistogramFlag
);
141 counter_for_count2
->AddTime(time_to_paint
);
142 } else if (render_widget_hosts_loading_
.find(render_widget_host
) ==
143 render_widget_hosts_loading_
.end()) {
144 // If this is a host for a tab we're not loading some other tab
145 // has rendered and there's no point tracking the time. This could
146 // happen because the user opened a different tab or restored tabs
147 // to an already existing browser and an existing tab painted.
148 got_first_paint_
= true;
154 NOTREACHED() << "Unknown notification received:" << type
;
158 // Check if we are done and if so, reset |this_retainer_| as the collector no
159 // longer needs to stay alive.
160 if ((got_first_paint_
|| render_widget_hosts_to_paint_
.empty()) &&
161 tabs_tracked_
.empty() && render_widget_hosts_loading_
.empty())
162 this_retainer_
= nullptr;
165 void SessionRestoreStatsCollector::AddTabs(
166 const std::vector
<SessionRestoreDelegate::RestoredTab
>& tabs
,
168 tab_count_
+= tabs
.size();
169 for (auto& tab
: tabs
) {
170 // If we are only restoring active tabs and the tab is not active, nothing
172 if (active_only
&& !tab
.is_active
)
174 RegisterForNotifications(&tab
.contents
->GetController());
176 RenderWidgetHost
* render_widget_host
=
177 GetRenderWidgetHost(&tab
.contents
->GetController());
178 render_widget_hosts_loading_
.insert(render_widget_host
);
183 void SessionRestoreStatsCollector::RemoveTab(NavigationController
* tab
) {
184 registrar_
.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
185 content::Source
<WebContents
>(tab
->GetWebContents()));
186 registrar_
.Remove(this, content::NOTIFICATION_LOAD_STOP
,
187 content::Source
<NavigationController
>(tab
));
188 registrar_
.Remove(this, content::NOTIFICATION_LOAD_START
,
189 content::Source
<NavigationController
>(tab
));
190 if (render_widget_hosts_loading_
.size() > max_parallel_tab_loads_
)
191 max_parallel_tab_loads_
= render_widget_hosts_loading_
.size();
192 RenderWidgetHost
* render_widget_host
= GetRenderWidgetHost(tab
);
193 render_widget_hosts_loading_
.erase(render_widget_host
);
194 tabs_tracked_
.erase(tab
);
196 // If there are no more tabs loading or being tracked, restore is done and
197 // record the time. Note that we are not yet finished, as we might still be
198 // waiting for our first paint, which can happen after all tabs are done
200 // TODO(georgesak): review behaviour of ForegroundTabFirstPaint.
201 if (tabs_tracked_
.empty() && render_widget_hosts_loading_
.empty()) {
202 base::TimeDelta time_to_load
= base::TimeTicks::Now() - restore_started_
;
203 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.AllTabsLoaded", time_to_load
,
204 base::TimeDelta::FromMilliseconds(10),
205 base::TimeDelta::FromSeconds(100), 100);
206 // Record a time for the number of tabs, to help track down contention.
207 std::string time_for_count
=
208 base::StringPrintf("SessionRestore.AllTabsLoaded_%d", tab_count_
);
209 base::HistogramBase
* counter_for_count
= base::Histogram::FactoryTimeGet(
210 time_for_count
, base::TimeDelta::FromMilliseconds(10),
211 base::TimeDelta::FromSeconds(100), 100,
212 base::Histogram::kUmaTargetedHistogramFlag
);
213 counter_for_count
->AddTime(time_to_load
);
215 UMA_HISTOGRAM_COUNTS_100("SessionRestore.ParallelTabLoads",
216 max_parallel_tab_loads_
);
220 void SessionRestoreStatsCollector::RegisterForNotifications(
221 NavigationController
* tab
) {
222 registrar_
.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED
,
223 content::Source
<WebContents
>(tab
->GetWebContents()));
224 registrar_
.Add(this, content::NOTIFICATION_LOAD_STOP
,
225 content::Source
<NavigationController
>(tab
));
226 registrar_
.Add(this, content::NOTIFICATION_LOAD_START
,
227 content::Source
<NavigationController
>(tab
));
228 tabs_tracked_
.insert(tab
);
231 RenderWidgetHost
* SessionRestoreStatsCollector::GetRenderWidgetHost(
232 NavigationController
* tab
) {
233 WebContents
* web_contents
= tab
->GetWebContents();
235 content::RenderWidgetHostView
* render_widget_host_view
=
236 web_contents
->GetRenderWidgetHostView();
237 if (render_widget_host_view
)
238 return render_widget_host_view
->GetRenderWidgetHost();
244 SessionRestoreStatsCollector
* SessionRestoreStatsCollector::shared_collector_
=