memory_details_linux.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright (c) 2012 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 <sys/types.h> 8#include <unistd.h> 9 10#include <map> 11#include <set> 12 13#include "base/bind.h" 14#include "base/process_util.h" 15#include "base/strings/string_util.h" 16#include "base/strings/utf_string_conversions.h" 17#include "chrome/common/chrome_constants.h" 18#include "content/public/browser/browser_thread.h" 19#include "content/public/common/process_type.h" 20#include "grit/chromium_strings.h" 21#include "ui/base/l10n/l10n_util.h" 22 23using base::ProcessEntry; 24using content::BrowserThread; 25 26// Known browsers which we collect details for. 27enum BrowserType { 28 CHROME = 0, 29 FIREFOX, 30 ICEWEASEL, 31 OPERA, 32 KONQUEROR, 33 EPIPHANY, 34 MIDORI, 35 MAX_BROWSERS 36} BrowserProcess; 37 38// The pretty printed names of those browsers. Matches up with enum 39// BrowserType. 40static const char kBrowserPrettyNames[][10] = { 41 "Chrome", 42 "Firefox", 43 "Iceweasel", 44 "Opera", 45 "Konqueror", 46 "Epiphany", 47 "Midori", 48}; 49 50// A mapping from process name to the type of browser. 51static const struct { 52 const char process_name[16]; 53 BrowserType browser; 54} kBrowserBinaryNames[] = { 55 { "firefox", FIREFOX }, 56 { "firefox-3.5", FIREFOX }, 57 { "firefox-3.0", FIREFOX }, 58 { "firefox-bin", FIREFOX }, 59 { "iceweasel", ICEWEASEL }, 60 { "opera", OPERA }, 61 { "konqueror", KONQUEROR }, 62 { "epiphany-browse", EPIPHANY }, 63 { "epiphany", EPIPHANY }, 64 { "midori", MIDORI }, 65 { "", MAX_BROWSERS }, 66}; 67 68MemoryDetails::MemoryDetails() 69 : user_metrics_mode_(UPDATE_USER_METRICS) { 70} 71 72ProcessData* MemoryDetails::ChromeBrowser() { 73 return &process_data_[0]; 74} 75 76struct Process { 77 pid_t pid; 78 pid_t parent; 79 std::string name; 80}; 81 82typedef std::map<pid_t, Process> ProcessMap; 83 84// Get information on all the processes running on the system. 85static ProcessMap GetProcesses() { 86 ProcessMap map; 87 88 base::ProcessIterator process_iter(NULL); 89 while (const ProcessEntry* process_entry = process_iter.NextProcessEntry()) { 90 Process process; 91 process.pid = process_entry->pid(); 92 process.parent = process_entry->parent_pid(); 93 process.name = process_entry->exe_file(); 94 map.insert(std::make_pair(process.pid, process)); 95 } 96 return map; 97} 98 99// Given a process name, return the type of the browser which created that 100// process, or |MAX_BROWSERS| if we don't know about it. 101static BrowserType GetBrowserType(const std::string& process_name) { 102 for (unsigned i = 0; kBrowserBinaryNames[i].process_name[0]; ++i) { 103 if (strcmp(process_name.c_str(), kBrowserBinaryNames[i].process_name) == 0) 104 return kBrowserBinaryNames[i].browser; 105 } 106 107 return MAX_BROWSERS; 108} 109 110// For each of a list of pids, collect memory information about that process. 111static ProcessData GetProcessDataMemoryInformation( 112 const std::vector<pid_t>& pids) { 113 ProcessData process_data; 114 for (std::vector<pid_t>::const_iterator iter = pids.begin(); 115 iter != pids.end(); 116 ++iter) { 117 ProcessMemoryInformation pmi; 118 119 pmi.pid = *iter; 120 pmi.num_processes = 1; 121 122 if (pmi.pid == base::GetCurrentProcId()) 123 pmi.process_type = content::PROCESS_TYPE_BROWSER; 124 else 125 pmi.process_type = content::PROCESS_TYPE_UNKNOWN; 126 127 base::ProcessMetrics* metrics = 128 base::ProcessMetrics::CreateProcessMetrics(*iter); 129 metrics->GetWorkingSetKBytes(&pmi.working_set); 130 delete metrics; 131 132 process_data.processes.push_back(pmi); 133 } 134 return process_data; 135} 136 137// Find all children of the given process with pid |root|. 138static std::vector<pid_t> GetAllChildren(const ProcessMap& processes, 139 const pid_t root) { 140 std::vector<pid_t> children; 141 children.push_back(root); 142 143 std::set<pid_t> wavefront, next_wavefront; 144 wavefront.insert(root); 145 146 while (wavefront.size()) { 147 for (ProcessMap::const_iterator iter = processes.begin(); 148 iter != processes.end(); 149 ++iter) { 150 const Process& process = iter->second; 151 if (wavefront.count(process.parent)) { 152 children.push_back(process.pid); 153 next_wavefront.insert(process.pid); 154 } 155 } 156 157 wavefront.clear(); 158 wavefront.swap(next_wavefront); 159 } 160 return children; 161} 162 163void MemoryDetails::CollectProcessData( 164 const std::vector<ProcessMemoryInformation>& child_info) { 165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 166 167 ProcessMap process_map = GetProcesses(); 168 std::set<pid_t> browsers_found; 169 170 // For each process on the system, if it appears to be a browser process and 171 // it's parent isn't a browser process, then record it in |browsers_found|. 172 for (ProcessMap::const_iterator iter = process_map.begin(); 173 iter != process_map.end(); 174 ++iter) { 175 const Process& current_process = iter->second; 176 const BrowserType type = GetBrowserType(current_process.name); 177 if (type == MAX_BROWSERS) 178 continue; 179 180 ProcessMap::const_iterator parent_iter = 181 process_map.find(current_process.parent); 182 if (parent_iter == process_map.end()) { 183 browsers_found.insert(current_process.pid); 184 continue; 185 } 186 187 if (GetBrowserType(parent_iter->second.name) != type) { 188 // We found a process whose type is diffent from its parent's type. 189 // That means it is the root process of the browser. 190 browsers_found.insert(current_process.pid); 191 break; 192 } 193 } 194 195 ProcessData current_browser = 196 GetProcessDataMemoryInformation(GetAllChildren(process_map, getpid())); 197 current_browser.name = l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME); 198 current_browser.process_name = ASCIIToUTF16("chrome"); 199 200 for (std::vector<ProcessMemoryInformation>::iterator 201 i = current_browser.processes.begin(); 202 i != current_browser.processes.end(); ++i) { 203 // Check if this is one of the child processes whose data we collected 204 // on the IO thread, and if so copy over that data. 205 for (size_t child = 0; child < child_info.size(); child++) { 206 if (child_info[child].pid != i->pid) 207 continue; 208 i->titles = child_info[child].titles; 209 i->process_type = child_info[child].process_type; 210 break; 211 } 212 } 213 214 process_data_.push_back(current_browser); 215 216 // For each browser process, collect a list of its children and get the 217 // memory usage of each. 218 for (std::set<pid_t>::const_iterator iter = browsers_found.begin(); 219 iter != browsers_found.end(); 220 ++iter) { 221 std::vector<pid_t> browser_processes = GetAllChildren(process_map, *iter); 222 ProcessData browser = GetProcessDataMemoryInformation(browser_processes); 223 224 ProcessMap::const_iterator process_iter = process_map.find(*iter); 225 if (process_iter == process_map.end()) 226 continue; 227 BrowserType type = GetBrowserType(process_iter->second.name); 228 if (type != MAX_BROWSERS) 229 browser.name = ASCIIToUTF16(kBrowserPrettyNames[type]); 230 process_data_.push_back(browser); 231 } 232 233 // Finally return to the browser thread. 234 BrowserThread::PostTask( 235 BrowserThread::UI, FROM_HERE, 236 base::Bind(&MemoryDetails::CollectChildInfoOnUIThread, this)); 237} 238