OLD | NEW |
(Empty) | |
| 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 "app/l10n_util.h" |
| 12 #include "base/eintr_wrapper.h" |
| 13 #include "base/file_version_info.h" |
| 14 #include "base/string_util.h" |
| 15 #include "base/process_util.h" |
| 16 #include "chrome/browser/browser_process.h" |
| 17 #include "chrome/browser/chrome_thread.h" |
| 18 #include "chrome/common/child_process_host.h" |
| 19 #include "chrome/common/chrome_constants.h" |
| 20 #include "chrome/common/url_constants.h" |
| 21 #include "grit/chromium_strings.h" |
| 22 |
| 23 // Known browsers which we collect details for. |
| 24 enum BrowserType { |
| 25 CHROME = 0, |
| 26 FIREFOX, |
| 27 OPERA, |
| 28 KONQUEROR, |
| 29 EPIPHANY, |
| 30 MIDORI, |
| 31 MAX_BROWSERS |
| 32 } BrowserProcess; |
| 33 |
| 34 // The pretty printed names of those browsers. Matches up with enum |
| 35 // BrowserType. |
| 36 static const char kBrowserPrettyNames[][10] = { |
| 37 "Chrome", |
| 38 "Firefox", |
| 39 "Opera", |
| 40 "Konqueror", |
| 41 "Epiphany", |
| 42 "Midori", |
| 43 }; |
| 44 |
| 45 // A mapping from process name to the type of browser. |
| 46 static const struct { |
| 47 const char process_name[17]; |
| 48 BrowserType browser; |
| 49 } kBrowserBinaryNames[] = { |
| 50 { "firefox", FIREFOX }, |
| 51 { "firefox-3.5", FIREFOX }, |
| 52 { "firefox-3.0", FIREFOX }, |
| 53 { "opera", OPERA }, |
| 54 { "konqueror", KONQUEROR }, |
| 55 { "epiphany-browser", EPIPHANY }, |
| 56 { "epiphany", EPIPHANY }, |
| 57 { "midori", MIDORI }, |
| 58 { "", MAX_BROWSERS }, |
| 59 }; |
| 60 |
| 61 MemoryDetails::MemoryDetails() |
| 62 : ui_loop_(NULL) { |
| 63 } |
| 64 |
| 65 ProcessData* MemoryDetails::ChromeBrowser() { |
| 66 return &process_data_[0]; |
| 67 } |
| 68 |
| 69 struct Process { |
| 70 pid_t pid; |
| 71 pid_t parent; |
| 72 std::string name; |
| 73 }; |
| 74 |
| 75 // Walk /proc and get information on all the processes running on the system. |
| 76 static bool GetProcesses(std::vector<Process>* processes) { |
| 77 processes->clear(); |
| 78 |
| 79 DIR* dir = opendir("/proc"); |
| 80 if (!dir) |
| 81 return false; |
| 82 |
| 83 struct dirent* dent; |
| 84 while ((dent = readdir(dir))) { |
| 85 bool candidate = true; |
| 86 |
| 87 // Filter out names which aren't ^[0-9]*$ |
| 88 for (const char* p = dent->d_name; *p; ++p) { |
| 89 if (*p < '0' || *p > '9') { |
| 90 candidate = false; |
| 91 break; |
| 92 } |
| 93 } |
| 94 |
| 95 if (!candidate) |
| 96 continue; |
| 97 |
| 98 char buf[256]; |
| 99 snprintf(buf, sizeof(buf), "/proc/%s/stat", dent->d_name); |
| 100 const int fd = open(buf, O_RDONLY); |
| 101 if (fd < 0) |
| 102 continue; |
| 103 |
| 104 const ssize_t len = HANDLE_EINTR(read(fd, buf, sizeof(buf) - 1)); |
| 105 HANDLE_EINTR(close(fd)); |
| 106 if (len < 1) |
| 107 continue; |
| 108 buf[len] = 0; |
| 109 |
| 110 // The start of the file looks like: |
| 111 // <pid> (<name>) R <parent pid> |
| 112 unsigned pid, ppid; |
| 113 char *process_name; |
| 114 if (sscanf(buf, "%u (%a[^)]) %*c %u", &pid, &process_name, &ppid) != 3) |
| 115 continue; |
| 116 |
| 117 Process process; |
| 118 process.pid = pid; |
| 119 process.parent = ppid; |
| 120 process.name = process_name; |
| 121 free(process_name); |
| 122 processes->push_back(process); |
| 123 } |
| 124 |
| 125 closedir(dir); |
| 126 return true; |
| 127 } |
| 128 |
| 129 // Given a process name, return the type of the browser which created that |
| 130 // process, or |MAX_BROWSERS| if we don't know about it. |
| 131 static BrowserType GetBrowserType(const std::string& process_name) { |
| 132 for (unsigned i = 0; kBrowserBinaryNames[i].process_name[0]; ++i) { |
| 133 if (strcmp(process_name.c_str(), kBrowserBinaryNames[i].process_name) == 0) |
| 134 return kBrowserBinaryNames[i].browser; |
| 135 } |
| 136 |
| 137 return MAX_BROWSERS; |
| 138 } |
| 139 |
| 140 // For each of a list of pids, collect memory information about that process |
| 141 // and append a record to |out| |
| 142 static void GetProcessDataMemoryInformation( |
| 143 const std::vector<pid_t>& pids, ProcessData* out) { |
| 144 for (std::vector<pid_t>::const_iterator |
| 145 i = pids.begin(); i != pids.end(); ++i) { |
| 146 ProcessMemoryInformation pmi; |
| 147 |
| 148 pmi.pid = *i; |
| 149 pmi.num_processes = 1; |
| 150 |
| 151 if (pmi.pid == base::GetCurrentProcId()) |
| 152 pmi.type = ChildProcessInfo::BROWSER_PROCESS; |
| 153 else |
| 154 pmi.type = ChildProcessInfo::UNKNOWN_PROCESS; |
| 155 |
| 156 base::ProcessMetrics* metrics = |
| 157 base::ProcessMetrics::CreateProcessMetrics(*i); |
| 158 metrics->GetWorkingSetKBytes(&pmi.working_set); |
| 159 delete metrics; |
| 160 |
| 161 out->processes.push_back(pmi); |
| 162 } |
| 163 } |
| 164 |
| 165 // Find all children of the given process. |
| 166 static void GetAllChildren(const std::vector<Process>& processes, |
| 167 pid_t root, std::vector<pid_t>* out) { |
| 168 out->clear(); |
| 169 out->push_back(root); |
| 170 |
| 171 std::set<pid_t> wavefront, next_wavefront; |
| 172 wavefront.insert(root); |
| 173 |
| 174 while (wavefront.size()) { |
| 175 for (std::vector<Process>::const_iterator |
| 176 i = processes.begin(); i != processes.end(); ++i) { |
| 177 if (wavefront.count(i->parent)) { |
| 178 out->push_back(i->pid); |
| 179 next_wavefront.insert(i->pid); |
| 180 } |
| 181 } |
| 182 |
| 183 wavefront.clear(); |
| 184 wavefront.swap(next_wavefront); |
| 185 } |
| 186 } |
| 187 |
| 188 void MemoryDetails::CollectProcessData( |
| 189 std::vector<ProcessMemoryInformation> child_info) { |
| 190 DCHECK(MessageLoop::current() == |
| 191 ChromeThread::GetMessageLoop(ChromeThread::FILE)); |
| 192 |
| 193 std::vector<Process> processes; |
| 194 GetProcesses(&processes); |
| 195 std::set<pid_t> browsers_found; |
| 196 |
| 197 // For each process on the system, if it appears to be a browser process and |
| 198 // it's parent isn't a browser process, then record it in |browsers_found|. |
| 199 for (std::vector<Process>::const_iterator |
| 200 i = processes.begin(); i != processes.end(); ++i) { |
| 201 const BrowserType type = GetBrowserType(i->name); |
| 202 if (type != MAX_BROWSERS) { |
| 203 bool found_parent = false; |
| 204 |
| 205 // Find the parent of |i| |
| 206 for (std::vector<Process>::const_iterator |
| 207 j = processes.begin(); j != processes.end(); ++j) { |
| 208 if (j->pid == i->parent) { |
| 209 found_parent = true; |
| 210 |
| 211 if (GetBrowserType(j->name) != type) { |
| 212 // We went too far and ended up with something else, which means |
| 213 // that |i| is a browser. |
| 214 browsers_found.insert(i->pid); |
| 215 break; |
| 216 } |
| 217 } |
| 218 } |
| 219 |
| 220 if (!found_parent) |
| 221 browsers_found.insert(i->pid); |
| 222 } |
| 223 } |
| 224 |
| 225 std::vector<pid_t> current_browser_processes; |
| 226 GetAllChildren(processes, getpid(), ¤t_browser_processes); |
| 227 ProcessData current_browser; |
| 228 GetProcessDataMemoryInformation(current_browser_processes, ¤t_browser); |
| 229 current_browser.name = chrome::kBrowserAppName; |
| 230 current_browser.process_name = L"chrome"; |
| 231 process_data_.push_back(current_browser); |
| 232 |
| 233 // For each browser process, collect a list of its children and get the |
| 234 // memory usage of each. |
| 235 for (std::set<pid_t>::const_iterator |
| 236 i = browsers_found.begin(); i != browsers_found.end(); ++i) { |
| 237 std::vector<pid_t> browser_processes; |
| 238 GetAllChildren(processes, *i, &browser_processes); |
| 239 ProcessData browser; |
| 240 GetProcessDataMemoryInformation(browser_processes, &browser); |
| 241 |
| 242 for (std::vector<Process>::const_iterator |
| 243 j = processes.begin(); j != processes.end(); ++j) { |
| 244 if (j->pid == *i) { |
| 245 BrowserType type = GetBrowserType(j->name); |
| 246 if (type != MAX_BROWSERS) |
| 247 browser.name = ASCIIToWide(kBrowserPrettyNames[type]); |
| 248 break; |
| 249 } |
| 250 } |
| 251 |
| 252 process_data_.push_back(browser); |
| 253 } |
| 254 |
| 255 // Finally return to the browser thread. |
| 256 ui_loop_->PostTask(FROM_HERE, |
| 257 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnUIThread)); |
| 258 } |
OLD | NEW |