blob: 0169163f45fa630a6b4fd51abbc5ae1ebaf57e35 [file] [log] [blame]
// Copyright 2017 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/metrics/process_memory_metrics_emitter.h"
#include "base/metrics/histogram_macros.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/service_manager_connection.h"
#include "content/public/common/service_names.mojom.h"
#include "extensions/features/features.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
#include "services/resource_coordinator/public/interfaces/service_constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "url/gurl.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/process_map.h"
#include "extensions/common/extension.h"
#endif
using ProcessMemoryDumpPtr =
memory_instrumentation::mojom::ProcessMemoryDumpPtr;
namespace {
void EmitBrowserMemoryMetrics(const ProcessMemoryDumpPtr& pmd,
ukm::SourceId ukm_source_id,
ukm::UkmRecorder* ukm_recorder) {
ukm::builders::Memory_Experimental builder(ukm_source_id);
builder.SetProcessType(static_cast<int64_t>(
memory_instrumentation::mojom::ProcessType::BROWSER));
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental.Browser2.Resident",
pmd->os_dump->resident_set_kb / 1024);
builder.SetResident(pmd->os_dump->resident_set_kb / 1024);
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental.Browser2.Malloc",
pmd->chrome_dump->malloc_total_kb / 1024);
builder.SetMalloc(pmd->chrome_dump->malloc_total_kb / 1024);
UMA_HISTOGRAM_MEMORY_LARGE_MB(
"Memory.Experimental.Browser2.PrivateMemoryFootprint",
pmd->os_dump->private_footprint_kb / 1024);
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Browser.PrivateMemoryFootprint",
pmd->os_dump->private_footprint_kb / 1024);
builder.SetPrivateMemoryFootprint(pmd->os_dump->private_footprint_kb / 1024);
builder.Record(ukm_recorder);
}
#define RENDERER_MEMORY_UMA_HISTOGRAMS(type) \
do { \
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental." type "2.Resident", \
pmd->os_dump->resident_set_kb / 1024); \
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental." type "2.Malloc", \
pmd->chrome_dump->malloc_total_kb / 1024); \
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental." type \
"2.PrivateMemoryFootprint", \
pmd->os_dump->private_footprint_kb / 1024); \
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory." type ".PrivateMemoryFootprint", \
pmd->os_dump->private_footprint_kb / 1024); \
UMA_HISTOGRAM_MEMORY_LARGE_MB( \
"Memory.Experimental." type "2.PartitionAlloc", \
pmd->chrome_dump->partition_alloc_total_kb / 1024); \
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental." type "2.BlinkGC", \
pmd->chrome_dump->blink_gc_total_kb / 1024); \
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental." type "2.V8", \
pmd->chrome_dump->v8_total_kb / 1024); \
} while (false)
void EmitRendererMemoryMetrics(const ProcessMemoryDumpPtr& pmd,
ukm::SourceId ukm_source_id,
ukm::UkmRecorder* ukm_recorder,
int number_of_extensions) {
// UMA
if (number_of_extensions == 0) {
RENDERER_MEMORY_UMA_HISTOGRAMS("Renderer");
} else {
RENDERER_MEMORY_UMA_HISTOGRAMS("Extension");
}
// UKM
ukm::builders::Memory_Experimental builder(ukm_source_id);
builder.SetProcessType(static_cast<int64_t>(
memory_instrumentation::mojom::ProcessType::RENDERER));
builder.SetResident(pmd->os_dump->resident_set_kb / 1024);
builder.SetMalloc(pmd->chrome_dump->malloc_total_kb / 1024);
builder.SetPrivateMemoryFootprint(pmd->os_dump->private_footprint_kb / 1024);
builder.SetPartitionAlloc(pmd->chrome_dump->partition_alloc_total_kb / 1024);
builder.SetBlinkGC(pmd->chrome_dump->blink_gc_total_kb / 1024);
builder.SetV8(pmd->chrome_dump->v8_total_kb / 1024);
builder.SetNumberOfExtensions(number_of_extensions);
builder.Record(ukm_recorder);
}
void EmitGpuMemoryMetrics(const ProcessMemoryDumpPtr& pmd,
ukm::SourceId ukm_source_id,
ukm::UkmRecorder* ukm_recorder) {
ukm::builders::Memory_Experimental builder(ukm_source_id);
builder.SetProcessType(
static_cast<int64_t>(memory_instrumentation::mojom::ProcessType::GPU));
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental.Gpu2.Resident",
pmd->os_dump->resident_set_kb / 1024);
builder.SetResident(pmd->os_dump->resident_set_kb / 1024);
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Experimental.Gpu2.Malloc",
pmd->chrome_dump->malloc_total_kb / 1024);
builder.SetMalloc(pmd->chrome_dump->malloc_total_kb / 1024);
UMA_HISTOGRAM_MEMORY_LARGE_MB(
"Memory.Experimental.Gpu2.CommandBuffer",
pmd->chrome_dump->command_buffer_total_kb / 1024);
builder.SetCommandBuffer(pmd->chrome_dump->command_buffer_total_kb / 1024);
UMA_HISTOGRAM_MEMORY_LARGE_MB(
"Memory.Experimental.Gpu2.PrivateMemoryFootprint",
pmd->os_dump->private_footprint_kb / 1024);
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Gpu.PrivateMemoryFootprint",
pmd->os_dump->private_footprint_kb / 1024);
builder.SetPrivateMemoryFootprint(pmd->os_dump->private_footprint_kb / 1024);
builder.Record(ukm_recorder);
}
} // namespace
ProcessMemoryMetricsEmitter::ProcessMemoryMetricsEmitter() {}
void ProcessMemoryMetricsEmitter::FetchAndEmitProcessMemoryMetrics() {
MarkServiceRequestsInProgress();
service_manager::Connector* connector =
content::ServiceManagerConnection::GetForProcess()->GetConnector();
connector->BindInterface(content::mojom::kBrowserServiceName,
mojo::MakeRequest(&coordinator_));
// The callback keeps this object alive until the callback is invoked..
auto callback =
base::Bind(&ProcessMemoryMetricsEmitter::ReceivedMemoryDump, this);
base::trace_event::MemoryDumpRequestArgs args = {
0, base::trace_event::MemoryDumpType::SUMMARY_ONLY,
base::trace_event::MemoryDumpLevelOfDetail::BACKGROUND};
coordinator_->RequestGlobalMemoryDump(args, callback);
// The callback keeps this object alive until the callback is invoked.
if (IsResourceCoordinatorEnabled()) {
connector->BindInterface(resource_coordinator::mojom::kServiceName,
mojo::MakeRequest(&introspector_));
auto callback2 =
base::Bind(&ProcessMemoryMetricsEmitter::ReceivedProcessInfos, this);
introspector_->GetProcessToURLMap(callback2);
}
}
void ProcessMemoryMetricsEmitter::MarkServiceRequestsInProgress() {
memory_dump_in_progress_ = true;
if (IsResourceCoordinatorEnabled())
get_process_urls_in_progress_ = true;
}
ProcessMemoryMetricsEmitter::~ProcessMemoryMetricsEmitter() {}
void ProcessMemoryMetricsEmitter::ReceivedMemoryDump(
bool success,
uint64_t dump_guid,
memory_instrumentation::mojom::GlobalMemoryDumpPtr ptr) {
memory_dump_in_progress_ = false;
if (!success)
return;
global_dump_ = std::move(ptr);
CollateResults();
}
void ProcessMemoryMetricsEmitter::ReceivedProcessInfos(
std::vector<resource_coordinator::mojom::ProcessInfoPtr> process_infos) {
get_process_urls_in_progress_ = false;
process_infos_.clear();
process_infos_.reserve(process_infos.size());
// If there are duplicate pids, keep the latest ProcessInfoPtr.
for (resource_coordinator::mojom::ProcessInfoPtr& process_info :
process_infos) {
base::ProcessId pid = process_info->pid;
process_infos_[pid] = std::move(process_info);
}
CollateResults();
}
bool ProcessMemoryMetricsEmitter::IsResourceCoordinatorEnabled() {
return resource_coordinator::IsResourceCoordinatorEnabled();
}
ukm::UkmRecorder* ProcessMemoryMetricsEmitter::GetUkmRecorder() {
return ukm::UkmRecorder::Get();
}
int ProcessMemoryMetricsEmitter::GetNumberOfExtensions(base::ProcessId pid) {
int number_of_extensions = 0;
#if BUILDFLAG(ENABLE_EXTENSIONS)
// Retrieve the renderer process host for the given pid.
int rph_id = -1;
auto iter = content::RenderProcessHost::AllHostsIterator();
while (!iter.IsAtEnd()) {
if (base::GetProcId(iter.GetCurrentValue()->GetHandle()) == pid) {
rph_id = iter.GetCurrentValue()->GetID();
break;
}
iter.Advance();
}
if (iter.IsAtEnd())
return 0;
// Count the number of extensions associated with that renderer process host
// in all profiles.
for (Profile* profile :
g_browser_process->profile_manager()->GetLoadedProfiles()) {
extensions::ProcessMap* process_map = extensions::ProcessMap::Get(profile);
extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistry::Get(profile);
std::set<std::string> extension_ids =
process_map->GetExtensionsInProcess(rph_id);
for (const std::string& extension_id : extension_ids) {
// Only count non hosted apps extensions.
const extensions::Extension* extension =
registry->enabled_extensions().GetByID(extension_id);
if (extension && !extension->is_hosted_app())
number_of_extensions++;
}
}
#endif
return number_of_extensions;
}
void ProcessMemoryMetricsEmitter::CollateResults() {
if (memory_dump_in_progress_ || get_process_urls_in_progress_)
return;
if (!global_dump_)
return;
uint32_t private_footprint_total_kb = 0;
for (const ProcessMemoryDumpPtr& pmd : global_dump_->process_dumps) {
private_footprint_total_kb += pmd->os_dump->private_footprint_kb;
switch (pmd->process_type) {
case memory_instrumentation::mojom::ProcessType::BROWSER: {
EmitBrowserMemoryMetrics(pmd, ukm::UkmRecorder::GetNewSourceID(),
GetUkmRecorder());
break;
}
case memory_instrumentation::mojom::ProcessType::RENDERER: {
ukm::SourceId ukm_source_id = ukm::UkmRecorder::GetNewSourceID();
// If there is more than one frame being hosted in a renderer, don't
// emit any URLs. This is not ideal, but UKM does not support
// multiple-URLs per entry, and we must have one entry per process.
if (process_infos_.find(pmd->pid) != process_infos_.end()) {
const resource_coordinator::mojom::ProcessInfoPtr& process_info =
process_infos_[pmd->pid];
if (process_info->ukm_source_ids.size() == 1) {
ukm_source_id = process_info->ukm_source_ids[0];
}
}
int number_of_extensions = GetNumberOfExtensions(pmd->pid);
EmitRendererMemoryMetrics(pmd, ukm_source_id, GetUkmRecorder(),
number_of_extensions);
break;
}
case memory_instrumentation::mojom::ProcessType::GPU: {
EmitGpuMemoryMetrics(pmd, ukm::UkmRecorder::GetNewSourceID(),
GetUkmRecorder());
break;
}
case memory_instrumentation::mojom::ProcessType::UTILITY:
case memory_instrumentation::mojom::ProcessType::PLUGIN:
case memory_instrumentation::mojom::ProcessType::OTHER:
break;
}
}
UMA_HISTOGRAM_MEMORY_LARGE_MB(
"Memory.Experimental.Total2.PrivateMemoryFootprint",
private_footprint_total_kb / 1024);
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.PrivateMemoryFootprint",
private_footprint_total_kb / 1024);
ukm::builders::Memory_Experimental(ukm::UkmRecorder::GetNewSourceID())
.SetTotal2_PrivateMemoryFootprint(private_footprint_total_kb / 1024)
.Record(GetUkmRecorder());
}