blob: ea5d35fb2e6f9e16f4ca87c0de5ce4c367791a5b [file] [log] [blame]
[email protected]8a34e6602010-10-02 17:29:431// Copyright (c) 2010 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
5#include "chrome/browser/memory_details.h"
initial.commit09911bf2008-07-26 23:55:296
initial.commit09911bf2008-07-26 23:55:297#include "base/file_version_info.h"
[email protected]835d7c82010-10-14 04:38:388#include "base/metrics/histogram.h"
[email protected]76543b92009-08-31 17:27:459#include "base/process_util.h"
[email protected]33b9df42010-07-29 06:46:3910#include "base/string_util.h"
[email protected]64048bd2010-03-08 23:28:5811#include "base/utf_string_conversions.h"
[email protected]79dc42cd2011-01-08 21:43:3512#include "chrome/browser/extensions/extension_service.h"
13#include "chrome/browser/profiles/profile.h"
[email protected]fcf79352010-12-28 20:13:2014#include "chrome/common/bindings_policy.h"
15#include "chrome/common/extensions/extension.h"
[email protected]cd3d7892009-03-04 23:55:0616#include "chrome/common/url_constants.h"
[email protected]a01efd22011-03-01 00:38:3217#include "content/browser/browser_child_process_host.h"
18#include "content/browser/browser_thread.h"
19#include "content/browser/renderer_host/backing_store_manager.h"
20#include "content/browser/renderer_host/render_process_host.h"
21#include "content/browser/renderer_host/render_view_host.h"
22#include "content/browser/tab_contents/navigation_entry.h"
23#include "content/browser/tab_contents/tab_contents.h"
[email protected]ae5ca892009-07-30 18:00:4724#include "grit/chromium_strings.h"
[email protected]4f260d02010-12-23 18:35:4225#include "grit/generated_resources.h"
[email protected]c051a1b2011-01-21 23:30:1726#include "ui/base/l10n/l10n_util.h"
initial.commit09911bf2008-07-26 23:55:2927
[email protected]54fd1d32009-09-01 00:12:5828#if defined(OS_LINUX)
[email protected]a01efd22011-03-01 00:38:3229#include "content/browser/zygote_host_linux.h"
30#include "content/browser/renderer_host/render_sandbox_host_linux.h"
[email protected]54fd1d32009-09-01 00:12:5831#endif
initial.commit09911bf2008-07-26 23:55:2932
[email protected]8e383412010-10-19 16:57:0333ProcessMemoryInformation::ProcessMemoryInformation()
34 : pid(0),
35 num_processes(0),
36 is_diagnostics(false),
[email protected]fcf79352010-12-28 20:13:2037 type(ChildProcessInfo::UNKNOWN_PROCESS),
38 renderer_type(ChildProcessInfo::RENDERER_UNKNOWN) {
[email protected]8e383412010-10-19 16:57:0339}
40
41ProcessMemoryInformation::~ProcessMemoryInformation() {}
42
[email protected]93aa89c72010-10-20 21:32:0443ProcessData::ProcessData() {}
44
45ProcessData::ProcessData(const ProcessData& rhs)
46 : name(rhs.name),
47 process_name(rhs.process_name),
48 processes(rhs.processes) {
49}
50
51ProcessData::~ProcessData() {}
52
53ProcessData& ProcessData::operator=(const ProcessData& rhs) {
54 name = rhs.name;
55 process_name = rhs.process_name;
56 processes = rhs.processes;
57 return *this;
58}
59
initial.commit09911bf2008-07-26 23:55:2960// About threading:
61//
62// This operation will hit no fewer than 3 threads.
63//
[email protected]a436d922009-02-13 23:16:4264// The ChildProcessInfo::Iterator can only be accessed from the IO thread.
initial.commit09911bf2008-07-26 23:55:2965//
66// The RenderProcessHostIterator can only be accessed from the UI thread.
67//
68// This operation can take 30-100ms to complete. We never want to have
69// one task run for that long on the UI or IO threads. So, we run the
70// expensive parts of this operation over on the file thread.
71//
initial.commit09911bf2008-07-26 23:55:2972void MemoryDetails::StartFetch() {
[email protected]f8b3ef82010-10-11 02:45:5273 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
74 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE));
initial.commit09911bf2008-07-26 23:55:2975
76 // In order to process this request, we need to use the plugin information.
77 // However, plugin process information is only available from the IO thread.
[email protected]f8b3ef82010-10-11 02:45:5278 BrowserThread::PostTask(
79 BrowserThread::IO, FROM_HERE,
[email protected]a27a9382009-02-11 23:55:1080 NewRunnableMethod(this, &MemoryDetails::CollectChildInfoOnIOThread));
initial.commit09911bf2008-07-26 23:55:2981}
82
[email protected]8e383412010-10-19 16:57:0383MemoryDetails::~MemoryDetails() {}
84
[email protected]a27a9382009-02-11 23:55:1085void MemoryDetails::CollectChildInfoOnIOThread() {
[email protected]f8b3ef82010-10-11 02:45:5286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:2987
[email protected]a27a9382009-02-11 23:55:1088 std::vector<ProcessMemoryInformation> child_info;
89
[email protected]a436d922009-02-13 23:16:4290 // Collect the list of child processes.
[email protected]d27893f62010-07-03 05:47:4291 for (BrowserChildProcessHost::Iterator iter; !iter.Done(); ++iter) {
[email protected]a27a9382009-02-11 23:55:1092 ProcessMemoryInformation info;
[email protected]76543b92009-08-31 17:27:4593 info.pid = base::GetProcId(iter->handle());
[email protected]a27a9382009-02-11 23:55:1094 if (!info.pid)
95 continue;
96
[email protected]a436d922009-02-13 23:16:4297 info.type = iter->type();
[email protected]fcf79352010-12-28 20:13:2098 info.renderer_type = iter->renderer_type();
[email protected]4f260d02010-12-23 18:35:4299 info.titles.push_back(WideToUTF16Hack(iter->name()));
[email protected]a27a9382009-02-11 23:55:10100 child_info.push_back(info);
initial.commit09911bf2008-07-26 23:55:29101 }
102
103 // Now go do expensive memory lookups from the file thread.
[email protected]f8b3ef82010-10-11 02:45:52104 BrowserThread::PostTask(
105 BrowserThread::FILE, FROM_HERE,
[email protected]a27a9382009-02-11 23:55:10106 NewRunnableMethod(this, &MemoryDetails::CollectProcessData, child_info));
initial.commit09911bf2008-07-26 23:55:29107}
108
[email protected]a27a9382009-02-11 23:55:10109void MemoryDetails::CollectChildInfoOnUIThread() {
[email protected]f8b3ef82010-10-11 02:45:52110 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
initial.commit09911bf2008-07-26 23:55:29111
[email protected]54fd1d32009-09-01 00:12:58112#if defined(OS_LINUX)
[email protected]d3c6c0d72010-12-09 08:15:04113 const pid_t zygote_pid = ZygoteHost::GetInstance()->pid();
114 const pid_t sandbox_helper_pid = RenderSandboxHostLinux::GetInstance()->pid();
[email protected]54fd1d32009-09-01 00:12:58115#endif
116
117 ProcessData* const chrome_browser = ChromeBrowser();
[email protected]a27a9382009-02-11 23:55:10118 // Get more information about the process.
[email protected]54fd1d32009-09-01 00:12:58119 for (size_t index = 0; index < chrome_browser->processes.size();
initial.commit09911bf2008-07-26 23:55:29120 index++) {
[email protected]a27a9382009-02-11 23:55:10121 // Check if it's a renderer, if so get the list of page titles in it and
[email protected]fcf79352010-12-28 20:13:20122 // check if it's a diagnostics-related process. We skip about:memory pages.
123 // Iterate the RenderProcessHosts to find the tab contents.
[email protected]54fd1d32009-09-01 00:12:58124 ProcessMemoryInformation& process =
125 chrome_browser->processes[index];
126
[email protected]019191a2009-10-02 20:37:27127 for (RenderProcessHost::iterator renderer_iter(
128 RenderProcessHost::AllHostsIterator()); !renderer_iter.IsAtEnd();
129 renderer_iter.Advance()) {
[email protected]fcf79352010-12-28 20:13:20130 RenderProcessHost* render_process_host = renderer_iter.GetCurrentValue();
131 DCHECK(render_process_host);
[email protected]8a34e6602010-10-02 17:29:43132 // Ignore processes that don't have a connection, such as crashed tabs.
[email protected]fcf79352010-12-28 20:13:20133 if (!render_process_host->HasConnection() ||
134 process.pid != base::GetProcId(render_process_host->GetHandle())) {
[email protected]a27a9382009-02-11 23:55:10135 continue;
[email protected]201b2732009-11-13 18:57:46136 }
[email protected]a27a9382009-02-11 23:55:10137 process.type = ChildProcessInfo::RENDER_PROCESS;
[email protected]79dc42cd2011-01-08 21:43:35138 Profile* profile = render_process_host->profile();
139 ExtensionService* extension_service = profile->GetExtensionService();
140
[email protected]a27a9382009-02-11 23:55:10141 // The RenderProcessHost may host multiple TabContents. Any
142 // of them which contain diagnostics information make the whole
143 // process be considered a diagnostics process.
144 //
145 // NOTE: This is a bit dangerous. We know that for now, listeners
146 // are always RenderWidgetHosts. But in theory, they don't
147 // have to be.
[email protected]9de09f82009-08-17 20:13:53148 RenderProcessHost::listeners_iterator iter(
[email protected]fcf79352010-12-28 20:13:20149 render_process_host->ListenersIterator());
[email protected]9de09f82009-08-17 20:13:53150 for (; !iter.IsAtEnd(); iter.Advance()) {
151 const RenderWidgetHost* widget =
152 static_cast<const RenderWidgetHost*>(iter.GetCurrentValue());
[email protected]a27a9382009-02-11 23:55:10153 DCHECK(widget);
154 if (!widget || !widget->IsRenderView())
155 continue;
initial.commit09911bf2008-07-26 23:55:29156
[email protected]9de09f82009-08-17 20:13:53157 const RenderViewHost* host = static_cast<const RenderViewHost*>(widget);
[email protected]fcf79352010-12-28 20:13:20158 RenderViewHostDelegate* host_delegate = host->delegate();
159 GURL url = host_delegate->GetURL();
160 ViewType::Type type = host_delegate->GetRenderViewType();
[email protected]c09163a2011-02-15 00:05:55161 if (host->enabled_bindings() & BindingsPolicy::WEB_UI) {
[email protected]fcf79352010-12-28 20:13:20162 // TODO(erikkay) the type for devtools doesn't actually appear to
163 // be set.
164 if (type == ViewType::DEV_TOOLS_UI)
165 process.renderer_type = ChildProcessInfo::RENDERER_DEVTOOLS;
166 else
167 process.renderer_type = ChildProcessInfo::RENDERER_CHROME;
168 } else if (host->enabled_bindings() & BindingsPolicy::EXTENSION) {
169 process.renderer_type = ChildProcessInfo::RENDERER_EXTENSION;
170 }
[email protected]6d53eb22009-02-13 20:48:39171 TabContents* contents = NULL;
[email protected]fcf79352010-12-28 20:13:20172 if (host_delegate)
173 contents = host_delegate->GetAsTabContents();
174 if (!contents) {
175 if (host->is_extension_process()) {
[email protected]79dc42cd2011-01-08 21:43:35176 const Extension* extension =
177 extension_service->GetExtensionByURL(url);
178 if (extension) {
179 string16 title = UTF8ToUTF16(extension->name());
180 process.titles.push_back(title);
181 }
[email protected]fcf79352010-12-28 20:13:20182 } else if (process.renderer_type ==
183 ChildProcessInfo::RENDERER_UNKNOWN) {
184 process.titles.push_back(UTF8ToUTF16(url.spec()));
185 switch (type) {
186 case ViewType::BACKGROUND_CONTENTS:
187 process.renderer_type =
188 ChildProcessInfo::RENDERER_BACKGROUND_APP;
189 break;
190 case ViewType::INTERSTITIAL_PAGE:
191 process.renderer_type = ChildProcessInfo::RENDERER_INTERSTITIAL;
192 break;
193 case ViewType::NOTIFICATION:
194 process.renderer_type = ChildProcessInfo::RENDERER_NOTIFICATION;
195 break;
196 default:
197 process.renderer_type = ChildProcessInfo::RENDERER_UNKNOWN;
198 break;
199 }
200 }
[email protected]a27a9382009-02-11 23:55:10201 continue;
[email protected]fcf79352010-12-28 20:13:20202 }
203
204 // Since We have a TabContents and and the renderer type hasn't been
205 // set yet, it must be a normal tabbed renderer.
206 if (process.renderer_type == ChildProcessInfo::RENDERER_UNKNOWN)
207 process.renderer_type = ChildProcessInfo::RENDERER_NORMAL;
208
[email protected]4f260d02010-12-23 18:35:42209 string16 title = contents->GetTitle();
[email protected]a27a9382009-02-11 23:55:10210 if (!title.length())
[email protected]4f260d02010-12-23 18:35:42211 title = l10n_util::GetStringUTF16(IDS_DEFAULT_TAB_TITLE);
[email protected]a27a9382009-02-11 23:55:10212 process.titles.push_back(title);
[email protected]cd3d7892009-03-04 23:55:06213
[email protected]ebe89e062009-08-13 23:16:54214 // We need to check the pending entry as well as the virtual_url to
[email protected]cd3d7892009-03-04 23:55:06215 // see if it's an about:memory URL (we don't want to count these in the
216 // total memory usage of the browser).
217 //
218 // When we reach here, about:memory will be the pending entry since we
219 // haven't responded with any data such that it would be committed. If
220 // you have another about:memory tab open (which would be committed),
221 // we don't want to count it either, so we also check the last committed
222 // entry.
223 //
224 // Either the pending or last committed entries can be NULL.
[email protected]6df40742009-03-19 22:24:50225 const NavigationEntry* pending_entry =
[email protected]ce3fa3c2009-04-20 19:55:57226 contents->controller().pending_entry();
[email protected]cd3d7892009-03-04 23:55:06227 const NavigationEntry* last_committed_entry =
[email protected]ce3fa3c2009-04-20 19:55:57228 contents->controller().GetLastCommittedEntry();
[email protected]cd3d7892009-03-04 23:55:06229 if ((last_committed_entry &&
[email protected]ebe89e062009-08-13 23:16:54230 LowerCaseEqualsASCII(last_committed_entry->virtual_url().spec(),
[email protected]cd3d7892009-03-04 23:55:06231 chrome::kAboutMemoryURL)) ||
232 (pending_entry &&
[email protected]ebe89e062009-08-13 23:16:54233 LowerCaseEqualsASCII(pending_entry->virtual_url().spec(),
[email protected]cd3d7892009-03-04 23:55:06234 chrome::kAboutMemoryURL)))
[email protected]a27a9382009-02-11 23:55:10235 process.is_diagnostics = true;
initial.commit09911bf2008-07-26 23:55:29236 }
237 }
[email protected]54fd1d32009-09-01 00:12:58238
239#if defined(OS_LINUX)
240 if (process.pid == zygote_pid) {
241 process.type = ChildProcessInfo::ZYGOTE_PROCESS;
242 } else if (process.pid == sandbox_helper_pid) {
243 process.type = ChildProcessInfo::SANDBOX_HELPER_PROCESS;
244 }
245#endif
initial.commit09911bf2008-07-26 23:55:29246 }
247
[email protected]a27a9382009-02-11 23:55:10248 // Get rid of other Chrome processes that are from a different profile.
[email protected]54fd1d32009-09-01 00:12:58249 for (size_t index = 0; index < chrome_browser->processes.size();
[email protected]a27a9382009-02-11 23:55:10250 index++) {
[email protected]54fd1d32009-09-01 00:12:58251 if (chrome_browser->processes[index].type ==
[email protected]a27a9382009-02-11 23:55:10252 ChildProcessInfo::UNKNOWN_PROCESS) {
[email protected]54fd1d32009-09-01 00:12:58253 chrome_browser->processes.erase(
254 chrome_browser->processes.begin() + index);
[email protected]a436d922009-02-13 23:16:42255 index--;
[email protected]a27a9382009-02-11 23:55:10256 }
257 }
258
initial.commit09911bf2008-07-26 23:55:29259 UpdateHistograms();
260
261 OnDetailsAvailable();
262}
263
264void MemoryDetails::UpdateHistograms() {
265 // Reports a set of memory metrics to UMA.
[email protected]57c4b852009-08-17 21:59:29266 // Memory is measured in KB.
initial.commit09911bf2008-07-26 23:55:29267
[email protected]54fd1d32009-09-01 00:12:58268 const ProcessData& browser = *ChromeBrowser();
initial.commit09911bf2008-07-26 23:55:29269 size_t aggregate_memory = 0;
[email protected]fcf79352010-12-28 20:13:20270 int chrome_count = 0;
271 int extension_count = 0;
[email protected]a27a9382009-02-11 23:55:10272 int plugin_count = 0;
[email protected]fcf79352010-12-28 20:13:20273 int renderer_count = 0;
274 int other_count = 0;
[email protected]a27a9382009-02-11 23:55:10275 int worker_count = 0;
initial.commit09911bf2008-07-26 23:55:29276 for (size_t index = 0; index < browser.processes.size(); index++) {
[email protected]921cd0cc2008-10-21 22:30:55277 int sample = static_cast<int>(browser.processes[index].working_set.priv);
278 aggregate_memory += sample;
[email protected]a27a9382009-02-11 23:55:10279 switch (browser.processes[index].type) {
[email protected]f164cea2009-11-05 23:37:40280 case ChildProcessInfo::BROWSER_PROCESS:
281 UMA_HISTOGRAM_MEMORY_KB("Memory.Browser", sample);
282 break;
[email protected]fcf79352010-12-28 20:13:20283 case ChildProcessInfo::RENDER_PROCESS: {
284 ChildProcessInfo::RendererProcessType renderer_type =
285 browser.processes[index].renderer_type;
286 switch (renderer_type) {
287 case ChildProcessInfo::RENDERER_EXTENSION:
288 UMA_HISTOGRAM_MEMORY_KB("Memory.Extension", sample);
289 extension_count++;
290 break;
291 case ChildProcessInfo::RENDERER_CHROME:
292 UMA_HISTOGRAM_MEMORY_KB("Memory.Chrome", sample);
293 chrome_count++;
294 break;
295 case ChildProcessInfo::RENDERER_UNKNOWN:
296 NOTREACHED() << "Unknown renderer process type.";
297 break;
298 case ChildProcessInfo::RENDERER_NORMAL:
299 default:
300 // TODO(erikkay): Should we bother splitting out the other subtypes?
301 UMA_HISTOGRAM_MEMORY_KB("Memory.Renderer", sample);
302 renderer_count++;
303 break;
304 }
[email protected]f164cea2009-11-05 23:37:40305 break;
[email protected]fcf79352010-12-28 20:13:20306 }
[email protected]f164cea2009-11-05 23:37:40307 case ChildProcessInfo::PLUGIN_PROCESS:
308 UMA_HISTOGRAM_MEMORY_KB("Memory.Plugin", sample);
309 plugin_count++;
310 break;
311 case ChildProcessInfo::WORKER_PROCESS:
312 UMA_HISTOGRAM_MEMORY_KB("Memory.Worker", sample);
313 worker_count++;
314 break;
315 case ChildProcessInfo::UTILITY_PROCESS:
316 UMA_HISTOGRAM_MEMORY_KB("Memory.Utility", sample);
[email protected]fcf79352010-12-28 20:13:20317 other_count++;
[email protected]f164cea2009-11-05 23:37:40318 break;
319 case ChildProcessInfo::ZYGOTE_PROCESS:
320 UMA_HISTOGRAM_MEMORY_KB("Memory.Zygote", sample);
[email protected]fcf79352010-12-28 20:13:20321 other_count++;
[email protected]f164cea2009-11-05 23:37:40322 break;
323 case ChildProcessInfo::SANDBOX_HELPER_PROCESS:
324 UMA_HISTOGRAM_MEMORY_KB("Memory.SandboxHelper", sample);
[email protected]fcf79352010-12-28 20:13:20325 other_count++;
[email protected]f164cea2009-11-05 23:37:40326 break;
[email protected]103607e2010-02-01 18:57:09327 case ChildProcessInfo::NACL_LOADER_PROCESS:
[email protected]f164cea2009-11-05 23:37:40328 UMA_HISTOGRAM_MEMORY_KB("Memory.NativeClient", sample);
[email protected]fcf79352010-12-28 20:13:20329 other_count++;
[email protected]f164cea2009-11-05 23:37:40330 break;
[email protected]aef8d5ae2010-03-17 22:40:52331 case ChildProcessInfo::NACL_BROKER_PROCESS:
332 UMA_HISTOGRAM_MEMORY_KB("Memory.NativeClientBroker", sample);
[email protected]fcf79352010-12-28 20:13:20333 other_count++;
[email protected]aef8d5ae2010-03-17 22:40:52334 break;
[email protected]96fcbf2d2010-08-03 16:10:27335 case ChildProcessInfo::GPU_PROCESS:
336 UMA_HISTOGRAM_MEMORY_KB("Memory.Gpu", sample);
[email protected]fcf79352010-12-28 20:13:20337 other_count++;
[email protected]96fcbf2d2010-08-03 16:10:27338 break;
[email protected]f164cea2009-11-05 23:37:40339 default:
340 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:29341 }
342 }
[email protected]57c4b852009-08-17 21:59:29343 UMA_HISTOGRAM_MEMORY_KB("Memory.BackingStore",
344 BackingStoreManager::MemorySize() / 1024);
[email protected]a27a9382009-02-11 23:55:10345
[email protected]553dba62009-02-24 19:08:23346 UMA_HISTOGRAM_COUNTS_100("Memory.ProcessCount",
initial.commit09911bf2008-07-26 23:55:29347 static_cast<int>(browser.processes.size()));
[email protected]fcf79352010-12-28 20:13:20348 UMA_HISTOGRAM_COUNTS_100("Memory.ChromeProcessCount", chrome_count);
349 UMA_HISTOGRAM_COUNTS_100("Memory.ExtensionProcessCount", extension_count);
350 UMA_HISTOGRAM_COUNTS_100("Memory.OtherProcessCount", other_count);
[email protected]553dba62009-02-24 19:08:23351 UMA_HISTOGRAM_COUNTS_100("Memory.PluginProcessCount", plugin_count);
[email protected]fcf79352010-12-28 20:13:20352 UMA_HISTOGRAM_COUNTS_100("Memory.RendererProcessCount", renderer_count);
[email protected]553dba62009-02-24 19:08:23353 UMA_HISTOGRAM_COUNTS_100("Memory.WorkerProcessCount", worker_count);
[email protected]f164cea2009-11-05 23:37:40354 // TODO(viettrungluu): Do we want separate counts for the other
355 // (platform-specific) process types?
[email protected]921cd0cc2008-10-21 22:30:55356
357 int total_sample = static_cast<int>(aggregate_memory / 1000);
[email protected]553dba62009-02-24 19:08:23358 UMA_HISTOGRAM_MEMORY_MB("Memory.Total", total_sample);
initial.commit09911bf2008-07-26 23:55:29359}