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. 26enum 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. 39static 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. 50static 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 67MemoryDetails::MemoryDetails() { 68} 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)); 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. 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, 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 202void 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, ¤t_browser_processes); 241 ProcessData current_browser; 242 GetProcessDataMemoryInformation(current_browser_processes, ¤t_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