| Index: chrome/browser/memory_details_linux.cc
|
| diff --git a/chrome/browser/memory_details_linux.cc b/chrome/browser/memory_details_linux.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a1ed5e9eef78ef43a46d359997babcb2d656cac8
|
| --- /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));
|
| +}
|
|
|