| // 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 <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/process/process_handle.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "build/build_config.h" |
| #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h" |
| #include "components/ukm/test_ukm_recorder.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "services/metrics/public/cpp/ukm_builders.h" |
| #include "services/metrics/public/cpp/ukm_recorder.h" |
| #include "services/resource_coordinator/public/cpp/memory_instrumentation/browser_metrics.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using GlobalMemoryDump = memory_instrumentation::GlobalMemoryDump; |
| using GlobalMemoryDumpPtr = memory_instrumentation::mojom::GlobalMemoryDumpPtr; |
| using HistogramProcessType = memory_instrumentation::HistogramProcessType; |
| using ProcessMemoryDumpPtr = |
| memory_instrumentation::mojom::ProcessMemoryDumpPtr; |
| using OSMemDumpPtr = memory_instrumentation::mojom::OSMemDumpPtr; |
| using PageInfo = ProcessMemoryMetricsEmitter::PageInfo; |
| using ProcessType = memory_instrumentation::mojom::ProcessType; |
| using ProcessInfo = ProcessMemoryMetricsEmitter::ProcessInfo; |
| using ProcessInfoVector = std::vector<ProcessInfo>; |
| |
| namespace { |
| |
| using UkmEntry = ukm::builders::Memory_Experimental; |
| |
| using MetricMap = base::flat_map<const char*, int64_t>; |
| |
| int GetResidentValue(const MetricMap& metric_map) { |
| #if defined(OS_MAC) |
| // Resident set is not populated on Mac. |
| return 0; |
| #else |
| auto it = metric_map.find("Resident"); |
| EXPECT_NE(it, metric_map.end()); |
| return it->second; |
| #endif |
| } |
| |
| // Provide fake to surface ReceivedMemoryDump and ReceivedProcessInfos to public |
| // visibility. |
| class ProcessMemoryMetricsEmitterFake : public ProcessMemoryMetricsEmitter { |
| public: |
| ProcessMemoryMetricsEmitterFake( |
| ukm::TestAutoSetUkmRecorder& test_ukm_recorder) |
| : ukm_recorder_(&test_ukm_recorder) { |
| MarkServiceRequestsInProgress(); |
| } |
| |
| void ReceivedMemoryDump(bool success, |
| std::unique_ptr<GlobalMemoryDump> ptr) override { |
| ProcessMemoryMetricsEmitter::ReceivedMemoryDump(success, std::move(ptr)); |
| } |
| |
| void ReceivedProcessInfos(ProcessInfoVector process_infos) override { |
| ProcessMemoryMetricsEmitter::ReceivedProcessInfos(std::move(process_infos)); |
| } |
| |
| ukm::UkmRecorder* GetUkmRecorder() override { return ukm_recorder_; } |
| |
| int GetNumberOfExtensions(base::ProcessId pid) override { |
| switch (pid) { |
| case 401: |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| |
| absl::optional<base::TimeDelta> GetProcessUptime( |
| const base::Time& now, |
| base::ProcessId pid) override { |
| switch (pid) { |
| case 401: |
| return base::TimeDelta::FromSeconds(21); |
| default: |
| return base::TimeDelta::FromSeconds(42); |
| } |
| } |
| |
| private: |
| ~ProcessMemoryMetricsEmitterFake() override {} |
| |
| ukm::UkmRecorder* ukm_recorder_; |
| DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMetricsEmitterFake); |
| }; |
| |
| void SetAllocatorDumpMetric(ProcessMemoryDumpPtr& pmd, |
| const std::string& dump_name, |
| const std::string& metric_name, |
| uint64_t value) { |
| auto it = pmd->chrome_allocator_dumps.find(dump_name); |
| if (it == pmd->chrome_allocator_dumps.end()) { |
| memory_instrumentation::mojom::AllocatorMemDumpPtr amd( |
| memory_instrumentation::mojom::AllocatorMemDump::New()); |
| amd->numeric_entries.insert(std::make_pair(metric_name, value)); |
| pmd->chrome_allocator_dumps.insert( |
| std::make_pair(dump_name, std::move(amd))); |
| } else { |
| it->second->numeric_entries.insert(std::make_pair(metric_name, value)); |
| } |
| } |
| |
| OSMemDumpPtr GetFakeOSMemDump(uint32_t resident_set_kb, |
| uint32_t private_footprint_kb, |
| uint32_t shared_footprint_kb |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| , |
| uint32_t private_swap_footprint_kb |
| #endif |
| ) { |
| using memory_instrumentation::mojom::VmRegion; |
| |
| return memory_instrumentation::mojom::OSMemDump::New( |
| resident_set_kb, resident_set_kb /* peak_resident_set_kb */, |
| true /* is_peak_rss_resettable */, private_footprint_kb, |
| shared_footprint_kb |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| , |
| private_swap_footprint_kb |
| #endif |
| ); |
| } |
| |
| constexpr uint64_t kGpuSharedImagesSizeMB = 32; |
| constexpr uint64_t kGpuSkiaGpuResourcesMB = 87; |
| constexpr uint64_t kGpuCommandBufferMB = 240; |
| constexpr uint64_t kGpuTotalMemory = |
| kGpuCommandBufferMB + kGpuSharedImagesSizeMB + kGpuSkiaGpuResourcesMB; |
| |
| void PopulateBrowserMetrics(GlobalMemoryDumpPtr& global_dump, |
| MetricMap& metrics_mb) { |
| ProcessMemoryDumpPtr pmd( |
| memory_instrumentation::mojom::ProcessMemoryDump::New()); |
| pmd->process_type = ProcessType::BROWSER; |
| SetAllocatorDumpMetric(pmd, "malloc", "effective_size", |
| metrics_mb["Malloc"] * 1024 * 1024); |
| // These three categories are required for total gpu memory, but do not |
| // have a UKM value set for them, so don't appear in metrics_mb. |
| SetAllocatorDumpMetric(pmd, "gpu/gl", "effective_size", |
| kGpuCommandBufferMB * 1024 * 1024); |
| SetAllocatorDumpMetric(pmd, "gpu/shared_images", "effective_size", |
| kGpuSharedImagesSizeMB * 1024 * 1024); |
| SetAllocatorDumpMetric(pmd, "skia/gpu_resources", "effective_size", |
| kGpuSkiaGpuResourcesMB * 1024 * 1024); |
| OSMemDumpPtr os_dump = |
| GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024, |
| metrics_mb["PrivateMemoryFootprint"] * 1024, |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| // accessing PrivateSwapFootprint on other OSes will |
| // modify metrics_mb to create the value, which leads to |
| // expectation failures. |
| metrics_mb["SharedMemoryFootprint"] * 1024, |
| metrics_mb["PrivateSwapFootprint"] * 1024 |
| #else |
| metrics_mb["SharedMemoryFootprint"] * 1024 |
| #endif |
| ); |
| pmd->os_dump = std::move(os_dump); |
| global_dump->process_dumps.push_back(std::move(pmd)); |
| } |
| |
| MetricMap GetExpectedBrowserMetrics() { |
| return MetricMap({ |
| {"ProcessType", static_cast<int64_t>(ProcessType::BROWSER)}, |
| #if !defined(OS_MAC) |
| {"Resident", 10}, |
| #endif |
| {"Malloc", 20}, {"PrivateMemoryFootprint", 30}, |
| {"SharedMemoryFootprint", 35}, {"Uptime", 42}, |
| {"GpuMemory", kGpuTotalMemory * 1024 * 1024}, |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| {"PrivateSwapFootprint", 50}, |
| #endif |
| }); |
| } |
| |
| void PopulateRendererMetrics(GlobalMemoryDumpPtr& global_dump, |
| MetricMap& metrics_mb_or_count, |
| base::ProcessId pid) { |
| ProcessMemoryDumpPtr pmd( |
| memory_instrumentation::mojom::ProcessMemoryDump::New()); |
| pmd->process_type = ProcessType::RENDERER; |
| SetAllocatorDumpMetric(pmd, "malloc", "effective_size", |
| metrics_mb_or_count["Malloc"] * 1024 * 1024); |
| SetAllocatorDumpMetric(pmd, "partition_alloc", "effective_size", |
| metrics_mb_or_count["PartitionAlloc"] * 1024 * 1024); |
| SetAllocatorDumpMetric(pmd, "blink_gc", "effective_size", |
| metrics_mb_or_count["BlinkGC"] * 1024 * 1024); |
| SetAllocatorDumpMetric(pmd, "v8", "effective_size", |
| metrics_mb_or_count["V8"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8", "allocated_objects_size", |
| metrics_mb_or_count["V8.AllocatedObjects"] * 1024 * 1024); |
| |
| SetAllocatorDumpMetric(pmd, "v8/main", "effective_size", |
| metrics_mb_or_count["V8.Main"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main", "allocated_objects_size", |
| metrics_mb_or_count["V8.Main.AllocatedObjects"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/global_handles", "effective_size", |
| metrics_mb_or_count["V8.Main.GlobalHandles"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/global_handles", "allocated_objects_size", |
| metrics_mb_or_count["V8.Main.GlobalHandles.AllocatedObjects"] * 1024 * |
| 1024); |
| |
| SetAllocatorDumpMetric(pmd, "v8/main/heap", "effective_size", |
| metrics_mb_or_count["V8.Main.Heap"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap", "allocated_objects_size", |
| metrics_mb_or_count["V8.Main.Heap.AllocatedObjects"] * 1024 * 1024); |
| |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/code_large_object_space", "effective_size", |
| metrics_mb_or_count["V8.Main.Heap.CodeLargeObjectSpace"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/code_large_object_space", "allocated_objects_size", |
| metrics_mb_or_count |
| ["V8.Main.Heap.CodeLargeObjectSpace.AllocatedObjects"] * |
| 1024 * 1024); |
| |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/code_space", "effective_size", |
| metrics_mb_or_count["V8.Main.Heap.CodeSpace"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/code_space", "allocated_objects_size", |
| metrics_mb_or_count["V8.Main.Heap.CodeSpace.AllocatedObjects"] * 1024 * |
| 1024); |
| |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/large_object_space", "effective_size", |
| metrics_mb_or_count["V8.Main.Heap.LargeObjectSpace"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/large_object_space", "allocated_objects_size", |
| metrics_mb_or_count["V8.Main.Heap.LargeObjectSpace.AllocatedObjects"] * |
| 1024 * 1024); |
| |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/map_space", "effective_size", |
| metrics_mb_or_count["V8.Main.Heap.MapSpace"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/map_space", "allocated_objects_size", |
| metrics_mb_or_count["V8.Main.Heap.MapSpace.AllocatedObjects"] * 1024 * |
| 1024); |
| |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/new_large_object_space", "effective_size", |
| metrics_mb_or_count["V8.Main.Heap.NewLargeObjectSpace"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/new_large_object_space", "allocated_objects_size", |
| metrics_mb_or_count["V8.Main.Heap.NewLargeObjectSpace.AllocatedObjects"] * |
| 1024 * 1024); |
| |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/new_space", "effective_size", |
| metrics_mb_or_count["V8.Main.Heap.NewSpace"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/new_space", "allocated_objects_size", |
| metrics_mb_or_count["V8.Main.Heap.NewSpace.AllocatedObjects"] * 1024 * |
| 1024); |
| |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/old_space", "effective_size", |
| metrics_mb_or_count["V8.Main.Heap.OldSpace"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/old_space", "allocated_objects_size", |
| metrics_mb_or_count["V8.Main.Heap.OldSpace.AllocatedObjects"] * 1024 * |
| 1024); |
| |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/read_only_space", "effective_size", |
| metrics_mb_or_count["V8.Main.Heap.ReadOnlySpace"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/main/heap/read_only_space", "allocated_objects_size", |
| metrics_mb_or_count["V8.Main.Heap.ReadOnlySpace.AllocatedObjects"] * |
| 1024 * 1024); |
| |
| SetAllocatorDumpMetric(pmd, "v8/main/malloc", "effective_size", |
| metrics_mb_or_count["V8.Main.Malloc"] * 1024 * 1024); |
| |
| SetAllocatorDumpMetric(pmd, "v8/workers", "effective_size", |
| metrics_mb_or_count["V8.Workers"] * 1024 * 1024); |
| SetAllocatorDumpMetric( |
| pmd, "v8/workers", "allocated_objects_size", |
| metrics_mb_or_count["V8.Workers.AllocatedObjects"] * 1024 * 1024); |
| |
| SetAllocatorDumpMetric(pmd, "blink_objects/AdSubframe", "object_count", |
| metrics_mb_or_count["NumberOfAdSubframes"]); |
| SetAllocatorDumpMetric(pmd, "blink_objects/DetachedScriptState", |
| "object_count", |
| metrics_mb_or_count["NumberOfDetachedScriptStates"]); |
| SetAllocatorDumpMetric(pmd, "blink_objects/Document", "object_count", |
| metrics_mb_or_count["NumberOfDocuments"]); |
| SetAllocatorDumpMetric(pmd, "blink_objects/Frame", "object_count", |
| metrics_mb_or_count["NumberOfFrames"]); |
| SetAllocatorDumpMetric(pmd, "blink_objects/LayoutObject", "object_count", |
| metrics_mb_or_count["NumberOfLayoutObjects"]); |
| SetAllocatorDumpMetric(pmd, "blink_objects/Node", "object_count", |
| metrics_mb_or_count["NumberOfNodes"]); |
| SetAllocatorDumpMetric( |
| pmd, "partition_alloc/partitions/array_buffer", "size", |
| metrics_mb_or_count["PartitionAlloc.Partitions.ArrayBuffer"] * 1024 * |
| 1024); |
| |
| OSMemDumpPtr os_dump = |
| GetFakeOSMemDump(GetResidentValue(metrics_mb_or_count) * 1024, |
| metrics_mb_or_count["PrivateMemoryFootprint"] * 1024, |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| // accessing PrivateSwapFootprint on other OSes will |
| // modify metrics_mb_or_count to create the value, which |
| // leads to expectation failures. |
| metrics_mb_or_count["SharedMemoryFootprint"] * 1024, |
| metrics_mb_or_count["PrivateSwapFootprint"] * 1024 |
| #else |
| metrics_mb_or_count["SharedMemoryFootprint"] * 1024 |
| #endif |
| ); |
| pmd->os_dump = std::move(os_dump); |
| pmd->pid = pid; |
| global_dump->process_dumps.push_back(std::move(pmd)); |
| } |
| |
| constexpr int kTestRendererPrivateMemoryFootprint = 130; |
| constexpr int kTestRendererSharedMemoryFootprint = 135; |
| constexpr int kNativeLibraryResidentMemoryFootprint = 27560; |
| constexpr int kNativeLibraryResidentNotOrderedCodeFootprint = 12345; |
| constexpr int kNativeLibraryNotResidentOrderedCodeFootprint = 23456; |
| |
| #if !defined(OS_MAC) |
| constexpr int kTestRendererResidentSet = 110; |
| #endif |
| |
| constexpr base::ProcessId kTestRendererPid201 = 201; |
| constexpr base::ProcessId kTestRendererPid202 = 202; |
| constexpr base::ProcessId kTestRendererPid203 = 203; |
| |
| MetricMap GetExpectedRendererMetrics() { |
| return MetricMap({ |
| {"ProcessType", static_cast<int64_t>(ProcessType::RENDERER)}, |
| #if !defined(OS_MAC) |
| {"Resident", kTestRendererResidentSet}, |
| #endif |
| {"Malloc", 120}, |
| {"PrivateMemoryFootprint", kTestRendererPrivateMemoryFootprint}, |
| {"SharedMemoryFootprint", kTestRendererSharedMemoryFootprint}, |
| {"PartitionAlloc", 140}, {"BlinkGC", 150}, {"V8", 160}, |
| {"V8.AllocatedObjects", 70}, {"V8.Main", 100}, |
| {"V8.Main.AllocatedObjects", 30}, {"V8.Main.Heap", 98}, |
| {"V8.Main.GlobalHandles", 3}, |
| {"V8.Main.GlobalHandles.AllocatedObjects", 2}, |
| {"V8.Main.Heap.AllocatedObjects", 28}, {"V8.Main.Heap.CodeSpace", 11}, |
| {"V8.Main.Heap.CodeSpace.AllocatedObjects", 1}, |
| {"V8.Main.Heap.LargeObjectSpace", 12}, |
| {"V8.Main.Heap.LargeObjectSpace.AllocatedObjects", 2}, |
| {"V8.Main.Heap.MapSpace", 13}, |
| {"V8.Main.Heap.MapSpace.AllocatedObjects", 3}, |
| {"V8.Main.Heap.NewLargeObjectSpace", 14}, |
| {"V8.Main.Heap.NewLargeObjectSpace.AllocatedObjects", 4}, |
| {"V8.Main.Heap.NewSpace", 15}, |
| {"V8.Main.Heap.NewSpace.AllocatedObjects", 5}, |
| {"V8.Main.Heap.OldSpace", 16}, |
| {"V8.Main.Heap.NewSpace.AllocatedObjects", 6}, |
| {"V8.Main.Heap.ReadOnlySpace", 17}, |
| {"V8.Main.Heap.ReadOnlySpace.AllocatedObjects", 7}, |
| {"V8.Main.Malloc", 2}, {"V8.Workers", 60}, |
| {"V8.Workers.AllocatedObjects", 40}, {"NumberOfExtensions", 0}, |
| {"Uptime", 42}, |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| {"PrivateSwapFootprint", 50}, |
| #endif |
| {"NumberOfAdSubframes", 28}, {"NumberOfDetachedScriptStates", 11}, |
| {"NumberOfDocuments", 1}, {"NumberOfFrames", 2}, |
| {"NumberOfLayoutObjects", 5}, {"NumberOfNodes", 3}, |
| {"PartitionAlloc.Partitions.ArrayBuffer", 10}, |
| }); |
| } |
| |
| void AddPageMetrics(MetricMap& expected_metrics) { |
| expected_metrics["IsVisible"] = true; |
| expected_metrics["TimeSinceLastNavigation"] = 20; |
| expected_metrics["TimeSinceLastVisibilityChange"] = 15; |
| } |
| |
| void PopulateGpuMetrics(GlobalMemoryDumpPtr& global_dump, |
| MetricMap& metrics_mb) { |
| ProcessMemoryDumpPtr pmd( |
| memory_instrumentation::mojom::ProcessMemoryDump::New()); |
| pmd->process_type = ProcessType::GPU; |
| SetAllocatorDumpMetric(pmd, "malloc", "effective_size", |
| metrics_mb["Malloc"] * 1024 * 1024); |
| SetAllocatorDumpMetric(pmd, "gpu/gl", "effective_size", |
| metrics_mb["CommandBuffer"] * 1024 * 1024); |
| // These two categories are required for total gpu memory, but do not |
| // have a UKM value set for them, so don't appear in metrics_mb. |
| SetAllocatorDumpMetric(pmd, "gpu/shared_images", "effective_size", |
| kGpuSharedImagesSizeMB * 1024 * 1024); |
| SetAllocatorDumpMetric(pmd, "skia/gpu_resources", "effective_size", |
| kGpuSkiaGpuResourcesMB * 1024 * 1024); |
| OSMemDumpPtr os_dump = |
| GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024, |
| metrics_mb["PrivateMemoryFootprint"] * 1024, |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| // accessing PrivateSwapFootprint on other OSes will |
| // modify metrics_mb to create the value, which leads to |
| // expectation failures. |
| metrics_mb["SharedMemoryFootprint"] * 1024, |
| metrics_mb["PrivateSwapFootprint"] * 1024 |
| #else |
| metrics_mb["SharedMemoryFootprint"] * 1024 |
| #endif |
| ); |
| pmd->os_dump = std::move(os_dump); |
| global_dump->process_dumps.push_back(std::move(pmd)); |
| } |
| |
| MetricMap GetExpectedGpuMetrics() { |
| return MetricMap({ |
| {"ProcessType", static_cast<int64_t>(ProcessType::GPU)}, |
| #if !defined(OS_MAC) |
| {"Resident", 210}, |
| #endif |
| {"Malloc", 220}, {"PrivateMemoryFootprint", 230}, |
| {"SharedMemoryFootprint", 235}, {"CommandBuffer", kGpuCommandBufferMB}, |
| {"Uptime", 42}, {"GpuMemory", kGpuTotalMemory * 1024 * 1024}, |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| {"PrivateSwapFootprint", 50}, |
| #endif |
| }); |
| } |
| |
| void PopulateAudioServiceMetrics(GlobalMemoryDumpPtr& global_dump, |
| MetricMap& metrics_mb) { |
| ProcessMemoryDumpPtr pmd( |
| memory_instrumentation::mojom::ProcessMemoryDump::New()); |
| pmd->process_type = ProcessType::UTILITY; |
| SetAllocatorDumpMetric(pmd, "malloc", "effective_size", |
| metrics_mb["Malloc"] * 1024 * 1024); |
| OSMemDumpPtr os_dump = |
| GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024, |
| metrics_mb["PrivateMemoryFootprint"] * 1024, |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| // accessing PrivateSwapFootprint on other OSes will |
| // modify metrics_mb to create the value, which leads to |
| // expectation failures. |
| metrics_mb["SharedMemoryFootprint"] * 1024, |
| metrics_mb["PrivateSwapFootprint"] * 1024 |
| #else |
| metrics_mb["SharedMemoryFootprint"] * 1024 |
| #endif |
| ); |
| pmd->os_dump = std::move(os_dump); |
| global_dump->process_dumps.push_back(std::move(pmd)); |
| } |
| |
| MetricMap GetExpectedAudioServiceMetrics() { |
| return MetricMap({ |
| {"ProcessType", static_cast<int64_t>(ProcessType::UTILITY)}, |
| #if !defined(OS_MAC) |
| {"Resident", 10}, |
| #endif |
| {"Malloc", 20}, {"PrivateMemoryFootprint", 30}, |
| {"SharedMemoryFootprint", 35}, {"Uptime", 42}, |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| {"PrivateSwapFootprint", 50}, |
| #endif |
| }); |
| } |
| |
| void PopulatePaintPreviewCompositorMetrics(GlobalMemoryDumpPtr& global_dump, |
| MetricMap& metrics_mb) { |
| auto process_memory_dump = |
| memory_instrumentation::mojom::ProcessMemoryDump::New(); |
| process_memory_dump->service_name = |
| paint_preview::mojom::PaintPreviewCompositorCollection::Name_; |
| ProcessMemoryDumpPtr pmd(std::move(process_memory_dump)); |
| pmd->process_type = ProcessType::UTILITY; |
| OSMemDumpPtr os_dump = |
| GetFakeOSMemDump(GetResidentValue(metrics_mb) * 1024, |
| metrics_mb["PrivateMemoryFootprint"] * 1024, |
| metrics_mb["SharedMemoryFootprint"] * 1024 |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| // accessing PrivateSwapFootprint on other OSes will |
| // modify metrics_mb to create the value, which leads to |
| // expectation failures. |
| , |
| metrics_mb["PrivateSwapFootprint"] * 1024 |
| #endif |
| ); |
| pmd->os_dump = std::move(os_dump); |
| global_dump->process_dumps.push_back(std::move(pmd)); |
| } |
| |
| MetricMap GetExpectedPaintPreviewCompositorMetrics() { |
| return MetricMap({ |
| {"ProcessType", static_cast<int64_t>(ProcessType::UTILITY)}, |
| #if !defined(OS_MAC) |
| {"Resident", 10}, |
| #endif |
| {"PrivateMemoryFootprint", 30}, {"SharedMemoryFootprint", 35}, |
| #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) |
| {"PrivateSwapFootprint", 50}, |
| #endif |
| }); |
| } |
| |
| void PopulateMetrics(GlobalMemoryDumpPtr& global_dump, |
| HistogramProcessType ptype, |
| MetricMap& metrics_mb) { |
| switch (ptype) { |
| case HistogramProcessType::kAudioService: |
| PopulateAudioServiceMetrics(global_dump, metrics_mb); |
| return; |
| case HistogramProcessType::kBrowser: |
| PopulateBrowserMetrics(global_dump, metrics_mb); |
| return; |
| case HistogramProcessType::kGpu: |
| PopulateGpuMetrics(global_dump, metrics_mb); |
| return; |
| case HistogramProcessType::kPaintPreviewCompositor: |
| PopulatePaintPreviewCompositorMetrics(global_dump, metrics_mb); |
| return; |
| case HistogramProcessType::kRenderer: |
| PopulateRendererMetrics(global_dump, metrics_mb, 101); |
| return; |
| case HistogramProcessType::kExtension: |
| case HistogramProcessType::kNetworkService: |
| case HistogramProcessType::kUtility: |
| break; |
| } |
| |
| // We shouldn't reach here. |
| CHECK(false); |
| } |
| |
| MetricMap GetExpectedProcessMetrics(HistogramProcessType ptype) { |
| switch (ptype) { |
| case HistogramProcessType::kAudioService: |
| return GetExpectedAudioServiceMetrics(); |
| case HistogramProcessType::kBrowser: |
| return GetExpectedBrowserMetrics(); |
| case HistogramProcessType::kGpu: |
| return GetExpectedGpuMetrics(); |
| case HistogramProcessType::kPaintPreviewCompositor: |
| return GetExpectedPaintPreviewCompositorMetrics(); |
| case HistogramProcessType::kRenderer: |
| return GetExpectedRendererMetrics(); |
| case HistogramProcessType::kExtension: |
| case HistogramProcessType::kNetworkService: |
| case HistogramProcessType::kUtility: |
| break; |
| } |
| |
| // We shouldn't reach here. |
| CHECK(false); |
| return MetricMap(); |
| } |
| |
| ProcessInfoVector GetProcessInfo(ukm::TestUkmRecorder& ukm_recorder) { |
| ProcessInfoVector process_infos; |
| |
| // Process 200 always has no URLs. |
| { |
| ProcessInfo process_info; |
| process_info.pid = 200; |
| process_infos.push_back(std::move(process_info)); |
| } |
| |
| // Process kTestRendererPid201 always has 1 URL |
| { |
| ProcessInfo process_info; |
| process_info.pid = kTestRendererPid201; |
| ukm::SourceId first_source_id = ukm::UkmRecorder::GetNewSourceID(); |
| ukm_recorder.UpdateSourceURL(first_source_id, |
| GURL("http://www.url201.com/")); |
| PageInfo page_info; |
| |
| page_info.ukm_source_id = first_source_id; |
| page_info.tab_id = 201; |
| page_info.hosts_main_frame = true; |
| page_info.is_visible = true; |
| page_info.time_since_last_visibility_change = |
| base::TimeDelta::FromSeconds(15); |
| page_info.time_since_last_navigation = base::TimeDelta::FromSeconds(20); |
| process_info.page_infos.push_back(page_info); |
| process_infos.push_back(std::move(process_info)); |
| } |
| |
| // Process kTestRendererPid202 always has 2 URL |
| { |
| ProcessInfo process_info; |
| process_info.pid = kTestRendererPid202; |
| ukm::SourceId first_source_id = ukm::UkmRecorder::GetNewSourceID(); |
| ukm::SourceId second_source_id = ukm::UkmRecorder::GetNewSourceID(); |
| ukm_recorder.UpdateSourceURL(first_source_id, |
| GURL("http://www.url2021.com/")); |
| ukm_recorder.UpdateSourceURL(second_source_id, |
| GURL("http://www.url2022.com/")); |
| PageInfo page_info1; |
| page_info1.ukm_source_id = first_source_id; |
| page_info1.tab_id = 2021; |
| page_info1.hosts_main_frame = true; |
| page_info1.time_since_last_visibility_change = |
| base::TimeDelta::FromSeconds(11); |
| page_info1.time_since_last_navigation = base::TimeDelta::FromSeconds(21); |
| PageInfo page_info2; |
| page_info2.ukm_source_id = second_source_id; |
| page_info2.tab_id = 2022; |
| page_info2.hosts_main_frame = true; |
| page_info2.time_since_last_visibility_change = |
| base::TimeDelta::FromSeconds(12); |
| page_info2.time_since_last_navigation = base::TimeDelta::FromSeconds(22); |
| process_info.page_infos.push_back(std::move(page_info1)); |
| process_info.page_infos.push_back(std::move(page_info2)); |
| |
| process_infos.push_back(std::move(process_info)); |
| } |
| return process_infos; |
| } |
| |
| } // namespace |
| |
| class ProcessMemoryMetricsEmitterTest |
| : public testing::TestWithParam<HistogramProcessType> { |
| public: |
| ProcessMemoryMetricsEmitterTest() {} |
| ~ProcessMemoryMetricsEmitterTest() override {} |
| |
| protected: |
| void CheckMemoryUkmEntryMetrics(const std::vector<MetricMap>& expected, |
| size_t expected_total_memory_entries = 1u) { |
| const auto& entries = |
| test_ukm_recorder_.GetEntriesByName(UkmEntry::kEntryName); |
| size_t i = 0; |
| size_t total_memory_entries = 0; |
| for (const auto* entry : entries) { |
| if (test_ukm_recorder_.EntryHasMetric( |
| entry, UkmEntry::kTotal2_PrivateMemoryFootprintName)) { |
| total_memory_entries++; |
| continue; |
| } |
| if (i >= expected.size()) { |
| FAIL() << "Unexpected non-total entry."; |
| } |
| for (const auto& kv : expected[i]) { |
| test_ukm_recorder_.ExpectEntryMetric(entry, kv.first, kv.second); |
| } |
| i++; |
| } |
| EXPECT_EQ(expected_total_memory_entries, total_memory_entries); |
| EXPECT_EQ(expected.size() + expected_total_memory_entries, entries.size()); |
| } |
| |
| content::BrowserTaskEnvironment task_environment_; |
| ukm::TestAutoSetUkmRecorder test_ukm_recorder_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMetricsEmitterTest); |
| }; |
| |
| TEST_P(ProcessMemoryMetricsEmitterTest, CollectsSingleProcessUKMs) { |
| MetricMap expected_metrics = GetExpectedProcessMetrics(GetParam()); |
| |
| GlobalMemoryDumpPtr global_dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| PopulateMetrics(global_dump, GetParam(), expected_metrics); |
| |
| scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter( |
| new ProcessMemoryMetricsEmitterFake(test_ukm_recorder_)); |
| emitter->ReceivedProcessInfos(ProcessInfoVector()); |
| emitter->ReceivedMemoryDump( |
| true, GlobalMemoryDump::MoveFrom(std::move(global_dump))); |
| |
| std::vector<MetricMap> expected_entries; |
| expected_entries.push_back(expected_metrics); |
| CheckMemoryUkmEntryMetrics(expected_entries); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| SinglePtype, |
| ProcessMemoryMetricsEmitterTest, |
| testing::Values(HistogramProcessType::kBrowser, |
| HistogramProcessType::kRenderer, |
| HistogramProcessType::kGpu, |
| HistogramProcessType::kPaintPreviewCompositor, |
| HistogramProcessType::kAudioService)); |
| |
| TEST_F(ProcessMemoryMetricsEmitterTest, CollectsExtensionProcessUKMs) { |
| MetricMap expected_metrics = GetExpectedRendererMetrics(); |
| expected_metrics["NumberOfExtensions"] = 1; |
| expected_metrics["Uptime"] = 21; |
| |
| GlobalMemoryDumpPtr global_dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| PopulateRendererMetrics(global_dump, expected_metrics, 401); |
| |
| scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter( |
| new ProcessMemoryMetricsEmitterFake(test_ukm_recorder_)); |
| emitter->ReceivedProcessInfos(ProcessInfoVector()); |
| emitter->ReceivedMemoryDump( |
| true, GlobalMemoryDump::MoveFrom(std::move(global_dump))); |
| |
| std::vector<MetricMap> expected_entries; |
| expected_entries.push_back(expected_metrics); |
| CheckMemoryUkmEntryMetrics(expected_entries); |
| } |
| |
| TEST_F(ProcessMemoryMetricsEmitterTest, CollectsManyProcessUKMsSingleDump) { |
| std::vector<HistogramProcessType> entries_ptypes = { |
| HistogramProcessType::kBrowser, |
| HistogramProcessType::kRenderer, |
| HistogramProcessType::kGpu, |
| HistogramProcessType::kAudioService, |
| HistogramProcessType::kPaintPreviewCompositor, |
| HistogramProcessType::kPaintPreviewCompositor, |
| HistogramProcessType::kAudioService, |
| HistogramProcessType::kGpu, |
| HistogramProcessType::kRenderer, |
| HistogramProcessType::kBrowser, |
| }; |
| |
| GlobalMemoryDumpPtr global_dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| std::vector<MetricMap> entries_metrics; |
| for (const auto& ptype : entries_ptypes) { |
| auto expected_metrics = GetExpectedProcessMetrics(ptype); |
| PopulateMetrics(global_dump, ptype, expected_metrics); |
| entries_metrics.push_back(expected_metrics); |
| } |
| |
| scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter( |
| new ProcessMemoryMetricsEmitterFake(test_ukm_recorder_)); |
| emitter->ReceivedProcessInfos(ProcessInfoVector()); |
| emitter->ReceivedMemoryDump( |
| true, GlobalMemoryDump::MoveFrom(std::move(global_dump))); |
| |
| CheckMemoryUkmEntryMetrics(entries_metrics); |
| } |
| |
| TEST_F(ProcessMemoryMetricsEmitterTest, CollectsManyProcessUKMsManyDumps) { |
| std::vector<std::vector<HistogramProcessType>> entries_ptypes = { |
| {HistogramProcessType::kBrowser, HistogramProcessType::kRenderer, |
| HistogramProcessType::kGpu, |
| HistogramProcessType::kPaintPreviewCompositor, |
| HistogramProcessType::kAudioService}, |
| {HistogramProcessType::kBrowser, HistogramProcessType::kRenderer, |
| HistogramProcessType::kGpu, |
| HistogramProcessType::kPaintPreviewCompositor, |
| HistogramProcessType::kAudioService}, |
| }; |
| |
| std::vector<MetricMap> entries_metrics; |
| for (int i = 0; i < 2; ++i) { |
| scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter( |
| new ProcessMemoryMetricsEmitterFake(test_ukm_recorder_)); |
| GlobalMemoryDumpPtr global_dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| for (const auto& ptype : entries_ptypes[i]) { |
| auto expected_metrics = GetExpectedProcessMetrics(ptype); |
| PopulateMetrics(global_dump, ptype, expected_metrics); |
| expected_metrics.erase("TimeSinceLastVisible"); |
| entries_metrics.push_back(expected_metrics); |
| } |
| emitter->ReceivedProcessInfos(ProcessInfoVector()); |
| emitter->ReceivedMemoryDump( |
| true, GlobalMemoryDump::MoveFrom(std::move(global_dump))); |
| } |
| |
| CheckMemoryUkmEntryMetrics(entries_metrics, 2u); |
| } |
| |
| TEST_F(ProcessMemoryMetricsEmitterTest, ReceiveProcessInfoFirst) { |
| GlobalMemoryDumpPtr global_dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| MetricMap expected_metrics = GetExpectedRendererMetrics(); |
| AddPageMetrics(expected_metrics); |
| PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid201); |
| |
| scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter( |
| new ProcessMemoryMetricsEmitterFake(test_ukm_recorder_)); |
| emitter->ReceivedProcessInfos(GetProcessInfo(test_ukm_recorder_)); |
| emitter->ReceivedMemoryDump( |
| true, GlobalMemoryDump::MoveFrom(std::move(global_dump))); |
| |
| auto entries = test_ukm_recorder_.GetEntriesByName(UkmEntry::kEntryName); |
| ASSERT_EQ(entries.size(), 2u); |
| int total_memory_entries = 0; |
| for (const auto* const entry : entries) { |
| if (test_ukm_recorder_.EntryHasMetric( |
| entry, UkmEntry::kTotal2_PrivateMemoryFootprintName)) { |
| total_memory_entries++; |
| } else { |
| test_ukm_recorder_.ExpectEntrySourceHasUrl( |
| entry, GURL("http://www.url201.com/")); |
| } |
| } |
| EXPECT_EQ(1, total_memory_entries); |
| |
| std::vector<MetricMap> expected_entries; |
| expected_entries.push_back(expected_metrics); |
| CheckMemoryUkmEntryMetrics(expected_entries); |
| } |
| |
| TEST_F(ProcessMemoryMetricsEmitterTest, ReceiveProcessInfoSecond) { |
| GlobalMemoryDumpPtr global_dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| MetricMap expected_metrics = GetExpectedRendererMetrics(); |
| AddPageMetrics(expected_metrics); |
| PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid201); |
| |
| scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter( |
| new ProcessMemoryMetricsEmitterFake(test_ukm_recorder_)); |
| emitter->ReceivedMemoryDump( |
| true, GlobalMemoryDump::MoveFrom(std::move(global_dump))); |
| emitter->ReceivedProcessInfos(GetProcessInfo(test_ukm_recorder_)); |
| |
| auto entries = test_ukm_recorder_.GetEntriesByName(UkmEntry::kEntryName); |
| ASSERT_EQ(entries.size(), 2u); |
| int total_memory_entries = 0; |
| for (const auto* const entry : entries) { |
| if (test_ukm_recorder_.EntryHasMetric( |
| entry, UkmEntry::kTotal2_PrivateMemoryFootprintName)) { |
| total_memory_entries++; |
| } else { |
| test_ukm_recorder_.ExpectEntrySourceHasUrl( |
| entry, GURL("http://www.url201.com/")); |
| } |
| } |
| EXPECT_EQ(1, total_memory_entries); |
| |
| std::vector<MetricMap> expected_entries; |
| expected_entries.push_back(expected_metrics); |
| CheckMemoryUkmEntryMetrics(expected_entries); |
| } |
| |
| TEST_F(ProcessMemoryMetricsEmitterTest, GlobalDumpFailed) { |
| GlobalMemoryDumpPtr global_dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| MetricMap expected_metrics = GetExpectedRendererMetrics(); |
| AddPageMetrics(expected_metrics); |
| PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid201); |
| |
| scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter( |
| new ProcessMemoryMetricsEmitterFake(test_ukm_recorder_)); |
| emitter->ReceivedMemoryDump( |
| false, GlobalMemoryDump::MoveFrom(std::move(global_dump))); |
| emitter->ReceivedProcessInfos(GetProcessInfo(test_ukm_recorder_)); |
| |
| // Should not record any metrics since the memory dump failed, and don't |
| // crash. |
| auto entries = test_ukm_recorder_.GetEntriesByName(UkmEntry::kEntryName); |
| ASSERT_EQ(entries.size(), 0u); |
| } |
| |
| TEST_F(ProcessMemoryMetricsEmitterTest, ProcessInfoHasTwoURLs) { |
| GlobalMemoryDumpPtr global_dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| MetricMap expected_metrics = GetExpectedRendererMetrics(); |
| PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid201); |
| PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid202); |
| PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid203); |
| |
| scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter( |
| new ProcessMemoryMetricsEmitterFake(test_ukm_recorder_)); |
| emitter->ReceivedMemoryDump( |
| true, GlobalMemoryDump::MoveFrom(std::move(global_dump))); |
| emitter->ReceivedProcessInfos(GetProcessInfo(test_ukm_recorder_)); |
| |
| // Check that if there are two URLs, neither is emitted. |
| auto entries = test_ukm_recorder_.GetEntriesByName(UkmEntry::kEntryName); |
| int total_memory_entries = 0; |
| int entries_with_urls = 0; |
| for (const auto* const entry : entries) { |
| if (test_ukm_recorder_.EntryHasMetric( |
| entry, UkmEntry::kTotal2_PrivateMemoryFootprintName)) { |
| total_memory_entries++; |
| } else { |
| if (test_ukm_recorder_.GetSourceForSourceId(entry->source_id)) { |
| entries_with_urls++; |
| test_ukm_recorder_.ExpectEntrySourceHasUrl( |
| entry, GURL("http://www.url201.com/")); |
| } |
| } |
| } |
| EXPECT_EQ(4u, entries.size()); |
| EXPECT_EQ(1, total_memory_entries); |
| EXPECT_EQ(1, entries_with_urls); |
| } |
| |
| TEST_F(ProcessMemoryMetricsEmitterTest, RendererAndTotalHistogramsAreRecorded) { |
| // Take a snapshot of the current state of the histograms. |
| base::HistogramTester histograms; |
| |
| GlobalMemoryDumpPtr global_dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| global_dump->aggregated_metrics = |
| memory_instrumentation::mojom::AggregatedMetrics::New(); |
| MetricMap expected_metrics = GetExpectedRendererMetrics(); |
| PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid201); |
| PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid202); |
| global_dump->aggregated_metrics->native_library_resident_kb = |
| kNativeLibraryResidentMemoryFootprint; |
| global_dump->aggregated_metrics->native_library_not_resident_ordered_kb = |
| kNativeLibraryNotResidentOrderedCodeFootprint; |
| global_dump->aggregated_metrics->native_library_resident_not_ordered_kb = |
| kNativeLibraryResidentNotOrderedCodeFootprint; |
| |
| // No histograms should have been recorded yet. |
| histograms.ExpectTotalCount("Memory.Renderer.PrivateMemoryFootprint", 0); |
| histograms.ExpectTotalCount("Memory.Renderer.SharedMemoryFootprint", 0); |
| histograms.ExpectTotalCount("Memory.Renderer.ResidentSet", 0); |
| |
| histograms.ExpectTotalCount("Memory.Total.PrivateMemoryFootprint", 0); |
| histograms.ExpectTotalCount("Memory.Total.RendererPrivateMemoryFootprint", 0); |
| histograms.ExpectTotalCount("Memory.Total.SharedMemoryFootprint", 0); |
| histograms.ExpectTotalCount("Memory.Total.ResidentSet", 0); |
| histograms.ExpectTotalCount( |
| "Memory.NativeLibrary.MappedAndResidentMemoryFootprint3", 0); |
| histograms.ExpectTotalCount( |
| "Memory.NativeLibrary.NotResidentOrderedCodeMemoryFootprint", 0); |
| histograms.ExpectTotalCount( |
| "Memory.NativeLibrary.ResidentNotOrderedCodeMemoryFootprint", 0); |
| |
| // Simulate some metrics emission. |
| scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter = |
| base::MakeRefCounted<ProcessMemoryMetricsEmitterFake>(test_ukm_recorder_); |
| emitter->ReceivedMemoryDump( |
| true, GlobalMemoryDump::MoveFrom(std::move(global_dump))); |
| emitter->ReceivedProcessInfos(GetProcessInfo(test_ukm_recorder_)); |
| |
| // Check that the expected values have been emitted to histograms. |
| histograms.ExpectUniqueSample("Memory.Renderer.PrivateMemoryFootprint", |
| kTestRendererPrivateMemoryFootprint, 2); |
| histograms.ExpectUniqueSample("Memory.Renderer.SharedMemoryFootprint", |
| kTestRendererSharedMemoryFootprint, 2); |
| #if defined(OS_MAC) |
| histograms.ExpectTotalCount("Memory.Renderer.ResidentSet", 0); |
| #else |
| histograms.ExpectUniqueSample("Memory.Renderer.ResidentSet", |
| kTestRendererResidentSet, 2); |
| #endif |
| |
| histograms.ExpectUniqueSample("Memory.Total.PrivateMemoryFootprint", |
| 2 * kTestRendererPrivateMemoryFootprint, 1); |
| histograms.ExpectUniqueSample("Memory.Total.RendererPrivateMemoryFootprint", |
| 2 * kTestRendererPrivateMemoryFootprint, 1); |
| histograms.ExpectUniqueSample("Memory.Total.SharedMemoryFootprint", |
| 2 * kTestRendererSharedMemoryFootprint, 1); |
| #if defined(OS_MAC) |
| histograms.ExpectTotalCount("Memory.Total.ResidentSet", 0); |
| #else |
| histograms.ExpectUniqueSample("Memory.Total.ResidentSet", |
| 2 * kTestRendererResidentSet, 1); |
| #endif |
| histograms.ExpectUniqueSample( |
| "Memory.NativeLibrary.MappedAndResidentMemoryFootprint3", |
| kNativeLibraryResidentMemoryFootprint, 1); |
| histograms.ExpectUniqueSample( |
| "Memory.NativeLibrary.NotResidentOrderedCodeMemoryFootprint", |
| kNativeLibraryNotResidentOrderedCodeFootprint, 1); |
| histograms.ExpectUniqueSample( |
| "Memory.NativeLibrary.ResidentNotOrderedCodeMemoryFootprint", |
| kNativeLibraryResidentNotOrderedCodeFootprint, 1); |
| } |
| |
| TEST_F(ProcessMemoryMetricsEmitterTest, MainFramePMFEmitted) { |
| GlobalMemoryDumpPtr global_dump( |
| memory_instrumentation::mojom::GlobalMemoryDump::New()); |
| MetricMap expected_metrics = GetExpectedRendererMetrics(); |
| AddPageMetrics(expected_metrics); |
| PopulateRendererMetrics(global_dump, expected_metrics, kTestRendererPid201); |
| |
| auto entries = test_ukm_recorder_.GetEntriesByName( |
| ukm::builders::Memory_TabFootprint::kEntryName); |
| ASSERT_EQ(entries.size(), 0u); |
| |
| scoped_refptr<ProcessMemoryMetricsEmitterFake> emitter( |
| new ProcessMemoryMetricsEmitterFake(test_ukm_recorder_)); |
| emitter->ReceivedMemoryDump( |
| true, GlobalMemoryDump::MoveFrom(std::move(global_dump))); |
| emitter->ReceivedProcessInfos(GetProcessInfo(test_ukm_recorder_)); |
| |
| entries = test_ukm_recorder_.GetEntriesByName( |
| ukm::builders::Memory_TabFootprint::kEntryName); |
| ASSERT_EQ(entries.size(), 1u); |
| const auto* entry = entries.front(); |
| ASSERT_TRUE(test_ukm_recorder_.EntryHasMetric( |
| entry, ukm::builders::Memory_TabFootprint::kMainFrameProcessPMFName)); |
| } |