summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/browser_process_impl.h2
-rw-r--r--chrome/browser/memory/oom_priority_manager.cc (renamed from chrome/browser/memory/oom_priority_manager_chromeos.cc)281
-rw-r--r--chrome/browser/memory/oom_priority_manager.h118
-rw-r--r--chrome/browser/memory/oom_priority_manager_browsertest.cc (renamed from chrome/browser/memory/oom_priority_manager_browsertest_chromeos.cc)12
-rw-r--r--chrome/browser/memory/oom_priority_manager_chromeos_unittest.cc218
-rw-r--r--chrome/browser/memory/oom_priority_manager_delegate_chromeos.cc216
-rw-r--r--chrome/browser/memory/oom_priority_manager_delegate_chromeos.h82
-rw-r--r--chrome/browser/memory/oom_priority_manager_delegate_chromeos_unittest.cc79
-rw-r--r--chrome/browser/memory/oom_priority_manager_unittest.cc148
-rw-r--r--chrome/browser/memory/tab_stats.cc24
-rw-r--r--chrome/browser/memory/tab_stats.h36
-rw-r--r--chrome/chrome_browser.gypi6
-rw-r--r--chrome/chrome_tests.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi3
14 files changed, 709 insertions, 518 deletions
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index 6c1595c..c84f1c4 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -310,6 +310,8 @@ class BrowserProcessImpl : public BrowserProcess,
#endif
#if defined(OS_CHROMEOS)
+ // Any change to this #ifdef must be reflected as well in
+ // chrome/browser/memory/oom_priority_manager_browsertest.cc
scoped_ptr<memory::OomPriorityManager> oom_priority_manager_;
#endif
diff --git a/chrome/browser/memory/oom_priority_manager_chromeos.cc b/chrome/browser/memory/oom_priority_manager.cc
index 72f759c..ae06da0 100644
--- a/chrome/browser/memory/oom_priority_manager_chromeos.cc
+++ b/chrome/browser/memory/oom_priority_manager.cc
@@ -22,7 +22,6 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
-#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
@@ -37,14 +36,13 @@
#include "chrome/browser/ui/tabs/tab_utils.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/url_constants.h"
-#include "chromeos/chromeos_switches.h"
#include "content/public/browser/browser_thread.h"
-#include "content/public/browser/notification_service.h"
-#include "content/public/browser/notification_types.h"
#include "content/public/browser/render_process_host.h"
-#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/web_contents.h"
-#include "content/public/browser/zygote_host_linux.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/memory/oom_priority_manager_delegate_chromeos.h"
+#endif
using base::TimeDelta;
using base::TimeTicks;
@@ -67,13 +65,7 @@ const int kRecentTabDiscardIntervalSeconds = 60;
// machine was suspended and correct our timing statistics.
const int kSuspendThresholdSeconds = kAdjustmentIntervalSeconds * 4;
-// When switching to a new tab the tab's renderer's OOM score needs to be
-// updated to reflect its front-most status and protect it from discard.
-// However, doing this immediately might slow down tab switch time, so wait
-// a little while before doing the adjustment.
-const int kFocusedTabScoreAdjustIntervalMs = 500;
-
-// Returns a unique ID for a WebContents. Do not cast back to a pointer, as
+// Returns a unique ID for a WebContents. Do not cast back to a pointer, as
// the WebContents could be deleted if the user closed the tab.
int64 IdFromWebContents(WebContents* web_contents) {
return reinterpret_cast<int64>(web_contents);
@@ -84,30 +76,11 @@ int64 IdFromWebContents(WebContents* web_contents) {
////////////////////////////////////////////////////////////////////////////////
// OomPriorityManager
-OomPriorityManager::TabStats::TabStats()
- : is_app(false),
- is_reloadable_ui(false),
- is_playing_audio(false),
- is_pinned(false),
- is_selected(false),
- is_discarded(false),
- renderer_handle(0),
- tab_contents_id(0) {
-}
-
-OomPriorityManager::TabStats::~TabStats() {
-}
-
OomPriorityManager::OomPriorityManager()
- : focused_tab_process_info_(std::make_pair(0, 0)),
- discard_count_(0),
- recent_tab_discard_(false) {
- registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
- content::NotificationService::AllBrowserContextsAndSources());
- registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
- content::NotificationService::AllBrowserContextsAndSources());
- registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
- content::NotificationService::AllBrowserContextsAndSources());
+ : discard_count_(0), recent_tab_discard_(false) {
+#if defined(OS_CHROMEOS)
+ delegate_.reset(new OomPriorityManagerDelegate);
+#endif
}
OomPriorityManager::~OomPriorityManager() {
@@ -115,9 +88,10 @@ OomPriorityManager::~OomPriorityManager() {
}
void OomPriorityManager::Start() {
- if (!timer_.IsRunning()) {
- timer_.Start(FROM_HERE, TimeDelta::FromSeconds(kAdjustmentIntervalSeconds),
- this, &OomPriorityManager::AdjustOomPriorities);
+ if (!update_timer_.IsRunning()) {
+ update_timer_.Start(FROM_HERE,
+ TimeDelta::FromSeconds(kAdjustmentIntervalSeconds),
+ this, &OomPriorityManager::UpdateTimerCallback);
}
if (!recent_tab_discard_timer_.IsRunning()) {
recent_tab_discard_timer_.Start(
@@ -139,26 +113,27 @@ void OomPriorityManager::Start() {
}
void OomPriorityManager::Stop() {
- timer_.Stop();
+ update_timer_.Stop();
recent_tab_discard_timer_.Stop();
memory_pressure_listener_.reset();
}
std::vector<base::string16> OomPriorityManager::GetTabTitles() {
TabStatsList stats = GetTabStatsOnUIThread();
- base::AutoLock oom_score_autolock(oom_score_lock_);
std::vector<base::string16> titles;
titles.reserve(stats.size());
TabStatsList::iterator it = stats.begin();
for (; it != stats.end(); ++it) {
base::string16 str;
str.reserve(4096);
- int score = oom_score_map_[it->child_process_host_id];
+#if defined(OS_CHROMEOS)
+ int score = delegate_->GetOomScore(it->child_process_host_id);
str += base::IntToString16(score);
str += base::ASCIIToUTF16(" - ");
+#endif
str += it->title;
str += base::ASCIIToUTF16(it->is_app ? " app" : "");
- str += base::ASCIIToUTF16(it->is_reloadable_ui ? " reloadable_ui" : "");
+ str += base::ASCIIToUTF16(it->is_internal_page ? " internal_page" : "");
str += base::ASCIIToUTF16(it->is_playing_audio ? " playing_audio" : "");
str += base::ASCIIToUTF16(it->is_pinned ? " pinned" : "");
str += base::ASCIIToUTF16(it->is_discarded ? " discarded" : "");
@@ -187,7 +162,7 @@ bool OomPriorityManager::DiscardTab() {
void OomPriorityManager::LogMemoryAndDiscardTab() {
LogMemory("Tab Discards Memory details",
- base::Bind(&OomPriorityManager::PurgeMemoryAndDiscardTabs));
+ base::Bind(&OomPriorityManager::PurgeMemoryAndDiscardTab));
}
void OomPriorityManager::LogMemory(const std::string& title,
@@ -200,7 +175,7 @@ void OomPriorityManager::LogMemory(const std::string& title,
// OomPriorityManager, private:
// static
-void OomPriorityManager::PurgeMemoryAndDiscardTabs() {
+void OomPriorityManager::PurgeMemoryAndDiscardTab() {
if (g_browser_process && g_browser_process->GetOomPriorityManager()) {
OomPriorityManager* manager = g_browser_process->GetOomPriorityManager();
manager->PurgeBrowserMemory();
@@ -209,10 +184,10 @@ void OomPriorityManager::PurgeMemoryAndDiscardTabs() {
}
// static
-bool OomPriorityManager::IsReloadableUI(const GURL& url) {
+bool OomPriorityManager::IsInternalPage(const GURL& url) {
// There are many chrome:// UI URLs, but only look for the ones that users
// are likely to have open. Most of the benefit is the from NTP URL.
- const char* const kReloadableUrlPrefixes[] = {
+ const char* const kInternalPagePrefixes[] = {
chrome::kChromeUIDownloadsURL,
chrome::kChromeUIHistoryURL,
chrome::kChromeUINewTabURL,
@@ -220,9 +195,9 @@ bool OomPriorityManager::IsReloadableUI(const GURL& url) {
};
// Prefix-match against the table above. Use strncmp to avoid allocating
// memory to convert the URL prefix constants into std::strings.
- for (size_t i = 0; i < arraysize(kReloadableUrlPrefixes); ++i) {
- if (!strncmp(url.spec().c_str(), kReloadableUrlPrefixes[i],
- strlen(kReloadableUrlPrefixes[i])))
+ for (size_t i = 0; i < arraysize(kInternalPagePrefixes); ++i) {
+ if (!strncmp(url.spec().c_str(), kInternalPagePrefixes[i],
+ strlen(kInternalPagePrefixes[i])))
return true;
}
return false;
@@ -262,12 +237,13 @@ void OomPriorityManager::RecordDiscardStatistics() {
// TODO(jamescook): Maybe incorporate extension count?
UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.TabCount", GetTabCount(), 1, 100,
50);
+#if defined(OS_CHROMEOS)
// Record the discarded tab in relation to the amount of simultaneously
// logged in users.
ash::MultiProfileUMA::RecordDiscardedTab(ash::Shell::GetInstance()
->session_state_delegate()
->NumberOfLoggedInUsers());
-
+#endif
// TODO(jamescook): If the time stats prove too noisy, then divide up users
// based on how heavily they use Chrome using tab count as a proxy.
// Bin into <= 1, <= 2, <= 4, <= 8, etc.
@@ -287,10 +263,12 @@ void OomPriorityManager::RecordDiscardStatistics() {
UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.Discard.IntervalTime2", interval_ms, 100,
100000 * 1000, 50);
}
- // Record chromeos's concept of system memory usage at the time of the
- // discard.
+// TODO(georgesak): Remove this #if when RecordMemoryStats is implemented for
+// all platforms.
+#if defined(OS_WIN) || defined(OS_CHROMEOS)
+ // Record system memory usage at the time of the discard.
RecordMemoryStats(RECORD_MEMORY_STATS_TAB_DISCARDED);
-
+#endif
// Set up to record the next interval.
last_discard_time_ = TimeTicks::Now();
}
@@ -335,8 +313,8 @@ bool OomPriorityManager::CompareTabStats(TabStats first, TabStats second) {
// Tab with internal web UI like NTP or Settings are good choices to discard,
// so protect non-Web UI and let the other conditionals finish the sort.
- if (first.is_reloadable_ui != second.is_reloadable_ui)
- return !first.is_reloadable_ui;
+ if (first.is_internal_page != second.is_internal_page)
+ return !first.is_internal_page;
// Being pinned is important to protect.
if (first.is_pinned != second.is_pinned)
@@ -362,97 +340,17 @@ bool OomPriorityManager::CompareTabStats(TabStats first, TabStats second) {
return first.last_active > second.last_active;
}
-void OomPriorityManager::AdjustFocusedTabScoreOnFileThread() {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
- base::AutoLock oom_score_autolock(oom_score_lock_);
- base::ProcessHandle pid = focused_tab_process_info_.second;
- content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
- pid, chrome::kLowestRendererOomScore);
- oom_score_map_[focused_tab_process_info_.first] =
- chrome::kLowestRendererOomScore;
-}
-
-void OomPriorityManager::OnFocusTabScoreAdjustmentTimeout() {
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- base::Bind(&OomPriorityManager::AdjustFocusedTabScoreOnFileThread,
- base::Unretained(this)));
-}
-
-void OomPriorityManager::Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) {
- base::AutoLock oom_score_autolock(oom_score_lock_);
- switch (type) {
- case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
- case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
- content::RenderProcessHost* host =
- content::Source<content::RenderProcessHost>(source).ptr();
- oom_score_map_.erase(host->GetID());
- // Coming here we know that a renderer was just killed and memory should
- // come back into the pool. However - the memory pressure observer did
- // not yet update its status and therefore we ask it to redo the
- // measurement, calling us again if we have to release more.
- // Note: We do not only accelerate the discarding speed by doing another
- // check in short succession - we also accelerate it because the timer
- // driven MemoryPressureMonitor will continue to produce timed events
- // on top. So the longer the cleanup phase takes, the more tabs will
- // get discarded in parallel.
- base::chromeos::MemoryPressureMonitor* monitor =
- base::chromeos::MemoryPressureMonitor::Get();
- if (monitor)
- monitor->ScheduleEarlyCheck();
- break;
- }
- case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: {
- bool visible = *content::Details<bool>(details).ptr();
- if (visible) {
- content::RenderProcessHost* render_host =
- content::Source<content::RenderWidgetHost>(source)
- .ptr()
- ->GetProcess();
- focused_tab_process_info_ =
- std::make_pair(render_host->GetID(), render_host->GetHandle());
-
- // If the currently focused tab already has a lower score, do not
- // set it. This can happen in case the newly focused tab is script
- // connected to the previous tab.
- ProcessScoreMap::iterator it;
- it = oom_score_map_.find(focused_tab_process_info_.first);
- if (it == oom_score_map_.end() ||
- it->second != chrome::kLowestRendererOomScore) {
- // By starting a timer we guarantee that the tab is focused for
- // certain amount of time. Secondly, it also does not add overhead
- // to the tab switching time.
- if (focus_tab_score_adjust_timer_.IsRunning())
- focus_tab_score_adjust_timer_.Reset();
- else
- focus_tab_score_adjust_timer_.Start(
- FROM_HERE,
- TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs),
- this, &OomPriorityManager::OnFocusTabScoreAdjustmentTimeout);
- }
- }
- break;
- }
- default:
- NOTREACHED() << L"Received unexpected notification";
- break;
- }
-}
-
-// Here we collect most of the information we need to sort the
-// existing renderers in priority order, and hand out oom_score_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
-// 3) is the tab currently selected
-void OomPriorityManager::AdjustOomPriorities() {
+// This function is called when |update_timer_| fires. It will adjust the clock
+// if needed (if we detect that the machine was asleep) and will fire the stats
+// updating on ChromeOS via the delegate.
+void OomPriorityManager::UpdateTimerCallback() {
+#if defined(USE_ASH) || defined(OS_CHROMEOS)
if (BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH)->empty())
return;
+#else
+ if (BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE)->empty())
+ return;
+#endif
// Check for a discontinuity in time caused by the machine being suspended.
if (!last_adjust_time_.is_null()) {
@@ -467,23 +365,33 @@ void OomPriorityManager::AdjustOomPriorities() {
}
last_adjust_time_ = TimeTicks::Now();
+#if defined(OS_CHROMEOS)
TabStatsList stats_list = GetTabStatsOnUIThread();
- BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
- base::Bind(&OomPriorityManager::AdjustOomPrioritiesOnFileThread,
- base::Unretained(this), stats_list));
+ // This starts the CrOS specific OOM adjustments in /proc/<pid>/oom_score_adj.
+ delegate_->AdjustOomPriorities(stats_list);
+#endif
}
-OomPriorityManager::TabStatsList OomPriorityManager::GetTabStatsOnUIThread() {
+// 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
+// 3) is the tab currently selected
+TabStatsList OomPriorityManager::GetTabStatsOnUIThread() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
TabStatsList stats_list;
stats_list.reserve(32); // 99% of users have < 30 tabs open
bool browser_active = true;
- const BrowserList* ash_browser_list =
+#if defined(USE_ASH) || defined(OS_CHROMEOS)
+ const BrowserList* browser_list =
BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_ASH);
+#else
+ const BrowserList* browser_list =
+ BrowserList::GetInstance(chrome::HOST_DESKTOP_TYPE_NATIVE);
+#endif
for (BrowserList::const_reverse_iterator browser_iterator =
- ash_browser_list->begin_last_active();
- browser_iterator != ash_browser_list->end_last_active();
+ browser_list->begin_last_active();
+ browser_iterator != browser_list->end_last_active();
++browser_iterator) {
Browser* browser = *browser_iterator;
bool is_browser_for_app = browser->is_app();
@@ -493,8 +401,8 @@ OomPriorityManager::TabStatsList OomPriorityManager::GetTabStatsOnUIThread() {
if (!contents->IsCrashed()) {
TabStats stats;
stats.is_app = is_browser_for_app;
- stats.is_reloadable_ui =
- IsReloadableUI(contents->GetLastCommittedURL());
+ stats.is_internal_page =
+ IsInternalPage(contents->GetLastCommittedURL());
stats.is_playing_audio = chrome::IsPlayingAudio(contents);
stats.is_pinned = model->IsTabPinned(i);
stats.is_selected = browser_active && model->IsTabSelected(i);
@@ -516,69 +424,6 @@ OomPriorityManager::TabStatsList OomPriorityManager::GetTabStatsOnUIThread() {
return stats_list;
}
-// static
-std::vector<OomPriorityManager::ProcessInfo>
-OomPriorityManager::GetChildProcessInfos(const TabStatsList& stats_list) {
- std::vector<ProcessInfo> process_infos;
- std::set<base::ProcessHandle> already_seen;
- for (TabStatsList::const_iterator iterator = stats_list.begin();
- iterator != stats_list.end(); ++iterator) {
- // stats_list contains entries for already-discarded tabs. If the PID
- // (renderer_handle) is zero, we don't need to adjust the oom_score.
- if (iterator->renderer_handle == 0)
- continue;
-
- bool inserted = already_seen.insert(iterator->renderer_handle).second;
- if (!inserted) {
- // We've already seen this process handle.
- continue;
- }
-
- process_infos.push_back(std::make_pair(iterator->child_process_host_id,
- iterator->renderer_handle));
- }
- return process_infos;
-}
-
-void OomPriorityManager::AdjustOomPrioritiesOnFileThread(
- TabStatsList stats_list) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
- base::AutoLock oom_score_autolock(oom_score_lock_);
-
- // Remove any duplicate PIDs. Order of the list is maintained, so each
- // renderer process will take on the oom_score_adj of the most important
- // (least likely to be killed) tab.
- std::vector<ProcessInfo> process_infos = GetChildProcessInfos(stats_list);
-
- // Now we assign priorities based on the sorted list. We're
- // assigning priorities in the range of kLowestRendererOomScore to
- // kHighestRendererOomScore (defined in chrome_constants.h).
- // oom_score_adj takes values from -1000 to 1000. Negative values
- // are reserved for system processes, and we want to give some room
- // below the range we're using to allow for things that want to be
- // above the renderers in priority, so the defined range 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.
- float priority = chrome::kLowestRendererOomScore;
- const int kPriorityRange =
- chrome::kHighestRendererOomScore - chrome::kLowestRendererOomScore;
- float priority_increment =
- static_cast<float>(kPriorityRange) / process_infos.size();
- for (const auto& process_info : process_infos) {
- int score = static_cast<int>(priority + 0.5f);
- ProcessScoreMap::iterator it = oom_score_map_.find(process_info.first);
- // If a process has the same score as the newly calculated value,
- // do not set it.
- if (it == oom_score_map_.end() || it->second != score) {
- content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
- process_info.second, score);
- oom_score_map_[process_info.first] = score;
- }
- priority += priority_increment;
- }
-}
-
void OomPriorityManager::OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
// For the moment we only do something when we reach a critical state.
diff --git a/chrome/browser/memory/oom_priority_manager.h b/chrome/browser/memory/oom_priority_manager.h
index a320c9d..6f63a7b 100644
--- a/chrome/browser/memory/oom_priority_manager.h
+++ b/chrome/browser/memory/oom_priority_manager.h
@@ -9,35 +9,42 @@
#include <vector>
#include "base/compiler_specific.h"
-#include "base/containers/hash_tables.h"
#include "base/gtest_prod_util.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/scoped_ptr.h"
-#include "base/process/process.h"
#include "base/strings/string16.h"
-#include "base/synchronization/lock.h"
-#include "base/time/time.h"
#include "base/timer/timer.h"
-#include "content/public/browser/notification_observer.h"
-#include "content/public/browser/notification_registrar.h"
+#include "chrome/browser/memory/tab_stats.h"
class GURL;
namespace memory {
-// The OomPriorityManager periodically checks (see
-// ADJUSTMENT_INTERVAL_SECONDS in the source) the status of renderers
-// and adjusts the out of memory (OOM) adjustment value (in
-// /proc/<pid>/oom_score_adj) of the renderers so that they match the
-// algorithm embedded here for priority in being killed upon OOM
-// conditions.
+#if defined(OS_CHROMEOS)
+class OomPriorityManagerDelegate;
+#endif
+
+// The OomPriorityManager periodically updates (see
+// |kAdjustmentIntervalSeconds| in the source) the status of renderers
+// which are then used by the algorithm embedded here for priority in being
+// killed upon OOM conditions.
//
// The algorithm used favors killing tabs that are not selected, not pinned,
// and have been idle for longest, in that order of priority.
-class OomPriorityManager : public content::NotificationObserver {
+//
+// On Chrome OS (via the delegate), the kernel (via /proc/<pid>/oom_score_adj)
+// will be informed of each renderer's score, which is based on the status, so
+// in case Chrome is not able to relieve the pressure quickly enough and the
+// kernel is forced to kill processes, it will be able to do so using the same
+// algorithm as the one used here.
+//
+// Note that the browser tests are only active for platforms that use
+// OomPriorityManager (CrOS only for now) and need to be adjusted accordingly if
+// support for new platforms is added.
+class OomPriorityManager {
public:
OomPriorityManager();
- ~OomPriorityManager() override;
+ ~OomPriorityManager();
// Number of discard events since Chrome started.
int discard_count() const { return discard_count_; }
@@ -67,33 +74,15 @@ class OomPriorityManager : public content::NotificationObserver {
private:
FRIEND_TEST_ALL_PREFIXES(OomPriorityManagerTest, Comparator);
- FRIEND_TEST_ALL_PREFIXES(OomPriorityManagerTest, IsReloadableUI);
- FRIEND_TEST_ALL_PREFIXES(OomPriorityManagerTest, GetProcessHandles);
-
- struct TabStats {
- TabStats();
- ~TabStats();
- bool is_app; // browser window is an app
- bool is_reloadable_ui; // Reloadable web UI page, like NTP or Settings.
- bool is_playing_audio;
- bool is_pinned;
- bool is_selected; // selected in the currently active browser window
- bool is_discarded;
- base::TimeTicks last_active;
- base::ProcessHandle renderer_handle;
- int child_process_host_id;
- base::string16 title;
- int64 tab_contents_id; // unique ID per WebContents
- };
- typedef std::vector<TabStats> TabStatsList;
-
- static void PurgeMemoryAndDiscardTabs();
+ FRIEND_TEST_ALL_PREFIXES(OomPriorityManagerTest, IsInternalPage);
+
+ static void PurgeMemoryAndDiscardTab();
// Returns true if the |url| represents an internal Chrome web UI page that
// can be easily reloaded and hence makes a good choice to discard.
- static bool IsReloadableUI(const GURL& url);
+ static bool IsInternalPage(const GURL& url);
- // Discards a tab with the given unique ID. Returns true if discard occurred.
+ // Discards a tab with the given unique ID. Returns true if discard occurred.
bool DiscardTabById(int64 target_web_contents_id);
// Records UMA histogram statistics for a tab discard. We record statistics
@@ -111,56 +100,27 @@ class OomPriorityManager : public content::NotificationObserver {
// Returns the number of tabs open in all browser instances.
int GetTabCount() const;
+ // Returns the list of the stats for all renderers.
TabStatsList GetTabStatsOnUIThread();
- // Called when the timer fires, sets oom_adjust_score for all renderers.
- void AdjustOomPriorities();
-
- // Pair to hold child process host id and ProcessHandle
- typedef std::pair<int, base::ProcessHandle> ProcessInfo;
-
- // Returns a list of child process host ids and ProcessHandles from
- // |stats_list| with unique pids. If multiple tabs use the same process,
- // returns the first child process host id and corresponding pid. This implies
- // that the processes are selected based on their "most important" tab.
- static std::vector<ProcessInfo> GetChildProcessInfos(
- const TabStatsList& stats_list);
-
- // Called by AdjustOomPriorities.
- void AdjustOomPrioritiesOnFileThread(TabStatsList stats_list);
-
- // Posts AdjustFocusedTabScore task to the file thread.
- void OnFocusTabScoreAdjustmentTimeout();
-
- // Sets the score of the focused tab to the least value.
- void AdjustFocusedTabScoreOnFileThread();
+ // Callback for when |update_timer_| fires. Takes care of executing the tasks
+ // that need to be run periodically (see comment in implementation).
+ void UpdateTimerCallback();
static bool CompareTabStats(TabStats first, TabStats second);
- void Observe(int type,
- const content::NotificationSource& source,
- const content::NotificationDetails& details) override;
-
// Called by the memory pressure listener when the memory pressure rises.
void OnMemoryPressure(
base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
- base::RepeatingTimer<OomPriorityManager> timer_;
- base::OneShotTimer<OomPriorityManager> focus_tab_score_adjust_timer_;
+ // Timer to periodically update the stats of the renderers.
+ base::RepeatingTimer<OomPriorityManager> update_timer_;
+
+ // Timer to periodically report whether a tab has been discarded since the
+ // last time the timer has fired.
base::RepeatingTimer<OomPriorityManager> recent_tab_discard_timer_;
- content::NotificationRegistrar registrar_;
-
- // This lock is for |oom_score_map_| and |focused_tab_process_info_|.
- base::Lock oom_score_lock_;
- // Map maintaining the child process host id - oom_score mapping.
- typedef base::hash_map<int, int> ProcessScoreMap;
- ProcessScoreMap oom_score_map_;
- // Holds the focused tab's child process host id.
- ProcessInfo focused_tab_process_info_;
-
- // A listener to global memory pressure events. This will be used if the
- // memory pressure system was instantiated - otherwise the LowMemoryObserver
- // will be used.
+
+ // A listener to global memory pressure events.
scoped_ptr<base::MemoryPressureListener> memory_pressure_listener_;
// Wall-clock time when the priority manager started running.
@@ -181,6 +141,10 @@ class OomPriorityManager : public content::NotificationObserver {
// used for statistics normalized by usage.
bool recent_tab_discard_;
+#if defined(OS_CHROMEOS)
+ scoped_ptr<OomPriorityManagerDelegate> delegate_;
+#endif
+
DISALLOW_COPY_AND_ASSIGN(OomPriorityManager);
};
diff --git a/chrome/browser/memory/oom_priority_manager_browsertest_chromeos.cc b/chrome/browser/memory/oom_priority_manager_browsertest.cc
index 6ebc413..ceae384 100644
--- a/chrome/browser/memory/oom_priority_manager_browsertest_chromeos.cc
+++ b/chrome/browser/memory/oom_priority_manager_browsertest.cc
@@ -19,6 +19,9 @@
using content::OpenURLParams;
+// OomPriorityManager is only active on Chrome OS.
+#if defined(OS_CHROMEOS)
+
namespace memory {
namespace {
@@ -26,9 +29,9 @@ typedef InProcessBrowserTest OomPriorityManagerTest;
IN_PROC_BROWSER_TEST_F(OomPriorityManagerTest, OomPriorityManagerBasics) {
using content::WindowedNotificationObserver;
-
OomPriorityManager* oom_priority_manager =
g_browser_process->GetOomPriorityManager();
+ ASSERT_TRUE(oom_priority_manager);
EXPECT_FALSE(oom_priority_manager->recent_tab_discard());
// Get three tabs open.
@@ -162,6 +165,8 @@ IN_PROC_BROWSER_TEST_F(OomPriorityManagerTest, OomPriorityManagerBasics) {
IN_PROC_BROWSER_TEST_F(OomPriorityManagerTest, OomPressureListener) {
OomPriorityManager* oom_priority_manager =
g_browser_process->GetOomPriorityManager();
+ ASSERT_TRUE(oom_priority_manager);
+
// Get three tabs open.
content::WindowedNotificationObserver load1(
content::NOTIFICATION_NAV_ENTRY_COMMITTED,
@@ -194,7 +199,8 @@ IN_PROC_BROWSER_TEST_F(OomPriorityManagerTest, OomPressureListener) {
const int kIntervalTimeInMS = 5;
int timeout = kTimeoutTimeInMS / kIntervalTimeInMS;
while (--timeout) {
- usleep(kIntervalTimeInMS * 1000);
+ base::PlatformThread::Sleep(
+ base::TimeDelta::FromMilliseconds(kIntervalTimeInMS));
base::RunLoop().RunUntilIdle();
if (oom_priority_manager->recent_tab_discard())
break;
@@ -204,3 +210,5 @@ IN_PROC_BROWSER_TEST_F(OomPriorityManagerTest, OomPressureListener) {
} // namespace
} // namespace memory
+
+#endif // defined(OS_CHROMEOS)
diff --git a/chrome/browser/memory/oom_priority_manager_chromeos_unittest.cc b/chrome/browser/memory/oom_priority_manager_chromeos_unittest.cc
deleted file mode 100644
index 58a48bd..0000000
--- a/chrome/browser/memory/oom_priority_manager_chromeos_unittest.cc
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright (c) 2012 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 <algorithm>
-#include <vector>
-
-#include "base/logging.h"
-#include "base/strings/string16.h"
-#include "base/time/time.h"
-#include "chrome/browser/memory/oom_priority_manager.h"
-#include "chrome/common/url_constants.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "url/gurl.h"
-
-namespace memory {
-
-typedef testing::Test OomPriorityManagerTest;
-
-namespace {
-enum TestIndicies {
- kSelected,
- kPinned,
- kApp,
- kPlayingAudio,
- kRecent,
- kOld,
- kReallyOld,
- kOldButPinned,
- kReloadableUI,
-};
-} // namespace
-
-// Tests the sorting comparator so that we know it's producing the
-// desired order.
-TEST_F(OomPriorityManagerTest, Comparator) {
- OomPriorityManager::TabStatsList test_list;
- const base::TimeTicks now = base::TimeTicks::Now();
-
- // Add kSelected last to verify we are sorting the array.
-
- {
- OomPriorityManager::TabStats stats;
- stats.is_pinned = true;
- stats.renderer_handle = kPinned;
- stats.child_process_host_id = kPinned;
- test_list.push_back(stats);
- }
-
- {
- OomPriorityManager::TabStats stats;
- stats.is_app = true;
- stats.renderer_handle = kApp;
- stats.child_process_host_id = kApp;
- test_list.push_back(stats);
- }
-
- {
- OomPriorityManager::TabStats stats;
- stats.is_playing_audio = true;
- stats.renderer_handle = kPlayingAudio;
- stats.child_process_host_id = kPlayingAudio;
- test_list.push_back(stats);
- }
-
- {
- OomPriorityManager::TabStats stats;
- stats.last_active = now - base::TimeDelta::FromSeconds(10);
- stats.renderer_handle = kRecent;
- stats.child_process_host_id = kRecent;
- test_list.push_back(stats);
- }
-
- {
- OomPriorityManager::TabStats stats;
- stats.last_active = now - base::TimeDelta::FromMinutes(15);
- stats.renderer_handle = kOld;
- stats.child_process_host_id = kOld;
- test_list.push_back(stats);
- }
-
- {
- OomPriorityManager::TabStats stats;
- stats.last_active = now - base::TimeDelta::FromDays(365);
- stats.renderer_handle = kReallyOld;
- stats.child_process_host_id = kReallyOld;
- test_list.push_back(stats);
- }
-
- {
- OomPriorityManager::TabStats stats;
- stats.is_pinned = true;
- stats.last_active = now - base::TimeDelta::FromDays(365);
- stats.renderer_handle = kOldButPinned;
- stats.child_process_host_id = kOldButPinned;
- test_list.push_back(stats);
- }
-
- {
- OomPriorityManager::TabStats stats;
- stats.is_reloadable_ui = true;
- stats.renderer_handle = kReloadableUI;
- stats.child_process_host_id = kReloadableUI;
- test_list.push_back(stats);
- }
-
- // This entry sorts to the front, so by adding it last we verify that
- // we are actually sorting the array.
- {
- OomPriorityManager::TabStats stats;
- stats.is_selected = true;
- stats.renderer_handle = kSelected;
- stats.child_process_host_id = kSelected;
- test_list.push_back(stats);
- }
-
- std::sort(test_list.begin(), test_list.end(),
- OomPriorityManager::CompareTabStats);
-
- int index = 0;
- EXPECT_EQ(kSelected, test_list[index++].renderer_handle);
- EXPECT_EQ(kPinned, test_list[index++].renderer_handle);
- EXPECT_EQ(kOldButPinned, test_list[index++].renderer_handle);
- EXPECT_EQ(kApp, test_list[index++].renderer_handle);
- EXPECT_EQ(kPlayingAudio, test_list[index++].renderer_handle);
- EXPECT_EQ(kRecent, test_list[index++].renderer_handle);
- EXPECT_EQ(kOld, test_list[index++].renderer_handle);
- EXPECT_EQ(kReallyOld, test_list[index++].renderer_handle);
- EXPECT_EQ(kReloadableUI, test_list[index++].renderer_handle);
-
- index = 0;
- EXPECT_EQ(kSelected, test_list[index++].child_process_host_id);
- EXPECT_EQ(kPinned, test_list[index++].child_process_host_id);
- EXPECT_EQ(kOldButPinned, test_list[index++].child_process_host_id);
- EXPECT_EQ(kApp, test_list[index++].child_process_host_id);
- EXPECT_EQ(kPlayingAudio, test_list[index++].child_process_host_id);
- EXPECT_EQ(kRecent, test_list[index++].child_process_host_id);
- EXPECT_EQ(kOld, test_list[index++].child_process_host_id);
- EXPECT_EQ(kReallyOld, test_list[index++].child_process_host_id);
- EXPECT_EQ(kReloadableUI, test_list[index++].child_process_host_id);
-}
-
-TEST_F(OomPriorityManagerTest, IsReloadableUI) {
- EXPECT_TRUE(
- OomPriorityManager::IsReloadableUI(GURL(chrome::kChromeUIDownloadsURL)));
- EXPECT_TRUE(
- OomPriorityManager::IsReloadableUI(GURL(chrome::kChromeUIHistoryURL)));
- EXPECT_TRUE(
- OomPriorityManager::IsReloadableUI(GURL(chrome::kChromeUINewTabURL)));
- EXPECT_TRUE(
- OomPriorityManager::IsReloadableUI(GURL(chrome::kChromeUISettingsURL)));
-
- // Debugging URLs are not included.
- EXPECT_FALSE(
- OomPriorityManager::IsReloadableUI(GURL(chrome::kChromeUIDiscardsURL)));
- EXPECT_FALSE(OomPriorityManager::IsReloadableUI(
- GURL(chrome::kChromeUINetInternalsURL)));
-
- // Prefix matches are included.
- EXPECT_TRUE(OomPriorityManager::IsReloadableUI(
- GURL("chrome://settings/fakeSetting")));
-}
-
-TEST_F(OomPriorityManagerTest, GetProcessHandles) {
- OomPriorityManager::TabStats stats;
- std::vector<OomPriorityManager::ProcessInfo> process_id_pairs;
-
- // Empty stats list gives empty |process_id_pairs| list.
- OomPriorityManager::TabStatsList empty_list;
- process_id_pairs = OomPriorityManager::GetChildProcessInfos(empty_list);
- EXPECT_EQ(0u, process_id_pairs.size());
-
- // Two tabs in two different processes generates two
- // |child_process_host_id| out.
- OomPriorityManager::TabStatsList two_list;
- stats.child_process_host_id = 100;
- stats.renderer_handle = 101;
- two_list.push_back(stats);
- stats.child_process_host_id = 200;
- stats.renderer_handle = 201;
- two_list.push_back(stats);
- process_id_pairs = OomPriorityManager::GetChildProcessInfos(two_list);
- EXPECT_EQ(2u, process_id_pairs.size());
- EXPECT_EQ(100, process_id_pairs[0].first);
- EXPECT_EQ(101, process_id_pairs[0].second);
- EXPECT_EQ(200, process_id_pairs[1].first);
- EXPECT_EQ(201, process_id_pairs[1].second);
-
- // Zero handles are removed.
- OomPriorityManager::TabStatsList zero_handle_list;
- stats.child_process_host_id = 100;
- stats.renderer_handle = 0;
- zero_handle_list.push_back(stats);
- process_id_pairs = OomPriorityManager::GetChildProcessInfos(zero_handle_list);
- EXPECT_EQ(0u, process_id_pairs.size());
-
- // Two tabs in the same process generates one handle out. When a duplicate
- // occurs the later instance is dropped.
- OomPriorityManager::TabStatsList same_process_list;
- stats.child_process_host_id = 100;
- stats.renderer_handle = 101;
- same_process_list.push_back(stats);
- stats.child_process_host_id = 200;
- stats.renderer_handle = 201;
- same_process_list.push_back(stats);
- stats.child_process_host_id = 300;
- stats.renderer_handle = 101; // Duplicate.
- same_process_list.push_back(stats);
- process_id_pairs =
- OomPriorityManager::GetChildProcessInfos(same_process_list);
- EXPECT_EQ(2u, process_id_pairs.size());
- EXPECT_EQ(100, process_id_pairs[0].first);
- EXPECT_EQ(101, process_id_pairs[0].second);
- EXPECT_EQ(200, process_id_pairs[1].first);
- EXPECT_EQ(201, process_id_pairs[1].second);
-}
-
-} // namespace memory
diff --git a/chrome/browser/memory/oom_priority_manager_delegate_chromeos.cc b/chrome/browser/memory/oom_priority_manager_delegate_chromeos.cc
new file mode 100644
index 0000000..e7f5c1a
--- /dev/null
+++ b/chrome/browser/memory/oom_priority_manager_delegate_chromeos.cc
@@ -0,0 +1,216 @@
+// Copyright 2015 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/memory/oom_priority_manager_delegate_chromeos.h"
+
+#include "base/memory/memory_pressure_monitor_chromeos.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/memory/tab_stats.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/common/chrome_constants.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_process_host.h"
+#include "content/public/browser/render_widget_host.h"
+#include "content/public/browser/zygote_host_linux.h"
+
+using base::TimeDelta;
+using content::BrowserThread;
+
+namespace memory {
+namespace {
+
+// When switching to a new tab the tab's renderer's OOM score needs to be
+// updated to reflect its front-most status and protect it from discard.
+// However, doing this immediately might slow down tab switch time, so wait
+// a little while before doing the adjustment.
+const int kFocusedTabScoreAdjustIntervalMs = 500;
+
+} // namespace
+
+OomPriorityManagerDelegate::OomPriorityManagerDelegate()
+ : focused_tab_process_info_(std::make_pair(0, 0)) {
+ registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
+ content::NotificationService::AllBrowserContextsAndSources());
+ registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_TERMINATED,
+ content::NotificationService::AllBrowserContextsAndSources());
+ registrar_.Add(this, content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED,
+ content::NotificationService::AllBrowserContextsAndSources());
+}
+
+OomPriorityManagerDelegate::~OomPriorityManagerDelegate() {
+}
+
+int OomPriorityManagerDelegate::GetOomScore(int child_process_host_id) {
+ base::AutoLock oom_score_autolock(oom_score_lock_);
+ int score = oom_score_map_[child_process_host_id];
+ return score;
+}
+
+void OomPriorityManagerDelegate::AdjustFocusedTabScoreOnFileThread() {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ base::AutoLock oom_score_autolock(oom_score_lock_);
+ base::ProcessHandle pid = focused_tab_process_info_.second;
+ content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
+ pid, chrome::kLowestRendererOomScore);
+ oom_score_map_[focused_tab_process_info_.first] =
+ chrome::kLowestRendererOomScore;
+}
+
+void OomPriorityManagerDelegate::OnFocusTabScoreAdjustmentTimeout() {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OomPriorityManagerDelegate::AdjustFocusedTabScoreOnFileThread,
+ base::Unretained(this)));
+}
+void OomPriorityManagerDelegate::Observe(
+ int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) {
+ base::AutoLock oom_score_autolock(oom_score_lock_);
+ switch (type) {
+ case content::NOTIFICATION_RENDERER_PROCESS_CLOSED:
+ case content::NOTIFICATION_RENDERER_PROCESS_TERMINATED: {
+ content::RenderProcessHost* host =
+ content::Source<content::RenderProcessHost>(source).ptr();
+ oom_score_map_.erase(host->GetID());
+ // Coming here we know that a renderer was just killed and memory should
+ // come back into the pool. However - the memory pressure observer did
+ // not yet update its status and therefore we ask it to redo the
+ // measurement, calling us again if we have to release more.
+ // Note: We do not only accelerate the discarding speed by doing another
+ // check in short succession - we also accelerate it because the timer
+ // driven MemoryPressureMonitor will continue to produce timed events
+ // on top. So the longer the cleanup phase takes, the more tabs will
+ // get discarded in parallel.
+ base::chromeos::MemoryPressureMonitor* monitor =
+ base::chromeos::MemoryPressureMonitor::Get();
+ if (monitor)
+ monitor->ScheduleEarlyCheck();
+ break;
+ }
+ case content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED: {
+ bool visible = *content::Details<bool>(details).ptr();
+ if (visible) {
+ content::RenderProcessHost* render_host =
+ content::Source<content::RenderWidgetHost>(source)
+ .ptr()
+ ->GetProcess();
+ focused_tab_process_info_ =
+ std::make_pair(render_host->GetID(), render_host->GetHandle());
+
+ // If the currently focused tab already has a lower score, do not
+ // set it. This can happen in case the newly focused tab is script
+ // connected to the previous tab.
+ ProcessScoreMap::iterator it;
+ it = oom_score_map_.find(focused_tab_process_info_.first);
+ if (it == oom_score_map_.end() ||
+ it->second != chrome::kLowestRendererOomScore) {
+ // By starting a timer we guarantee that the tab is focused for
+ // certain amount of time. Secondly, it also does not add overhead
+ // to the tab switching time.
+ if (focus_tab_score_adjust_timer_.IsRunning())
+ focus_tab_score_adjust_timer_.Reset();
+ else
+ focus_tab_score_adjust_timer_.Start(
+ FROM_HERE,
+ TimeDelta::FromMilliseconds(kFocusedTabScoreAdjustIntervalMs),
+ this,
+ &OomPriorityManagerDelegate::OnFocusTabScoreAdjustmentTimeout);
+ }
+ }
+ break;
+ }
+ default:
+ NOTREACHED() << "Received unexpected notification";
+ break;
+ }
+}
+
+// Here we collect most of the information we need to sort the existing
+// renderers in priority order, and hand out oom_score_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
+// 3) is the tab currently selected
+void OomPriorityManagerDelegate::AdjustOomPriorities(
+ const TabStatsList& stats_list) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&OomPriorityManagerDelegate::AdjustOomPrioritiesOnFileThread,
+ base::Unretained(this), stats_list));
+}
+
+// static
+std::vector<OomPriorityManagerDelegate::ProcessInfo>
+OomPriorityManagerDelegate::GetChildProcessInfos(
+ const TabStatsList& stats_list) {
+ std::vector<ProcessInfo> process_infos;
+ std::set<base::ProcessHandle> already_seen;
+ for (TabStatsList::const_iterator iterator = stats_list.begin();
+ iterator != stats_list.end(); ++iterator) {
+ // stats_list contains entries for already-discarded tabs. If the PID
+ // (renderer_handle) is zero, we don't need to adjust the oom_score.
+ if (iterator->renderer_handle == 0)
+ continue;
+
+ bool inserted = already_seen.insert(iterator->renderer_handle).second;
+ if (!inserted) {
+ // We've already seen this process handle.
+ continue;
+ }
+
+ process_infos.push_back(std::make_pair(iterator->child_process_host_id,
+ iterator->renderer_handle));
+ }
+ return process_infos;
+}
+
+void OomPriorityManagerDelegate::AdjustOomPrioritiesOnFileThread(
+ TabStatsList stats_list) {
+ DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+ base::AutoLock oom_score_autolock(oom_score_lock_);
+
+ // Remove any duplicate PIDs. Order of the list is maintained, so each
+ // renderer process will take on the oom_score_adj of the most important
+ // (least likely to be killed) tab.
+ std::vector<ProcessInfo> process_infos = GetChildProcessInfos(stats_list);
+
+ // Now we assign priorities based on the sorted list. We're assigning
+ // priorities in the range of kLowestRendererOomScore to
+ // kHighestRendererOomScore (defined in chrome_constants.h). oom_score_adj
+ // takes values from -1000 to 1000. Negative values are reserved for system
+ // processes, and we want to give some room below the range we're using to
+ // allow for things that want to be above the renderers in priority, so the
+ // defined range 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.
+ float priority = chrome::kLowestRendererOomScore;
+ const int kPriorityRange =
+ chrome::kHighestRendererOomScore - chrome::kLowestRendererOomScore;
+ float priority_increment =
+ static_cast<float>(kPriorityRange) / process_infos.size();
+ for (const auto& process_info : process_infos) {
+ int score = static_cast<int>(priority + 0.5f);
+ ProcessScoreMap::iterator it = oom_score_map_.find(process_info.first);
+ // If a process has the same score as the newly calculated value,
+ // do not set it.
+ if (it == oom_score_map_.end() || it->second != score) {
+ content::ZygoteHost::GetInstance()->AdjustRendererOOMScore(
+ process_info.second, score);
+ oom_score_map_[process_info.first] = score;
+ }
+ priority += priority_increment;
+ }
+}
+
+} // namespace memory
diff --git a/chrome/browser/memory/oom_priority_manager_delegate_chromeos.h b/chrome/browser/memory/oom_priority_manager_delegate_chromeos.h
new file mode 100644
index 0000000..a9824b4
--- /dev/null
+++ b/chrome/browser/memory/oom_priority_manager_delegate_chromeos.h
@@ -0,0 +1,82 @@
+// Copyright 2015 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.
+
+#ifndef CHROME_BROWSER_MEMORY_OOM_PRIORITY_MANAGER_DELEGATE_CHROMEOS_H_
+#define CHROME_BROWSER_MEMORY_OOM_PRIORITY_MANAGER_DELEGATE_CHROMEOS_H_
+
+#include <utility>
+#include <vector>
+
+#include "base/containers/hash_tables.h"
+#include "base/process/process.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/memory/oom_priority_manager.h"
+#include "chrome/browser/memory/tab_stats.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+
+namespace memory {
+
+// The Chrome OS OomPriorityManagerDelegate is responsible for keeping the
+// renderers' scores up to date in /proc/<pid>/oom_score_adj.
+//
+// Note that AdjustOomPriorities will be called on the UI thread by
+// OomPriorityManager, but the actual work will take place on the file thread
+// (see implementation of AdjustOomPriorities).
+class OomPriorityManagerDelegate : public content::NotificationObserver {
+ public:
+ OomPriorityManagerDelegate();
+ ~OomPriorityManagerDelegate() override;
+
+ // Return the score of a process.
+ int GetOomScore(int child_process_host_id);
+
+ // Called when the timer fires, sets oom_adjust_score for all renderers.
+ void AdjustOomPriorities(const TabStatsList& stats_list);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(OomPriorityManagerDelegateTest, GetProcessHandles);
+
+ // content::NotificationObserver:
+ void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) override;
+
+ // Pair to hold child process host id and ProcessHandle.
+ typedef std::pair<int, base::ProcessHandle> ProcessInfo;
+
+ // Returns a list of child process host ids and ProcessHandles from
+ // |stats_list| with unique pids. If multiple tabs use the same process,
+ // returns the first child process host id and corresponding pid. This implies
+ // that the processes are selected based on their "most important" tab.
+ static std::vector<ProcessInfo> GetChildProcessInfos(
+ const TabStatsList& stats_list);
+
+ // Called by AdjustOomPriorities.
+ void AdjustOomPrioritiesOnFileThread(TabStatsList stats_list);
+
+ // Posts AdjustFocusedTabScore task to the file thread.
+ void OnFocusTabScoreAdjustmentTimeout();
+
+ // Sets the score of the focused tab to the least value.
+ void AdjustFocusedTabScoreOnFileThread();
+
+ // Registrar to receive renderer notifications.
+ content::NotificationRegistrar registrar_;
+ // Timer to guarantee that the tab is focused for a certain amount of time.
+ base::OneShotTimer<OomPriorityManagerDelegate> focus_tab_score_adjust_timer_;
+ // This lock is for |oom_score_map_| and |focused_tab_process_info_|.
+ base::Lock oom_score_lock_;
+ // Map maintaining the child process host id - oom_score mapping.
+ typedef base::hash_map<int, int> ProcessScoreMap;
+ ProcessScoreMap oom_score_map_;
+ // Holds the focused tab's child process host id.
+ ProcessInfo focused_tab_process_info_;
+
+ DISALLOW_COPY_AND_ASSIGN(OomPriorityManagerDelegate);
+};
+
+} // namespace memory
+
+#endif // CHROME_BROWSER_MEMORY_OOM_PRIORITY_MANAGER_DELEGATE_CHROMEOS_H_
diff --git a/chrome/browser/memory/oom_priority_manager_delegate_chromeos_unittest.cc b/chrome/browser/memory/oom_priority_manager_delegate_chromeos_unittest.cc
new file mode 100644
index 0000000..4b73b6e
--- /dev/null
+++ b/chrome/browser/memory/oom_priority_manager_delegate_chromeos_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2012 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/memory/oom_priority_manager.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "chrome/browser/memory/oom_priority_manager_delegate_chromeos.h"
+#include "chrome/browser/memory/tab_stats.h"
+#include "chrome/common/url_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace memory {
+
+typedef testing::Test OomPriorityManagerDelegateTest;
+
+TEST_F(OomPriorityManagerDelegateTest, GetProcessHandles) {
+ TabStats stats;
+ std::vector<OomPriorityManagerDelegate::ProcessInfo> process_id_pairs;
+
+ // Empty stats list gives empty |process_id_pairs| list.
+ TabStatsList empty_list;
+ process_id_pairs =
+ OomPriorityManagerDelegate::GetChildProcessInfos(empty_list);
+ EXPECT_EQ(0u, process_id_pairs.size());
+
+ // Two tabs in two different processes generates two
+ // |child_process_host_id| out.
+ TabStatsList two_list;
+ stats.child_process_host_id = 100;
+ stats.renderer_handle = 101;
+ two_list.push_back(stats);
+ stats.child_process_host_id = 200;
+ stats.renderer_handle = 201;
+ two_list.push_back(stats);
+ process_id_pairs = OomPriorityManagerDelegate::GetChildProcessInfos(two_list);
+ EXPECT_EQ(2u, process_id_pairs.size());
+ EXPECT_EQ(100, process_id_pairs[0].first);
+ EXPECT_EQ(101, process_id_pairs[0].second);
+ EXPECT_EQ(200, process_id_pairs[1].first);
+ EXPECT_EQ(201, process_id_pairs[1].second);
+
+ // Zero handles are removed.
+ TabStatsList zero_handle_list;
+ stats.child_process_host_id = 100;
+ stats.renderer_handle = 0;
+ zero_handle_list.push_back(stats);
+ process_id_pairs =
+ OomPriorityManagerDelegate::GetChildProcessInfos(zero_handle_list);
+ EXPECT_EQ(0u, process_id_pairs.size());
+
+ // Two tabs in the same process generates one handle out. When a duplicate
+ // occurs the later instance is dropped.
+ TabStatsList same_process_list;
+ stats.child_process_host_id = 100;
+ stats.renderer_handle = 101;
+ same_process_list.push_back(stats);
+ stats.child_process_host_id = 200;
+ stats.renderer_handle = 201;
+ same_process_list.push_back(stats);
+ stats.child_process_host_id = 300;
+ stats.renderer_handle = 101; // Duplicate.
+ same_process_list.push_back(stats);
+ process_id_pairs =
+ OomPriorityManagerDelegate::GetChildProcessInfos(same_process_list);
+ EXPECT_EQ(2u, process_id_pairs.size());
+ EXPECT_EQ(100, process_id_pairs[0].first);
+ EXPECT_EQ(101, process_id_pairs[0].second);
+ EXPECT_EQ(200, process_id_pairs[1].first);
+ EXPECT_EQ(201, process_id_pairs[1].second);
+}
+
+} // namespace memory
diff --git a/chrome/browser/memory/oom_priority_manager_unittest.cc b/chrome/browser/memory/oom_priority_manager_unittest.cc
new file mode 100644
index 0000000..f3af781
--- /dev/null
+++ b/chrome/browser/memory/oom_priority_manager_unittest.cc
@@ -0,0 +1,148 @@
+// Copyright (c) 2012 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/memory/oom_priority_manager.h"
+
+#include <algorithm>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+#include "chrome/browser/memory/tab_stats.h"
+#include "chrome/common/url_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace memory {
+
+typedef testing::Test OomPriorityManagerTest;
+
+namespace {
+enum TestIndicies {
+ kSelected,
+ kPinned,
+ kApp,
+ kPlayingAudio,
+ kRecent,
+ kOld,
+ kReallyOld,
+ kOldButPinned,
+ kInternalPage,
+};
+} // namespace
+
+// Tests the sorting comparator so that we know it's producing the
+// desired order.
+TEST_F(OomPriorityManagerTest, Comparator) {
+ TabStatsList test_list;
+ const base::TimeTicks now = base::TimeTicks::Now();
+
+ // Add kSelected last to verify we are sorting the array.
+
+ {
+ TabStats stats;
+ stats.is_pinned = true;
+ stats.child_process_host_id = kPinned;
+ test_list.push_back(stats);
+ }
+
+ {
+ TabStats stats;
+ stats.is_app = true;
+ stats.child_process_host_id = kApp;
+ test_list.push_back(stats);
+ }
+
+ {
+ TabStats stats;
+ stats.is_playing_audio = true;
+ stats.child_process_host_id = kPlayingAudio;
+ test_list.push_back(stats);
+ }
+
+ {
+ TabStats stats;
+ stats.last_active = now - base::TimeDelta::FromSeconds(10);
+ stats.child_process_host_id = kRecent;
+ test_list.push_back(stats);
+ }
+
+ {
+ TabStats stats;
+ stats.last_active = now - base::TimeDelta::FromMinutes(15);
+ stats.child_process_host_id = kOld;
+ test_list.push_back(stats);
+ }
+
+ {
+ TabStats stats;
+ stats.last_active = now - base::TimeDelta::FromDays(365);
+ stats.child_process_host_id = kReallyOld;
+ test_list.push_back(stats);
+ }
+
+ {
+ TabStats stats;
+ stats.is_pinned = true;
+ stats.last_active = now - base::TimeDelta::FromDays(365);
+ stats.child_process_host_id = kOldButPinned;
+ test_list.push_back(stats);
+ }
+
+ {
+ TabStats stats;
+ stats.is_internal_page = true;
+ stats.child_process_host_id = kInternalPage;
+ test_list.push_back(stats);
+ }
+
+ // This entry sorts to the front, so by adding it last we verify that
+ // we are actually sorting the array.
+ {
+ TabStats stats;
+ stats.is_selected = true;
+ stats.child_process_host_id = kSelected;
+ test_list.push_back(stats);
+ }
+
+ std::sort(test_list.begin(), test_list.end(),
+ OomPriorityManager::CompareTabStats);
+
+ int index = 0;
+ EXPECT_EQ(kSelected, test_list[index++].child_process_host_id);
+ EXPECT_EQ(kPinned, test_list[index++].child_process_host_id);
+ EXPECT_EQ(kOldButPinned, test_list[index++].child_process_host_id);
+ EXPECT_EQ(kApp, test_list[index++].child_process_host_id);
+ EXPECT_EQ(kPlayingAudio, test_list[index++].child_process_host_id);
+ EXPECT_EQ(kRecent, test_list[index++].child_process_host_id);
+ EXPECT_EQ(kOld, test_list[index++].child_process_host_id);
+ EXPECT_EQ(kReallyOld, test_list[index++].child_process_host_id);
+ EXPECT_EQ(kInternalPage, test_list[index++].child_process_host_id);
+}
+
+TEST_F(OomPriorityManagerTest, IsInternalPage) {
+ EXPECT_TRUE(
+ OomPriorityManager::IsInternalPage(GURL(chrome::kChromeUIDownloadsURL)));
+ EXPECT_TRUE(
+ OomPriorityManager::IsInternalPage(GURL(chrome::kChromeUIHistoryURL)));
+ EXPECT_TRUE(
+ OomPriorityManager::IsInternalPage(GURL(chrome::kChromeUINewTabURL)));
+ EXPECT_TRUE(
+ OomPriorityManager::IsInternalPage(GURL(chrome::kChromeUISettingsURL)));
+
+// Debugging URLs are not included.
+#if defined(OS_CHROMEOS)
+ EXPECT_FALSE(
+ OomPriorityManager::IsInternalPage(GURL(chrome::kChromeUIDiscardsURL)));
+#endif
+ EXPECT_FALSE(OomPriorityManager::IsInternalPage(
+ GURL(chrome::kChromeUINetInternalsURL)));
+
+ // Prefix matches are included.
+ EXPECT_TRUE(OomPriorityManager::IsInternalPage(
+ GURL("chrome://settings/fakeSetting")));
+}
+
+} // namespace memory
diff --git a/chrome/browser/memory/tab_stats.cc b/chrome/browser/memory/tab_stats.cc
new file mode 100644
index 0000000..a7badb9
--- /dev/null
+++ b/chrome/browser/memory/tab_stats.cc
@@ -0,0 +1,24 @@
+// Copyright 2015 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/memory/tab_stats.h"
+
+namespace memory {
+
+TabStats::TabStats()
+ : is_app(false),
+ is_internal_page(false),
+ is_playing_audio(false),
+ is_pinned(false),
+ is_selected(false),
+ is_discarded(false),
+ renderer_handle(0),
+ child_process_host_id(0),
+ tab_contents_id(0) {
+}
+
+TabStats::~TabStats() {
+}
+
+} // namespace memory
diff --git a/chrome/browser/memory/tab_stats.h b/chrome/browser/memory/tab_stats.h
new file mode 100644
index 0000000..690f6a8
--- /dev/null
+++ b/chrome/browser/memory/tab_stats.h
@@ -0,0 +1,36 @@
+// Copyright 2015 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.
+
+#ifndef CHROME_BROWSER_MEMORY_TAB_STATS_H_
+#define CHROME_BROWSER_MEMORY_TAB_STATS_H_
+
+#include <vector>
+
+#include "base/process/process.h"
+#include "base/strings/string16.h"
+#include "base/time/time.h"
+
+namespace memory {
+
+struct TabStats {
+ TabStats();
+ ~TabStats();
+ bool is_app; // Browser window is an app.
+ bool is_internal_page; // Internal page, such as NTP or Settings.
+ bool is_playing_audio;
+ bool is_pinned;
+ bool is_selected; // Selected in the currently active browser window.
+ bool is_discarded;
+ base::TimeTicks last_active;
+ base::ProcessHandle renderer_handle;
+ int child_process_host_id;
+ base::string16 title;
+ int64 tab_contents_id; // Unique ID per WebContents.
+};
+
+typedef std::vector<TabStats> TabStatsList;
+
+} // namespace memory
+
+#endif // CHROME_BROWSER_MEMORY_TAB_STATS_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 6718aad..4ab2be3 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -623,11 +623,15 @@
'browser/media/webrtc_log_list.h',
'browser/memory/oom_memory_details.cc',
'browser/memory/oom_memory_details.h',
+ 'browser/memory/oom_priority_manager.cc',
'browser/memory/oom_priority_manager.h',
- 'browser/memory/oom_priority_manager_chromeos.cc',
+ 'browser/memory/oom_priority_manager_delegate_chromeos.cc',
+ 'browser/memory/oom_priority_manager_delegate_chromeos.h',
'browser/memory/system_memory_stats_recorder.h',
'browser/memory/system_memory_stats_recorder_linux.cc',
'browser/memory/system_memory_stats_recorder_win.cc',
+ 'browser/memory/tab_stats.cc',
+ 'browser/memory/tab_stats.h',
'browser/memory_details.cc',
'browser/memory_details.h',
'browser/memory_details_android.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 00e1932..b324ac9 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -340,7 +340,7 @@
'browser/media_galleries/fileapi/media_file_validator_browsertest.cc',
'browser/media_galleries/media_galleries_dialog_controller_mock.cc',
'browser/media_galleries/media_galleries_dialog_controller_mock.h',
- 'browser/memory/oom_priority_manager_browsertest_chromeos.cc',
+ 'browser/memory/oom_priority_manager_browsertest.cc',
'browser/metrics/metrics_memory_details_browsertest.cc',
'browser/metrics/metrics_service_browsertest.cc',
'browser/net/cookie_policy_browsertest.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 8db95cf..5dc6a29 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -126,7 +126,8 @@
'browser/manifest/manifest_icon_selector_unittest.cc',
'browser/media/midi_permission_context_unittest.cc',
'browser/media/native_desktop_media_list_unittest.cc',
- 'browser/memory/oom_priority_manager_chromeos_unittest.cc',
+ 'browser/memory/oom_priority_manager_delegate_chromeos_unittest.cc',
+ 'browser/memory/oom_priority_manager_unittest.cc',
'browser/metrics/chrome_browser_main_extra_parts_metrics_unittest.cc',
'browser/metrics/chrome_metrics_service_accessor_unittest.cc',
'browser/metrics/cloned_install_detector_unittest.cc',