blob: 26c484e1606b66ed8aa2f7acd28cb2b4668a1468 [file] [log] [blame]
[email protected]b0b67cf2012-01-18 21:59:571// Copyright (c) 2012 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
asvitkine89406d1f2015-01-17 06:57:107#include <algorithm>
8#include <set>
9
[email protected]24d69692011-10-21 18:26:5110#include "base/bind.h"
initial.commit09911bf2008-07-26 23:55:2911#include "base/file_version_info.h"
[email protected]835d7c82010-10-14 04:38:3812#include "base/metrics/histogram.h"
[email protected]f9b294362013-06-10 20:22:3113#include "base/strings/string_util.h"
14#include "base/strings/stringprintf.h"
[email protected]112158af2013-06-07 23:46:1815#include "base/strings/utf_string_conversions.h"
avi6846aef2015-12-26 01:09:3816#include "build/build_config.h"
[email protected]79dc42cd2011-01-08 21:43:3517#include "chrome/browser/profiles/profile.h"
[email protected]cd3d7892009-03-04 23:55:0618#include "chrome/common/url_constants.h"
[email protected]af39f002014-08-22 10:18:1819#include "chrome/grit/generated_resources.h"
[email protected]d5d383252013-07-04 14:44:3220#include "components/nacl/common/nacl_process_type.h"
sdefresne957f4a72016-02-18 12:44:2921#include "components/strings/grit/components_strings.h"
[email protected]4967f792012-01-20 22:14:4022#include "content/public/browser/browser_child_process_host_iterator.h"
[email protected]c38831a12011-10-28 12:44:4923#include "content/public/browser/browser_thread.h"
[email protected]9c1662b2012-03-06 15:44:3324#include "content/public/browser/child_process_data.h"
[email protected]a53209b2012-01-20 16:48:1625#include "content/public/browser/navigation_controller.h"
[email protected]022af742011-12-28 18:37:2526#include "content/public/browser/navigation_entry.h"
[email protected]f3b1a082011-11-18 00:34:3027#include "content/public/browser/render_process_host.h"
[email protected]9c1662b2012-03-06 15:44:3328#include "content/public/browser/render_view_host.h"
avif9ab5d942015-10-15 14:05:4429#include "content/public/browser/render_widget_host.h"
[email protected]a801cb32013-09-11 18:02:2730#include "content/public/browser/render_widget_host_iterator.h"
[email protected]83ff91c2012-01-05 20:54:1331#include "content/public/browser/web_contents.h"
[email protected]e091df82011-10-11 18:13:2132#include "content/public/common/bindings_policy.h"
thestig5ef7dc82014-11-06 05:36:2633#include "content/public/common/content_constants.h"
[email protected]c051a1b2011-01-21 23:30:1734#include "ui/base/l10n/l10n_util.h"
initial.commit09911bf2008-07-26 23:55:2935
[email protected]a423c9e2012-03-06 18:02:3136#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
[email protected]c2c68b1f2012-02-25 00:29:1537#include "content/public/browser/zygote_host_linux.h"
[email protected]54fd1d32009-09-01 00:12:5838#endif
initial.commit09911bf2008-07-26 23:55:2939
[email protected]ddef62e2014-07-12 06:18:5740#if defined(ENABLE_EXTENSIONS)
reillyga3acbc12014-11-11 23:17:1241#include "extensions/browser/extension_registry.h"
[email protected]ddef62e2014-07-12 06:18:5742#include "extensions/browser/process_manager.h"
43#include "extensions/browser/process_map.h"
44#include "extensions/browser/view_type_utils.h"
45#include "extensions/common/extension.h"
46#endif
47
[email protected]4306df72012-04-20 18:58:5748using base::StringPrintf;
[email protected]4967f792012-01-20 22:14:4049using content::BrowserChildProcessHostIterator;
[email protected]631bb742011-11-02 11:29:3950using content::BrowserThread;
[email protected]10f417c52011-12-28 21:04:2351using content::NavigationEntry;
[email protected]eaabba22012-03-07 15:02:1152using content::RenderViewHost;
53using content::RenderWidgetHost;
[email protected]83ff91c2012-01-05 20:54:1354using content::WebContents;
[email protected]ddef62e2014-07-12 06:18:5755#if defined(ENABLE_EXTENSIONS)
[email protected]1c321ee2012-05-21 03:02:3456using extensions::Extension;
[email protected]ddef62e2014-07-12 06:18:5757#endif
[email protected]631bb742011-11-02 11:29:3958
[email protected]2c1978a2011-11-29 17:02:3959// static
60std::string ProcessMemoryInformation::GetRendererTypeNameInEnglish(
61 RendererProcessType type) {
62 switch (type) {
63 case RENDERER_NORMAL:
64 return "Tab";
65 case RENDERER_CHROME:
66 return "Tab (Chrome)";
67 case RENDERER_EXTENSION:
68 return "Extension";
69 case RENDERER_DEVTOOLS:
70 return "Devtools";
71 case RENDERER_INTERSTITIAL:
72 return "Interstitial";
[email protected]2c1978a2011-11-29 17:02:3973 case RENDERER_BACKGROUND_APP:
74 return "Background App";
75 case RENDERER_UNKNOWN:
76 default:
77 NOTREACHED() << "Unknown renderer process type!";
78 return "Unknown";
79 }
80}
81
82// static
83std::string ProcessMemoryInformation::GetFullTypeNameInEnglish(
[email protected]f3b357692013-03-22 05:16:1384 int process_type,
[email protected]2c1978a2011-11-29 17:02:3985 RendererProcessType rtype) {
[email protected]f3b357692013-03-22 05:16:1386 if (process_type == content::PROCESS_TYPE_RENDERER)
[email protected]2c1978a2011-11-29 17:02:3987 return GetRendererTypeNameInEnglish(rtype);
[email protected]f3b357692013-03-22 05:16:1388 return content::GetProcessTypeNameInEnglish(process_type);
[email protected]2c1978a2011-11-29 17:02:3989}
90
[email protected]8e383412010-10-19 16:57:0391ProcessMemoryInformation::ProcessMemoryInformation()
92 : pid(0),
93 num_processes(0),
94 is_diagnostics(false),
[email protected]f3b357692013-03-22 05:16:1395 process_type(content::PROCESS_TYPE_UNKNOWN),
[email protected]2c1978a2011-11-29 17:02:3996 renderer_type(RENDERER_UNKNOWN) {
[email protected]8e383412010-10-19 16:57:0397}
98
vmpstrb8aacbe2016-02-26 02:00:4899ProcessMemoryInformation::ProcessMemoryInformation(
100 const ProcessMemoryInformation& other) = default;
101
[email protected]8e383412010-10-19 16:57:03102ProcessMemoryInformation::~ProcessMemoryInformation() {}
103
[email protected]8e23c882012-05-05 01:14:11104bool ProcessMemoryInformation::operator<(
105 const ProcessMemoryInformation& rhs) const {
106 return working_set.priv < rhs.working_set.priv;
107}
108
[email protected]93aa89c72010-10-20 21:32:04109ProcessData::ProcessData() {}
110
111ProcessData::ProcessData(const ProcessData& rhs)
112 : name(rhs.name),
113 process_name(rhs.process_name),
114 processes(rhs.processes) {
115}
116
117ProcessData::~ProcessData() {}
118
119ProcessData& ProcessData::operator=(const ProcessData& rhs) {
120 name = rhs.name;
121 process_name = rhs.process_name;
122 processes = rhs.processes;
123 return *this;
124}
125
initial.commit09911bf2008-07-26 23:55:29126// About threading:
127//
128// This operation will hit no fewer than 3 threads.
129//
[email protected]8be45842012-04-13 19:49:29130// The BrowserChildProcessHostIterator can only be accessed from the IO thread.
initial.commit09911bf2008-07-26 23:55:29131//
132// The RenderProcessHostIterator can only be accessed from the UI thread.
133//
134// This operation can take 30-100ms to complete. We never want to have
135// one task run for that long on the UI or IO threads. So, we run the
hashimotoa8ea28d2015-04-11 02:50:48136// expensive parts of this operation over on the blocking pool.
initial.commit09911bf2008-07-26 23:55:29137//
asvitkine6f5f3592015-01-21 20:50:37138void MemoryDetails::StartFetch(CollectionMode mode) {
[email protected]9bb480ee2011-08-03 21:41:16139 // This might get called from the UI or FILE threads, but should not be
140 // getting called from the IO thread.
[email protected]f8b3ef82010-10-11 02:45:52141 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29142
143 // In order to process this request, we need to use the plugin information.
144 // However, plugin process information is only available from the IO thread.
[email protected]f8b3ef82010-10-11 02:45:52145 BrowserThread::PostTask(
146 BrowserThread::IO, FROM_HERE,
asvitkine6f5f3592015-01-21 20:50:37147 base::Bind(&MemoryDetails::CollectChildInfoOnIOThread, this, mode));
initial.commit09911bf2008-07-26 23:55:29148}
149
[email protected]8e383412010-10-19 16:57:03150MemoryDetails::~MemoryDetails() {}
151
[email protected]4306df72012-04-20 18:58:57152std::string MemoryDetails::ToLogString() {
153 std::string log;
154 log.reserve(4096);
[email protected]8e23c882012-05-05 01:14:11155 ProcessMemoryInformationList processes = ChromeBrowser()->processes;
156 // Sort by memory consumption, low to high.
157 std::sort(processes.begin(), processes.end());
158 // Print from high to low.
159 for (ProcessMemoryInformationList::reverse_iterator iter1 =
160 processes.rbegin();
161 iter1 != processes.rend();
162 ++iter1) {
[email protected]4306df72012-04-20 18:58:57163 log += ProcessMemoryInformation::GetFullTypeNameInEnglish(
[email protected]f3b357692013-03-22 05:16:13164 iter1->process_type, iter1->renderer_type);
[email protected]4306df72012-04-20 18:58:57165 if (!iter1->titles.empty()) {
166 log += " [";
[email protected]d2065e062013-12-12 23:49:52167 for (std::vector<base::string16>::const_iterator iter2 =
[email protected]4306df72012-04-20 18:58:57168 iter1->titles.begin();
169 iter2 != iter1->titles.end(); ++iter2) {
170 if (iter2 != iter1->titles.begin())
171 log += "|";
[email protected]6778fed2013-12-24 20:09:37172 log += base::UTF16ToUTF8(*iter2);
[email protected]4306df72012-04-20 18:58:57173 }
174 log += "]";
175 }
[email protected]aa1255b2013-07-31 22:03:09176 log += StringPrintf(" %d MB private, %d MB shared",
[email protected]4306df72012-04-20 18:58:57177 static_cast<int>(iter1->working_set.priv) / 1024,
178 static_cast<int>(iter1->working_set.shared) / 1024);
[email protected]aa1255b2013-07-31 22:03:09179#if defined(OS_CHROMEOS)
180 log += StringPrintf(", %d MB swapped",
181 static_cast<int>(iter1->working_set.swapped) / 1024);
182#endif
183 log += "\n";
[email protected]4306df72012-04-20 18:58:57184 }
185 return log;
186}
187
asvitkine6f5f3592015-01-21 20:50:37188void MemoryDetails::CollectChildInfoOnIOThread(CollectionMode mode) {
[email protected]f8b3ef82010-10-11 02:45:52189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29190
[email protected]a27a9382009-02-11 23:55:10191 std::vector<ProcessMemoryInformation> child_info;
192
[email protected]82a14c12012-11-13 18:40:55193 // Collect the list of child processes. A 0 |handle| means that
194 // the process is being launched, so we skip it.
[email protected]4967f792012-01-20 22:14:40195 for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
[email protected]a27a9382009-02-11 23:55:10196 ProcessMemoryInformation info;
[email protected]82a14c12012-11-13 18:40:55197 if (!iter.GetData().handle)
198 continue;
[email protected]4967f792012-01-20 22:14:40199 info.pid = base::GetProcId(iter.GetData().handle);
[email protected]a27a9382009-02-11 23:55:10200 if (!info.pid)
201 continue;
202
[email protected]f3b357692013-03-22 05:16:13203 info.process_type = iter.GetData().process_type;
[email protected]2c1978a2011-11-29 17:02:39204 info.renderer_type = ProcessMemoryInformation::RENDERER_UNKNOWN;
[email protected]4967f792012-01-20 22:14:40205 info.titles.push_back(iter.GetData().name);
[email protected]a27a9382009-02-11 23:55:10206 child_info.push_back(info);
initial.commit09911bf2008-07-26 23:55:29207 }
208
hashimotoa8ea28d2015-04-11 02:50:48209 // Now go do expensive memory lookups on the blocking pool.
210 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
211 FROM_HERE,
212 base::Bind(&MemoryDetails::CollectProcessData, this, mode, child_info),
213 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
initial.commit09911bf2008-07-26 23:55:29214}
215
[email protected]a27a9382009-02-11 23:55:10216void MemoryDetails::CollectChildInfoOnUIThread() {
[email protected]f8b3ef82010-10-11 02:45:52217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]54fd1d32009-09-01 00:12:58218 ProcessData* const chrome_browser = ChromeBrowser();
nickb2545f72015-10-30 20:05:15219
220 // First pass, collate the widgets by process ID.
221 std::map<base::ProcessId, std::vector<RenderWidgetHost*>> widgets_by_pid;
222 scoped_ptr<content::RenderWidgetHostIterator> widget_it(
223 RenderWidgetHost::GetRenderWidgetHosts());
224 while (content::RenderWidgetHost* widget = widget_it->GetNextHost()) {
225 // Ignore processes that don't have a connection, such as crashed tabs.
226 if (!widget->GetProcess()->HasConnection())
227 continue;
228 base::ProcessId pid = base::GetProcId(widget->GetProcess()->GetHandle());
229 widgets_by_pid[pid].push_back(widget);
230 }
231
[email protected]a27a9382009-02-11 23:55:10232 // Get more information about the process.
nickb2545f72015-10-30 20:05:15233 for (ProcessMemoryInformation& process : chrome_browser->processes) {
234 // If there's at least one widget in the process, it is some kind of
235 // renderer process belonging to this browser. All these widgets will share
236 // a RenderProcessHost.
237 content::RenderProcessHost* render_process_host = nullptr;
238 if (!widgets_by_pid[process.pid].empty()) {
239 // Mark it as a normal renderer process, if we don't refine it to some
240 // other |renderer_type| later.
[email protected]ddef62e2014-07-12 06:18:57241 process.process_type = content::PROCESS_TYPE_RENDERER;
nickb2545f72015-10-30 20:05:15242 process.renderer_type = ProcessMemoryInformation::RENDERER_NORMAL;
243 render_process_host = widgets_by_pid[process.pid].front()->GetProcess();
244 }
245
[email protected]ddef62e2014-07-12 06:18:57246#if defined(ENABLE_EXTENSIONS)
nickb2545f72015-10-30 20:05:15247 // Determine if this is an extension process.
248 bool process_is_for_extensions = false;
249 if (render_process_host) {
[email protected]ddef62e2014-07-12 06:18:57250 content::BrowserContext* context =
251 render_process_host->GetBrowserContext();
reillyga3acbc12014-11-11 23:17:12252 extensions::ExtensionRegistry* extension_registry =
253 extensions::ExtensionRegistry::Get(context);
nickb2545f72015-10-30 20:05:15254 extensions::ProcessMap* process_map =
[email protected]ddef62e2014-07-12 06:18:57255 extensions::ProcessMap::Get(context);
nickb2545f72015-10-30 20:05:15256 int rph_id = render_process_host->GetID();
257 process_is_for_extensions = process_map->Contains(rph_id);
258
259 // For our purposes, don't count processes containing only hosted apps
260 // as extension processes. See also: crbug.com/102533.
261 for (auto& extension_id : process_map->GetExtensionsInProcess(rph_id)) {
262 const Extension* extension =
263 extension_registry->enabled_extensions().GetByID(extension_id);
264 if (extension && !extension->is_hosted_app()) {
265 process.renderer_type = ProcessMemoryInformation::RENDERER_EXTENSION;
266 break;
267 }
268 }
269 }
[email protected]ddef62e2014-07-12 06:18:57270#endif
271
nickb2545f72015-10-30 20:05:15272 // Use the list of widgets to iterate over the WebContents instances whose
273 // main RenderFrameHosts are in |process|. Refine our determination of the
274 // |process.renderer_type|, and record the page titles.
275 for (content::RenderWidgetHost* widget : widgets_by_pid[process.pid]) {
276 DCHECK_EQ(render_process_host, widget->GetProcess());
277
278 RenderViewHost* rvh = RenderViewHost::From(widget);
279 if (!rvh)
280 continue;
281
282 WebContents* contents = WebContents::FromRenderViewHost(rvh);
283
284 // Assume that an RVH without a web contents is an interstitial.
285 if (!contents) {
286 process.renderer_type = ProcessMemoryInformation::RENDERER_INTERSTITIAL;
287 continue;
[email protected]039b84a42013-06-21 20:23:37288 }
nickb2545f72015-10-30 20:05:15289
290 // If this is a RVH for a subframe; skip it to avoid double-counting the
291 // WebContents.
292 if (rvh != contents->GetRenderViewHost())
293 continue;
294
295 // The rest of this block will happen only once per WebContents.
296 GURL page_url = contents->GetLastCommittedURL();
297 SiteData& site_data =
298 chrome_browser->site_data[contents->GetBrowserContext()];
299 SiteDetails::CollectSiteInfo(contents, &site_data);
300
301 bool is_webui =
302 rvh->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI;
303
304 if (is_webui) {
[email protected]039b84a42013-06-21 20:23:37305 process.renderer_type = ProcessMemoryInformation::RENDERER_CHROME;
[email protected]039b84a42013-06-21 20:23:37306 }
nickb2545f72015-10-30 20:05:15307
[email protected]ddef62e2014-07-12 06:18:57308#if defined(ENABLE_EXTENSIONS)
nickb2545f72015-10-30 20:05:15309 if (!is_webui && process_is_for_extensions) {
[email protected]039b84a42013-06-21 20:23:37310 const Extension* extension =
nickb2545f72015-10-30 20:05:15311 extensions::ExtensionRegistry::Get(
312 render_process_host->GetBrowserContext())
313 ->enabled_extensions()
314 .GetByID(page_url.host());
[email protected]039b84a42013-06-21 20:23:37315 if (extension) {
[email protected]6778fed2013-12-24 20:09:37316 base::string16 title = base::UTF8ToUTF16(extension->name());
[email protected]039b84a42013-06-21 20:23:37317 process.titles.push_back(title);
[email protected]299d7f12012-05-23 05:31:15318 process.renderer_type =
[email protected]039b84a42013-06-21 20:23:37319 ProcessMemoryInformation::RENDERER_EXTENSION;
[email protected]299d7f12012-05-23 05:31:15320 continue;
321 }
[email protected]039b84a42013-06-21 20:23:37322 }
[email protected]299d7f12012-05-23 05:31:15323
nickb2545f72015-10-30 20:05:15324 extensions::ViewType type = extensions::GetViewType(contents);
[email protected]039b84a42013-06-21 20:23:37325 if (type == extensions::VIEW_TYPE_BACKGROUND_CONTENTS) {
nickb2545f72015-10-30 20:05:15326 process.titles.push_back(base::UTF8ToUTF16(page_url.spec()));
[email protected]039b84a42013-06-21 20:23:37327 process.renderer_type =
328 ProcessMemoryInformation::RENDERER_BACKGROUND_APP;
329 continue;
330 }
[email protected]ddef62e2014-07-12 06:18:57331#endif
[email protected]299d7f12012-05-23 05:31:15332
[email protected]0085863a2013-12-06 21:19:03333 base::string16 title = contents->GetTitle();
[email protected]039b84a42013-06-21 20:23:37334 if (!title.length())
335 title = l10n_util::GetStringUTF16(IDS_DEFAULT_TAB_TITLE);
336 process.titles.push_back(title);
337
nickb2545f72015-10-30 20:05:15338 // The presence of a single WebContents with a diagnostics page will make
339 // the whole process be considered a diagnostics process.
340 //
[email protected]039b84a42013-06-21 20:23:37341 // We need to check the pending entry as well as the virtual_url to
342 // see if it's a chrome://memory URL (we don't want to count these in
343 // the total memory usage of the browser).
344 //
345 // When we reach here, chrome://memory will be the pending entry since
346 // we haven't responded with any data such that it would be committed.
347 // If you have another chrome://memory tab open (which would be
348 // committed), we don't want to count it either, so we also check the
349 // last committed entry.
350 //
351 // Either the pending or last committed entries can be NULL.
352 const NavigationEntry* pending_entry =
353 contents->GetController().GetPendingEntry();
354 const NavigationEntry* last_committed_entry =
355 contents->GetController().GetLastCommittedEntry();
356 if ((last_committed_entry &&
brettwbc17d2c82015-06-09 22:39:08357 base::LowerCaseEqualsASCII(
358 last_committed_entry->GetVirtualURL().spec(),
359 chrome::kChromeUIMemoryURL)) ||
[email protected]039b84a42013-06-21 20:23:37360 (pending_entry &&
brettwbc17d2c82015-06-09 22:39:08361 base::LowerCaseEqualsASCII(
362 pending_entry->GetVirtualURL().spec(),
363 chrome::kChromeUIMemoryURL))) {
[email protected]039b84a42013-06-21 20:23:37364 process.is_diagnostics = true;
initial.commit09911bf2008-07-26 23:55:29365 }
366 }
[email protected]54fd1d32009-09-01 00:12:58367
[email protected]a423c9e2012-03-06 18:02:31368#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
kerrnelafd49a83b2016-01-22 21:16:15369 if (content::ZygoteHost::GetInstance()->IsZygotePid(process.pid)) {
[email protected]f3b357692013-03-22 05:16:13370 process.process_type = content::PROCESS_TYPE_ZYGOTE;
[email protected]54fd1d32009-09-01 00:12:58371 }
372#endif
initial.commit09911bf2008-07-26 23:55:29373 }
374
[email protected]a27a9382009-02-11 23:55:10375 // Get rid of other Chrome processes that are from a different profile.
nickb2545f72015-10-30 20:05:15376 auto is_unknown = [](ProcessMemoryInformation& process) {
377 return process.process_type == content::PROCESS_TYPE_UNKNOWN;
378 };
379 auto& vector = chrome_browser->processes;
380 vector.erase(std::remove_if(vector.begin(), vector.end(), is_unknown),
381 vector.end());
[email protected]a27a9382009-02-11 23:55:10382
initial.commit09911bf2008-07-26 23:55:29383 OnDetailsAvailable();
384}