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