memory_details.cc 13 KB

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