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