| 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/chromeos/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 |
| 11 #include "ash/multi_profile_uma.h" | 11 #include "ash/multi_profile_uma.h" |
| 12 #include "ash/session/session_state_delegate.h" | 12 #include "ash/session/session_state_delegate.h" |
| 13 #include "ash/shell.h" | 13 #include "ash/shell.h" |
| 14 #include "base/bind.h" | 14 #include "base/bind.h" |
| 15 #include "base/bind_helpers.h" | 15 #include "base/bind_helpers.h" |
| 16 #include "base/command_line.h" | 16 #include "base/command_line.h" |
| 17 #include "base/memory/memory_pressure_monitor.h" | 17 #include "base/memory/memory_pressure_monitor.h" |
| 18 #include "base/metrics/field_trial.h" | 18 #include "base/metrics/field_trial.h" |
| 19 #include "base/metrics/histogram.h" | 19 #include "base/metrics/histogram.h" |
| 20 #include "base/process/process.h" | 20 #include "base/process/process.h" |
| 21 #include "base/strings/string16.h" | 21 #include "base/strings/string16.h" |
| 22 #include "base/strings/string_number_conversions.h" | 22 #include "base/strings/string_number_conversions.h" |
| 23 #include "base/strings/string_util.h" | 23 #include "base/strings/string_util.h" |
| 24 #include "base/strings/utf_string_conversions.h" | 24 #include "base/strings/utf_string_conversions.h" |
| 25 #include "base/synchronization/lock.h" | 25 #include "base/synchronization/lock.h" |
| 26 #include "base/threading/thread.h" | 26 #include "base/threading/thread.h" |
| 27 #include "build/build_config.h" | 27 #include "build/build_config.h" |
| 28 #include "chrome/browser/browser_process.h" | 28 #include "chrome/browser/browser_process.h" |
| 29 #include "chrome/browser/browser_process_platform_part_chromeos.h" | 29 #include "chrome/browser/browser_process_platform_part_chromeos.h" |
| 30 #include "chrome/browser/chromeos/memory/low_memory_observer.h" | 30 #include "chrome/browser/memory/low_memory_observer_chromeos.h" |
| 31 #include "chrome/browser/chromeos/memory/oom_memory_details.h" | 31 #include "chrome/browser/memory/oom_memory_details.h" |
| 32 #include "chrome/browser/chromeos/memory/system_memory_stats_recorder.h" | 32 #include "chrome/browser/memory/system_memory_stats_recorder.h" |
| 33 #include "chrome/browser/ui/browser.h" | 33 #include "chrome/browser/ui/browser.h" |
| 34 #include "chrome/browser/ui/browser_iterator.h" | 34 #include "chrome/browser/ui/browser_iterator.h" |
| 35 #include "chrome/browser/ui/browser_list.h" | 35 #include "chrome/browser/ui/browser_list.h" |
| 36 #include "chrome/browser/ui/host_desktop.h" | 36 #include "chrome/browser/ui/host_desktop.h" |
| 37 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" | 37 #include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" |
| 38 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 38 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 39 #include "chrome/browser/ui/tabs/tab_utils.h" | 39 #include "chrome/browser/ui/tabs/tab_utils.h" |
| 40 #include "chrome/common/chrome_constants.h" | 40 #include "chrome/common/chrome_constants.h" |
| 41 #include "chrome/common/url_constants.h" | 41 #include "chrome/common/url_constants.h" |
| 42 #include "chromeos/chromeos_switches.h" | 42 #include "chromeos/chromeos_switches.h" |
| 43 #include "content/public/browser/browser_thread.h" | 43 #include "content/public/browser/browser_thread.h" |
| 44 #include "content/public/browser/notification_service.h" | 44 #include "content/public/browser/notification_service.h" |
| 45 #include "content/public/browser/notification_types.h" | 45 #include "content/public/browser/notification_types.h" |
| 46 #include "content/public/browser/render_process_host.h" | 46 #include "content/public/browser/render_process_host.h" |
| 47 #include "content/public/browser/render_widget_host.h" | 47 #include "content/public/browser/render_widget_host.h" |
| 48 #include "content/public/browser/web_contents.h" | 48 #include "content/public/browser/web_contents.h" |
| 49 #include "content/public/browser/zygote_host_linux.h" | 49 #include "content/public/browser/zygote_host_linux.h" |
| 50 | 50 |
| 51 using base::TimeDelta; | 51 using base::TimeDelta; |
| 52 using base::TimeTicks; | 52 using base::TimeTicks; |
| 53 using content::BrowserThread; | 53 using content::BrowserThread; |
| 54 using content::WebContents; | 54 using content::WebContents; |
| 55 | 55 |
| 56 namespace chromeos { | 56 namespace memory { |
| 57 | |
| 58 namespace { | 57 namespace { |
| 59 | 58 |
| 60 // The default interval in seconds after which to adjust the oom_score_adj | 59 // The default interval in seconds after which to adjust the oom_score_adj |
| 61 // value. | 60 // value. |
| 62 const int kAdjustmentIntervalSeconds = 10; | 61 const int kAdjustmentIntervalSeconds = 10; |
| 63 | 62 |
| 64 // For each period of this length we record a statistic to indicate whether | 63 // For each period of this length we record a statistic to indicate whether |
| 65 // or not the user experienced a low memory event. If you change this interval | 64 // or not the user experienced a low memory event. If you change this interval |
| 66 // you must replace Tabs.Discard.DiscardInLastMinute with a new statistic. | 65 // you must replace Tabs.Discard.DiscardInLastMinute with a new statistic. |
| 67 const int kRecentTabDiscardIntervalSeconds = 60; | 66 const int kRecentTabDiscardIntervalSeconds = 60; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 81 int64 IdFromWebContents(WebContents* web_contents) { | 80 int64 IdFromWebContents(WebContents* web_contents) { |
| 82 return reinterpret_cast<int64>(web_contents); | 81 return reinterpret_cast<int64>(web_contents); |
| 83 } | 82 } |
| 84 | 83 |
| 85 } // namespace | 84 } // namespace |
| 86 | 85 |
| 87 //////////////////////////////////////////////////////////////////////////////// | 86 //////////////////////////////////////////////////////////////////////////////// |
| 88 // OomPriorityManager | 87 // OomPriorityManager |
| 89 | 88 |
| 90 OomPriorityManager::TabStats::TabStats() | 89 OomPriorityManager::TabStats::TabStats() |
| 91 : is_app(false), | 90 : is_app(false), |
| 92 is_reloadable_ui(false), | 91 is_reloadable_ui(false), |
| 93 is_playing_audio(false), | 92 is_playing_audio(false), |
| 94 is_pinned(false), | 93 is_pinned(false), |
| 95 is_selected(false), | 94 is_selected(false), |
| 96 is_discarded(false), | 95 is_discarded(false), |
| 97 renderer_handle(0), | 96 renderer_handle(0), |
| 98 tab_contents_id(0) { | 97 tab_contents_id(0) { |
| 99 } | 98 } |
| 100 | 99 |
| 101 OomPriorityManager::TabStats::~TabStats() { | 100 OomPriorityManager::TabStats::~TabStats() { |
| 102 } | 101 } |
| 103 | 102 |
| 104 OomPriorityManager::OomPriorityManager() | 103 OomPriorityManager::OomPriorityManager() |
| 105 : focused_tab_process_info_(std::make_pair(0, 0)), | 104 : focused_tab_process_info_(std::make_pair(0, 0)), |
| 106 discard_count_(0), | 105 discard_count_(0), |
| 107 recent_tab_discard_(false) { | 106 recent_tab_discard_(false) { |
| 108 // Use the old |LowMemoryObserver| when there is no |MemoryPressureMonitor|. | 107 // Use the old |LowMemoryObserver| when there is no |MemoryPressureMonitor|. |
| 109 if (!base::MemoryPressureMonitor::Get()) | 108 if (!base::MemoryPressureMonitor::Get()) |
| 110 low_memory_observer_.reset(new LowMemoryObserver); | 109 low_memory_observer_.reset(new LowMemoryObserver); |
| 111 | 110 |
| 112 registrar_.Add(this, | 111 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED, |
| 113 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, | 112 content::NotificationService::AllBrowserContextsAndSources()); |
| 114 content::NotificationService::AllBrowserContextsAndSources()); | 113 registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, |
| 115 registrar_.Add(this, | 114 content::NotificationService::AllBrowserContextsAndSources()); |
| 116 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, | 115 registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, |
| 117 content::NotificationService::AllBrowserContextsAndSources()); | 116 content::NotificationService::AllBrowserContextsAndSources()); |
| 118 registrar_.Add(this, | |
| 119 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, | |
| 120 content::NotificationService::AllBrowserContextsAndSources()); | |
| 121 } | 117 } |
| 122 | 118 |
| 123 OomPriorityManager::~OomPriorityManager() { | 119 OomPriorityManager::~OomPriorityManager() { |
| 124 Stop(); | 120 Stop(); |
| 125 } | 121 } |
| 126 | 122 |
| 127 void OomPriorityManager::Start() { | 123 void OomPriorityManager::Start() { |
| 128 if (!timer_.IsRunning()) { | 124 if (!timer_.IsRunning()) { |
| 129 timer_.Start(FROM_HERE, | 125 timer_.Start(FROM_HERE, TimeDelta::FromSeconds(kAdjustmentIntervalSeconds), |
| 130 TimeDelta::FromSeconds(kAdjustmentIntervalSeconds), | 126 this, &OomPriorityManager::AdjustOomPriorities); |
| 131 this, | |
| 132 &OomPriorityManager::AdjustOomPriorities); | |
| 133 } | 127 } |
| 134 if (!recent_tab_discard_timer_.IsRunning()) { | 128 if (!recent_tab_discard_timer_.IsRunning()) { |
| 135 recent_tab_discard_timer_.Start( | 129 recent_tab_discard_timer_.Start( |
| 136 FROM_HERE, | 130 FROM_HERE, TimeDelta::FromSeconds(kRecentTabDiscardIntervalSeconds), |
| 137 TimeDelta::FromSeconds(kRecentTabDiscardIntervalSeconds), | 131 this, &OomPriorityManager::RecordRecentTabDiscard); |
| 138 this, | |
| 139 &OomPriorityManager::RecordRecentTabDiscard); | |
| 140 } | 132 } |
| 141 start_time_ = TimeTicks::Now(); | 133 start_time_ = TimeTicks::Now(); |
| 142 // If a |LowMemoryObserver| exists we use the old system, otherwise we create | 134 // If a |LowMemoryObserver| exists we use the old system, otherwise we create |
| 143 // a |MemoryPressureListener| to listen for memory events. | 135 // a |MemoryPressureListener| to listen for memory events. |
| 144 if (low_memory_observer_) { | 136 if (low_memory_observer_) { |
| 145 low_memory_observer_->Start(); | 137 low_memory_observer_->Start(); |
| 146 } else { | 138 } else { |
| 147 base::MemoryPressureMonitor* monitor = base::MemoryPressureMonitor::Get(); | 139 base::MemoryPressureMonitor* monitor = base::MemoryPressureMonitor::Get(); |
| 148 if (monitor) { | 140 if (monitor) { |
| 149 memory_pressure_listener_.reset(new base::MemoryPressureListener( | 141 memory_pressure_listener_.reset( |
| 150 base::Bind(&OomPriorityManager::OnMemoryPressure, | 142 new base::MemoryPressureListener(base::Bind( |
| 151 base::Unretained(this)))); | 143 &OomPriorityManager::OnMemoryPressure, base::Unretained(this)))); |
| 152 base::MemoryPressureListener::MemoryPressureLevel level = | 144 base::MemoryPressureListener::MemoryPressureLevel level = |
| 153 monitor->GetCurrentPressureLevel(); | 145 monitor->GetCurrentPressureLevel(); |
| 154 if (level == | 146 if (level == |
| 155 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { | 147 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { |
| 156 OnMemoryPressure(level); | 148 OnMemoryPressure(level); |
| 157 } | 149 } |
| 158 } | 150 } |
| 159 } | 151 } |
| 160 } | 152 } |
| 161 | 153 |
| 162 void OomPriorityManager::Stop() { | 154 void OomPriorityManager::Stop() { |
| 163 timer_.Stop(); | 155 timer_.Stop(); |
| 164 recent_tab_discard_timer_.Stop(); | 156 recent_tab_discard_timer_.Stop(); |
| 165 if (low_memory_observer_) | 157 if (low_memory_observer_) |
| 166 low_memory_observer_->Stop(); | 158 low_memory_observer_->Stop(); |
| 167 else | 159 else |
| 168 memory_pressure_listener_.reset(); | 160 memory_pressure_listener_.reset(); |
| 169 } | 161 } |
| 170 | 162 |
| 171 std::vector<base::string16> OomPriorityManager::GetTabTitles() { | 163 std::vector<base::string16> OomPriorityManager::GetTabTitles() { |
| 172 TabStatsList stats = GetTabStatsOnUIThread(); | 164 TabStatsList stats = GetTabStatsOnUIThread(); |
| 173 base::AutoLock oom_score_autolock(oom_score_lock_); | 165 base::AutoLock oom_score_autolock(oom_score_lock_); |
| 174 std::vector<base::string16> titles; | 166 std::vector<base::string16> titles; |
| 175 titles.reserve(stats.size()); | 167 titles.reserve(stats.size()); |
| 176 TabStatsList::iterator it = stats.begin(); | 168 TabStatsList::iterator it = stats.begin(); |
| 177 for ( ; it != stats.end(); ++it) { | 169 for (; it != stats.end(); ++it) { |
| 178 base::string16 str; | 170 base::string16 str; |
| 179 str.reserve(4096); | 171 str.reserve(4096); |
| 180 int score = oom_score_map_[it->child_process_host_id]; | 172 int score = oom_score_map_[it->child_process_host_id]; |
| 181 str += base::IntToString16(score); | 173 str += base::IntToString16(score); |
| 182 str += base::ASCIIToUTF16(" - "); | 174 str += base::ASCIIToUTF16(" - "); |
| 183 str += it->title; | 175 str += it->title; |
| 184 str += base::ASCIIToUTF16(it->is_app ? " app" : ""); | 176 str += base::ASCIIToUTF16(it->is_app ? " app" : ""); |
| 185 str += base::ASCIIToUTF16(it->is_reloadable_ui ? " reloadable_ui" : ""); | 177 str += base::ASCIIToUTF16(it->is_reloadable_ui ? " reloadable_ui" : ""); |
| 186 str += base::ASCIIToUTF16(it->is_playing_audio ? " playing_audio" : ""); | 178 str += base::ASCIIToUTF16(it->is_playing_audio ? " playing_audio" : ""); |
| 187 str += base::ASCIIToUTF16(it->is_pinned ? " pinned" : ""); | 179 str += base::ASCIIToUTF16(it->is_pinned ? " pinned" : ""); |
| 188 str += base::ASCIIToUTF16(it->is_discarded ? " discarded" : ""); | 180 str += base::ASCIIToUTF16(it->is_discarded ? " discarded" : ""); |
| 189 titles.push_back(str); | 181 titles.push_back(str); |
| 190 } | 182 } |
| 191 return titles; | 183 return titles; |
| 192 } | 184 } |
| 193 | 185 |
| 194 // TODO(jamescook): This should consider tabs with references to other tabs, | 186 // TODO(jamescook): This should consider tabs with references to other tabs, |
| 195 // such as tabs created with JavaScript window.open(). We might want to | 187 // such as tabs created with JavaScript window.open(). We might want to |
| 196 // discard the entire set together, or use that in the priority computation. | 188 // discard the entire set together, or use that in the priority computation. |
| 197 bool OomPriorityManager::DiscardTab() { | 189 bool OomPriorityManager::DiscardTab() { |
| 198 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 190 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 199 TabStatsList stats = GetTabStatsOnUIThread(); | 191 TabStatsList stats = GetTabStatsOnUIThread(); |
| 200 if (stats.empty()) | 192 if (stats.empty()) |
| 201 return false; | 193 return false; |
| 202 // Loop until we find a non-discarded tab to kill. | 194 // Loop until we find a non-discarded tab to kill. |
| 203 for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin(); | 195 for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin(); |
| 204 stats_rit != stats.rend(); | 196 stats_rit != stats.rend(); ++stats_rit) { |
| 205 ++stats_rit) { | |
| 206 int64 least_important_tab_id = stats_rit->tab_contents_id; | 197 int64 least_important_tab_id = stats_rit->tab_contents_id; |
| 207 if (DiscardTabById(least_important_tab_id)) | 198 if (DiscardTabById(least_important_tab_id)) |
| 208 return true; | 199 return true; |
| 209 } | 200 } |
| 210 return false; | 201 return false; |
| 211 } | 202 } |
| 212 | 203 |
| 213 void OomPriorityManager::LogMemoryAndDiscardTab() { | 204 void OomPriorityManager::LogMemoryAndDiscardTab() { |
| 214 LogMemory("Tab Discards Memory details", | 205 LogMemory("Tab Discards Memory details", |
| 215 base::Bind(&OomPriorityManager::PurgeMemoryAndDiscardTabs)); | 206 base::Bind(&OomPriorityManager::PurgeMemoryAndDiscardTabs)); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 241 // are likely to have open. Most of the benefit is the from NTP URL. | 232 // are likely to have open. Most of the benefit is the from NTP URL. |
| 242 const char* const kReloadableUrlPrefixes[] = { | 233 const char* const kReloadableUrlPrefixes[] = { |
| 243 chrome::kChromeUIDownloadsURL, | 234 chrome::kChromeUIDownloadsURL, |
| 244 chrome::kChromeUIHistoryURL, | 235 chrome::kChromeUIHistoryURL, |
| 245 chrome::kChromeUINewTabURL, | 236 chrome::kChromeUINewTabURL, |
| 246 chrome::kChromeUISettingsURL, | 237 chrome::kChromeUISettingsURL, |
| 247 }; | 238 }; |
| 248 // Prefix-match against the table above. Use strncmp to avoid allocating | 239 // Prefix-match against the table above. Use strncmp to avoid allocating |
| 249 // memory to convert the URL prefix constants into std::strings. | 240 // memory to convert the URL prefix constants into std::strings. |
| 250 for (size_t i = 0; i < arraysize(kReloadableUrlPrefixes); ++i) { | 241 for (size_t i = 0; i < arraysize(kReloadableUrlPrefixes); ++i) { |
| 251 if (!strncmp(url.spec().c_str(), | 242 if (!strncmp(url.spec().c_str(), kReloadableUrlPrefixes[i], |
| 252 kReloadableUrlPrefixes[i], | |
| 253 strlen(kReloadableUrlPrefixes[i]))) | 243 strlen(kReloadableUrlPrefixes[i]))) |
| 254 return true; | 244 return true; |
| 255 } | 245 } |
| 256 return false; | 246 return false; |
| 257 } | 247 } |
| 258 | 248 |
| 259 bool OomPriorityManager::DiscardTabById(int64 target_web_contents_id) { | 249 bool OomPriorityManager::DiscardTabById(int64 target_web_contents_id) { |
| 260 for (chrome::BrowserIterator it; !it.done(); it.Next()) { | 250 for (chrome::BrowserIterator it; !it.done(); it.Next()) { |
| 261 Browser* browser = *it; | 251 Browser* browser = *it; |
| 262 TabStripModel* model = browser->tab_strip_model(); | 252 TabStripModel* model = browser->tab_strip_model(); |
| 263 for (int idx = 0; idx < model->count(); idx++) { | 253 for (int idx = 0; idx < model->count(); idx++) { |
| 264 // Can't discard tabs that are already discarded or active. | 254 // Can't discard tabs that are already discarded or active. |
| 265 if (model->IsTabDiscarded(idx) || (model->active_index() == idx)) | 255 if (model->IsTabDiscarded(idx) || (model->active_index() == idx)) |
| 266 continue; | 256 continue; |
| 267 WebContents* web_contents = model->GetWebContentsAt(idx); | 257 WebContents* web_contents = model->GetWebContentsAt(idx); |
| 268 int64 web_contents_id = IdFromWebContents(web_contents); | 258 int64 web_contents_id = IdFromWebContents(web_contents); |
| 269 if (web_contents_id == target_web_contents_id) { | 259 if (web_contents_id == target_web_contents_id) { |
| 270 LOG(WARNING) << "Discarding tab " << idx | 260 LOG(WARNING) << "Discarding tab " << idx << " id " |
| 271 << " id " << target_web_contents_id; | 261 << target_web_contents_id; |
| 272 // Record statistics before discarding because we want to capture the | 262 // Record statistics before discarding because we want to capture the |
| 273 // memory state that lead to the discard. | 263 // memory state that lead to the discard. |
| 274 RecordDiscardStatistics(); | 264 RecordDiscardStatistics(); |
| 275 model->DiscardWebContentsAt(idx); | 265 model->DiscardWebContentsAt(idx); |
| 276 recent_tab_discard_ = true; | 266 recent_tab_discard_ = true; |
| 277 return true; | 267 return true; |
| 278 } | 268 } |
| 279 } | 269 } |
| 280 } | 270 } |
| 281 return false; | 271 return false; |
| 282 } | 272 } |
| 283 | 273 |
| 284 void OomPriorityManager::RecordDiscardStatistics() { | 274 void OomPriorityManager::RecordDiscardStatistics() { |
| 285 // Record a raw count so we can compare to discard reloads. | 275 // Record a raw count so we can compare to discard reloads. |
| 286 discard_count_++; | 276 discard_count_++; |
| 287 UMA_HISTOGRAM_CUSTOM_COUNTS( | 277 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.DiscardCount", discard_count_, 1, |
| 288 "Tabs.Discard.DiscardCount", discard_count_, 1, 1000, 50); | 278 1000, 50); |
| 289 | 279 |
| 290 // TODO(jamescook): Maybe incorporate extension count? | 280 // TODO(jamescook): Maybe incorporate extension count? |
| 291 UMA_HISTOGRAM_CUSTOM_COUNTS( | 281 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.TabCount", GetTabCount(), 1, 100, |
| 292 "Tabs.Discard.TabCount", GetTabCount(), 1, 100, 50); | 282 50); |
| 293 // Record the discarded tab in relation to the amount of simultaneously | 283 // Record the discarded tab in relation to the amount of simultaneously |
| 294 // logged in users. | 284 // logged in users. |
| 295 ash::MultiProfileUMA::RecordDiscardedTab( | 285 ash::MultiProfileUMA::RecordDiscardedTab(ash::Shell::GetInstance() |
| 296 ash::Shell::GetInstance()->session_state_delegate()-> | 286 ->session_state_delegate() |
| 297 NumberOfLoggedInUsers()); | 287 ->NumberOfLoggedInUsers()); |
| 298 | 288 |
| 299 // TODO(jamescook): If the time stats prove too noisy, then divide up users | 289 // TODO(jamescook): If the time stats prove too noisy, then divide up users |
| 300 // based on how heavily they use Chrome using tab count as a proxy. | 290 // based on how heavily they use Chrome using tab count as a proxy. |
| 301 // Bin into <= 1, <= 2, <= 4, <= 8, etc. | 291 // Bin into <= 1, <= 2, <= 4, <= 8, etc. |
| 302 if (last_discard_time_.is_null()) { | 292 if (last_discard_time_.is_null()) { |
| 303 // This is the first discard this session. | 293 // This is the first discard this session. |
| 304 TimeDelta interval = TimeTicks::Now() - start_time_; | 294 TimeDelta interval = TimeTicks::Now() - start_time_; |
| 305 int interval_seconds = static_cast<int>(interval.InSeconds()); | 295 int interval_seconds = static_cast<int>(interval.InSeconds()); |
| 306 // Record time in seconds over an interval of approximately 1 day. | 296 // Record time in seconds over an interval of approximately 1 day. |
| 307 UMA_HISTOGRAM_CUSTOM_COUNTS( | 297 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.InitialTime2", interval_seconds, |
| 308 "Tabs.Discard.InitialTime2", interval_seconds, 1, 100000, 50); | 298 1, 100000, 50); |
| 309 } else { | 299 } else { |
| 310 // Not the first discard, so compute time since last discard. | 300 // Not the first discard, so compute time since last discard. |
| 311 TimeDelta interval = TimeTicks::Now() - last_discard_time_; | 301 TimeDelta interval = TimeTicks::Now() - last_discard_time_; |
| 312 int interval_ms = static_cast<int>(interval.InMilliseconds()); | 302 int interval_ms = static_cast<int>(interval.InMilliseconds()); |
| 313 // Record time in milliseconds over an interval of approximately 1 day. | 303 // Record time in milliseconds over an interval of approximately 1 day. |
| 314 // Start at 100 ms to get extra resolution in the target 750 ms range. | 304 // Start at 100 ms to get extra resolution in the target 750 ms range. |
| 315 UMA_HISTOGRAM_CUSTOM_COUNTS( | 305 UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.IntervalTime2", interval_ms, 100, |
| 316 "Tabs.Discard.IntervalTime2", interval_ms, 100, 100000 * 1000, 50); | 306 100000 * 1000, 50); |
| 317 } | 307 } |
| 318 // Record chromeos's concept of system memory usage at the time of the | 308 // Record chromeos's concept of system memory usage at the time of the |
| 319 // discard. | 309 // discard. |
| 320 RecordMemoryStats(RECORD_MEMORY_STATS_TAB_DISCARDED); | 310 RecordMemoryStats(RECORD_MEMORY_STATS_TAB_DISCARDED); |
| 321 | 311 |
| 322 // Set up to record the next interval. | 312 // Set up to record the next interval. |
| 323 last_discard_time_ = TimeTicks::Now(); | 313 last_discard_time_ = TimeTicks::Now(); |
| 324 } | 314 } |
| 325 | 315 |
| 326 void OomPriorityManager::RecordRecentTabDiscard() { | 316 void OomPriorityManager::RecordRecentTabDiscard() { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 349 | 339 |
| 350 int OomPriorityManager::GetTabCount() const { | 340 int OomPriorityManager::GetTabCount() const { |
| 351 int tab_count = 0; | 341 int tab_count = 0; |
| 352 for (chrome::BrowserIterator it; !it.done(); it.Next()) | 342 for (chrome::BrowserIterator it; !it.done(); it.Next()) |
| 353 tab_count += it->tab_strip_model()->count(); | 343 tab_count += it->tab_strip_model()->count(); |
| 354 return tab_count; | 344 return tab_count; |
| 355 } | 345 } |
| 356 | 346 |
| 357 // Returns true if |first| is considered less desirable to be killed | 347 // Returns true if |first| is considered less desirable to be killed |
| 358 // than |second|. | 348 // than |second|. |
| 359 bool OomPriorityManager::CompareTabStats(TabStats first, | 349 bool OomPriorityManager::CompareTabStats(TabStats first, TabStats second) { |
| 360 TabStats second) { | |
| 361 // Being currently selected is most important to protect. | 350 // Being currently selected is most important to protect. |
| 362 if (first.is_selected != second.is_selected) | 351 if (first.is_selected != second.is_selected) |
| 363 return first.is_selected; | 352 return first.is_selected; |
| 364 | 353 |
| 365 // Tab with internal web UI like NTP or Settings are good choices to discard, | 354 // Tab with internal web UI like NTP or Settings are good choices to discard, |
| 366 // so protect non-Web UI and let the other conditionals finish the sort. | 355 // so protect non-Web UI and let the other conditionals finish the sort. |
| 367 if (first.is_reloadable_ui != second.is_reloadable_ui) | 356 if (first.is_reloadable_ui != second.is_reloadable_ui) |
| 368 return !first.is_reloadable_ui; | 357 return !first.is_reloadable_ui; |
| 369 | 358 |
| 370 // Being pinned is important to protect. | 359 // Being pinned is important to protect. |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 432 base::chromeos::MemoryPressureMonitor::Get(); | 421 base::chromeos::MemoryPressureMonitor::Get(); |
| 433 if (monitor) | 422 if (monitor) |
| 434 monitor->ScheduleEarlyCheck(); | 423 monitor->ScheduleEarlyCheck(); |
| 435 } | 424 } |
| 436 break; | 425 break; |
| 437 } | 426 } |
| 438 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: { | 427 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: { |
| 439 bool visible = *content::Details<bool>(details).ptr(); | 428 bool visible = *content::Details<bool>(details).ptr(); |
| 440 if (visible) { | 429 if (visible) { |
| 441 content::RenderProcessHost* render_host = | 430 content::RenderProcessHost* render_host = |
| 442 content::Source<content::RenderWidgetHost>(source).ptr()-> | 431 content::Source<content::RenderWidgetHost>(source) |
| 443 GetProcess(); | 432 .ptr() |
| 444 focused_tab_process_info_ = std::make_pair(render_host->GetID(), | 433 ->GetProcess(); |
| 445 render_host->GetHandle()); | 434 focused_tab_process_info_ = |
| 435 std::make_pair(render_host->GetID(), render_host->GetHandle()); |
| 446 | 436 |
| 447 // If the currently focused tab already has a lower score, do not | 437 // If the currently focused tab already has a lower score, do not |
| 448 // set it. This can happen in case the newly focused tab is script | 438 // set it. This can happen in case the newly focused tab is script |
| 449 // connected to the previous tab. | 439 // connected to the previous tab. |
| 450 ProcessScoreMap::iterator it; | 440 ProcessScoreMap::iterator it; |
| 451 it = oom_score_map_.find(focused_tab_process_info_.first); | 441 it = oom_score_map_.find(focused_tab_process_info_.first); |
| 452 if (it == oom_score_map_.end() | 442 if (it == oom_score_map_.end() || |
| 453 || it->second != chrome::kLowestRendererOomScore) { | 443 it->second != chrome::kLowestRendererOomScore) { |
| 454 // By starting a timer we guarantee that the tab is focused for | 444 // By starting a timer we guarantee that the tab is focused for |
| 455 // certain amount of time. Secondly, it also does not add overhead | 445 // certain amount of time. Secondly, it also does not add overhead |
| 456 // to the tab switching time. | 446 // to the tab switching time. |
| 457 if (focus_tab_score_adjust_timer_.IsRunning()) | 447 if (focus_tab_score_adjust_timer_.IsRunning()) |
| 458 focus_tab_score_adjust_timer_.Reset(); | 448 focus_tab_score_adjust_timer_.Reset(); |
| 459 else | 449 else |
| 460 focus_tab_score_adjust_timer_.Start(FROM_HERE, | 450 focus_tab_score_adjust_timer_.Start( |
| 461 TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs), | 451 FROM_HERE, |
| 462 this, &OomPriorityManager::OnFocusTabScoreAdjustmentTimeout); | 452 TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs), |
| 453 this, &OomPriorityManager::OnFocusTabScoreAdjustmentTimeout); |
| 463 } | 454 } |
| 464 } | 455 } |
| 465 break; | 456 break; |
| 466 } | 457 } |
| 467 default: | 458 default: |
| 468 NOTREACHED() << L"Received unexpected notification"; | 459 NOTREACHED() << L"Received unexpected notification"; |
| 469 break; | 460 break; |
| 470 } | 461 } |
| 471 } | 462 } |
| 472 | 463 |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 540 browser_active = false; | 531 browser_active = false; |
| 541 } | 532 } |
| 542 // Sort the data we collected so that least desirable to be | 533 // Sort the data we collected so that least desirable to be |
| 543 // killed is first, most desirable is last. | 534 // killed is first, most desirable is last. |
| 544 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); | 535 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); |
| 545 return stats_list; | 536 return stats_list; |
| 546 } | 537 } |
| 547 | 538 |
| 548 // static | 539 // static |
| 549 std::vector<OomPriorityManager::ProcessInfo> | 540 std::vector<OomPriorityManager::ProcessInfo> |
| 550 OomPriorityManager::GetChildProcessInfos( | 541 OomPriorityManager::GetChildProcessInfos(const TabStatsList& stats_list) { |
| 551 const TabStatsList& stats_list) { | |
| 552 std::vector<ProcessInfo> process_infos; | 542 std::vector<ProcessInfo> process_infos; |
| 553 std::set<base::ProcessHandle> already_seen; | 543 std::set<base::ProcessHandle> already_seen; |
| 554 for (TabStatsList::const_iterator iterator = stats_list.begin(); | 544 for (TabStatsList::const_iterator iterator = stats_list.begin(); |
| 555 iterator != stats_list.end(); ++iterator) { | 545 iterator != stats_list.end(); ++iterator) { |
| 556 // stats_list contains entries for already-discarded tabs. If the PID | 546 // stats_list contains entries for already-discarded tabs. If the PID |
| 557 // (renderer_handle) is zero, we don't need to adjust the oom_score. | 547 // (renderer_handle) is zero, we don't need to adjust the oom_score. |
| 558 if (iterator->renderer_handle == 0) | 548 if (iterator->renderer_handle == 0) |
| 559 continue; | 549 continue; |
| 560 | 550 |
| 561 bool inserted = already_seen.insert(iterator->renderer_handle).second; | 551 bool inserted = already_seen.insert(iterator->renderer_handle).second; |
| 562 if (!inserted) { | 552 if (!inserted) { |
| 563 // We've already seen this process handle. | 553 // We've already seen this process handle. |
| 564 continue; | 554 continue; |
| 565 } | 555 } |
| 566 | 556 |
| 567 process_infos.push_back(std::make_pair( | 557 process_infos.push_back(std::make_pair(iterator->child_process_host_id, |
| 568 iterator->child_process_host_id, iterator->renderer_handle)); | 558 iterator->renderer_handle)); |
| 569 } | 559 } |
| 570 return process_infos; | 560 return process_infos; |
| 571 } | 561 } |
| 572 | 562 |
| 573 void OomPriorityManager::AdjustOomPrioritiesOnFileThread( | 563 void OomPriorityManager::AdjustOomPrioritiesOnFileThread( |
| 574 TabStatsList stats_list) { | 564 TabStatsList stats_list) { |
| 575 DCHECK_CURRENTLY_ON(BrowserThread::FILE); | 565 DCHECK_CURRENTLY_ON(BrowserThread::FILE); |
| 576 base::AutoLock oom_score_autolock(oom_score_lock_); | 566 base::AutoLock oom_score_autolock(oom_score_lock_); |
| 577 | 567 |
| 578 // Remove any duplicate PIDs. Order of the list is maintained, so each | 568 // Remove any duplicate PIDs. Order of the list is maintained, so each |
| 579 // renderer process will take on the oom_score_adj of the most important | 569 // renderer process will take on the oom_score_adj of the most important |
| 580 // (least likely to be killed) tab. | 570 // (least likely to be killed) tab. |
| 581 std::vector<ProcessInfo> process_infos = GetChildProcessInfos(stats_list); | 571 std::vector<ProcessInfo> process_infos = GetChildProcessInfos(stats_list); |
| 582 | 572 |
| 583 // Now we assign priorities based on the sorted list. We're | 573 // Now we assign priorities based on the sorted list. We're |
| 584 // assigning priorities in the range of kLowestRendererOomScore to | 574 // assigning priorities in the range of kLowestRendererOomScore to |
| 585 // kHighestRendererOomScore (defined in chrome_constants.h). | 575 // kHighestRendererOomScore (defined in chrome_constants.h). |
| 586 // oom_score_adj takes values from -1000 to 1000. Negative values | 576 // oom_score_adj takes values from -1000 to 1000. Negative values |
| 587 // are reserved for system processes, and we want to give some room | 577 // are reserved for system processes, and we want to give some room |
| 588 // below the range we're using to allow for things that want to be | 578 // below the range we're using to allow for things that want to be |
| 589 // above the renderers in priority, so the defined range gives us | 579 // above the renderers in priority, so the defined range gives us |
| 590 // some variation in priority without taking up the whole range. In | 580 // some variation in priority without taking up the whole range. In |
| 591 // the end, however, it's a pretty arbitrary range to use. Higher | 581 // the end, however, it's a pretty arbitrary range to use. Higher |
| 592 // values are more likely to be killed by the OOM killer. | 582 // values are more likely to be killed by the OOM killer. |
| 593 float priority = chrome::kLowestRendererOomScore; | 583 float priority = chrome::kLowestRendererOomScore; |
| 594 const int kPriorityRange = chrome::kHighestRendererOomScore - | 584 const int kPriorityRange = |
| 595 chrome::kLowestRendererOomScore; | 585 chrome::kHighestRendererOomScore - chrome::kLowestRendererOomScore; |
| 596 float priority_increment = | 586 float priority_increment = |
| 597 static_cast<float>(kPriorityRange) / process_infos.size(); | 587 static_cast<float>(kPriorityRange) / process_infos.size(); |
| 598 for (const auto& process_info : process_infos) { | 588 for (const auto& process_info : process_infos) { |
| 599 int score = static_cast<int>(priority + 0.5f); | 589 int score = static_cast<int>(priority + 0.5f); |
| 600 ProcessScoreMap::iterator it = | 590 ProcessScoreMap::iterator it = oom_score_map_.find(process_info.first); |
| 601 oom_score_map_.find(process_info.first); | |
| 602 // If a process has the same score as the newly calculated value, | 591 // If a process has the same score as the newly calculated value, |
| 603 // do not set it. | 592 // do not set it. |
| 604 if (it == oom_score_map_.end() || it->second != score) { | 593 if (it == oom_score_map_.end() || it->second != score) { |
| 605 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore( | 594 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore( |
| 606 process_info.second, score); | 595 process_info.second, score); |
| 607 oom_score_map_[process_info.first] = score; | 596 oom_score_map_[process_info.first] = score; |
| 608 } | 597 } |
| 609 priority += priority_increment; | 598 priority += priority_increment; |
| 610 } | 599 } |
| 611 } | 600 } |
| 612 | 601 |
| 613 void OomPriorityManager::OnMemoryPressure( | 602 void OomPriorityManager::OnMemoryPressure( |
| 614 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { | 603 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) { |
| 615 // For the moment we only do something when we reach a critical state. | 604 // For the moment we only do something when we reach a critical state. |
| 616 if (memory_pressure_level == | 605 if (memory_pressure_level == |
| 617 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { | 606 base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL) { |
| 618 LogMemoryAndDiscardTab(); | 607 LogMemoryAndDiscardTab(); |
| 619 } | 608 } |
| 620 // TODO(skuhne): If more memory pressure levels are introduced, we might | 609 // TODO(skuhne): If more memory pressure levels are introduced, we might |
| 621 // consider to call PurgeBrowserMemory() before CRITICAL is reached. | 610 // consider to call PurgeBrowserMemory() before CRITICAL is reached. |
| 622 } | 611 } |
| 623 | 612 |
| 624 } // namespace chromeos | 613 } // namespace memory |
| OLD | NEW |