blob: cbb9f4cac86f6ba49ecaf68d08c6fdcb1b6a4361 [file] [log] [blame]
[email protected]4306df72012-04-20 18:58:571// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]54fd1d32009-09-01 00:12:582// 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
[email protected]500c8722012-06-18 17:34:237#include <sys/types.h>
8#include <unistd.h>
9
[email protected]e9720e822012-06-07 00:07:4810#include <map>
[email protected]8e2b6472010-12-15 22:19:4811#include <set>
12
[email protected]24d69692011-10-21 18:26:5113#include "base/bind.h"
[email protected]48e303442013-07-18 19:13:1514#include "base/file_util.h"
[email protected]d09a4ce1c2013-07-24 17:37:0215#include "base/process/process_iterator.h"
16#include "base/process/process_metrics.h"
[email protected]48e303442013-07-18 19:13:1517#include "base/strings/string_number_conversions.h"
[email protected]f9b294362013-06-10 20:22:3118#include "base/strings/string_util.h"
[email protected]112158af2013-06-07 23:46:1819#include "base/strings/utf_string_conversions.h"
[email protected]54fd1d32009-09-01 00:12:5820#include "chrome/common/chrome_constants.h"
[email protected]c38831a12011-10-28 12:44:4921#include "content/public/browser/browser_thread.h"
[email protected]bd5d6cf2011-12-01 00:39:1222#include "content/public/common/process_type.h"
[email protected]b4320742013-02-06 02:57:3123#include "grit/chromium_strings.h"
24#include "ui/base/l10n/l10n_util.h"
[email protected]54fd1d32009-09-01 00:12:5825
[email protected]e9720e822012-06-07 00:07:4826using base::ProcessEntry;
[email protected]631bb742011-11-02 11:29:3927using content::BrowserThread;
28
[email protected]54fd1d32009-09-01 00:12:5829// Known browsers which we collect details for.
30enum BrowserType {
31 CHROME = 0,
32 FIREFOX,
[email protected]f3f032f82010-07-07 17:32:5333 ICEWEASEL,
[email protected]54fd1d32009-09-01 00:12:5834 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",
[email protected]f3f032f82010-07-07 17:32:5346 "Iceweasel",
[email protected]54fd1d32009-09-01 00:12:5847 "Opera",
48 "Konqueror",
49 "Epiphany",
50 "Midori",
51};
52
53// A mapping from process name to the type of browser.
54static const struct {
[email protected]576786482010-04-05 18:54:3255 const char process_name[16];
[email protected]54fd1d32009-09-01 00:12:5856 BrowserType browser;
[email protected]b4320742013-02-06 02:57:3157} kBrowserBinaryNames[] = {
[email protected]54fd1d32009-09-01 00:12:5858 { "firefox", FIREFOX },
59 { "firefox-3.5", FIREFOX },
60 { "firefox-3.0", FIREFOX },
[email protected]576786482010-04-05 18:54:3261 { "firefox-bin", FIREFOX },
[email protected]f3f032f82010-07-07 17:32:5362 { "iceweasel", ICEWEASEL },
[email protected]54fd1d32009-09-01 00:12:5863 { "opera", OPERA },
64 { "konqueror", KONQUEROR },
[email protected]576786482010-04-05 18:54:3265 { "epiphany-browse", EPIPHANY },
[email protected]54fd1d32009-09-01 00:12:5866 { "epiphany", EPIPHANY },
67 { "midori", MIDORI },
68 { "", MAX_BROWSERS },
69};
70
[email protected]4306df72012-04-20 18:58:5771MemoryDetails::MemoryDetails()
72 : user_metrics_mode_(UPDATE_USER_METRICS) {
[email protected]54fd1d32009-09-01 00:12:5873}
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
[email protected]e9720e822012-06-07 00:07:4885typedef std::map<pid_t, Process> ProcessMap;
[email protected]54fd1d32009-09-01 00:12:5886
[email protected]e9720e822012-06-07 00:07:4887// Get information on all the processes running on the system.
88static ProcessMap GetProcesses() {
89 ProcessMap map;
[email protected]54fd1d32009-09-01 00:12:5890
[email protected]e9720e822012-06-07 00:07:4891 base::ProcessIterator process_iter(NULL);
92 while (const ProcessEntry* process_entry = process_iter.NextProcessEntry()) {
[email protected]54fd1d32009-09-01 00:12:5893 Process process;
[email protected]e9720e822012-06-07 00:07:4894 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));
[email protected]54fd1d32009-09-01 00:12:5898 }
[email protected]e9720e822012-06-07 00:07:4899 return map;
[email protected]54fd1d32009-09-01 00:12:58100}
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
[email protected]e9720e822012-06-07 00:07:48113// 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) {
[email protected]54fd1d32009-09-01 00:12:58120 ProcessMemoryInformation pmi;
121
[email protected]e9720e822012-06-07 00:07:48122 pmi.pid = *iter;
[email protected]54fd1d32009-09-01 00:12:58123 pmi.num_processes = 1;
124
125 if (pmi.pid == base::GetCurrentProcId())
[email protected]f3b357692013-03-22 05:16:13126 pmi.process_type = content::PROCESS_TYPE_BROWSER;
[email protected]54fd1d32009-09-01 00:12:58127 else
[email protected]f3b357692013-03-22 05:16:13128 pmi.process_type = content::PROCESS_TYPE_UNKNOWN;
[email protected]54fd1d32009-09-01 00:12:58129
130 base::ProcessMetrics* metrics =
[email protected]e9720e822012-06-07 00:07:48131 base::ProcessMetrics::CreateProcessMetrics(*iter);
[email protected]54fd1d32009-09-01 00:12:58132 metrics->GetWorkingSetKBytes(&pmi.working_set);
133 delete metrics;
134
[email protected]e9720e822012-06-07 00:07:48135 process_data.processes.push_back(pmi);
[email protected]54fd1d32009-09-01 00:12:58136 }
[email protected]e9720e822012-06-07 00:07:48137 return process_data;
[email protected]54fd1d32009-09-01 00:12:58138}
139
[email protected]e9720e822012-06-07 00:07:48140// 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);
[email protected]54fd1d32009-09-01 00:12:58145
146 std::set<pid_t> wavefront, next_wavefront;
147 wavefront.insert(root);
148
149 while (wavefront.size()) {
[email protected]e9720e822012-06-07 00:07:48150 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);
[email protected]54fd1d32009-09-01 00:12:58157 }
158 }
159
160 wavefront.clear();
161 wavefront.swap(next_wavefront);
162 }
[email protected]e9720e822012-06-07 00:07:48163 return children;
[email protected]54fd1d32009-09-01 00:12:58164}
165
[email protected]48e303442013-07-18 19:13:15166#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 uint64 file_as_uint64;
172 if (!base::StringToUint64(file_as_string, &file_as_uint64))
173 return 0;
174 return file_as_uint64;
175}
176
177static void GetSwapData(SwapData* swap_data) {
178 base::FilePath zram_path("/sys/block/zram0");
179 uint64 orig_data_size = ReadFileToUint64(zram_path.Append("orig_data_size"));
180 if (orig_data_size <= 4096) {
181 // A single page is compressed at startup, and has a high compression
182 // ratio. We ignore this as it doesn't indicate any real swapping.
183 swap_data->orig_data_size = 0;
184 swap_data->num_reads = 0;
185 swap_data->num_writes = 0;
186 swap_data->compr_data_size = 0;
187 swap_data->mem_used_total = 0;
[email protected]d24f4262013-07-18 22:26:10188 return;
[email protected]48e303442013-07-18 19:13:15189 }
190 swap_data->orig_data_size = orig_data_size;
191 swap_data->num_reads = ReadFileToUint64(zram_path.Append("num_reads"));
192 swap_data->num_writes = ReadFileToUint64(zram_path.Append("num_writes"));
193 swap_data->compr_data_size =
194 ReadFileToUint64(zram_path.Append("compr_data_size"));
195 swap_data->mem_used_total =
196 ReadFileToUint64(zram_path.Append("mem_used_total"));
197}
198#endif
199
[email protected]54fd1d32009-09-01 00:12:58200void MemoryDetails::CollectProcessData(
[email protected]4df3ac62011-03-11 04:38:52201 const std::vector<ProcessMemoryInformation>& child_info) {
[email protected]f8b3ef82010-10-11 02:45:52202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
[email protected]54fd1d32009-09-01 00:12:58203
[email protected]e9720e822012-06-07 00:07:48204 ProcessMap process_map = GetProcesses();
[email protected]54fd1d32009-09-01 00:12:58205 std::set<pid_t> browsers_found;
206
207 // For each process on the system, if it appears to be a browser process and
208 // it's parent isn't a browser process, then record it in |browsers_found|.
[email protected]e9720e822012-06-07 00:07:48209 for (ProcessMap::const_iterator iter = process_map.begin();
210 iter != process_map.end();
211 ++iter) {
212 const Process& current_process = iter->second;
213 const BrowserType type = GetBrowserType(current_process.name);
214 if (type == MAX_BROWSERS)
215 continue;
[email protected]54fd1d32009-09-01 00:12:58216
[email protected]e9720e822012-06-07 00:07:48217 ProcessMap::const_iterator parent_iter =
218 process_map.find(current_process.parent);
219 if (parent_iter == process_map.end()) {
220 browsers_found.insert(current_process.pid);
221 continue;
222 }
[email protected]54fd1d32009-09-01 00:12:58223
[email protected]e9720e822012-06-07 00:07:48224 if (GetBrowserType(parent_iter->second.name) != type) {
225 // We found a process whose type is diffent from its parent's type.
226 // That means it is the root process of the browser.
227 browsers_found.insert(current_process.pid);
228 break;
[email protected]54fd1d32009-09-01 00:12:58229 }
230 }
231
[email protected]e9720e822012-06-07 00:07:48232 ProcessData current_browser =
233 GetProcessDataMemoryInformation(GetAllChildren(process_map, getpid()));
[email protected]b4320742013-02-06 02:57:31234 current_browser.name = l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME);
[email protected]4f260d02010-12-23 18:35:42235 current_browser.process_name = ASCIIToUTF16("chrome");
[email protected]e1873b92011-05-13 19:22:22236
237 for (std::vector<ProcessMemoryInformation>::iterator
238 i = current_browser.processes.begin();
239 i != current_browser.processes.end(); ++i) {
240 // Check if this is one of the child processes whose data we collected
241 // on the IO thread, and if so copy over that data.
242 for (size_t child = 0; child < child_info.size(); child++) {
243 if (child_info[child].pid != i->pid)
244 continue;
245 i->titles = child_info[child].titles;
[email protected]f3b357692013-03-22 05:16:13246 i->process_type = child_info[child].process_type;
[email protected]e1873b92011-05-13 19:22:22247 break;
248 }
249 }
250
[email protected]54fd1d32009-09-01 00:12:58251 process_data_.push_back(current_browser);
252
253 // For each browser process, collect a list of its children and get the
254 // memory usage of each.
[email protected]e9720e822012-06-07 00:07:48255 for (std::set<pid_t>::const_iterator iter = browsers_found.begin();
256 iter != browsers_found.end();
257 ++iter) {
258 std::vector<pid_t> browser_processes = GetAllChildren(process_map, *iter);
259 ProcessData browser = GetProcessDataMemoryInformation(browser_processes);
[email protected]54fd1d32009-09-01 00:12:58260
[email protected]e9720e822012-06-07 00:07:48261 ProcessMap::const_iterator process_iter = process_map.find(*iter);
262 if (process_iter == process_map.end())
263 continue;
264 BrowserType type = GetBrowserType(process_iter->second.name);
265 if (type != MAX_BROWSERS)
266 browser.name = ASCIIToUTF16(kBrowserPrettyNames[type]);
[email protected]54fd1d32009-09-01 00:12:58267 process_data_.push_back(browser);
268 }
269
[email protected]48e303442013-07-18 19:13:15270#if defined(OS_CHROMEOS)
271 GetSwapData(&swap_data_);
272#endif
273
[email protected]54fd1d32009-09-01 00:12:58274 // Finally return to the browser thread.
[email protected]f8b3ef82010-10-11 02:45:52275 BrowserThread::PostTask(
276 BrowserThread::UI, FROM_HERE,
[email protected]24d69692011-10-21 18:26:51277 base::Bind(&MemoryDetails::CollectChildInfoOnUIThread, this));
[email protected]54fd1d32009-09-01 00:12:58278}