[M85 Dev][EFL] Fix errors to generate ninja files
[platform/framework/web/chromium-efl.git] / chrome / browser / memory_details.cc
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.
4
5 #include "chrome/browser/memory_details.h"
6
7 #include <algorithm>
8 #include <set>
9
10 #include "base/bind.h"
11 #include "base/file_version_info.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/task/thread_pool.h"
18 #include "build/build_config.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "components/nacl/common/nacl_process_type.h"
21 #include "components/strings/grit/components_strings.h"
22 #include "content/public/browser/browser_child_process_host_iterator.h"
23 #include "content/public/browser/browser_task_traits.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "content/public/browser/child_process_data.h"
26 #include "content/public/browser/navigation_controller.h"
27 #include "content/public/browser/navigation_entry.h"
28 #include "content/public/browser/render_frame_host.h"
29 #include "content/public/browser/render_process_host.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "content/public/browser/render_widget_host.h"
32 #include "content/public/browser/render_widget_host_iterator.h"
33 #include "content/public/browser/web_contents.h"
34 #include "content/public/common/bindings_policy.h"
35 #include "content/public/common/content_constants.h"
36 #include "extensions/buildflags/buildflags.h"
37 #include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
38 #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
39 #include "ui/base/l10n/l10n_util.h"
40
41 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
42 #include "content/public/browser/zygote_host/zygote_host_linux.h"
43 #endif
44
45 #if BUILDFLAG(ENABLE_EXTENSIONS)
46 #include "extensions/browser/extension_registry.h"
47 #include "extensions/browser/process_manager.h"
48 #include "extensions/browser/process_map.h"
49 #include "extensions/browser/view_type_utils.h"
50 #include "extensions/common/extension.h"
51 #endif
52
53 using base::StringPrintf;
54 using content::BrowserChildProcessHostIterator;
55 using content::BrowserThread;
56 using content::NavigationEntry;
57 using content::RenderViewHost;
58 using content::RenderWidgetHost;
59 using content::WebContents;
60 #if BUILDFLAG(ENABLE_EXTENSIONS)
61 using extensions::Extension;
62 #endif
63
64 // static
65 std::string ProcessMemoryInformation::GetRendererTypeNameInEnglish(
66     RendererProcessType type) {
67   switch (type) {
68     case RENDERER_NORMAL:
69       return "Tab";
70     case RENDERER_CHROME:
71       return "Tab (Chrome)";
72     case RENDERER_EXTENSION:
73       return "Extension";
74     case RENDERER_DEVTOOLS:
75       return "Devtools";
76     case RENDERER_INTERSTITIAL:
77       return "Interstitial";
78     case RENDERER_BACKGROUND_APP:
79       return "Background App";
80     case RENDERER_UNKNOWN:
81     default:
82       NOTREACHED() << "Unknown renderer process type!";
83       return "Unknown";
84   }
85 }
86
87 // static
88 std::string ProcessMemoryInformation::GetFullTypeNameInEnglish(
89     int process_type,
90     RendererProcessType rtype) {
91   if (process_type == content::PROCESS_TYPE_RENDERER)
92     return GetRendererTypeNameInEnglish(rtype);
93   return content::GetProcessTypeNameInEnglish(process_type);
94 }
95
96 ProcessMemoryInformation::ProcessMemoryInformation()
97     : pid(0),
98       num_processes(0),
99       process_type(content::PROCESS_TYPE_UNKNOWN),
100       num_open_fds(-1),
101       open_fds_soft_limit(-1),
102       renderer_type(RENDERER_UNKNOWN),
103       private_memory_footprint_kb(0) {}
104
105 ProcessMemoryInformation::ProcessMemoryInformation(
106     const ProcessMemoryInformation& other) = default;
107
108 ProcessMemoryInformation::~ProcessMemoryInformation() {}
109
110 bool ProcessMemoryInformation::operator<(
111     const ProcessMemoryInformation& rhs) const {
112   return private_memory_footprint_kb < rhs.private_memory_footprint_kb;
113 }
114
115 ProcessData::ProcessData() {}
116
117 ProcessData::ProcessData(const ProcessData& rhs)
118     : name(rhs.name),
119       process_name(rhs.process_name),
120       processes(rhs.processes) {
121 }
122
123 ProcessData::~ProcessData() {}
124
125 ProcessData& ProcessData::operator=(const ProcessData& rhs) {
126   name = rhs.name;
127   process_name = rhs.process_name;
128   processes = rhs.processes;
129   return *this;
130 }
131
132 // About threading:
133 //
134 // This operation will hit no fewer than 3 threads.
135 //
136 // The BrowserChildProcessHostIterator can only be accessed from the IO thread.
137 //
138 // The RenderProcessHostIterator can only be accessed from the UI thread.
139 //
140 // This operation can take 30-100ms to complete.  We never want to have
141 // one task run for that long on the UI or IO threads.  So, we run the
142 // expensive parts of this operation over on the blocking pool.
143 //
144 void MemoryDetails::StartFetch() {
145   // This might get called from the UI or FILE threads, but should not be
146   // getting called from the IO thread.
147   DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO));
148
149   // In order to process this request, we need to use the plugin information.
150   // However, plugin process information is only available from the IO thread.
151   content::GetIOThreadTaskRunner({})->PostTask(
152       FROM_HERE,
153       base::BindOnce(&MemoryDetails::CollectChildInfoOnIOThread, this));
154 }
155
156 MemoryDetails::~MemoryDetails() {}
157
158 std::string MemoryDetails::ToLogString(bool include_tab_title) {
159   std::string log;
160   log.reserve(4096);
161   ProcessMemoryInformationList processes = ChromeBrowser()->processes;
162   // Sort by memory consumption, low to high.
163   std::sort(processes.begin(), processes.end());
164   // Print from high to low.
165   for (auto iter1 = processes.rbegin(); iter1 != processes.rend(); ++iter1) {
166     log += ProcessMemoryInformation::GetFullTypeNameInEnglish(
167             iter1->process_type, iter1->renderer_type);
168     // The title of a renderer may contain PII.
169     if ((iter1->process_type != content::PROCESS_TYPE_RENDERER ||
170          include_tab_title) &&
171         !iter1->titles.empty()) {
172       log += " [";
173       for (std::vector<base::string16>::const_iterator iter2 =
174                iter1->titles.begin();
175            iter2 != iter1->titles.end(); ++iter2) {
176         if (iter2 != iter1->titles.begin())
177           log += "|";
178         log += base::UTF16ToUTF8(*iter2);
179       }
180       log += "]";
181     }
182     log += StringPrintf(
183         " %d MB", static_cast<int>(iter1->private_memory_footprint_kb) / 1024);
184     if (iter1->num_open_fds != -1 || iter1->open_fds_soft_limit != -1) {
185       log += StringPrintf(", %d FDs open of %d", iter1->num_open_fds,
186                           iter1->open_fds_soft_limit);
187     }
188     log += "\n";
189   }
190   return log;
191 }
192
193 void MemoryDetails::CollectChildInfoOnIOThread() {
194   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
195
196   std::vector<ProcessMemoryInformation> child_info;
197
198   // Collect the list of child processes. A 0 |handle| means that
199   // the process is being launched, so we skip it.
200   for (BrowserChildProcessHostIterator iter; !iter.Done(); ++iter) {
201     ProcessMemoryInformation info;
202     if (!iter.GetData().GetProcess().IsValid())
203       continue;
204     info.pid = iter.GetData().GetProcess().Pid();
205     if (!info.pid)
206       continue;
207
208     info.process_type = iter.GetData().process_type;
209     info.renderer_type = ProcessMemoryInformation::RENDERER_UNKNOWN;
210     info.titles.push_back(iter.GetData().name);
211     child_info.push_back(info);
212   }
213
214   // Now go do expensive memory lookups in a thread pool.
215   base::ThreadPool::PostTask(
216       FROM_HERE,
217       {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
218        base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
219       base::BindOnce(&MemoryDetails::CollectProcessData, this, child_info));
220 }
221
222 void MemoryDetails::CollectChildInfoOnUIThread() {
223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224   ProcessData* const chrome_browser = ChromeBrowser();
225
226   // First pass, collate the widgets by process ID.
227   std::map<base::ProcessId, std::vector<RenderWidgetHost*>> widgets_by_pid;
228   std::unique_ptr<content::RenderWidgetHostIterator> widget_it(
229       RenderWidgetHost::GetRenderWidgetHosts());
230   while (content::RenderWidgetHost* widget = widget_it->GetNextHost()) {
231     // Ignore processes that don't have a connection, such as crashed tabs,
232     // or processes that are still launching.
233     if (!widget->GetProcess()->IsReady())
234       continue;
235     base::ProcessId pid = widget->GetProcess()->GetProcess().Pid();
236     widgets_by_pid[pid].push_back(widget);
237   }
238
239   // Get more information about the process.
240   for (ProcessMemoryInformation& process : chrome_browser->processes) {
241     // If there's at least one widget in the process, it is some kind of
242     // renderer process belonging to this browser. All these widgets will share
243     // a RenderProcessHost.
244     content::RenderProcessHost* render_process_host = nullptr;
245     if (!widgets_by_pid[process.pid].empty()) {
246       // Mark it as a normal renderer process, if we don't refine it to some
247       // other |renderer_type| later.
248       process.process_type = content::PROCESS_TYPE_RENDERER;
249       process.renderer_type = ProcessMemoryInformation::RENDERER_NORMAL;
250       render_process_host = widgets_by_pid[process.pid].front()->GetProcess();
251     }
252
253 #if BUILDFLAG(ENABLE_EXTENSIONS)
254     // Determine if this is an extension process.
255     bool process_is_for_extensions = false;
256     if (render_process_host) {
257       content::BrowserContext* context =
258           render_process_host->GetBrowserContext();
259       extensions::ExtensionRegistry* extension_registry =
260           extensions::ExtensionRegistry::Get(context);
261       extensions::ProcessMap* process_map =
262           extensions::ProcessMap::Get(context);
263       int rph_id = render_process_host->GetID();
264       process_is_for_extensions = process_map->Contains(rph_id);
265
266       // For our purposes, don't count processes containing only hosted apps
267       // as extension processes. See also: crbug.com/102533.
268       for (auto& extension_id : process_map->GetExtensionsInProcess(rph_id)) {
269         const Extension* extension =
270             extension_registry->enabled_extensions().GetByID(extension_id);
271         if (extension && !extension->is_hosted_app()) {
272           process.renderer_type = ProcessMemoryInformation::RENDERER_EXTENSION;
273           break;
274         }
275       }
276     }
277 #endif
278
279     // Use the list of widgets to iterate over the WebContents instances whose
280     // main RenderFrameHosts are in |process|. Refine our determination of the
281     // |process.renderer_type|, and record the page titles.
282     for (content::RenderWidgetHost* widget : widgets_by_pid[process.pid]) {
283       DCHECK_EQ(render_process_host, widget->GetProcess());
284
285       RenderViewHost* rvh = RenderViewHost::From(widget);
286       if (!rvh)
287         continue;
288
289       WebContents* contents = WebContents::FromRenderViewHost(rvh);
290
291       // Assume that an RVH without a web contents is an interstitial.
292       if (!contents) {
293         process.renderer_type = ProcessMemoryInformation::RENDERER_INTERSTITIAL;
294         continue;
295       }
296
297       // If this is a RVH for a subframe; skip it to avoid double-counting the
298       // WebContents.
299       if (rvh != contents->GetRenderViewHost())
300         continue;
301
302       // The rest of this block will happen only once per WebContents.
303       GURL page_url = contents->GetLastCommittedURL();
304       bool is_webui = rvh->GetMainFrame()->GetEnabledBindings() &
305                       content::BINDINGS_POLICY_WEB_UI;
306
307       if (is_webui) {
308         process.renderer_type = ProcessMemoryInformation::RENDERER_CHROME;
309       }
310
311 #if BUILDFLAG(ENABLE_EXTENSIONS)
312       if (!is_webui && process_is_for_extensions) {
313         const Extension* extension =
314             extensions::ExtensionRegistry::Get(
315                 render_process_host->GetBrowserContext())
316                 ->enabled_extensions()
317                 .GetByID(page_url.host());
318         if (extension) {
319           base::string16 title = base::UTF8ToUTF16(extension->name());
320           process.titles.push_back(title);
321           process.renderer_type =
322               ProcessMemoryInformation::RENDERER_EXTENSION;
323           continue;
324         }
325       }
326
327       extensions::ViewType type = extensions::GetViewType(contents);
328       if (type == extensions::VIEW_TYPE_BACKGROUND_CONTENTS) {
329         process.titles.push_back(base::UTF8ToUTF16(page_url.spec()));
330         process.renderer_type =
331             ProcessMemoryInformation::RENDERER_BACKGROUND_APP;
332         continue;
333       }
334 #endif
335
336       base::string16 title = contents->GetTitle();
337       if (!title.length())
338         title = l10n_util::GetStringUTF16(IDS_DEFAULT_TAB_TITLE);
339       process.titles.push_back(title);
340     }
341
342 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
343     if (content::ZygoteHost::GetInstance()->IsZygotePid(process.pid)) {
344       process.process_type = content::PROCESS_TYPE_ZYGOTE;
345     }
346 #endif
347   }
348
349   // Get rid of other Chrome processes that are from a different profile.
350   auto is_unknown = [](ProcessMemoryInformation& process) {
351     return process.process_type == content::PROCESS_TYPE_UNKNOWN;
352   };
353   auto& vector = chrome_browser->processes;
354   base::EraseIf(vector, is_unknown);
355
356   // Grab a memory dump for all processes.
357   // Using AdaptCallbackForRepeating allows for an easier transition to
358   // OnceCallbacks for https://crbug.com/714018.
359   memory_instrumentation::MemoryInstrumentation::GetInstance()
360       ->RequestPrivateMemoryFootprint(
361           base::kNullProcessId,
362           base::BindOnce(&MemoryDetails::DidReceiveMemoryDump, this));
363 }
364
365 void MemoryDetails::DidReceiveMemoryDump(
366     bool success,
367     std::unique_ptr<memory_instrumentation::GlobalMemoryDump> global_dump) {
368   ProcessData* const chrome_browser = ChromeBrowser();
369   if (success) {
370     for (const memory_instrumentation::GlobalMemoryDump::ProcessDump& dump :
371          global_dump->process_dumps()) {
372       base::ProcessId dump_pid = dump.pid();
373       for (ProcessMemoryInformation& pmi : chrome_browser->processes) {
374         if (pmi.pid == dump_pid) {
375           pmi.private_memory_footprint_kb = dump.os_dump().private_footprint_kb;
376           break;
377         }
378       }
379     }
380   }
381
382   OnDetailsAvailable();
383 }