Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / sessions / session_restore_stats_collector.cc
blobe333bd474571f9181e494abc8482a5baf31f2290
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/sessions/session_restore_stats_collector.h"
7 #include <string>
9 #include "base/metrics/histogram.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/time/default_tick_clock.h"
12 #include "content/public/browser/notification_service.h"
13 #include "content/public/browser/notification_types.h"
14 #include "content/public/browser/render_widget_host.h"
15 #include "content/public/browser/render_widget_host_view.h"
16 #include "content/public/browser/web_contents.h"
18 namespace {
20 using content::NavigationController;
21 using content::RenderWidgetHost;
22 using content::RenderWidgetHostView;
23 using content::Source;
24 using content::WebContents;
26 // The enumeration values stored in the "SessionRestore.Actions" histogram.
27 enum SessionRestoreActionsUma {
28 // Counts the total number of session restores that have occurred.
29 SESSION_RESTORE_ACTIONS_UMA_INITIATED = 0,
30 // Counts the number of session restores that have seen deferred tab loadings
31 // for whatever reason (almost certainly due to memory pressure).
32 SESSION_RESTORE_ACTIONS_UMA_DEFERRED_TABS = 1,
33 // The size of this enum. Must be the last entry.
34 SESSION_RESTORE_ACTIONS_UMA_MAX,
37 // Emits a SessionRestore.Actions UMA event.
38 void EmitUmaSessionRestoreActionEvent(SessionRestoreActionsUma action) {
39 UMA_HISTOGRAM_ENUMERATION("SessionRestore.Actions", action,
40 SESSION_RESTORE_ACTIONS_UMA_MAX);
43 // The enumeration of values stored in the "SessionRestore.TabActions"
44 // histogram.
45 enum SessionRestoreTabActionsUma {
46 // Incremented for each tab created in a session restore.
47 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_CREATED = 0,
48 // Incremented for each tab that session restore decides not to load.
49 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADING_DEFERRED = 1,
50 // Incremented for each tab that is successfully loaded.
51 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADED = 2,
52 // Incremented for each session-restore-deferred tab that is subsequently
53 // loaded.
54 SESSION_RESTORE_TAB_ACTIONS_UMA_DEFERRED_TAB_LOADED = 3,
55 // Incremented for each tab that starts loading due to the session restore.
56 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOAD_STARTED = 4,
57 // The size of this enum. Must be the last entry.
58 SESSION_RESTORE_TAB_ACTIONS_UMA_MAX,
61 // Emits a SessionRestore.TabActions UMA event.
62 void EmitUmaSessionRestoreTabActionEvent(SessionRestoreTabActionsUma action) {
63 UMA_HISTOGRAM_ENUMERATION("SessionRestore.TabActions", action,
64 SESSION_RESTORE_TAB_ACTIONS_UMA_MAX);
67 // Returns the RenderWidgetHostView associated with a NavigationController.
68 RenderWidgetHostView* GetRenderWidgetHostView(
69 NavigationController* tab) {
70 WebContents* web_contents = tab->GetWebContents();
71 if (web_contents)
72 return web_contents->GetRenderWidgetHostView();
73 return nullptr;
76 // Returns the RenderWidgetHost associated with a NavigationController.
77 RenderWidgetHost* GetRenderWidgetHost(
78 NavigationController* tab) {
79 content::RenderWidgetHostView* render_widget_host_view =
80 GetRenderWidgetHostView(tab);
81 if (render_widget_host_view)
82 return render_widget_host_view->GetRenderWidgetHost();
83 return nullptr;
86 // Determines if the RenderWidgetHostView associated with a given
87 // NavigationController is visible.
88 bool IsShowing(NavigationController* tab) {
89 content::RenderWidgetHostView* render_widget_host_view =
90 GetRenderWidgetHostView(tab);
91 return render_widget_host_view && render_widget_host_view->IsShowing();
94 } // namespace
96 SessionRestoreStatsCollector::TabLoaderStats::TabLoaderStats()
97 : tab_count(0u),
98 tabs_deferred(0u),
99 tabs_load_started(0u),
100 tabs_loaded(0u),
101 parallel_tab_loads(0u) {
104 SessionRestoreStatsCollector::TabState::TabState(
105 NavigationController* controller)
106 : controller(controller),
107 is_deferred(false),
108 loading_state(TAB_IS_NOT_LOADING) {
111 SessionRestoreStatsCollector::SessionRestoreStatsCollector(
112 const base::TimeTicks& restore_started,
113 scoped_ptr<StatsReportingDelegate> reporting_delegate)
114 : done_tracking_non_deferred_tabs_(false),
115 got_first_foreground_load_(false),
116 got_first_paint_(false),
117 restore_started_(restore_started),
118 waiting_for_load_tab_count_(0u),
119 loading_tab_count_(0u),
120 deferred_tab_count_(0u),
121 tick_clock_(new base::DefaultTickClock()),
122 reporting_delegate_(reporting_delegate.Pass()) {
123 this_retainer_ = this;
126 SessionRestoreStatsCollector::~SessionRestoreStatsCollector() {
129 void SessionRestoreStatsCollector::TrackTabs(
130 const std::vector<SessionRestoreDelegate::RestoredTab>& tabs) {
131 DCHECK(!done_tracking_non_deferred_tabs_);
133 // If this is the first call to TrackTabs then start observing events.
134 if (tab_loader_stats_.tab_count == 0) {
135 registrar_.Add(
136 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
137 content::NotificationService::AllSources());
140 tab_loader_stats_.tab_count += tabs.size();
141 waiting_for_load_tab_count_ += tabs.size();
142 for (const auto& tab : tabs) {
143 TabState* tab_state =
144 RegisterForNotifications(&tab.contents()->GetController());
145 // Active tabs have already started loading.
146 if (tab.is_active())
147 MarkTabAsLoading(tab_state);
151 void SessionRestoreStatsCollector::DeferTab(NavigationController* tab) {
152 TabState* tab_state = GetTabState(tab);
154 // If the tab is no longer being tracked it has already finished loading.
155 // This can occur if the user forces the tab to load before the entire session
156 // restore is over, and the TabLoader then decides it would defer loading of
157 // that tab.
158 if (!tab_state)
159 return;
161 // Mark this tab as deferred, if its still being tracked. A tab should not be
162 // marked as deferred twice.
163 DCHECK(!tab_state->is_deferred);
164 tab_state->is_deferred = true;
165 ++deferred_tab_count_;
166 ++tab_loader_stats_.tabs_deferred;
168 // A tab that didn't start loading before it was deferred is not to be
169 // actively monitored for loading.
170 if (tab_state->loading_state == TAB_IS_NOT_LOADING) {
171 DCHECK_LT(0u, waiting_for_load_tab_count_);
172 if (--waiting_for_load_tab_count_ == 0)
173 ReleaseIfDoneTracking();
176 reporting_delegate_->ReportTabDeferred();
179 void SessionRestoreStatsCollector::Observe(
180 int type,
181 const content::NotificationSource& source,
182 const content::NotificationDetails& details) {
183 switch (type) {
184 case content::NOTIFICATION_LOAD_START: {
185 // This occurs when a tab has started to load. This can be because of
186 // the tab loader (only for non-deferred tabs) or because the user clicked
187 // on the tab.
188 NavigationController* tab = Source<NavigationController>(source).ptr();
189 TabState* tab_state = GetTabState(tab);
190 MarkTabAsLoading(tab_state);
191 break;
193 case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
194 // This happens when a tab has been closed. A tab can be in any state
195 // when this occurs. Simply stop tracking the tab.
196 WebContents* web_contents = Source<WebContents>(source).ptr();
197 NavigationController* tab = &web_contents->GetController();
198 RemoveTab(tab);
199 break;
201 case content::NOTIFICATION_LOAD_STOP: {
202 // This occurs to loading tabs when they have finished loading. The tab
203 // may or may not already have painted at this point.
205 // Update the tab state and any global state as necessary.
206 NavigationController* tab = Source<NavigationController>(source).ptr();
207 TabState* tab_state = GetTabState(tab);
208 DCHECK(tab_state);
209 tab_state->loading_state = TAB_IS_LOADED;
210 DCHECK_LT(0u, loading_tab_count_);
211 --loading_tab_count_;
212 if (!tab_state->is_deferred) {
213 DCHECK_LT(0u, waiting_for_load_tab_count_);
214 --waiting_for_load_tab_count_;
217 if (tab_state->is_deferred) {
218 reporting_delegate_->ReportDeferredTabLoaded();
219 } else {
220 DCHECK(!done_tracking_non_deferred_tabs_);
221 ++tab_loader_stats_.tabs_loaded;
224 // Update statistics for foreground tabs.
225 base::TimeDelta time_to_load = tick_clock_->NowTicks() - restore_started_;
226 if (!got_first_foreground_load_ && IsShowing(tab_state->controller)) {
227 got_first_foreground_load_ = true;
228 DCHECK(!done_tracking_non_deferred_tabs_);
229 tab_loader_stats_.foreground_tab_first_loaded = time_to_load;
232 // Update statistics for all tabs, if this wasn't a deferred tab. This is
233 // done here and not in ReleaseIfDoneTracking because it is possible to
234 // wait for a paint long after all loads have completed.
235 if (!done_tracking_non_deferred_tabs_ && !tab_state->is_deferred)
236 tab_loader_stats_.non_deferred_tabs_loaded = time_to_load;
238 // By default tabs transition to being tracked for paint events after the
239 // load event has been seen. However, if the first paint event has already
240 // been seen then this is not necessary and the tab can be removed.
241 if (got_first_paint_)
242 RemoveTab(tab);
244 break;
246 case content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE: {
247 // This notification is across all tabs in the browser so notifications
248 // will arrive for tabs that the collector is not explicitly tracking.
250 // Only process this event if first paint hasn't been seen and this is a
251 // paint of a visible tab.
252 RenderWidgetHost* render_widget_host =
253 Source<RenderWidgetHost>(source).ptr();
254 if (!got_first_paint_ && render_widget_host->GetView() &&
255 render_widget_host->GetView()->IsShowing()) {
256 got_first_paint_ = true;
257 TabState* tab_state = GetTabState(render_widget_host);
258 if (tab_state) {
259 // This is a paint for a tab that is explicitly being tracked so
260 // update the statistics. Otherwise the host is for a tab that's not
261 // being tracked thus some other tab has visibility and has rendered
262 // and there's no point in tracking the time to first paint. This can
263 // happen because the user opened a different tab or restored tabs
264 // to an already existing browser and an existing tab was in the
265 // foreground.
266 base::TimeDelta time_to_paint =
267 tick_clock_->NowTicks() - restore_started_;
268 DCHECK(!done_tracking_non_deferred_tabs_);
269 tab_loader_stats_.foreground_tab_first_paint = time_to_paint;
272 // Once first paint has been observed the entire to-paint tracking
273 // mechanism is no longer needed.
274 registrar_.Remove(
275 this,
276 content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
277 content::NotificationService::AllSources());
279 // Remove any tabs that have loaded. These were only being kept around
280 // while waiting for a paint event.
281 std::vector<NavigationController*> loaded_tabs;
282 for (auto& map_entry : tabs_tracked_) {
283 TabState& tab_state = map_entry.second;
284 if (tab_state.loading_state == TAB_IS_LOADED)
285 loaded_tabs.push_back(tab_state.controller);
287 for (auto& tab : loaded_tabs)
288 RemoveTab(tab);
290 break;
292 default:
293 NOTREACHED() << "Unknown notification received:" << type;
294 break;
297 ReleaseIfDoneTracking();
300 void SessionRestoreStatsCollector::RemoveTab(NavigationController* tab) {
301 // Stop observing this tab.
302 registrar_.Remove(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
303 Source<WebContents>(tab->GetWebContents()));
304 registrar_.Remove(this, content::NOTIFICATION_LOAD_STOP,
305 Source<NavigationController>(tab));
306 registrar_.Remove(this, content::NOTIFICATION_LOAD_START,
307 Source<NavigationController>(tab));
309 auto tab_it = tabs_tracked_.find(tab);
310 DCHECK(tab_it != tabs_tracked_.end());
311 TabState& tab_state = tab_it->second;
313 // If this tab was waiting for a NOTIFICATION_LOAD_STOP event then update
314 // the loading counts.
315 if (tab_state.loading_state == TAB_IS_LOADING) {
316 DCHECK_LT(0u, loading_tab_count_);
317 --loading_tab_count_;
320 // Only non-deferred not-loading/not-loaded tabs are waiting to be loaded.
321 if (tab_state.loading_state != TAB_IS_LOADED && !tab_state.is_deferred) {
322 DCHECK_LT(0u, waiting_for_load_tab_count_);
323 // It's possible for waiting_for_load_tab_count_ to reach zero here. This
324 // function is only called from 'Observe', so the transition will be
325 // noticed there.
326 --waiting_for_load_tab_count_;
329 if (tab_state.is_deferred)
330 --deferred_tab_count_;
332 // Remove the tab from the |tracked_tabs_| map.
333 tabs_tracked_.erase(tab_it);
335 // It is possible for all restored contents to be destroyed or forcibly
336 // renavigated before a first paint has arrived. This can be detected by
337 // tabs_tracked_ containing only deferred tabs. At this point the paint
338 // mechanism can be disabled and stats collection will stop.
339 if (tabs_tracked_.size() == deferred_tab_count_ && !got_first_paint_) {
340 got_first_paint_ = true;
341 registrar_.Remove(
342 this, content::NOTIFICATION_RENDER_WIDGET_HOST_DID_UPDATE_BACKING_STORE,
343 content::NotificationService::AllSources());
347 SessionRestoreStatsCollector::TabState*
348 SessionRestoreStatsCollector::RegisterForNotifications(
349 NavigationController* tab) {
350 registrar_.Add(this, content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
351 Source<WebContents>(tab->GetWebContents()));
352 registrar_.Add(this, content::NOTIFICATION_LOAD_STOP,
353 Source<NavigationController>(tab));
354 registrar_.Add(this, content::NOTIFICATION_LOAD_START,
355 Source<NavigationController>(tab));
356 auto result = tabs_tracked_.insert(std::make_pair(tab, TabState(tab)));
357 DCHECK(result.second);
358 TabState* tab_state = &result.first->second;
359 return tab_state;
362 SessionRestoreStatsCollector::TabState*
363 SessionRestoreStatsCollector::GetTabState(NavigationController* tab) {
364 // This lookup can fail because DeferTab calls can arrive for tabs that have
365 // already loaded (user forced) and are no longer tracked.
366 auto it = tabs_tracked_.find(tab);
367 if (it == tabs_tracked_.end())
368 return nullptr;
369 return &it->second;
372 SessionRestoreStatsCollector::TabState*
373 SessionRestoreStatsCollector::GetTabState(RenderWidgetHost* tab) {
374 for (auto& pair : tabs_tracked_) {
375 auto rwh = GetRenderWidgetHost(pair.first);
376 if (rwh == tab)
377 return &pair.second;
379 // It's possible for this lookup to fail as paint events can be received for
380 // tabs that aren't being tracked.
381 return nullptr;
384 void SessionRestoreStatsCollector::MarkTabAsLoading(TabState* tab_state) {
385 // If the tab has already started or finished loading then a user navigation
386 // has caused the tab to be forcibly reloaded. This tab can be removed from
387 // observation.
388 if (tab_state->loading_state == TAB_IS_LOADED) {
389 RemoveTab(tab_state->controller);
390 return;
393 DCHECK_EQ(TAB_IS_NOT_LOADING, tab_state->loading_state);
394 if (tab_state->loading_state != TAB_IS_NOT_LOADING)
395 return;
396 tab_state->loading_state = TAB_IS_LOADING;
397 ++loading_tab_count_;
399 if (!done_tracking_non_deferred_tabs_) {
400 ++tab_loader_stats_.tabs_load_started;
401 tab_loader_stats_.parallel_tab_loads =
402 std::max(tab_loader_stats_.parallel_tab_loads, loading_tab_count_);
406 void SessionRestoreStatsCollector::ReleaseIfDoneTracking() {
407 // If non-deferred tabs are no longer being tracked then report tab loader
408 // statistics.
409 if (!done_tracking_non_deferred_tabs_ && got_first_paint_ &&
410 waiting_for_load_tab_count_ == 0) {
411 done_tracking_non_deferred_tabs_ = true;
412 reporting_delegate_->ReportTabLoaderStats(tab_loader_stats_);
415 // If tracking is completely finished then emit collected metrics and destroy
416 // this stats collector.
417 if (done_tracking_non_deferred_tabs_ && tabs_tracked_.empty())
418 this_retainer_ = nullptr;
421 SessionRestoreStatsCollector::UmaStatsReportingDelegate::
422 UmaStatsReportingDelegate()
423 : got_report_tab_deferred_(false) {
426 void SessionRestoreStatsCollector::UmaStatsReportingDelegate::
427 ReportTabLoaderStats(const TabLoaderStats& tab_loader_stats) {
428 UMA_HISTOGRAM_COUNTS_100("SessionRestore.TabCount",
429 tab_loader_stats.tab_count);
431 // Emit suffix specializations of the SessionRestore.TabCount metric.
432 if (tab_loader_stats.tabs_deferred) {
433 UMA_HISTOGRAM_COUNTS_100("SessionRestore.TabCount_MemoryPressure",
434 tab_loader_stats.tab_count);
435 UMA_HISTOGRAM_COUNTS_100("SessionRestore.TabCount_MemoryPressure_Loaded",
436 tab_loader_stats.tabs_loaded);
437 UMA_HISTOGRAM_COUNTS_100(
438 "SessionRestore.TabCount_MemoryPressure_LoadStarted",
439 tab_loader_stats.tabs_load_started);
440 UMA_HISTOGRAM_COUNTS_100("SessionRestore.TabCount_MemoryPressure_Deferred",
441 tab_loader_stats.tabs_deferred);
442 } else {
443 UMA_HISTOGRAM_COUNTS_100("SessionRestore.TabCount_NoMemoryPressure",
444 tab_loader_stats.tab_count);
445 UMA_HISTOGRAM_COUNTS_100("SessionRestore.TabCount_NoMemoryPressure_Loaded",
446 tab_loader_stats.tabs_loaded);
447 UMA_HISTOGRAM_COUNTS_100(
448 "SessionRestore.TabCount_NoMemoryPressure_LoadStarted",
449 tab_loader_stats.tabs_load_started);
452 EmitUmaSessionRestoreActionEvent(SESSION_RESTORE_ACTIONS_UMA_INITIATED);
454 for (size_t i = 0; i < tab_loader_stats.tab_count; ++i) {
455 EmitUmaSessionRestoreTabActionEvent(
456 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_CREATED);
459 for (size_t i = 0; i < tab_loader_stats.tabs_loaded; ++i) {
460 EmitUmaSessionRestoreTabActionEvent(
461 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADED);
464 for (size_t i = 0; i < tab_loader_stats.tabs_load_started; ++i) {
465 EmitUmaSessionRestoreTabActionEvent(
466 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOAD_STARTED);
469 if (!tab_loader_stats.foreground_tab_first_loaded.is_zero()) {
470 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstLoaded",
471 tab_loader_stats.foreground_tab_first_loaded,
472 base::TimeDelta::FromMilliseconds(10),
473 base::TimeDelta::FromSeconds(100), 100);
475 // Record a time for the number of tabs, to help track down contention.
476 std::string time_for_count = base::StringPrintf(
477 "SessionRestore.ForegroundTabFirstLoaded_%u",
478 static_cast<unsigned int>(tab_loader_stats.tab_count));
479 base::HistogramBase* counter_for_count = base::Histogram::FactoryTimeGet(
480 time_for_count, base::TimeDelta::FromMilliseconds(10),
481 base::TimeDelta::FromSeconds(100), 100,
482 base::Histogram::kUmaTargetedHistogramFlag);
483 counter_for_count->AddTime(tab_loader_stats.foreground_tab_first_loaded);
486 if (!tab_loader_stats.foreground_tab_first_paint.is_zero()) {
487 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.ForegroundTabFirstPaint3",
488 tab_loader_stats.foreground_tab_first_paint,
489 base::TimeDelta::FromMilliseconds(100),
490 base::TimeDelta::FromMinutes(16), 50);
492 std::string time_for_count = base::StringPrintf(
493 "SessionRestore.ForegroundTabFirstPaint3_%u",
494 static_cast<unsigned int>(tab_loader_stats.tab_count));
495 base::HistogramBase* counter_for_count = base::Histogram::FactoryTimeGet(
496 time_for_count, base::TimeDelta::FromMilliseconds(100),
497 base::TimeDelta::FromMinutes(16), 50,
498 base::Histogram::kUmaTargetedHistogramFlag);
499 counter_for_count->AddTime(tab_loader_stats.foreground_tab_first_paint);
502 if (!tab_loader_stats.non_deferred_tabs_loaded.is_zero()) {
503 UMA_HISTOGRAM_CUSTOM_TIMES("SessionRestore.AllTabsLoaded",
504 tab_loader_stats.non_deferred_tabs_loaded,
505 base::TimeDelta::FromMilliseconds(10),
506 base::TimeDelta::FromSeconds(100), 100);
508 // Record a time for the number of tabs, to help track down contention.
509 std::string time_for_count = base::StringPrintf(
510 "SessionRestore.AllTabsLoaded_%u",
511 static_cast<unsigned int>(tab_loader_stats.tab_count));
512 base::HistogramBase* counter_for_count = base::Histogram::FactoryTimeGet(
513 time_for_count, base::TimeDelta::FromMilliseconds(10),
514 base::TimeDelta::FromSeconds(100), 100,
515 base::Histogram::kUmaTargetedHistogramFlag);
516 counter_for_count->AddTime(tab_loader_stats.non_deferred_tabs_loaded);
519 UMA_HISTOGRAM_COUNTS_100("SessionRestore.ParallelTabLoads",
520 tab_loader_stats.parallel_tab_loads);
523 void SessionRestoreStatsCollector::UmaStatsReportingDelegate::
524 ReportTabDeferred() {
525 if (!got_report_tab_deferred_) {
526 got_report_tab_deferred_ = true;
527 EmitUmaSessionRestoreActionEvent(SESSION_RESTORE_ACTIONS_UMA_DEFERRED_TABS);
530 EmitUmaSessionRestoreTabActionEvent(
531 SESSION_RESTORE_TAB_ACTIONS_UMA_TAB_LOADING_DEFERRED);
534 void SessionRestoreStatsCollector::UmaStatsReportingDelegate::
535 ReportDeferredTabLoaded() {
536 EmitUmaSessionRestoreTabActionEvent(
537 SESSION_RESTORE_TAB_ACTIONS_UMA_DEFERRED_TAB_LOADED);