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