Chromium Code Reviews| 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 |