Linux: about:memory
(based on http://code.google.com/p/chromium/issues/detail?id=16251)
Add about:memory support to Linux. Rather than try and copy the
Windows output, we use a couple of metrics which make more sense on
Linux: USS and PSS.
http://codereview.chromium.org/177024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24979 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/base/process_util.h b/base/process_util.h
index f002f85..91cca615 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -286,12 +286,20 @@
};
// Working Set (resident) memory usage broken down by
+//
+// On Windows:
// priv (private): These pages (kbytes) cannot be shared with any other process.
// shareable: These pages (kbytes) can be shared with other processes under
// the right circumstances.
// shared : These pages (kbytes) are currently shared with at least one
// other process.
+//
+// On Linux:
+// priv: Pages mapped only by this process
+// shared: PSS or 0 if the kernel doesn't support this
+// shareable: 0
struct WorkingSetKBytes {
+ WorkingSetKBytes() : priv(0), shareable(0), shared(0) {}
size_t priv;
size_t shareable;
size_t shared;
@@ -304,6 +312,7 @@
// image: These pages are mapped into the view of an image section (backed by
// file system)
struct CommittedKBytes {
+ CommittedKBytes() : priv(0), mapped(0), image(0) {}
size_t priv;
size_t mapped;
size_t image;
diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc
index a05e9bb..53e7d028 100644
--- a/base/process_util_linux.cc
+++ b/base/process_util_linux.cc
@@ -292,11 +292,10 @@
ws_usage->priv = private_kb;
// Sharable is not calculated, as it does not provide interesting data.
ws_usage->shareable = 0;
- if (have_pss) {
- ws_usage->shared = pss_kb - private_kb;
- } else {
- ws_usage->shared = shared_kb;
- }
+
+ ws_usage->shared = 0;
+ if (have_pss)
+ ws_usage->shared = pss_kb;
return true;
}
diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc
index 6cc4491c..f4a5da6 100644
--- a/chrome/browser/browser_about_handler.cc
+++ b/chrome/browser/browser_about_handler.cc
@@ -620,17 +620,17 @@
ListValue* browsers = new ListValue();
root.Set(L"browsers", browsers);
- ProcessData* browser_processes = processes();
+ const std::vector<ProcessData>& browser_processes = processes();
// Aggregate per-process data into browser summary data.
std::wstring log_string;
- for (int index = 0; index < MemoryDetails::MAX_BROWSERS; index++) {
+ for (size_t index = 0; index < browser_processes.size(); index++) {
if (browser_processes[index].processes.size() == 0)
continue;
// Sum the information for the processes within this browser.
ProcessMemoryInformation aggregate;
- ProcessMemoryInformationList::iterator iterator;
+ ProcessMemoryInformationList::const_iterator iterator;
iterator = browser_processes[index].processes.begin();
aggregate.pid = iterator->pid;
aggregate.version = iterator->version;
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index ca12d5d..b8406044 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -13,7 +13,15 @@
<includes>
<include name="IDR_ABOUT_PLUGINS_HTML" file="resources\about_plugins.html" type="BINDATA" />
<include name="IDR_ABOUT_VERSION_HTML" file="resources\about_version.html" flattenhtml="true" type="BINDATA" />
- <include name="IDR_ABOUT_MEMORY_HTML" file="resources\about_memory.html" flattenhtml="true" type="BINDATA" />
+
+ <if expr="os == 'linux2'">
+ <include name="IDR_ABOUT_MEMORY_HTML" file="resources\about_memory_linux.html" flattenhtml="true" type="BINDATA" />
+ </if>
+
+ <if expr="os != 'linux2'">
+ <include name="IDR_ABOUT_MEMORY_HTML" file="resources\about_memory.html" flattenhtml="true" type="BINDATA" />
+ </if>
+
<include name="IDR_ABOUT_STATS_HTML" file="resources\about_stats.html" type="BINDATA" />
<include name="IDR_SSL_ROAD_BLOCK_HTML" file="security\resources\ssl_roadblock.html" flattenhtml="true" type="BINDATA" />
<include name="IDR_SSL_ERROR_HTML" file="security\resources\ssl_error.html" flattenhtml="true" type="BINDATA" />
diff --git a/chrome/browser/memory_details.cc b/chrome/browser/memory_details.cc
index cfdafc3..219333f 100644
--- a/chrome/browser/memory_details.cc
+++ b/chrome/browser/memory_details.cc
@@ -3,7 +3,6 @@
// found in the LICENSE file.
#include "chrome/browser/memory_details.h"
-#include <psapi.h>
#include "app/l10n_util.h"
#include "base/file_version_info.h"
@@ -19,11 +18,10 @@
#include "chrome/common/url_constants.h"
#include "grit/chromium_strings.h"
-class RenderViewHostDelegate;
-
-// Template of static data we use for finding browser process information.
-// These entries must match the ordering for MemoryDetails::BrowserProcess.
-static ProcessData g_process_template[MemoryDetails::MAX_BROWSERS];
+#if defined(OS_LINUX)
+#include "chrome/browser/zygote_host_linux.h"
+#include "chrome/browser/renderer_host/render_sandbox_host_linux.h"
+#endif
// About threading:
//
@@ -38,25 +36,6 @@
// expensive parts of this operation over on the file thread.
//
-MemoryDetails::MemoryDetails() : ui_loop_(NULL) {
- static const std::wstring google_browser_name =
- l10n_util::GetString(IDS_PRODUCT_NAME);
- ProcessData g_process_template[MemoryDetails::MAX_BROWSERS] = {
- { google_browser_name.c_str(), L"chrome.exe", },
- { L"IE", L"iexplore.exe", },
- { L"Firefox", L"firefox.exe", },
- { L"Opera", L"opera.exe", },
- { L"Safari", L"safari.exe", },
- { L"IE (64bit)", L"iexplore.exe", },
- { L"Konqueror", L"konqueror.exe", },
- };
-
- for (int index = 0; index < arraysize(g_process_template); ++index) {
- process_data_[index].name = g_process_template[index].name;
- process_data_[index].process_name = g_process_template[index].process_name;
- }
-}
-
void MemoryDetails::StartFetch() {
ui_loop_ = MessageLoop::current();
@@ -92,106 +71,17 @@
NewRunnableMethod(this, &MemoryDetails::CollectProcessData, child_info));
}
-void MemoryDetails::CollectProcessData(
- std::vector<ProcessMemoryInformation> child_info) {
- DCHECK(MessageLoop::current() ==
- ChromeThread::GetMessageLoop(ChromeThread::FILE));
-
- // Clear old data.
- for (int index = 0; index < arraysize(g_process_template); index++)
- process_data_[index].processes.clear();
-
- SYSTEM_INFO system_info;
- GetNativeSystemInfo(&system_info);
- bool is_64bit_os =
- system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
-
- ScopedHandle snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
- PROCESSENTRY32 process_entry = {sizeof(PROCESSENTRY32)};
- if (!snapshot.Get()) {
- LOG(ERROR) << "CreateToolhelp32Snaphot failed: " << GetLastError();
- return;
- }
- if (!::Process32First(snapshot, &process_entry)) {
- LOG(ERROR) << "Process32First failed: " << GetLastError();
- return;
- }
- do {
- int pid = process_entry.th32ProcessID;
- ScopedHandle handle(::OpenProcess(
- PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
- if (!handle.Get())
- continue;
- bool is_64bit_process = false;
- // IsWow64Process() returns FALSE for a 32bit process on a 32bit OS.
- // We need to check if the real OS is 64bit.
- if (is_64bit_os) {
- BOOL is_wow64 = FALSE;
- // IsWow64Process() is supported by Windows XP SP2 or later.
- IsWow64Process(handle, &is_wow64);
- is_64bit_process = !is_wow64;
- }
- for (int index2 = 0; index2 < arraysize(g_process_template); index2++) {
- if (_wcsicmp(process_data_[index2].process_name,
- process_entry.szExeFile) != 0)
- continue;
- if (index2 == IE_BROWSER && is_64bit_process)
- continue; // Should use IE_64BIT_BROWSER
- // Get Memory Information.
- ProcessMemoryInformation info;
- info.pid = pid;
- if (info.pid == GetCurrentProcessId())
- info.type = ChildProcessInfo::BROWSER_PROCESS;
- else
- info.type = ChildProcessInfo::UNKNOWN_PROCESS;
-
- scoped_ptr<base::ProcessMetrics> metrics;
- metrics.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
- metrics->GetCommittedKBytes(&info.committed);
- metrics->GetWorkingSetKBytes(&info.working_set);
-
- // Get Version Information.
- TCHAR name[MAX_PATH];
- if (index2 == CHROME_BROWSER) {
- scoped_ptr<FileVersionInfo> version_info(
- FileVersionInfo::CreateFileVersionInfoForCurrentModule());
- if (version_info != NULL)
- info.version = version_info->file_version();
- // Check if this is one of the child processes whose data we collected
- // on the IO thread, and if so copy over that data.
- for (size_t child = 0; child < child_info.size(); child++) {
- if (child_info[child].pid != info.pid)
- continue;
- info.titles = child_info[child].titles;
- info.type = child_info[child].type;
- break;
- }
- } else if (GetModuleFileNameEx(handle, NULL, name, MAX_PATH - 1)) {
- std::wstring str_name(name);
- scoped_ptr<FileVersionInfo> version_info(
- FileVersionInfo::CreateFileVersionInfo(str_name));
- if (version_info != NULL) {
- info.version = version_info->product_version();
- info.product_name = version_info->product_name();
- }
- }
-
- // Add the process info to our list.
- process_data_[index2].processes.push_back(info);
- break;
- }
- } while (::Process32Next(snapshot, &process_entry));
-
- // Finally return to the browser thread.
- ui_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
-}
-
void MemoryDetails::CollectChildInfoOnUIThread() {
DCHECK(MessageLoop::current() == ui_loop_);
+#if defined(OS_LINUX)
+ const pid_t zygote_pid = Singleton<ZygoteHost>()->pid();
+ const pid_t sandbox_helper_pid = Singleton<RenderSandboxHostLinux>()->pid();
+#endif
+
+ ProcessData* const chrome_browser = ChromeBrowser();
// Get more information about the process.
- for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size();
+ for (size_t index = 0; index < chrome_browser->processes.size();
index++) {
// Check if it's a renderer, if so get the list of page titles in it and
// check if it's a diagnostics-related process. We skip all diagnostics
@@ -199,10 +89,11 @@
// the tab contents.
RenderProcessHost::iterator renderer_iter(
RenderProcessHost::AllHostsIterator());
+ ProcessMemoryInformation& process =
+ chrome_browser->processes[index];
+
for (; !renderer_iter.IsAtEnd(); renderer_iter.Advance()) {
DCHECK(renderer_iter.GetCurrentValue());
- ProcessMemoryInformation& process =
- process_data_[CHROME_BROWSER].processes[index];
if (process.pid != renderer_iter.GetCurrentValue()->process().pid())
continue;
process.type = ChildProcessInfo::RENDER_PROCESS;
@@ -257,15 +148,23 @@
process.is_diagnostics = true;
}
}
+
+#if defined(OS_LINUX)
+ if (process.pid == zygote_pid) {
+ process.type = ChildProcessInfo::ZYGOTE_PROCESS;
+ } else if (process.pid == sandbox_helper_pid) {
+ process.type = ChildProcessInfo::SANDBOX_HELPER_PROCESS;
+ }
+#endif
}
// Get rid of other Chrome processes that are from a different profile.
- for (size_t index = 0; index < process_data_[CHROME_BROWSER].processes.size();
+ for (size_t index = 0; index < chrome_browser->processes.size();
index++) {
- if (process_data_[CHROME_BROWSER].processes[index].type ==
+ if (chrome_browser->processes[index].type ==
ChildProcessInfo::UNKNOWN_PROCESS) {
- process_data_[CHROME_BROWSER].processes.erase(
- process_data_[CHROME_BROWSER].processes.begin() + index);
+ chrome_browser->processes.erase(
+ chrome_browser->processes.begin() + index);
index--;
}
}
@@ -279,7 +178,7 @@
// Reports a set of memory metrics to UMA.
// Memory is measured in KB.
- ProcessData browser = process_data_[CHROME_BROWSER];
+ const ProcessData& browser = *ChromeBrowser();
size_t aggregate_memory = 0;
int plugin_count = 0;
int worker_count = 0;
@@ -301,6 +200,14 @@
UMA_HISTOGRAM_MEMORY_KB("Memory.Worker", sample);
worker_count++;
break;
+ case ChildProcessInfo::ZYGOTE_PROCESS:
+ UMA_HISTOGRAM_MEMORY_KB("Memory.Zygote", sample);
+ break;
+ case ChildProcessInfo::SANDBOX_HELPER_PROCESS:
+ UMA_HISTOGRAM_MEMORY_KB("Memory.SandboxHelper", sample);
+ break;
+ default:
+ NOTREACHED();
}
}
UMA_HISTOGRAM_MEMORY_KB("Memory.BackingStore",
diff --git a/chrome/browser/memory_details.h b/chrome/browser/memory_details.h
index 8876c47..2e7e852 100644
--- a/chrome/browser/memory_details.h
+++ b/chrome/browser/memory_details.h
@@ -17,8 +17,11 @@
// have multiple processes (of course!). Even IE has multiple
// processes these days.
struct ProcessMemoryInformation {
- ProcessMemoryInformation() {
- memset(this, 0, sizeof(ProcessMemoryInformation));
+ ProcessMemoryInformation()
+ : pid(0),
+ num_processes(0),
+ is_diagnostics(false),
+ type(ChildProcessInfo::UNKNOWN_PROCESS) {
}
// The process id.
@@ -46,8 +49,8 @@
// Browser Process Information.
struct ProcessData {
- const wchar_t* name;
- const wchar_t* process_name;
+ std::wstring name;
+ std::wstring process_name;
ProcessMemoryInformationList processes;
};
@@ -71,26 +74,13 @@
// }
class MemoryDetails : public base::RefCountedThreadSafe<MemoryDetails> {
public:
- // Known browsers which we collect details for.
- enum {
- CHROME_BROWSER = 0,
- IE_BROWSER,
- FIREFOX_BROWSER,
- OPERA_BROWSER,
- SAFARI_BROWSER,
- IE_64BIT_BROWSER,
- KONQUEROR_BROWSER,
- MAX_BROWSERS
- } BrowserProcess;
-
// Constructor.
MemoryDetails();
virtual ~MemoryDetails() {}
- // Access to the process detail information. This is an array
- // of MAX_BROWSER ProcessData structures. This data is only available
+ // Access to the process detail information. This data is only available
// after OnDetailsAvailable() has been called.
- ProcessData* processes() { return process_data_; }
+ const std::vector<ProcessData>& processes() { return process_data_; }
// Initiate updating the current memory details. These are fetched
// asynchronously because data must be collected from multiple threads.
@@ -123,7 +113,10 @@
// the global histograms for tracking memory usage.
void UpdateHistograms();
- ProcessData process_data_[MAX_BROWSERS];
+ // Returns a pointer to the ProcessData structure for Chrome.
+ ProcessData* ChromeBrowser();
+
+ std::vector<ProcessData> process_data_;
MessageLoop* ui_loop_;
DISALLOW_EVIL_CONSTRUCTORS(MemoryDetails);
diff --git a/chrome/browser/memory_details_linux.cc b/chrome/browser/memory_details_linux.cc
new file mode 100644
index 0000000..a1ed5e9
--- /dev/null
+++ b/chrome/browser/memory_details_linux.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2006-2008 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_details.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include "app/l10n_util.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_version_info.h"
+#include "base/string_util.h"
+#include "base/process_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/common/child_process_host.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/url_constants.h"
+#include "grit/chromium_strings.h"
+
+// Known browsers which we collect details for.
+enum BrowserType {
+ CHROME = 0,
+ FIREFOX,
+ OPERA,
+ KONQUEROR,
+ EPIPHANY,
+ MIDORI,
+ MAX_BROWSERS
+} BrowserProcess;
+
+// The pretty printed names of those browsers. Matches up with enum
+// BrowserType.
+static const char kBrowserPrettyNames[][10] = {
+ "Chrome",
+ "Firefox",
+ "Opera",
+ "Konqueror",
+ "Epiphany",
+ "Midori",
+};
+
+// A mapping from process name to the type of browser.
+static const struct {
+ const char process_name[17];
+ BrowserType browser;
+ } kBrowserBinaryNames[] = {
+ { "firefox", FIREFOX },
+ { "firefox-3.5", FIREFOX },
+ { "firefox-3.0", FIREFOX },
+ { "opera", OPERA },
+ { "konqueror", KONQUEROR },
+ { "epiphany-browser", EPIPHANY },
+ { "epiphany", EPIPHANY },
+ { "midori", MIDORI },
+ { "", MAX_BROWSERS },
+};
+
+MemoryDetails::MemoryDetails()
+ : ui_loop_(NULL) {
+}
+
+ProcessData* MemoryDetails::ChromeBrowser() {
+ return &process_data_[0];
+}
+
+struct Process {
+ pid_t pid;
+ pid_t parent;
+ std::string name;
+};
+
+// Walk /proc and get information on all the processes running on the system.
+static bool GetProcesses(std::vector<Process>* processes) {
+ processes->clear();
+
+ DIR* dir = opendir("/proc");
+ if (!dir)
+ return false;
+
+ struct dirent* dent;
+ while ((dent = readdir(dir))) {
+ bool candidate = true;
+
+ // Filter out names which aren't ^[0-9]*$
+ for (const char* p = dent->d_name; *p; ++p) {
+ if (*p < '0' || *p > '9') {
+ candidate = false;
+ break;
+ }
+ }
+
+ if (!candidate)
+ continue;
+
+ char buf[256];
+ snprintf(buf, sizeof(buf), "/proc/%s/stat", dent->d_name);
+ const int fd = open(buf, O_RDONLY);
+ if (fd < 0)
+ continue;
+
+ const ssize_t len = HANDLE_EINTR(read(fd, buf, sizeof(buf) - 1));
+ HANDLE_EINTR(close(fd));
+ if (len < 1)
+ continue;
+ buf[len] = 0;
+
+ // The start of the file looks like:
+ // <pid> (<name>) R <parent pid>
+ unsigned pid, ppid;
+ char *process_name;
+ if (sscanf(buf, "%u (%a[^)]) %*c %u", &pid, &process_name, &ppid) != 3)
+ continue;
+
+ Process process;
+ process.pid = pid;
+ process.parent = ppid;
+ process.name = process_name;
+ free(process_name);
+ processes->push_back(process);
+ }
+
+ closedir(dir);
+ return true;
+}
+
+// Given a process name, return the type of the browser which created that
+// process, or |MAX_BROWSERS| if we don't know about it.
+static BrowserType GetBrowserType(const std::string& process_name) {
+ for (unsigned i = 0; kBrowserBinaryNames[i].process_name[0]; ++i) {
+ if (strcmp(process_name.c_str(), kBrowserBinaryNames[i].process_name) == 0)
+ return kBrowserBinaryNames[i].browser;
+ }
+
+ return MAX_BROWSERS;
+}
+
+// For each of a list of pids, collect memory information about that process
+// and append a record to |out|
+static void GetProcessDataMemoryInformation(
+ const std::vector<pid_t>& pids, ProcessData* out) {
+ for (std::vector<pid_t>::const_iterator
+ i = pids.begin(); i != pids.end(); ++i) {
+ ProcessMemoryInformation pmi;
+
+ pmi.pid = *i;
+ pmi.num_processes = 1;
+
+ if (pmi.pid == base::GetCurrentProcId())
+ pmi.type = ChildProcessInfo::BROWSER_PROCESS;
+ else
+ pmi.type = ChildProcessInfo::UNKNOWN_PROCESS;
+
+ base::ProcessMetrics* metrics =
+ base::ProcessMetrics::CreateProcessMetrics(*i);
+ metrics->GetWorkingSetKBytes(&pmi.working_set);
+ delete metrics;
+
+ out->processes.push_back(pmi);
+ }
+}
+
+// Find all children of the given process.
+static void GetAllChildren(const std::vector<Process>& processes,
+ pid_t root, std::vector<pid_t>* out) {
+ out->clear();
+ out->push_back(root);
+
+ std::set<pid_t> wavefront, next_wavefront;
+ wavefront.insert(root);
+
+ while (wavefront.size()) {
+ for (std::vector<Process>::const_iterator
+ i = processes.begin(); i != processes.end(); ++i) {
+ if (wavefront.count(i->parent)) {
+ out->push_back(i->pid);
+ next_wavefront.insert(i->pid);
+ }
+ }
+
+ wavefront.clear();
+ wavefront.swap(next_wavefront);
+ }
+}
+
+void MemoryDetails::CollectProcessData(
+ std::vector<ProcessMemoryInformation> child_info) {
+ DCHECK(MessageLoop::current() ==
+ ChromeThread::GetMessageLoop(ChromeThread::FILE));
+
+ std::vector<Process> processes;
+ GetProcesses(&processes);
+ std::set<pid_t> browsers_found;
+
+ // For each process on the system, if it appears to be a browser process and
+ // it's parent isn't a browser process, then record it in |browsers_found|.
+ for (std::vector<Process>::const_iterator
+ i = processes.begin(); i != processes.end(); ++i) {
+ const BrowserType type = GetBrowserType(i->name);
+ if (type != MAX_BROWSERS) {
+ bool found_parent = false;
+
+ // Find the parent of |i|
+ for (std::vector<Process>::const_iterator
+ j = processes.begin(); j != processes.end(); ++j) {
+ if (j->pid == i->parent) {
+ found_parent = true;
+
+ if (GetBrowserType(j->name) != type) {
+ // We went too far and ended up with something else, which means
+ // that |i| is a browser.
+ browsers_found.insert(i->pid);
+ break;
+ }
+ }
+ }
+
+ if (!found_parent)
+ browsers_found.insert(i->pid);
+ }
+ }
+
+ std::vector<pid_t> current_browser_processes;
+ GetAllChildren(processes, getpid(), ¤t_browser_processes);
+ ProcessData current_browser;
+ GetProcessDataMemoryInformation(current_browser_processes, ¤t_browser);
+ current_browser.name = chrome::kBrowserAppName;
+ current_browser.process_name = L"chrome";
+ process_data_.push_back(current_browser);
+
+ // For each browser process, collect a list of its children and get the
+ // memory usage of each.
+ for (std::set<pid_t>::const_iterator
+ i = browsers_found.begin(); i != browsers_found.end(); ++i) {
+ std::vector<pid_t> browser_processes;
+ GetAllChildren(processes, *i, &browser_processes);
+ ProcessData browser;
+ GetProcessDataMemoryInformation(browser_processes, &browser);
+
+ for (std::vector<Process>::const_iterator
+ j = processes.begin(); j != processes.end(); ++j) {
+ if (j->pid == *i) {
+ BrowserType type = GetBrowserType(j->name);
+ if (type != MAX_BROWSERS)
+ browser.name = ASCIIToWide(kBrowserPrettyNames[type]);
+ break;
+ }
+ }
+
+ process_data_.push_back(browser);
+ }
+
+ // Finally return to the browser thread.
+ ui_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
+}
diff --git a/chrome/browser/memory_details_win.cc b/chrome/browser/memory_details_win.cc
new file mode 100644
index 0000000..4c3d8be
--- /dev/null
+++ b/chrome/browser/memory_details_win.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2006-2008 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_details.h"
+#include <psapi.h>
+
+#include "app/l10n_util.h"
+#include "base/file_version_info.h"
+#include "base/string_util.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/renderer_host/backing_store_manager.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
+#include "chrome/common/child_process_host.h"
+#include "chrome/common/url_constants.h"
+#include "grit/chromium_strings.h"
+
+// Known browsers which we collect details for.
+enum {
+ CHROME_BROWSER = 0,
+ IE_BROWSER,
+ FIREFOX_BROWSER,
+ OPERA_BROWSER,
+ SAFARI_BROWSER,
+ IE_64BIT_BROWSER,
+ KONQUEROR_BROWSER,
+ MAX_BROWSERS
+} BrowserProcess;
+
+// Template of static data we use for finding browser process information.
+// These entries must match the ordering for MemoryDetails::BrowserProcess.
+static ProcessData g_process_template[MAX_BROWSERS];
+
+MemoryDetails::MemoryDetails()
+ : ui_loop_(NULL) {
+ static const std::wstring google_browser_name =
+ l10n_util::GetString(IDS_PRODUCT_NAME);
+ ProcessData g_process_template[MAX_BROWSERS] = {
+ { google_browser_name.c_str(), L"chrome.exe", },
+ { L"IE", L"iexplore.exe", },
+ { L"Firefox", L"firefox.exe", },
+ { L"Opera", L"opera.exe", },
+ { L"Safari", L"safari.exe", },
+ { L"IE (64bit)", L"iexplore.exe", },
+ { L"Konqueror", L"konqueror.exe", },
+ };
+
+ for (int index = 0; index < arraysize(g_process_template); ++index) {
+ ProcessData process;
+ process.name = g_process_template[index].name;
+ process.process_name = g_process_template[index].process_name;
+ process_data_.push_back(process);
+ }
+}
+
+ProcessData* MemoryDetails::ChromeBrowser() {
+ return &process_data_[CHROME_BROWSER];
+}
+
+void MemoryDetails::CollectProcessData(
+ std::vector<ProcessMemoryInformation> child_info) {
+ DCHECK(MessageLoop::current() ==
+ ChromeThread::GetMessageLoop(ChromeThread::FILE));
+
+ // Clear old data.
+ for (int index = 0; index < arraysize(g_process_template); index++)
+ process_data_[index].processes.clear();
+
+ SYSTEM_INFO system_info;
+ GetNativeSystemInfo(&system_info);
+ bool is_64bit_os =
+ system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
+
+ ScopedHandle snapshot(::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0));
+ PROCESSENTRY32 process_entry = {sizeof(PROCESSENTRY32)};
+ if (!snapshot.Get()) {
+ LOG(ERROR) << "CreateToolhelp32Snaphot failed: " << GetLastError();
+ return;
+ }
+ if (!::Process32First(snapshot, &process_entry)) {
+ LOG(ERROR) << "Process32First failed: " << GetLastError();
+ return;
+ }
+ do {
+ int pid = process_entry.th32ProcessID;
+ ScopedHandle handle(::OpenProcess(
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
+ if (!handle.Get())
+ continue;
+ bool is_64bit_process = false;
+ // IsWow64Process() returns FALSE for a 32bit process on a 32bit OS.
+ // We need to check if the real OS is 64bit.
+ if (is_64bit_os) {
+ BOOL is_wow64 = FALSE;
+ // IsWow64Process() is supported by Windows XP SP2 or later.
+ IsWow64Process(handle, &is_wow64);
+ is_64bit_process = !is_wow64;
+ }
+ for (int index2 = 0; index2 < arraysize(g_process_template); index2++) {
+ if (_wcsicmp(process_data_[index2].process_name.c_str(),
+ process_entry.szExeFile) != 0)
+ continue;
+ if (index2 == IE_BROWSER && is_64bit_process)
+ continue; // Should use IE_64BIT_BROWSER
+ // Get Memory Information.
+ ProcessMemoryInformation info;
+ info.pid = pid;
+ if (info.pid == GetCurrentProcessId())
+ info.type = ChildProcessInfo::BROWSER_PROCESS;
+ else
+ info.type = ChildProcessInfo::UNKNOWN_PROCESS;
+
+ scoped_ptr<base::ProcessMetrics> metrics;
+ metrics.reset(base::ProcessMetrics::CreateProcessMetrics(handle));
+ metrics->GetCommittedKBytes(&info.committed);
+ metrics->GetWorkingSetKBytes(&info.working_set);
+
+ // Get Version Information.
+ TCHAR name[MAX_PATH];
+ if (index2 == CHROME_BROWSER) {
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfoForCurrentModule());
+ if (version_info != NULL)
+ info.version = version_info->file_version();
+ // Check if this is one of the child processes whose data we collected
+ // on the IO thread, and if so copy over that data.
+ for (size_t child = 0; child < child_info.size(); child++) {
+ if (child_info[child].pid != info.pid)
+ continue;
+ info.titles = child_info[child].titles;
+ info.type = child_info[child].type;
+ break;
+ }
+ } else if (GetModuleFileNameEx(handle, NULL, name, MAX_PATH - 1)) {
+ std::wstring str_name(name);
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(str_name));
+ if (version_info != NULL) {
+ info.version = version_info->product_version();
+ info.product_name = version_info->product_name();
+ }
+ }
+
+ // Add the process info to our list.
+ process_data_[index2].processes.push_back(info);
+ break;
+ }
+ } while (::Process32Next(snapshot, &process_entry));
+
+ // Finally return to the browser thread.
+ ui_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
+}
diff --git a/chrome/browser/renderer_host/render_sandbox_host_linux.cc b/chrome/browser/renderer_host/render_sandbox_host_linux.cc
index eeb7a98..8ea30ab 100644
--- a/chrome/browser/renderer_host/render_sandbox_host_linux.cc
+++ b/chrome/browser/renderer_host/render_sandbox_host_linux.cc
@@ -295,8 +295,8 @@
const int child_lifeline_fd = pipefds[0];
childs_lifeline_fd_ = pipefds[1];
- const pid_t child = fork();
- if (child == 0) {
+ pid_ = fork();
+ if (pid_ == 0) {
SandboxIPCProcess handler(child_lifeline_fd, browser_socket);
handler.Run();
_exit(0);
diff --git a/chrome/browser/renderer_host/render_sandbox_host_linux.h b/chrome/browser/renderer_host/render_sandbox_host_linux.h
index 43fa447..5b38b4c 100644
--- a/chrome/browser/renderer_host/render_sandbox_host_linux.h
+++ b/chrome/browser/renderer_host/render_sandbox_host_linux.h
@@ -18,6 +18,7 @@
// Get the file descriptor which renderers should be given in order to signal
// crashes to the browser.
int GetRendererSocket() const { return renderer_socket_; }
+ pid_t pid() const { return pid_; }
private:
friend struct DefaultSingletonTraits<RenderSandboxHostLinux>;
@@ -27,6 +28,7 @@
int renderer_socket_;
int childs_lifeline_fd_;
+ pid_t pid_;
DISALLOW_EVIL_CONSTRUCTORS(RenderSandboxHostLinux);
};
diff --git a/chrome/browser/resources/about_memory_linux.html b/chrome/browser/resources/about_memory_linux.html
index a06002fa..eeee845 100644
--- a/chrome/browser/resources/about_memory_linux.html
+++ b/chrome/browser/resources/about_memory_linux.html
@@ -268,15 +268,13 @@
table.list#memoryDetails tr:not([class*='firstRow']) > *:nth-child(2),
table.list#memoryDetails tr:not([class*='firstRow']) > *:nth-child(5),
-table.list#memoryDetails tr.firstRow th:nth-child(2),
-table.list#memoryDetails tr.firstRow th:nth-child(3) {
+table.list#memoryDetails tr.firstRow th:nth-child(2) {
border-right: 1px solid #b5c6de;
}
table.list#browserComparison tr:not([class*='firstRow']) > *:nth-child(1),
table.list#browserComparison tr:not([class*='firstRow']) > *:nth-child(4),
-table.list#browserComparison tr.firstRow th:nth-child(1),
-table.list#browserComparison tr.firstRow th:nth-child(2) {
+table.list#browserComparison tr.firstRow th:nth-child(1) {
border-right: 1px solid #b5c6de;
}
table.list#browserComparison .name {
@@ -426,14 +424,11 @@
<col class='name' />
<col class='number' />
<col class='number' />
- <col class='number' />
- <col class='number' />
- <col class='number' />
</colgroup>
<tr class='firstRow doNotFilter'>
<th>
</th>
- <th colspan='3'>
+ <th colspan='2'>
Memory
<div class='help'>
<div>
@@ -446,38 +441,14 @@
This is the best indicator of browser memory resource usage.
</p>
<p>
- <strong>Shared:</strong>
- Resident memory size that is currently shared with 2 or more processes.
- Note: For browsers using multiple processes, if we simply added the shared memory
- of each individual process, this value would be inflated. Therefore, this value
- is computed as an approximate value for shared memory in each of the browser's
- processes. Note also that shared memory varies depending on what other processes
- are running on the system, and may be difficult to measure reproducibly.
+ <strong>Proportional:</strong>
+ Accounts for each page of memory as a fraction based on the number of
+ processes that have it mapped. Thus, for each page of memory mapped by two
+ processes, this sum will count half of the bytes towards each.
+ Therefore, this number is greater than the private count.
</p>
- <p>
- <strong>Total:</strong>
- The sum of the private + shared resident memory sizes.
- </p>
- </div>
- </div>
- </th>
- <th colspan='2'>
- Virtual memory
- <div class='help'>
- <div>
- <p>
- <strong>Virtual memory</strong>
- </p>
- <p>
- <strong>Private:</strong>
- The resident and paged bytes committed for use by only this process.
- </p>
- <p>
- <strong>Mapped:</strong>
- Total bytes allocated by this process that are mapped into the
- view of a section, backed by either system pagefile or file system. This
- is primarily memory-mapped files.
- </p>
+
+ <p><i>(Note that the memory for this tab is not included in the browser totals)</i></p>
</div>
</div>
</th>
@@ -489,18 +460,8 @@
<th class='name'>
Private
</th>
- </th>
<th class='number'>
- Shared
- </th>
- <th class='number'>
- Total
- </th>
- <th class='number'>
- Private
- </th>
- <th class='number'>
- Mapped
+ Proportional
</th>
</tr>
<tr jsselect="browsers">
@@ -510,19 +471,10 @@
</div>
</td>
<td class='number'>
- <span class='th' jscontent="formatNumber(ws_priv + ws_shareable - ws_shared)"></span><span class='k'>k</span>
+ <span class='th' jscontent="formatNumber(ws_priv)"></span><span class='k'>k</span>
</td>
<td class='number'>
- <span class='th' jscontent="formatNumber(ws_shared / processes)"></span><span class='k'>k</span>
- </td>
- <td class='number'>
- <span class='th' jscontent="formatNumber(ws_priv + ws_shareable - ws_shared + (ws_shared / processes))"></span><span class='k'>k</span>
- </td>
- <td class='number'>
- <span class='th' jscontent="formatNumber(comm_priv)"></span><span class='k'>k</span>
- </td>
- <td class='number'>
- <span class='th' jscontent="formatNumber(comm_map)"></span><span class='k'>k</span>
+ <span class='th' jscontent="formatNumber(ws_shared)"></span><span class='k'>k</span>
</td>
</tr>
</table>
@@ -550,22 +502,15 @@
<col class='name' />
<col class='number' />
<col class='number' />
- <col class='number' />
- <col class='number' />
- <col class='number' />
</colgroup>
<tr class='firstRow doNotFilter'>
<th>
</th>
<th>
</th>
- <th colspan='3'>
+ <th colspan='2'>
Memory
</th>
- <th colspan='2'>
- Virtual memory
- </th>
-
</tr>
<tr class='secondRow doNotFilter'>
<th class='pid'>
@@ -578,16 +523,7 @@
Private
</th>
<th class='number'>
- Shared
- </th>
- <th class='number'>
- Total
- </th>
- <th class='number'>
- Private
- </th>
- <th class='number'>
- Mapped
+ Proportional
</th>
</tr>
@@ -601,19 +537,10 @@
</div>
</td>
<td class='number'>
- <span class='th' jseval="addToSum('tot_ws_priv', $this.ws_priv + $this.ws_shareable - $this.ws_shared)" jscontent="ws_priv + ws_shareable - ws_shared"></span><span class='k'>k</span>
+ <span class='th' jseval="addToSum('tot_ws_priv', $this.ws_priv)" jscontent="formatNumber(ws_priv)"></span><span class='k'>k</span>
</td>
<td class='number'>
- <span class='th' jscontent="ws_shared"></span><span class='k'>k</span>
- </td>
- <td class='number'>
- <span class='th' jseval="addToSum('tot_ws_tot', $this.ws_priv + $this.ws_shareable)" jscontent="ws_priv + ws_shareable"></span><span class='k'>k</span>
- </td>
- <td class='number'>
- <span class='th' jseval="addToSum('tot_comm_priv', $this.comm_priv)" jscontent="comm_priv"></span><span class='k'>k</span>
- </td>
- <td class='number'>
- <span class='th' jseval="addToSum('tot_comm_map', $this.comm_map)" jscontent="comm_map"></span><span class='k'>k</span>
+ <span class='th' jscontent="formatNumber(ws_shared)"></span><span class='k'>k</span>
</td>
</tr>
<tr jsselect="child_data">
@@ -627,19 +554,10 @@
</div>
</td>
<td class='number'>
- <span class='th' jseval="addToSum('tot_ws_priv', $this.ws_priv + $this.ws_shareable - $this.ws_shared)" jscontent="ws_priv + ws_shareable - ws_shared"></span><span class='k'>k</span>
+ <span class='th' jseval="addToSum('tot_ws_priv', $this.ws_priv)" jscontent="formatNumber(ws_priv)"></span><span class='k'>k</span>
</td>
<td class='number'>
- <span class='th' jscontent="ws_shared"></span><span class='k'>k</span>
- </td>
- <td class='number'>
- <span class='th' jseval="addToSum('tot_ws_tot', $this.ws_priv + $this.ws_shareable)" jscontent="ws_priv + ws_shareable"></span><span class='k'>k</span>
- </td>
- <td class='number'>
- <span class='th' jseval="addToSum('tot_comm_priv', $this.comm_priv)" jscontent="comm_priv"></span><span class='k'>k</span>
- </td>
- <td class='number'>
- <span class='th' jseval="addToSum('tot_comm_map', $this.comm_map)" jscontent="comm_map"></span><span class='k'>k</span>
+ <span class='th' jscontent="formatNumber(ws_shared)"></span><span class='k'>k</span>
</td>
</tr>
<tr class='total doNotFilter'>
@@ -653,27 +571,6 @@
</td>
<td class='number'>
</td>
- <td class='number'>
- <span class='th' id="tot_ws_tot">0</span><span class='k'>k</span>
- </td>
- <td class='number'>
- <span class='th' id="tot_comm_priv">0</span><span class='k'>k</span>
- </td>
- <td class='number'>
- <div class='help'>
- <div>
- <p>
- This is an approximation. Conceptually, this is the total
- amount of in-memory pages for the entire logical Chromium
- application, without double counting shared pages (e.g.
- mapped
- DLLs, SharedMemory bitmaps, etc.) across the browser and
- renderers.
- </p>
- </div>
- </div>
- <span class='th' id="tot_comm_map">0</span><span class='k'>k</span>
- </td>
</tr>
<tr class='noResults'>
diff --git a/chrome/browser/zygote_host_linux.cc b/chrome/browser/zygote_host_linux.cc
index 627d62b..e0fe349 100644
--- a/chrome/browser/zygote_host_linux.cc
+++ b/chrome/browser/zygote_host_linux.cc
@@ -121,6 +121,7 @@
base::LaunchApp(cmd_line.argv(), fds_to_map, false, &process);
CHECK(process != -1) << "Failed to launch zygote process";
+ pid_ = process;
close(fds[1]);
control_fd_ = fds[0];
}
diff --git a/chrome/browser/zygote_host_linux.h b/chrome/browser/zygote_host_linux.h
index 94ac92ee..516a18b 100644
--- a/chrome/browser/zygote_host_linux.h
+++ b/chrome/browser/zygote_host_linux.h
@@ -8,6 +8,8 @@
#include <string>
#include <vector>
+#include <unistd.h>
+
#include "base/global_descriptors_posix.h"
#include "base/process.h"
@@ -39,12 +41,15 @@
kCmdDidProcessCrash = 2, // Check if child process crashed.
};
+ pid_t pid() const { return pid_; }
+
private:
friend struct DefaultSingletonTraits<ZygoteHost>;
ZygoteHost();
void LaunchZygoteProcess();
int control_fd_; // the socket to the zygote
+ pid_t pid_;
};
#endif // CHROME_BROWSER_ZYGOTE_HOST_LINUX_H_
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 177f6da3..2fcee59 100644
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1464,6 +1464,8 @@
'browser/login_prompt_mac.mm',
'browser/login_prompt_win.cc',
'browser/memory_details.cc',
+ 'browser/memory_details_linux.cc',
+ 'browser/memory_details_win.cc',
'browser/memory_details.h',
'browser/meta_table_helper.cc',
'browser/meta_table_helper.h',
@@ -2600,7 +2602,6 @@
'browser/ime_input.cc',
'browser/importer/ie_importer.cc',
'browser/jumplist.cc',
- 'browser/memory_details.cc',
'browser/sandbox_policy.cc',
'browser/tab_contents/web_drag_source.cc',
'browser/tab_contents/web_drop_target.cc',
diff --git a/chrome/common/child_process_info.cc b/chrome/common/child_process_info.cc
index 770c9866..f6cb8ed 100644
--- a/chrome/common/child_process_info.cc
+++ b/chrome/common/child_process_info.cc
@@ -48,6 +48,10 @@
return L"Web Worker";
case UTILITY_PROCESS:
return L"Utility";
+ case ZYGOTE_PROCESS:
+ return L"Zygote";
+ case SANDBOX_HELPER_PROCESS:
+ return L"Sandbox helper";
case UNKNOWN_PROCESS:
default:
DCHECK(false) << "Unknown child process type!";
diff --git a/chrome/common/child_process_info.h b/chrome/common/child_process_info.h
index 3d80a1d..fa03c0f7 100644
--- a/chrome/common/child_process_info.h
+++ b/chrome/common/child_process_info.h
@@ -18,6 +18,8 @@
PLUGIN_PROCESS,
WORKER_PROCESS,
UTILITY_PROCESS,
+ ZYGOTE_PROCESS,
+ SANDBOX_HELPER_PROCESS,
UNKNOWN_PROCESS,
};
diff --git a/chrome/common/temp_scaffolding_stubs.cc b/chrome/common/temp_scaffolding_stubs.cc
index 78e4085..94f3260 100644
--- a/chrome/common/temp_scaffolding_stubs.cc
+++ b/chrome/common/temp_scaffolding_stubs.cc
@@ -210,6 +210,7 @@
//--------------------------------------------------------------------------
+#if defined(OS_MACOSX)
MemoryDetails::MemoryDetails() {
NOTIMPLEMENTED();
}
@@ -218,6 +219,7 @@
NOTIMPLEMENTED();
OnDetailsAvailable();
}
+#endif
#if defined(OS_LINUX) && defined(TOOLKIT_VIEWS)
// This should prompt the user if she wants to allow more than one concurrent