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)); |
+} |