| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/memory_details.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/file_version_info.h" |
| #include "base/files/file_path.h" |
| #include "base/mac/foundation_util.h" |
| #include "base/process/process_iterator.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/scoped_blocking_call.h" |
| #include "base/threading/thread.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/grit/chromium_strings.h" |
| #include "components/version_info/version_info.h" |
| #include "content/public/browser/browser_child_process_host.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/common/process_type.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| namespace { |
| |
| // A helper for |CollectProcessData()|, collecting data on the Chrome/Chromium |
| // process with PID |pid|. The collected data is added to |processes|. |
| void CollectProcessDataForChromeProcess( |
| const std::vector<ProcessMemoryInformation>& child_info, |
| base::ProcessId pid, |
| ProcessMemoryInformationList* processes) { |
| ProcessMemoryInformation info; |
| info.pid = pid; |
| if (info.pid == base::GetCurrentProcId()) |
| info.process_type = content::PROCESS_TYPE_BROWSER; |
| else |
| info.process_type = content::PROCESS_TYPE_UNKNOWN; |
| |
| info.product_name = base::ASCIIToUTF16(version_info::GetProductName()); |
| info.version = base::ASCIIToUTF16(version_info::GetVersionNumber()); |
| |
| // A PortProvider is not necessary to acquire information about the number |
| // of open file descriptors. |
| std::unique_ptr<base::ProcessMetrics> metrics( |
| base::ProcessMetrics::CreateProcessMetrics(pid, nullptr)); |
| info.num_open_fds = metrics->GetOpenFdCount(); |
| info.open_fds_soft_limit = metrics->GetOpenFdSoftLimit(); |
| |
| // Check if this is one of the child processes whose data was already |
| // collected and exists in |child_data|. |
| for (const ProcessMemoryInformation& child : child_info) { |
| if (child.pid == info.pid) { |
| info.titles = child.titles; |
| info.process_type = child.process_type; |
| break; |
| } |
| } |
| |
| processes->push_back(info); |
| } |
| |
| } // namespace |
| |
| MemoryDetails::MemoryDetails() { |
| const base::FilePath browser_process_path = |
| base::GetProcessExecutablePath(base::GetCurrentProcessHandle()); |
| |
| ProcessData process; |
| process.name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME); |
| process.process_name = |
| base::UTF8ToUTF16(browser_process_path.BaseName().value()); |
| process_data_.push_back(process); |
| } |
| |
| ProcessData* MemoryDetails::ChromeBrowser() { |
| return &process_data_[0]; |
| } |
| |
| void MemoryDetails::CollectProcessData( |
| const std::vector<ProcessMemoryInformation>& child_info) { |
| base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, |
| base::BlockingType::MAY_BLOCK); |
| |
| // Clear old data. |
| process_data_[0].processes.clear(); |
| |
| // First, we use |NamedProcessIterator| to get the PIDs of the processes we're |
| // interested in; we save our results to avoid extra calls to |
| // |NamedProcessIterator| (for performance reasons) and to avoid additional |
| // inconsistencies caused by racing. Then we run |/bin/ps| *once* to get |
| // information on those PIDs. Then we used our saved information to iterate |
| // over browsers, then over PIDs. |
| |
| // Get PIDs of main browser processes. |
| std::vector<base::ProcessId> all_pids; |
| { |
| base::NamedProcessIterator process_it( |
| base::UTF16ToUTF8(process_data_[0].process_name), NULL); |
| |
| while (const base::ProcessEntry* entry = process_it.NextProcessEntry()) { |
| all_pids.push_back(entry->pid()); |
| } |
| } |
| |
| // Get PIDs of the helper. |
| { |
| base::NamedProcessIterator helper_it(chrome::kHelperProcessExecutableName, |
| NULL, /*use_prefix_match=*/true); |
| while (const base::ProcessEntry* entry = helper_it.NextProcessEntry()) { |
| all_pids.push_back(entry->pid()); |
| } |
| } |
| |
| ProcessMemoryInformationList* chrome_processes = &process_data_[0].processes; |
| |
| // Collect data about Chrome/Chromium. |
| for (const base::ProcessId& pid : all_pids) |
| CollectProcessDataForChromeProcess(child_info, pid, chrome_processes); |
| |
| // Finally return to the browser thread. |
| content::GetUIThreadTaskRunner({})->PostTask( |
| FROM_HERE, |
| base::BindOnce(&MemoryDetails::CollectChildInfoOnUIThread, this)); |
| } |