diff options
-rw-r--r-- | base/process_util.h | 9 | ||||
-rw-r--r-- | base/process_util_linux.cc | 9 | ||||
-rw-r--r-- | chrome/browser/browser_about_handler.cc | 6 | ||||
-rw-r--r-- | chrome/browser/browser_resources.grd | 10 | ||||
-rw-r--r-- | chrome/browser/memory_details.cc | 163 | ||||
-rw-r--r-- | chrome/browser/memory_details.h | 33 | ||||
-rw-r--r-- | chrome/browser/memory_details_linux.cc | 258 | ||||
-rw-r--r-- | chrome/browser/memory_details_win.cc | 156 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_sandbox_host_linux.cc | 4 | ||||
-rw-r--r-- | chrome/browser/renderer_host/render_sandbox_host_linux.h | 2 | ||||
-rw-r--r-- | chrome/browser/resources/about_memory_linux.html | 141 | ||||
-rw-r--r-- | chrome/browser/zygote_host_linux.cc | 1 | ||||
-rw-r--r-- | chrome/browser/zygote_host_linux.h | 5 | ||||
-rw-r--r-- | chrome/chrome.gyp | 3 | ||||
-rw-r--r-- | chrome/common/child_process_info.cc | 4 | ||||
-rw-r--r-- | chrome/common/child_process_info.h | 2 | ||||
-rw-r--r-- | chrome/common/temp_scaffolding_stubs.cc | 2 |
17 files changed, 526 insertions, 282 deletions
diff --git a/base/process_util.h b/base/process_util.h index f002f85..91cca61 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -286,12 +286,20 @@ class NamedProcessIterator { }; // 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 @@ struct WorkingSetKBytes { // 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..53e7d02 100644 --- a/base/process_util_linux.cc +++ b/base/process_util_linux.cc @@ -292,11 +292,10 @@ bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { 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 6cc4491..f4a5da6 100644 --- a/chrome/browser/browser_about_handler.cc +++ b/chrome/browser/browser_about_handler.cc @@ -620,17 +620,17 @@ void AboutMemoryHandler::OnDetailsAvailable() { 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..b840604 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd @@ -13,7 +13,15 @@ without changes to the corresponding grd file. aa3 --> <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 @@ static ProcessData g_process_template[MemoryDetails::MAX_BROWSERS]; // 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 @@ void MemoryDetails::CollectChildInfoOnIOThread() { 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 @@ void MemoryDetails::CollectChildInfoOnUIThread() { // 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 @@ void MemoryDetails::CollectChildInfoOnUIThread() { 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 @@ void MemoryDetails::UpdateHistograms() { // 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 @@ void MemoryDetails::UpdateHistograms() { 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 @@ class MessageLoop; // 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 @@ typedef std::vector<ProcessMemoryInformation> ProcessMemoryInformationList; // 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 @@ struct ProcessData { // } 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 @@ class MemoryDetails : public base::RefCountedThreadSafe<MemoryDetails> { // 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 @@ RenderSandboxHostLinux::RenderSandboxHostLinux() { 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 @@ class RenderSandboxHostLinux { // 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 @@ class RenderSandboxHostLinux { 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 a06002f..eeee845 100644 --- a/chrome/browser/resources/about_memory_linux.html +++ b/chrome/browser/resources/about_memory_linux.html @@ -268,15 +268,13 @@ table.list .noResults td { 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 @@ function enableHelpTooltips() { <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 @@ function enableHelpTooltips() { 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. - </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. + <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><i>(Note that the memory for this tab is not included in the browser totals)</i></p> </div> </div> </th> @@ -489,18 +460,8 @@ function enableHelpTooltips() { <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 @@ function enableHelpTooltips() { </div> </td> <td class='number'> - <span class='th' jscontent="formatNumber(ws_priv + ws_shareable - ws_shared)"></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> + <span class='th' jscontent="formatNumber(ws_priv)"></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 @@ function enableHelpTooltips() { <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'> - Memory - </th> <th colspan='2'> - Virtual memory + Memory </th> - </tr> <tr class='secondRow doNotFilter'> <th class='pid'> @@ -578,16 +523,7 @@ function enableHelpTooltips() { 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 @@ function enableHelpTooltips() { </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> - </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> + <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' 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 @@ function enableHelpTooltips() { </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 @@ function enableHelpTooltips() { </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 @@ ZygoteHost::ZygoteHost() { 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 94ac92e..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 @@ class ZygoteHost { 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 177f6da..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 @@ std::wstring ChildProcessInfo::GetTypeNameInEnglish( 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..fa03c0f 100644 --- a/chrome/common/child_process_info.h +++ b/chrome/common/child_process_info.h @@ -18,6 +18,8 @@ class ChildProcessInfo { 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 @@ bool RLZTracker::RecordProductEvent(Product product, AccessPoint point, //-------------------------------------------------------------------------- +#if defined(OS_MACOSX) MemoryDetails::MemoryDetails() { NOTIMPLEMENTED(); } @@ -218,6 +219,7 @@ void MemoryDetails::StartFetch() { NOTIMPLEMENTED(); OnDetailsAvailable(); } +#endif #if defined(OS_LINUX) && defined(TOOLKIT_VIEWS) // This should prompt the user if she wants to allow more than one concurrent |