oom_priority_manager.cc revision 5e3f23d412006dc4db4e659864679f29341e113f
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 3// found in the LICENSE file. 4 5#include "chrome/browser/chromeos/memory/oom_priority_manager.h" 6 7#include <algorithm> 8#include <set> 9#include <vector> 10 11#include "base/bind.h" 12#include "base/bind_helpers.h" 13#include "base/command_line.h" 14#include "base/metrics/field_trial.h" 15#include "base/metrics/histogram.h" 16#include "base/process.h" 17#include "base/process_util.h" 18#include "base/strings/string16.h" 19#include "base/strings/string_number_conversions.h" 20#include "base/strings/string_util.h" 21#include "base/strings/utf_string_conversions.h" 22#include "base/synchronization/lock.h" 23#include "base/threading/thread.h" 24#include "base/time.h" 25#include "build/build_config.h" 26#include "chrome/browser/browser_process.h" 27#include "chrome/browser/browser_process_platform_part_chromeos.h" 28#include "chrome/browser/memory_details.h" 29#include "chrome/browser/ui/browser.h" 30#include "chrome/browser/ui/browser_iterator.h" 31#include "chrome/browser/ui/browser_list.h" 32#include "chrome/browser/ui/host_desktop.h" 33#include "chrome/browser/ui/tab_contents/tab_contents_iterator.h" 34#include "chrome/browser/ui/tabs/tab_strip_model.h" 35#include "chrome/browser/ui/tabs/tab_utils.h" 36#include "chrome/common/chrome_constants.h" 37#include "chrome/common/url_constants.h" 38#include "chromeos/chromeos_switches.h" 39#include "chromeos/memory/low_memory_listener.h" 40#include "content/public/browser/browser_thread.h" 41#include "content/public/browser/notification_service.h" 42#include "content/public/browser/notification_types.h" 43#include "content/public/browser/render_process_host.h" 44#include "content/public/browser/render_widget_host.h" 45#include "content/public/browser/web_contents.h" 46#include "content/public/browser/zygote_host_linux.h" 47#include "ui/base/text/bytes_formatting.h" 48 49using base::TimeDelta; 50using base::TimeTicks; 51using content::BrowserThread; 52using content::WebContents; 53 54namespace chromeos { 55 56namespace { 57 58// Record a size in megabytes, over a potential interval up to 32 GB. 59#define HISTOGRAM_MEGABYTES(name, sample) \ 60 UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 32768, 50) 61 62// The default interval in seconds after which to adjust the oom_score_adj 63// value. 64const int kAdjustmentIntervalSeconds = 10; 65 66// For each period of this length we record a statistic to indicate whether 67// or not the user experienced a low memory event. If you change this interval 68// you must replace Tabs.Discard.DiscardInLastMinute with a new statistic. 69const int kRecentTabDiscardIntervalSeconds = 60; 70 71// If there has been no priority adjustment in this interval, we assume the 72// machine was suspended and correct our timing statistics. 73const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4; 74 75// When switching to a new tab the tab's renderer's OOM score needs to be 76// updated to reflect its front-most status and protect it from discard. 77// However, doing this immediately might slow down tab switch time, so wait 78// a little while before doing the adjustment. 79const int kFocusedTabScoreAdjustIntervalMs = 500; 80 81// Returns a unique ID for a WebContents. Do not cast back to a pointer, as 82// the WebContents could be deleted if the user closed the tab. 83int64 IdFromWebContents(WebContents* web_contents) { 84 return reinterpret_cast<int64>(web_contents); 85} 86 87// Records a statistics |sample| for UMA histogram |name| using a linear 88// distribution of buckets. 89void RecordLinearHistogram(const std::string& name, 90 int sample, 91 int maximum, 92 size_t bucket_count) { 93 // Do not use the UMA_HISTOGRAM_... macros here. They cache the Histogram 94 // instance and thus only work if |name| is constant. 95 base::HistogramBase* counter = base::LinearHistogram::FactoryGet( 96 name, 97 1, // Minimum. The 0 bin for underflow is automatically added. 98 maximum + 1, // Ensure bucket size of |maximum| / |bucket_count|. 99 bucket_count + 2, // Account for the underflow and overflow bins. 100 base::Histogram::kUmaTargetedHistogramFlag); 101 counter->Add(sample); 102} 103 104} // namespace 105 106//////////////////////////////////////////////////////////////////////////////// 107// OomMemoryDetails logs details about all Chrome processes during an out-of- 108// memory event in an attempt to identify the culprit, then discards a tab and 109// deletes itself. 110class OomMemoryDetails : public MemoryDetails { 111 public: 112 OomMemoryDetails(); 113 114 // MemoryDetails overrides: 115 virtual void OnDetailsAvailable() OVERRIDE; 116 117 private: 118 virtual ~OomMemoryDetails() {} 119 120 TimeTicks start_time_; 121 122 DISALLOW_COPY_AND_ASSIGN(OomMemoryDetails); 123}; 124 125OomMemoryDetails::OomMemoryDetails() { 126 AddRef(); // Released in OnDetailsAvailable(). 127 start_time_ = TimeTicks::Now(); 128} 129 130void OomMemoryDetails::OnDetailsAvailable() { 131 TimeDelta delta = TimeTicks::Now() - start_time_; 132 // These logs are collected by user feedback reports. We want them to help 133 // diagnose user-reported problems with frequently discarded tabs. 134 std::string log_string = ToLogString(); 135 base::SystemMemoryInfoKB memory; 136 if (base::GetSystemMemoryInfo(&memory) && memory.gem_size != -1) { 137 log_string += "Graphics "; 138 log_string += UTF16ToASCII(ui::FormatBytes(memory.gem_size)); 139 } 140 LOG(WARNING) << "OOM details (" << delta.InMilliseconds() << " ms):\n" 141 << log_string; 142 if (g_browser_process && 143 g_browser_process->platform_part()->oom_priority_manager()) { 144 OomPriorityManager* manager = 145 g_browser_process->platform_part()->oom_priority_manager(); 146 manager->PurgeBrowserMemory(); 147 manager->DiscardTab(); 148 } 149 // Delete ourselves so we don't have to worry about OomPriorityManager 150 // deleting us when we're still working. 151 Release(); 152} 153 154//////////////////////////////////////////////////////////////////////////////// 155// OomPriorityManager 156 157OomPriorityManager::TabStats::TabStats() 158 : is_app(false), 159 is_reloadable_ui(false), 160 is_playing_audio(false), 161 is_pinned(false), 162 is_selected(false), 163 is_discarded(false), 164 renderer_handle(0), 165 tab_contents_id(0) { 166} 167 168OomPriorityManager::TabStats::~TabStats() { 169} 170 171OomPriorityManager::OomPriorityManager() 172 : focused_tab_pid_(0), 173 discard_count_(0), 174 recent_tab_discard_(false) { 175 // We only need the low memory observer if we want to discard tabs. 176 if (!CommandLine::ForCurrentProcess()->HasSwitch( 177 chromeos::switches::kNoDiscardTabs)) 178 low_memory_listener_.reset(new LowMemoryListener(this)); 179 180 registrar_.Add(this, 181 content::NOTIFICATION_RENDERER_PROCESS_CLOSED, 182 content::NotificationService::AllBrowserContextsAndSources()); 183 registrar_.Add(this, 184 content::NOTIFICATION_RENDERER_PROCESS_TERMINATED, 185 content::NotificationService::AllBrowserContextsAndSources()); 186 registrar_.Add(this, 187 content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, 188 content::NotificationService::AllBrowserContextsAndSources()); 189} 190 191OomPriorityManager::~OomPriorityManager() { 192 Stop(); 193} 194 195void OomPriorityManager::Start() { 196 if (!timer_.IsRunning()) { 197 timer_.Start(FROM_HERE, 198 TimeDelta::FromSeconds(kAdjustmentIntervalSeconds), 199 this, 200 &OomPriorityManager::AdjustOomPriorities); 201 } 202 if (!recent_tab_discard_timer_.IsRunning()) { 203 recent_tab_discard_timer_.Start( 204 FROM_HERE, 205 TimeDelta::FromSeconds(kRecentTabDiscardIntervalSeconds), 206 this, 207 &OomPriorityManager::RecordRecentTabDiscard); 208 } 209 if (low_memory_listener_.get()) 210 low_memory_listener_->Start(); 211 start_time_ = TimeTicks::Now(); 212} 213 214void OomPriorityManager::Stop() { 215 timer_.Stop(); 216 recent_tab_discard_timer_.Stop(); 217 if (low_memory_listener_.get()) 218 low_memory_listener_->Stop(); 219} 220 221std::vector<string16> OomPriorityManager::GetTabTitles() { 222 TabStatsList stats = GetTabStatsOnUIThread(); 223 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); 224 std::vector<string16> titles; 225 titles.reserve(stats.size()); 226 TabStatsList::iterator it = stats.begin(); 227 for ( ; it != stats.end(); ++it) { 228 string16 str; 229 str.reserve(4096); 230 int score = pid_to_oom_score_[it->renderer_handle]; 231 str += base::IntToString16(score); 232 str += ASCIIToUTF16(" - "); 233 str += it->title; 234 str += ASCIIToUTF16(it->is_app ? " app" : ""); 235 str += ASCIIToUTF16(it->is_reloadable_ui ? " reloadable_ui" : ""); 236 str += ASCIIToUTF16(it->is_playing_audio ? " playing_audio" : ""); 237 str += ASCIIToUTF16(it->is_pinned ? " pinned" : ""); 238 str += ASCIIToUTF16(it->is_discarded ? " discarded" : ""); 239 titles.push_back(str); 240 } 241 return titles; 242} 243 244// TODO(jamescook): This should consider tabs with references to other tabs, 245// such as tabs created with JavaScript window.open(). We might want to 246// discard the entire set together, or use that in the priority computation. 247bool OomPriorityManager::DiscardTab() { 248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 249 TabStatsList stats = GetTabStatsOnUIThread(); 250 if (stats.empty()) 251 return false; 252 // Loop until we find a non-discarded tab to kill. 253 for (TabStatsList::const_reverse_iterator stats_rit = stats.rbegin(); 254 stats_rit != stats.rend(); 255 ++stats_rit) { 256 int64 least_important_tab_id = stats_rit->tab_contents_id; 257 if (DiscardTabById(least_important_tab_id)) 258 return true; 259 } 260 return false; 261} 262 263void OomPriorityManager::LogMemoryAndDiscardTab() { 264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 265 // Deletes itself upon completion. 266 OomMemoryDetails* details = new OomMemoryDetails(); 267 details->StartFetch(MemoryDetails::SKIP_USER_METRICS); 268} 269 270/////////////////////////////////////////////////////////////////////////////// 271// OomPriorityManager, private: 272 273// static 274bool OomPriorityManager::IsReloadableUI(const GURL& url) { 275 // There are many chrome:// UI URLs, but only look for the ones that users 276 // are likely to have open. Most of the benefit is the from NTP URL. 277 const char* kReloadableUrlPrefixes[] = { 278 chrome::kChromeUIDownloadsURL, 279 chrome::kChromeUIHistoryURL, 280 chrome::kChromeUINewTabURL, 281 chrome::kChromeUISettingsURL, 282 }; 283 // Prefix-match against the table above. Use strncmp to avoid allocating 284 // memory to convert the URL prefix constants into std::strings. 285 for (size_t i = 0; i < arraysize(kReloadableUrlPrefixes); ++i) { 286 if (!strncmp(url.spec().c_str(), 287 kReloadableUrlPrefixes[i], 288 strlen(kReloadableUrlPrefixes[i]))) 289 return true; 290 } 291 return false; 292} 293 294bool OomPriorityManager::DiscardTabById(int64 target_web_contents_id) { 295 for (chrome::BrowserIterator it; !it.done(); it.Next()) { 296 Browser* browser = *it; 297 TabStripModel* model = browser->tab_strip_model(); 298 for (int idx = 0; idx < model->count(); idx++) { 299 // Can't discard tabs that are already discarded or active. 300 if (model->IsTabDiscarded(idx) || (model->active_index() == idx)) 301 continue; 302 WebContents* web_contents = model->GetWebContentsAt(idx); 303 int64 web_contents_id = IdFromWebContents(web_contents); 304 if (web_contents_id == target_web_contents_id) { 305 LOG(WARNING) << "Discarding tab " << idx 306 << " id " << target_web_contents_id; 307 // Record statistics before discarding because we want to capture the 308 // memory state that lead to the discard. 309 RecordDiscardStatistics(); 310 model->DiscardWebContentsAt(idx); 311 recent_tab_discard_ = true; 312 return true; 313 } 314 } 315 } 316 return false; 317} 318 319void OomPriorityManager::RecordDiscardStatistics() { 320 // Record a raw count so we can compare to discard reloads. 321 discard_count_++; 322 UMA_HISTOGRAM_CUSTOM_COUNTS( 323 "Tabs.Discard.DiscardCount", discard_count_, 1, 1000, 50); 324 325 // TODO(jamescook): Maybe incorporate extension count? 326 UMA_HISTOGRAM_CUSTOM_COUNTS( 327 "Tabs.Discard.TabCount", GetTabCount(), 1, 100, 50); 328 329 // TODO(jamescook): If the time stats prove too noisy, then divide up users 330 // based on how heavily they use Chrome using tab count as a proxy. 331 // Bin into <= 1, <= 2, <= 4, <= 8, etc. 332 if (last_discard_time_.is_null()) { 333 // This is the first discard this session. 334 TimeDelta interval = TimeTicks::Now() - start_time_; 335 int interval_seconds = static_cast<int>(interval.InSeconds()); 336 // Record time in seconds over an interval of approximately 1 day. 337 UMA_HISTOGRAM_CUSTOM_COUNTS( 338 "Tabs.Discard.InitialTime2", interval_seconds, 1, 100000, 50); 339 } else { 340 // Not the first discard, so compute time since last discard. 341 TimeDelta interval = TimeTicks::Now() - last_discard_time_; 342 int interval_ms = static_cast<int>(interval.InMilliseconds()); 343 // Record time in milliseconds over an interval of approximately 1 day. 344 // Start at 100 ms to get extra resolution in the target 750 ms range. 345 UMA_HISTOGRAM_CUSTOM_COUNTS( 346 "Tabs.Discard.IntervalTime2", interval_ms, 100, 100000 * 1000, 50); 347 } 348 // Record Chrome's concept of system memory usage at the time of the discard. 349 base::SystemMemoryInfoKB memory; 350 if (base::GetSystemMemoryInfo(&memory)) { 351 // TODO(jamescook): Remove this after R25 is deployed to stable. It does 352 // not have sufficient resolution in the 2-4 GB range and does not properly 353 // account for graphics memory on ARM. Replace with MemAllocatedMB below. 354 int mem_anonymous_mb = (memory.active_anon + memory.inactive_anon) / 1024; 355 HISTOGRAM_MEGABYTES("Tabs.Discard.MemAnonymousMB", mem_anonymous_mb); 356 357 // Record graphics GEM object size in a histogram with 50 MB buckets. 358 int mem_graphics_gem_mb = 0; 359 if (memory.gem_size != -1) 360 mem_graphics_gem_mb = memory.gem_size / 1024 / 1024; 361 RecordLinearHistogram( 362 "Tabs.Discard.MemGraphicsMB", mem_graphics_gem_mb, 2500, 50); 363 364 // Record shared memory (used by renderer/GPU buffers). 365 int mem_shmem_mb = memory.shmem / 1024; 366 RecordLinearHistogram("Tabs.Discard.MemShmemMB", mem_shmem_mb, 2500, 50); 367 368 // On Intel, graphics objects are in anonymous pages, but on ARM they are 369 // not. For a total "allocated count" add in graphics pages on ARM. 370 int mem_allocated_mb = mem_anonymous_mb; 371#if defined(ARCH_CPU_ARM_FAMILY) 372 mem_allocated_mb += mem_graphics_gem_mb; 373#endif 374 UMA_HISTOGRAM_CUSTOM_COUNTS( 375 "Tabs.Discard.MemAllocatedMB", mem_allocated_mb, 256, 32768, 50); 376 377 int mem_available_mb = 378 (memory.active_file + memory.inactive_file + memory.free) / 1024; 379 HISTOGRAM_MEGABYTES("Tabs.Discard.MemAvailableMB", mem_available_mb); 380 } 381 // Set up to record the next interval. 382 last_discard_time_ = TimeTicks::Now(); 383} 384 385void OomPriorityManager::RecordRecentTabDiscard() { 386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 387 // If we change the interval we need to change the histogram name. 388 UMA_HISTOGRAM_BOOLEAN("Tabs.Discard.DiscardInLastMinute", 389 recent_tab_discard_); 390 // Reset for the next interval. 391 recent_tab_discard_ = false; 392} 393 394void OomPriorityManager::PurgeBrowserMemory() { 395 // Based on experimental evidence, attempts to free memory from renderers 396 // have been too slow to use in OOM situations (V8 garbage collection) or 397 // do not lead to persistent decreased usage (image/bitmap caches). This 398 // function therefore only targets large blocks of memory in the browser. 399 for (TabContentsIterator it; !it.done(); it.Next()) { 400 WebContents* web_contents = *it; 401 // Screenshots can consume ~5 MB per web contents for platforms that do 402 // touch back/forward. 403 web_contents->GetController().ClearAllScreenshots(); 404 } 405 // TODO(jamescook): Are there other things we could flush? Drive metadata? 406} 407 408int OomPriorityManager::GetTabCount() const { 409 int tab_count = 0; 410 for (chrome::BrowserIterator it; !it.done(); it.Next()) 411 tab_count += it->tab_strip_model()->count(); 412 return tab_count; 413} 414 415// Returns true if |first| is considered less desirable to be killed 416// than |second|. 417bool OomPriorityManager::CompareTabStats(TabStats first, 418 TabStats second) { 419 // Being currently selected is most important to protect. 420 if (first.is_selected != second.is_selected) 421 return first.is_selected; 422 423 // Tab with internal web UI like NTP or Settings are good choices to discard, 424 // so protect non-Web UI and let the other conditionals finish the sort. 425 if (first.is_reloadable_ui != second.is_reloadable_ui) 426 return !first.is_reloadable_ui; 427 428 // Being pinned is important to protect. 429 if (first.is_pinned != second.is_pinned) 430 return first.is_pinned; 431 432 // Being an app is important too, as you're the only visible surface in the 433 // window and we don't want to discard that. 434 if (first.is_app != second.is_app) 435 return first.is_app; 436 437 // Protect streaming audio and video conferencing tabs. 438 if (first.is_playing_audio != second.is_playing_audio) 439 return first.is_playing_audio; 440 441 // TODO(jamescook): Incorporate sudden_termination_allowed into the sort 442 // order. We don't do this now because pages with unload handlers set 443 // sudden_termination_allowed false, and that covers too many common pages 444 // with ad networks and statistics scripts. Ideally we would like to check 445 // for beforeUnload handlers, which are likely to present a dialog asking 446 // if the user wants to discard state. crbug.com/123049 447 448 // Being more recently selected is more important. 449 return first.last_selected > second.last_selected; 450} 451 452void OomPriorityManager::AdjustFocusedTabScoreOnFileThread() { 453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 454 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); 455 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore( 456 focused_tab_pid_, chrome::kLowestRendererOomScore); 457 pid_to_oom_score_[focused_tab_pid_] = chrome::kLowestRendererOomScore; 458} 459 460void OomPriorityManager::OnFocusTabScoreAdjustmentTimeout() { 461 BrowserThread::PostTask( 462 BrowserThread::FILE, FROM_HERE, 463 base::Bind(&OomPriorityManager::AdjustFocusedTabScoreOnFileThread, 464 base::Unretained(this))); 465} 466 467void OomPriorityManager::Observe(int type, 468 const content::NotificationSource& source, 469 const content::NotificationDetails& details) { 470 base::ProcessHandle handle = 0; 471 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); 472 switch (type) { 473 case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: { 474 handle = 475 content::Details<content::RenderProcessHost::RendererClosedDetails>( 476 details)->handle; 477 pid_to_oom_score_.erase(handle); 478 break; 479 } 480 case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: { 481 handle = content::Source<content::RenderProcessHost>(source)-> 482 GetHandle(); 483 pid_to_oom_score_.erase(handle); 484 break; 485 } 486 case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: { 487 bool visible = *content::Details<bool>(details).ptr(); 488 if (visible) { 489 focused_tab_pid_ = 490 content::Source<content::RenderWidgetHost>(source).ptr()-> 491 GetProcess()->GetHandle(); 492 493 // If the currently focused tab already has a lower score, do not 494 // set it. This can happen in case the newly focused tab is script 495 // connected to the previous tab. 496 ProcessScoreMap::iterator it; 497 it = pid_to_oom_score_.find(focused_tab_pid_); 498 if (it == pid_to_oom_score_.end() 499 || it->second != chrome::kLowestRendererOomScore) { 500 // By starting a timer we guarantee that the tab is focused for 501 // certain amount of time. Secondly, it also does not add overhead 502 // to the tab switching time. 503 if (focus_tab_score_adjust_timer_.IsRunning()) 504 focus_tab_score_adjust_timer_.Reset(); 505 else 506 focus_tab_score_adjust_timer_.Start(FROM_HERE, 507 TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs), 508 this, &OomPriorityManager::OnFocusTabScoreAdjustmentTimeout); 509 } 510 } 511 break; 512 } 513 default: 514 NOTREACHED() << L"Received unexpected notification"; 515 break; 516 } 517} 518 519// Here we collect most of the information we need to sort the 520// existing renderers in priority order, and hand out oom_score_adj 521// scores based on that sort order. 522// 523// Things we need to collect on the browser thread (because 524// TabStripModel isn't thread safe): 525// 1) whether or not a tab is pinned 526// 2) last time a tab was selected 527// 3) is the tab currently selected 528void OomPriorityManager::AdjustOomPriorities() { 529 if (BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH)->empty()) 530 return; 531 532 // Check for a discontinuity in time caused by the machine being suspended. 533 if (!last_adjust_time_.is_null()) { 534 TimeDelta suspend_time = TimeTicks::Now() - last_adjust_time_; 535 if (suspend_time.InSeconds() > kSuspendThresholdSeconds) { 536 // We were probably suspended, move our event timers forward in time so 537 // when we subtract them out later we are counting "uptime". 538 start_time_ += suspend_time; 539 if (!last_discard_time_.is_null()) 540 last_discard_time_ += suspend_time; 541 } 542 } 543 last_adjust_time_ = TimeTicks::Now(); 544 545 TabStatsList stats_list = GetTabStatsOnUIThread(); 546 BrowserThread::PostTask( 547 BrowserThread::FILE, FROM_HERE, 548 base::Bind(&OomPriorityManager::AdjustOomPrioritiesOnFileThread, 549 base::Unretained(this), stats_list)); 550} 551 552OomPriorityManager::TabStatsList OomPriorityManager::GetTabStatsOnUIThread() { 553 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 554 TabStatsList stats_list; 555 stats_list.reserve(32); // 99% of users have < 30 tabs open 556 bool browser_active = true; 557 const BrowserList* ash_browser_list = 558 BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH); 559 for (BrowserList::const_reverse_iterator browser_iterator = 560 ash_browser_list->begin_last_active(); 561 browser_iterator != ash_browser_list->end_last_active(); 562 ++browser_iterator) { 563 Browser* browser = *browser_iterator; 564 bool is_browser_for_app = browser->is_app(); 565 const TabStripModel* model = browser->tab_strip_model(); 566 for (int i = 0; i < model->count(); i++) { 567 WebContents* contents = model->GetWebContentsAt(i); 568 if (!contents->IsCrashed()) { 569 TabStats stats; 570 stats.is_app = is_browser_for_app; 571 stats.is_reloadable_ui = IsReloadableUI(contents->GetURL()); 572 stats.is_playing_audio = chrome::IsPlayingAudio(contents); 573 stats.is_pinned = model->IsTabPinned(i); 574 stats.is_selected = browser_active && model->IsTabSelected(i); 575 stats.is_discarded = model->IsTabDiscarded(i); 576 stats.last_selected = contents->GetLastSelectedTime(); 577 stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); 578 stats.title = contents->GetTitle(); 579 stats.tab_contents_id = IdFromWebContents(contents); 580 stats_list.push_back(stats); 581 } 582 } 583 // We process the active browser window in the first iteration. 584 browser_active = false; 585 } 586 // Sort the data we collected so that least desirable to be 587 // killed is first, most desirable is last. 588 std::sort(stats_list.begin(), stats_list.end(), CompareTabStats); 589 return stats_list; 590} 591 592void OomPriorityManager::AdjustOomPrioritiesOnFileThread( 593 TabStatsList stats_list) { 594 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 595 base::AutoLock pid_to_oom_score_autolock(pid_to_oom_score_lock_); 596 597 // Now we assign priorities based on the sorted list. We're 598 // assigning priorities in the range of kLowestRendererOomScore to 599 // kHighestRendererOomScore (defined in chrome_constants.h). 600 // oom_score_adj takes values from -1000 to 1000. Negative values 601 // are reserved for system processes, and we want to give some room 602 // below the range we're using to allow for things that want to be 603 // above the renderers in priority, so the defined range gives us 604 // some variation in priority without taking up the whole range. In 605 // the end, however, it's a pretty arbitrary range to use. Higher 606 // values are more likely to be killed by the OOM killer. 607 // 608 // We also remove any duplicate PIDs, leaving the most important 609 // (least likely to be killed) of the duplicates, so that a 610 // particular renderer process takes on the oom_score_adj of the 611 // least likely tab to be killed. 612 const int kPriorityRange = chrome::kHighestRendererOomScore - 613 chrome::kLowestRendererOomScore; 614 float priority_increment = 615 static_cast<float>(kPriorityRange) / stats_list.size(); 616 float priority = chrome::kLowestRendererOomScore; 617 std::set<base::ProcessHandle> already_seen; 618 int score = 0; 619 ProcessScoreMap::iterator it; 620 for (TabStatsList::iterator iterator = stats_list.begin(); 621 iterator != stats_list.end(); ++iterator) { 622 // stats_list also contains discarded tab stat. If renderer_handler is zero, 623 // we don't need to adjust oom_score. 624 if (iterator->renderer_handle == 0) 625 continue; 626 if (already_seen.find(iterator->renderer_handle) == already_seen.end()) { 627 already_seen.insert(iterator->renderer_handle); 628 // If a process has the same score as the newly calculated value, 629 // do not set it. 630 score = static_cast<int>(priority + 0.5f); 631 it = pid_to_oom_score_.find(iterator->renderer_handle); 632 if (it == pid_to_oom_score_.end() || it->second != score) { 633 content::ZygoteHost::GetInstance()->AdjustRendererOOMScore( 634 iterator->renderer_handle, score); 635 pid_to_oom_score_[iterator->renderer_handle] = score; 636 } 637 priority += priority_increment; 638 } 639 } 640} 641 642void OomPriorityManager::OnMemoryLow() { 643 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 644 LogMemoryAndDiscardTab(); 645} 646 647} // namespace chromeos 648