blob: 0da07a251f998fc19f0037e94486bf82aa98501a [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"
asvitkineaa060312016-09-01 22:44:1312#include "base/metrics/histogram_macros.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]af39f002014-08-22 10:18:1818#include "chrome/grit/generated_resources.h"
[email protected]d5d383252013-07-04 14:44:3219#include "components/nacl/common/nacl_process_type.h"
sdefresne957f4a72016-02-18 12:44:2920#include "components/strings/grit/components_strings.h"
[email protected]4967f792012-01-20 22:14:4021#include "content/public/browser/browser_child_process_host_iterator.h"
[email protected]c38831a12011-10-28 12:44:4922#include "content/public/browser/browser_thread.h"
[email protected]9c1662b2012-03-06 15:44:3323#include "content/public/browser/child_process_data.h"
[email protected]a53209b2012-01-20 16:48:1624#include "content/public/browser/navigation_controller.h"
[email protected]022af742011-12-28 18:37:2525#include "content/public/browser/navigation_entry.h"
[email protected]f3b1a082011-11-18 00:34:3026#include "content/public/browser/render_process_host.h"
[email protected]9c1662b2012-03-06 15:44:3327#include "content/public/browser/render_view_host.h"
avif9ab5d942015-10-15 14:05:4428#include "content/public/browser/render_widget_host.h"
[email protected]a801cb32013-09-11 18:02:2729#include "content/public/browser/render_widget_host_iterator.h"
[email protected]83ff91c2012-01-05 20:54:1330#include "content/public/browser/web_contents.h"
[email protected]e091df82011-10-11 18:13:2131#include "content/public/common/bindings_policy.h"
thestig5ef7dc82014-11-06 05:36:2632#include "content/public/common/content_constants.h"
brettw00899e62016-11-12 02:10:1733#include "extensions/features/features.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
brettw00899e62016-11-12 02:10:1740#if BUILDFLAG(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;
brettw00899e62016-11-12 02:10:1755#if BUILDFLAG(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),
[email protected]f3b357692013-03-22 05:16:1394 process_type(content::PROCESS_TYPE_UNKNOWN),
[email protected]2c1978a2011-11-29 17:02:3995 renderer_type(RENDERER_UNKNOWN) {
[email protected]8e383412010-10-19 16:57:0396}
97
vmpstrb8aacbe2016-02-26 02:00:4898ProcessMemoryInformation::ProcessMemoryInformation(
99 const ProcessMemoryInformation& other) = default;
100
[email protected]8e383412010-10-19 16:57:03101ProcessMemoryInformation::~ProcessMemoryInformation() {}
102
[email protected]8e23c882012-05-05 01:14:11103bool ProcessMemoryInformation::operator<(
104 const ProcessMemoryInformation& rhs) const {
105 return working_set.priv < rhs.working_set.priv;
106}
107
[email protected]93aa89c72010-10-20 21:32:04108ProcessData::ProcessData() {}
109
110ProcessData::ProcessData(const ProcessData& rhs)
111 : name(rhs.name),
112 process_name(rhs.process_name),
113 processes(rhs.processes) {
114}
115
116ProcessData::~ProcessData() {}
117
118ProcessData& ProcessData::operator=(const ProcessData& rhs) {
119 name = rhs.name;
120 process_name = rhs.process_name;
121 processes = rhs.processes;
122 return *this;
123}
124
initial.commit09911bf2008-07-26 23:55:29125// About threading:
126//
127// This operation will hit no fewer than 3 threads.
128//
[email protected]8be45842012-04-13 19:49:29129// The BrowserChildProcessHostIterator can only be accessed from the IO thread.
initial.commit09911bf2008-07-26 23:55:29130//
131// The RenderProcessHostIterator can only be accessed from the UI thread.
132//
133// This operation can take 30-100ms to complete. We never want to have
134// one task run for that long on the UI or IO threads. So, we run the
hashimotoa8ea28d2015-04-11 02:50:48135// expensive parts of this operation over on the blocking pool.
initial.commit09911bf2008-07-26 23:55:29136//
ellyjonescd6e449d2016-04-13 19:31:15137void MemoryDetails::StartFetch() {
[email protected]9bb480ee2011-08-03 21:41:16138 // This might get called from the UI or FILE threads, but should not be
139 // getting called from the IO thread.
[email protected]f8b3ef82010-10-11 02:45:52140 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29141
142 // In order to process this request, we need to use the plugin information.
143 // However, plugin process information is only available from the IO thread.
[email protected]f8b3ef82010-10-11 02:45:52144 BrowserThread::PostTask(
145 BrowserThread::IO, FROM_HERE,
ellyjonescd6e449d2016-04-13 19:31:15146 base::Bind(&MemoryDetails::CollectChildInfoOnIOThread, this));
initial.commit09911bf2008-07-26 23:55:29147}
148
[email protected]8e383412010-10-19 16:57:03149MemoryDetails::~MemoryDetails() {}
150
[email protected]4306df72012-04-20 18:58:57151std::string MemoryDetails::ToLogString() {
152 std::string log;
153 log.reserve(4096);
[email protected]8e23c882012-05-05 01:14:11154 ProcessMemoryInformationList processes = ChromeBrowser()->processes;
155 // Sort by memory consumption, low to high.
156 std::sort(processes.begin(), processes.end());
157 // Print from high to low.
158 for (ProcessMemoryInformationList::reverse_iterator iter1 =
159 processes.rbegin();
160 iter1 != processes.rend();
161 ++iter1) {
[email protected]4306df72012-04-20 18:58:57162 log += ProcessMemoryInformation::GetFullTypeNameInEnglish(
[email protected]f3b357692013-03-22 05:16:13163 iter1->process_type, iter1->renderer_type);
[email protected]4306df72012-04-20 18:58:57164 if (!iter1->titles.empty()) {
165 log += " [";
[email protected]d2065e062013-12-12 23:49:52166 for (std::vector<base::string16>::const_iterator iter2 =
[email protected]4306df72012-04-20 18:58:57167 iter1->titles.begin();
168 iter2 != iter1->titles.end(); ++iter2) {
169 if (iter2 != iter1->titles.begin())
170 log += "|";
[email protected]6778fed2013-12-24 20:09:37171 log += base::UTF16ToUTF8(*iter2);
[email protected]4306df72012-04-20 18:58:57172 }
173 log += "]";
174 }
[email protected]aa1255b2013-07-31 22:03:09175 log += StringPrintf(" %d MB private, %d MB shared",
[email protected]4306df72012-04-20 18:58:57176 static_cast<int>(iter1->working_set.priv) / 1024,
177 static_cast<int>(iter1->working_set.shared) / 1024);
[email protected]aa1255b2013-07-31 22:03:09178#if defined(OS_CHROMEOS)
179 log += StringPrintf(", %d MB swapped",
180 static_cast<int>(iter1->working_set.swapped) / 1024);
181#endif
182 log += "\n";
[email protected]4306df72012-04-20 18:58:57183 }
184 return log;
185}
186
ellyjonescd6e449d2016-04-13 19:31:15187void MemoryDetails::CollectChildInfoOnIOThread() {
[email protected]f8b3ef82010-10-11 02:45:52188 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29189
[email protected]a27a9382009-02-11 23:55:10190 std::vector<ProcessMemoryInformation> child_info;
191
[email protected]82a14c12012-11-13 18:40:55192 // Collect the list of child processes. A 0 |handle| means that
193 // the process is being launched, so we skip it.
[email protected]4967f792012-01-20 22:14:40194 for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
[email protected]a27a9382009-02-11 23:55:10195 ProcessMemoryInformation info;
[email protected]82a14c12012-11-13 18:40:55196 if (!iter.GetData().handle)
197 continue;
[email protected]4967f792012-01-20 22:14:40198 info.pid = base::GetProcId(iter.GetData().handle);
[email protected]a27a9382009-02-11 23:55:10199 if (!info.pid)
200 continue;
201
[email protected]f3b357692013-03-22 05:16:13202 info.process_type = iter.GetData().process_type;
[email protected]2c1978a2011-11-29 17:02:39203 info.renderer_type = ProcessMemoryInformation::RENDERER_UNKNOWN;
[email protected]4967f792012-01-20 22:14:40204 info.titles.push_back(iter.GetData().name);
[email protected]a27a9382009-02-11 23:55:10205 child_info.push_back(info);
initial.commit09911bf2008-07-26 23:55:29206 }
207
hashimotoa8ea28d2015-04-11 02:50:48208 // Now go do expensive memory lookups on the blocking pool.
209 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
210 FROM_HERE,
ellyjonescd6e449d2016-04-13 19:31:15211 base::Bind(&MemoryDetails::CollectProcessData, this, child_info),
hashimotoa8ea28d2015-04-11 02:50:48212 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
initial.commit09911bf2008-07-26 23:55:29213}
214
[email protected]a27a9382009-02-11 23:55:10215void MemoryDetails::CollectChildInfoOnUIThread() {
[email protected]f8b3ef82010-10-11 02:45:52216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]54fd1d32009-09-01 00:12:58217 ProcessData* const chrome_browser = ChromeBrowser();
nickb2545f72015-10-30 20:05:15218
219 // First pass, collate the widgets by process ID.
220 std::map<base::ProcessId, std::vector<RenderWidgetHost*>> widgets_by_pid;
dcheng4af48582016-04-19 00:29:35221 std::unique_ptr<content::RenderWidgetHostIterator> widget_it(
nickb2545f72015-10-30 20:05:15222 RenderWidgetHost::GetRenderWidgetHosts());
223 while (content::RenderWidgetHost* widget = widget_it->GetNextHost()) {
224 // Ignore processes that don't have a connection, such as crashed tabs.
225 if (!widget->GetProcess()->HasConnection())
226 continue;
227 base::ProcessId pid = base::GetProcId(widget->GetProcess()->GetHandle());
228 widgets_by_pid[pid].push_back(widget);
229 }
230
[email protected]a27a9382009-02-11 23:55:10231 // Get more information about the process.
nickb2545f72015-10-30 20:05:15232 for (ProcessMemoryInformation& process : chrome_browser->processes) {
233 // If there's at least one widget in the process, it is some kind of
234 // renderer process belonging to this browser. All these widgets will share
235 // a RenderProcessHost.
236 content::RenderProcessHost* render_process_host = nullptr;
237 if (!widgets_by_pid[process.pid].empty()) {
238 // Mark it as a normal renderer process, if we don't refine it to some
239 // other |renderer_type| later.
[email protected]ddef62e2014-07-12 06:18:57240 process.process_type = content::PROCESS_TYPE_RENDERER;
nickb2545f72015-10-30 20:05:15241 process.renderer_type = ProcessMemoryInformation::RENDERER_NORMAL;
242 render_process_host = widgets_by_pid[process.pid].front()->GetProcess();
243 }
244
brettw00899e62016-11-12 02:10:17245#if BUILDFLAG(ENABLE_EXTENSIONS)
nickb2545f72015-10-30 20:05:15246 // Determine if this is an extension process.
247 bool process_is_for_extensions = false;
248 if (render_process_host) {
[email protected]ddef62e2014-07-12 06:18:57249 content::BrowserContext* context =
250 render_process_host->GetBrowserContext();
reillyga3acbc12014-11-11 23:17:12251 extensions::ExtensionRegistry* extension_registry =
252 extensions::ExtensionRegistry::Get(context);
nickb2545f72015-10-30 20:05:15253 extensions::ProcessMap* process_map =
[email protected]ddef62e2014-07-12 06:18:57254 extensions::ProcessMap::Get(context);
nickb2545f72015-10-30 20:05:15255 int rph_id = render_process_host->GetID();
256 process_is_for_extensions = process_map->Contains(rph_id);
257
258 // For our purposes, don't count processes containing only hosted apps
259 // as extension processes. See also: crbug.com/102533.
260 for (auto& extension_id : process_map->GetExtensionsInProcess(rph_id)) {
261 const Extension* extension =
262 extension_registry->enabled_extensions().GetByID(extension_id);
263 if (extension && !extension->is_hosted_app()) {
264 process.renderer_type = ProcessMemoryInformation::RENDERER_EXTENSION;
265 break;
266 }
267 }
268 }
[email protected]ddef62e2014-07-12 06:18:57269#endif
270
nickb2545f72015-10-30 20:05:15271 // Use the list of widgets to iterate over the WebContents instances whose
272 // main RenderFrameHosts are in |process|. Refine our determination of the
273 // |process.renderer_type|, and record the page titles.
274 for (content::RenderWidgetHost* widget : widgets_by_pid[process.pid]) {
275 DCHECK_EQ(render_process_host, widget->GetProcess());
276
277 RenderViewHost* rvh = RenderViewHost::From(widget);
278 if (!rvh)
279 continue;
280
281 WebContents* contents = WebContents::FromRenderViewHost(rvh);
282
283 // Assume that an RVH without a web contents is an interstitial.
284 if (!contents) {
285 process.renderer_type = ProcessMemoryInformation::RENDERER_INTERSTITIAL;
286 continue;
[email protected]039b84a42013-06-21 20:23:37287 }
nickb2545f72015-10-30 20:05:15288
289 // If this is a RVH for a subframe; skip it to avoid double-counting the
290 // WebContents.
291 if (rvh != contents->GetRenderViewHost())
292 continue;
293
294 // The rest of this block will happen only once per WebContents.
295 GURL page_url = contents->GetLastCommittedURL();
296 SiteData& site_data =
297 chrome_browser->site_data[contents->GetBrowserContext()];
298 SiteDetails::CollectSiteInfo(contents, &site_data);
299
300 bool is_webui =
301 rvh->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI;
302
303 if (is_webui) {
[email protected]039b84a42013-06-21 20:23:37304 process.renderer_type = ProcessMemoryInformation::RENDERER_CHROME;
[email protected]039b84a42013-06-21 20:23:37305 }
nickb2545f72015-10-30 20:05:15306
brettw00899e62016-11-12 02:10:17307#if BUILDFLAG(ENABLE_EXTENSIONS)
nickb2545f72015-10-30 20:05:15308 if (!is_webui && process_is_for_extensions) {
[email protected]039b84a42013-06-21 20:23:37309 const Extension* extension =
nickb2545f72015-10-30 20:05:15310 extensions::ExtensionRegistry::Get(
311 render_process_host->GetBrowserContext())
312 ->enabled_extensions()
313 .GetByID(page_url.host());
[email protected]039b84a42013-06-21 20:23:37314 if (extension) {
[email protected]6778fed2013-12-24 20:09:37315 base::string16 title = base::UTF8ToUTF16(extension->name());
[email protected]039b84a42013-06-21 20:23:37316 process.titles.push_back(title);
[email protected]299d7f12012-05-23 05:31:15317 process.renderer_type =
[email protected]039b84a42013-06-21 20:23:37318 ProcessMemoryInformation::RENDERER_EXTENSION;
[email protected]299d7f12012-05-23 05:31:15319 continue;
320 }
[email protected]039b84a42013-06-21 20:23:37321 }
[email protected]299d7f12012-05-23 05:31:15322
nickb2545f72015-10-30 20:05:15323 extensions::ViewType type = extensions::GetViewType(contents);
[email protected]039b84a42013-06-21 20:23:37324 if (type == extensions::VIEW_TYPE_BACKGROUND_CONTENTS) {
nickb2545f72015-10-30 20:05:15325 process.titles.push_back(base::UTF8ToUTF16(page_url.spec()));
[email protected]039b84a42013-06-21 20:23:37326 process.renderer_type =
327 ProcessMemoryInformation::RENDERER_BACKGROUND_APP;
328 continue;
329 }
[email protected]ddef62e2014-07-12 06:18:57330#endif
[email protected]299d7f12012-05-23 05:31:15331
[email protected]0085863a2013-12-06 21:19:03332 base::string16 title = contents->GetTitle();
[email protected]039b84a42013-06-21 20:23:37333 if (!title.length())
334 title = l10n_util::GetStringUTF16(IDS_DEFAULT_TAB_TITLE);
335 process.titles.push_back(title);
initial.commit09911bf2008-07-26 23:55:29336 }
[email protected]54fd1d32009-09-01 00:12:58337
[email protected]a423c9e2012-03-06 18:02:31338#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
kerrnelafd49a83b2016-01-22 21:16:15339 if (content::ZygoteHost::GetInstance()->IsZygotePid(process.pid)) {
[email protected]f3b357692013-03-22 05:16:13340 process.process_type = content::PROCESS_TYPE_ZYGOTE;
[email protected]54fd1d32009-09-01 00:12:58341 }
342#endif
initial.commit09911bf2008-07-26 23:55:29343 }
344
[email protected]a27a9382009-02-11 23:55:10345 // Get rid of other Chrome processes that are from a different profile.
nickb2545f72015-10-30 20:05:15346 auto is_unknown = [](ProcessMemoryInformation& process) {
347 return process.process_type == content::PROCESS_TYPE_UNKNOWN;
348 };
349 auto& vector = chrome_browser->processes;
350 vector.erase(std::remove_if(vector.begin(), vector.end(), is_unknown),
351 vector.end());
[email protected]a27a9382009-02-11 23:55:10352
initial.commit09911bf2008-07-26 23:55:29353 OnDetailsAvailable();
354}