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