diff options
author | jamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-01 23:36:00 +0000 |
---|---|---|
committer | jamescook@chromium.org <jamescook@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-08-01 23:36:00 +0000 |
commit | f31deb497a7543d9815131cf054ad7b0067f2508 (patch) | |
tree | 21e1b06cc163c6e1e1d09fb3ef2eeacaed9080f4 | |
parent | 58f5ca425866f9b119929fb291fba420cd462542 (diff) | |
download | chromium_src-f31deb497a7543d9815131cf054ad7b0067f2508.zip chromium_src-f31deb497a7543d9815131cf054ad7b0067f2508.tar.gz chromium_src-f31deb497a7543d9815131cf054ad7b0067f2508.tar.bz2 |
CrOS - Add memory consumption status bar widget behind flag
Pass --memory-widget on CrOS to get a real-time display, updated every 5 seconds, of the system's free memory. Tooltip and menu itself detail the data from /proc/meminfo. Also made base::GetSystemMemoryInfo() available on Linux systems to provide detailed data.
BUG=chromium-os:18446
TEST=manual
Review URL: http://codereview.chromium.org/7518010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@94999 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/process_util.h | 9 | ||||
-rw-r--r-- | base/process_util_linux.cc | 36 | ||||
-rw-r--r-- | chrome/browser/chromeos/status/memory_menu_button.cc | 166 | ||||
-rw-r--r-- | chrome/browser/chromeos/status/memory_menu_button.h | 69 | ||||
-rw-r--r-- | chrome/browser/chromeos/status/status_area_view.cc | 11 | ||||
-rw-r--r-- | chrome/browser/chromeos/status/status_area_view.h | 2 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 2 | ||||
-rw-r--r-- | chrome/common/chrome_switches.cc | 3 | ||||
-rw-r--r-- | chrome/common/chrome_switches.h | 1 |
9 files changed, 289 insertions, 10 deletions
diff --git a/base/process_util.h b/base/process_util.h index 3d9b7d1..5f88c62 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -653,7 +653,14 @@ class BASE_API ProcessMetrics { DISALLOW_COPY_AND_ASSIGN(ProcessMetrics); }; -// Returns the memory commited by the system in KBytes. +#if defined(OS_LINUX) +// Retrieves data from /proc/meminfo about system-wide memory consumption. +// Values are in KB. Returns true on success. +BASE_API bool GetSystemMemoryInfo(int* total_kb, int* free_kb, int* buffers_kb, + int* cache_kb, int* shmem_kb); +#endif + +// Returns the memory committed by the system in KBytes. // Returns 0 if it can't compute the commit charge. BASE_API size_t GetSystemCommitCharge(); diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc index aabd201..3ea383f 100644 --- a/base/process_util_linux.cc +++ b/base/process_util_linux.cc @@ -562,7 +562,8 @@ const size_t kMemCacheIndex = 10; } // namespace -size_t GetSystemCommitCharge() { +bool GetSystemMemoryInfo(int* mem_total, int* mem_free, int* mem_buffers, + int* mem_cache, int* shmem) { // Synchronously reading files in /proc is safe. base::ThreadRestrictions::ScopedAllowIO allow_io; @@ -571,7 +572,7 @@ size_t GetSystemCommitCharge() { std::string meminfo_data; if (!file_util::ReadFileToString(meminfo_file, &meminfo_data)) { LOG(WARNING) << "Failed to open /proc/meminfo."; - return 0; + return false; } std::vector<std::string> meminfo_fields; SplitStringAlongWhitespace(meminfo_data, &meminfo_fields); @@ -579,7 +580,7 @@ size_t GetSystemCommitCharge() { if (meminfo_fields.size() < kMemCacheIndex) { LOG(WARNING) << "Failed to parse /proc/meminfo. Only found " << meminfo_fields.size() << " fields."; - return 0; + return false; } DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:"); @@ -587,13 +588,30 @@ size_t GetSystemCommitCharge() { DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:"); DCHECK_EQ(meminfo_fields[kMemCacheIndex-1], "Cached:"); - int mem_total, mem_free, mem_buffers, mem_cache; - base::StringToInt(meminfo_fields[kMemTotalIndex], &mem_total); - base::StringToInt(meminfo_fields[kMemFreeIndex], &mem_free); - base::StringToInt(meminfo_fields[kMemBuffersIndex], &mem_buffers); - base::StringToInt(meminfo_fields[kMemCacheIndex], &mem_cache); + base::StringToInt(meminfo_fields[kMemTotalIndex], mem_total); + base::StringToInt(meminfo_fields[kMemFreeIndex], mem_free); + base::StringToInt(meminfo_fields[kMemBuffersIndex], mem_buffers); + base::StringToInt(meminfo_fields[kMemCacheIndex], mem_cache); +#if defined(OS_CHROMEOS) + // Chrome OS has a tweaked kernel that allows us to query Shmem, which is + // usually video memory otherwise invisible to the OS. Unfortunately, the + // meminfo format varies on different hardware so we have to search for the + // string. It always appears after "Cached:". + for (size_t i = kMemCacheIndex+2; i < meminfo_fields.size(); i += 3) { + if (meminfo_fields[i] == "Shmem:") { + base::StringToInt(meminfo_fields[i+1], shmem); + break; + } + } +#endif + return true; +} - return mem_total - mem_free - mem_buffers - mem_cache; +size_t GetSystemCommitCharge() { + int total, free, buffers, cache, shmem; + if (!GetSystemMemoryInfo(&total, &free, &buffers, &cache, &shmem)) + return 0; + return total - free - buffers - cache; } namespace { diff --git a/chrome/browser/chromeos/status/memory_menu_button.cc b/chrome/browser/chromeos/status/memory_menu_button.cc new file mode 100644 index 0000000..e19b50f --- /dev/null +++ b/chrome/browser/chromeos/status/memory_menu_button.cc @@ -0,0 +1,166 @@ +// Copyright (c) 2011 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/chromeos/status/memory_menu_button.h" + +#include "base/process_util.h" // GetSystemMemoryInfo +#include "base/stringprintf.h" +#include "chrome/browser/chromeos/status/status_area_host.h" +#include "grit/generated_resources.h" +#include "third_party/tcmalloc/chromium/src/google/heap-profiler.h" +#include "ui/base/l10n/l10n_util.h" +#include "views/widget/widget.h" + +namespace { + +// views::MenuItemView item ids +enum { + MEM_TOTAL_ITEM, + MEM_FREE_ITEM, + MEM_BUFFERS_ITEM, + MEM_CACHE_ITEM, + SHMEM_ITEM, + HEAP_PROFILE_START_ITEM, + HEAP_PROFILE_STOP_ITEM, +}; + +} // namespace + +namespace chromeos { + +// Delay between updates, in seconds. +const int kUpdateIntervalSeconds = 5; + +MemoryMenuButton::MemoryMenuButton(StatusAreaHost* host) + : StatusAreaButton(host, this), + mem_total_(0), + shmem_(0), + mem_free_(0), + mem_buffers_(0), + mem_cache_(0) { + UpdateTextAndSetNextTimer(); +} + +MemoryMenuButton::~MemoryMenuButton() { +} + +void MemoryMenuButton::UpdateTextAndSetNextTimer() { + UpdateText(); + + timer_.Start(base::TimeDelta::FromSeconds(kUpdateIntervalSeconds), this, + &MemoryMenuButton::UpdateTextAndSetNextTimer); +} + +void MemoryMenuButton::UpdateText() { + base::GetSystemMemoryInfo(&mem_total_, &mem_free_, &mem_buffers_, &mem_cache_, + &shmem_); + std::wstring label = base::StringPrintf(L"%d MB", mem_free_ / 1024); + SetText(label); + 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", + mem_total_ / 1024, + mem_free_ / 1024, + mem_buffers_ / 1024, + mem_cache_ / 1024, + shmem_ / 1024); + SetTooltipText(tooltip); + SchedulePaint(); +} + +// MemoryMenuButton, views::MenuDelegate implementation: +std::wstring MemoryMenuButton::GetLabel(int id) const { + switch (id) { + case MEM_TOTAL_ITEM: + return StringPrintf(L"%d MB total", mem_total_ / 1024); + case MEM_FREE_ITEM: + return StringPrintf(L"%d MB free", mem_free_ / 1024); + case MEM_BUFFERS_ITEM: + return StringPrintf(L"%d MB buffers", mem_buffers_ / 1024); + case MEM_CACHE_ITEM: + return StringPrintf(L"%d MB cache", mem_cache_ / 1024); + case SHMEM_ITEM: + return StringPrintf(L"%d MB shmem", shmem_ / 1024); + case HEAP_PROFILE_START_ITEM: + return std::wstring(L"Start heap profile"); + case HEAP_PROFILE_STOP_ITEM: + return std::wstring(L"Stop heap profile"); + default: + return std::wstring(); + } +} + +bool MemoryMenuButton::IsCommandEnabled(int id) const { + switch (id) { + case MEM_TOTAL_ITEM: + case MEM_FREE_ITEM: + case MEM_BUFFERS_ITEM: + case MEM_CACHE_ITEM: + case SHMEM_ITEM: + return false; + case HEAP_PROFILE_START_ITEM: + return !IsHeapProfilerRunning(); + case HEAP_PROFILE_STOP_ITEM: + return IsHeapProfilerRunning(); + default: + NOTREACHED(); + return false; + } +} + +void MemoryMenuButton::ExecuteCommand(int id) { + switch (id) { + case HEAP_PROFILE_START_ITEM: + HeapProfilerStart("/tmp/heap"); + break; + case HEAP_PROFILE_STOP_ITEM: + HeapProfilerDump("Heap profile stopped"); + HeapProfilerStop(); + break; + default: + NOTREACHED(); + break; + } +} + +int MemoryMenuButton::horizontal_padding() { + return 3; +} + +// MemoryMenuButton, views::ViewMenuDelegate implementation: + +void MemoryMenuButton::RunMenu(views::View* source, const gfx::Point& pt) { + // View passed in must be a views::MenuButton, i.e. the MemoryMenuButton. + DCHECK_EQ(source, this); + + EnsureMenu(); + + gfx::Point screen_location; + views::View::ConvertPointToScreen(source, &screen_location); + gfx::Rect bounds(screen_location, source->size()); + menu_->RunMenuAt( + source->GetWidget()->GetTopLevelWidget(), + this, + bounds, + views::MenuItemView::TOPRIGHT, + true); +} + +// MemoryMenuButton, views::View implementation: + +void MemoryMenuButton::EnsureMenu() { + // Just rebuild the menu each time to ensure the labels are up-to-date. + menu_.reset(new views::MenuItemView(this)); + // Text for these items will be set by GetLabel(). + menu_->AppendDelegateMenuItem(MEM_TOTAL_ITEM); + menu_->AppendDelegateMenuItem(MEM_FREE_ITEM); + menu_->AppendDelegateMenuItem(MEM_BUFFERS_ITEM); + menu_->AppendDelegateMenuItem(MEM_CACHE_ITEM); + menu_->AppendDelegateMenuItem(SHMEM_ITEM); + menu_->AppendSeparator(); + // TODO(jamescook): Add items to run memory_purger, dump tcmalloc stats. + menu_->AppendDelegateMenuItem(HEAP_PROFILE_START_ITEM); + menu_->AppendDelegateMenuItem(HEAP_PROFILE_STOP_ITEM); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/status/memory_menu_button.h b/chrome/browser/chromeos/status/memory_menu_button.h new file mode 100644 index 0000000..c399df7 --- /dev/null +++ b/chrome/browser/chromeos/status/memory_menu_button.h @@ -0,0 +1,69 @@ +// Copyright (c) 2011 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_CHROMEOS_STATUS_MEMORY_MENU_BUTTON_H_ +#define CHROME_BROWSER_CHROMEOS_STATUS_MEMORY_MENU_BUTTON_H_ +#pragma once + +#include "base/memory/scoped_ptr.h" +#include "base/timer.h" +#include "chrome/browser/chromeos/status/status_area_button.h" +#include "views/controls/menu/menu_delegate.h" +#include "views/controls/menu/view_menu_delegate.h" + +namespace views { +class MenuItemView; +} + +namespace chromeos { + +class StatusAreaHost; + +// Memory debugging display that lives in the status area. +class MemoryMenuButton : public StatusAreaButton, + public views::MenuDelegate, + public views::ViewMenuDelegate { + public: + explicit MemoryMenuButton(StatusAreaHost* host); + virtual ~MemoryMenuButton(); + + // views::MenuDelegate implementation + virtual std::wstring GetLabel(int id) const OVERRIDE; + virtual bool IsCommandEnabled(int id) const OVERRIDE; + virtual void ExecuteCommand(int id) OVERRIDE; + + // Updates the text on the menu button. + void UpdateText(); + + protected: + virtual int horizontal_padding(); + + private: + // views::ViewMenuDelegate implementation. + virtual void RunMenu(views::View* source, const gfx::Point& pt); + + // Create and initialize menu if not already present. + void EnsureMenu(); + + // Updates text and schedules the timer to fire at the next minute interval. + void UpdateTextAndSetNextTimer(); + + base::OneShotTimer<MemoryMenuButton> timer_; + + // NOTE: we use a scoped_ptr here as menu calls into 'this' from the + // constructor. + scoped_ptr<views::MenuItemView> menu_; + + int mem_total_; + int shmem_; // video driver memory, hidden from OS + int mem_free_; + int mem_buffers_; + int mem_cache_; + + DISALLOW_COPY_AND_ASSIGN(MemoryMenuButton); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_STATUS_MEMORY_MENU_BUTTON_H_ diff --git a/chrome/browser/chromeos/status/status_area_view.cc b/chrome/browser/chromeos/status/status_area_view.cc index 161edfb..b87ca3c 100644 --- a/chrome/browser/chromeos/status/status_area_view.cc +++ b/chrome/browser/chromeos/status/status_area_view.cc @@ -6,12 +6,15 @@ #include <algorithm> +#include "base/command_line.h" #include "chrome/browser/chromeos/status/caps_lock_menu_button.h" #include "chrome/browser/chromeos/status/clock_menu_button.h" #include "chrome/browser/chromeos/status/input_method_menu_button.h" +#include "chrome/browser/chromeos/status/memory_menu_button.h" #include "chrome/browser/chromeos/status/network_menu_button.h" #include "chrome/browser/chromeos/status/power_menu_button.h" #include "chrome/browser/chromeos/status/status_area_host.h" +#include "chrome/common/chrome_switches.h" #include "grit/theme_resources.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" @@ -32,11 +35,17 @@ StatusAreaView::StatusAreaView(StatusAreaHost* host) caps_lock_view_(NULL), clock_view_(NULL), input_method_view_(NULL), + memory_view_(NULL), network_view_(NULL), power_view_(NULL) { } void StatusAreaView::Init() { + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kMemoryWidget)) { + memory_view_ = new MemoryMenuButton(host_); + AddChildView(memory_view_); + } + caps_lock_view_ = new CapsLockMenuButton(host_); caps_lock_view_->set_border(views::Border::CreateEmptyBorder(0, 1, 0, 0)); AddChildView(caps_lock_view_); @@ -102,6 +111,8 @@ void StatusAreaView::ChildPreferredSizeChanged(View* child) { } void StatusAreaView::MakeButtonsActive(bool active) { + if (memory_view_) + memory_view_->set_active(active); clock_view()->set_active(active); input_method_view()->set_active(active); network_view()->set_active(active); diff --git a/chrome/browser/chromeos/status/status_area_view.h b/chrome/browser/chromeos/status/status_area_view.h index dc8c801..ec43087 100644 --- a/chrome/browser/chromeos/status/status_area_view.h +++ b/chrome/browser/chromeos/status/status_area_view.h @@ -15,6 +15,7 @@ namespace chromeos { class CapsLockMenuButton; class ClockMenuButton; class InputMethodMenuButton; +class MemoryMenuButton; class NetworkMenuButton; class PowerMenuButton; class StatusAreaHost; @@ -47,6 +48,7 @@ class StatusAreaView : public AccessiblePaneView { CapsLockMenuButton* caps_lock_view_; ClockMenuButton* clock_view_; InputMethodMenuButton* input_method_view_; + MemoryMenuButton* memory_view_; NetworkMenuButton* network_view_; PowerMenuButton* power_view_; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 94a7158..66b2e19 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -710,6 +710,8 @@ 'browser/chromeos/status/input_method_menu.h', 'browser/chromeos/status/input_method_menu_button.cc', 'browser/chromeos/status/input_method_menu_button.h', + 'browser/chromeos/status/memory_menu_button.cc', + 'browser/chromeos/status/memory_menu_button.h', 'browser/chromeos/status/network_dropdown_button.cc', 'browser/chromeos/status/network_dropdown_button.h', 'browser/chromeos/status/network_menu.cc', diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 1db0e04..e100d59 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -1061,6 +1061,9 @@ const char kLoginScreen[] = "login-screen"; // Allows control over the initial login screen size. Pass width,height. const char kLoginScreenSize[] = "login-screen-size"; +// Shows a memory consumption status area widget for OOM debugging. +const char kMemoryWidget[] = "memory-widget"; + // Attempts to load libcros and validate it, then exits. A nonzero return code // means the library could not be loaded correctly. const char kTestLoadLibcros[] = "test-load-libcros"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 6b0c375..5cc034c 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -292,6 +292,7 @@ extern const char kLoginManager[]; // purposes. extern const char kLoginScreen[]; extern const char kLoginScreenSize[]; +extern const char kMemoryWidget[]; extern const char kTestLoadLibcros[]; extern const char kLoginProfile[]; extern const char kLoginUser[]; |