| Index: chrome/browser/oom_priority_manager.cc
|
| diff --git a/chrome/browser/oom_priority_manager.cc b/chrome/browser/oom_priority_manager.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a8390a13c0606e909c319705faf1f19b8010784c
|
| --- /dev/null
|
| +++ b/chrome/browser/oom_priority_manager.cc
|
| @@ -0,0 +1,169 @@
|
| +// 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/chrome_thread.h"
|
| +#include "chrome/browser/renderer_host/render_process_host.h"
|
| +#include "chrome/browser/tab_contents/tab_contents.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();
|
| +}
|
| +
|
| +
|
| +namespace {
|
| +struct RendererStats {
|
| + bool is_pinned;
|
| + TimeTicks last_selected;
|
| + size_t memory_used;
|
| + ProcessHandle renderer_handle;
|
| +};
|
| +
|
| +// Returns true if |first| is considered less desirable to be killed
|
| +// than |second|.
|
| +bool SortRendererStats(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;
|
| +}
|
| +} // anonymous namespace
|
| +
|
| +// Post a new task for the file thread to actually adjust the priorities.
|
| +void OomPriorityManager::AdjustOomPriorities() {
|
| + ChromeThread::PostTask(
|
| + ChromeThread::FILE, FROM_HERE,
|
| + NewRunnableMethod(this, &OomPriorityManager::DoAdjustOomPriorities));
|
| +}
|
| +
|
| +// Here we collect all 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:
|
| +// 1) whether or not a tab is pinned
|
| +// 2) last time a tab was selected
|
| +// 3) size in memory of a tab
|
| +void OomPriorityManager::DoAdjustOomPriorities() {
|
| + std::list<RendererStats> renderer_stats;
|
| +
|
| + if (BrowserList::size() == 0)
|
| + return;
|
| +
|
| + 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);
|
| + RendererStats stats;
|
| + stats.last_selected = contents->last_selected_time();
|
| + stats.renderer_handle = contents->GetRenderProcessHost()->GetHandle();
|
| + scoped_ptr<ProcessMetrics> metrics(
|
| + ProcessMetrics::CreateProcessMetrics(stats.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.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).
|
| + stats.memory_used = metrics->GetWorkingSetSize();
|
| + }
|
| +
|
| + stats.is_pinned = model->IsTabPinned(i);
|
| + renderer_stats.push_back(stats);
|
| + }
|
| + }
|
| +
|
| + // Now we sort the data we collected so that least desirable to be
|
| + // killed is first, most desirable is last.
|
| + renderer_stats.sort(SortRendererStats);
|
| +
|
| + // 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.
|
| + 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;
|
| + for (std::list<RendererStats>::iterator iterator = renderer_stats.begin();
|
| + iterator != renderer_stats.end(); ++iterator) {
|
| + Singleton<ZygoteHost>()->AdjustRendererOOMScore(
|
| + iterator->renderer_handle,
|
| + static_cast<int>(priority + 0.5f));
|
| + priority += priority_increment;
|
| + }
|
| +}
|
| +
|
| +} // namespace browser
|
|
|