CrOS - about:discards shows tab to discard in OOM situations
Added a simple, non-localized debug page to show the output of
the OOM-adjust algorithm, in preparation for discarding a tab in
low memory situations. See "Out of memory handling" Chrome OS
design doc for details.
BUG=chromium-os:18375
TEST=open 3 tabs and navigate to web pages, wait 10 seconds, then open a tab to "about:discards". It should list 3 tabs and itself. Order is unimportant for now.
Review URL: http://codereview.chromium.org/7754002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@100170 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc
index e460e21..1e37ef36 100644
--- a/chrome/browser/browser_about_handler.cc
+++ b/chrome/browser/browser_about_handler.cc
@@ -77,6 +77,7 @@
#include "chrome/browser/chromeos/customization_document.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/version_loader.h"
+#include "chrome/browser/oom_priority_manager.h"
#include "content/browser/zygote_host_linux.h"
#elif defined(OS_LINUX)
#include "content/browser/zygote_host_linux.h"
@@ -158,6 +159,7 @@
chrome::kChromeUIActiveDownloadsHost,
chrome::kChromeUIChooseMobileNetworkHost,
chrome::kChromeUICryptohomeHost,
+ chrome::kChromeUIDiscardsHost,
chrome::kChromeUIImageBurnerHost,
chrome::kChromeUIKeyboardOverlayHost,
chrome::kChromeUILoginHost,
@@ -191,8 +193,9 @@
chrome::kChromeUISandboxHost,
#endif
#if defined(OS_CHROMEOS)
- chrome::kChromeUINetworkHost,
chrome::kChromeUICryptohomeHost,
+ chrome::kChromeUIDiscardsHost,
+ chrome::kChromeUINetworkHost,
chrome::kChromeUIOSCreditsHost,
#endif
};
@@ -255,7 +258,7 @@
request_id_(request_id) {
}
- virtual void OnDetailsAvailable();
+ virtual void OnDetailsAvailable() OVERRIDE;
private:
~AboutMemoryHandler() {}
@@ -443,6 +446,11 @@
// Html output helper functions
// TODO(stevenjb): L10N this.
+// Helper function to wrap HTML with a tag.
+std::string WrapWithTag(const std::string& tag, const std::string& text) {
+ return "<" + tag + ">" + text + "</" + tag + ">";
+}
+
// Helper function to wrap Html with <th> tag.
std::string WrapWithTH(const std::string& text) {
return "<th>" + text + "</th>";
@@ -670,6 +678,31 @@
return GetCryptohomeHtmlInfo(refresh);
}
+std::string AboutDiscards() {
+ std::string output;
+ AppendHeader(&output, 0, "About discards");
+ AppendBody(&output);
+ output.append("<h3>About discards</h3>");
+ output.append(
+ "<p>Tabs sorted from most interesting to least interesting. The least "
+ "interesting tab may be discarded if we run out of physical memory.</p>");
+
+ std::vector<string16> titles = browser::OomPriorityManager::GetTabTitles();
+ if (!titles.empty()) {
+ output.append("<ol>");
+ std::vector<string16>::iterator it = titles.begin();
+ for ( ; it != titles.end(); ++it) {
+ std::string title = UTF16ToUTF8(*it);
+ output.append(WrapWithTag("li", title));
+ }
+ output.append("</ol>");
+ } else {
+ output.append("<p>None found. Wait 10 seconds, then refresh.</p>");
+ }
+ AppendFooter(&output);
+ return output;
+}
+
#endif // OS_CHROMEOS
// AboutDnsHandler bounces the request back to the IO thread to collect
@@ -1346,26 +1379,62 @@
int request_id) {
std::string response;
std::string host = source_name();
- if (host == chrome::kChromeUIDNSHost) {
+ // Add your data source here, in alphabetical order.
+ if (host == chrome::kChromeUIChromeURLsHost) {
+ response = ChromeURLs();
+ } else if (host == chrome::kChromeUICreditsHost) {
+ int idr = (path == kCreditsJsPath) ? IDR_CREDITS_JS : IDR_CREDITS_HTML;
+ response = ResourceBundle::GetSharedInstance().GetRawDataResource(
+ idr).as_string();
+#if defined(OS_CHROMEOS)
+ } else if (host == chrome::kChromeUICryptohomeHost) {
+ response = AboutCryptohome(path);
+ } else if (host == chrome::kChromeUIDiscardsHost) {
+ response = AboutDiscards();
+#endif
+ } else if (host == chrome::kChromeUIDNSHost) {
AboutDnsHandler::Start(this, request_id);
return;
} else if (host == chrome::kChromeUIHistogramsHost) {
response = AboutHistograms(path);
+#if defined(OS_LINUX)
+ } else if (host == chrome::kChromeUILinuxProxyConfigHost) {
+ response = AboutLinuxProxyConfig();
+#endif
} else if (host == chrome::kChromeUIMemoryHost) {
response = GetAboutMemoryRedirectResponse(profile());
} else if (host == chrome::kChromeUIMemoryRedirectHost) {
AboutMemory(path, this, request_id);
return;
+#if defined(OS_CHROMEOS)
+ } else if (host == chrome::kChromeUINetworkHost) {
+ response = AboutNetwork(path);
+ } else if (host == chrome::kChromeUIOSCreditsHost) {
+ response = ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_OS_CREDITS_HTML).as_string();
+#endif
+#if defined(OS_LINUX)
+ } else if (host == chrome::kChromeUISandboxHost) {
+ response = AboutSandbox();
+#endif
+ } else if (host == chrome::kChromeUIStatsHost) {
+ response = AboutStats(path);
#ifdef TRACK_ALL_TASK_OBJECTS
} else if (host == chrome::kChromeUITaskManagerHost) {
response = AboutObjects(path);
#endif
- } else if (host == chrome::kChromeUIStatsHost) {
- response = AboutStats(path);
#if defined(USE_TCMALLOC)
} else if (host == chrome::kChromeUITCMallocHost) {
response = AboutTcmalloc();
#endif
+ } else if (host == chrome::kChromeUITermsHost) {
+#if defined(OS_CHROMEOS)
+ ChromeOSTermsHandler::Start(this, path, request_id);
+ return;
+#else
+ response = ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_TERMS_HTML).as_string();
+#endif
} else if (host == chrome::kChromeUIVersionHost) {
if (path == kStringsJsPath) {
#if defined(OS_CHROMEOS)
@@ -1379,35 +1448,6 @@
} else {
response = AboutVersionStaticContent(path);
}
- } else if (host == chrome::kChromeUICreditsHost) {
- int idr = (path == kCreditsJsPath) ? IDR_CREDITS_JS : IDR_CREDITS_HTML;
- response = ResourceBundle::GetSharedInstance().GetRawDataResource(
- idr).as_string();
- } else if (host == chrome::kChromeUIChromeURLsHost) {
- response = ChromeURLs();
-#if defined(OS_CHROMEOS)
- } else if (host == chrome::kChromeUIOSCreditsHost) {
- response = ResourceBundle::GetSharedInstance().GetRawDataResource(
- IDR_OS_CREDITS_HTML).as_string();
- } else if (host == chrome::kChromeUINetworkHost) {
- response = AboutNetwork(path);
- } else if (host == chrome::kChromeUICryptohomeHost) {
- response = AboutCryptohome(path);
-#endif
- } else if (host == chrome::kChromeUITermsHost) {
-#if defined(OS_CHROMEOS)
- ChromeOSTermsHandler::Start(this, path, request_id);
- return;
-#else
- response = ResourceBundle::GetSharedInstance().GetRawDataResource(
- IDR_TERMS_HTML).as_string();
-#endif
-#if defined(OS_LINUX)
- } else if (host == chrome::kChromeUILinuxProxyConfigHost) {
- response = AboutLinuxProxyConfig();
- } else if (host == chrome::kChromeUISandboxHost) {
- response = AboutSandbox();
-#endif
}
FinishDataRequest(response, request_id);
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc
index cbac055..a069e422 100644
--- a/chrome/browser/browser_main.cc
+++ b/chrome/browser/browser_main.cc
@@ -701,7 +701,7 @@
shutdown_watcher_(new ShutdownWatcherHelper()) {
// If we're running tests (ui_task is non-null).
if (parameters.ui_task)
- browser_defaults::enable_help_app = false;
+ browser_defaults::enable_help_app = false;
}
ChromeBrowserMainParts::~ChromeBrowserMainParts() {
@@ -1765,11 +1765,8 @@
// Run the Out of Memory priority manager while in this scope. Wait
// until here to start so that we give the most amount of time for
// the other services to start up before we start adjusting the oom
- // priority. In reality, it doesn't matter much where in this scope
- // this is started, but it must be started in this scope so it will
- // also be terminated when this scope exits.
- scoped_ptr<browser::OomPriorityManager> oom_priority_manager(
- new browser::OomPriorityManager);
+ // priority.
+ browser::OomPriorityManager::Create();
#endif
// Create the instance of the cloud print proxy service so that it can launch
@@ -1899,13 +1896,16 @@
}
#endif
+#if defined(OS_CHROMEOS)
+ browser::OomPriorityManager::Destroy();
+#endif
+
// Some tests don't set parameters.ui_task, so they started translate
// language fetch that was never completed so we need to cleanup here
// otherwise it will be done by the destructor in a wrong thread.
if (parameters().ui_task == NULL && translate_manager != NULL)
translate_manager->CleanupPendingUlrFetcher();
-
process_singleton.Cleanup();
// Stop all tasks that might run on WatchDogThread.
diff --git a/chrome/browser/oom_priority_manager.cc b/chrome/browser/oom_priority_manager.cc
index d3db051..24832c1 100644
--- a/chrome/browser/oom_priority_manager.cc
+++ b/chrome/browser/oom_priority_manager.cc
@@ -4,11 +4,15 @@
#include "chrome/browser/oom_priority_manager.h"
-#include <list>
+#include <algorithm>
+#include <vector>
#include "base/process.h"
#include "base/process_util.h"
+#include "base/string16.h"
+#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
+#include "base/timer.h"
#include "build/build_config.h"
#include "chrome/browser/tabs/tab_strip_model.h"
#include "chrome/browser/ui/browser_list.h"
@@ -38,31 +42,86 @@
// "equal".
#define BUCKET_INTERVAL_MINUTES 10
-OomPriorityManager::OomPriorityManager() {
+class OomPriorityManagerImpl {
+ public:
+ OomPriorityManagerImpl();
+ ~OomPriorityManagerImpl();
+
+ void StartTimer();
+ void StopTimer();
+
+ std::vector<string16> GetTabTitles();
+
+ struct RendererStats {
+ bool is_pinned;
+ bool is_selected;
+ base::TimeTicks last_selected;
+ size_t memory_used;
+ base::ProcessHandle renderer_handle;
+ string16 title;
+ };
+ typedef std::vector<RendererStats> StatsList;
+
+ // Posts DoAdjustOomPriorities task to the file thread. Called when
+ // the timer fires.
+ void AdjustOomPriorities();
+
+ // Called by AdjustOomPriorities. Runs on the file thread.
+ void DoAdjustOomPriorities();
+
+ static bool CompareRendererStats(RendererStats first, RendererStats second);
+
+ base::RepeatingTimer<OomPriorityManagerImpl> timer_;
+ // renderer_stats_ is used on both UI and file threads.
+ base::Lock renderer_stats_lock_;
+ StatsList renderer_stats_;
+
+ DISALLOW_COPY_AND_ASSIGN(OomPriorityManagerImpl);
+};
+
+} // namespace browser
+
+DISABLE_RUNNABLE_METHOD_REFCOUNT(browser::OomPriorityManagerImpl);
+
+namespace browser {
+
+OomPriorityManagerImpl::OomPriorityManagerImpl() {
+ renderer_stats_.reserve(32); // 99% of users have < 30 tabs open
StartTimer();
}
-OomPriorityManager::~OomPriorityManager() {
+OomPriorityManagerImpl::~OomPriorityManagerImpl() {
StopTimer();
}
-void OomPriorityManager::StartTimer() {
+void OomPriorityManagerImpl::StartTimer() {
if (!timer_.IsRunning()) {
timer_.Start(FROM_HERE,
TimeDelta::FromSeconds(ADJUSTMENT_INTERVAL_SECONDS),
this,
- &OomPriorityManager::AdjustOomPriorities);
+ &OomPriorityManagerImpl::AdjustOomPriorities);
}
}
-void OomPriorityManager::StopTimer() {
+void OomPriorityManagerImpl::StopTimer() {
timer_.Stop();
}
+std::vector<string16> OomPriorityManagerImpl::GetTabTitles() {
+ base::AutoLock renderer_stats_autolock(renderer_stats_lock_);
+ std::vector<string16> titles;
+ titles.reserve(renderer_stats_.size());
+ StatsList::iterator it = renderer_stats_.begin();
+ for ( ; it != renderer_stats_.end(); ++it) {
+ titles.push_back(it->title);
+ }
+ return titles;
+}
+
// Returns true if |first| is considered less desirable to be killed
// than |second|.
-bool OomPriorityManager::CompareRendererStats(RendererStats first,
- RendererStats second) {
+bool OomPriorityManagerImpl::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 =
@@ -101,36 +160,41 @@
// 4) 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() {
+void OomPriorityManagerImpl::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.
- stats.is_selected = model->IsTabSelected(i);
- renderer_stats.push_back(stats);
+ {
+ base::AutoLock renderer_stats_autolock(renderer_stats_lock_);
+ renderer_stats_.clear();
+ 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; // Calculated in DoAdjustOomPriorities.
+ stats.is_selected = model->IsTabSelected(i);
+ stats.title = contents->GetTitle();
+ renderer_stats_.push_back(stats);
+ }
}
}
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
- NewRunnableMethod(this, &OomPriorityManager::DoAdjustOomPriorities,
- renderer_stats));
+ NewRunnableMethod(this, &OomPriorityManagerImpl::DoAdjustOomPriorities));
}
-void OomPriorityManager::DoAdjustOomPriorities(StatsList renderer_stats) {
- for (StatsList::iterator stats_iter = renderer_stats.begin();
- stats_iter != renderer_stats.end(); ++stats_iter) {
+void OomPriorityManagerImpl::DoAdjustOomPriorities() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
+ base::AutoLock renderer_stats_autolock(renderer_stats_lock_);
+ 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));
@@ -150,7 +214,9 @@
// 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);
+ std::sort(renderer_stats_.begin(),
+ renderer_stats_.end(),
+ OomPriorityManagerImpl::CompareRendererStats);
// Now we assign priorities based on the sorted list. We're
// assigning priorities in the range of kLowestRendererOomScore to
@@ -170,11 +236,11 @@
const int kPriorityRange = chrome::kHighestRendererOomScore -
chrome::kLowestRendererOomScore;
float priority_increment =
- static_cast<float>(kPriorityRange) / renderer_stats.size();
+ static_cast<float>(kPriorityRange) / renderer_stats_.size();
float priority = chrome::kLowestRendererOomScore;
std::set<base::ProcessHandle> already_seen;
- for (StatsList::iterator iterator = renderer_stats.begin();
- iterator != renderer_stats.end(); ++iterator) {
+ 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);
ZygoteHost::GetInstance()->AdjustRendererOOMScore(
@@ -184,4 +250,27 @@
}
}
+//////////////////////////////////////////////////////////////////////////////
+// Pointer-to-impl glue
+
+OomPriorityManagerImpl* OomPriorityManager::impl_ = NULL;
+
+// static
+void OomPriorityManager::Create() {
+ impl_ = new OomPriorityManagerImpl();
+}
+
+// static
+void OomPriorityManager::Destroy() {
+ delete impl_;
+ impl_ = NULL;
+}
+
+// static
+std::vector<string16> OomPriorityManager::GetTabTitles() {
+ if (!impl_)
+ return std::vector<string16>();
+ return impl_->GetTabTitles();
+}
+
} // namespace browser
diff --git a/chrome/browser/oom_priority_manager.h b/chrome/browser/oom_priority_manager.h
index 3090947d..a5eeef6 100644
--- a/chrome/browser/oom_priority_manager.h
+++ b/chrome/browser/oom_priority_manager.h
@@ -5,13 +5,14 @@
#ifndef CHROME_BROWSER_OOM_PRIORITY_MANAGER_H_
#define CHROME_BROWSER_OOM_PRIORITY_MANAGER_H_
-#include <list>
+#include <vector>
-#include "base/timer.h"
-#include "base/process.h"
+#include "base/string16.h"
namespace browser {
+class OomPriorityManagerImpl;
+
// 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
@@ -26,37 +27,18 @@
// them, as no two tabs will have exactly the same idle time.
class OomPriorityManager {
public:
- OomPriorityManager();
- ~OomPriorityManager();
+ // We need to explicitly manage our destruction, so don't use Singleton.
+ static void Create();
+ static void Destroy();
+
+ // Returns list of tab titles sorted from most interesting (don't kill)
+ // to least interesting (OK to kill).
+ static std::vector<string16> GetTabTitles();
private:
- struct RendererStats {
- bool is_pinned;
- bool is_selected;
- base::TimeTicks last_selected;
- size_t memory_used;
- base::ProcessHandle renderer_handle;
- };
- typedef std::list<RendererStats> StatsList;
-
- void StartTimer();
- void StopTimer();
-
- // Posts DoAdjustOomPriorities task to the file thread. Called when
- // the timer fires.
- void AdjustOomPriorities();
-
- // Called by AdjustOomPriorities. Runs on the file thread.
- void DoAdjustOomPriorities(StatsList list);
-
- static bool CompareRendererStats(RendererStats first, RendererStats second);
-
- base::RepeatingTimer<OomPriorityManager> timer_;
-
- DISALLOW_COPY_AND_ASSIGN(OomPriorityManager);
+ static OomPriorityManagerImpl* impl_;
};
-} // namespace browser
-DISABLE_RUNNABLE_METHOD_REFCOUNT(browser::OomPriorityManager);
+} // namespace browser
#endif // CHROME_BROWSER_OOM_PRIORITY_MANAGER_H_
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc
index 78e3298..4050249 100644
--- a/chrome/common/url_constants.cc
+++ b/chrome/common/url_constants.cc
@@ -69,6 +69,7 @@
const char kChromeUIChooseMobileNetworkURL[] =
"chrome://choose-mobile-network/";
const char kChromeUICollectedCookiesURL[] = "chrome://collected-cookies/";
+const char kChromeUIDiscardsURL[] = "chrome://discards/";
const char kChromeUIEnterpriseEnrollmentURL[] =
"chrome://enterprise-enrollment/";
const char kChromeUIHttpAuthURL[] = "chrome://http-auth/";
@@ -176,6 +177,7 @@
const char kChromeUIChooseMobileNetworkHost[] = "choose-mobile-network";
const char kChromeUICollectedCookiesHost[] = "collected-cookies";
const char kChromeUICryptohomeHost[] = "cryptohome";
+const char kChromeUIDiscardsHost[] = "discards";
const char kChromeUIEnterpriseEnrollmentHost[] = "enterprise-enrollment";
const char kChromeUIHttpAuthHost[] = "http-auth";
const char kChromeUIImageBurnerHost[] = "imageburner";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h
index e6f8313..670bba4 100644
--- a/chrome/common/url_constants.h
+++ b/chrome/common/url_constants.h
@@ -70,6 +70,7 @@
extern const char kChromeUIActiveDownloadsURL[];
extern const char kChromeUIChooseMobileNetworkURL[];
extern const char kChromeUICollectedCookiesURL[];
+extern const char kChromeUIDiscardsURL[];
extern const char kChromeUIHttpAuthURL[];
extern const char kChromeUIImageBurnerURL[];
extern const char kChromeUIKeyboardOverlayURL[];
@@ -174,6 +175,7 @@
extern const char kChromeUIChooseMobileNetworkHost[];
extern const char kChromeUICollectedCookiesHost[];
extern const char kChromeUICryptohomeHost[];
+extern const char kChromeUIDiscardsHost[];
extern const char kChromeUIEnterpriseEnrollmentHost[];
extern const char kChromeUIHttpAuthHost[];
extern const char kChromeUIImageBurnerHost[];