blob: ffd655d56e3cb29345884d2ac22a7daafc57db3f [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// 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/mac/mac_util.h"
#include "base/process/process_iterator.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.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_thread.h"
#include "content/public/common/process_type.h"
#include "ui/base/l10n/l10n_util.h"
using content::BrowserThread;
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());
// 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;
}
}
std::unique_ptr<base::ProcessMetrics> metrics =
base::ProcessMetrics::CreateProcessMetrics(
pid, content::BrowserChildProcessHost::GetPortProvider());
metrics->GetCommittedAndWorkingSetKBytes(&info.committed, &info.working_set);
base::ProcessMetrics::TaskVMInfo vm_info = metrics->GetTaskVMInfo();
info.phys_footprint = vm_info.phys_footprint;
// TODO(erikchen): Remove this temporary estimate for private memory once the
// memory infra service emits the same metric. https://crbug.com/720541.
if (base::mac::IsAtLeastOS10_12()) {
info.private_memory_footprint = vm_info.phys_footprint;
} else {
info.private_memory_footprint = vm_info.internal + vm_info.compressed;
}
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::AssertBlockingAllowed();
// 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);
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.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&MemoryDetails::CollectChildInfoOnUIThread, this));
}