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

Side by Side Diff: chrome/browser/oom_priority_manager.cc

Issue 7983042: CrOS: Add Discard Tab command link to about:discards (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: revert tab_strip_model.h Created 9 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/oom_priority_manager.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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/oom_priority_manager.h" 5 #include "chrome/browser/oom_priority_manager.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/process.h" 10 #include "base/process.h"
11 #include "base/process_util.h" 11 #include "base/process_util.h"
12 #include "base/string_number_conversions.h"
12 #include "base/string16.h" 13 #include "base/string16.h"
13 #include "base/synchronization/lock.h" 14 #include "base/synchronization/lock.h"
14 #include "base/threading/thread.h" 15 #include "base/threading/thread.h"
15 #include "base/timer.h" 16 #include "base/timer.h"
17 #include "base/utf_string_conversions.h"
16 #include "build/build_config.h" 18 #include "build/build_config.h"
17 #include "chrome/browser/tabs/tab_strip_model.h" 19 #include "chrome/browser/tabs/tab_strip_model.h"
18 #include "chrome/browser/ui/browser_list.h" 20 #include "chrome/browser/ui/browser_list.h"
19 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" 21 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
20 #include "chrome/common/chrome_constants.h" 22 #include "chrome/common/chrome_constants.h"
21 #include "content/browser/browser_thread.h" 23 #include "content/browser/browser_thread.h"
22 #include "content/browser/renderer_host/render_process_host.h" 24 #include "content/browser/renderer_host/render_process_host.h"
23 #include "content/browser/renderer_host/render_widget_host.h" 25 #include "content/browser/renderer_host/render_widget_host.h"
24 #include "content/browser/tab_contents/tab_contents.h" 26 #include "content/browser/tab_contents/tab_contents.h"
25 #include "content/browser/zygote_host_linux.h" 27 #include "content/browser/zygote_host_linux.h"
28 #include "content/common/content_notification_types.h"
26 #include "content/common/notification_service.h" 29 #include "content/common/notification_service.h"
27 30
28 #if !defined(OS_CHROMEOS) 31 #if !defined(OS_CHROMEOS)
29 #error This file only meant to be compiled on ChromeOS 32 #error This file only meant to be compiled on ChromeOS
30 #endif 33 #endif
31 34
32 using base::TimeDelta; 35 using base::TimeDelta;
33 using base::TimeTicks; 36 using base::TimeTicks;
34 using base::ProcessHandle; 37 using base::ProcessHandle;
35 using base::ProcessMetrics; 38 using base::ProcessMetrics;
36 39
40 namespace {
41
42 // Returns a unique ID for a TabContents. Do not cast back to a pointer, as
43 // the TabContents could be deleted if the user closed the tab.
44 int64 IdFromTabContents(TabContents* tab_contents) {
45 return reinterpret_cast<int64>(tab_contents);
46 }
47
48 } // namespace
49
37 namespace browser { 50 namespace browser {
38 51
39 // The default interval in seconds after which to adjust the oom_score_adj 52 // The default interval in seconds after which to adjust the oom_score_adj
40 // value. 53 // value.
41 #define ADJUSTMENT_INTERVAL_SECONDS 10 54 #define ADJUSTMENT_INTERVAL_SECONDS 10
42 55
43 // The default interval in minutes for considering activation times
44 // "equal".
45 #define BUCKET_INTERVAL_MINUTES 10
46
47 // The default interval in milliseconds to wait before setting the score of 56 // The default interval in milliseconds to wait before setting the score of
48 // currently focused tab. 57 // currently focused tab.
49 #define FOCUSED_TAB_SCORE_ADJUST_INTERVAL_MS 500 58 #define FOCUSED_TAB_SCORE_ADJUST_INTERVAL_MS 500
50 59
51 OomPriorityManager::RendererStats::RendererStats() 60 OomPriorityManager::TabStats::TabStats()
52 : is_pinned(false), 61 : is_pinned(false),
53 is_selected(false), 62 is_selected(false),
54 memory_used(0), 63 renderer_handle(0),
55 renderer_handle(0) { 64 tab_contents_id(0) {
56 } 65 }
57 66
58 OomPriorityManager::RendererStats::~RendererStats() { 67 OomPriorityManager::TabStats::~TabStats() {
59 } 68 }
60 69
61 OomPriorityManager::OomPriorityManager() 70 OomPriorityManager::OomPriorityManager()
62 : focused_tab_pid_(0) { 71 : focused_tab_pid_(0) {
63 renderer_stats_.reserve(32); // 99% of users have < 30 tabs open
64 registrar_.Add(this, 72 registrar_.Add(this,
65 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, 73 content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
66 NotificationService::AllSources()); 74 NotificationService::AllSources());
67 registrar_.Add(this, 75 registrar_.Add(this,
68 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, 76 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
69 NotificationService::AllSources()); 77 NotificationService::AllSources());
70 registrar_.Add(this, 78 registrar_.Add(this,
71 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, 79 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
72 NotificationService::AllSources()); 80 NotificationService::AllSources());
73 } 81 }
74 82
75 OomPriorityManager::~OomPriorityManager() { 83 OomPriorityManager::~OomPriorityManager() {
76 Stop(); 84 Stop();
77 } 85 }
78 86
79 void OomPriorityManager::Start() { 87 void OomPriorityManager::Start() {
80 if (!timer_.IsRunning()) { 88 if (!timer_.IsRunning()) {
81 timer_.Start(FROM_HERE, 89 timer_.Start(FROM_HERE,
82 TimeDelta::FromSeconds(ADJUSTMENT_INTERVAL_SECONDS), 90 TimeDelta::FromSeconds(ADJUSTMENT_INTERVAL_SECONDS),
83 this, 91 this,
84 &OomPriorityManager::AdjustOomPriorities); 92 &OomPriorityManager::AdjustOomPriorities);
85 } 93 }
86 } 94 }
87 95
88 void OomPriorityManager::Stop() { 96 void OomPriorityManager::Stop() {
89 timer_.Stop(); 97 timer_.Stop();
90 } 98 }
91 99
92 std::vector<string16> OomPriorityManager::GetTabTitles() { 100 std::vector<string16> OomPriorityManager::GetTabTitles() {
93 base::AutoLock renderer_stats_autolock(renderer_stats_lock_); 101 TabStatsList stats = GetTabStatsOnUIThread();
102 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
94 std::vector<string16> titles; 103 std::vector<string16> titles;
95 titles.reserve(renderer_stats_.size()); 104 titles.reserve(stats.size());
96 StatsList::iterator it = renderer_stats_.begin(); 105 TabStatsList::iterator it = stats.begin();
97 for ( ; it != renderer_stats_.end(); ++it) { 106 for ( ; it != stats.end(); ++it) {
98 titles.push_back(it->title); 107 string16 str = it->title;
108 str += ASCIIToUTF16(" (");
109 int score = pid_to_oom_score_[it->renderer_handle];
110 str += base::IntToString16(score);
111 str += ASCIIToUTF16(")");
112 titles.push_back(str);
99 } 113 }
100 return titles; 114 return titles;
101 } 115 }
102 116
117 void OomPriorityManager::DiscardTab() {
118 TabStatsList stats = GetTabStatsOnUIThread();
119 if (stats.empty())
120 return;
121 std::sort(stats.begin(), stats.end(), CompareTabStats);
122 TabStatsList::const_reverse_iterator rit = stats.rbegin();
123 int64 least_important_tab_id = rit->tab_contents_id;
124 for (BrowserList::const_iterator browser_iterator = BrowserList::begin();
125 browser_iterator != BrowserList::end(); ++browser_iterator) {
126 Browser* browser = *browser_iterator;
127 TabStripModel* model = browser->tabstrip_model();
128 for (int idx = 0; idx < model->count(); idx++) {
129 TabContents* tab_contents = model->GetTabContentsAt(idx)->tab_contents();
130 int64 tab_contents_id = IdFromTabContents(tab_contents);
131 if (tab_contents_id == least_important_tab_id) {
132 model->CloseTabContentsAt(idx,
133 TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
134 }
135 }
136 }
137 }
138
103 // Returns true if |first| is considered less desirable to be killed 139 // Returns true if |first| is considered less desirable to be killed
104 // than |second|. 140 // than |second|.
105 bool OomPriorityManager::CompareRendererStats(RendererStats first, 141 bool OomPriorityManager::CompareTabStats(TabStats first,
106 RendererStats second) { 142 TabStats second) {
107 // The size of the slop in comparing activation times. [This is
108 // allocated here to avoid static initialization at startup time.]
109 static const int64 kTimeBucketInterval =
110 TimeDelta::FromMinutes(BUCKET_INTERVAL_MINUTES).ToInternalValue();
111
112 // Being currently selected is most important. 143 // Being currently selected is most important.
113 if (first.is_selected != second.is_selected) 144 if (first.is_selected != second.is_selected)
114 return first.is_selected == true; 145 return first.is_selected == true;
115 146
116 // Being pinned is second most important. 147 // Being pinned is second most important.
117 if (first.is_pinned != second.is_pinned) 148 if (first.is_pinned != second.is_pinned)
118 return first.is_pinned == true; 149 return first.is_pinned == true;
119 150
120 // We want to be a little "fuzzy" when we compare these, because 151 // Being more recently selected is more important.
121 // it's not really possible for the times to be identical, but if 152 return first.last_selected > second.last_selected;
122 // the user selected two tabs at about the same time, we still want
123 // to take the one that uses more memory.
124 if (abs((first.last_selected - second.last_selected).ToInternalValue()) <
125 kTimeBucketInterval)
126 return first.last_selected > second.last_selected;
127
128 return first.memory_used < second.memory_used;
129 } 153 }
130 154
131 void OomPriorityManager::AdjustFocusedTabScore() { 155 void OomPriorityManager::AdjustFocusedTabScoreOnFileThread() {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
132 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); 157 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
133 ZygoteHost::GetInstance()->AdjustRendererOOMScore( 158 ZygoteHost::GetInstance()->AdjustRendererOOMScore(
134 focused_tab_pid_, chrome::kLowestRendererOomScore); 159 focused_tab_pid_, chrome::kLowestRendererOomScore);
135 pid_to_oom_score_[focused_tab_pid_] = chrome::kLowestRendererOomScore; 160 pid_to_oom_score_[focused_tab_pid_] = chrome::kLowestRendererOomScore;
136 } 161 }
137 162
138 void OomPriorityManager::OnFocusTabScoreAdjustmentTimeout() { 163 void OomPriorityManager::OnFocusTabScoreAdjustmentTimeout() {
139 BrowserThread::PostTask( 164 BrowserThread::PostTask(
140 BrowserThread::FILE, FROM_HERE, 165 BrowserThread::FILE, FROM_HERE,
141 NewRunnableMethod(this, &OomPriorityManager::AdjustFocusedTabScore)); 166 NewRunnableMethod(
167 this, &OomPriorityManager::AdjustFocusedTabScoreOnFileThread));
142 } 168 }
143 169
144 void OomPriorityManager::Observe(int type, const NotificationSource& source, 170 void OomPriorityManager::Observe(int type, const NotificationSource& source,
145 const NotificationDetails& details) { 171 const NotificationDetails& details) {
146 base::ProcessHandle handle = 0; 172 base::ProcessHandle handle = 0;
147 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); 173 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
148 switch (type) { 174 switch (type) {
149 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: 175 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
150 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: { 176 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
151 handle = Source<RenderProcessHost>(source)->GetHandle(); 177 handle = Source<RenderProcessHost>(source)->GetHandle();
152 pid_to_oom_score_.erase(handle); 178 pid_to_oom_score_.erase(handle);
153 break; 179 break;
154 } 180 }
155 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: { 181 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: {
(...skipping 30 matching lines...) Expand all
186 212
187 // Here we collect most of the information we need to sort the 213 // Here we collect most of the information we need to sort the
188 // existing renderers in priority order, and hand out oom_score_adj 214 // existing renderers in priority order, and hand out oom_score_adj
189 // scores based on that sort order. 215 // scores based on that sort order.
190 // 216 //
191 // Things we need to collect on the browser thread (because 217 // Things we need to collect on the browser thread (because
192 // TabStripModel isn't thread safe): 218 // TabStripModel isn't thread safe):
193 // 1) whether or not a tab is pinned 219 // 1) whether or not a tab is pinned
194 // 2) last time a tab was selected 220 // 2) last time a tab was selected
195 // 3) is the tab currently selected 221 // 3) is the tab currently selected
196 //
197 // We also need to collect:
198 // 4) size in memory of a tab
199 // But we do that in DoAdjustOomPriorities on the FILE thread so that
200 // we avoid jank, because it accesses /proc.
201 void OomPriorityManager::AdjustOomPriorities() { 222 void OomPriorityManager::AdjustOomPriorities() {
202 if (BrowserList::size() == 0) 223 if (BrowserList::size() == 0)
203 return; 224 return;
225 TabStatsList stats_list = GetTabStatsOnUIThread();
226 BrowserThread::PostTask(
227 BrowserThread::FILE, FROM_HERE,
228 NewRunnableMethod(this,
229 &OomPriorityManager::AdjustOomPrioritiesOnFileThread,
230 stats_list));
231 }
204 232
205 { 233 OomPriorityManager::TabStatsList OomPriorityManager::GetTabStatsOnUIThread() {
206 base::AutoLock renderer_stats_autolock(renderer_stats_lock_); 234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
207 renderer_stats_.clear(); 235 TabStatsList stats_list;
208 for (BrowserList::const_iterator browser_iterator = BrowserList::begin(); 236 stats_list.reserve(32); // 99% of users have < 30 tabs open
209 browser_iterator != BrowserList::end(); ++browser_iterator) { 237 for (BrowserList::const_iterator browser_iterator = BrowserList::begin();
210 Browser* browser = *browser_iterator; 238 browser_iterator != BrowserList::end(); ++browser_iterator) {
211 const TabStripModel* model = browser->tabstrip_model(); 239 Browser* browser = *browser_iterator;
212 for (int i = 0; i < model->count(); i++) { 240 const TabStripModel* model = browser->tabstrip_model();
213 TabContents* contents = model->GetTabContentsAt(i)->tab_contents(); 241 for (int i = 0; i < model->count(); i++) {
214 if (!contents->is_crashed()) { 242 TabContents* contents = model->GetTabContentsAt(i)->tab_contents();
215 RendererStats stats; 243 if (!contents->is_crashed()) {
216 stats.last_selected = contents->last_selected_time(); 244 TabStats stats;
217 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); 245 stats.last_selected = contents->last_selected_time();
218 stats.is_pinned = model->IsTabPinned(i); 246 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle();
219 stats.memory_used = 0; // Calculated in DoAdjustOomPriorities. 247 stats.is_pinned = model->IsTabPinned(i);
220 stats.is_selected = model->IsTabSelected(i); 248 stats.is_selected = model->IsTabSelected(i);
221 stats.title = contents->GetTitle(); 249 stats.title = contents->GetTitle();
222 renderer_stats_.push_back(stats); 250 stats.tab_contents_id = IdFromTabContents(contents);
223 } 251 stats_list.push_back(stats);
224 } 252 }
225 } 253 }
226 } 254 }
227 255 // Sort the data we collected so that least desirable to be
228 BrowserThread::PostTask( 256 // killed is first, most desirable is last.
229 BrowserThread::FILE, FROM_HERE, 257 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats);
230 NewRunnableMethod(this, &OomPriorityManager::DoAdjustOomPriorities)); 258 return stats_list;
231 } 259 }
232 260
233 void OomPriorityManager::DoAdjustOomPriorities() { 261 void OomPriorityManager::AdjustOomPrioritiesOnFileThread(
262 TabStatsList stats_list) {
234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
235 base::AutoLock renderer_stats_autolock(renderer_stats_lock_);
236 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); 264 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_);
237 for (StatsList::iterator stats_iter = renderer_stats_.begin();
238 stats_iter != renderer_stats_.end(); ++stats_iter) {
239 scoped_ptr<ProcessMetrics> metrics(ProcessMetrics::CreateProcessMetrics(
240 stats_iter->renderer_handle));
241
242 base::WorkingSetKBytes working_set_kbytes;
243 if (metrics->GetWorkingSetKBytes(&working_set_kbytes)) {
244 // We use the proportional set size (PSS) to calculate memory
245 // usage "badness" on Linux.
246 stats_iter->memory_used = working_set_kbytes.shared * 1024;
247 } else {
248 // and if for some reason we can't get PSS, we revert to using
249 // resident set size (RSS). This will be zero if the process
250 // has already gone away, but we can live with that, since the
251 // process is gone anyhow.
252 stats_iter->memory_used = metrics->GetWorkingSetSize();
253 }
254 }
255
256 // Now we sort the data we collected so that least desirable to be
257 // killed is first, most desirable is last.
258 std::sort(renderer_stats_.begin(),
259 renderer_stats_.end(),
260 OomPriorityManager::CompareRendererStats);
261 265
262 // Now we assign priorities based on the sorted list. We're 266 // Now we assign priorities based on the sorted list. We're
263 // assigning priorities in the range of kLowestRendererOomScore to 267 // assigning priorities in the range of kLowestRendererOomScore to
264 // kHighestRendererOomScore (defined in chrome_constants.h). 268 // kHighestRendererOomScore (defined in chrome_constants.h).
265 // oom_score_adj takes values from -1000 to 1000. Negative values 269 // oom_score_adj takes values from -1000 to 1000. Negative values
266 // are reserved for system processes, and we want to give some room 270 // are reserved for system processes, and we want to give some room
267 // below the range we're using to allow for things that want to be 271 // below the range we're using to allow for things that want to be
268 // above the renderers in priority, so the defined range gives us 272 // above the renderers in priority, so the defined range gives us
269 // some variation in priority without taking up the whole range. In 273 // some variation in priority without taking up the whole range. In
270 // the end, however, it's a pretty arbitrary range to use. Higher 274 // the end, however, it's a pretty arbitrary range to use. Higher
271 // values are more likely to be killed by the OOM killer. 275 // values are more likely to be killed by the OOM killer.
272 // 276 //
273 // We also remove any duplicate PIDs, leaving the most important 277 // We also remove any duplicate PIDs, leaving the most important
274 // (least likely to be killed) of the duplicates, so that a 278 // (least likely to be killed) of the duplicates, so that a
275 // particular renderer process takes on the oom_score_adj of the 279 // particular renderer process takes on the oom_score_adj of the
276 // least likely tab to be killed. 280 // least likely tab to be killed.
277 const int kPriorityRange = chrome::kHighestRendererOomScore - 281 const int kPriorityRange = chrome::kHighestRendererOomScore -
278 chrome::kLowestRendererOomScore; 282 chrome::kLowestRendererOomScore;
279 float priority_increment = 283 float priority_increment =
280 static_cast<float>(kPriorityRange) / renderer_stats_.size(); 284 static_cast<float>(kPriorityRange) / stats_list.size();
281 float priority = chrome::kLowestRendererOomScore; 285 float priority = chrome::kLowestRendererOomScore;
282 std::set<base::ProcessHandle> already_seen; 286 std::set<base::ProcessHandle> already_seen;
283 int score = 0; 287 int score = 0;
284 ProcessScoreMap::iterator it; 288 ProcessScoreMap::iterator it;
285 for (StatsList::iterator iterator = renderer_stats_.begin(); 289 for (TabStatsList::iterator iterator = stats_list.begin();
286 iterator != renderer_stats_.end(); ++iterator) { 290 iterator != stats_list.end(); ++iterator) {
287 if (already_seen.find(iterator->renderer_handle) == already_seen.end()) { 291 if (already_seen.find(iterator->renderer_handle) == already_seen.end()) {
288 already_seen.insert(iterator->renderer_handle); 292 already_seen.insert(iterator->renderer_handle);
289 // If a process has the same score as the newly calculated value, 293 // If a process has the same score as the newly calculated value,
290 // do not set it. 294 // do not set it.
291 score = static_cast<int>(priority + 0.5f); 295 score = static_cast<int>(priority + 0.5f);
292 it = pid_to_oom_score_.find(iterator->renderer_handle); 296 it = pid_to_oom_score_.find(iterator->renderer_handle);
293 if (it == pid_to_oom_score_.end() || it->second != score) { 297 if (it == pid_to_oom_score_.end() || it->second != score) {
294 ZygoteHost::GetInstance()->AdjustRendererOOMScore( 298 ZygoteHost::GetInstance()->AdjustRendererOOMScore(
295 iterator->renderer_handle, score); 299 iterator->renderer_handle, score);
296 pid_to_oom_score_[iterator->renderer_handle] = score; 300 pid_to_oom_score_[iterator->renderer_handle] = score;
297 } 301 }
298 priority += priority_increment; 302 priority += priority_increment;
299 } 303 }
300 } 304 }
301 } 305 }
302 306
303 } // namespace browser 307 } // namespace browser
OLDNEW
« no previous file with comments | « chrome/browser/oom_priority_manager.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698