Allow the minimum time between banner trigger visits to be varied via field trials.
[chromium-blink-merge.git] / chrome / browser / memory_details.cc
blobc6a7e7c2c0f6520c256e25294d62b563a099d183
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.
5 #include "chrome/browser/memory_details.h"
7 #include <algorithm>
8 #include <set>
10 #include "base/bind.h"
11 #include "base/file_version_info.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/common/url_constants.h"
18 #include "chrome/grit/generated_resources.h"
19 #include "components/nacl/common/nacl_process_type.h"
20 #include "content/public/browser/browser_child_process_host_iterator.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/child_process_data.h"
23 #include "content/public/browser/navigation_controller.h"
24 #include "content/public/browser/navigation_entry.h"
25 #include "content/public/browser/render_process_host.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/browser/render_widget_host_iterator.h"
28 #include "content/public/browser/web_contents.h"
29 #include "content/public/common/bindings_policy.h"
30 #include "content/public/common/content_constants.h"
31 #include "ui/base/l10n/l10n_util.h"
33 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
34 #include "content/public/browser/zygote_host_linux.h"
35 #endif
37 #if defined(ENABLE_EXTENSIONS)
38 #include "extensions/browser/extension_registry.h"
39 #include "extensions/browser/process_manager.h"
40 #include "extensions/browser/process_map.h"
41 #include "extensions/browser/view_type_utils.h"
42 #include "extensions/common/extension.h"
43 #endif
45 using base::StringPrintf;
46 using content::BrowserChildProcessHostIterator;
47 using content::BrowserThread;
48 using content::NavigationEntry;
49 using content::RenderViewHost;
50 using content::RenderWidgetHost;
51 using content::WebContents;
52 #if defined(ENABLE_EXTENSIONS)
53 using extensions::Extension;
54 #endif
56 // static
57 std::string ProcessMemoryInformation::GetRendererTypeNameInEnglish(
58 RendererProcessType type) {
59 switch (type) {
60 case RENDERER_NORMAL:
61 return "Tab";
62 case RENDERER_CHROME:
63 return "Tab (Chrome)";
64 case RENDERER_EXTENSION:
65 return "Extension";
66 case RENDERER_DEVTOOLS:
67 return "Devtools";
68 case RENDERER_INTERSTITIAL:
69 return "Interstitial";
70 case RENDERER_BACKGROUND_APP:
71 return "Background App";
72 case RENDERER_UNKNOWN:
73 default:
74 NOTREACHED() << "Unknown renderer process type!";
75 return "Unknown";
79 // static
80 std::string ProcessMemoryInformation::GetFullTypeNameInEnglish(
81 int process_type,
82 RendererProcessType rtype) {
83 if (process_type == content::PROCESS_TYPE_RENDERER)
84 return GetRendererTypeNameInEnglish(rtype);
85 return content::GetProcessTypeNameInEnglish(process_type);
88 ProcessMemoryInformation::ProcessMemoryInformation()
89 : pid(0),
90 num_processes(0),
91 is_diagnostics(false),
92 process_type(content::PROCESS_TYPE_UNKNOWN),
93 renderer_type(RENDERER_UNKNOWN) {
96 ProcessMemoryInformation::~ProcessMemoryInformation() {}
98 bool ProcessMemoryInformation::operator<(
99 const ProcessMemoryInformation& rhs) const {
100 return working_set.priv < rhs.working_set.priv;
103 ProcessData::ProcessData() {}
105 ProcessData::ProcessData(const ProcessData& rhs)
106 : name(rhs.name),
107 process_name(rhs.process_name),
108 processes(rhs.processes) {
111 ProcessData::~ProcessData() {}
113 ProcessData& ProcessData::operator=(const ProcessData& rhs) {
114 name = rhs.name;
115 process_name = rhs.process_name;
116 processes = rhs.processes;
117 return *this;
120 // About threading:
122 // This operation will hit no fewer than 3 threads.
124 // The BrowserChildProcessHostIterator can only be accessed from the IO thread.
126 // The RenderProcessHostIterator can only be accessed from the UI thread.
128 // This operation can take 30-100ms to complete. We never want to have
129 // one task run for that long on the UI or IO threads. So, we run the
130 // expensive parts of this operation over on the blocking pool.
132 void MemoryDetails::StartFetch(CollectionMode mode) {
133 // This might get called from the UI or FILE threads, but should not be
134 // getting called from the IO thread.
135 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
137 // In order to process this request, we need to use the plugin information.
138 // However, plugin process information is only available from the IO thread.
139 BrowserThread::PostTask(
140 BrowserThread::IO, FROM_HERE,
141 base::Bind(&MemoryDetails::CollectChildInfoOnIOThread, this, mode));
144 MemoryDetails::~MemoryDetails() {}
146 std::string MemoryDetails::ToLogString() {
147 std::string log;
148 log.reserve(4096);
149 ProcessMemoryInformationList processes = ChromeBrowser()->processes;
150 // Sort by memory consumption, low to high.
151 std::sort(processes.begin(), processes.end());
152 // Print from high to low.
153 for (ProcessMemoryInformationList::reverse_iterator iter1 =
154 processes.rbegin();
155 iter1 != processes.rend();
156 ++iter1) {
157 log += ProcessMemoryInformation::GetFullTypeNameInEnglish(
158 iter1->process_type, iter1->renderer_type);
159 if (!iter1->titles.empty()) {
160 log += " [";
161 for (std::vector<base::string16>::const_iterator iter2 =
162 iter1->titles.begin();
163 iter2 != iter1->titles.end(); ++iter2) {
164 if (iter2 != iter1->titles.begin())
165 log += "|";
166 log += base::UTF16ToUTF8(*iter2);
168 log += "]";
170 log += StringPrintf(" %d MB private, %d MB shared",
171 static_cast<int>(iter1->working_set.priv) / 1024,
172 static_cast<int>(iter1->working_set.shared) / 1024);
173 #if defined(OS_CHROMEOS)
174 log += StringPrintf(", %d MB swapped",
175 static_cast<int>(iter1->working_set.swapped) / 1024);
176 #endif
177 log += "\n";
179 return log;
182 void MemoryDetails::CollectChildInfoOnIOThread(CollectionMode mode) {
183 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
185 std::vector<ProcessMemoryInformation> child_info;
187 // Collect the list of child processes. A 0 |handle| means that
188 // the process is being launched, so we skip it.
189 for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
190 ProcessMemoryInformation info;
191 if (!iter.GetData().handle)
192 continue;
193 info.pid = base::GetProcId(iter.GetData().handle);
194 if (!info.pid)
195 continue;
197 info.process_type = iter.GetData().process_type;
198 info.renderer_type = ProcessMemoryInformation::RENDERER_UNKNOWN;
199 info.titles.push_back(iter.GetData().name);
200 child_info.push_back(info);
203 // Now go do expensive memory lookups on the blocking pool.
204 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
205 FROM_HERE,
206 base::Bind(&MemoryDetails::CollectProcessData, this, mode, child_info),
207 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
210 void MemoryDetails::CollectChildInfoOnUIThread() {
211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
213 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
214 const pid_t zygote_pid = content::ZygoteHost::GetInstance()->GetPid();
215 #endif
217 ProcessData* const chrome_browser = ChromeBrowser();
218 // Get more information about the process.
219 for (size_t index = 0; index < chrome_browser->processes.size();
220 index++) {
221 // Check if it's a renderer, if so get the list of page titles in it and
222 // check if it's a diagnostics-related process. We skip about:memory pages.
223 // Iterate the RenderProcessHosts to find the tab contents.
224 ProcessMemoryInformation& process =
225 chrome_browser->processes[index];
227 scoped_ptr<content::RenderWidgetHostIterator> widgets(
228 RenderWidgetHost::GetRenderWidgetHosts());
229 while (content::RenderWidgetHost* widget = widgets->GetNextHost()) {
230 content::RenderProcessHost* render_process_host =
231 widget->GetProcess();
232 DCHECK(render_process_host);
233 // Ignore processes that don't have a connection, such as crashed tabs.
234 if (!render_process_host->HasConnection() ||
235 process.pid != base::GetProcId(render_process_host->GetHandle())) {
236 continue;
239 // The RenderProcessHost may host multiple WebContentses. Any
240 // of them which contain diagnostics information make the whole
241 // process be considered a diagnostics process.
242 if (!widget->IsRenderView())
243 continue;
245 process.process_type = content::PROCESS_TYPE_RENDERER;
246 bool is_extension = false;
247 RenderViewHost* host = RenderViewHost::From(widget);
248 #if defined(ENABLE_EXTENSIONS)
249 content::BrowserContext* context =
250 render_process_host->GetBrowserContext();
251 extensions::ExtensionRegistry* extension_registry =
252 extensions::ExtensionRegistry::Get(context);
253 extensions::ProcessMap* extension_process_map =
254 extensions::ProcessMap::Get(context);
255 is_extension = extension_process_map->Contains(
256 host->GetProcess()->GetID());
257 #endif
259 WebContents* contents = WebContents::FromRenderViewHost(host);
260 GURL url;
261 if (contents) {
262 url = contents->GetURL();
263 SiteData* site_data =
264 &chrome_browser->site_data[contents->GetBrowserContext()];
265 SiteDetails::CollectSiteInfo(contents, site_data);
267 #if defined(ENABLE_EXTENSIONS)
268 extensions::ViewType type = extensions::GetViewType(contents);
269 #endif
270 if (host->GetEnabledBindings() & content::BINDINGS_POLICY_WEB_UI) {
271 process.renderer_type = ProcessMemoryInformation::RENDERER_CHROME;
272 } else if (is_extension) {
273 #if defined(ENABLE_EXTENSIONS)
274 // For our purposes, don't count processes containing only hosted apps
275 // as extension processes. See also: crbug.com/102533.
276 std::set<std::string> extension_ids =
277 extension_process_map->GetExtensionsInProcess(
278 host->GetProcess()->GetID());
279 for (std::set<std::string>::iterator iter = extension_ids.begin();
280 iter != extension_ids.end(); ++iter) {
281 const Extension* extension =
282 extension_registry->enabled_extensions().GetByID(*iter);
283 if (extension && !extension->is_hosted_app()) {
284 process.renderer_type =
285 ProcessMemoryInformation::RENDERER_EXTENSION;
286 break;
289 #endif
291 #if defined(ENABLE_EXTENSIONS)
292 if (is_extension) {
293 const Extension* extension =
294 extension_registry->enabled_extensions().GetByID(url.host());
295 if (extension) {
296 base::string16 title = base::UTF8ToUTF16(extension->name());
297 process.titles.push_back(title);
298 process.renderer_type =
299 ProcessMemoryInformation::RENDERER_EXTENSION;
300 continue;
303 #endif
305 if (!contents) {
306 process.renderer_type =
307 ProcessMemoryInformation::RENDERER_INTERSTITIAL;
308 continue;
311 #if defined(ENABLE_EXTENSIONS)
312 if (type == extensions::VIEW_TYPE_BACKGROUND_CONTENTS) {
313 process.titles.push_back(base::UTF8ToUTF16(url.spec()));
314 process.renderer_type =
315 ProcessMemoryInformation::RENDERER_BACKGROUND_APP;
316 continue;
318 #endif
320 // Since we have a WebContents and and the renderer type hasn't been
321 // set yet, it must be a normal tabbed renderer.
322 if (process.renderer_type == ProcessMemoryInformation::RENDERER_UNKNOWN)
323 process.renderer_type = ProcessMemoryInformation::RENDERER_NORMAL;
325 base::string16 title = contents->GetTitle();
326 if (!title.length())
327 title = l10n_util::GetStringUTF16(IDS_DEFAULT_TAB_TITLE);
328 process.titles.push_back(title);
330 // We need to check the pending entry as well as the virtual_url to
331 // see if it's a chrome://memory URL (we don't want to count these in
332 // the total memory usage of the browser).
334 // When we reach here, chrome://memory will be the pending entry since
335 // we haven't responded with any data such that it would be committed.
336 // If you have another chrome://memory tab open (which would be
337 // committed), we don't want to count it either, so we also check the
338 // last committed entry.
340 // Either the pending or last committed entries can be NULL.
341 const NavigationEntry* pending_entry =
342 contents->GetController().GetPendingEntry();
343 const NavigationEntry* last_committed_entry =
344 contents->GetController().GetLastCommittedEntry();
345 if ((last_committed_entry &&
346 base::LowerCaseEqualsASCII(
347 last_committed_entry->GetVirtualURL().spec(),
348 chrome::kChromeUIMemoryURL)) ||
349 (pending_entry &&
350 base::LowerCaseEqualsASCII(
351 pending_entry->GetVirtualURL().spec(),
352 chrome::kChromeUIMemoryURL))) {
353 process.is_diagnostics = true;
357 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
358 if (process.pid == zygote_pid) {
359 process.process_type = content::PROCESS_TYPE_ZYGOTE;
361 #endif
364 // Get rid of other Chrome processes that are from a different profile.
365 for (size_t index = 0; index < chrome_browser->processes.size();
366 index++) {
367 if (chrome_browser->processes[index].process_type ==
368 content::PROCESS_TYPE_UNKNOWN) {
369 chrome_browser->processes.erase(
370 chrome_browser->processes.begin() + index);
371 index--;
375 OnDetailsAvailable();