CrOS - Memory debug widget shows anonymous memory and renderer kills.

Change the status area memory widget to display anonymous memory allocated,
which is a good proxy for "total memory allocated" and what we use in the
UMA histograms (Platform.MemuseAnon[0-5]).  Also display renderer kills, which
will tick up when we run out of memory and the OOM killer starts running.
Refactor GetSystemMemoryInfo to use a struct.

BUG=chromium-os:18969
TEST=manual, turn on memory status widget in about:flags and restart, open some tabs and watch memory consumption increase, use Task Manager / End Process to kill a process and watch the kill count increase

Review URL: http://codereview.chromium.org/7607035

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@96423 0039d316-1c4b-4281-b951-d872f2087c98
parent afd6c510
...@@ -657,11 +657,23 @@ class BASE_EXPORT ProcessMetrics { ...@@ -657,11 +657,23 @@ class BASE_EXPORT ProcessMetrics {
}; };
#if defined(OS_LINUX) #if defined(OS_LINUX)
// Data from /proc/meminfo about system-wide memory consumption.
// Values are in KB.
struct SystemMemoryInfoKB {
SystemMemoryInfoKB() : total(0), free(0), buffers(0), cached(0),
active_anon(0), inactive_anon(0), shmem(0) {}
int total;
int free;
int buffers;
int cached;
int active_anon;
int inactive_anon;
int shmem;
};
// Retrieves data from /proc/meminfo about system-wide memory consumption. // Retrieves data from /proc/meminfo about system-wide memory consumption.
// Values are in KB. Returns true on success. // Fills in the provided |meminfo| structure. Returns true on success.
BASE_EXPORT bool GetSystemMemoryInfo(int* total_kb, int* free_kb, // Exposed for memory debugging widget.
int* buffers_kb, int* cache_kb, BASE_EXPORT bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo);
int* shmem_kb);
#endif #endif
// Returns the memory committed by the system in KBytes. // Returns the memory committed by the system in KBytes.
......
...@@ -558,12 +558,13 @@ namespace { ...@@ -558,12 +558,13 @@ namespace {
const size_t kMemTotalIndex = 1; const size_t kMemTotalIndex = 1;
const size_t kMemFreeIndex = 4; const size_t kMemFreeIndex = 4;
const size_t kMemBuffersIndex = 7; const size_t kMemBuffersIndex = 7;
const size_t kMemCacheIndex = 10; const size_t kMemCachedIndex = 10;
const size_t kMemActiveAnonIndex = 22;
const size_t kMemInactiveAnonIndex = 25;
} // namespace } // namespace
bool GetSystemMemoryInfo(int* mem_total, int* mem_free, int* mem_buffers, bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
int* mem_cache, int* shmem) {
// Synchronously reading files in /proc is safe. // Synchronously reading files in /proc is safe.
base::ThreadRestrictions::ScopedAllowIO allow_io; base::ThreadRestrictions::ScopedAllowIO allow_io;
...@@ -577,7 +578,7 @@ bool GetSystemMemoryInfo(int* mem_total, int* mem_free, int* mem_buffers, ...@@ -577,7 +578,7 @@ bool GetSystemMemoryInfo(int* mem_total, int* mem_free, int* mem_buffers,
std::vector<std::string> meminfo_fields; std::vector<std::string> meminfo_fields;
SplitStringAlongWhitespace(meminfo_data, &meminfo_fields); SplitStringAlongWhitespace(meminfo_data, &meminfo_fields);
if (meminfo_fields.size() < kMemCacheIndex) { if (meminfo_fields.size() < kMemCachedIndex) {
LOG(WARNING) << "Failed to parse /proc/meminfo. Only found " << LOG(WARNING) << "Failed to parse /proc/meminfo. Only found " <<
meminfo_fields.size() << " fields."; meminfo_fields.size() << " fields.";
return false; return false;
...@@ -586,20 +587,25 @@ bool GetSystemMemoryInfo(int* mem_total, int* mem_free, int* mem_buffers, ...@@ -586,20 +587,25 @@ bool GetSystemMemoryInfo(int* mem_total, int* mem_free, int* mem_buffers,
DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:"); DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:");
DCHECK_EQ(meminfo_fields[kMemFreeIndex-1], "MemFree:"); DCHECK_EQ(meminfo_fields[kMemFreeIndex-1], "MemFree:");
DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:"); DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:");
DCHECK_EQ(meminfo_fields[kMemCacheIndex-1], "Cached:"); DCHECK_EQ(meminfo_fields[kMemCachedIndex-1], "Cached:");
DCHECK_EQ(meminfo_fields[kMemActiveAnonIndex-1], "Active(anon):");
base::StringToInt(meminfo_fields[kMemTotalIndex], mem_total); DCHECK_EQ(meminfo_fields[kMemInactiveAnonIndex-1], "Inactive(anon):");
base::StringToInt(meminfo_fields[kMemFreeIndex], mem_free);
base::StringToInt(meminfo_fields[kMemBuffersIndex], mem_buffers); base::StringToInt(meminfo_fields[kMemTotalIndex], &meminfo->total);
base::StringToInt(meminfo_fields[kMemCacheIndex], mem_cache); base::StringToInt(meminfo_fields[kMemFreeIndex], &meminfo->free);
base::StringToInt(meminfo_fields[kMemBuffersIndex], &meminfo->buffers);
base::StringToInt(meminfo_fields[kMemCachedIndex], &meminfo->cached);
base::StringToInt(meminfo_fields[kMemActiveAnonIndex], &meminfo->active_anon);
base::StringToInt(meminfo_fields[kMemInactiveAnonIndex],
&meminfo->inactive_anon);
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
// Chrome OS has a tweaked kernel that allows us to query Shmem, which is // Chrome OS has a tweaked kernel that allows us to query Shmem, which is
// usually video memory otherwise invisible to the OS. Unfortunately, the // usually video memory otherwise invisible to the OS. Unfortunately, the
// meminfo format varies on different hardware so we have to search for the // meminfo format varies on different hardware so we have to search for the
// string. It always appears after "Cached:". // string. It always appears after "Cached:".
for (size_t i = kMemCacheIndex+2; i < meminfo_fields.size(); i += 3) { for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) {
if (meminfo_fields[i] == "Shmem:") { if (meminfo_fields[i] == "Shmem:") {
base::StringToInt(meminfo_fields[i+1], shmem); base::StringToInt(meminfo_fields[i+1], &meminfo->shmem);
break; break;
} }
} }
...@@ -608,10 +614,10 @@ bool GetSystemMemoryInfo(int* mem_total, int* mem_free, int* mem_buffers, ...@@ -608,10 +614,10 @@ bool GetSystemMemoryInfo(int* mem_total, int* mem_free, int* mem_buffers,
} }
size_t GetSystemCommitCharge() { size_t GetSystemCommitCharge() {
int total, free, buffers, cache, shmem; SystemMemoryInfoKB meminfo;
if (!GetSystemMemoryInfo(&total, &free, &buffers, &cache, &shmem)) if (!GetSystemMemoryInfo(&meminfo))
return 0; return 0;
return total - free - buffers - cache; return meminfo.total - meminfo.free - meminfo.buffers - meminfo.cached;
} }
namespace { namespace {
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include "base/stringprintf.h" #include "base/stringprintf.h"
#include "chrome/browser/chromeos/status/status_area_host.h" #include "chrome/browser/chromeos/status/status_area_host.h"
#include "chrome/browser/memory_purger.h" #include "chrome/browser/memory_purger.h"
#include "content/browser/renderer_host/render_process_host.h"
#include "content/common/notification_service.h"
#include "grit/generated_resources.h" #include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "views/widget/widget.h" #include "views/widget/widget.h"
...@@ -33,11 +35,12 @@ const int kUpdateIntervalSeconds = 5; ...@@ -33,11 +35,12 @@ const int kUpdateIntervalSeconds = 5;
MemoryMenuButton::MemoryMenuButton(StatusAreaHost* host) MemoryMenuButton::MemoryMenuButton(StatusAreaHost* host)
: StatusAreaButton(host, this), : StatusAreaButton(host, this),
mem_total_(0), meminfo_(new base::SystemMemoryInfoKB()),
shmem_(0), renderer_kills_(0) {
mem_free_(0), // Track renderer kills, as the kernel OOM killer will start to kill our
mem_buffers_(0), // renderers as we run out of memory.
mem_cache_(0) { registrar_.Add(this, content::NOTIFICATION_RENDERER_PROCESS_CLOSED,
NotificationService::AllSources());
UpdateTextAndSetNextTimer(); UpdateTextAndSetNextTimer();
} }
...@@ -52,17 +55,20 @@ void MemoryMenuButton::UpdateTextAndSetNextTimer() { ...@@ -52,17 +55,20 @@ void MemoryMenuButton::UpdateTextAndSetNextTimer() {
} }
void MemoryMenuButton::UpdateText() { void MemoryMenuButton::UpdateText() {
base::GetSystemMemoryInfo(&mem_total_, &mem_free_, &mem_buffers_, &mem_cache_, base::GetSystemMemoryInfo(meminfo_.get());
&shmem_); // "Anonymous" memory, meaning not mapped to a file (which has a name),
std::wstring label = base::StringPrintf(L"%d MB", mem_free_ / 1024); // represents memory that has been dynamically allocated to a process.
// It thus approximates heap memory usage across all processes.
int anon_kb = meminfo_->active_anon + meminfo_->inactive_anon;
std::wstring label = base::StringPrintf(L"%d MB (%d)",
anon_kb / 1024,
renderer_kills_);
SetText(label); SetText(label);
std::wstring tooltip = base::StringPrintf( std::wstring tooltip = base::StringPrintf(
L"%d MB total\n%d MB free\n%d MB buffers\n%d MB cache\n%d MB shmem", L"%d MB allocated (anonymous)\n"
mem_total_ / 1024, L"%d renderer kill(s)",
mem_free_ / 1024, anon_kb / 1024,
mem_buffers_ / 1024, renderer_kills_);
mem_cache_ / 1024,
shmem_ / 1024);
SetTooltipText(tooltip); SetTooltipText(tooltip);
SchedulePaint(); SchedulePaint();
} }
...@@ -71,15 +77,15 @@ void MemoryMenuButton::UpdateText() { ...@@ -71,15 +77,15 @@ void MemoryMenuButton::UpdateText() {
std::wstring MemoryMenuButton::GetLabel(int id) const { std::wstring MemoryMenuButton::GetLabel(int id) const {
switch (id) { switch (id) {
case MEM_TOTAL_ITEM: case MEM_TOTAL_ITEM:
return StringPrintf(L"%d MB total", mem_total_ / 1024); return StringPrintf(L"%d MB total", meminfo_->total / 1024);
case MEM_FREE_ITEM: case MEM_FREE_ITEM:
return StringPrintf(L"%d MB free", mem_free_ / 1024); return StringPrintf(L"%d MB free", meminfo_->free / 1024);
case MEM_BUFFERS_ITEM: case MEM_BUFFERS_ITEM:
return StringPrintf(L"%d MB buffers", mem_buffers_ / 1024); return StringPrintf(L"%d MB buffers", meminfo_->buffers / 1024);
case MEM_CACHE_ITEM: case MEM_CACHE_ITEM:
return StringPrintf(L"%d MB cache", mem_cache_ / 1024); return StringPrintf(L"%d MB cache", meminfo_->cached / 1024);
case SHMEM_ITEM: case SHMEM_ITEM:
return StringPrintf(L"%d MB shmem", shmem_ / 1024); return StringPrintf(L"%d MB shmem", meminfo_->shmem / 1024);
case PURGE_MEMORY_ITEM: case PURGE_MEMORY_ITEM:
return L"Purge memory"; return L"Purge memory";
default: default:
...@@ -146,4 +152,28 @@ void MemoryMenuButton::EnsureMenu() { ...@@ -146,4 +152,28 @@ void MemoryMenuButton::EnsureMenu() {
menu_->AppendDelegateMenuItem(PURGE_MEMORY_ITEM); menu_->AppendDelegateMenuItem(PURGE_MEMORY_ITEM);
} }
/////////////////////////////////////////////////////////////////////////////
// NotificationObserver overrides.
void MemoryMenuButton::Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) {
switch (type) {
case content::NOTIFICATION_RENDERER_PROCESS_CLOSED: {
RenderProcessHost::RendererClosedDetails* process_details =
Details<RenderProcessHost::RendererClosedDetails>(details).ptr();
if (process_details->status ==
base::TERMINATION_STATUS_PROCESS_WAS_KILLED) {
renderer_kills_++;
// A kill is a very interesting event, so repaint immediately.
UpdateText();
}
break;
}
default:
NOTREACHED() << L"Received unexpected notification";
break;
}
}
} // namespace chromeos } // namespace chromeos
...@@ -9,9 +9,15 @@ ...@@ -9,9 +9,15 @@
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/timer.h" #include "base/timer.h"
#include "chrome/browser/chromeos/status/status_area_button.h" #include "chrome/browser/chromeos/status/status_area_button.h"
#include "content/common/notification_observer.h"
#include "content/common/notification_registrar.h"
#include "views/controls/menu/menu_delegate.h" #include "views/controls/menu/menu_delegate.h"
#include "views/controls/menu/view_menu_delegate.h" #include "views/controls/menu/view_menu_delegate.h"
namespace base {
struct SystemMemoryInfoKB;
}
namespace views { namespace views {
class MenuItemView; class MenuItemView;
} }
...@@ -23,7 +29,8 @@ class StatusAreaHost; ...@@ -23,7 +29,8 @@ class StatusAreaHost;
// Memory debugging display that lives in the status area. // Memory debugging display that lives in the status area.
class MemoryMenuButton : public StatusAreaButton, class MemoryMenuButton : public StatusAreaButton,
public views::MenuDelegate, public views::MenuDelegate,
public views::ViewMenuDelegate { public views::ViewMenuDelegate,
public NotificationObserver {
public: public:
explicit MemoryMenuButton(StatusAreaHost* host); explicit MemoryMenuButton(StatusAreaHost* host);
virtual ~MemoryMenuButton(); virtual ~MemoryMenuButton();
...@@ -33,6 +40,14 @@ class MemoryMenuButton : public StatusAreaButton, ...@@ -33,6 +40,14 @@ class MemoryMenuButton : public StatusAreaButton,
virtual bool IsCommandEnabled(int id) const OVERRIDE; virtual bool IsCommandEnabled(int id) const OVERRIDE;
virtual void ExecuteCommand(int id) OVERRIDE; virtual void ExecuteCommand(int id) OVERRIDE;
// views::ViewMenuDelegate implementation.
virtual void RunMenu(views::View* source, const gfx::Point& pt) OVERRIDE;
// NotificationObserver overrides.
virtual void Observe(int type,
const NotificationSource& source,
const NotificationDetails& details) OVERRIDE;
// Updates the text on the menu button. // Updates the text on the menu button.
void UpdateText(); void UpdateText();
...@@ -40,9 +55,6 @@ class MemoryMenuButton : public StatusAreaButton, ...@@ -40,9 +55,6 @@ class MemoryMenuButton : public StatusAreaButton,
virtual int horizontal_padding() OVERRIDE; virtual int horizontal_padding() OVERRIDE;
private: private:
// views::ViewMenuDelegate implementation.
virtual void RunMenu(views::View* source, const gfx::Point& pt);
// Create and initialize menu if not already present. // Create and initialize menu if not already present.
void EnsureMenu(); void EnsureMenu();
...@@ -55,11 +67,13 @@ class MemoryMenuButton : public StatusAreaButton, ...@@ -55,11 +67,13 @@ class MemoryMenuButton : public StatusAreaButton,
// constructor. // constructor.
scoped_ptr<views::MenuItemView> menu_; scoped_ptr<views::MenuItemView> menu_;
int mem_total_; // Raw data from /proc/meminfo
int shmem_; // video driver memory, hidden from OS scoped_ptr<base::SystemMemoryInfoKB> meminfo_;
int mem_free_;
int mem_buffers_; NotificationRegistrar registrar_;
int mem_cache_;
// Number of renderer kills we have observed.
int renderer_kills_;
DISALLOW_COPY_AND_ASSIGN(MemoryMenuButton); DISALLOW_COPY_AND_ASSIGN(MemoryMenuButton);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment