blob: 9ffccddfe9cabd1ba6536c64e33682f7c8d1e4ed [file] [log] [blame]
// Copyright 2017 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/metrics/process_memory_metrics_emitter.h"
#include <array>
#include <set>
#include <string>
#include <string_view>
#include <utility>
#include "base/allocator/buildflags.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/page_size.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/process/process_metrics.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/task/sequenced_task_runner.h"
#include "base/trace_event/memory_dump_request_args.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/tab_footprint_aggregator.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_switches.h"
#include "components/metrics/metrics_data_validation.h"
#include "components/performance_manager/public/graph/frame_node.h"
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/graph/graph_operations.h"
#include "components/performance_manager/public/graph/page_node.h"
#include "components/performance_manager/public/graph/process_node.h"
#include "components/performance_manager/public/performance_manager.h"
#include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
#include "content/public/browser/audio_service_info.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_switches.h"
#include "extensions/buildflags/buildflags.h"
#include "media/mojo/mojom/cdm_service.mojom.h"
#include "partition_alloc/buildflags.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/mojom/network_service.mojom.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/browser_metrics.h"
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
#include "url/gurl.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/child_process_binding_types.h"
#include "base/android/meminfo_dump_provider.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "media/mojo/mojom/media_foundation_service.mojom.h"
#endif
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/process_map.h"
#include "extensions/common/extension.h"
#endif
using base::trace_event::MemoryAllocatorDump;
using memory_instrumentation::GetPrivateFootprintHistogramName;
using memory_instrumentation::GlobalMemoryDump;
using memory_instrumentation::HistogramProcessType;
using memory_instrumentation::HistogramProcessTypeToString;
using memory_instrumentation::kMemoryHistogramPrefix;
using ukm::builders::Memory_Experimental;
namespace {
const char kEffectiveSize[] = "effective_size";
const char kSize[] = "size";
const char kAllocatedObjectsSize[] = "allocated_objects_size";
#if BUILDFLAG(IS_CHROMEOS)
const char kNonExoSize[] = "non_exo_size";
#endif
constexpr int kKiB = 1024;
constexpr int kMiB = 1024 * 1024;
struct MetricRange {
const int min;
const int max;
};
const MetricRange ImageSizeMetricRange = {1, 500 * kMiB /*500 MiB*/};
// Prefer predefined ranges kLarge, kSmall and kTiny over custom ranges.
enum class MetricSize {
kPercentage, // percentages, 0% - 100%
kLarge, // 1MiB - 64,000MiB
kSmall, // 10 - 500,000KiB
kTiny, // 1 - 500,000B
kCustom, // custom range, in bytes
};
enum class EmitTo {
kCountsInUkmOnly,
kCountsInUkmAndSizeInUma,
kSizeInUkmAndUma,
kSizeInUmaOnly,
kIgnored
};
struct Metric {
// The root dump name that represents the required metric.
const char* const dump_name;
// The name of the metric to be recorded in UMA.
const char* const uma_name;
// Indicates the size range of the metric. Only relevant if the |metric| is a
// size metric.
const MetricSize metric_size;
// The type of metric that is measured, usually size in bytes or object count.
const char* const metric;
// Indicates where to emit the metric.
const EmitTo target;
// The setter method for the metric in UKM recorder.
Memory_Experimental& (Memory_Experimental::*ukm_setter)(int64_t);
// Size range for the kCustom |metric_size|. Represents the min and max of the
// size range, in bytes.
const MetricRange range;
};
const Metric kAllocatorDumpNamesForMetrics[] = {
{"accessibility/ax_platform_node",
"AXPlatformNodeCount",
MetricSize::kCustom,
MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly,
/*ukm_setter=*/nullptr,
{1, 1000000}},
{"accessibility/ax_platform_win_dormant_node",
"AXPlatformWinDormantNodeCount",
MetricSize::kCustom,
MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly,
/*ukm_setter=*/nullptr,
{1, 1000000}},
{"accessibility/ax_platform_win_ghost_node",
"AXPlatformWinGhostNodeCount",
MetricSize::kCustom,
MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly,
/*ukm_setter=*/nullptr,
{1, 1000000}},
{"accessibility/ax_platform_win_live_node",
"AXPlatformWinLiveNodeCount",
MetricSize::kCustom,
MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly,
/*ukm_setter=*/nullptr,
{1, 1000000}},
{"blink_gc", "BlinkGC", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetBlinkGC},
{"blink_gc", "BlinkGC.AllocatedObjects", MetricSize::kLarge,
kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetBlinkGC_AllocatedObjects},
{"blink_gc", "BlinkGC.Fragmentation", MetricSize::kPercentage,
"fragmentation", EmitTo::kSizeInUmaOnly, nullptr},
{"blink_gc/main", "BlinkGC.Main.Heap", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUmaOnly, nullptr},
{"blink_gc/main", "BlinkGC.Main.Heap.AllocatedObjects", MetricSize::kLarge,
kAllocatedObjectsSize, EmitTo::kSizeInUmaOnly, nullptr},
{"blink_gc/main", "BlinkGC.Main.Heap.Fragmentation",
MetricSize::kPercentage, "fragmentation", EmitTo::kSizeInUmaOnly, nullptr},
{"blink_objects/Document", "NumberOfDocuments", MetricSize::kTiny,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmAndSizeInUma,
&Memory_Experimental::SetNumberOfDocuments},
{"blink_objects/ArrayBufferContents", "NumberOfArrayBufferContents",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kCountsInUkmAndSizeInUma,
&Memory_Experimental::SetNumberOfArrayBufferContents},
{"blink_objects/AdSubframe", "NumberOfAdSubframes", MetricSize::kTiny,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmAndSizeInUma,
&Memory_Experimental::SetNumberOfAdSubframes},
{"blink_objects/DetachedScriptState", "NumberOfDetachedScriptStates",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kCountsInUkmAndSizeInUma,
&Memory_Experimental::SetNumberOfDetachedScriptStates},
{"blink_objects/Frame", "NumberOfFrames", MetricSize::kTiny,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmAndSizeInUma,
&Memory_Experimental::SetNumberOfFrames},
{"blink_objects/LayoutObject", "NumberOfLayoutObjects", MetricSize::kTiny,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmAndSizeInUma,
&Memory_Experimental::SetNumberOfLayoutObjects},
{"blink_objects/Node", "NumberOfNodes", MetricSize::kSmall,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmAndSizeInUma,
&Memory_Experimental::SetNumberOfNodes},
{"blink_objects/AudioHandler", "NumberOfAudioHandler", MetricSize::kTiny,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kSizeInUmaOnly, nullptr},
{"blink_objects/JSEventListener", "NumberOfJSEventListener",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly, nullptr},
{"blink_objects/MediaKeySession", "NumberOfMediaKeySession",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly, nullptr},
{"blink_objects/MediaKeys", "NumberOfMediaKeys", MetricSize::kTiny,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kSizeInUmaOnly, nullptr},
{"blink_objects/Resource", "NumberOfResources", MetricSize::kTiny,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kSizeInUmaOnly, nullptr},
{"blink_objects/ContextLifecycleStateObserver",
"NumberOfContextLifecycleStateObserver", MetricSize::kTiny,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kSizeInUmaOnly, nullptr},
{"blink_objects/V8PerContextData", "NumberOfV8PerContextData",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly, nullptr},
{"blink_objects/WorkerGlobalScope", "NumberOfWorkerGlobalScope",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly, nullptr},
{"blink_objects/UACSSResource", "NumberOfUACSSResource", MetricSize::kTiny,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kSizeInUmaOnly, nullptr},
{"blink_objects/RTCPeerConnection", "NumberOfRTCPeerConnection",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly, nullptr},
{"blink_objects/ResourceFetcher", "NumberOfResourceFetcher",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly, nullptr},
{"canvas/hibernated", "HibernatedCanvas.Size", MetricSize::kSmall, kSize,
EmitTo::kSizeInUmaOnly, nullptr},
{"canvas/hibernated", "HibernatedCanvas.OriginalSize", MetricSize::kSmall,
"original_size", EmitTo::kSizeInUmaOnly, nullptr},
{"cc/tile_memory", "TileMemory", MetricSize::kSmall, kSize,
EmitTo::kSizeInUmaOnly, nullptr},
{"components/download", "DownloadService", MetricSize::kSmall,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetDownloadService},
{"discardable", "Discardable", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetDiscardable},
{"discardable", "Discardable.FreelistSize", MetricSize::kSmall,
"freelist_size", EmitTo::kSizeInUmaOnly, nullptr},
{"discardable", "Discardable.FreelistSize.Dirty", MetricSize::kSmall,
"freelist_size_dirty", EmitTo::kSizeInUmaOnly, nullptr},
{"discardable", "Discardable.ResidentSize", MetricSize::kSmall,
"resident_size", EmitTo::kSizeInUmaOnly, nullptr},
{"discardable", "Discardable.VirtualSize", MetricSize::kSmall,
"virtual_size", EmitTo::kSizeInUmaOnly, nullptr},
{"extensions/functions", "ExtensionFunctions", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
{"extensions/value_store", "Extensions.ValueStore", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetExtensions_ValueStore},
{"font_caches", "FontCaches", MetricSize::kSmall, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetFontCaches},
{"gpu/dawn", "DawnSharedContext", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/dawn/textures", "DawnSharedContext.Textures", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/dawn/textures/depth_stencil", "DawnSharedContext.DepthStencil",
MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/dawn/textures/msaa", "DawnSharedContext.MSAA", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/dawn/textures/msaa",
"DawnSharedContext.MSAA.Count",
MetricSize::kCustom,
MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly,
nullptr,
{0, 10000}},
{"gpu/dawn/textures/msaa", "DawnSharedContext.MSAA.Largest",
MetricSize::kLarge, "biggest_size", EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/dawn/buffers", "DawnSharedContext.Buffers", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/discardable_cache", "ServiceDiscardableManager", MetricSize::kCustom,
kSize, EmitTo::kSizeInUmaOnly, nullptr, ImageSizeMetricRange},
{"gpu/discardable_cache", "ServiceDiscardableManager.AvgImageSize",
MetricSize::kCustom, "average_size", EmitTo::kSizeInUmaOnly, nullptr,
ImageSizeMetricRange},
{"gpu/gl", "CommandBuffer", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetCommandBuffer},
{"gpu/shader_cache/graphite_cache", "Gpu.GraphiteShaderCache",
MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/shader_cache/gr_shader_cache", "Gpu.GrShaderCache",
MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/mapped_memory", "GpuMappedMemory", MetricSize::kSmall, kEffectiveSize,
EmitTo::kSizeInUmaOnly, nullptr},
// Not effective size, to account for the total footprint, a large fraction
// of it being claimed by renderers.
{"gpu/shared_images", "SharedImages", MetricSize::kLarge, kSize,
EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/shared_images", "SharedImages.Purgeable", MetricSize::kLarge,
"purgeable_size", EmitTo::kSizeInUmaOnly, nullptr},
#if BUILDFLAG(IS_CHROMEOS)
{"gpu/shared_images", "SharedImages.NonExo", MetricSize::kLarge,
kNonExoSize, EmitTo::kSizeInUmaOnly, nullptr},
#endif // BUILDFLAG(IS_CHROMEOS)
{"gpu/transfer_cache", "ServiceTransferCache", MetricSize::kCustom, kSize,
EmitTo::kSizeInUmaOnly, nullptr, ImageSizeMetricRange},
{"gpu/transfer_cache", "ServiceTransferCache.AvgImageSize",
MetricSize::kCustom, "average_size", EmitTo::kSizeInUmaOnly, nullptr,
ImageSizeMetricRange},
// For the Vulkan Memory Allocator, "allocated_size" is the amount of GPU
// memory used by the allocator except lazily allocated memory; "used_size"
// is the amount allocated by clients except lazily used memory.
{"gpu/vulkan", "Vulkan2", MetricSize::kLarge, "allocated_size",
EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/vulkan", "Vulkan2.AllocatedObjects", MetricSize::kLarge, "used_size",
EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/vulkan", "Vulkan2.Fragmentation", MetricSize::kLarge,
"fragmentation_size", EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/vulkan", "Vulkan2.LazyAllocatedObjects", MetricSize::kLarge,
"lazy_allocated_size", EmitTo::kSizeInUmaOnly, nullptr},
{"gpu/vulkan", "Vulkan2.LazyUsedObjects", MetricSize::kLarge,
"lazy_used_size", EmitTo::kSizeInUmaOnly, nullptr},
{"history", "History", MetricSize::kSmall, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetHistory},
#if BUILDFLAG(IS_MAC)
{"iosurface", "IOSurface", MetricSize::kLarge, kSize,
EmitTo::kSizeInUmaOnly, nullptr},
{"iosurface", "IOSurface.DirtyMemory", MetricSize::kLarge,
"resident_swapped", EmitTo::kSizeInUmaOnly, nullptr},
{"iosurface", "IOSurface.NonPurgeable", MetricSize::kLarge,
"nonpurgeable_size", EmitTo::kSizeInUmaOnly, nullptr},
{"iosurface", "IOSurface.Purgeable", MetricSize::kLarge, "purgeable_size",
EmitTo::kSizeInUmaOnly, nullptr},
{"ioaccelerator", "IOAccelerator", MetricSize::kLarge, kSize,
EmitTo::kSizeInUmaOnly, nullptr},
{"ioaccelerator", "IOAccelerator.DirtyMemory", MetricSize::kLarge,
"resident_swapped", EmitTo::kSizeInUmaOnly, nullptr},
{"ioaccelerator", "IOAccelerator.NonPurgeable", MetricSize::kLarge,
"nonpurgeable_size", EmitTo::kSizeInUmaOnly, nullptr},
{"ioaccelerator", "IOAccelerator.Purgeable", MetricSize::kLarge,
"purgeable_size", EmitTo::kSizeInUmaOnly, nullptr},
#endif
{"java_heap", "JavaHeap", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetJavaHeap},
{"leveldatabase", "LevelDatabase", MetricSize::kSmall, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetLevelDatabase},
{"malloc", "Malloc", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetMalloc},
{"malloc/allocated_objects", "Malloc.AllocatedObjects", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetMalloc_AllocatedObjects},
{"malloc/allocated_objects", "Malloc.AllocatedObjects.ObjectCount",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/original", "Malloc.Original.ObjectCount",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/aligned", "Malloc.Aligned.ObjectCount",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator", "Malloc.Allocator.ObjectCount",
MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
EmitTo::kSizeInUmaOnly, nullptr},
#if PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
{"malloc/partitions/allocator", "Malloc.BRPQuarantined", MetricSize::kSmall,
"brp_quarantined_size", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator", "Malloc.BRPQuarantinedCount",
MetricSize::kTiny, "brp_quarantined_count", EmitTo::kSizeInUmaOnly,
nullptr},
{"partition_alloc/partitions", "PartitionAlloc.BRPQuarantined",
MetricSize::kSmall, "brp_quarantined_size", EmitTo::kSizeInUmaOnly,
nullptr},
{"partition_alloc/partitions", "PartitionAlloc.BRPQuarantinedCount",
MetricSize::kTiny, "brp_quarantined_count", EmitTo::kSizeInUmaOnly,
nullptr},
{"partition_alloc/partitions/fast_malloc",
"PartitionAlloc.BRPQuarantined.FastMalloc", MetricSize::kSmall,
"brp_quarantined_size", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/fast_malloc",
"PartitionAlloc.BRPQuarantinedCount.FastMalloc", MetricSize::kTiny,
"brp_quarantined_count", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/buffer",
"PartitionAlloc.BRPQuarantined.Buffer", MetricSize::kSmall,
"brp_quarantined_size", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/buffer",
"PartitionAlloc.BRPQuarantinedCount.Buffer", MetricSize::kTiny,
"brp_quarantined_count", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/array_buffer",
"PartitionAlloc.BRPQuarantined.ArrayBuffer", MetricSize::kSmall,
"brp_quarantined_size", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/array_buffer",
"PartitionAlloc.BRPQuarantinedCount.ArrayBuffer", MetricSize::kTiny,
"brp_quarantined_count", EmitTo::kSizeInUmaOnly, nullptr},
#endif // PA_BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
{"malloc/partitions", "Malloc.BRPQuarantinedBytesPerMinute",
MetricSize::kSmall, "brp_quarantined_bytes_per_minute",
EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions", "Malloc.BRPQuarantinedCountPerMinute",
MetricSize::kTiny, "brp_quarantined_count_per_minute",
EmitTo::kSizeInUmaOnly, nullptr},
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
{"malloc/extreme_lud/large_objects", "Malloc.ExtremeLUD.LargeObjects.Count",
MetricSize::kTiny, "count", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/large_objects",
"Malloc.ExtremeLUD.LargeObjects.SizeInBytes", MetricSize::kSmall,
"size_in_bytes", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/large_objects",
"Malloc.ExtremeLUD.LargeObjects.CumulativeCount", MetricSize::kSmall,
"cumulative_count", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/large_objects",
"Malloc.ExtremeLUD.LargeObjects.CumulativeSizeInBytes", MetricSize::kLarge,
"cumulative_size_in_bytes", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/large_objects",
"Malloc.ExtremeLUD.LargeObjects.QuarantineMissCount", MetricSize::kTiny,
"quarantine_miss_count", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/large_objects",
"Malloc.ExtremeLUD.LargeObjects.BytesPerMinute", MetricSize::kSmall,
"bytes_per_minute", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/large_objects",
"Malloc.ExtremeLUD.LargeObjects.CountPerMinute", MetricSize::kTiny,
"count_per_minute", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/large_objects",
"Malloc.ExtremeLUD.LargeObjects.MissCountPerMinute", MetricSize::kTiny,
"miss_count_per_minute", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/large_objects",
"Malloc.ExtremeLUD.LargeObjects.QuarantinedTime", MetricSize::kSmall,
"quarantined_time", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/small_objects", "Malloc.ExtremeLUD.SmallObjects.Count",
MetricSize::kTiny, "count", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/small_objects",
"Malloc.ExtremeLUD.SmallObjects.SizeInBytes", MetricSize::kSmall,
"size_in_bytes", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/small_objects",
"Malloc.ExtremeLUD.SmallObjects.CumulativeCount", MetricSize::kSmall,
"cumulative_count", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/small_objects",
"Malloc.ExtremeLUD.SmallObjects.CumulativeSizeInBytes", MetricSize::kLarge,
"cumulative_size_in_bytes", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/small_objects",
"Malloc.ExtremeLUD.SmallObjects.QuarantineMissCount", MetricSize::kTiny,
"quarantine_miss_count", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/small_objects",
"Malloc.ExtremeLUD.SmallObjects.BytesPerMinute", MetricSize::kSmall,
"bytes_per_minute", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/small_objects",
"Malloc.ExtremeLUD.SmallObjects.CountPerMinute", MetricSize::kTiny,
"count_per_minute", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/small_objects",
"Malloc.ExtremeLUD.SmallObjects.MissCountPerMinute", MetricSize::kTiny,
"miss_count_per_minute", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/extreme_lud/small_objects",
"Malloc.ExtremeLUD.SmallObjects.QuarantinedTime", MetricSize::kSmall,
"quarantined_time", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator/scheduler_loop_quarantine",
"Malloc.SchedulerLoopQuarantine.Count", MetricSize::kTiny, "count",
EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator/scheduler_loop_quarantine",
"Malloc.SchedulerLoopQuarantine.SizeInBytes", MetricSize::kSmall,
"size_in_bytes", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator/scheduler_loop_quarantine",
"Malloc.SchedulerLoopQuarantine.CumulativeCount", MetricSize::kTiny,
"cumulative_count", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator/scheduler_loop_quarantine",
"Malloc.SchedulerLoopQuarantine.CumulativeSizeInBytes", MetricSize::kSmall,
"cumulative_size_in_bytes", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator/scheduler_loop_quarantine/",
"Malloc.SchedulerLoopQuarantine.QuarantineMissCount", MetricSize::kTiny,
"quarantine_miss_count", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator/thread_cache", "Malloc.ThreadCache",
MetricSize::kSmall, kSize, EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator", "Malloc.MaxAllocatedSize",
MetricSize::kLarge, "max_allocated_size", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator", "Malloc.MaxCommittedSize",
MetricSize::kLarge, "max_committed_size", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator", "Malloc.CommittedSize", MetricSize::kLarge,
"virtual_committed_size", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator", "Malloc.Wasted", MetricSize::kLarge,
"wasted", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc/partitions/allocator", "Malloc.Fragmentation",
MetricSize::kPercentage, "fragmentation", EmitTo::kSizeInUmaOnly, nullptr},
{"malloc", "Malloc.SyscallsPerMinute", MetricSize::kTiny,
"syscalls_per_minute", EmitTo::kSizeInUmaOnly, nullptr},
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
{"mojo", "NumberOfMojoHandles", MetricSize::kSmall,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmOnly,
&Memory_Experimental::SetNumberOfMojoHandles},
{"media/webmediaplayer/audio", "WebMediaPlayer.Audio", MetricSize::kSmall,
kSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebMediaPlayer_Audio},
{"media/webmediaplayer/video", "WebMediaPlayer.Video", MetricSize::kLarge,
kSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebMediaPlayer_Video},
{"media/webmediaplayer/data_source", "WebMediaPlayer.DataSource",
MetricSize::kLarge, kSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebMediaPlayer_DataSource},
{"media/webmediaplayer/demuxer", "WebMediaPlayer.Demuxer",
MetricSize::kLarge, kSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebMediaPlayer_Demuxer},
{"media/webmediaplayer", "WebMediaPlayer.Instances", MetricSize::kTiny,
MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmOnly,
&Memory_Experimental::SetNumberOfWebMediaPlayers},
{"omnibox", "OmniboxSuggestions", MetricSize::kSmall, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetOmniboxSuggestions},
{"parkable_images", "ParkableImage.OnDiskSize", MetricSize::kSmall,
"on_disk_size", EmitTo::kSizeInUmaOnly, nullptr},
{"parkable_images", "ParkableImage.UnparkedSize", MetricSize::kSmall,
"unparked_size", EmitTo::kSizeInUmaOnly, nullptr},
{"parkable_images", "ParkableImage.TotalSize", MetricSize::kSmall,
"total_size", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc", "PartitionAlloc", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetPartitionAlloc},
{"partition_alloc/allocated_objects", "PartitionAlloc.AllocatedObjects",
MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetPartitionAlloc_AllocatedObjects},
{"partition_alloc/partitions/array_buffer",
"PartitionAlloc.Partitions.ArrayBuffer", MetricSize::kLarge, kSize,
EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetPartitionAlloc_Partitions_ArrayBuffer},
{"partition_alloc/partitions/array_buffer",
"PartitionAlloc.Fragmentation.ArrayBuffer", MetricSize::kPercentage,
"fragmentation", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/array_buffer",
"PartitionAlloc.MaxCommittedSize.ArrayBuffer", MetricSize::kLarge,
"max_committed", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/array_buffer",
"PartitionAlloc.CommittedSize.ArrayBuffer", MetricSize::kLarge,
"virtual_committed_size", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/array_buffer",
"PartitionAlloc.MaxAllocatedSize.ArrayBuffer", MetricSize::kLarge,
"max_allocated_size", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/array_buffer",
"PartitionAlloc.Wasted.ArrayBuffer", MetricSize::kLarge, "wasted",
EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/buffer", "PartitionAlloc.Partitions.Buffer",
MetricSize::kLarge, kSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetPartitionAlloc_Partitions_Buffer},
{"partition_alloc/partitions/buffer", "PartitionAlloc.Fragmentation.Buffer",
MetricSize::kPercentage, "fragmentation", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/buffer",
"PartitionAlloc.MaxCommittedSize.Buffer", MetricSize::kLarge,
"max_committed", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/buffer", "PartitionAlloc.CommittedSize.Buffer",
MetricSize::kLarge, "virtual_committed_size", EmitTo::kSizeInUmaOnly,
nullptr},
{"partition_alloc/partitions/buffer",
"PartitionAlloc.MaxAllocatedSize.Buffer", MetricSize::kLarge,
"max_allocated_size", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/buffer", "PartitionAlloc.Wasted.Buffer",
MetricSize::kLarge, "wasted", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/fast_malloc",
"PartitionAlloc.Partitions.FastMalloc", MetricSize::kLarge, kSize,
EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetPartitionAlloc_Partitions_FastMalloc},
{"partition_alloc/partitions/fast_malloc",
"PartitionAlloc.Fragmentation.FastMalloc", MetricSize::kPercentage,
"fragmentation", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/fast_malloc",
"PartitionAlloc.MaxCommittedSize.FastMalloc", MetricSize::kLarge,
"max_committed", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/fast_malloc",
"PartitionAlloc.CommittedSize.FastMalloc", MetricSize::kLarge,
"virtual_committed_size", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/fast_malloc",
"PartitionAlloc.MaxAllocatedSize.FastMalloc", MetricSize::kLarge,
"max_allocated_size", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/fast_malloc",
"PartitionAlloc.Wasted.FastMalloc", MetricSize::kLarge, "wasted",
EmitTo::kSizeInUmaOnly, nullptr},
#if !PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
{"partition_alloc/partitions/fast_malloc/thread_cache",
"PartitionAlloc.Partitions.FastMalloc.ThreadCache", MetricSize::kSmall,
kSize, EmitTo::kSizeInUmaOnly, nullptr},
#endif
{"partition_alloc/partitions/layout", "PartitionAlloc.Partitions.Layout",
MetricSize::kLarge, kSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetPartitionAlloc_Partitions_Layout},
{"partition_alloc/partitions/layout", "PartitionAlloc.Fragmentation.Layout",
MetricSize::kPercentage, "fragmentation", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/layout",
"PartitionAlloc.MaxCommittedSize.Layout", MetricSize::kLarge,
"max_committed", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/layout", "PartitionAlloc.CommittedSize.Layout",
MetricSize::kLarge, "virtual_committed_size", EmitTo::kSizeInUmaOnly,
nullptr},
{"partition_alloc/partitions/layout",
"PartitionAlloc.MaxAllocatedSize.Layout", MetricSize::kLarge,
"max_allocated_size", EmitTo::kSizeInUmaOnly, nullptr},
{"partition_alloc/partitions/layout", "PartitionAlloc.Wasted.Layout",
MetricSize::kLarge, "wasted", EmitTo::kSizeInUmaOnly, nullptr},
{"passwords", "ManualFillingCache", MetricSize::kSmall, kEffectiveSize,
EmitTo::kSizeInUmaOnly, nullptr},
{"site_storage", "SiteStorage", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetSiteStorage},
{"site_storage/blob_storage", "SiteStorage.BlobStorage", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetSiteStorage_BlobStorage},
{"site_storage/index_db", "SiteStorage.IndexDB", MetricSize::kSmall,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetSiteStorage_IndexDB},
{"site_storage/localstorage", "SiteStorage.LocalStorage",
MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetSiteStorage_LocalStorage},
{"site_storage/session_storage", "SiteStorage.SessionStorage",
MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetSiteStorage_SessionStorage},
{"skia", "Skia", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetSkia},
{"skia", "Skia.PurgeableSize", MetricSize::kLarge, "purgeable_size",
EmitTo::kSizeInUmaOnly, nullptr},
{"skia/gpu_resources", "SharedContextState", MetricSize::kLarge,
kEffectiveSize, EmitTo::kIgnored, nullptr},
{"skia/sk_glyph_cache", "Skia.SkGlyphCache", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetSkia_SkGlyphCache},
{"skia/sk_resource_cache", "Skia.SkResourceCache", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetSkia_SkResourceCache},
{"sqlite", "Sqlite", MetricSize::kSmall, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetSqlite},
{"sync", "Sync", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetSync},
{"tab_restore", "TabRestore", MetricSize::kSmall, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetTabRestore},
{"ui", "UI", MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetUI},
{"v8", "V8", MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8},
{"v8", "V8.AllocatedObjects", MetricSize::kLarge, kAllocatedObjectsSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetV8_AllocatedObjects},
{"v8/main", "V8.Main", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetV8_Main},
{"v8/main", "V8.Main.AllocatedObjects", MetricSize::kLarge,
kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_AllocatedObjects},
{"v8/main/global_handles", "V8.Main.GlobalHandles", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_GlobalHandles},
{"v8/main/global_handles", "V8.Main.GlobalHandles.AllocatedObjects",
MetricSize::kLarge, kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_GlobalHandles_AllocatedObjects},
{"v8/main/heap", "V8.Main.Heap", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetV8_Main_Heap},
{"v8/main/heap", "V8.Main.Heap.AllocatedObjects", MetricSize::kLarge,
kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_AllocatedObjects},
{"v8/main/heap/code_large_object_space",
"V8.Main.Heap.CodeLargeObjectSpace", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_CodeLargeObjectSpace},
{"v8/main/heap/code_large_object_space",
"V8.Main.Heap.CodeLargeObjectSpace.AllocatedObjects", MetricSize::kLarge,
kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::
SetV8_Main_Heap_CodeLargeObjectSpace_AllocatedObjects},
{"v8/main/heap/code_space", "V8.Main.Heap.CodeSpace", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_CodeSpace},
{"v8/main/heap/code_space", "V8.Main.Heap.CodeSpace.AllocatedObjects",
MetricSize::kLarge, kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_CodeSpace_AllocatedObjects},
{"v8/main/heap/large_object_space", "V8.Main.Heap.LargeObjectSpace",
MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_LargeObjectSpace},
{"v8/main/heap/large_object_space",
"V8.Main.Heap.LargeObjectSpace.AllocatedObjects", MetricSize::kLarge,
kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_LargeObjectSpace_AllocatedObjects},
{"v8/main/heap/map_space", "V8.Main.Heap.MapSpace", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_MapSpace},
{"v8/main/heap/map_space", "V8.Main.Heap.MapSpace.AllocatedObjects",
MetricSize::kLarge, kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_MapSpace_AllocatedObjects},
{"v8/main/heap/new_large_object_space", "V8.Main.Heap.NewLargeObjectSpace",
MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_NewLargeObjectSpace},
{"v8/main/heap/new_large_object_space",
"V8.Main.Heap.NewLargeObjectSpace.AllocatedObjects", MetricSize::kLarge,
kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::
SetV8_Main_Heap_NewLargeObjectSpace_AllocatedObjects},
{"v8/main/heap/new_space", "V8.Main.Heap.NewSpace", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_NewSpace},
{"v8/main/heap/new_space", "V8.Main.Heap.NewSpace.AllocatedObjects",
MetricSize::kLarge, kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_NewSpace_AllocatedObjects},
{"v8/main/heap/old_space", "V8.Main.Heap.OldSpace", MetricSize::kLarge,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_OldSpace},
{"v8/main/heap/old_space", "V8.Main.Heap.OldSpace.AllocatedObjects",
MetricSize::kLarge, kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_OldSpace_AllocatedObjects},
{"v8/main/heap/read_only_space", "V8.Main.Heap.ReadOnlySpace",
MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_ReadOnlySpace},
{"v8/main/heap/read_only_space",
"V8.Main.Heap.ReadOnlySpace.AllocatedObjects", MetricSize::kLarge,
kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_ReadOnlySpace_AllocatedObjects},
{"v8/main/heap/large_object_space", "V8.Main.Heap.SharedLargeObjectSpace",
MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_SharedLargeObjectSpace},
{"v8/main/heap/large_object_space",
"V8.Main.Heap.SharedLargeObjectSpace.AllocatedObjects", MetricSize::kLarge,
kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::
SetV8_Main_Heap_SharedLargeObjectSpace_AllocatedObjects},
{"v8/main/heap/shared_space", "V8.Main.Heap.SharedSpace",
MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_SharedSpace},
{"v8/main/heap/shared_space", "V8.Main.Heap.SharedSpace.AllocatedObjects",
MetricSize::kLarge, kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Main_Heap_SharedSpace_AllocatedObjects},
{"v8/main/malloc", "V8.Main.Malloc", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetV8_Main_Malloc},
{"v8/workers", "V8.Workers", MetricSize::kLarge, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetV8_Workers},
{"v8/workers", "V8.Workers.AllocatedObjects", MetricSize::kLarge,
kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetV8_Workers_AllocatedObjects},
{"web_cache", "WebCache", MetricSize::kSmall, kEffectiveSize,
EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetWebCache},
{"web_cache/Image_resources", "WebCache.ImageResources", MetricSize::kSmall,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebCache_ImageResources},
{"web_cache/CSS stylesheet_resources", "WebCache.CSSStylesheetResources",
MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebCache_CSSStylesheetResources},
{"web_cache/Script_resources", "WebCache.ScriptResources",
MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebCache_ScriptResources},
{"web_cache/XSL stylesheet_resources", "WebCache.XSLStylesheetResources",
MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebCache_XSLStylesheetResources},
{"web_cache/Font_resources", "WebCache.FontResources", MetricSize::kSmall,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebCache_FontResources},
{"web_cache/Code_cache", "WebCache.V8CodeCache", MetricSize::kSmall,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebCache_V8CodeCache},
{"web_cache/Encoded_size_duplicated_in_data_urls",
"WebCache.EncodedSizeDuplicatedInDataUrls", MetricSize::kSmall,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebCache_EncodedSizeDuplicatedInDataUrls},
{"web_cache/Other_resources", "WebCache.OtherResources", MetricSize::kSmall,
kEffectiveSize, EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetWebCache_OtherResources},
#if BUILDFLAG(IS_ANDROID)
{base::android::MeminfoDumpProvider::kDumpName, "AndroidOtherPss",
MetricSize::kLarge, base::android::MeminfoDumpProvider::kPssMetricName,
EmitTo::kSizeInUmaOnly, nullptr},
{base::android::MeminfoDumpProvider::kDumpName, "AndroidOtherPrivateDirty",
MetricSize::kLarge,
base::android::MeminfoDumpProvider::kPrivateDirtyMetricName,
EmitTo::kSizeInUmaOnly, nullptr},
#endif
};
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
// Metrics specific to PartitionAlloc's address space stats (cf.
// kAllocatorDumpNamesForMetrics above). All of these metrics come in
// three variants: bare, after 1 hour, and after 24 hours. These metrics
// are only recorded in UMA.
const Metric kPartitionAllocAddressSpaceMetrics[] = {
Metric{
.uma_name = "PartitionAlloc.AddressSpace.BlocklistSize",
.metric_size = MetricSize::kTiny,
.metric = "blocklist_size",
},
Metric{
.uma_name = "PartitionAlloc.AddressSpace.BlocklistHitCount",
.metric_size = MetricSize::kTiny,
.metric = "blocklist_hit_count",
},
Metric{
.uma_name = "PartitionAlloc.AddressSpace."
"RegularPoolLargestAvailableReservation",
.metric_size = MetricSize::kLarge,
.metric = "regular_pool_largest_reservation",
},
Metric{
.uma_name = "PartitionAlloc.AddressSpace.RegularPoolUsage",
.metric_size = MetricSize::kLarge,
.metric = "regular_pool_usage",
},
Metric{
.uma_name =
"PartitionAlloc.AddressSpace.BRPPoolLargestAvailableReservation",
.metric_size = MetricSize::kLarge,
.metric = "brp_pool_largest_reservation",
},
Metric{
.uma_name = "PartitionAlloc.AddressSpace.BRPPoolUsage",
.metric_size = MetricSize::kLarge,
.metric = "brp_pool_usage",
},
Metric{
.uma_name = "PartitionAlloc.AddressSpace."
"ConfigurablePoolLargestAvailableReservation",
.metric_size = MetricSize::kLarge,
.metric = "configurable_pool_largest_reservation",
},
Metric{
.uma_name = "PartitionAlloc.AddressSpace.ConfigurablePoolUsage",
.metric_size = MetricSize::kLarge,
.metric = "configurable_pool_usage",
},
Metric{
.uma_name = "PartitionAlloc.AddressSpace."
"ThreadIsolatedPoolLargestAvailableReservation",
.metric_size = MetricSize::kLarge,
.metric = "thread_isolated_pool_largest_reservation",
},
Metric{
.uma_name = "PartitionAlloc.AddressSpace.ThreadIsolatedPoolUsage",
.metric_size = MetricSize::kLarge,
.metric = "thread_isolated_pool_usage",
},
};
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
// Record a memory size in megabytes, over a potential interval up to 32 GB.
#define UMA_HISTOGRAM_LARGE_MEMORY_MB(name, sample) \
UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, 1, 32768, 50)
#define EXPERIMENTAL_UMA_PREFIX "Memory.Experimental."
#define VERSION_SUFFIX_PERCENT "2."
#define VERSION_SUFFIX_NORMAL "2."
#define VERSION_SUFFIX_SMALL "2.Small."
#define VERSION_SUFFIX_TINY "2.Tiny."
#define VERSION_SUFFIX_CUSTOM "2.Custom."
void EmitProcessUkm(const Metric& item,
uint64_t value,
Memory_Experimental* builder) {
DCHECK(item.ukm_setter) << "UKM metrics must provide a setter";
(builder->*(item.ukm_setter))(value);
}
const char* MetricSizeToVersionSuffix(MetricSize size) {
switch (size) {
case MetricSize::kPercentage:
return VERSION_SUFFIX_PERCENT;
case MetricSize::kLarge:
return VERSION_SUFFIX_NORMAL;
case MetricSize::kSmall:
return VERSION_SUFFIX_SMALL;
case MetricSize::kTiny:
return VERSION_SUFFIX_TINY;
case MetricSize::kCustom:
return VERSION_SUFFIX_CUSTOM;
}
}
void EmitProcessUma(HistogramProcessType process_type,
const Metric& item,
uint64_t value) {
std::string uma_name;
// Always use "Gpu" in process name for command buffers to be
// consistent even in single process mode.
if (std::string_view(item.uma_name) == "CommandBuffer") {
uma_name =
EXPERIMENTAL_UMA_PREFIX "Gpu" VERSION_SUFFIX_NORMAL "CommandBuffer";
DCHECK(item.metric_size == MetricSize::kLarge);
} else {
uma_name = base::StrCat(
{EXPERIMENTAL_UMA_PREFIX, HistogramProcessTypeToString(process_type),
MetricSizeToVersionSuffix(item.metric_size), item.uma_name});
}
switch (item.metric_size) {
case MetricSize::kPercentage:
base::UmaHistogramPercentage(uma_name, value);
break;
case MetricSize::kLarge: // 1 - 64,000 MiB
MEMORY_METRICS_HISTOGRAM_MB(uma_name, value / kMiB);
break;
case MetricSize::kSmall: // 10 - 500,000 KiB
base::UmaHistogramCustomCounts(uma_name, value / kKiB, 10, 500000, 100);
break;
case MetricSize::kTiny: // 1 - 500,000 bytes
base::UmaHistogramCustomCounts(uma_name, value, 1, 500000, 100);
break;
case MetricSize::kCustom:
base::UmaHistogramCustomCounts(uma_name, value, item.range.min,
item.range.max, 100);
break;
}
}
void EmitPartitionAllocFragmentationStat(
const GlobalMemoryDump::ProcessDump& pmd,
HistogramProcessType process_type,
const char* dump_name,
const char* uma_name) {
std::optional<uint64_t> value = pmd.GetMetric(dump_name, "fragmentation");
if (value.has_value()) {
Metric fragmentation_metric = {dump_name,
uma_name,
MetricSize::kPercentage,
"fragmentation",
EmitTo::kSizeInUmaOnly,
nullptr};
EmitProcessUma(process_type, fragmentation_metric, value.value());
}
}
void EmitPartitionAllocWastedStat(const GlobalMemoryDump::ProcessDump& pmd,
HistogramProcessType process_type,
const char* dump_name,
const char* uma_name) {
std::optional<uint64_t> value = pmd.GetMetric(dump_name, "wasted");
if (value.has_value()) {
Metric wasted_metric = {dump_name,
uma_name,
MetricSize::kLarge,
"wasted",
EmitTo::kSizeInUmaOnly,
nullptr};
EmitProcessUma(process_type, wasted_metric, value.value());
}
}
void EmitMallocStats(const GlobalMemoryDump::ProcessDump& pmd,
HistogramProcessType process_type,
const std::optional<base::TimeDelta>& uptime) {
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
const char* const kMallocDumpName = "malloc/partitions/allocator";
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
static constexpr int kRecordHours[] = {1, 24};
// First element of the pair is the name as found in memory dumps, second
// element is the corresponding name to emit in UMA.
static constexpr std::pair<const char*, const char*> kPartitionNames[] = {
{"array_buffer", "ArrayBuffer"},
{"buffer", "Buffer"},
{"fast_malloc", "FastMalloc"},
{"layout", "Layout"}};
for (int hours : kRecordHours) {
if (uptime <= base::Hours(hours))
continue;
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
EmitPartitionAllocFragmentationStat(
pmd, process_type, kMallocDumpName,
base::StringPrintf("Malloc.Fragmentation.After%dH", hours).c_str());
EmitPartitionAllocWastedStat(
pmd, process_type, kMallocDumpName,
base::StringPrintf("Malloc.Wasted.After%dH", hours).c_str());
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
for (const auto& partition_name : kPartitionNames) {
const auto* dump_name = partition_name.first;
const auto* uma_name = partition_name.second;
EmitPartitionAllocFragmentationStat(
pmd, process_type, dump_name,
base::StringPrintf("PartitionAlloc.Fragmentation.%s.After%dH",
uma_name, hours)
.c_str());
EmitPartitionAllocWastedStat(
pmd, process_type, dump_name,
base::StringPrintf("PartitionAlloc.Wasted.%s.After%dH", uma_name,
hours)
.c_str());
}
}
}
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
void EmitPartitionAllocAddressSpaceStatVariants(
const Metric& metric,
const uint64_t metric_value,
HistogramProcessType process_type,
const std::optional<base::TimeDelta>& uptime) {
// Emit the bare metric.
EmitProcessUma(process_type, metric, metric_value);
// These address space stats also come in variants for "after 1H"
// and "after 24H." If the time is right, we emit those too.
if (!uptime.has_value()) {
return;
}
static constexpr int kRecordHours[] = {1, 24};
for (int hours : kRecordHours) {
if (uptime.value() <= base::Hours(hours)) {
continue;
}
const std::string uma_name_with_time =
base::StringPrintf("%s.After%dH", metric.uma_name, hours);
EmitProcessUma(process_type,
// Lazily populated only with applicable members.
Metric{
.uma_name = uma_name_with_time.c_str(),
.metric_size = metric.metric_size,
},
metric_value);
}
}
void EmitPartitionAllocAddressSpaceStats(
const GlobalMemoryDump::ProcessDump& pmd,
HistogramProcessType process_type,
const std::optional<base::TimeDelta>& uptime) {
for (const auto& metric : kPartitionAllocAddressSpaceMetrics) {
std::optional<uint64_t> metric_value =
pmd.GetMetric("partition_alloc/address_space", metric.metric);
if (!metric_value.has_value()) {
continue;
}
EmitPartitionAllocAddressSpaceStatVariants(metric, metric_value.value(),
process_type, uptime);
}
}
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
void EmitProcessUmaAndUkm(const GlobalMemoryDump::ProcessDump& pmd,
HistogramProcessType process_type,
const std::optional<base::TimeDelta>& uptime,
bool record_uma,
Memory_Experimental* builder) {
for (const auto& item : kAllocatorDumpNamesForMetrics) {
std::optional<uint64_t> value = pmd.GetMetric(item.dump_name, item.metric);
if (!value)
continue;
switch (item.target) {
case EmitTo::kCountsInUkmOnly:
EmitProcessUkm(item, value.value(), builder);
break;
case EmitTo::kCountsInUkmAndSizeInUma:
EmitProcessUkm(item, value.value(), builder);
if (record_uma)
EmitProcessUma(process_type, item, value.value());
break;
case EmitTo::kSizeInUmaOnly:
if (record_uma)
EmitProcessUma(process_type, item, value.value());
break;
case EmitTo::kSizeInUkmAndUma:
// For each 'size' metric, emit size as MB.
EmitProcessUkm(item, value.value() / kMiB, builder);
if (record_uma)
EmitProcessUma(process_type, item, value.value());
break;
case EmitTo::kIgnored:
break;
default:
NOTREACHED();
}
}
// Resident set is not populated on Mac.
builder->SetResident(pmd.os_dump().resident_set_kb / kKiB);
builder->SetPrivateMemoryFootprint(pmd.os_dump().private_footprint_kb / kKiB);
builder->SetSharedMemoryFootprint(pmd.os_dump().shared_footprint_kb / kKiB);
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
builder->SetPrivateSwapFootprint(pmd.os_dump().private_footprint_swap_kb /
kKiB);
#endif
if (uptime)
builder->SetUptime(uptime.value().InSeconds());
if (!record_uma)
return;
const char* process_name = HistogramProcessTypeToString(process_type);
MEMORY_METRICS_HISTOGRAM_MB(
base::StrCat({kMemoryHistogramPrefix, process_name, ".ResidentSet"}),
pmd.os_dump().resident_set_kb / kKiB);
MEMORY_METRICS_HISTOGRAM_MB(GetPrivateFootprintHistogramName(process_type),
pmd.os_dump().private_footprint_kb / kKiB);
MEMORY_METRICS_HISTOGRAM_MB(
base::StrCat(
{kMemoryHistogramPrefix, process_name, ".SharedMemoryFootprint"}),
pmd.os_dump().shared_footprint_kb / kKiB);
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
MEMORY_METRICS_HISTOGRAM_MB(
base::StrCat(
{kMemoryHistogramPrefix, process_name, ".PrivateSwapFootprint"}),
pmd.os_dump().private_footprint_swap_kb / kKiB);
// We expect counts to be capped at ~65k on most systems, as this is the
// default maximum in the kernel.
base::UmaHistogramCounts100000(
base::StrCat({kMemoryHistogramPrefix, process_name, ".MappingsCount"}),
pmd.os_dump().mappings_count);
base::UmaHistogramMemoryMB(
base::StrCat({kMemoryHistogramPrefix, process_name, ".Pss2"}),
pmd.os_dump().pss_kb / kKiB);
base::UmaHistogramMemoryMB(
base::StrCat({kMemoryHistogramPrefix, process_name, ".SwapPss2"}),
pmd.os_dump().swap_pss_kb / kKiB);
#endif
if (record_uma) {
EmitMallocStats(pmd, process_type, uptime);
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
EmitPartitionAllocAddressSpaceStats(pmd, process_type, uptime);
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
}
}
void EmitSummedGpuMemory(const GlobalMemoryDump::ProcessDump& pmd,
Memory_Experimental* builder,
bool record_uma) {
// Combine several categories together to sum up Chrome-reported gpu memory.
static auto gpu_categories = std::to_array<const char*>({
"gpu/gl",
"gpu/shared_images",
"skia/gpu_resources",
});
Metric synthetic_metric = {nullptr,
"GpuMemory",
MetricSize::kLarge,
kEffectiveSize,
EmitTo::kSizeInUkmAndUma,
&Memory_Experimental::SetGpuMemory};
uint64_t total = 0;
for (size_t i = 0; i < std::size(gpu_categories); ++i) {
total +=
pmd.GetMetric(gpu_categories[i], synthetic_metric.metric).value_or(0);
}
// We log this metric for both the browser and GPU process, and only one will
// have entries for |gpu_categories|, so only log if |total| > 0. There should
// be almost no meaningful cases where |total| is actually zero.
if (total == 0)
return;
// Always use kGpu as the process name for this even for the in process
// command buffer case.
EmitProcessUkm(synthetic_metric, total, builder);
if (record_uma)
EmitProcessUma(HistogramProcessType::kGpu, synthetic_metric, total);
}
#if BUILDFLAG(IS_CHROMEOS)
void EmitGpuMemoryNonExo(const GlobalMemoryDump::ProcessDump& pmd,
bool record_uma) {
if (!record_uma) {
return;
}
Metric synthetic_metric = {
nullptr, "GpuMemoryNonExo", MetricSize::kLarge,
kSize, EmitTo::kSizeInUmaOnly, nullptr};
// Combine several categories together to sum up Chrome-reported gpu memory.
uint64_t total = 0;
total += pmd.GetMetric("gpu/shared_images", kNonExoSize).value_or(0);
total += pmd.GetMetric("skia/gpu_resources", kSize).value_or(0);
// We only report this metric for the GPU process, so we always use kGpu.
EmitProcessUma(HistogramProcessType::kGpu, synthetic_metric, total);
}
#endif // BUILDFLAG(IS_CHROMEOS)
void EmitBrowserMemoryMetrics(const GlobalMemoryDump::ProcessDump& pmd,
ukm::SourceId ukm_source_id,
ukm::UkmRecorder* ukm_recorder,
const std::optional<base::TimeDelta>& uptime,
bool record_uma) {
Memory_Experimental builder(ukm_source_id);
builder.SetProcessType(static_cast<int64_t>(
memory_instrumentation::mojom::ProcessType::BROWSER));
EmitProcessUmaAndUkm(pmd, HistogramProcessType::kBrowser, uptime, record_uma,
&builder);
EmitSummedGpuMemory(pmd, &builder, record_uma);
builder.Record(ukm_recorder);
}
void EmitRendererMemoryMetrics(
const GlobalMemoryDump::ProcessDump& pmd,
const ProcessMemoryMetricsEmitter::PageInfo* page_info,
ukm::UkmRecorder* ukm_recorder,
int number_of_extensions,
const std::optional<base::TimeDelta>& uptime,
bool record_uma) {
// If the renderer doesn't host a single page, no page_info will be passed in,
// and there's no single URL to associate its memory with.
ukm::SourceId ukm_source_id =
page_info ? page_info->ukm_source_id : ukm::NoURLSourceId();
Memory_Experimental builder(ukm_source_id);
builder.SetProcessType(static_cast<int64_t>(
memory_instrumentation::mojom::ProcessType::RENDERER));
builder.SetNumberOfExtensions(number_of_extensions);
const HistogramProcessType process_type =
(number_of_extensions == 0) ? HistogramProcessType::kRenderer
: HistogramProcessType::kExtension;
EmitProcessUmaAndUkm(pmd, process_type, uptime, record_uma, &builder);
if (page_info) {
builder.SetIsVisible(page_info->is_visible);
builder.SetTimeSinceLastVisibilityChange(
page_info->time_since_last_visibility_change.InSeconds());
builder.SetTimeSinceLastNavigation(
page_info->time_since_last_navigation.InSeconds());
}
builder.Record(ukm_recorder);
}
void EmitGpuMemoryMetrics(const GlobalMemoryDump::ProcessDump& pmd,
ukm::SourceId ukm_source_id,
ukm::UkmRecorder* ukm_recorder,
const std::optional<base::TimeDelta>& uptime,
bool record_uma) {
Memory_Experimental builder(ukm_source_id);
builder.SetProcessType(
static_cast<int64_t>(memory_instrumentation::mojom::ProcessType::GPU));
EmitProcessUmaAndUkm(pmd, HistogramProcessType::kGpu, uptime, record_uma,
&builder);
EmitSummedGpuMemory(pmd, &builder, record_uma);
#if BUILDFLAG(IS_CHROMEOS)
EmitGpuMemoryNonExo(pmd, record_uma);
#endif
builder.Record(ukm_recorder);
}
void EmitUtilityMemoryMetrics(HistogramProcessType ptype,
const GlobalMemoryDump::ProcessDump& pmd,
ukm::SourceId ukm_source_id,
ukm::UkmRecorder* ukm_recorder,
const std::optional<base::TimeDelta>& uptime,
bool record_uma) {
Memory_Experimental builder(ukm_source_id);
builder.SetProcessType(static_cast<int64_t>(
memory_instrumentation::mojom::ProcessType::UTILITY));
EmitProcessUmaAndUkm(pmd, ptype, uptime, record_uma, &builder);
builder.Record(ukm_recorder);
}
#if BUILDFLAG(IS_ANDROID)
// Return the base::android::ChildBindingState if the process with `pid` is a
// renderer. If the `pid` is not in the list of live renderers it is assumed to
// be unbound. If the `process_type` is not for a renderer return nullopt.
std::optional<base::android::ChildBindingState>
GetAndroidRendererProcessBindingState(
memory_instrumentation::mojom::ProcessType process_type,
base::ProcessId pid) {
if (process_type != memory_instrumentation::mojom::ProcessType::RENDERER) {
return std::nullopt;
}
for (auto iter = content::RenderProcessHost::AllHostsIterator();
!iter.IsAtEnd(); iter.Advance()) {
if (!iter.GetCurrentValue()->GetProcess().IsValid())
continue;
if (iter.GetCurrentValue()->GetProcess().Pid() == pid) {
return iter.GetCurrentValue()->GetEffectiveChildBindingState();
}
}
// This can occur if the process no longer exists. Specifically, it is
// possible a memory dump was requested, but the process was killed before
// reaching this point so we cannot check its status. Treat as UNBOUND.
return base::android::ChildBindingState::UNBOUND;
}
#endif // BUILDFLAG(IS_ANDROID)
} // namespace
ProcessMemoryMetricsEmitter::ProcessMemoryMetricsEmitter()
: pid_scope_(base::kNullProcessId) {}
ProcessMemoryMetricsEmitter::ProcessMemoryMetricsEmitter(
base::ProcessId pid_scope)
: pid_scope_(pid_scope) {}
void ProcessMemoryMetricsEmitter::FetchAndEmitProcessMemoryMetrics() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// https://crbug.com/330751658 (hopefully temporary).
#if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_X86_64)
// Do not skip when command-line is used to specifically test it.
if (base::GetPageSize() == 16 * 1024) {
const base::CommandLine* command_line =
base::CommandLine::ForCurrentProcess();
int test_delay_in_minutes = 0;
if (command_line->HasSwitch(switches::kTestMemoryLogDelayInMinutes)) {
base::StringToInt(command_line->GetSwitchValueASCII(
switches::kTestMemoryLogDelayInMinutes),
&test_delay_in_minutes);
}
// Allow a negative value to test the crashing scenario.
if (test_delay_in_minutes >= 0) {
LOG(WARNING) << "Ignoring dump request to avoid emulator crash. "
<< "https://crbug.com/330751658";
return;
}
}
#endif
MarkServiceRequestsInProgress();
auto* instrumentation =
memory_instrumentation::MemoryInstrumentation::GetInstance();
// nullptr means content layer is not initialized yet (there's no memory
// metrics to log in this case)
if (instrumentation) {
// The callback keeps this object alive until the callback is invoked.
auto callback =
base::BindOnce(&ProcessMemoryMetricsEmitter::ReceivedMemoryDump, this);
std::vector<std::string> mad_list;
for (const auto& metric : kAllocatorDumpNamesForMetrics)
mad_list.push_back(metric.dump_name);
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
mad_list.push_back("partition_alloc/address_space");
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
if (pid_scope_ != base::kNullProcessId) {
instrumentation->RequestGlobalDumpForPid(pid_scope_, mad_list,
std::move(callback));
} else {
instrumentation->RequestGlobalDump(mad_list, std::move(callback));
}
}
performance_manager::Graph* graph =
performance_manager::PerformanceManager::GetGraph();
auto process_infos = GetProcessToPageInfoMap(graph);
ReceivedProcessInfos(std::move(process_infos));
}
void ProcessMemoryMetricsEmitter::MarkServiceRequestsInProgress() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
memory_dump_in_progress_ = true;
get_process_urls_in_progress_ = true;
}
ProcessMemoryMetricsEmitter::~ProcessMemoryMetricsEmitter() = default;
void ProcessMemoryMetricsEmitter::ReceivedMemoryDump(
bool success,
std::unique_ptr<GlobalMemoryDump> dump) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
memory_dump_in_progress_ = false;
if (!success)
return;
global_dump_ = std::move(dump);
CollateResults();
}
void ProcessMemoryMetricsEmitter::ReceivedProcessInfos(
std::vector<ProcessInfo> process_infos) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
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 (ProcessInfo& process_info : process_infos) {
base::ProcessId pid = process_info.pid;
process_infos_[pid] = std::move(process_info);
}
CollateResults();
}
ukm::UkmRecorder* ProcessMemoryMetricsEmitter::GetUkmRecorder() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return ukm::UkmRecorder::Get();
}
int ProcessMemoryMetricsEmitter::GetNumberOfExtensions(base::ProcessId pid) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#if BUILDFLAG(ENABLE_EXTENSIONS)
// Retrieve the renderer process host for the given pid.
content::RenderProcessHost* rph = nullptr;
for (auto iter = content::RenderProcessHost::AllHostsIterator();
!iter.IsAtEnd(); iter.Advance()) {
if (!iter.GetCurrentValue()->GetProcess().IsValid())
continue;
if (iter.GetCurrentValue()->GetProcess().Pid() == pid) {
rph = iter.GetCurrentValue();
break;
}
}
if (!rph) {
return 0;
}
// Count the number of extensions associated with this `rph`'s profile.
extensions::ProcessMap* process_map =
extensions::ProcessMap::Get(rph->GetBrowserContext());
if (!process_map) {
return 0;
}
const extensions::Extension* extension =
process_map->GetEnabledExtensionByProcessID(rph->GetDeprecatedID());
// Only include this extension if it's not a hosted app.
return (extension && !extension->is_hosted_app()) ? 1 : 0;
#else
return 0;
#endif
}
std::optional<base::TimeDelta> ProcessMemoryMetricsEmitter::GetProcessUptime(
base::TimeTicks now,
base::ProcessId pid) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto process_info = process_infos_.find(pid);
if (process_info != process_infos_.end()) {
if (!process_info->second.launch_time.is_null())
return now - process_info->second.launch_time;
}
return std::optional<base::TimeDelta>();
}
void ProcessMemoryMetricsEmitter::CollateResults() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (memory_dump_in_progress_ || get_process_urls_in_progress_)
return;
// The memory dump can be done, yet |global_dump_| not set if:
// - Process metrics collection fails first.
// - Process Infos arrive later.
if (!global_dump_)
return;
uint32_t private_footprint_total_kb = 0;
#if BUILDFLAG(IS_ANDROID)
uint32_t private_swap_footprint_total_kb = 0;
uint32_t renderer_private_swap_footprint_total_kb = 0;
uint32_t private_footprint_excluding_waived_total_kb = 0;
uint32_t renderer_private_footprint_excluding_waived_total_kb = 0;
uint32_t private_footprint_visible_or_higher_total_kb = 0;
uint32_t renderer_private_footprint_visible_or_higher_total_kb = 0;
#endif // BUILDFLAG(IS_ANDROID)
uint32_t renderer_private_footprint_total_kb = 0;
uint32_t renderer_malloc_total_kb = 0;
uint32_t renderer_blink_gc_total_kb = 0;
uint32_t renderer_blink_gc_fragmentation_total_kb = 0;
uint32_t shared_footprint_total_kb = 0;
uint32_t resident_set_total_kb = 0;
uint64_t tiles_total_memory = 0;
uint64_t hibernated_canvas_total_memory = 0;
uint64_t hibernated_canvas_total_original_memory = 0;
uint64_t gpu_mapped_memory_total = 0;
bool emit_metrics_for_all_processes = pid_scope_ == base::kNullProcessId;
TabFootprintAggregator per_tab_metrics;
base::TimeTicks now = base::TimeTicks::Now();
for (const auto& pmd : global_dump_->process_dumps()) {
uint32_t process_pmf_kb = pmd.os_dump().private_footprint_kb;
private_footprint_total_kb += process_pmf_kb;
#if BUILDFLAG(IS_ANDROID)
uint32_t process_swap_kb = pmd.os_dump().private_footprint_swap_kb;
private_swap_footprint_total_kb += process_swap_kb;
bool is_waived_renderer = false;
bool is_less_than_visible_renderer = false;
auto renderer_binding_state_android =
GetAndroidRendererProcessBindingState(pmd.process_type(), pmd.pid());
if (renderer_binding_state_android) {
// Also exclude base::android::ChildBindingState::UNBOUND which can occur
// as the state change can be racy.
is_waived_renderer = *renderer_binding_state_android <=
base::android::ChildBindingState::WAIVED;
is_less_than_visible_renderer = *renderer_binding_state_android <
base::android::ChildBindingState::VISIBLE;
}
private_footprint_excluding_waived_total_kb +=
is_waived_renderer ? 0 : process_pmf_kb;
private_footprint_visible_or_higher_total_kb +=
is_less_than_visible_renderer ? 0 : process_pmf_kb;
#endif // BUILDFLAG(IS_ANDROID)
shared_footprint_total_kb += pmd.os_dump().shared_footprint_kb;
resident_set_total_kb += pmd.os_dump().resident_set_kb;
if (!emit_metrics_for_all_processes && pid_scope_ != pmd.pid())
continue;
switch (pmd.process_type()) {
case memory_instrumentation::mojom::ProcessType::BROWSER: {
EmitBrowserMemoryMetrics(
pmd, ukm::UkmRecorder::GetNewSourceID(), GetUkmRecorder(),
GetProcessUptime(now, pmd.pid()), emit_metrics_for_all_processes);
break;
}
case memory_instrumentation::mojom::ProcessType::RENDERER: {
renderer_private_footprint_total_kb += process_pmf_kb;
#if BUILDFLAG(IS_ANDROID)
renderer_private_swap_footprint_total_kb += process_swap_kb;
renderer_private_footprint_excluding_waived_total_kb +=
is_waived_renderer ? 0 : process_pmf_kb;
renderer_private_footprint_visible_or_higher_total_kb +=
is_less_than_visible_renderer ? 0 : process_pmf_kb;
#endif // BUILDFLAG(IS_ANDROID)
hibernated_canvas_total_memory +=
pmd.GetMetric("canvas/hibernated", kSize).value_or(0);
hibernated_canvas_total_original_memory +=
pmd.GetMetric("canvas/hibernated", "original_size").value_or(0);
const PageInfo* single_page_info = nullptr;
auto iter = process_infos_.find(pmd.pid());
if (iter != process_infos_.end()) {
const ProcessInfo& process_info = iter->second;
if (emit_metrics_for_all_processes) {
// Renderer metrics-by-tab only make sense if we're visiting all
// render processes.
for (const PageInfo& page_info : process_info.page_infos) {
if (page_info.hosts_main_frame) {
per_tab_metrics.AssociateMainFrame(page_info.ukm_source_id,
pmd.pid(), page_info.tab_id,
process_pmf_kb);
} else {
per_tab_metrics.AssociateSubFrame(page_info.ukm_source_id,
pmd.pid(), page_info.tab_id,
process_pmf_kb);
}
}
}
// If there is more than one tab being hosted in a renderer, don't
// emit certain data. This is not ideal, but UKM does not support
// multiple-URLs per entry, and we must have one entry per process.
if (process_info.page_infos.size() == 1) {
single_page_info = &process_info.page_infos[0];
}
}
// Sum malloc memory from all renderers.
renderer_malloc_total_kb +=
pmd.GetMetric("malloc", "effective_size").value_or(0) / kKiB;
// Sum Blink memory from all renderers.
const uint64_t blink_gc_bytes =
pmd.GetMetric("blink_gc", kEffectiveSize).value_or(0);
const uint64_t blink_gc_allocated_objects_bytes =
pmd.GetMetric("blink_gc", kAllocatedObjectsSize).value_or(0);
renderer_blink_gc_total_kb += blink_gc_bytes / kKiB;
renderer_blink_gc_fragmentation_total_kb +=
(blink_gc_bytes - blink_gc_allocated_objects_bytes) / kKiB;
int number_of_extensions = GetNumberOfExtensions(pmd.pid());
EmitRendererMemoryMetrics(
pmd, single_page_info, GetUkmRecorder(), number_of_extensions,
GetProcessUptime(now, pmd.pid()), emit_metrics_for_all_processes);
break;
}
case memory_instrumentation::mojom::ProcessType::GPU: {
EmitGpuMemoryMetrics(pmd, ukm::UkmRecorder::GetNewSourceID(),
GetUkmRecorder(), GetProcessUptime(now, pmd.pid()),
emit_metrics_for_all_processes);
break;
}
case memory_instrumentation::mojom::ProcessType::UTILITY: {
HistogramProcessType ptype;
if (pmd.pid() == content::GetProcessIdForAudioService()) {
ptype = HistogramProcessType::kAudioService;
} else if (pmd.service_name() ==
media::mojom::CdmServiceBroker::Name_) {
ptype = HistogramProcessType::kCdmService;
#if BUILDFLAG(IS_WIN)
} else if (pmd.service_name() ==
media::mojom::MediaFoundationServiceBroker::Name_) {
ptype = HistogramProcessType::kMediaFoundationService;
#endif
} else if (pmd.service_name() ==
network::mojom::NetworkService::Name_) {
ptype = HistogramProcessType::kNetworkService;
} else if (pmd.service_name() ==
paint_preview::mojom::PaintPreviewCompositorCollection::
Name_) {
ptype = HistogramProcessType::kPaintPreviewCompositor;
} else {
ptype = HistogramProcessType::kUtility;
}
EmitUtilityMemoryMetrics(
ptype, pmd, ukm::UkmRecorder::GetNewSourceID(), GetUkmRecorder(),
GetProcessUptime(now, pmd.pid()), emit_metrics_for_all_processes);
break;
}
case memory_instrumentation::mojom::ProcessType::PLUGIN:
[[fallthrough]];
case memory_instrumentation::mojom::ProcessType::ARC:
[[fallthrough]];
case memory_instrumentation::mojom::ProcessType::OTHER:
break;
}
if (emit_metrics_for_all_processes) {
// cc/ has clients in all process types. Careful though about
// double-counting: in the GPU process a lot of the memory is allocated on
// behalf of other process types, so the size vs effective_size
// distinction matters there.
//
// Not using effective size as tiles are not shared across processes, but
// they are shared with the GPU process (under a different name), and we
// don't want to count these partially if priority is not set right.
tiles_total_memory += pmd.GetMetric("cc/tile_memory", kSize).value_or(0);
gpu_mapped_memory_total +=
pmd.GetMetric("gpu/mapped_memory", kSize).value_or(0);
}
}
if (emit_metrics_for_all_processes) {
const auto& metrics = global_dump_->aggregated_metrics();
int32_t native_resident_kb = metrics.native_library_resident_kb();
int32_t native_library_resident_not_ordered_kb =
metrics.native_library_resident_not_ordered_kb();
int32_t native_library_not_resident_ordered_kb =
metrics.native_library_not_resident_ordered_kb();
// |native_resident_kb| is only calculated for android devices that support
// code ordering.
if (native_resident_kb != metrics.kInvalid) {
// Size of the native library on android is ~40MB.
// More precision is needed in the middle buckets, hence the range.
base::UmaHistogramCustomCounts(
"Memory.NativeLibrary.MappedAndResidentMemoryFootprint3",
native_resident_kb, 1000, 100000, 100);
if (native_library_not_resident_ordered_kb != metrics.kInvalid) {
base::UmaHistogramCustomCounts(
"Memory.NativeLibrary.NotResidentOrderedCodeMemoryFootprint",
native_library_not_resident_ordered_kb, 1000, 100000, 100);
}
if (native_library_resident_not_ordered_kb != metrics.kInvalid) {
base::UmaHistogramCustomCounts(
"Memory.NativeLibrary.ResidentNotOrderedCodeMemoryFootprint",
native_library_resident_not_ordered_kb, 1000, 100000, 100);
}
}
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.ResidentSet",
resident_set_total_kb / kKiB);
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.PrivateMemoryFootprint",
private_footprint_total_kb / kKiB);
// The pseudo metric of Memory.Total.PrivateMemoryFootprint. Only used to
// assess field trial data quality.
UMA_HISTOGRAM_MEMORY_LARGE_MB(
"UMA.Pseudo.Memory.Total.PrivateMemoryFootprint",
metrics::GetPseudoMetricsSample(
static_cast<double>(private_footprint_total_kb) / kKiB));
#if BUILDFLAG(IS_ANDROID)
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.PrivateSwapFootprint",
private_swap_footprint_total_kb / kKiB);
UMA_HISTOGRAM_MEMORY_LARGE_MB(
"Memory.Total.RendererPrivateSwapFootprint",
renderer_private_swap_footprint_total_kb / kKiB);
#endif // BUILDFLAG(IS_ANDROID)
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.RendererPrivateMemoryFootprint",
renderer_private_footprint_total_kb / kKiB);
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.RendererMalloc",
renderer_malloc_total_kb / kKiB);
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.RendererBlinkGC",
renderer_blink_gc_total_kb / kKiB);
UMA_HISTOGRAM_MEMORY_LARGE_MB(
"Memory.Total.RendererBlinkGC.Fragmentation",
renderer_blink_gc_fragmentation_total_kb / kKiB);
UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.SharedMemoryFootprint",
shared_footprint_total_kb / kKiB);
UMA_HISTOGRAM_MEMORY_MEDIUM_MB("Memory.Total.TileMemory",
tiles_total_memory / kMiB);
UMA_HISTOGRAM_MEMORY_MEDIUM_MB("Memory.Total.GpuMappedMemory",
gpu_mapped_memory_total / kMiB);
#if BUILDFLAG(IS_ANDROID)
UMA_HISTOGRAM_MEMORY_LARGE_MB(
"Memory.Total.PrivateMemoryFootprintExcludingWaivedRenderers",
private_footprint_excluding_waived_total_kb / kKiB);
UMA_HISTOGRAM_MEMORY_LARGE_MB(
"Memory.Total.RendererPrivateMemoryFootprintExcludingWaived",
renderer_private_footprint_excluding_waived_total_kb / kKiB);
UMA_HISTOGRAM_MEMORY_LARGE_MB(
"Memory.Total.PrivateMemoryFootprintVisibleOrHigherPriorityRenderers",
private_footprint_visible_or_higher_total_kb / kKiB);
UMA_HISTOGRAM_MEMORY_LARGE_MB(
"Memory.Total.RendererPrivateMemoryFootprintVisibleOrHigherPriority",
renderer_private_footprint_visible_or_higher_total_kb / kKiB);
#endif
UMA_HISTOGRAM_MEMORY_MEDIUM_MB("Memory.Total.HibernatedCanvas.Size",
hibernated_canvas_total_memory / kMiB);
UMA_HISTOGRAM_MEMORY_MEDIUM_MB(
"Memory.Total.HibernatedCanvas.OriginalSize",
hibernated_canvas_total_original_memory / kMiB);
Memory_Experimental(ukm::UkmRecorder::GetNewSourceID())
.SetTotal2_PrivateMemoryFootprint(private_footprint_total_kb / kKiB)
.SetTotal2_SharedMemoryFootprint(shared_footprint_total_kb / kKiB)
.Record(GetUkmRecorder());
// Renderer metrics-by-tab only make sense if we're visiting all render
// processes.
per_tab_metrics.RecordPmfs(GetUkmRecorder());
#if BUILDFLAG(IS_CHROMEOS)
base::SystemMemoryInfo system_meminfo;
if (base::GetSystemMemoryInfo(&system_meminfo)) {
int64_t mem_used_mb =
(system_meminfo.total - system_meminfo.available).InMiB();
UMA_HISTOGRAM_LARGE_MEMORY_MB("Memory.System.MemAvailableMB",
system_meminfo.available.InMiB());
UMA_HISTOGRAM_LARGE_MEMORY_MB("Memory.System.MemUsedMB", mem_used_mb);
}
#endif
}
global_dump_ = nullptr;
}
namespace {
// Returns true iff the given |process| is responsible for hosting the
// main-frame of the given |page|.
bool HostsMainFrame(const performance_manager::ProcessNode* process,
const performance_manager::PageNode* page) {
const performance_manager::FrameNode* main_frame = page->GetMainFrameNode();
if (main_frame == nullptr) {
// |process| can't host a frame that doesn't exist.
return false;
}
return main_frame->GetProcessNode() == process;
}
} // namespace
std::vector<ProcessMemoryMetricsEmitter::ProcessInfo>
ProcessMemoryMetricsEmitter::GetProcessToPageInfoMap(
performance_manager::Graph* graph) {
std::vector<ProcessInfo> process_infos;
// Assign page nodes unique IDs within this lookup only.
base::flat_map<const performance_manager::PageNode*, uint64_t> page_id_map;
for (const performance_manager::ProcessNode* process_node :
graph->GetAllProcessNodes()) {
if (process_node->GetProcessId() == base::kNullProcessId)
continue;
// First add all processes and their basic information.
ProcessInfo& process_info = process_infos.emplace_back();
process_info.pid = process_node->GetProcessId();
process_info.launch_time = process_node->GetLaunchTime();
// Then add information about their associated page nodes. Only renderers
// are associated with page nodes.
if (process_node->GetProcessType() != content::PROCESS_TYPE_RENDERER) {
continue;
}
base::flat_set<const performance_manager::PageNode*> page_nodes =
performance_manager::GraphOperations::GetAssociatedPageNodes(
process_node);
const base::TimeTicks now = base::TimeTicks::Now();
for (const performance_manager::PageNode* page_node : page_nodes) {
if (page_node->GetUkmSourceID() == ukm::kInvalidSourceId)
continue;
// Get or generate the tab id.
uint64_t& tab_id = page_id_map[page_node];
if (tab_id == 0u) {
// 0 is an invalid id, meaning `page_node` was just inserted in
// `page_id_map` and its tab id must be generated.
tab_id = page_id_map.size();
}
PageInfo& page_info = process_info.page_infos.emplace_back();
page_info.ukm_source_id = page_node->GetUkmSourceID();
page_info.tab_id = tab_id;
page_info.hosts_main_frame = HostsMainFrame(process_node, page_node);
page_info.is_visible = page_node->IsVisible();
page_info.time_since_last_visibility_change =
now - page_node->GetLastVisibilityChangeTime();
page_info.time_since_last_navigation =
page_node->GetTimeSinceLastNavigation();
}
}
return process_infos;
}
ProcessMemoryMetricsEmitter::ProcessInfo::ProcessInfo() = default;
ProcessMemoryMetricsEmitter::ProcessInfo::ProcessInfo(ProcessInfo&& other) =
default;
ProcessMemoryMetricsEmitter::ProcessInfo::~ProcessInfo() = default;
ProcessMemoryMetricsEmitter::ProcessInfo&
ProcessMemoryMetricsEmitter::ProcessInfo::operator=(const ProcessInfo& other) =
default;