blob: 14506c31397ca017bd31c300deb9c0ef5fb64e6f [file] [log] [blame]
[email protected]54fd1d32009-09-01 00:12:581// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/memory_details.h"
6
7#include <unistd.h>
8#include <fcntl.h>
9#include <dirent.h>
10
[email protected]8e2b6472010-12-15 22:19:4811#include <set>
12
[email protected]54fd1d32009-09-01 00:12:5813#include "base/eintr_wrapper.h"
14#include "base/file_version_info.h"
15#include "base/string_util.h"
16#include "base/process_util.h"
[email protected]be1ce6a72010-08-03 14:35:2217#include "base/utf_string_conversions.h"
[email protected]d27893f62010-07-03 05:47:4218#include "chrome/browser/browser_child_process_host.h"
[email protected]e36717272010-10-12 12:07:1319#include "chrome/browser/browser_thread.h"
[email protected]21fc8e72010-03-09 21:25:1920#include "chrome/browser/zygote_host_linux.h"
[email protected]54fd1d32009-09-01 00:12:5821#include "chrome/common/chrome_constants.h"
22#include "chrome/common/url_constants.h"
23#include "grit/chromium_strings.h"
24
25// Known browsers which we collect details for.
26enum BrowserType {
27 CHROME = 0,
28 FIREFOX,
[email protected]f3f032f82010-07-07 17:32:5329 ICEWEASEL,
[email protected]54fd1d32009-09-01 00:12:5830 OPERA,
31 KONQUEROR,
32 EPIPHANY,
33 MIDORI,
34 MAX_BROWSERS
35} BrowserProcess;
36
37// The pretty printed names of those browsers. Matches up with enum
38// BrowserType.
39static const char kBrowserPrettyNames[][10] = {
40 "Chrome",
41 "Firefox",
[email protected]f3f032f82010-07-07 17:32:5342 "Iceweasel",
[email protected]54fd1d32009-09-01 00:12:5843 "Opera",
44 "Konqueror",
45 "Epiphany",
46 "Midori",
47};
48
49// A mapping from process name to the type of browser.
50static const struct {
[email protected]576786482010-04-05 18:54:3251 const char process_name[16];
[email protected]54fd1d32009-09-01 00:12:5852 BrowserType browser;
53 } kBrowserBinaryNames[] = {
54 { "firefox", FIREFOX },
55 { "firefox-3.5", FIREFOX },
56 { "firefox-3.0", FIREFOX },
[email protected]576786482010-04-05 18:54:3257 { "firefox-bin", FIREFOX },
[email protected]f3f032f82010-07-07 17:32:5358 { "iceweasel", ICEWEASEL },
[email protected]54fd1d32009-09-01 00:12:5859 { "opera", OPERA },
60 { "konqueror", KONQUEROR },
[email protected]576786482010-04-05 18:54:3261 { "epiphany-browse", EPIPHANY },
[email protected]54fd1d32009-09-01 00:12:5862 { "epiphany", EPIPHANY },
63 { "midori", MIDORI },
64 { "", MAX_BROWSERS },
65};
66
[email protected]6fad2632009-11-02 05:59:3767MemoryDetails::MemoryDetails() {
[email protected]54fd1d32009-09-01 00:12:5868}
69
70ProcessData* MemoryDetails::ChromeBrowser() {
71 return &process_data_[0];
72}
73
74struct Process {
75 pid_t pid;
76 pid_t parent;
77 std::string name;
78};
79
80// Walk /proc and get information on all the processes running on the system.
81static bool GetProcesses(std::vector<Process>* processes) {
82 processes->clear();
83
84 DIR* dir = opendir("/proc");
85 if (!dir)
86 return false;
87
88 struct dirent* dent;
89 while ((dent = readdir(dir))) {
90 bool candidate = true;
91
92 // Filter out names which aren't ^[0-9]*$
93 for (const char* p = dent->d_name; *p; ++p) {
94 if (*p < '0' || *p > '9') {
95 candidate = false;
96 break;
97 }
98 }
99
100 if (!candidate)
101 continue;
102
103 char buf[256];
104 snprintf(buf, sizeof(buf), "/proc/%s/stat", dent->d_name);
105 const int fd = open(buf, O_RDONLY);
106 if (fd < 0)
107 continue;
108
109 const ssize_t len = HANDLE_EINTR(read(fd, buf, sizeof(buf) - 1));
[email protected]19cb9292010-04-16 23:00:15110 if (HANDLE_EINTR(close(fd)) < 0)
111 PLOG(ERROR) << "close";
[email protected]54fd1d32009-09-01 00:12:58112 if (len < 1)
113 continue;
114 buf[len] = 0;
115
116 // The start of the file looks like:
117 // <pid> (<name>) R <parent pid>
118 unsigned pid, ppid;
119 char *process_name;
120 if (sscanf(buf, "%u (%a[^)]) %*c %u", &pid, &process_name, &ppid) != 3)
121 continue;
122
123 Process process;
124 process.pid = pid;
125 process.parent = ppid;
126 process.name = process_name;
127 free(process_name);
128 processes->push_back(process);
129 }
130
131 closedir(dir);
132 return true;
133}
134
135// Given a process name, return the type of the browser which created that
136// process, or |MAX_BROWSERS| if we don't know about it.
137static BrowserType GetBrowserType(const std::string& process_name) {
138 for (unsigned i = 0; kBrowserBinaryNames[i].process_name[0]; ++i) {
139 if (strcmp(process_name.c_str(), kBrowserBinaryNames[i].process_name) == 0)
140 return kBrowserBinaryNames[i].browser;
141 }
142
143 return MAX_BROWSERS;
144}
145
146// For each of a list of pids, collect memory information about that process
147// and append a record to |out|
148static void GetProcessDataMemoryInformation(
149 const std::vector<pid_t>& pids, ProcessData* out) {
150 for (std::vector<pid_t>::const_iterator
151 i = pids.begin(); i != pids.end(); ++i) {
152 ProcessMemoryInformation pmi;
153
154 pmi.pid = *i;
155 pmi.num_processes = 1;
156
157 if (pmi.pid == base::GetCurrentProcId())
158 pmi.type = ChildProcessInfo::BROWSER_PROCESS;
159 else
160 pmi.type = ChildProcessInfo::UNKNOWN_PROCESS;
161
162 base::ProcessMetrics* metrics =
163 base::ProcessMetrics::CreateProcessMetrics(*i);
164 metrics->GetWorkingSetKBytes(&pmi.working_set);
165 delete metrics;
166
167 out->processes.push_back(pmi);
168 }
169}
170
171// Find all children of the given process.
172static void GetAllChildren(const std::vector<Process>& processes,
[email protected]21fc8e72010-03-09 21:25:19173 const pid_t root, const pid_t zygote,
174 std::vector<pid_t>* out) {
[email protected]54fd1d32009-09-01 00:12:58175 out->clear();
176 out->push_back(root);
177
178 std::set<pid_t> wavefront, next_wavefront;
179 wavefront.insert(root);
[email protected]21fc8e72010-03-09 21:25:19180 bool zygote_found = zygote ? false : true;
[email protected]54fd1d32009-09-01 00:12:58181
182 while (wavefront.size()) {
183 for (std::vector<Process>::const_iterator
184 i = processes.begin(); i != processes.end(); ++i) {
[email protected]21fc8e72010-03-09 21:25:19185 // Handle the zygote separately. With the SUID sandbox and a separate
186 // pid namespace, the zygote's parent process is not the browser.
187 if (!zygote_found && zygote == i->pid) {
188 zygote_found = true;
189 out->push_back(i->pid);
190 next_wavefront.insert(i->pid);
191 } else if (wavefront.count(i->parent)) {
[email protected]54fd1d32009-09-01 00:12:58192 out->push_back(i->pid);
193 next_wavefront.insert(i->pid);
194 }
195 }
196
197 wavefront.clear();
198 wavefront.swap(next_wavefront);
199 }
200}
201
202void MemoryDetails::CollectProcessData(
203 std::vector<ProcessMemoryInformation> child_info) {
[email protected]f8b3ef82010-10-11 02:45:52204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
[email protected]54fd1d32009-09-01 00:12:58205
206 std::vector<Process> processes;
207 GetProcesses(&processes);
208 std::set<pid_t> browsers_found;
209
210 // For each process on the system, if it appears to be a browser process and
211 // it's parent isn't a browser process, then record it in |browsers_found|.
212 for (std::vector<Process>::const_iterator
213 i = processes.begin(); i != processes.end(); ++i) {
214 const BrowserType type = GetBrowserType(i->name);
215 if (type != MAX_BROWSERS) {
216 bool found_parent = false;
217
218 // Find the parent of |i|
219 for (std::vector<Process>::const_iterator
220 j = processes.begin(); j != processes.end(); ++j) {
221 if (j->pid == i->parent) {
222 found_parent = true;
223
224 if (GetBrowserType(j->name) != type) {
225 // We went too far and ended up with something else, which means
226 // that |i| is a browser.
227 browsers_found.insert(i->pid);
228 break;
229 }
230 }
231 }
232
233 if (!found_parent)
234 browsers_found.insert(i->pid);
235 }
236 }
237
238 std::vector<pid_t> current_browser_processes;
[email protected]d3c6c0d72010-12-09 08:15:04239 const pid_t zygote = ZygoteHost::GetInstance()->pid();
[email protected]21fc8e72010-03-09 21:25:19240 GetAllChildren(processes, getpid(), zygote, &current_browser_processes);
[email protected]54fd1d32009-09-01 00:12:58241 ProcessData current_browser;
242 GetProcessDataMemoryInformation(current_browser_processes, &current_browser);
243 current_browser.name = chrome::kBrowserAppName;
244 current_browser.process_name = L"chrome";
245 process_data_.push_back(current_browser);
246
247 // For each browser process, collect a list of its children and get the
248 // memory usage of each.
249 for (std::set<pid_t>::const_iterator
250 i = browsers_found.begin(); i != browsers_found.end(); ++i) {
251 std::vector<pid_t> browser_processes;
[email protected]21fc8e72010-03-09 21:25:19252 GetAllChildren(processes, *i, 0, &browser_processes);
[email protected]54fd1d32009-09-01 00:12:58253 ProcessData browser;
254 GetProcessDataMemoryInformation(browser_processes, &browser);
255
256 for (std::vector<Process>::const_iterator
257 j = processes.begin(); j != processes.end(); ++j) {
258 if (j->pid == *i) {
259 BrowserType type = GetBrowserType(j->name);
260 if (type != MAX_BROWSERS)
261 browser.name = ASCIIToWide(kBrowserPrettyNames[type]);
262 break;
263 }
264 }
265
266 process_data_.push_back(browser);
267 }
268
269 // Finally return to the browser thread.
[email protected]f8b3ef82010-10-11 02:45:52270 BrowserThread::PostTask(
271 BrowserThread::UI, FROM_HERE,
[email protected]54fd1d32009-09-01 00:12:58272 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
273}