diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-01 00:12:58 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-01 00:12:58 +0000 |
commit | 54fd1d3ac91d117719f157fad3ead41518865f52 (patch) | |
tree | be3ec12e4da55384888a13707d8eee0f05e60a07 /chrome/browser/memory_details_linux.cc | |
parent | 1f758608ae7c38a580d574ec7e5061eb0bfa7ee2 (diff) | |
download | chromium_src-54fd1d3ac91d117719f157fad3ead41518865f52.zip chromium_src-54fd1d3ac91d117719f157fad3ead41518865f52.tar.gz chromium_src-54fd1d3ac91d117719f157fad3ead41518865f52.tar.bz2 |
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
Diffstat (limited to 'chrome/browser/memory_details_linux.cc')
-rw-r--r-- | chrome/browser/memory_details_linux.cc | 258 |
1 files changed, 258 insertions, 0 deletions
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)); +} |