OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/sessions/session_restore_stats_collector.h" | 5 #include "chrome/browser/sessions/session_restore_stats_collector.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
10 #include "base/strings/stringprintf.h" | 10 #include "base/strings/stringprintf.h" |
| 11 #include "base/time/default_tick_clock.h" |
11 #include "content/public/browser/notification_service.h" | 12 #include "content/public/browser/notification_service.h" |
12 #include "content/public/browser/notification_types.h" | 13 #include "content/public/browser/notification_types.h" |
13 #include "content/public/browser/render_widget_host_view.h" | 14 #include "content/public/browser/render_widget_host_view.h" |
14 #include "content/public/browser/web_contents.h" | 15 #include "content/public/browser/web_contents.h" |
15 | 16 |
| 17 namespace { |
| 18 |
16 using content::NavigationController; | 19 using content::NavigationController; |
17 using content::RenderWidgetHost; | 20 using content::RenderWidgetHost; |
| 21 using content::Source; |
18 using content::WebContents; | 22 using content::WebContents; |
19 | 23 |
20 // static | 24 // The enumeration values stored in the "SessionRestore.Actions" histogram. |
21 void SessionRestoreStatsCollector::TrackTabs( | 25 enum SessionRestoreActionsUma { |
22 const std::vector<SessionRestoreDelegate::RestoredTab>& tabs, | 26 // Counts the total number of session restores that have occurred. |
23 const base::TimeTicks& restore_started) { | 27 SESSION_RESTORE_ACTIONS_UMA_INITIATED = 0, |
24 if (!shared_collector_) | 28 // Counts the number of session restores that have seen deferred tab loadings |
25 shared_collector_ = new SessionRestoreStatsCollector(restore_started); | 29 // for whatever reason (almost certainly due to memory pressure). |
| 30 SESSION_RESTORE_ACTIONS_UMA_DEFERRED_TABS = 1, |
| 31 // The size of this enum. Must be the last entry. |
| 32 SESSION_RESTORE_ACTIONS_UMA_MAX, |
| 33 }; |
26 | 34 |
27 shared_collector_->AddTabs(tabs); | 35 // Emits a SessionRestore.Actions UMA event. |
| 36 void EmitUmaSessionRestoreActionEvent(SessionRestoreActionsUma action) { |
| 37 UMA_HISTOGRAM_ENUMERATION("SessionRestore.Actions", action, |
| 38 SESSION_RESTORE_ACTIONS_UMA_MAX); |
28 } | 39 } |
29 | 40 |
30 // static | 41 // The enumeration of values stored in the "SessionRestore.TabActions" |
31 void SessionRestoreStatsCollector::TrackActiveTabs( | 42 // histogram. |
32 const std::vector<SessionRestoreDelegate::RestoredTab>& tabs, | 43 enum SessionRestoreTabActionsUma { |
33 const base::TimeTicks& restore_started) { | 44 // Incremented for each tab created in a session restore. |
34 if (!shared_collector_) | 45 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_CREATED = 0, |
35 shared_collector_ = new SessionRestoreStatsCollector(restore_started); | 46 // Incremented for each tab that session restore decides not to load. |
| 47 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADING_DEFERRED = 1, |
| 48 // Incremented for each tab that is successfully loaded. |
| 49 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADED = 2, |
| 50 // Incremented for each session-restore-deferred tab that is subsequently |
| 51 // loaded. |
| 52 SESSION_RESTORE_TAB_ACTIONS_UMA_DEFERRED_TAB_LOADED = 3, |
| 53 // The size of this enum. Must be the last entry. |
| 54 SESSION_RESTORE_TAB_ACTIONS_UMA_MAX, |
| 55 }; |
36 | 56 |
37 std::vector<SessionRestoreDelegate::RestoredTab> active_tabs; | 57 // Emits a SessionRestore.TabActions UMA event. |
38 for (auto tab : tabs) { | 58 void EmitUmaSessionRestoreTabActionEvent(SessionRestoreTabActionsUma action) { |
39 if (tab.is_active()) | 59 UMA_HISTOGRAM_ENUMERATION("SessionRestore.TabActions", action, |
40 active_tabs.push_back(tab); | 60 SESSION_RESTORE_TAB_ACTIONS_UMA_MAX); |
41 } | 61 } |
42 shared_collector_->AddTabs(active_tabs); | 62 |
| 63 } // namespace |
| 64 |
| 65 SessionRestoreStatsCollector::TabLoaderStats::TabLoaderStats() |
| 66 : tab_count(0u), tabs_loaded(0u), parallel_tab_loads(0u) { |
| 67 } |
| 68 |
| 69 SessionRestoreStatsCollector::TabState::TabState( |
| 70 NavigationController* controller) |
| 71 : controller(controller), |
| 72 render_widget_host(nullptr), |
| 73 is_deferred(false), |
| 74 loading_state(TAB_IS_NOT_LOADING) { |
43 } | 75 } |
44 | 76 |
45 SessionRestoreStatsCollector::SessionRestoreStatsCollector( | 77 SessionRestoreStatsCollector::SessionRestoreStatsCollector( |
46 const base::TimeTicks& restore_started) | 78 const base::TimeTicks& restore_started, |
47 : got_first_foreground_load_(false), | 79 scoped_ptr<StatsReportingDelegate> reporting_delegate) |
| 80 : done_tracking_non_deferred_tabs_(false), |
| 81 got_first_foreground_load_(false), |
48 got_first_paint_(false), | 82 got_first_paint_(false), |
49 restore_started_(restore_started), | 83 restore_started_(restore_started), |
50 tab_count_(0), | 84 waiting_for_load_tab_count_(0u), |
51 max_parallel_tab_loads_(0) { | 85 loading_tab_count_(0u), |
| 86 deferred_tab_count_(0u), |
| 87 tick_clock_(new base::DefaultTickClock()), |
| 88 reporting_delegate_(reporting_delegate.Pass()) { |
52 this_retainer_ = this; | 89 this_retainer_ = this; |
53 registrar_.Add( | |
54 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, | |
55 content::NotificationService::AllSources()); | |
56 } | 90 } |
57 | 91 |
58 SessionRestoreStatsCollector::~SessionRestoreStatsCollector() { | 92 SessionRestoreStatsCollector::~SessionRestoreStatsCollector() { |
59 DCHECK((got_first_paint_ || render_widget_hosts_to_paint_.empty()) && | 93 } |
60 tabs_tracked_.empty() && render_widget_hosts_loading_.empty()); | 94 |
61 DCHECK(shared_collector_ == this); | 95 void SessionRestoreStatsCollector::TrackTabs( |
62 shared_collector_ = nullptr; | 96 const std::vector<SessionRestoreDelegate::RestoredTab>& tabs) { |
| 97 DCHECK(!done_tracking_non_deferred_tabs_); |
| 98 |
| 99 // If this is the first call to TrackTabs then start observing events. |
| 100 if (tab_loader_stats_.tab_count == 0) { |
| 101 registrar_.Add( |
| 102 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, |
| 103 content::NotificationService::AllSources()); |
| 104 } |
| 105 |
| 106 tab_loader_stats_.tab_count += tabs.size(); |
| 107 waiting_for_load_tab_count_ += tabs.size(); |
| 108 for (const auto& tab : tabs) { |
| 109 TabState* tab_state = |
| 110 RegisterForNotifications(&tab.contents()->GetController()); |
| 111 // Active tabs have already started loading. |
| 112 if (tab.is_active()) |
| 113 MarkTabAsLoading(tab_state); |
| 114 } |
| 115 } |
| 116 |
| 117 void SessionRestoreStatsCollector::DeferTab(NavigationController* tab) { |
| 118 TabState* tab_state = GetTabState(tab); |
| 119 |
| 120 // If the tab is no longer being tracked it has already finished loading. |
| 121 // This can occur if the user forces the tab to load before the entire session |
| 122 // restore is over, and the TabLoader then decides it would defer loading of |
| 123 // that tab. |
| 124 if (!tab_state) |
| 125 return; |
| 126 |
| 127 // Mark this tab as deferred, if its still being tracked. A tab should not be |
| 128 // marked as deferred twice. |
| 129 DCHECK(!tab_state->is_deferred); |
| 130 tab_state->is_deferred = true; |
| 131 ++deferred_tab_count_; |
| 132 |
| 133 // A tab that didn't start loading before it was deferred is not to be |
| 134 // actively monitored for loading. |
| 135 if (tab_state->loading_state == TAB_IS_NOT_LOADING) { |
| 136 DCHECK_LT(0u, waiting_for_load_tab_count_); |
| 137 if (--waiting_for_load_tab_count_ == 0) |
| 138 ReleaseIfDoneTracking(); |
| 139 } |
| 140 |
| 141 reporting_delegate_->ReportTabDeferred(); |
63 } | 142 } |
64 | 143 |
65 void SessionRestoreStatsCollector::Observe( | 144 void SessionRestoreStatsCollector::Observe( |
66 int type, | 145 int type, |
67 const content::NotificationSource& source, | 146 const content::NotificationSource& source, |
68 const content::NotificationDetails& details) { | 147 const content::NotificationDetails& details) { |
69 switch (type) { | 148 switch (type) { |
70 case content::NOTIFICATION_LOAD_START: { | 149 case content::NOTIFICATION_LOAD_START: { |
71 // Add this render_widget_host to the set of those we're waiting for | 150 // This occurs when a tab has started to load. This can be because of |
72 // paints on. We want to only record stats for paints that occur after | 151 // the tab loader (only for non-deferred tabs) or because the user clicked |
73 // a load has finished. | 152 // on the tab. |
74 NavigationController* tab = | 153 NavigationController* tab = Source<NavigationController>(source).ptr(); |
75 content::Source<NavigationController>(source).ptr(); | 154 TabState* tab_state = GetTabState(tab); |
76 RenderWidgetHost* render_widget_host = GetRenderWidgetHost(tab); | 155 MarkTabAsLoading(tab_state); |
77 DCHECK(render_widget_host); | |
78 render_widget_hosts_loading_.insert(render_widget_host); | |
79 break; | 156 break; |
80 } | 157 } |
81 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: { | 158 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: { |
82 WebContents* web_contents = content::Source<WebContents>(source).ptr(); | 159 // This happens when a tab has been closed. A tab can be in any state |
83 RemoveTab(&web_contents->GetController()); | 160 // when this occurs. Simply stop tracking the tab. |
| 161 WebContents* web_contents = Source<WebContents>(source).ptr(); |
| 162 NavigationController* tab = &web_contents->GetController(); |
| 163 RemoveTab(tab); |
84 break; | 164 break; |
85 } | 165 } |
86 case content::NOTIFICATION_LOAD_STOP: { | 166 case content::NOTIFICATION_LOAD_STOP: { |
87 NavigationController* tab = | 167 // This occurs to loading tabs when they have finished loading. The tab |
88 content::Source<NavigationController>(source).ptr(); | 168 // may or may not already have painted at this point. |
89 RenderWidgetHost* render_widget_host = GetRenderWidgetHost(tab); | 169 |
90 render_widget_hosts_to_paint_.insert(render_widget_host); | 170 // Update the tab state and any global state as necessary. |
91 RemoveTab(tab); | 171 NavigationController* tab = Source<NavigationController>(source).ptr(); |
92 if (!got_first_foreground_load_ && render_widget_host && | 172 TabState* tab_state = GetTabState(tab); |
93 render_widget_host->GetView() && | 173 DCHECK(tab_state); |
| 174 tab_state->loading_state = TAB_IS_LOADED; |
| 175 DCHECK_LT(0u, loading_tab_count_); |
| 176 --loading_tab_count_; |
| 177 if (!tab_state->is_deferred) { |
| 178 DCHECK_LT(0u, waiting_for_load_tab_count_); |
| 179 --waiting_for_load_tab_count_; |
| 180 } |
| 181 |
| 182 if (tab_state->is_deferred) { |
| 183 reporting_delegate_->ReportDeferredTabLoaded(); |
| 184 } else { |
| 185 DCHECK(!done_tracking_non_deferred_tabs_); |
| 186 ++tab_loader_stats_.tabs_loaded; |
| 187 } |
| 188 |
| 189 // Update statistics for foreground tabs. |
| 190 base::TimeDelta time_to_load = tick_clock_->NowTicks() - restore_started_; |
| 191 if (!got_first_foreground_load_ && tab_state->render_widget_host && |
| 192 tab_state->render_widget_host->GetView() && |
| 193 tab_state->render_widget_host->GetView()->IsShowing()) { |
| 194 got_first_foreground_load_ = true; |
| 195 DCHECK(!done_tracking_non_deferred_tabs_); |
| 196 tab_loader_stats_.foreground_tab_first_loaded = time_to_load; |
| 197 } |
| 198 |
| 199 // Update statistics for all tabs, if this wasn't a deferred tab. This is |
| 200 // done here and not in ReleaseIfDoneTracking because it is possible to |
| 201 // wait for a paint long after all loads have completed. |
| 202 if (!done_tracking_non_deferred_tabs_ && !tab_state->is_deferred) |
| 203 tab_loader_stats_.non_deferred_tabs_loaded = time_to_load; |
| 204 |
| 205 // By default tabs transition to being tracked for paint events after the |
| 206 // load event has been seen. However, if the first paint event has already |
| 207 // been seen then this is not necessary and the tab can be removed. |
| 208 if (got_first_paint_) |
| 209 RemoveTab(tab); |
| 210 |
| 211 break; |
| 212 } |
| 213 case content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE: { |
| 214 // This notification is across all tabs in the browser so notifications |
| 215 // will arrive for tabs that the collector is not explicitly tracking. |
| 216 |
| 217 // Only process this event if first paint hasn't been seen and this is a |
| 218 // paint of a visible tab. |
| 219 RenderWidgetHost* render_widget_host = |
| 220 Source<RenderWidgetHost>(source).ptr(); |
| 221 if (!got_first_paint_ && render_widget_host->GetView() && |
94 render_widget_host->GetView()->IsShowing()) { | 222 render_widget_host->GetView()->IsShowing()) { |
95 got_first_foreground_load_ = true; | 223 got_first_paint_ = true; |
96 base::TimeDelta time_to_load = | 224 TabState* tab_state = GetTabState(render_widget_host); |
97 base::TimeTicks::Now() - restore_started_; | 225 if (tab_state) { |
98 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstLoaded", | 226 // This is a paint for a tab that is explicitly being tracked so |
99 time_to_load, | 227 // update the statistics. Otherwise the host is for a tab that's not |
100 base::TimeDelta::FromMilliseconds(10), | 228 // being tracked thus some other tab has visibility and has rendered |
101 base::TimeDelta::FromSeconds(100), 100); | 229 // and there's no point in tracking the time to first paint. This can |
102 // Record a time for the number of tabs, to help track down | 230 // happen because the user opened a different tab or restored tabs |
103 // contention. | 231 // to an already existing browser and an existing tab was in the |
104 std::string time_for_count = base::StringPrintf( | 232 // foreground. |
105 "SessionRestore.ForegroundTabFirstLoaded_%d", tab_count_); | 233 base::TimeDelta time_to_paint = |
106 base::HistogramBase* counter_for_count = | 234 tick_clock_->NowTicks() - restore_started_; |
107 base::Histogram::FactoryTimeGet( | 235 DCHECK(!done_tracking_non_deferred_tabs_); |
108 time_for_count, base::TimeDelta::FromMilliseconds(10), | 236 tab_loader_stats_.foreground_tab_first_paint = time_to_paint; |
109 base::TimeDelta::FromSeconds(100), 100, | 237 } |
110 base::Histogram::kUmaTargetedHistogramFlag); | 238 |
111 counter_for_count->AddTime(time_to_load); | 239 // Once first paint has been observed the entire to-paint tracking |
| 240 // mechanism is no longer needed. |
| 241 registrar_.Remove( |
| 242 this, |
| 243 content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, |
| 244 content::NotificationService::AllSources()); |
| 245 |
| 246 // Remove any tabs that have loaded. These were only being kept around |
| 247 // while waiting for a paint event. |
| 248 std::vector<NavigationController*> loaded_tabs; |
| 249 for (auto& map_entry : tabs_tracked_) { |
| 250 TabState& tab_state = map_entry.second; |
| 251 if (tab_state.loading_state == TAB_IS_LOADED) |
| 252 loaded_tabs.push_back(tab_state.controller); |
| 253 } |
| 254 for (auto& tab : loaded_tabs) |
| 255 RemoveTab(tab); |
112 } | 256 } |
113 break; | 257 break; |
114 } | 258 } |
115 case content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE: { | |
116 RenderWidgetHost* render_widget_host = | |
117 content::Source<RenderWidgetHost>(source).ptr(); | |
118 if (!got_first_paint_ && render_widget_host->GetView() && | |
119 render_widget_host->GetView()->IsShowing()) { | |
120 if (render_widget_hosts_to_paint_.find(render_widget_host) != | |
121 render_widget_hosts_to_paint_.end()) { | |
122 // Got a paint for one of our renderers, so record time. | |
123 got_first_paint_ = true; | |
124 base::TimeDelta time_to_paint = | |
125 base::TimeTicks::Now() - restore_started_; | |
126 // TODO(danduong): to remove this with 467680, to make sure we | |
127 // don't forget to clean this up. | |
128 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstPaint", | |
129 time_to_paint, | |
130 base::TimeDelta::FromMilliseconds(10), | |
131 base::TimeDelta::FromSeconds(100), 100); | |
132 // Record a time for the number of tabs, to help track down | |
133 // contention. | |
134 std::string time_for_count = base::StringPrintf( | |
135 "SessionRestore.ForegroundTabFirstPaint_%d", tab_count_); | |
136 base::HistogramBase* counter_for_count = | |
137 base::Histogram::FactoryTimeGet( | |
138 time_for_count, base::TimeDelta::FromMilliseconds(10), | |
139 base::TimeDelta::FromSeconds(100), 100, | |
140 base::Histogram::kUmaTargetedHistogramFlag); | |
141 counter_for_count->AddTime(time_to_paint); | |
142 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstPaint2", | |
143 time_to_paint, | |
144 base::TimeDelta::FromMilliseconds(100), | |
145 base::TimeDelta::FromMinutes(16), 50); | |
146 // Record a time for the number of tabs, to help track down | |
147 // contention. | |
148 std::string time_for_count2 = base::StringPrintf( | |
149 "SessionRestore.ForegroundTabFirstPaint2_%d", tab_count_); | |
150 base::HistogramBase* counter_for_count2 = | |
151 base::Histogram::FactoryTimeGet( | |
152 time_for_count2, base::TimeDelta::FromMilliseconds(100), | |
153 base::TimeDelta::FromMinutes(16), 50, | |
154 base::Histogram::kUmaTargetedHistogramFlag); | |
155 counter_for_count2->AddTime(time_to_paint); | |
156 } else if (render_widget_hosts_loading_.find(render_widget_host) == | |
157 render_widget_hosts_loading_.end()) { | |
158 // If this is a host for a tab we're not loading some other tab | |
159 // has rendered and there's no point tracking the time. This could | |
160 // happen because the user opened a different tab or restored tabs | |
161 // to an already existing browser and an existing tab painted. | |
162 got_first_paint_ = true; | |
163 } | |
164 } | |
165 break; | |
166 } | |
167 default: | 259 default: |
168 NOTREACHED() << "Unknown notification received:" << type; | 260 NOTREACHED() << "Unknown notification received:" << type; |
169 break; | 261 break; |
170 } | 262 } |
171 | 263 |
172 // Check if we are done and if so, reset |this_retainer_| as the collector no | 264 ReleaseIfDoneTracking(); |
173 // longer needs to stay alive. | |
174 if ((got_first_paint_ || render_widget_hosts_to_paint_.empty()) && | |
175 tabs_tracked_.empty() && render_widget_hosts_loading_.empty()) | |
176 this_retainer_ = nullptr; | |
177 } | 265 } |
178 | 266 |
179 void SessionRestoreStatsCollector::AddTabs( | 267 void SessionRestoreStatsCollector::RemoveTab(NavigationController* tab) { |
180 const std::vector<SessionRestoreDelegate::RestoredTab>& tabs) { | 268 // Stop observing this tab. |
181 tab_count_ += tabs.size(); | 269 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |
182 for (auto& tab : tabs) { | 270 Source<WebContents>(tab->GetWebContents())); |
183 RegisterForNotifications(&tab.contents()->GetController()); | 271 registrar_.Remove(this, content::NOTIFICATION_LOAD_STOP, |
184 if (tab.is_active()) { | 272 Source<NavigationController>(tab)); |
185 RenderWidgetHost* render_widget_host = | 273 registrar_.Remove(this, content::NOTIFICATION_LOAD_START, |
186 GetRenderWidgetHost(&tab.contents()->GetController()); | 274 Source<NavigationController>(tab)); |
187 render_widget_hosts_loading_.insert(render_widget_host); | 275 |
188 } | 276 auto tab_it = tabs_tracked_.find(tab); |
| 277 DCHECK(tab_it != tabs_tracked_.end()); |
| 278 TabState& tab_state = tab_it->second; |
| 279 |
| 280 // If this tab was waiting for a NOTIFICATION_LOAD_STOP event then update |
| 281 // the loading counts. |
| 282 if (tab_state.loading_state == TAB_IS_LOADING) { |
| 283 DCHECK_LT(0u, loading_tab_count_); |
| 284 --loading_tab_count_; |
| 285 } |
| 286 |
| 287 // Only non-deferred not-loading/not-loaded tabs are waiting to be loaded. |
| 288 if (tab_state.loading_state != TAB_IS_LOADED && !tab_state.is_deferred) { |
| 289 DCHECK_LT(0u, waiting_for_load_tab_count_); |
| 290 // It's possible for waiting_for_load_tab_count_ to reach zero here. This |
| 291 // function is only called from 'Observe', so the transition will be |
| 292 // noticed there. |
| 293 --waiting_for_load_tab_count_; |
| 294 } |
| 295 |
| 296 if (tab_state.is_deferred) |
| 297 --deferred_tab_count_; |
| 298 |
| 299 // Remove the tab from the |tracked_tabs_| map. |
| 300 tabs_tracked_.erase(tab_it); |
| 301 |
| 302 // It is possible for all restored contents to be destroyed or forcibly |
| 303 // renavigated before a first paint has arrived. This can be detected by |
| 304 // tabs_tracked_ containing only deferred tabs. At this point the paint |
| 305 // mechanism can be disabled and stats collection will stop. |
| 306 if (tabs_tracked_.size() == deferred_tab_count_ && !got_first_paint_) { |
| 307 got_first_paint_ = true; |
| 308 registrar_.Remove( |
| 309 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, |
| 310 content::NotificationService::AllSources()); |
189 } | 311 } |
190 } | 312 } |
191 | 313 |
192 void SessionRestoreStatsCollector::RemoveTab(NavigationController* tab) { | 314 SessionRestoreStatsCollector::TabState* |
193 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | 315 SessionRestoreStatsCollector::RegisterForNotifications( |
194 content::Source<WebContents>(tab->GetWebContents())); | |
195 registrar_.Remove(this, content::NOTIFICATION_LOAD_STOP, | |
196 content::Source<NavigationController>(tab)); | |
197 registrar_.Remove(this, content::NOTIFICATION_LOAD_START, | |
198 content::Source<NavigationController>(tab)); | |
199 if (render_widget_hosts_loading_.size() > max_parallel_tab_loads_) | |
200 max_parallel_tab_loads_ = render_widget_hosts_loading_.size(); | |
201 RenderWidgetHost* render_widget_host = GetRenderWidgetHost(tab); | |
202 render_widget_hosts_loading_.erase(render_widget_host); | |
203 tabs_tracked_.erase(tab); | |
204 | |
205 // If there are no more tabs loading or being tracked, restore is done and | |
206 // record the time. Note that we are not yet finished, as we might still be | |
207 // waiting for our first paint, which can happen after all tabs are done | |
208 // loading. | |
209 // TODO(georgesak): review behaviour of ForegroundTabFirstPaint. | |
210 if (tabs_tracked_.empty() && render_widget_hosts_loading_.empty()) { | |
211 base::TimeDelta time_to_load = base::TimeTicks::Now() - restore_started_; | |
212 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.AllTabsLoaded", time_to_load, | |
213 base::TimeDelta::FromMilliseconds(10), | |
214 base::TimeDelta::FromSeconds(100), 100); | |
215 // Record a time for the number of tabs, to help track down contention. | |
216 std::string time_for_count = | |
217 base::StringPrintf("SessionRestore.AllTabsLoaded_%d", tab_count_); | |
218 base::HistogramBase* counter_for_count = base::Histogram::FactoryTimeGet( | |
219 time_for_count, base::TimeDelta::FromMilliseconds(10), | |
220 base::TimeDelta::FromSeconds(100), 100, | |
221 base::Histogram::kUmaTargetedHistogramFlag); | |
222 counter_for_count->AddTime(time_to_load); | |
223 | |
224 UMA_HISTOGRAM_COUNTS_100("SessionRestore.ParallelTabLoads", | |
225 max_parallel_tab_loads_); | |
226 } | |
227 } | |
228 | |
229 void SessionRestoreStatsCollector::RegisterForNotifications( | |
230 NavigationController* tab) { | 316 NavigationController* tab) { |
231 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | 317 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |
232 content::Source<WebContents>(tab->GetWebContents())); | 318 Source<WebContents>(tab->GetWebContents())); |
233 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, | 319 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, |
234 content::Source<NavigationController>(tab)); | 320 Source<NavigationController>(tab)); |
235 registrar_.Add(this, content::NOTIFICATION_LOAD_START, | 321 registrar_.Add(this, content::NOTIFICATION_LOAD_START, |
236 content::Source<NavigationController>(tab)); | 322 Source<NavigationController>(tab)); |
237 tabs_tracked_.insert(tab); | 323 auto result = tabs_tracked_.insert(std::make_pair(tab, TabState(tab))); |
| 324 DCHECK(result.second); |
| 325 TabState* tab_state = &result.first->second; |
| 326 return tab_state; |
238 } | 327 } |
239 | 328 |
240 RenderWidgetHost* SessionRestoreStatsCollector::GetRenderWidgetHost( | 329 RenderWidgetHost* SessionRestoreStatsCollector::GetRenderWidgetHost( |
241 NavigationController* tab) { | 330 NavigationController* tab) { |
242 WebContents* web_contents = tab->GetWebContents(); | 331 WebContents* web_contents = tab->GetWebContents(); |
243 if (web_contents) { | 332 if (web_contents) { |
244 content::RenderWidgetHostView* render_widget_host_view = | 333 content::RenderWidgetHostView* render_widget_host_view = |
245 web_contents->GetRenderWidgetHostView(); | 334 web_contents->GetRenderWidgetHostView(); |
246 if (render_widget_host_view) | 335 if (render_widget_host_view) |
247 return render_widget_host_view->GetRenderWidgetHost(); | 336 return render_widget_host_view->GetRenderWidgetHost(); |
248 } | 337 } |
249 return nullptr; | 338 return nullptr; |
250 } | 339 } |
251 | 340 |
252 // static | 341 SessionRestoreStatsCollector::TabState* |
253 SessionRestoreStatsCollector* SessionRestoreStatsCollector::shared_collector_ = | 342 SessionRestoreStatsCollector::GetTabState(NavigationController* tab) { |
254 nullptr; | 343 // This lookup can fail because DeferTab calls can arrive for tabs that have |
| 344 // already loaded (user forced) and are no longer tracked. |
| 345 auto it = tabs_tracked_.find(tab); |
| 346 if (it == tabs_tracked_.end()) |
| 347 return nullptr; |
| 348 return &it->second; |
| 349 } |
| 350 |
| 351 SessionRestoreStatsCollector::TabState* |
| 352 SessionRestoreStatsCollector::GetTabState(RenderWidgetHost* tab) { |
| 353 for (auto& pair : tabs_tracked_) { |
| 354 if (pair.second.render_widget_host == tab) |
| 355 return &pair.second; |
| 356 } |
| 357 // It's possible for this lookup to fail as paint events can be received for |
| 358 // tabs that aren't being tracked. |
| 359 return nullptr; |
| 360 } |
| 361 |
| 362 void SessionRestoreStatsCollector::MarkTabAsLoading(TabState* tab_state) { |
| 363 // If the tab has already started or finished loading then a user navigation |
| 364 // has caused the tab to be forcibly reloaded. This tab can be removed from |
| 365 // observation. |
| 366 if (tab_state->loading_state == TAB_IS_LOADED) { |
| 367 RemoveTab(tab_state->controller); |
| 368 return; |
| 369 } |
| 370 |
| 371 DCHECK_EQ(TAB_IS_NOT_LOADING, tab_state->loading_state); |
| 372 if (tab_state->loading_state != TAB_IS_NOT_LOADING) |
| 373 return; |
| 374 tab_state->loading_state = TAB_IS_LOADING; |
| 375 ++loading_tab_count_; |
| 376 |
| 377 if (!done_tracking_non_deferred_tabs_) { |
| 378 tab_loader_stats_.parallel_tab_loads = |
| 379 std::max(tab_loader_stats_.parallel_tab_loads, loading_tab_count_); |
| 380 } |
| 381 |
| 382 // Get the RenderWidgetHost for the tab and add it to the secondary index. |
| 383 RenderWidgetHost* render_widget_host = |
| 384 GetRenderWidgetHost(tab_state->controller); |
| 385 DCHECK(render_widget_host); |
| 386 tab_state->render_widget_host = render_widget_host; |
| 387 } |
| 388 |
| 389 void SessionRestoreStatsCollector::ReleaseIfDoneTracking() { |
| 390 // If non-deferred tabs are no longer being tracked then report tab loader |
| 391 // statistics. |
| 392 if (!done_tracking_non_deferred_tabs_ && got_first_paint_ && |
| 393 waiting_for_load_tab_count_ == 0) { |
| 394 done_tracking_non_deferred_tabs_ = true; |
| 395 reporting_delegate_->ReportTabLoaderStats(tab_loader_stats_); |
| 396 } |
| 397 |
| 398 // If tracking is completely finished then emit collected metrics and destroy |
| 399 // this stats collector. |
| 400 if (done_tracking_non_deferred_tabs_ && tabs_tracked_.empty()) |
| 401 this_retainer_ = nullptr; |
| 402 } |
| 403 |
| 404 SessionRestoreStatsCollector::UmaStatsReportingDelegate:: |
| 405 UmaStatsReportingDelegate() |
| 406 : got_report_tab_deferred_(false) { |
| 407 } |
| 408 |
| 409 void SessionRestoreStatsCollector::UmaStatsReportingDelegate:: |
| 410 ReportTabLoaderStats(const TabLoaderStats& tab_loader_stats) { |
| 411 UMA_HISTOGRAM_COUNTS_100("SessionRestore.TabCount", |
| 412 tab_loader_stats.tab_count); |
| 413 |
| 414 EmitUmaSessionRestoreActionEvent(SESSION_RESTORE_ACTIONS_UMA_INITIATED); |
| 415 |
| 416 for (size_t i = 0; i < tab_loader_stats.tab_count; ++i) { |
| 417 EmitUmaSessionRestoreTabActionEvent( |
| 418 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_CREATED); |
| 419 } |
| 420 |
| 421 for (size_t i = 0; i < tab_loader_stats.tabs_loaded; ++i) { |
| 422 EmitUmaSessionRestoreTabActionEvent( |
| 423 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADED); |
| 424 } |
| 425 |
| 426 if (!tab_loader_stats.foreground_tab_first_loaded.is_zero()) { |
| 427 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstLoaded", |
| 428 tab_loader_stats.foreground_tab_first_loaded, |
| 429 base::TimeDelta::FromMilliseconds(10), |
| 430 base::TimeDelta::FromSeconds(100), 100); |
| 431 |
| 432 // Record a time for the number of tabs, to help track down contention. |
| 433 std::string time_for_count = base::StringPrintf( |
| 434 "SessionRestore.ForegroundTabFirstLoaded_%u", |
| 435 static_cast<unsigned int>(tab_loader_stats.tab_count)); |
| 436 base::HistogramBase* counter_for_count = base::Histogram::FactoryTimeGet( |
| 437 time_for_count, base::TimeDelta::FromMilliseconds(10), |
| 438 base::TimeDelta::FromSeconds(100), 100, |
| 439 base::Histogram::kUmaTargetedHistogramFlag); |
| 440 counter_for_count->AddTime(tab_loader_stats.foreground_tab_first_loaded); |
| 441 } |
| 442 |
| 443 if (!tab_loader_stats.foreground_tab_first_paint.is_zero()) { |
| 444 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstPaint3", |
| 445 tab_loader_stats.foreground_tab_first_paint, |
| 446 base::TimeDelta::FromMilliseconds(100), |
| 447 base::TimeDelta::FromMinutes(16), 50); |
| 448 |
| 449 std::string time_for_count = base::StringPrintf( |
| 450 "SessionRestore.ForegroundTabFirstPaint3_%u", |
| 451 static_cast<unsigned int>(tab_loader_stats.tab_count)); |
| 452 base::HistogramBase* counter_for_count = base::Histogram::FactoryTimeGet( |
| 453 time_for_count, base::TimeDelta::FromMilliseconds(100), |
| 454 base::TimeDelta::FromMinutes(16), 50, |
| 455 base::Histogram::kUmaTargetedHistogramFlag); |
| 456 counter_for_count->AddTime(tab_loader_stats.foreground_tab_first_paint); |
| 457 } |
| 458 |
| 459 if (!tab_loader_stats.non_deferred_tabs_loaded.is_zero()) { |
| 460 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.AllTabsLoaded", |
| 461 tab_loader_stats.non_deferred_tabs_loaded, |
| 462 base::TimeDelta::FromMilliseconds(10), |
| 463 base::TimeDelta::FromSeconds(100), 100); |
| 464 |
| 465 // Record a time for the number of tabs, to help track down contention. |
| 466 std::string time_for_count = base::StringPrintf( |
| 467 "SessionRestore.AllTabsLoaded_%u", |
| 468 static_cast<unsigned int>(tab_loader_stats.tab_count)); |
| 469 base::HistogramBase* counter_for_count = base::Histogram::FactoryTimeGet( |
| 470 time_for_count, base::TimeDelta::FromMilliseconds(10), |
| 471 base::TimeDelta::FromSeconds(100), 100, |
| 472 base::Histogram::kUmaTargetedHistogramFlag); |
| 473 counter_for_count->AddTime(tab_loader_stats.non_deferred_tabs_loaded); |
| 474 } |
| 475 |
| 476 UMA_HISTOGRAM_COUNTS_100("SessionRestore.ParallelTabLoads", |
| 477 tab_loader_stats.parallel_tab_loads); |
| 478 } |
| 479 |
| 480 void SessionRestoreStatsCollector::UmaStatsReportingDelegate:: |
| 481 ReportTabDeferred() { |
| 482 if (!got_report_tab_deferred_) { |
| 483 got_report_tab_deferred_ = true; |
| 484 EmitUmaSessionRestoreActionEvent(SESSION_RESTORE_ACTIONS_UMA_DEFERRED_TABS); |
| 485 } |
| 486 |
| 487 EmitUmaSessionRestoreTabActionEvent( |
| 488 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADING_DEFERRED); |
| 489 } |
| 490 |
| 491 void SessionRestoreStatsCollector::UmaStatsReportingDelegate:: |
| 492 ReportDeferredTabLoaded() { |
| 493 EmitUmaSessionRestoreTabActionEvent( |
| 494 SESSION_RESTORE_TAB_ACTIONS_UMA_DEFERRED_TAB_LOADED); |
| 495 } |
OLD | NEW |