diff options
Diffstat (limited to 'chrome/browser/oom_priority_manager.cc')
-rw-r--r-- | chrome/browser/oom_priority_manager.cc | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/chrome/browser/oom_priority_manager.cc b/chrome/browser/oom_priority_manager.cc new file mode 100644 index 0000000..446f108 --- /dev/null +++ b/chrome/browser/oom_priority_manager.cc @@ -0,0 +1,177 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/oom_priority_manager.h" + +#include <list> + +#include "base/process.h" +#include "base/process_util.h" +#include "base/thread.h" +#include "build/build_config.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents_wrapper.h" +#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/browser/zygote_host_linux.h" + +#if !defined(OS_CHROMEOS) +#error This file only meant to be compiled on ChromeOS +#endif + +using base::TimeDelta; +using base::TimeTicks; +using base::ProcessHandle; +using base::ProcessMetrics; + +namespace browser { + +// The default interval in seconds after which to adjust the oom_adj +// value. +#define ADJUSTMENT_INTERVAL_SECONDS 10 + +// The default interval in minutes for considering activation times +// "equal". +#define BUCKET_INTERVAL_MINUTES 10 + +OomPriorityManager::OomPriorityManager() { + StartTimer(); +} + +OomPriorityManager::~OomPriorityManager() { + StopTimer(); +} + +void OomPriorityManager::StartTimer() { + if (!timer_.IsRunning()) { + timer_.Start(TimeDelta::FromSeconds(ADJUSTMENT_INTERVAL_SECONDS), + this, + &OomPriorityManager::AdjustOomPriorities); + } +} + +void OomPriorityManager::StopTimer() { + timer_.Stop(); +} + +// Returns true if |first| is considered less desirable to be killed +// than |second|. +bool OomPriorityManager::CompareRendererStats(RendererStats first, + RendererStats second) { + // The size of the slop in comparing activation times. [This is + // allocated here to avoid static initialization at startup time.] + static const int64 kTimeBucketInterval = + TimeDelta::FromMinutes(BUCKET_INTERVAL_MINUTES).ToInternalValue(); + + // Being pinned is most important. + if (first.is_pinned != second.is_pinned) + return first.is_pinned == true; + + // We want to be a little "fuzzy" when we compare these, because + // it's not really possible for the times to be identical, but if + // the user selected two tabs at about the same time, we still want + // to take the one that uses more memory. + if (abs((first.last_selected - second.last_selected).ToInternalValue()) < + kTimeBucketInterval) + return first.last_selected < second.last_selected; + + return first.memory_used < second.memory_used; +} + +// Here we collect most of the information we need to sort the +// existing renderers in priority order, and hand out oom_adj scores +// based on that sort order. +// +// Things we need to collect on the browser thread (because +// TabStripModel isn't thread safe): +// 1) whether or not a tab is pinned +// 2) last time a tab was selected +// +// We also need to collect: +// 3) size in memory of a tab +// But we do that in DoAdjustOomPriorities on the FILE thread so that +// we avoid jank, because it accesses /proc. +void OomPriorityManager::AdjustOomPriorities() { + if (BrowserList::size() == 0) + return; + + StatsList renderer_stats; + for (BrowserList::const_iterator browser_iterator = BrowserList::begin(); + browser_iterator != BrowserList::end(); ++browser_iterator) { + Browser* browser = *browser_iterator; + const TabStripModel* model = browser->tabstrip_model(); + for (int i = 0; i < model->count(); i++) { + TabContents* contents = model->GetTabContentsAt(i)->tab_contents(); + RendererStats stats; + stats.last_selected = contents->last_selected_time(); + stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle(); + stats.is_pinned = model->IsTabPinned(i); + stats.memory_used = 0; // This gets calculated in DoAdjustOomPriorities. + renderer_stats.push_back(stats); + } + } + + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, &OomPriorityManager::DoAdjustOomPriorities, + renderer_stats)); +} + +void OomPriorityManager::DoAdjustOomPriorities(StatsList renderer_stats) { + for (StatsList::iterator stats_iter = renderer_stats.begin(); + stats_iter != renderer_stats.end(); ++stats_iter) { + scoped_ptr<ProcessMetrics> metrics(ProcessMetrics::CreateProcessMetrics( + stats_iter->renderer_handle)); + + base::WorkingSetKBytes working_set_kbytes; + if (metrics->GetWorkingSetKBytes(&working_set_kbytes)) { + // We use the proportional set size (PSS) to calculate memory + // usage "badness" on Linux. + stats_iter->memory_used = working_set_kbytes.shared * 1024; + } else { + // and if for some reason we can't get PSS, we revert to using + // resident set size (RSS). This will be zero if the process + // has already gone away, but we can live with that, since the + // process is gone anyhow. + stats_iter->memory_used = metrics->GetWorkingSetSize(); + } + } + + // Now we sort the data we collected so that least desirable to be + // killed is first, most desirable is last. + renderer_stats.sort(OomPriorityManager::CompareRendererStats); + + // Now we assign priorities based on the sorted list. We're + // assigning priorities in the range of 5 to 10. oom_adj takes + // values from -17 to 15. Negative values are reserved for system + // processes, and we want to give some room on either side of the + // range we're using to allow for things that want to be above or + // below the renderers in priority, so 5 to 10 gives us some + // variation in priority without taking up the whole range. In the + // end, however, it's a pretty arbitrary range to use. Higher + // values are more likely to be killed by the OOM killer. We also + // remove any duplicate PIDs, leaving the most important of the + // duplicates. + const int kMinPriority = 5; + const int kMaxPriority = 10; + const int kPriorityRange = kMaxPriority - kMinPriority; + float priority_increment = + static_cast<float>(kPriorityRange) / renderer_stats.size(); + float priority = kMinPriority; + std::set<base::ProcessHandle> already_seen; + for (StatsList::iterator iterator = renderer_stats.begin(); + iterator != renderer_stats.end(); ++iterator) { + if (already_seen.find(iterator->renderer_handle) == already_seen.end()) { + already_seen.insert(iterator->renderer_handle); + Singleton<ZygoteHost>::get()->AdjustRendererOOMScore( + iterator->renderer_handle, + static_cast<int>(priority + 0.5f)); + priority += priority_increment; + } + } +} + +} // namespace browser |