• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
11 #include <set>
12 
13 #include "base/eintr_wrapper.h"
14 #include "base/file_version_info.h"
15 #include "base/string_util.h"
16 #include "base/process_util.h"
17 #include "base/utf_string_conversions.h"
18 #include "chrome/common/chrome_constants.h"
19 #include "chrome/common/url_constants.h"
20 #include "content/browser/browser_child_process_host.h"
21 #include "content/browser/browser_thread.h"
22 #include "content/browser/zygote_host_linux.h"
23 #include "grit/chromium_strings.h"
24 
25 // Known browsers which we collect details for.
26 enum BrowserType {
27   CHROME = 0,
28   FIREFOX,
29   ICEWEASEL,
30   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.
39 static const char kBrowserPrettyNames[][10] = {
40   "Chrome",
41   "Firefox",
42   "Iceweasel",
43   "Opera",
44   "Konqueror",
45   "Epiphany",
46   "Midori",
47 };
48 
49 // A mapping from process name to the type of browser.
50 static const struct {
51   const char process_name[16];
52   BrowserType browser;
53   } kBrowserBinaryNames[] = {
54   { "firefox", FIREFOX },
55   { "firefox-3.5", FIREFOX },
56   { "firefox-3.0", FIREFOX },
57   { "firefox-bin", FIREFOX },
58   { "iceweasel", ICEWEASEL },
59   { "opera", OPERA },
60   { "konqueror", KONQUEROR },
61   { "epiphany-browse", EPIPHANY },
62   { "epiphany", EPIPHANY },
63   { "midori", MIDORI },
64   { "", MAX_BROWSERS },
65 };
66 
MemoryDetails()67 MemoryDetails::MemoryDetails() {
68 }
69 
ChromeBrowser()70 ProcessData* MemoryDetails::ChromeBrowser() {
71   return &process_data_[0];
72 }
73 
74 struct 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.
GetProcesses(std::vector<Process> * processes)81 static 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));
110     if (HANDLE_EINTR(close(fd)) < 0)
111       PLOG(ERROR) << "close";
112     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.
GetBrowserType(const std::string & process_name)137 static 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|
GetProcessDataMemoryInformation(const std::vector<pid_t> & pids,ProcessData * out)148 static 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.
GetAllChildren(const std::vector<Process> & processes,const pid_t root,const pid_t zygote,std::vector<pid_t> * out)172 static void GetAllChildren(const std::vector<Process>& processes,
173                            const pid_t root, const pid_t zygote,
174                            std::vector<pid_t>* out) {
175   out->clear();
176   out->push_back(root);
177 
178   std::set<pid_t> wavefront, next_wavefront;
179   wavefront.insert(root);
180   bool zygote_found = zygote ? false : true;
181 
182   while (wavefront.size()) {
183     for (std::vector<Process>::const_iterator
184          i = processes.begin(); i != processes.end(); ++i) {
185       // 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)) {
192         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 
CollectProcessData(const std::vector<ProcessMemoryInformation> & child_info)202 void MemoryDetails::CollectProcessData(
203     const std::vector<ProcessMemoryInformation>& child_info) {
204   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
205 
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;
239   const pid_t zygote = ZygoteHost::GetInstance()->pid();
240   GetAllChildren(processes, getpid(), zygote, &current_browser_processes);
241   ProcessData current_browser;
242   GetProcessDataMemoryInformation(current_browser_processes, &current_browser);
243   current_browser.name = WideToUTF16(chrome::kBrowserAppName);
244   current_browser.process_name = ASCIIToUTF16("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;
252     GetAllChildren(processes, *i, 0, &browser_processes);
253     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 = ASCIIToUTF16(kBrowserPrettyNames[type]);
262         break;
263       }
264     }
265 
266     process_data_.push_back(browser);
267   }
268 
269   // Finally return to the browser thread.
270   BrowserThread::PostTask(
271       BrowserThread::UI, FROM_HERE,
272       NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread));
273 }
274