Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(30)

Side by Side Diff: ios/chrome/browser/metrics/tab_usage_recorder.mm

Issue 2585233003: Upstream Chrome on iOS source code [2/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2013 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.
4
5 #include "ios/chrome/browser/metrics/tab_usage_recorder.h"
6
7 #include "base/metrics/histogram.h"
8 #include "ios/chrome/browser/chrome_url_constants.h"
9 #import "ios/chrome/browser/metrics/previous_session_info.h"
10 #import "ios/chrome/browser/tabs/tab.h"
11 #import "ios/web/web_state/ui/crw_web_controller.h"
12
13 const char kTabUsageHistogramPrefix[] = "Tab";
14
15 // The histogram recording the state of the tab the user switches to.
16 const char kSelectedTabHistogramName[] =
17 "Tab.StatusWhenSwitchedBackToForeground";
18
19 // The histogram to record the number of page loads before an evicted tab is
20 // selected.
21 const char kPageLoadsBeforeEvictedTabSelected[] =
22 "Tab.PageLoadsSinceLastSwitchToEvictedTab";
23
24 // Records the time it takes for an evicted tab to reload.
25 const char kEvictedTabReloadTime[] = "Tab.RestoreTime";
26
27 // Records success vs failure of an evicted tab's reload.
28 const char kEvictedTabReloadSuccessRate[] = "Tab.RestoreResult";
29
30 // Records whether or not the user switched tabs before an evicted tab finished
31 // reloading.
32 const char kDidUserWaitForEvictedTabReload[] = "Tab.RestoreUserPersistence";
33
34 // The name of the histogram that records time intervals between tab restores.
35 const char kTimeBetweenRestores[] = "Tabs.TimeBetweenRestores";
36
37 // The name of the histogram that records time intervals since the last restore.
38 const char kTimeAfterLastRestore[] = "Tabs.TimeAfterLastRestore";
39
40 // Name of histogram to record whether a memory warning had been recently
41 // received when a renderer termination occurred.
42 const char kRendererTerminationSawMemoryWarning[] =
43 "Tab.RendererTermination.RecentlyReceivedMemoryWarning";
44
45 // Name of histogram to record the number of alive renderers when a renderer
46 // termination is received.
47 const char kRendererTerminationAliveRenderers[] =
48 "Tab.RendererTermination.AliveRenderersCount";
49
50 // Name of histogram to record the number of renderers that were alive shortly
51 // before a renderer termination. This metric is being recorded in case the OS
52 // kills renderers in batches.
53 const char kRendererTerminationRecentlyAliveRenderers[] =
54 "Tab.RendererTermination.RecentlyAliveRenderersCount";
55
56 // The recently alive renderer count metric counts all renderers that were alive
57 // x seconds before a renderer termination. |kSecondsBeforeRendererTermination|
58 // specifies x.
59 const int kSecondsBeforeRendererTermination = 2;
60
61 TabUsageRecorder::TabUsageRecorder(id<TabUsageRecorderDelegate> delegate)
62 : page_loads_(0), evicted_tab_(NULL), recorder_delegate_(delegate) {
63 restore_start_time_ = base::TimeTicks::Now();
64 }
65
66 TabUsageRecorder::~TabUsageRecorder() {}
67
68 void TabUsageRecorder::InitialRestoredTabs(Tab* active_tab, NSArray* tabs) {
69 #if !defined(NDEBUG)
70 // Debugging check to ensure this is called at most once per run.
71 // Specifically, this function is called in either of two cases:
72 // 1. For a normal (not post-crash launch), during the tab model's creation.
73 // It assumes that the tab model will not be deleted and recreated during the
74 // application's lifecycle even if the app is backgrounded/foregrounded.
75 // 2. For a post-crash launch, when the session is restored. In that case,
76 // the tab model will not have been created with existing tabs, so this
77 // function will not have been called during its creation.
78 static bool kColdStartTabsRecorded = false;
79 static dispatch_once_t once = 0;
80 dispatch_once(&once, ^{
81 DCHECK(kColdStartTabsRecorded == false);
82 kColdStartTabsRecorded = true;
83 });
84 #endif
85
86 // Do not set eviction reason on active tab since it will be reloaded without
87 // being processed as a switch to the foreground tab.
88 for (Tab* tab in tabs) {
89 if (tab != active_tab) {
90 base::WeakNSObject<Tab> weak_tab(tab);
91 evicted_tabs_[weak_tab] = EVICTED_DUE_TO_COLD_START;
92 }
93 }
94 }
95
96 void TabUsageRecorder::TabCreatedForSelection(Tab* tab) {
97 tab_created_selected_ = tab;
98 }
99
100 void TabUsageRecorder::SetDelegate(id<TabUsageRecorderDelegate> delegate) {
101 recorder_delegate_.reset(delegate);
102 }
103
104 void TabUsageRecorder::RecordTabSwitched(Tab* old_tab, Tab* new_tab) {
105 // If a tab was created to be selected, and is selected shortly thereafter,
106 // it should not add its state to the "kSelectedTabHistogramName" metric.
107 // |tab_created_selected_| is reset at the first tab switch seen after it was
108 // created, regardless of whether or not it was the tab selected.
109 bool was_just_created = new_tab == tab_created_selected_;
110 tab_created_selected_ = NULL;
111
112 // Disregard reselecting the same tab, but only if the mode has not changed
113 // since the last time this tab was selected. I.e. going to incognito and
114 // back to normal mode is an event we want to track, but simply going into
115 // stack view and back out, without changing modes, isn't.
116 if (new_tab == old_tab && new_tab != mode_switch_tab_)
117 return;
118 mode_switch_tab_ = NULL;
119
120 // Disregard opening a new tab with no previous tab.
121 if (!old_tab)
122 return;
123
124 // Before knowledge of the previous tab, |old_tab|, is lost, see if it is a
125 // previously-evicted tab still reloading. If it is, record that the
126 // user did not wait for the evicted tab to finish reloading.
127 if (old_tab == evicted_tab_ && old_tab != new_tab &&
128 evicted_tab_reload_start_time_ != base::TimeTicks()) {
129 UMA_HISTOGRAM_ENUMERATION(kDidUserWaitForEvictedTabReload,
130 USER_DID_NOT_WAIT, USER_BEHAVIOR_COUNT);
131 }
132 ResetEvictedTab();
133
134 if (ShouldIgnoreTab(new_tab) || was_just_created)
135 return;
136
137 // Should never happen. Keeping the check to ensure that the prerender logic
138 // is never overlooked, should behavior at the tab_model level change.
139 DCHECK(![new_tab isPrerenderTab]);
140
141 TabStateWhenSelected tab_state = ExtractTabState(new_tab);
142 if (tab_state != IN_MEMORY) {
143 // Keep track of the current 'evicted' tab.
144 evicted_tab_ = new_tab;
145 evicted_tab_state_ = tab_state;
146 UMA_HISTOGRAM_COUNTS(kPageLoadsBeforeEvictedTabSelected, page_loads_);
147 ResetPageLoads();
148 }
149
150 UMA_HISTOGRAM_ENUMERATION(kSelectedTabHistogramName, tab_state,
151 TAB_STATE_COUNT);
152 }
153
154 void TabUsageRecorder::RecordPrimaryTabModelChange(BOOL primary_tab_model,
155 Tab* active_tab) {
156 if (primary_tab_model) {
157 // User just came back to this tab model, so record a tab selection even
158 // though the current tab was reselected.
159 if (mode_switch_tab_ == active_tab)
160 RecordTabSwitched(active_tab, active_tab);
161 } else {
162 // Keep track of the selected tab when this tab model is moved to
163 // background. This way when the tab model is moved to the foreground, and
164 // the current tab reselected, it is handled as a tab selection rather than
165 // a no-op.
166 mode_switch_tab_ = active_tab;
167 }
168 }
169
170 void TabUsageRecorder::RecordPageLoadStart(Tab* tab) {
171 if (!ShouldIgnoreTab(tab)) {
172 page_loads_++;
173 if (![[tab webController] isViewAlive]) {
174 // On the iPad, there is no notification that a tab is being re-selected
175 // after changing modes. This catches the case where the pre-incognito
176 // selected tab is selected again when leaving incognito mode.
177 if (mode_switch_tab_ == tab)
178 RecordTabSwitched(tab, tab);
179 if (evicted_tab_ == tab)
180 RecordRestoreStartTime();
181 }
182 } else {
183 // If there is a currently-evicted tab reloading, make sure it is recorded
184 // that the user did not wait for it to load.
185 if (evicted_tab_ && evicted_tab_reload_start_time_ != base::TimeTicks()) {
186 UMA_HISTOGRAM_ENUMERATION(kDidUserWaitForEvictedTabReload,
187 USER_DID_NOT_WAIT, USER_BEHAVIOR_COUNT);
188 }
189 ResetEvictedTab();
190 }
191 }
192
193 void TabUsageRecorder::RecordPageLoadDone(Tab* tab, bool success) {
194 if (!tab)
195 return;
196 if (tab == evicted_tab_) {
197 if (success) {
198 LOCAL_HISTOGRAM_TIMES(
199 kEvictedTabReloadTime,
200 base::TimeTicks::Now() - evicted_tab_reload_start_time_);
201 }
202 UMA_HISTOGRAM_ENUMERATION(kEvictedTabReloadSuccessRate,
203 success ? LOAD_SUCCESS : LOAD_FAILURE,
204 LOAD_DONE_STATE_COUNT);
205
206 UMA_HISTOGRAM_ENUMERATION(kDidUserWaitForEvictedTabReload, USER_WAITED,
207 USER_BEHAVIOR_COUNT);
208 ResetEvictedTab();
209 }
210 }
211
212 void TabUsageRecorder::RecordReload(Tab* tab) {
213 if (!ShouldIgnoreTab(tab)) {
214 page_loads_++;
215 }
216 }
217
218 void TabUsageRecorder::RendererTerminated(Tab* terminated_tab, bool visible) {
219 if (!visible) {
220 DCHECK(!TabAlreadyEvicted(terminated_tab));
221 base::WeakNSObject<Tab> weak_tab(terminated_tab);
222 evicted_tabs_[weak_tab] = EVICTED_DUE_TO_RENDERER_TERMINATION;
223 }
224 base::TimeTicks now = base::TimeTicks::Now();
225 termination_timestamps_.push_back(now);
226
227 // Log if a memory warning was seen recently.
228 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
229 BOOL saw_memory_warning =
230 [defaults boolForKey:previous_session_info_constants::
231 kDidSeeMemoryWarningShortlyBeforeTerminating];
232 UMA_HISTOGRAM_BOOLEAN(kRendererTerminationSawMemoryWarning,
233 saw_memory_warning);
234
235 // Log number of live tabs after the renderer termination. This count does not
236 // include |terminated_tab_|.
237 NSUInteger live_tabs_count = [recorder_delegate_ liveTabsCount];
238 UMA_HISTOGRAM_COUNTS_100(kRendererTerminationAliveRenderers, live_tabs_count);
239
240 // Clear |termination_timestamps_| of timestamps older than
241 // |kSecondsBeforeRendererTermination| ago.
242 base::TimeDelta seconds_before =
243 base::TimeDelta::FromSeconds(kSecondsBeforeRendererTermination);
244 base::TimeTicks timestamp_boundary = now - seconds_before;
245 while (termination_timestamps_.front() < timestamp_boundary) {
246 termination_timestamps_.pop_front();
247 }
248
249 // Log number of recently alive tabs, where recently alive is defined to mean
250 // alive within the past |kSecondsBeforeRendererTermination|.
251 NSUInteger recently_live_tabs_count =
252 live_tabs_count + termination_timestamps_.size();
253 UMA_HISTOGRAM_COUNTS_100(kRendererTerminationRecentlyAliveRenderers,
254 recently_live_tabs_count);
255 }
256
257 void TabUsageRecorder::AppDidEnterBackground() {
258 base::TimeTicks time_now = base::TimeTicks::Now();
259 LOCAL_HISTOGRAM_TIMES(kTimeAfterLastRestore, time_now - restore_start_time_);
260
261 if (evicted_tab_ && evicted_tab_reload_start_time_ != base::TimeTicks()) {
262 UMA_HISTOGRAM_ENUMERATION(kDidUserWaitForEvictedTabReload, USER_LEFT_CHROME,
263 USER_BEHAVIOR_COUNT);
264 ResetEvictedTab();
265 }
266 ClearDeletedTabs();
267 }
268
269 void TabUsageRecorder::AppWillEnterForeground() {
270 ClearDeletedTabs();
271 restore_start_time_ = base::TimeTicks::Now();
272 }
273
274 void TabUsageRecorder::ResetPageLoads() {
275 page_loads_ = 0;
276 }
277
278 int TabUsageRecorder::EvictedTabsMapSize() {
279 return evicted_tabs_.size();
280 }
281
282 void TabUsageRecorder::ResetAll() {
283 ResetEvictedTab();
284 ResetPageLoads();
285 evicted_tabs_.clear();
286 }
287
288 void TabUsageRecorder::ResetEvictedTab() {
289 evicted_tab_ = NULL;
290 evicted_tab_state_ = IN_MEMORY;
291 evicted_tab_reload_start_time_ = base::TimeTicks();
292 }
293
294 bool TabUsageRecorder::ShouldIgnoreTab(Tab* tab) {
295 // Do not count chrome:// urls to avoid data noise. For example, if they were
296 // counted, every new tab created would add noise to the page load count.
297 return [tab url].SchemeIs(kChromeUIScheme);
298 }
299
300 bool TabUsageRecorder::TabAlreadyEvicted(Tab* tab) {
301 base::WeakNSObject<Tab> weak_tab(tab);
302 auto tab_item = evicted_tabs_.find(weak_tab);
303 return tab_item != evicted_tabs_.end();
304 }
305
306 TabUsageRecorder::TabStateWhenSelected TabUsageRecorder::ExtractTabState(
307 Tab* tab) {
308 if ([[tab webController] isViewAlive])
309 return IN_MEMORY;
310
311 base::WeakNSObject<Tab> weak_tab(tab);
312 auto tab_item = evicted_tabs_.find(weak_tab);
313 if (tab_item != evicted_tabs_.end()) {
314 TabStateWhenSelected tabState = tab_item->second;
315 evicted_tabs_.erase(weak_tab);
316 return tabState;
317 }
318 return EVICTED;
319 }
320
321 void TabUsageRecorder::RecordRestoreStartTime() {
322 base::TimeTicks time_now = base::TimeTicks::Now();
323 // Record the time delta since the last eviction reload was seen.
324 LOCAL_HISTOGRAM_TIMES(kTimeBetweenRestores, time_now - restore_start_time_);
325 restore_start_time_ = time_now;
326 evicted_tab_reload_start_time_ = time_now;
327 }
328
329 void TabUsageRecorder::ClearDeletedTabs() {
330 base::WeakNSObject<Tab> empty_tab(nil);
331 auto tab_item = evicted_tabs_.find(empty_tab);
332 while (tab_item != evicted_tabs_.end()) {
333 evicted_tabs_.erase(empty_tab);
334 tab_item = evicted_tabs_.find(empty_tab);
335 }
336 }
OLDNEW
« no previous file with comments | « ios/chrome/browser/metrics/tab_usage_recorder.h ('k') | ios/chrome/browser/metrics/tab_usage_recorder_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698