OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/memory/oom_priority_manager.h" | 5 #include "chrome/browser/memory/oom_priority_manager.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <set> | 8 #include <set> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
111 } | 111 } |
112 } | 112 } |
113 } | 113 } |
114 | 114 |
115 void OomPriorityManager::Stop() { | 115 void OomPriorityManager::Stop() { |
116 update_timer_.Stop(); | 116 update_timer_.Stop(); |
117 recent_tab_discard_timer_.Stop(); | 117 recent_tab_discard_timer_.Stop(); |
118 memory_pressure_listener_.reset(); | 118 memory_pressure_listener_.reset(); |
119 } | 119 } |
120 | 120 |
121 std::vector<base::string16> OomPriorityManager::GetTabTitles() { | 121 // Things we need to collect on the browser thread (because TabStripModel isn't |
122 TabStatsList stats = GetTabStatsOnUIThread(); | 122 // thread safe): |
123 std::vector<base::string16> titles; | 123 // 1) whether or not a tab is pinned |
124 titles.reserve(stats.size()); | 124 // 2) last time a tab was selected |
125 TabStatsList::iterator it = stats.begin(); | 125 // 3) is the tab currently selected |
126 for (; it != stats.end(); ++it) { | 126 TabStatsList OomPriorityManager::GetTabStats() { |
127 base::string16 str; | 127 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
128 str.reserve(4096); | 128 TabStatsList stats_list; |
129 #if defined(OS_CHROMEOS) | 129 stats_list.reserve(32); // 99% of users have < 30 tabs open |
130 int score = delegate_->GetOomScore(it->child_process_host_id); | 130 |
131 str += base::IntToString16(score); | 131 // We go through each window to get all the tabs. Depending on the platform, |
132 str += base::ASCIIToUTF16(" - "); | 132 // windows are either native or ash or both. We want to make sure to go |
133 #endif | 133 // through them all, starting with the active window first (we use |
134 str += it->title; | 134 // chrome::GetActiveDesktop to get the current used type). |
135 str += base::ASCIIToUTF16(it->is_app ? " app" : ""); | 135 AddTabStats(BrowserList::GetInstance(chrome::GetActiveDesktop()), true, |
136 str += base::ASCIIToUTF16(it->is_internal_page ? " internal_page" : ""); | 136 &stats_list); |
137 str += base::ASCIIToUTF16(it->is_playing_audio ? " playing_audio" : ""); | 137 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_NATIVE) { |
138 str += base::ASCIIToUTF16(it->is_pinned ? " pinned" : ""); | 138 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE), |
139 str += base::ASCIIToUTF16(it->is_discarded ? " discarded" : ""); | 139 false, &stats_list); |
140 titles.push_back(str); | 140 } else if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH) { |
141 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH), false, | |
142 &stats_list); | |
141 } | 143 } |
142 return titles; | 144 |
145 // Sort the data we collected so that least desirable to be | |
146 // killed is first, most desirable is last. | |
147 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); | |
148 return stats_list; | |
143 } | 149 } |
144 | 150 |
145 // TODO(jamescook): This should consider tabs with references to other tabs, | 151 // TODO(jamescook): This should consider tabs with references to other tabs, |
146 // such as tabs created with JavaScript window.open(). We might want to | 152 // such as tabs created with JavaScript window.open(). We might want to |
147 // discard the entire set together, or use that in the priority computation. | 153 // discard the entire set together, or use that in the priority computation. |
148 bool OomPriorityManager::DiscardTab() { | 154 bool OomPriorityManager::DiscardTab() { |
149 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 155 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
150 TabStatsList stats = GetTabStatsOnUIThread(); | 156 TabStatsList stats = GetTabStats(); |
151 if (stats.empty()) | 157 if (stats.empty()) |
152 return false; | 158 return false; |
153 // Loop until we find a non-discarded tab to kill. | 159 // Loop until we find a non-discarded tab to kill. |
154 for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin(); | 160 for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin(); |
155 stats_rit != stats.rend(); ++stats_rit) { | 161 stats_rit != stats.rend(); ++stats_rit) { |
156 int64 least_important_tab_id = stats_rit->tab_contents_id; | 162 int64 least_important_tab_id = stats_rit->tab_contents_id; |
157 if (DiscardTabById(least_important_tab_id)) | 163 if (DiscardTabById(least_important_tab_id)) |
158 return true; | 164 return true; |
159 } | 165 } |
160 return false; | 166 return false; |
161 } | 167 } |
162 | 168 |
169 bool OomPriorityManager::DiscardTabById(int64 target_web_contents_id) { | |
170 for (chrome::BrowserIterator it; !it.done(); it.Next()) { | |
171 Browser* browser = *it; | |
172 TabStripModel* model = browser->tab_strip_model(); | |
173 for (int idx = 0; idx < model->count(); idx++) { | |
174 // Can't discard tabs that are already discarded or active. | |
175 if (model->IsTabDiscarded(idx) || (model->active_index() == idx)) | |
176 continue; | |
177 WebContents* web_contents = model->GetWebContentsAt(idx); | |
178 int64 web_contents_id = IdFromWebContents(web_contents); | |
179 if (web_contents_id == target_web_contents_id) { | |
180 LOG(WARNING) << "Discarding tab " << idx << " id " | |
sky
2015/07/21 22:57:10
I see you're just moving code, but I'm surprised w
proberge
2015/07/22 14:21:06
I tried demoting it to LOG(INFO) but am getting a
sky
2015/07/22 15:29:41
You should use VLOG
proberge
2015/07/22 18:05:29
Thanks! Done.
| |
181 << target_web_contents_id; | |
182 // Record statistics before discarding because we want to capture the | |
183 // memory state that lead to the discard. | |
184 RecordDiscardStatistics(); | |
185 model->DiscardWebContentsAt(idx); | |
186 recent_tab_discard_ = true; | |
187 return true; | |
188 } | |
189 } | |
190 } | |
191 return false; | |
192 } | |
193 | |
163 void OomPriorityManager::LogMemoryAndDiscardTab() { | 194 void OomPriorityManager::LogMemoryAndDiscardTab() { |
164 LogMemory("Tab Discards Memory details", | 195 LogMemory("Tab Discards Memory details", |
165 base::Bind(&OomPriorityManager::PurgeMemoryAndDiscardTab)); | 196 base::Bind(&OomPriorityManager::PurgeMemoryAndDiscardTab)); |
166 } | 197 } |
167 | 198 |
168 void OomPriorityManager::LogMemory(const std::string& title, | 199 void OomPriorityManager::LogMemory(const std::string& title, |
169 const base::Closure& callback) { | 200 const base::Closure& callback) { |
170 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 201 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
171 OomMemoryDetails::Log(title, callback); | 202 OomMemoryDetails::Log(title, callback); |
172 } | 203 } |
(...skipping 23 matching lines...) Expand all Loading... | |
196 // Prefix-match against the table above. Use strncmp to avoid allocating | 227 // Prefix-match against the table above. Use strncmp to avoid allocating |
197 // memory to convert the URL prefix constants into std::strings. | 228 // memory to convert the URL prefix constants into std::strings. |
198 for (size_t i = 0; i < arraysize(kInternalPagePrefixes); ++i) { | 229 for (size_t i = 0; i < arraysize(kInternalPagePrefixes); ++i) { |
199 if (!strncmp(url.spec().c_str(), kInternalPagePrefixes[i], | 230 if (!strncmp(url.spec().c_str(), kInternalPagePrefixes[i], |
200 strlen(kInternalPagePrefixes[i]))) | 231 strlen(kInternalPagePrefixes[i]))) |
201 return true; | 232 return true; |
202 } | 233 } |
203 return false; | 234 return false; |
204 } | 235 } |
205 | 236 |
206 bool OomPriorityManager::DiscardTabById(int64 target_web_contents_id) { | |
207 for (chrome::BrowserIterator it; !it.done(); it.Next()) { | |
208 Browser* browser = *it; | |
209 TabStripModel* model = browser->tab_strip_model(); | |
210 for (int idx = 0; idx < model->count(); idx++) { | |
211 // Can't discard tabs that are already discarded or active. | |
212 if (model->IsTabDiscarded(idx) || (model->active_index() == idx)) | |
213 continue; | |
214 WebContents* web_contents = model->GetWebContentsAt(idx); | |
215 int64 web_contents_id = IdFromWebContents(web_contents); | |
216 if (web_contents_id == target_web_contents_id) { | |
217 LOG(WARNING) << "Discarding tab " << idx << " id " | |
218 << target_web_contents_id; | |
219 // Record statistics before discarding because we want to capture the | |
220 // memory state that lead to the discard. | |
221 RecordDiscardStatistics(); | |
222 model->DiscardWebContentsAt(idx); | |
223 recent_tab_discard_ = true; | |
224 return true; | |
225 } | |
226 } | |
227 } | |
228 return false; | |
229 } | |
230 | |
231 void OomPriorityManager::RecordDiscardStatistics() { | 237 void OomPriorityManager::RecordDiscardStatistics() { |
232 // Record a raw count so we can compare to discard reloads. | 238 // Record a raw count so we can compare to discard reloads. |
233 discard_count_++; | 239 discard_count_++; |
234 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.DiscardCount", discard_count_, 1, | 240 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.DiscardCount", discard_count_, 1, |
235 1000, 50); | 241 1000, 50); |
236 | 242 |
237 // TODO(jamescook): Maybe incorporate extension count? | 243 // TODO(jamescook): Maybe incorporate extension count? |
238 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.TabCount", GetTabCount(), 1, 100, | 244 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.TabCount", GetTabCount(), 1, 100, |
239 50); | 245 50); |
240 #if defined(OS_CHROMEOS) | 246 #if defined(OS_CHROMEOS) |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
355 // We were probably suspended, move our event timers forward in time so | 361 // We were probably suspended, move our event timers forward in time so |
356 // when we subtract them out later we are counting "uptime". | 362 // when we subtract them out later we are counting "uptime". |
357 start_time_ += suspend_time; | 363 start_time_ += suspend_time; |
358 if (!last_discard_time_.is_null()) | 364 if (!last_discard_time_.is_null()) |
359 last_discard_time_ += suspend_time; | 365 last_discard_time_ += suspend_time; |
360 } | 366 } |
361 } | 367 } |
362 last_adjust_time_ = TimeTicks::Now(); | 368 last_adjust_time_ = TimeTicks::Now(); |
363 | 369 |
364 #if defined(OS_CHROMEOS) | 370 #if defined(OS_CHROMEOS) |
365 TabStatsList stats_list = GetTabStatsOnUIThread(); | 371 TabStatsList stats_list = GetTabStats(); |
366 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj. | 372 // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj. |
367 delegate_->AdjustOomPriorities(stats_list); | 373 delegate_->AdjustOomPriorities(stats_list); |
368 #endif | 374 #endif |
369 } | 375 } |
370 | 376 |
371 // Things we need to collect on the browser thread (because TabStripModel isn't | |
372 // thread safe): | |
373 // 1) whether or not a tab is pinned | |
374 // 2) last time a tab was selected | |
375 // 3) is the tab currently selected | |
376 TabStatsList OomPriorityManager::GetTabStatsOnUIThread() { | |
377 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
378 TabStatsList stats_list; | |
379 stats_list.reserve(32); // 99% of users have < 30 tabs open | |
380 | |
381 // We go through each window to get all the tabs. Depending on the platform, | |
382 // windows are either native or ash or both. We want to make sure to go | |
383 // through them all, starting with the active window first (we use | |
384 // chrome::GetActiveDesktop to get the current used type). | |
385 AddTabStats(BrowserList::GetInstance(chrome::GetActiveDesktop()), true, | |
386 &stats_list); | |
387 if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_NATIVE) { | |
388 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE), | |
389 false, &stats_list); | |
390 } else if (chrome::GetActiveDesktop() != chrome::HOST_DESKTOP_TYPE_ASH) { | |
391 AddTabStats(BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH), false, | |
392 &stats_list); | |
393 } | |
394 | |
395 // Sort the data we collected so that least desirable to be | |
396 // killed is first, most desirable is last. | |
397 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); | |
398 return stats_list; | |
399 } | |
400 | |
401 void OomPriorityManager::AddTabStats(BrowserList* browser_list, | 377 void OomPriorityManager::AddTabStats(BrowserList* browser_list, |
402 bool active_desktop, | 378 bool active_desktop, |
403 TabStatsList* stats_list) { | 379 TabStatsList* stats_list) { |
404 // If it's the active desktop, the first window will be the active one. | 380 // If it's the active desktop, the first window will be the active one. |
405 // Otherwise, we assume no active windows. | 381 // Otherwise, we assume no active windows. |
406 bool browser_active = active_desktop; | 382 bool browser_active = active_desktop; |
407 for (BrowserList::const_reverse_iterator browser_iterator = | 383 for (BrowserList::const_reverse_iterator browser_iterator = |
408 browser_list->begin_last_active(); | 384 browser_list->begin_last_active(); |
409 browser_iterator != browser_list->end_last_active(); | 385 browser_iterator != browser_list->end_last_active(); |
410 ++browser_iterator) { | 386 ++browser_iterator) { |
411 Browser* browser = *browser_iterator; | 387 Browser* browser = *browser_iterator; |
412 bool is_browser_for_app = browser->is_app(); | 388 bool is_browser_for_app = browser->is_app(); |
413 const TabStripModel* model = browser->tab_strip_model(); | 389 const TabStripModel* model = browser->tab_strip_model(); |
414 for (int i = 0; i < model->count(); i++) { | 390 for (int i = 0; i < model->count(); i++) { |
415 WebContents* contents = model->GetWebContentsAt(i); | 391 WebContents* contents = model->GetWebContentsAt(i); |
416 if (!contents->IsCrashed()) { | 392 if (!contents->IsCrashed()) { |
417 TabStats stats; | 393 TabStats stats; |
418 stats.is_app = is_browser_for_app; | 394 stats.is_app = is_browser_for_app; |
419 stats.is_internal_page = | 395 stats.is_internal_page = |
420 IsInternalPage(contents->GetLastCommittedURL()); | 396 IsInternalPage(contents->GetLastCommittedURL()); |
421 stats.is_playing_audio = chrome::IsPlayingAudio(contents); | 397 stats.is_playing_audio = chrome::IsPlayingAudio(contents); |
422 stats.is_pinned = model->IsTabPinned(i); | 398 stats.is_pinned = model->IsTabPinned(i); |
423 stats.is_selected = browser_active && model->IsTabSelected(i); | 399 stats.is_selected = browser_active && model->IsTabSelected(i); |
424 stats.is_discarded = model->IsTabDiscarded(i); | 400 stats.is_discarded = model->IsTabDiscarded(i); |
425 stats.last_active = contents->GetLastActiveTime(); | 401 stats.last_active = contents->GetLastActiveTime(); |
426 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); | 402 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); |
427 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); | 403 stats.child_process_host_id = contents->GetRenderProcessHost()->GetID(); |
404 #if defined(OS_CHROMEOS) | |
405 stats.oom_score = delegate_->GetOomScore(stats.child_process_host_id); | |
406 #endif | |
428 stats.title = contents->GetTitle(); | 407 stats.title = contents->GetTitle(); |
429 stats.tab_contents_id = IdFromWebContents(contents); | 408 stats.tab_contents_id = IdFromWebContents(contents); |
430 stats_list->push_back(stats); | 409 stats_list->push_back(stats); |
431 } | 410 } |
432 } | 411 } |
433 // We process the active browser window in the first iteration. | 412 // We process the active browser window in the first iteration. |
434 browser_active = false; | 413 browser_active = false; |
435 } | 414 } |
436 } | 415 } |
437 | 416 |
438 void OomPriorityManager::OnMemoryPressure( | 417 void OomPriorityManager::OnMemoryPressure( |
439 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { | 418 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { |
440 // For the moment we only do something when we reach a critical state. | 419 // For the moment we only do something when we reach a critical state. |
441 if (memory_pressure_level == | 420 if (memory_pressure_level == |
442 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { | 421 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { |
443 LogMemoryAndDiscardTab(); | 422 LogMemoryAndDiscardTab(); |
444 } | 423 } |
445 // TODO(skuhne): If more memory pressure levels are introduced, we might | 424 // TODO(skuhne): If more memory pressure levels are introduced, we might |
446 // consider to call PurgeBrowserMemory() before CRITICAL is reached. | 425 // consider to call PurgeBrowserMemory() before CRITICAL is reached. |
447 } | 426 } |
448 | 427 |
449 } // namespace memory | 428 } // namespace memory |
OLD | NEW |