Index: chrome/android/java/src/org/chromium/chrome/browser/TabUma.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/TabUma.java b/chrome/android/java/src/org/chromium/chrome/browser/TabUma.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0f4db8309dc6b50ce5bfa02c24f7c3d0d04ddfbc |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/TabUma.java |
@@ -0,0 +1,257 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser; |
+ |
+import android.os.SystemClock; |
+ |
+import org.chromium.base.metrics.RecordHistogram; |
+import org.chromium.chrome.browser.tabmodel.TabModel; |
+import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; |
+import org.chromium.net.NetError; |
+ |
+/** |
+ * Centralizes UMA data collection for Tab management. |
+ * This will drive our memory optimization efforts, specially tab restoring and |
+ * eviction. |
+ * All calls must be made from the UI thread. |
+ */ |
+public class TabUma { |
+ // TabStatus defined in tools/metrics/histograms/histograms.xml. |
+ static final int TAB_STATUS_MEMORY_RESIDENT = 0; |
+ static final int TAB_STATUS_RELOAD_EVICTED = 1; |
+ static final int TAB_STATUS_RELOAD_COLD_START_FG = 6; |
+ static final int TAB_STATUS_RELOAD_COLD_START_BG = 7; |
+ static final int TAB_STATUS_LAZY_LOAD_FOR_BG_TAB = 8; |
+ static final int TAB_STATUS_LIM = 9; |
+ |
+ // TabBackgroundLoadStatus defined in tools/metrics/histograms/histograms.xml. |
+ static final int TAB_BACKGROUND_LOAD_SHOWN = 0; |
+ static final int TAB_BACKGROUND_LOAD_LOST = 1; |
+ static final int TAB_BACKGROUND_LOAD_SKIPPED = 2; |
+ static final int TAB_BACKGROUND_LOAD_LIM = 3; |
+ |
+ // The enum values for the Tab.RestoreResult histogram. The unusual order is to |
+ // keep compatibility with the previous instance of the histogram that was using |
+ // a boolean. |
+ // |
+ // Defined in tools/metrics/histograms/histograms.xml. |
+ private static final int TAB_RESTORE_RESULT_FAILURE_OTHER = 0; |
+ private static final int TAB_RESTORE_RESULT_SUCCESS = 1; |
+ private static final int TAB_RESTORE_RESULT_FAILURE_NETWORK_CONNECTIVITY = 2; |
+ private static final int TAB_RESTORE_RESULT_COUNT = 3; |
+ |
+ // Counter of tab shows (as per onShow()) for all tabs. |
+ private static long sAllTabsShowCount = 0; |
+ |
+ private final Tab mTab; |
+ private final TabModel mTabModel; |
+ |
+ /** |
+ * State in which the tab was created. This can be used in metric accounting - e.g. to |
+ * distinguish reasons for a tab to be restored upon first display. |
+ */ |
+ public enum TabCreationState { |
+ LIVE_IN_FOREGROUND, |
+ LIVE_IN_BACKGROUND, |
+ FROZEN_ON_RESTORE, |
+ FROZEN_FOR_LAZY_LOAD |
+ } |
+ |
+ private final TabCreationState mTabCreationState; |
+ |
+ // Timestamp when this tab was last shown. |
+ private long mLastShowMillis = -1; |
+ |
+ // Timestamp of the beginning of the current tab restore. |
+ private long mRestoreStartedAtMillis = -1; |
+ |
+ /** |
+ * Constructs a new UMA tracker for a specific tab. |
+ * @param tab The tab whose metrics we want to track. |
+ * @param creationState In what state the tab was created. |
+ * @param model The tab model that contains this tab. |
+ */ |
+ public TabUma(Tab tab, TabCreationState creationState, TabModel model) { |
+ mTab = tab; |
+ mTabCreationState = creationState; |
+ mTabModel = model; |
+ } |
+ |
+ /** |
+ * Records the tab restore result into several UMA histograms. |
+ * @param succeeded Whether or not the tab restore succeeded. |
+ * @param time The time taken to perform the tab restore. |
+ * @param perceivedTime The perceived time taken to perform the tab restore. |
+ * @param errorCode The error code, on failure (as denoted by the |succeeded| parameter). |
+ */ |
+ void recordTabRestoreResult(boolean succeeded, long time, long perceivedTime, int errorCode) { |
+ if (succeeded) { |
+ RecordHistogram.recordEnumeratedHistogram( |
+ "Tab.RestoreResult", TAB_RESTORE_RESULT_SUCCESS, TAB_RESTORE_RESULT_COUNT); |
+ RecordHistogram.recordCountHistogram("Tab.RestoreTime", (int) time); |
+ RecordHistogram.recordCountHistogram("Tab.PerceivedRestoreTime", (int) perceivedTime); |
+ } else { |
+ switch (errorCode) { |
+ case NetError.ERR_INTERNET_DISCONNECTED: |
+ case NetError.ERR_NAME_RESOLUTION_FAILED: |
+ case NetError.ERR_DNS_TIMED_OUT: |
+ RecordHistogram.recordEnumeratedHistogram("Tab.RestoreResult", |
+ TAB_RESTORE_RESULT_FAILURE_NETWORK_CONNECTIVITY, |
+ TAB_RESTORE_RESULT_COUNT); |
+ break; |
+ default: |
+ RecordHistogram.recordEnumeratedHistogram("Tab.RestoreResult", |
+ TAB_RESTORE_RESULT_FAILURE_OTHER, TAB_RESTORE_RESULT_COUNT); |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Called upon tab display. |
+ * @param selectionType determines how the tab was being shown |
+ * @param previousTimestampMillis time of the previous display or creation time for the tabs |
+ * opened in background and not yet displayed |
+ */ |
+ void onShow(TabSelectionType selectionType, long previousTimestampMillis) { |
+ long now = SystemClock.elapsedRealtime(); |
+ // Do not collect the tab switching data for the first switch to a tab after the cold start |
+ // and for the tab switches that were not user-originated (e.g. the user closes the last |
+ // incognito tab and the current normal mode tab is shown). |
+ if (mLastShowMillis != -1 && selectionType == TabSelectionType.FROM_USER) { |
+ long age = now - mLastShowMillis; |
+ int rank = computeMRURank(mTab, mTabModel); |
+ RecordHistogram.recordCountHistogram("Tab.SwitchedToForegroundAge", (int) age); |
+ RecordHistogram.recordCountHistogram("Tab.SwitchedToForegroundMRURank", rank); |
+ } |
+ |
+ increaseTabShowCount(); |
+ boolean isOnBrowserStartup = sAllTabsShowCount == 1; |
+ boolean performsLazyLoad = |
+ mTabCreationState == TabCreationState.FROZEN_FOR_LAZY_LOAD && mLastShowMillis == -1; |
+ |
+ int status; |
+ if (mRestoreStartedAtMillis == -1 && !performsLazyLoad) { |
+ // The tab is *not* being restored or loaded lazily on first display. |
+ status = TAB_STATUS_MEMORY_RESIDENT; |
+ } else if (mLastShowMillis == -1) { |
+ // This is first display and the tab is being restored or loaded lazily. |
+ if (isOnBrowserStartup) { |
+ status = TAB_STATUS_RELOAD_COLD_START_FG; |
+ } else if (mTabCreationState == TabCreationState.FROZEN_ON_RESTORE) { |
+ status = TAB_STATUS_RELOAD_COLD_START_BG; |
+ } else if (mTabCreationState == TabCreationState.FROZEN_FOR_LAZY_LOAD) { |
+ status = TAB_STATUS_LAZY_LOAD_FOR_BG_TAB; |
+ } else { |
+ assert mTabCreationState == TabCreationState.LIVE_IN_FOREGROUND |
+ || mTabCreationState == TabCreationState.LIVE_IN_BACKGROUND; |
+ status = TAB_STATUS_RELOAD_EVICTED; |
+ } |
+ } else { |
+ // The tab is being restored and this is *not* the first time the tab is shown. |
+ status = TAB_STATUS_RELOAD_EVICTED; |
+ } |
+ |
+ // Record only user-visible switches to existing tabs. Do not record displays of newly |
+ // created tabs (FROM_NEW) or selections of the previous tab that happen when we close the |
+ // tab opened from intent while exiting Chrome (FROM_CLOSE). |
+ if (selectionType == TabSelectionType.FROM_USER) { |
+ RecordHistogram.recordEnumeratedHistogram( |
+ "Tab.StatusWhenSwitchedBackToForeground", status, TAB_STATUS_LIM); |
+ } |
+ |
+ // Record Tab.BackgroundLoadStatus. |
+ if (mLastShowMillis == -1) { |
+ if (mTabCreationState == TabCreationState.LIVE_IN_BACKGROUND) { |
+ if (mRestoreStartedAtMillis == -1) { |
+ RecordHistogram.recordEnumeratedHistogram("Tab.BackgroundLoadStatus", |
+ TAB_BACKGROUND_LOAD_SHOWN, TAB_BACKGROUND_LOAD_LIM); |
+ } else { |
+ RecordHistogram.recordEnumeratedHistogram("Tab.BackgroundLoadStatus", |
+ TAB_BACKGROUND_LOAD_LOST, TAB_BACKGROUND_LOAD_LIM); |
+ } |
+ } else if (mTabCreationState == TabCreationState.FROZEN_FOR_LAZY_LOAD) { |
+ assert mRestoreStartedAtMillis == -1; |
+ RecordHistogram.recordEnumeratedHistogram("Tab.BackgroundLoadStatus", |
+ TAB_BACKGROUND_LOAD_SKIPPED, TAB_BACKGROUND_LOAD_LIM); |
+ } |
+ } |
+ |
+ // Record "tab age upon first display" metrics. previousTimestampMillis is persisted through |
+ // cold starts. |
+ if (mLastShowMillis == -1 && previousTimestampMillis > 0) { |
+ if (isOnBrowserStartup) { |
+ RecordHistogram.recordCountHistogram("Tabs.ForegroundTabAgeAtStartup", |
+ (int) millisecondsToMinutes(System.currentTimeMillis() |
+ - previousTimestampMillis)); |
+ } else if (selectionType == TabSelectionType.FROM_USER) { |
+ RecordHistogram.recordCountHistogram("Tab.AgeUponRestoreFromColdStart", |
+ (int) millisecondsToMinutes(System.currentTimeMillis() |
+ - previousTimestampMillis)); |
+ } |
+ } |
+ |
+ mLastShowMillis = now; |
+ } |
+ |
+ /** Called when restore of the corresponding tab is triggered. */ |
+ void onRestoreStarted() { |
+ mRestoreStartedAtMillis = SystemClock.elapsedRealtime(); |
+ } |
+ |
+ /** Called when the correspoding tab completes a page load. */ |
+ void onLoadFinished() { |
+ // Record only tab restores that the user became aware of. If the restore is triggered |
+ // speculatively and completes before the user switches to the tab, then this case is |
+ // reflected in Tab.StatusWhenSwitchedBackToForeground metric. |
+ if (mRestoreStartedAtMillis != -1 && mLastShowMillis >= mRestoreStartedAtMillis) { |
+ long now = SystemClock.elapsedRealtime(); |
+ long restoreTime = now - mRestoreStartedAtMillis; |
+ long perceivedRestoreTime = now - mLastShowMillis; |
+ recordTabRestoreResult(true, restoreTime, perceivedRestoreTime, -1); |
+ } |
+ mRestoreStartedAtMillis = -1; |
+ } |
+ |
+ /** Called when the correspoding tab fails a page load. */ |
+ void onLoadFailed(int errorCode) { |
+ if (mRestoreStartedAtMillis != -1 && mLastShowMillis >= mRestoreStartedAtMillis) { |
+ // Load time is ignored for failed loads. |
+ recordTabRestoreResult(false, -1, -1, errorCode); |
+ } |
+ mRestoreStartedAtMillis = -1; |
+ } |
+ |
+ /** Called when the renderer of the correspoding tab crashes. */ |
+ void onRendererCrashed() { |
+ if (mRestoreStartedAtMillis != -1) { |
+ // TODO(ppi): Add a bucket in Tab.RestoreResult for restores failed due to |
+ // renderer crashes and start to track that. |
+ mRestoreStartedAtMillis = -1; |
+ } |
+ } |
+ |
+ private static void increaseTabShowCount() { |
+ sAllTabsShowCount++; |
+ } |
+ |
+ private static long millisecondsToMinutes(long msec) { |
+ return msec / 1000 / 60; |
+ } |
+ |
+ /** |
+ * @return The most recently used rank for this tab in [0 .. tabs.length - 1]. |
+ */ |
+ private static int computeMRURank(Tab tab, TabModel model) { |
+ final long tabLastShow = tab.getTabUma().mLastShowMillis; |
+ int mruRank = 0; |
+ for (int i = 0; i < model.getCount(); i++) { |
+ Tab otherTab = model.getTabAt(i); |
+ if (otherTab != tab && otherTab.getTabUma().mLastShowMillis > tabLastShow) { |
+ mruRank++; |
+ } |
+ } |
+ return mruRank; |
+ } |
+} |