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 "content/public/browser/notification_service.h" | 11 #include "content/public/browser/notification_service.h" |
| 12 #include "content/public/browser/notification_types.h" | 12 #include "content/public/browser/notification_types.h" |
| 13 #include "content/public/browser/render_widget_host_view.h" | 13 #include "content/public/browser/render_widget_host_view.h" |
| 14 #include "content/public/browser/web_contents.h" | 14 #include "content/public/browser/web_contents.h" |
| 15 | 15 |
| 16 using content::NavigationController; | 16 using content::NavigationController; |
| 17 using content::RenderWidgetHost; | 17 using content::RenderWidgetHost; |
| 18 using content::WebContents; | 18 using content::WebContents; |
| 19 | 19 |
| 20 namespace { | |
| 21 | |
| 22 // Metric names. | |
| 23 const char kSessionRestoreActions[] = "SessionRestore.Actions"; | |
| 24 const char kSessionRestoreTabCount[] = "SessionRestore.TabCount"; | |
| 25 const char kSessionRestoreTabActions[] = "SessionRestore.TabActions"; | |
| 26 | |
| 27 // The enumeration values stored in the kSessionRestoreActions histogram. | |
| 28 enum SessionRestoreActionsUma { | |
| 29 // Counts the total number of session restores that have occurred. | |
| 30 SESSION_RESTORE_ACTIONS_UMA_INITIATED = 0, | |
| 31 // Counts the number of session restores that have been deferred tab loadings | |
| 32 // for whatever reason (almost certainly due to memory pressure). | |
| 33 SESSION_RESTORE_ACTIONS_UMA_DEFERRED_TABS = 1, | |
| 34 // The size of this enum. Must be the last entry. | |
| 35 SESSION_RESTORE_ACTIONS_UMA_MAX, | |
| 36 }; | |
| 37 | |
| 38 // The enumeration of values stored in the kSessionRestoreTabActions histogram. | |
| 39 enum SessionRestoreTabActionsUma { | |
| 40 // Incremented for each tab created in a session restore. | |
| 41 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_CREATED = 0, | |
| 42 // Incremented for each tab that session restore decides not to load. | |
| 43 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADING_DEFERRED = 1, | |
| 44 // Incremented for each tab that is successfully loaded. | |
| 45 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADED = 2, | |
| 46 // Incremented for each session-restore-deferred tab that is subsequently | |
| 47 // loaded. | |
| 48 SESSION_RESTORE_TAB_ACTIONS_UMA_DEFERRED_TAB_LOADED = 3, | |
| 49 // The size of this enum. Must be the last entry. | |
| 50 SESSION_RESTORE_TAB_ACTIONS_UMA_MAX, | |
| 51 }; | |
| 52 | |
| 53 } // namespace | |
| 54 | |
| 20 // static | 55 // static |
| 21 void SessionRestoreStatsCollector::TrackTabs( | 56 void SessionRestoreStatsCollector::TrackTabs( |
| 22 const std::vector<SessionRestoreDelegate::RestoredTab>& tabs, | 57 const std::vector<SessionRestoreDelegate::RestoredTab>& tabs, |
| 23 const base::TimeTicks& restore_started) { | 58 const base::TimeTicks& restore_started) { |
| 24 if (!shared_collector_) | 59 if (!shared_collector_) |
| 25 shared_collector_ = new SessionRestoreStatsCollector(restore_started); | 60 shared_collector_ = new SessionRestoreStatsCollector(restore_started); |
| 26 | 61 |
| 27 shared_collector_->AddTabs(tabs); | 62 shared_collector_->AddTabs(tabs); |
| 28 } | 63 } |
| 29 | 64 |
| 30 // static | 65 // static |
| 31 void SessionRestoreStatsCollector::TrackActiveTabs( | 66 void SessionRestoreStatsCollector::DeferTab( |
| 32 const std::vector<SessionRestoreDelegate::RestoredTab>& tabs, | 67 content::NavigationController* tab) { |
| 33 const base::TimeTicks& restore_started) { | 68 // It only makes sense for this to have been called *after* TrackTabs, so a |
| 34 if (!shared_collector_) | 69 // stats collector will always exist. |
| 35 shared_collector_ = new SessionRestoreStatsCollector(restore_started); | 70 DCHECK(shared_collector_); |
| 36 | 71 shared_collector_->DeferTabImpl(tab); |
| 37 std::vector<SessionRestoreDelegate::RestoredTab> active_tabs; | |
| 38 for (auto tab : tabs) { | |
| 39 if (tab.is_active()) | |
| 40 active_tabs.push_back(tab); | |
| 41 } | |
| 42 shared_collector_->AddTabs(active_tabs); | |
| 43 } | 72 } |
| 44 | 73 |
| 45 SessionRestoreStatsCollector::SessionRestoreStatsCollector( | 74 SessionRestoreStatsCollector::SessionRestoreStatsCollector( |
| 46 const base::TimeTicks& restore_started) | 75 const base::TimeTicks& restore_started) |
| 47 : got_first_foreground_load_(false), | 76 : got_first_foreground_load_(false), |
| 48 got_first_paint_(false), | 77 got_first_paint_(false), |
| 49 restore_started_(restore_started), | 78 restore_started_(restore_started), |
| 50 tab_count_(0), | 79 render_widget_hosts_loading_deferred_count_(0u), |
| 51 max_parallel_tab_loads_(0) { | 80 tabs_tracked_deferred_count_(0u), |
| 81 tab_count_(0u), | |
| 82 tab_deferred_count_(0u), | |
| 83 max_parallel_tab_loads_(0u) { | |
| 52 this_retainer_ = this; | 84 this_retainer_ = this; |
| 53 registrar_.Add( | 85 registrar_.Add( |
| 54 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, | 86 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, |
| 55 content::NotificationService::AllSources()); | 87 content::NotificationService::AllSources()); |
| 56 } | 88 } |
| 57 | 89 |
| 58 SessionRestoreStatsCollector::~SessionRestoreStatsCollector() { | 90 SessionRestoreStatsCollector::~SessionRestoreStatsCollector() { |
| 59 DCHECK((got_first_paint_ || render_widget_hosts_to_paint_.empty()) && | 91 // The collector should only be destroyed when tracking is entirely |
| 60 tabs_tracked_.empty() && render_widget_hosts_loading_.empty()); | 92 // finished. It should already have beend detached from being the shared |
| 61 DCHECK(shared_collector_ == this); | 93 // collector. |
| 62 shared_collector_ = nullptr; | 94 DCHECK(shared_collector_ != this); |
| 95 DCHECK(DoneTracking()); | |
| 63 } | 96 } |
| 64 | 97 |
| 65 void SessionRestoreStatsCollector::Observe( | 98 void SessionRestoreStatsCollector::Observe( |
| 66 int type, | 99 int type, |
| 67 const content::NotificationSource& source, | 100 const content::NotificationSource& source, |
| 68 const content::NotificationDetails& details) { | 101 const content::NotificationDetails& details) { |
| 69 switch (type) { | 102 switch (type) { |
| 70 case content::NOTIFICATION_LOAD_START: { | 103 case content::NOTIFICATION_LOAD_START: { |
| 71 // Add this render_widget_host to the set of those we're waiting for | |
| 72 // paints on. We want to only record stats for paints that occur after | |
| 73 // a load has finished. | |
| 74 NavigationController* tab = | 104 NavigationController* tab = |
| 75 content::Source<NavigationController>(source).ptr(); | 105 content::Source<NavigationController>(source).ptr(); |
| 106 bool is_deferred = IsDeferred(tab); | |
| 107 | |
| 108 // Add this render_widget_host to the set of those that are loading. | |
| 76 RenderWidgetHost* render_widget_host = GetRenderWidgetHost(tab); | 109 RenderWidgetHost* render_widget_host = GetRenderWidgetHost(tab); |
| 77 DCHECK(render_widget_host); | 110 DCHECK(render_widget_host); |
| 78 render_widget_hosts_loading_.insert(render_widget_host); | 111 render_widget_hosts_loading_.insert( |
| 112 std::make_pair(render_widget_host, is_deferred)); | |
| 113 if (is_deferred) | |
| 114 ++render_widget_hosts_loading_deferred_count_; | |
| 115 | |
| 79 break; | 116 break; |
| 80 } | 117 } |
| 81 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: { | 118 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: { |
| 82 WebContents* web_contents = content::Source<WebContents>(source).ptr(); | 119 WebContents* web_contents = content::Source<WebContents>(source).ptr(); |
| 83 RemoveTab(&web_contents->GetController()); | 120 NavigationController* tab = &web_contents->GetController(); |
| 121 RemoveTab(tab); | |
| 84 break; | 122 break; |
| 85 } | 123 } |
| 86 case content::NOTIFICATION_LOAD_STOP: { | 124 case content::NOTIFICATION_LOAD_STOP: { |
| 125 // Remove the tab from the tracking maps. | |
| 87 NavigationController* tab = | 126 NavigationController* tab = |
| 88 content::Source<NavigationController>(source).ptr(); | 127 content::Source<NavigationController>(source).ptr(); |
| 89 RenderWidgetHost* render_widget_host = GetRenderWidgetHost(tab); | 128 RenderWidgetHost* render_widget_host = GetRenderWidgetHost(tab); |
| 90 render_widget_hosts_to_paint_.insert(render_widget_host); | 129 auto tab_it = tabs_tracked_.find(tab); |
| 130 bool is_deferred = tab_it->second; | |
| 91 RemoveTab(tab); | 131 RemoveTab(tab); |
| 132 | |
| 133 // If first paint hasn't yet been seen then add this tab to the set of | |
| 134 // those being tracked for first paints. | |
| 135 if (!got_first_paint_ && render_widget_host) | |
| 136 render_widget_hosts_to_paint_.insert(render_widget_host); | |
| 137 | |
| 138 // Note the fact that this tab loaded. | |
| 139 auto tab_action = is_deferred ? | |
| 140 SESSION_RESTORE_TAB_ACTIONS_UMA_DEFERRED_TAB_LOADED : | |
| 141 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADED; | |
| 142 UMA_HISTOGRAM_ENUMERATION(kSessionRestoreTabActions, | |
| 143 tab_action, | |
| 144 SESSION_RESTORE_TAB_ACTIONS_UMA_MAX); | |
| 145 | |
| 146 // Update UMA stats for foreground tabs. | |
| 92 if (!got_first_foreground_load_ && render_widget_host && | 147 if (!got_first_foreground_load_ && render_widget_host && |
| 93 render_widget_host->GetView() && | 148 render_widget_host->GetView() && |
| 94 render_widget_host->GetView()->IsShowing()) { | 149 render_widget_host->GetView()->IsShowing()) { |
| 95 got_first_foreground_load_ = true; | 150 got_first_foreground_load_ = true; |
| 96 base::TimeDelta time_to_load = | 151 base::TimeDelta time_to_load = |
| 97 base::TimeTicks::Now() - restore_started_; | 152 base::TimeTicks::Now() - restore_started_; |
| 98 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstLoaded", | 153 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstLoaded", |
| 99 time_to_load, | 154 time_to_load, |
| 100 base::TimeDelta::FromMilliseconds(10), | 155 base::TimeDelta::FromMilliseconds(10), |
| 101 base::TimeDelta::FromSeconds(100), 100); | 156 base::TimeDelta::FromSeconds(100), 100); |
| 102 // Record a time for the number of tabs, to help track down | 157 // Record a time for the number of tabs, to help track down |
| 103 // contention. | 158 // contention. |
| 104 std::string time_for_count = base::StringPrintf( | 159 std::string time_for_count = base::StringPrintf( |
| 105 "SessionRestore.ForegroundTabFirstLoaded_%d", tab_count_); | 160 "SessionRestore.ForegroundTabFirstLoaded_%d", tab_count_); |
| 106 base::HistogramBase* counter_for_count = | 161 base::HistogramBase* counter_for_count = |
| 107 base::Histogram::FactoryTimeGet( | 162 base::Histogram::FactoryTimeGet( |
| 108 time_for_count, base::TimeDelta::FromMilliseconds(10), | 163 time_for_count, base::TimeDelta::FromMilliseconds(10), |
| 109 base::TimeDelta::FromSeconds(100), 100, | 164 base::TimeDelta::FromSeconds(100), 100, |
| 110 base::Histogram::kUmaTargetedHistogramFlag); | 165 base::Histogram::kUmaTargetedHistogramFlag); |
| 111 counter_for_count->AddTime(time_to_load); | 166 counter_for_count->AddTime(time_to_load); |
| 112 } | 167 } |
| 113 break; | 168 break; |
| 114 } | 169 } |
| 115 case content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE: { | 170 case content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE: { |
| 171 // This notification is across all tabs in the browser so notifications | |
| 172 // will arrive for tabs that the collector is not explicitly tracking. | |
| 173 | |
| 174 // Only process this event if first paint hasn't been seen and this is a | |
| 175 // paint of a visible tab. | |
| 116 RenderWidgetHost* render_widget_host = | 176 RenderWidgetHost* render_widget_host = |
| 117 content::Source<RenderWidgetHost>(source).ptr(); | 177 content::Source<RenderWidgetHost>(source).ptr(); |
| 118 if (!got_first_paint_ && render_widget_host->GetView() && | 178 if (!got_first_paint_ && |
| 179 render_widget_host->GetView() && | |
| 119 render_widget_host->GetView()->IsShowing()) { | 180 render_widget_host->GetView()->IsShowing()) { |
| 120 if (render_widget_hosts_to_paint_.find(render_widget_host) != | 181 // If this is a paint for a tab that is explicitly being tracked for |
| 121 render_widget_hosts_to_paint_.end()) { | 182 // paint events then record the appropriate UMA statistic. |
| 183 auto render_widget_host_it = | |
| 184 render_widget_hosts_to_paint_.find(render_widget_host); | |
| 185 if (render_widget_host_it != render_widget_hosts_to_paint_.end()) { | |
| 186 // Stop tracking this tab. | |
| 187 render_widget_hosts_to_paint_.erase(render_widget_host_it); | |
| 188 | |
| 122 // Got a paint for one of our renderers, so record time. | 189 // Got a paint for one of our renderers, so record time. |
| 123 got_first_paint_ = true; | 190 got_first_paint_ = true; |
| 124 base::TimeDelta time_to_paint = | 191 base::TimeDelta time_to_paint = |
| 125 base::TimeTicks::Now() - restore_started_; | 192 base::TimeTicks::Now() - restore_started_; |
| 126 // TODO(danduong): to remove this with 467680, to make sure we | 193 // TODO(danduong): to remove this with 467680, to make sure we |
| 127 // don't forget to clean this up. | 194 // don't forget to clean this up. |
| 128 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstPaint", | 195 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstPaint", |
| 129 time_to_paint, | 196 time_to_paint, |
| 130 base::TimeDelta::FromMilliseconds(10), | 197 base::TimeDelta::FromMilliseconds(10), |
| 131 base::TimeDelta::FromSeconds(100), 100); | 198 base::TimeDelta::FromSeconds(100), 100); |
| 132 // Record a time for the number of tabs, to help track down | 199 // Record a time for the number of tabs, to help track down |
| 133 // contention. | 200 // contention. |
| 134 std::string time_for_count = base::StringPrintf( | 201 std::string time_for_count = base::StringPrintf( |
| 135 "SessionRestore.ForegroundTabFirstPaint_%d", tab_count_); | 202 "SessionRestore.ForegroundTabFirstPaint_%d", tab_count_); |
| 136 base::HistogramBase* counter_for_count = | 203 base::HistogramBase* counter_for_count = |
| 137 base::Histogram::FactoryTimeGet( | 204 base::Histogram::FactoryTimeGet( |
| 138 time_for_count, base::TimeDelta::FromMilliseconds(10), | 205 time_for_count, base::TimeDelta::FromMilliseconds(10), |
| 139 base::TimeDelta::FromSeconds(100), 100, | 206 base::TimeDelta::FromSeconds(100), 100, |
| 140 base::Histogram::kUmaTargetedHistogramFlag); | 207 base::Histogram::kUmaTargetedHistogramFlag); |
| 141 counter_for_count->AddTime(time_to_paint); | 208 counter_for_count->AddTime(time_to_paint); |
| 142 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstPaint2", | 209 UMA_HISTOGRAM_CUSTOM_TIMES( |
| 143 time_to_paint, | 210 "SessionRestore.ForegroundTabFirstPaint2", |
| 144 base::TimeDelta::FromMilliseconds(100), | 211 time_to_paint, |
| 145 base::TimeDelta::FromMinutes(16), 50); | 212 base::TimeDelta::FromMilliseconds(100), |
| 213 base::TimeDelta::FromMinutes(16), 50); | |
| 146 // Record a time for the number of tabs, to help track down | 214 // Record a time for the number of tabs, to help track down |
| 147 // contention. | 215 // contention. |
| 148 std::string time_for_count2 = base::StringPrintf( | 216 std::string time_for_count2 = base::StringPrintf( |
| 149 "SessionRestore.ForegroundTabFirstPaint2_%d", tab_count_); | 217 "SessionRestore.ForegroundTabFirstPaint2_%d", tab_count_); |
| 150 base::HistogramBase* counter_for_count2 = | 218 base::HistogramBase* counter_for_count2 = |
| 151 base::Histogram::FactoryTimeGet( | 219 base::Histogram::FactoryTimeGet( |
| 152 time_for_count2, base::TimeDelta::FromMilliseconds(100), | 220 time_for_count2, base::TimeDelta::FromMilliseconds(100), |
| 153 base::TimeDelta::FromMinutes(16), 50, | 221 base::TimeDelta::FromMinutes(16), 50, |
| 154 base::Histogram::kUmaTargetedHistogramFlag); | 222 base::Histogram::kUmaTargetedHistogramFlag); |
| 155 counter_for_count2->AddTime(time_to_paint); | 223 counter_for_count2->AddTime(time_to_paint); |
| 156 } else if (render_widget_hosts_loading_.find(render_widget_host) == | 224 } else if (render_widget_hosts_loading_.find(render_widget_host) == |
| 157 render_widget_hosts_loading_.end()) { | 225 render_widget_hosts_loading_.end()) { |
| 158 // If this is a host for a tab we're not loading some other tab | 226 // If this is a host for a tab we're not tracking then some other tab |
| 159 // has rendered and there's no point tracking the time. This could | 227 // has rendered and there's no point tracking the time. This could |
| 160 // happen because the user opened a different tab or restored tabs | 228 // happen because the user opened a different tab or restored tabs |
| 161 // to an already existing browser and an existing tab painted. | 229 // to an already existing browser and an existing tab painted. |
| 162 got_first_paint_ = true; | 230 got_first_paint_ = true; |
| 163 } | 231 } |
| 232 | |
| 233 if (got_first_paint_) { | |
| 234 // If the first paint has been observed the entire to-paint tracking | |
| 235 // mechanism is no longer needed. | |
| 236 registrar_.Remove( | |
| 237 this, | |
| 238 content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE, | |
| 239 content::NotificationService::AllSources()); | |
| 240 render_widget_hosts_to_paint_.clear(); | |
| 241 } | |
| 164 } | 242 } |
| 165 break; | 243 break; |
| 166 } | 244 } |
| 167 default: | 245 default: |
| 168 NOTREACHED() << "Unknown notification received:" << type; | 246 NOTREACHED() << "Unknown notification received:" << type; |
| 169 break; | 247 break; |
| 170 } | 248 } |
| 171 | 249 |
| 172 // Check if we are done and if so, reset |this_retainer_| as the collector no | 250 // If non-deferred tabs are no longer being tracked then detach this collector |
| 173 // longer needs to stay alive. | 251 // from being the shared collector. This allows new session restores to get |
| 174 if ((got_first_paint_ || render_widget_hosts_to_paint_.empty()) && | 252 // new stats collectors. It also means that multiple stats collectors can |
| 175 tabs_tracked_.empty() && render_widget_hosts_loading_.empty()) | 253 // exist simultaneously. |
| 254 if (shared_collector_ == this && DoneTrackingNonDeferredTabs()) | |
| 255 shared_collector_ = nullptr; | |
| 256 | |
| 257 // If tracking is completely finished destroy this stats collector. | |
| 258 if (shared_collector_ != this && DoneTracking()) | |
| 176 this_retainer_ = nullptr; | 259 this_retainer_ = nullptr; |
| 177 } | 260 } |
| 178 | 261 |
| 179 void SessionRestoreStatsCollector::AddTabs( | 262 void SessionRestoreStatsCollector::AddTabs( |
| 180 const std::vector<SessionRestoreDelegate::RestoredTab>& tabs) { | 263 const std::vector<SessionRestoreDelegate::RestoredTab>& tabs) { |
| 264 // A new session restore is starting. | |
| 265 if (tab_count_ == 0) { | |
| 266 // Each session restore makes a call to AddTabs with all of its tabs to be | |
| 267 // restored all at once. Record statistics about all simultaneous session | |
| 268 // restores (ie: multi-profile) as a whole. | |
| 269 UMA_HISTOGRAM_COUNTS_100(kSessionRestoreTabCount, tabs.size()); | |
| 270 UMA_HISTOGRAM_ENUMERATION(kSessionRestoreActions, | |
| 271 SESSION_RESTORE_ACTIONS_UMA_INITIATED, | |
| 272 SESSION_RESTORE_ACTIONS_UMA_MAX); | |
| 273 } | |
| 274 | |
| 181 tab_count_ += tabs.size(); | 275 tab_count_ += tabs.size(); |
| 182 for (auto& tab : tabs) { | 276 for (auto& tab : tabs) { |
| 277 // Record actions for each individual tab. | |
| 278 UMA_HISTOGRAM_ENUMERATION(kSessionRestoreTabActions, | |
| 279 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_CREATED, | |
| 280 SESSION_RESTORE_TAB_ACTIONS_UMA_MAX); | |
| 281 | |
| 183 RegisterForNotifications(&tab.contents()->GetController()); | 282 RegisterForNotifications(&tab.contents()->GetController()); |
| 184 if (tab.is_active()) { | 283 if (tab.is_active()) { |
| 185 RenderWidgetHost* render_widget_host = | 284 RenderWidgetHost* render_widget_host = |
| 186 GetRenderWidgetHost(&tab.contents()->GetController()); | 285 GetRenderWidgetHost(&tab.contents()->GetController()); |
| 187 render_widget_hosts_loading_.insert(render_widget_host); | 286 render_widget_hosts_loading_.insert( |
| 287 std::make_pair(render_widget_host, false)); | |
| 188 } | 288 } |
| 189 } | 289 } |
| 190 } | 290 } |
| 191 | 291 |
| 292 void SessionRestoreStatsCollector::DeferTabImpl( | |
| 293 content::NavigationController* tab) { | |
| 294 auto tab_it = tabs_tracked_.find(tab); | |
| 295 // If the tab is no longer being tracked it has already started loading, | |
| 296 // likely due to user action. Ignore the call to defer the tab. | |
| 297 // TODO(chrisha): Is it worth explicitly tracking these events as well? | |
|
chrisha
2015/05/13 21:18:02
Consider this a question... your thoughts?
| |
| 298 if (tab_it == tabs_tracked_.end()) | |
| 299 return; | |
| 300 | |
| 301 // Mark this tab as deferred, if its still being tracked. A tab should not be | |
| 302 // marked as deferred twice. | |
| 303 DCHECK(!tab_it->second); | |
| 304 tab_it->second = true; | |
| 305 ++tabs_tracked_deferred_count_; | |
| 306 ++tab_deferred_count_; | |
| 307 | |
| 308 // It is possible that the tab is loading so also look in the loading render | |
| 309 // widget host map in this case. | |
| 310 auto render_widget_host = GetRenderWidgetHost(tab); | |
| 311 auto render_widget_host_it = render_widget_hosts_loading_.find( | |
| 312 render_widget_host); | |
| 313 if (render_widget_host_it != render_widget_hosts_loading_.end()) { | |
| 314 DCHECK(!render_widget_host_it->second); | |
| 315 render_widget_host_it->second = true; | |
| 316 ++render_widget_hosts_loading_deferred_count_; | |
| 317 } | |
| 318 | |
| 319 // If this is the first tab being deferred indicate that the session has had | |
| 320 // deferred tabs. | |
| 321 if (tab_deferred_count_ == 1) { | |
| 322 // It is assumed that tabs are being deferred due to memory pressure as this | |
| 323 // is currently the only mechanism in which this can happen. | |
| 324 UMA_HISTOGRAM_ENUMERATION(kSessionRestoreActions, | |
| 325 SESSION_RESTORE_ACTIONS_UMA_DEFERRED_TABS, | |
| 326 SESSION_RESTORE_ACTIONS_UMA_MAX); | |
| 327 } | |
| 328 | |
| 329 UMA_HISTOGRAM_ENUMERATION( | |
| 330 kSessionRestoreTabActions, | |
| 331 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADING_DEFERRED, | |
| 332 SESSION_RESTORE_TAB_ACTIONS_UMA_MAX); | |
| 333 } | |
| 334 | |
| 192 void SessionRestoreStatsCollector::RemoveTab(NavigationController* tab) { | 335 void SessionRestoreStatsCollector::RemoveTab(NavigationController* tab) { |
| 336 // Stop observing this tab. | |
| 193 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | 337 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |
| 194 content::Source<WebContents>(tab->GetWebContents())); | 338 content::Source<WebContents>(tab->GetWebContents())); |
| 195 registrar_.Remove(this, content::NOTIFICATION_LOAD_STOP, | 339 registrar_.Remove(this, content::NOTIFICATION_LOAD_STOP, |
| 196 content::Source<NavigationController>(tab)); | 340 content::Source<NavigationController>(tab)); |
| 197 registrar_.Remove(this, content::NOTIFICATION_LOAD_START, | 341 registrar_.Remove(this, content::NOTIFICATION_LOAD_START, |
| 198 content::Source<NavigationController>(tab)); | 342 content::Source<NavigationController>(tab)); |
| 343 | |
| 344 // Remove the tab from the tracked_tabs map and be sure to update the count | |
| 345 // of deferred tracked tabs if necessary. | |
| 346 auto tab_it = tabs_tracked_.find(tab); | |
| 347 bool is_deferred = tab_it->second; | |
| 348 tabs_tracked_.erase(tab_it); | |
| 349 if (is_deferred) | |
| 350 --tabs_tracked_deferred_count_; | |
| 351 | |
| 352 // Keep track of the maximum number of tabs that are loading at the same time. | |
| 353 // NOTE: This counts session restore loaded tabs and *user* loaded tabs, | |
| 354 // both deferred and otherwise. This preserves the original behaviour of | |
| 355 // this metric. | |
| 199 if (render_widget_hosts_loading_.size() > max_parallel_tab_loads_) | 356 if (render_widget_hosts_loading_.size() > max_parallel_tab_loads_) |
| 200 max_parallel_tab_loads_ = render_widget_hosts_loading_.size(); | 357 max_parallel_tab_loads_ = render_widget_hosts_loading_.size(); |
| 358 | |
| 359 // Remove the tab from the loading render widget host map. If the tab is | |
| 360 // present in this map and deferred, then also update the deferred count. | |
| 201 RenderWidgetHost* render_widget_host = GetRenderWidgetHost(tab); | 361 RenderWidgetHost* render_widget_host = GetRenderWidgetHost(tab); |
| 202 render_widget_hosts_loading_.erase(render_widget_host); | 362 if (is_deferred && render_widget_hosts_loading_.erase(render_widget_host)) |
| 203 tabs_tracked_.erase(tab); | 363 --render_widget_hosts_loading_deferred_count_; |
| 204 | 364 |
| 205 // If there are no more tabs loading or being tracked, restore is done and | 365 // Calculate the number of non-deferred tracked and loading tabs. These are |
| 206 // record the time. Note that we are not yet finished, as we might still be | 366 // used to determine whether session restore has completed tab loading. |
| 207 // waiting for our first paint, which can happen after all tabs are done | 367 size_t tracked = tabs_tracked_.size() - tabs_tracked_deferred_count_; |
| 208 // loading. | 368 size_t loading = render_widget_hosts_loading_.size() - |
| 209 // TODO(georgesak): review behaviour of ForegroundTabFirstPaint. | 369 render_widget_hosts_loading_deferred_count_; |
| 210 if (tabs_tracked_.empty() && render_widget_hosts_loading_.empty()) { | 370 |
| 371 // If there are no more non-deferred tabs loading or being tracked, the active | |
| 372 // portion of restore is done so record the time. Note that stats collection | |
| 373 // is not yet finished, as there can be deferred tabs and pending paint | |
| 374 // events. | |
| 375 // NOTE: This is only concerned with tabs that are being actively tracked, | |
| 376 // and not deferred tabs. Deferred tabs are still tracked, but don't | |
| 377 // affect the AllTabsLoaded metric. | |
| 378 // TODO(georgesak): Review behaviour of ForegroundTabFirstPaint. | |
| 379 if (tracked == 0 && loading == 0) { | |
| 211 base::TimeDelta time_to_load = base::TimeTicks::Now() - restore_started_; | 380 base::TimeDelta time_to_load = base::TimeTicks::Now() - restore_started_; |
| 212 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.AllTabsLoaded", time_to_load, | 381 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.AllTabsLoaded", time_to_load, |
| 213 base::TimeDelta::FromMilliseconds(10), | 382 base::TimeDelta::FromMilliseconds(10), |
| 214 base::TimeDelta::FromSeconds(100), 100); | 383 base::TimeDelta::FromSeconds(100), 100); |
| 215 // Record a time for the number of tabs, to help track down contention. | 384 // Record a time for the number of tabs, to help track down contention. |
| 216 std::string time_for_count = | 385 std::string time_for_count = |
| 217 base::StringPrintf("SessionRestore.AllTabsLoaded_%d", tab_count_); | 386 base::StringPrintf("SessionRestore.AllTabsLoaded_%d", tab_count_); |
| 218 base::HistogramBase* counter_for_count = base::Histogram::FactoryTimeGet( | 387 base::HistogramBase* counter_for_count = base::Histogram::FactoryTimeGet( |
| 219 time_for_count, base::TimeDelta::FromMilliseconds(10), | 388 time_for_count, base::TimeDelta::FromMilliseconds(10), |
| 220 base::TimeDelta::FromSeconds(100), 100, | 389 base::TimeDelta::FromSeconds(100), 100, |
| 221 base::Histogram::kUmaTargetedHistogramFlag); | 390 base::Histogram::kUmaTargetedHistogramFlag); |
| 222 counter_for_count->AddTime(time_to_load); | 391 counter_for_count->AddTime(time_to_load); |
| 223 | 392 |
| 224 UMA_HISTOGRAM_COUNTS_100("SessionRestore.ParallelTabLoads", | 393 UMA_HISTOGRAM_COUNTS_100("SessionRestore.ParallelTabLoads", |
| 225 max_parallel_tab_loads_); | 394 max_parallel_tab_loads_); |
| 226 } | 395 } |
| 227 } | 396 } |
| 228 | 397 |
| 229 void SessionRestoreStatsCollector::RegisterForNotifications( | 398 void SessionRestoreStatsCollector::RegisterForNotifications( |
| 230 NavigationController* tab) { | 399 NavigationController* tab) { |
| 231 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, | 400 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED, |
| 232 content::Source<WebContents>(tab->GetWebContents())); | 401 content::Source<WebContents>(tab->GetWebContents())); |
| 233 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, | 402 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP, |
| 234 content::Source<NavigationController>(tab)); | 403 content::Source<NavigationController>(tab)); |
| 235 registrar_.Add(this, content::NOTIFICATION_LOAD_START, | 404 registrar_.Add(this, content::NOTIFICATION_LOAD_START, |
| 236 content::Source<NavigationController>(tab)); | 405 content::Source<NavigationController>(tab)); |
| 237 tabs_tracked_.insert(tab); | 406 // All tabs are non-deferred to start with. |
| 407 tabs_tracked_.insert(std::make_pair(tab, false)); | |
| 238 } | 408 } |
| 239 | 409 |
| 240 RenderWidgetHost* SessionRestoreStatsCollector::GetRenderWidgetHost( | 410 RenderWidgetHost* SessionRestoreStatsCollector::GetRenderWidgetHost( |
| 241 NavigationController* tab) { | 411 NavigationController* tab) { |
| 242 WebContents* web_contents = tab->GetWebContents(); | 412 WebContents* web_contents = tab->GetWebContents(); |
| 243 if (web_contents) { | 413 if (web_contents) { |
| 244 content::RenderWidgetHostView* render_widget_host_view = | 414 content::RenderWidgetHostView* render_widget_host_view = |
| 245 web_contents->GetRenderWidgetHostView(); | 415 web_contents->GetRenderWidgetHostView(); |
| 246 if (render_widget_host_view) | 416 if (render_widget_host_view) |
| 247 return render_widget_host_view->GetRenderWidgetHost(); | 417 return render_widget_host_view->GetRenderWidgetHost(); |
| 248 } | 418 } |
| 249 return nullptr; | 419 return nullptr; |
| 250 } | 420 } |
| 251 | 421 |
| 422 bool SessionRestoreStatsCollector::IsDeferred( | |
| 423 content::NavigationController* tab) const { | |
| 424 auto tab_it = tabs_tracked_.find(tab); | |
| 425 DCHECK(tab_it != tabs_tracked_.end()); | |
| 426 return tab_it->second; | |
| 427 } | |
| 428 | |
| 429 bool SessionRestoreStatsCollector::DoneTrackingNonDeferredTabs() const { | |
| 430 // If first paint hasn't been seen and tabs are being tracked for paints | |
| 431 // (ie: a first paint could yet be seen) then the stats collector is not done. | |
| 432 if (!got_first_paint_ && !render_widget_hosts_to_paint_.empty()) | |
| 433 return false; | |
| 434 | |
| 435 // No non-deferred tabs should be in the tracked map. | |
| 436 size_t tracked = tabs_tracked_.size() - tabs_tracked_deferred_count_; | |
| 437 if (tracked > 0) | |
| 438 return false; | |
| 439 | |
| 440 // No non-deferred tabs should be in the loading map. | |
| 441 size_t loading = render_widget_hosts_loading_.size() - | |
| 442 render_widget_hosts_loading_deferred_count_; | |
| 443 if (loading > 0) | |
| 444 return false; | |
| 445 | |
| 446 return true; | |
| 447 } | |
| 448 | |
| 449 bool SessionRestoreStatsCollector::DoneTracking() const { | |
| 450 // This should only be called once all non-deferred tabs have finished | |
| 451 // tracking. | |
| 452 DCHECK(DoneTrackingNonDeferredTabs()); | |
| 453 return render_widget_hosts_loading_.empty() && tabs_tracked_.empty(); | |
| 454 } | |
| 455 | |
| 252 // static | 456 // static |
| 253 SessionRestoreStatsCollector* SessionRestoreStatsCollector::shared_collector_ = | 457 SessionRestoreStatsCollector* SessionRestoreStatsCollector::shared_collector_ = |
| 254 nullptr; | 458 nullptr; |
| OLD | NEW |