1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/metrics/process_memory_metrics_emitter.h"
6 
7 #include <set>
8 #include <string>
9 #include <utility>
10 
11 #include "base/bind.h"
12 #include "base/compiler_specific.h"
13 #include "base/containers/flat_map.h"
14 #include "base/containers/flat_set.h"
15 #include "base/metrics/histogram_functions.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/stl_util.h"
18 #include "base/threading/sequenced_task_runner_handle.h"
19 #include "base/trace_event/memory_dump_request_args.h"
20 #include "build/build_config.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/metrics/tab_footprint_aggregator.h"
23 #include "chrome/browser/profiles/profile_manager.h"
24 #include "components/performance_manager/public/graph/frame_node.h"
25 #include "components/performance_manager/public/graph/graph.h"
26 #include "components/performance_manager/public/graph/graph_operations.h"
27 #include "components/performance_manager/public/graph/page_node.h"
28 #include "components/performance_manager/public/graph/process_node.h"
29 #include "components/performance_manager/public/performance_manager.h"
30 #include "components/services/paint_preview_compositor/public/mojom/paint_preview_compositor.mojom.h"
31 #include "content/public/browser/audio_service_info.h"
32 #include "content/public/browser/render_process_host.h"
33 #include "content/public/common/content_switches.h"
34 #include "extensions/buildflags/buildflags.h"
35 #include "services/metrics/public/cpp/ukm_builders.h"
36 #include "services/metrics/public/cpp/ukm_recorder.h"
37 #include "services/network/public/mojom/network_service.mojom.h"
38 #include "services/resource_coordinator/public/cpp/memory_instrumentation/browser_metrics.h"
39 #include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
40 #include "url/gurl.h"
41 
42 #if BUILDFLAG(ENABLE_EXTENSIONS)
43 #include "extensions/browser/extension_registry.h"
44 #include "extensions/browser/process_map.h"
45 #include "extensions/common/extension.h"
46 #endif
47 
48 using base::trace_event::MemoryAllocatorDump;
49 using memory_instrumentation::GetPrivateFootprintHistogramName;
50 using memory_instrumentation::GlobalMemoryDump;
51 using memory_instrumentation::HistogramProcessType;
52 using memory_instrumentation::HistogramProcessTypeToString;
53 using memory_instrumentation::kMemoryHistogramPrefix;
54 using ukm::builders::Memory_Experimental;
55 
56 namespace {
57 
58 const char kEffectiveSize[] = "effective_size";
59 const char kSize[] = "size";
60 const char kAllocatedObjectsSize[] = "allocated_objects_size";
61 
62 constexpr int kKiB = 1024;
63 constexpr int kMiB = 1024 * 1024;
64 
65 struct MetricRange {
66   const int min;
67   const int max;
68 };
69 
70 const MetricRange ImageSizeMetricRange = {1, 500 * kMiB /*500 MiB*/};
71 
72 // Prefer predefined ranges kLarge, kSmall and kTiny over custom ranges.
73 enum class MetricSize {
74   kLarge,   // 1MiB - 64,000MiB
75   kSmall,   // 10 - 500,000KiB
76   kTiny,    // 1 - 500,000B
77   kCustom,  // custom range, in bytes
78 };
79 
80 enum class EmitTo {
81   kCountsInUkmOnly,
82   kCountsInUkmAndSizeInUma,
83   kSizeInUkmAndUma,
84   kSizeInUmaOnly,
85   kIgnored
86 };
87 
88 struct Metric {
89   // The root dump name that represents the required metric.
90   const char* const dump_name;
91   // The name of the metric to be recorded in UMA.
92   const char* const uma_name;
93   // Indicates the size range of the metric. Only relevant if the |metric| is a
94   // size metric.
95   const MetricSize metric_size;
96   // The type of metric that is measured, usually size in bytes or object count.
97   const char* const metric;
98   // Indicates where to emit the metric.
99   const EmitTo target;
100   // The setter method for the metric in UKM recorder.
101   Memory_Experimental& (Memory_Experimental::*ukm_setter)(int64_t);
102   // Size range for the kCustom |metric_size|. Represents the min and max of the
103   // size range, in bytes.
104   const MetricRange range;
105 };
106 
107 const Metric kAllocatorDumpNamesForMetrics[] = {
108     {"blink_gc", "BlinkGC", MetricSize::kLarge, kEffectiveSize,
109      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetBlinkGC},
110     {"blink_gc", "BlinkGC.AllocatedObjects", MetricSize::kLarge,
111      kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
112      &Memory_Experimental::SetBlinkGC_AllocatedObjects},
113     {"blink_objects/Document", "NumberOfDocuments", MetricSize::kTiny,
114      MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmAndSizeInUma,
115      &Memory_Experimental::SetNumberOfDocuments},
116     {"blink_objects/ArrayBufferContents", "NumberOfArrayBufferContents",
117      MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
118      EmitTo::kCountsInUkmAndSizeInUma,
119      &Memory_Experimental::SetNumberOfArrayBufferContents},
120     {"blink_objects/AdSubframe", "NumberOfAdSubframes", MetricSize::kTiny,
121      MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmAndSizeInUma,
122      &Memory_Experimental::SetNumberOfAdSubframes},
123     {"blink_objects/DetachedScriptState", "NumberOfDetachedScriptStates",
124      MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
125      EmitTo::kCountsInUkmAndSizeInUma,
126      &Memory_Experimental::SetNumberOfDetachedScriptStates},
127     {"blink_objects/Frame", "NumberOfFrames", MetricSize::kTiny,
128      MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmAndSizeInUma,
129      &Memory_Experimental::SetNumberOfFrames},
130     {"blink_objects/LayoutObject", "NumberOfLayoutObjects", MetricSize::kTiny,
131      MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmAndSizeInUma,
132      &Memory_Experimental::SetNumberOfLayoutObjects},
133     {"blink_objects/Node", "NumberOfNodes", MetricSize::kSmall,
134      MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmAndSizeInUma,
135      &Memory_Experimental::SetNumberOfNodes},
136     {"blink_objects/AudioHandler", "NumberOfAudioHandler", MetricSize::kTiny,
137      MemoryAllocatorDump::kNameObjectCount, EmitTo::kSizeInUmaOnly, nullptr},
138     {"blink_objects/JSEventListener", "NumberOfJSEventListener",
139      MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
140      EmitTo::kSizeInUmaOnly, nullptr},
141     {"blink_objects/MediaKeySession", "NumberOfMediaKeySession",
142      MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
143      EmitTo::kSizeInUmaOnly, nullptr},
144     {"blink_objects/MediaKeys", "NumberOfMediaKeys", MetricSize::kTiny,
145      MemoryAllocatorDump::kNameObjectCount, EmitTo::kSizeInUmaOnly, nullptr},
146     {"blink_objects/Resource", "NumberOfResources", MetricSize::kTiny,
147      MemoryAllocatorDump::kNameObjectCount, EmitTo::kSizeInUmaOnly, nullptr},
148     {"blink_objects/ContextLifecycleStateObserver",
149      "NumberOfContextLifecycleStateObserver", MetricSize::kTiny,
150      MemoryAllocatorDump::kNameObjectCount, EmitTo::kSizeInUmaOnly, nullptr},
151     {"blink_objects/V8PerContextData", "NumberOfV8PerContextData",
152      MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
153      EmitTo::kSizeInUmaOnly, nullptr},
154     {"blink_objects/WorkerGlobalScope", "NumberOfWorkerGlobalScope",
155      MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
156      EmitTo::kSizeInUmaOnly, nullptr},
157     {"blink_objects/UACSSResource", "NumberOfUACSSResource", MetricSize::kTiny,
158      MemoryAllocatorDump::kNameObjectCount, EmitTo::kSizeInUmaOnly, nullptr},
159     {"blink_objects/RTCPeerConnection", "NumberOfRTCPeerConnection",
160      MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
161      EmitTo::kSizeInUmaOnly, nullptr},
162     {"blink_objects/ResourceFetcher", "NumberOfResourceFetcher",
163      MetricSize::kTiny, MemoryAllocatorDump::kNameObjectCount,
164      EmitTo::kSizeInUmaOnly, nullptr},
165     {"canvas/ResourceProvider/SkSurface", "CanvasResourceProvider.SkSurface",
166      MetricSize::kSmall, kSize, EmitTo::kCountsInUkmOnly,
167      &Memory_Experimental::SetCanvasResourceProvider_SkSurface},
168     {"components/download", "DownloadService", MetricSize::kSmall,
169      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
170      &Memory_Experimental::SetDownloadService},
171     {"discardable", "Discardable", MetricSize::kLarge, kEffectiveSize,
172      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetDiscardable},
173     {"discardable", "Discardable.FreelistSize", MetricSize::kSmall,
174      "freelist_size", EmitTo::kSizeInUmaOnly, nullptr},
175     {"discardable", "Discardable.VirtualSize", MetricSize::kSmall,
176      "virtual_size", EmitTo::kSizeInUmaOnly, nullptr},
177     {"extensions/functions", "ExtensionFunctions", MetricSize::kLarge,
178      kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
179     {"extensions/value_store", "Extensions.ValueStore", MetricSize::kLarge,
180      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
181      &Memory_Experimental::SetExtensions_ValueStore},
182     {"font_caches", "FontCaches", MetricSize::kSmall, kEffectiveSize,
183      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetFontCaches},
184     {"gpu/discardable_cache", "ServiceDiscardableManager", MetricSize::kCustom,
185      kSize, EmitTo::kSizeInUmaOnly, nullptr, ImageSizeMetricRange},
186     {"gpu/discardable_cache", "ServiceDiscardableManager.AvgImageSize",
187      MetricSize::kCustom, "average_size", EmitTo::kSizeInUmaOnly, nullptr,
188      ImageSizeMetricRange},
189     {"gpu/gl", "CommandBuffer", MetricSize::kLarge, kEffectiveSize,
190      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetCommandBuffer},
191     {"gpu/gr_shader_cache", "Gpu.GrShaderCache", MetricSize::kSmall,
192      kEffectiveSize, EmitTo::kSizeInUmaOnly, nullptr},
193     {"gpu/shared_images", "gpu::SharedImageStub", MetricSize::kSmall,
194      kEffectiveSize, EmitTo::kIgnored, nullptr},
195     {"gpu/transfer_cache", "ServiceTransferCache", MetricSize::kCustom, kSize,
196      EmitTo::kSizeInUmaOnly, nullptr, ImageSizeMetricRange},
197     {"gpu/transfer_cache", "ServiceTransferCache.AvgImageSize",
198      MetricSize::kCustom, "average_size", EmitTo::kSizeInUmaOnly, nullptr,
199      ImageSizeMetricRange},
200     {"history", "History", MetricSize::kSmall, kEffectiveSize,
201      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetHistory},
202     {"java_heap", "JavaHeap", MetricSize::kLarge, kEffectiveSize,
203      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetJavaHeap},
204     {"leveldatabase", "LevelDatabase", MetricSize::kSmall, kEffectiveSize,
205      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetLevelDatabase},
206     {"malloc", "Malloc", MetricSize::kLarge, kEffectiveSize,
207      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetMalloc},
208     {"malloc/allocated_objects", "Malloc.AllocatedObjects", MetricSize::kLarge,
209      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
210      &Memory_Experimental::SetMalloc_AllocatedObjects},
211     {"mojo", "NumberOfMojoHandles", MetricSize::kSmall,
212      MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmOnly,
213      &Memory_Experimental::SetNumberOfMojoHandles},
214     {"media/webmediaplayer/audio", "WebMediaPlayer.Audio", MetricSize::kSmall,
215      kSize, EmitTo::kSizeInUkmAndUma,
216      &Memory_Experimental::SetWebMediaPlayer_Audio},
217     {"media/webmediaplayer/video", "WebMediaPlayer.Video", MetricSize::kLarge,
218      kSize, EmitTo::kSizeInUkmAndUma,
219      &Memory_Experimental::SetWebMediaPlayer_Video},
220     {"media/webmediaplayer/data_source", "WebMediaPlayer.DataSource",
221      MetricSize::kLarge, kSize, EmitTo::kSizeInUkmAndUma,
222      &Memory_Experimental::SetWebMediaPlayer_DataSource},
223     {"media/webmediaplayer/demuxer", "WebMediaPlayer.Demuxer",
224      MetricSize::kLarge, kSize, EmitTo::kSizeInUkmAndUma,
225      &Memory_Experimental::SetWebMediaPlayer_Demuxer},
226     {"media/webmediaplayer", "WebMediaPlayer.Instances", MetricSize::kTiny,
227      MemoryAllocatorDump::kNameObjectCount, EmitTo::kCountsInUkmOnly,
228      &Memory_Experimental::SetNumberOfWebMediaPlayers},
229     {"net", "Net", MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
230      &Memory_Experimental::SetNet},
231     {"net/url_request_context", "Net.UrlRequestContext", MetricSize::kSmall,
232      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
233      &Memory_Experimental::SetNet_UrlRequestContext},
234     {"omnibox", "OmniboxSuggestions", MetricSize::kSmall, kEffectiveSize,
235      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetOmniboxSuggestions},
236     {"partition_alloc", "PartitionAlloc", MetricSize::kLarge, kEffectiveSize,
237      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetPartitionAlloc},
238     {"partition_alloc/allocated_objects", "PartitionAlloc.AllocatedObjects",
239      MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
240      &Memory_Experimental::SetPartitionAlloc_AllocatedObjects},
241     {"partition_alloc/partitions/array_buffer",
242      "PartitionAlloc.Partitions.ArrayBuffer", MetricSize::kLarge, kSize,
243      EmitTo::kSizeInUkmAndUma,
244      &Memory_Experimental::SetPartitionAlloc_Partitions_ArrayBuffer},
245     {"partition_alloc/partitions/buffer", "PartitionAlloc.Partitions.Buffer",
246      MetricSize::kLarge, kSize, EmitTo::kSizeInUkmAndUma,
247      &Memory_Experimental::SetPartitionAlloc_Partitions_Buffer},
248     {"partition_alloc/partitions/fast_malloc",
249      "PartitionAlloc.Partitions.FastMalloc", MetricSize::kLarge, kSize,
250      EmitTo::kSizeInUkmAndUma,
251      &Memory_Experimental::SetPartitionAlloc_Partitions_FastMalloc},
252     {"partition_alloc/partitions/layout", "PartitionAlloc.Partitions.Layout",
253      MetricSize::kLarge, kSize, EmitTo::kSizeInUkmAndUma,
254      &Memory_Experimental::SetPartitionAlloc_Partitions_Layout},
255     {"site_storage", "SiteStorage", MetricSize::kLarge, kEffectiveSize,
256      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetSiteStorage},
257     {"site_storage/blob_storage", "SiteStorage.BlobStorage", MetricSize::kLarge,
258      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
259      &Memory_Experimental::SetSiteStorage_BlobStorage},
260     {"site_storage/index_db", "SiteStorage.IndexDB", MetricSize::kSmall,
261      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
262      &Memory_Experimental::SetSiteStorage_IndexDB},
263     {"site_storage/localstorage", "SiteStorage.LocalStorage",
264      MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
265      &Memory_Experimental::SetSiteStorage_LocalStorage},
266     {"site_storage/session_storage", "SiteStorage.SessionStorage",
267      MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
268      &Memory_Experimental::SetSiteStorage_SessionStorage},
269     {"skia", "Skia", MetricSize::kLarge, kEffectiveSize,
270      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetSkia},
271     {"skia/gpu_resources", "SharedContextState", MetricSize::kLarge,
272      kEffectiveSize, EmitTo::kIgnored, nullptr},
273     {"skia/gpu_resources", "VizProcessContextProvider", MetricSize::kLarge,
274      kEffectiveSize, EmitTo::kIgnored, nullptr},
275     {"skia/sk_glyph_cache", "Skia.SkGlyphCache", MetricSize::kLarge,
276      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
277      &Memory_Experimental::SetSkia_SkGlyphCache},
278     {"skia/sk_resource_cache", "Skia.SkResourceCache", MetricSize::kLarge,
279      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
280      &Memory_Experimental::SetSkia_SkResourceCache},
281     {"sqlite", "Sqlite", MetricSize::kSmall, kEffectiveSize,
282      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetSqlite},
283     {"sync", "Sync", MetricSize::kLarge, kEffectiveSize,
284      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetSync},
285     {"tab_restore", "TabRestore", MetricSize::kSmall, kEffectiveSize,
286      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetTabRestore},
287     {"ui", "UI", MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
288      &Memory_Experimental::SetUI},
289     {"v8", "V8", MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
290      &Memory_Experimental::SetV8},
291     {"v8", "V8.AllocatedObjects", MetricSize::kLarge, kAllocatedObjectsSize,
292      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetV8_AllocatedObjects},
293     {"v8/main", "V8.Main", MetricSize::kLarge, kEffectiveSize,
294      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetV8_Main},
295     {"v8/main", "V8.Main.AllocatedObjects", MetricSize::kLarge,
296      kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
297      &Memory_Experimental::SetV8_Main_AllocatedObjects},
298     {"v8/main/global_handles", "V8.Main.GlobalHandles", MetricSize::kSmall,
299      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
300      &Memory_Experimental::SetV8_Main_GlobalHandles},
301     {"v8/main/heap", "V8.Main.Heap", MetricSize::kLarge, kEffectiveSize,
302      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetV8_Main_Heap},
303     {"v8/main/heap", "V8.Main.Heap.AllocatedObjects", MetricSize::kLarge,
304      kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
305      &Memory_Experimental::SetV8_Main_Heap_AllocatedObjects},
306     {"v8/main/heap/code_large_object_space",
307      "V8.Main.Heap.CodeLargeObjectSpace", MetricSize::kLarge, kEffectiveSize,
308      EmitTo::kSizeInUkmAndUma,
309      &Memory_Experimental::SetV8_Main_Heap_CodeLargeObjectSpace},
310     {"v8/main/heap/code_large_object_space",
311      "V8.Main.Heap.CodeLargeObjectSpace.AllocatedObjects", MetricSize::kLarge,
312      kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
313      &Memory_Experimental::
314          SetV8_Main_Heap_CodeLargeObjectSpace_AllocatedObjects},
315     {"v8/main/heap/code_space", "V8.Main.Heap.CodeSpace", MetricSize::kLarge,
316      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
317      &Memory_Experimental::SetV8_Main_Heap_CodeSpace},
318     {"v8/main/heap/code_space", "V8.Main.Heap.CodeSpace.AllocatedObjects",
319      MetricSize::kLarge, kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
320      &Memory_Experimental::SetV8_Main_Heap_CodeSpace_AllocatedObjects},
321     {"v8/main/heap/large_object_space", "V8.Main.Heap.LargeObjectSpace",
322      MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
323      &Memory_Experimental::SetV8_Main_Heap_LargeObjectSpace},
324     {"v8/main/heap/large_object_space",
325      "V8.Main.Heap.LargeObjectSpace.AllocatedObjects", MetricSize::kLarge,
326      kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
327      &Memory_Experimental::SetV8_Main_Heap_LargeObjectSpace_AllocatedObjects},
328     {"v8/main/heap/map_space", "V8.Main.Heap.MapSpace", MetricSize::kLarge,
329      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
330      &Memory_Experimental::SetV8_Main_Heap_MapSpace},
331     {"v8/main/heap/map_space", "V8.Main.Heap.MapSpace.AllocatedObjects",
332      MetricSize::kLarge, kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
333      &Memory_Experimental::SetV8_Main_Heap_MapSpace_AllocatedObjects},
334     {"v8/main/heap/new_large_object_space", "V8.Main.Heap.NewLargeObjectSpace",
335      MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
336      &Memory_Experimental::SetV8_Main_Heap_NewLargeObjectSpace},
337     {"v8/main/heap/new_large_object_space",
338      "V8.Main.Heap.NewLargeObjectSpace.AllocatedObjects", MetricSize::kLarge,
339      kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
340      &Memory_Experimental::
341          SetV8_Main_Heap_NewLargeObjectSpace_AllocatedObjects},
342     {"v8/main/heap/new_space", "V8.Main.Heap.NewSpace", MetricSize::kLarge,
343      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
344      &Memory_Experimental::SetV8_Main_Heap_NewSpace},
345     {"v8/main/heap/new_space", "V8.Main.Heap.NewSpace.AllocatedObjects",
346      MetricSize::kLarge, kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
347      &Memory_Experimental::SetV8_Main_Heap_NewSpace_AllocatedObjects},
348     {"v8/main/heap/old_space", "V8.Main.Heap.OldSpace", MetricSize::kLarge,
349      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
350      &Memory_Experimental::SetV8_Main_Heap_OldSpace},
351     {"v8/main/heap/old_space", "V8.Main.Heap.OldSpace.AllocatedObjects",
352      MetricSize::kLarge, kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
353      &Memory_Experimental::SetV8_Main_Heap_OldSpace_AllocatedObjects},
354     {"v8/main/heap/read_only_space", "V8.Main.Heap.ReadOnlySpace",
355      MetricSize::kLarge, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
356      &Memory_Experimental::SetV8_Main_Heap_ReadOnlySpace},
357     {"v8/main/heap/read_only_space",
358      "V8.Main.Heap.ReadOnlySpace.AllocatedObjects", MetricSize::kLarge,
359      kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
360      &Memory_Experimental::SetV8_Main_Heap_ReadOnlySpace_AllocatedObjects},
361     {"v8/main/malloc", "V8.Main.Malloc", MetricSize::kLarge, kEffectiveSize,
362      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetV8_Main_Malloc},
363     {"v8/workers", "V8.Workers", MetricSize::kLarge, kEffectiveSize,
364      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetV8_Workers},
365     {"v8/workers", "V8.Workers.AllocatedObjects", MetricSize::kLarge,
366      kAllocatedObjectsSize, EmitTo::kSizeInUkmAndUma,
367      &Memory_Experimental::SetV8_Workers_AllocatedObjects},
368     {"web_cache", "WebCache", MetricSize::kSmall, kEffectiveSize,
369      EmitTo::kSizeInUkmAndUma, &Memory_Experimental::SetWebCache},
370     {"web_cache/Image_resources", "WebCache.ImageResources", MetricSize::kSmall,
371      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
372      &Memory_Experimental::SetWebCache_ImageResources},
373     {"web_cache/CSS stylesheet_resources", "WebCache.CSSStylesheetResources",
374      MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
375      &Memory_Experimental::SetWebCache_CSSStylesheetResources},
376     {"web_cache/Script_resources", "WebCache.ScriptResources",
377      MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
378      &Memory_Experimental::SetWebCache_ScriptResources},
379     {"web_cache/XSL stylesheet_resources", "WebCache.XSLStylesheetResources",
380      MetricSize::kSmall, kEffectiveSize, EmitTo::kSizeInUkmAndUma,
381      &Memory_Experimental::SetWebCache_XSLStylesheetResources},
382     {"web_cache/Font_resources", "WebCache.FontResources", MetricSize::kSmall,
383      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
384      &Memory_Experimental::SetWebCache_FontResources},
385     {"web_cache/Code_cache", "WebCache.V8CodeCache", MetricSize::kSmall,
386      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
387      &Memory_Experimental::SetWebCache_V8CodeCache},
388     {"web_cache/Encoded_size_duplicated_in_data_urls",
389      "WebCache.EncodedSizeDuplicatedInDataUrls", MetricSize::kSmall,
390      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
391      &Memory_Experimental::SetWebCache_EncodedSizeDuplicatedInDataUrls},
392     {"web_cache/Other_resources", "WebCache.OtherResources", MetricSize::kSmall,
393      kEffectiveSize, EmitTo::kSizeInUkmAndUma,
394      &Memory_Experimental::SetWebCache_OtherResources},
395 };
396 
397 #define EXPERIMENTAL_UMA_PREFIX "Memory.Experimental."
398 #define VERSION_SUFFIX_NORMAL "2."
399 #define VERSION_SUFFIX_SMALL "2.Small."
400 #define VERSION_SUFFIX_TINY "2.Tiny."
401 #define VERSION_SUFFIX_CUSTOM "2.Custom."
402 
EmitProcessUkm(const Metric & item,uint64_t value,Memory_Experimental * builder)403 void EmitProcessUkm(const Metric& item,
404                     uint64_t value,
405                     Memory_Experimental* builder) {
406   DCHECK(item.ukm_setter) << "UKM metrics must provide a setter";
407   (builder->*(item.ukm_setter))(value);
408 }
409 
MetricSizeToVersionSuffix(MetricSize size)410 const char* MetricSizeToVersionSuffix(MetricSize size) {
411   switch (size) {
412     case MetricSize::kLarge:
413       return VERSION_SUFFIX_NORMAL;
414     case MetricSize::kSmall:
415       return VERSION_SUFFIX_SMALL;
416     case MetricSize::kTiny:
417       return VERSION_SUFFIX_TINY;
418     case MetricSize::kCustom:
419       return VERSION_SUFFIX_CUSTOM;
420   }
421 }
422 
EmitProcessUma(HistogramProcessType process_type,const Metric & item,uint64_t value)423 void EmitProcessUma(HistogramProcessType process_type,
424                     const Metric& item,
425                     uint64_t value) {
426   std::string uma_name;
427 
428   // Always use "Gpu" in process name for command buffers to be
429   // consistent even in single process mode.
430   if (base::StringPiece(item.uma_name) == "CommandBuffer") {
431     uma_name =
432         EXPERIMENTAL_UMA_PREFIX "Gpu" VERSION_SUFFIX_NORMAL "CommandBuffer";
433     DCHECK(item.metric_size == MetricSize::kLarge);
434   } else {
435     uma_name = std::string(EXPERIMENTAL_UMA_PREFIX) +
436                HistogramProcessTypeToString(process_type) +
437                MetricSizeToVersionSuffix(item.metric_size) + item.uma_name;
438   }
439 
440   switch (item.metric_size) {
441     case MetricSize::kLarge:  // 1 - 64,000 MiB
442       MEMORY_METRICS_HISTOGRAM_MB(uma_name, value / kMiB);
443       break;
444     case MetricSize::kSmall:  // 10 - 500,000 KiB
445       base::UmaHistogramCustomCounts(uma_name, value / kKiB, 10, 500000, 100);
446       break;
447     case MetricSize::kTiny:  // 1 - 500,000 bytes
448       base::UmaHistogramCustomCounts(uma_name, value, 1, 500000, 100);
449       break;
450     case MetricSize::kCustom:
451       base::UmaHistogramCustomCounts(uma_name, value, item.range.min,
452                                      item.range.max, 100);
453       break;
454   }
455 }
456 
EmitProcessUmaAndUkm(const GlobalMemoryDump::ProcessDump & pmd,HistogramProcessType process_type,const base::Optional<base::TimeDelta> & uptime,bool record_uma,Memory_Experimental * builder)457 void EmitProcessUmaAndUkm(const GlobalMemoryDump::ProcessDump& pmd,
458                           HistogramProcessType process_type,
459                           const base::Optional<base::TimeDelta>& uptime,
460                           bool record_uma,
461                           Memory_Experimental* builder) {
462   for (const auto& item : kAllocatorDumpNamesForMetrics) {
463     base::Optional<uint64_t> value = pmd.GetMetric(item.dump_name, item.metric);
464     if (!value)
465       continue;
466 
467     switch (item.target) {
468       case EmitTo::kCountsInUkmOnly:
469         EmitProcessUkm(item, value.value(), builder);
470         break;
471       case EmitTo::kCountsInUkmAndSizeInUma:
472         EmitProcessUkm(item, value.value(), builder);
473         if (record_uma)
474           EmitProcessUma(process_type, item, value.value());
475         break;
476       case EmitTo::kSizeInUmaOnly:
477         if (record_uma)
478           EmitProcessUma(process_type, item, value.value());
479         break;
480       case EmitTo::kSizeInUkmAndUma:
481         // For each 'size' metric, emit size as MB.
482         EmitProcessUkm(item, value.value() / kMiB, builder);
483         if (record_uma)
484           EmitProcessUma(process_type, item, value.value());
485         break;
486       case EmitTo::kIgnored:
487         break;
488       default:
489         NOTREACHED();
490     }
491   }
492 
493 #if !defined(OS_MAC)
494   // Resident set is not populated on Mac.
495   builder->SetResident(pmd.os_dump().resident_set_kb / kKiB);
496 #endif
497 
498   builder->SetPrivateMemoryFootprint(pmd.os_dump().private_footprint_kb / kKiB);
499   builder->SetSharedMemoryFootprint(pmd.os_dump().shared_footprint_kb / kKiB);
500 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) || defined(OS_BSD)
501   builder->SetPrivateSwapFootprint(pmd.os_dump().private_footprint_swap_kb /
502                                    kKiB);
503 #endif
504   if (uptime)
505     builder->SetUptime(uptime.value().InSeconds());
506   if (!record_uma)
507     return;
508 
509   const char* process_name = HistogramProcessTypeToString(process_type);
510 #if defined(OS_MAC)
511   // Resident set is not populated on Mac.
512   DCHECK_EQ(pmd.os_dump().resident_set_kb, 0U);
513 #else
514   MEMORY_METRICS_HISTOGRAM_MB(
515       std::string(kMemoryHistogramPrefix) + process_name + ".ResidentSet",
516       pmd.os_dump().resident_set_kb / kKiB);
517 #endif
518   MEMORY_METRICS_HISTOGRAM_MB(GetPrivateFootprintHistogramName(process_type),
519                               pmd.os_dump().private_footprint_kb / kKiB);
520   MEMORY_METRICS_HISTOGRAM_MB(std::string(kMemoryHistogramPrefix) +
521                                   process_name + ".SharedMemoryFootprint",
522                               pmd.os_dump().shared_footprint_kb / kKiB);
523 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) || defined(OS_BSD)
524   MEMORY_METRICS_HISTOGRAM_MB(std::string(kMemoryHistogramPrefix) +
525                                   process_name + ".PrivateSwapFootprint",
526                               pmd.os_dump().private_footprint_swap_kb / kKiB);
527 #endif
528 }
529 
EmitSummedGpuMemory(const GlobalMemoryDump::ProcessDump & pmd,Memory_Experimental * builder,bool record_uma)530 void EmitSummedGpuMemory(const GlobalMemoryDump::ProcessDump& pmd,
531                          Memory_Experimental* builder,
532                          bool record_uma) {
533   // Combine several categories together to sum up Chrome-reported gpu memory.
534   static const char* gpu_categories[] = {
535       "gpu/gl",
536       "gpu/shared_images",
537       "skia/gpu_resources",
538   };
539   Metric synthetic_metric = {nullptr,
540                              "GpuMemory",
541                              MetricSize::kLarge,
542                              kEffectiveSize,
543                              EmitTo::kSizeInUkmAndUma,
544                              &Memory_Experimental::SetGpuMemory};
545 
546   uint64_t total = 0;
547   for (size_t i = 0; i < base::size(gpu_categories); ++i) {
548     total +=
549         pmd.GetMetric(gpu_categories[i], synthetic_metric.metric).value_or(0);
550   }
551 
552   // We log this metric for both the browser and GPU process, and only one will
553   // have entries for |gpu_categories|, so only log if |total| > 0. There should
554   // be almost no meaningful cases where |total| is actually zero.
555   if (total == 0)
556     return;
557 
558   // Always use kGpu as the process name for this even for the in process
559   // command buffer case.
560   EmitProcessUkm(synthetic_metric, total, builder);
561   if (record_uma)
562     EmitProcessUma(HistogramProcessType::kGpu, synthetic_metric, total);
563 }
564 
EmitBrowserMemoryMetrics(const GlobalMemoryDump::ProcessDump & pmd,ukm::SourceId ukm_source_id,ukm::UkmRecorder * ukm_recorder,const base::Optional<base::TimeDelta> & uptime,bool record_uma)565 void EmitBrowserMemoryMetrics(const GlobalMemoryDump::ProcessDump& pmd,
566                               ukm::SourceId ukm_source_id,
567                               ukm::UkmRecorder* ukm_recorder,
568                               const base::Optional<base::TimeDelta>& uptime,
569                               bool record_uma) {
570   Memory_Experimental builder(ukm_source_id);
571   builder.SetProcessType(static_cast<int64_t>(
572       memory_instrumentation::mojom::ProcessType::BROWSER));
573   EmitProcessUmaAndUkm(pmd, HistogramProcessType::kBrowser, uptime, record_uma,
574                        &builder);
575   EmitSummedGpuMemory(pmd, &builder, record_uma);
576 
577   builder.Record(ukm_recorder);
578 }
579 
EmitRendererMemoryMetrics(const GlobalMemoryDump::ProcessDump & pmd,const ProcessMemoryMetricsEmitter::PageInfo * page_info,ukm::UkmRecorder * ukm_recorder,int number_of_extensions,const base::Optional<base::TimeDelta> & uptime,bool record_uma)580 void EmitRendererMemoryMetrics(
581     const GlobalMemoryDump::ProcessDump& pmd,
582     const ProcessMemoryMetricsEmitter::PageInfo* page_info,
583     ukm::UkmRecorder* ukm_recorder,
584     int number_of_extensions,
585     const base::Optional<base::TimeDelta>& uptime,
586     bool record_uma) {
587   ukm::SourceId ukm_source_id =
588       page_info ? page_info->ukm_source_id : ukm::UkmRecorder::GetNewSourceID();
589   Memory_Experimental builder(ukm_source_id);
590   builder.SetProcessType(static_cast<int64_t>(
591       memory_instrumentation::mojom::ProcessType::RENDERER));
592   builder.SetNumberOfExtensions(number_of_extensions);
593 
594   const HistogramProcessType process_type =
595       (number_of_extensions == 0) ? HistogramProcessType::kRenderer
596                                   : HistogramProcessType::kExtension;
597   EmitProcessUmaAndUkm(pmd, process_type, uptime, record_uma, &builder);
598 
599   if (page_info) {
600     builder.SetIsVisible(page_info->is_visible);
601     builder.SetTimeSinceLastVisibilityChange(
602         page_info->time_since_last_visibility_change.InSeconds());
603     builder.SetTimeSinceLastNavigation(
604         page_info->time_since_last_navigation.InSeconds());
605   }
606 
607   builder.Record(ukm_recorder);
608 }
609 
EmitGpuMemoryMetrics(const GlobalMemoryDump::ProcessDump & pmd,ukm::SourceId ukm_source_id,ukm::UkmRecorder * ukm_recorder,const base::Optional<base::TimeDelta> & uptime,bool record_uma)610 void EmitGpuMemoryMetrics(const GlobalMemoryDump::ProcessDump& pmd,
611                           ukm::SourceId ukm_source_id,
612                           ukm::UkmRecorder* ukm_recorder,
613                           const base::Optional<base::TimeDelta>& uptime,
614                           bool record_uma) {
615   Memory_Experimental builder(ukm_source_id);
616   builder.SetProcessType(
617       static_cast<int64_t>(memory_instrumentation::mojom::ProcessType::GPU));
618   EmitProcessUmaAndUkm(pmd, HistogramProcessType::kGpu, uptime, record_uma,
619                        &builder);
620   EmitSummedGpuMemory(pmd, &builder, record_uma);
621   builder.Record(ukm_recorder);
622 }
623 
EmitUtilityMemoryMetrics(HistogramProcessType ptype,const GlobalMemoryDump::ProcessDump & pmd,ukm::SourceId ukm_source_id,ukm::UkmRecorder * ukm_recorder,const base::Optional<base::TimeDelta> & uptime,bool record_uma)624 void EmitUtilityMemoryMetrics(HistogramProcessType ptype,
625                               const GlobalMemoryDump::ProcessDump& pmd,
626                               ukm::SourceId ukm_source_id,
627                               ukm::UkmRecorder* ukm_recorder,
628                               const base::Optional<base::TimeDelta>& uptime,
629                               bool record_uma) {
630   Memory_Experimental builder(ukm_source_id);
631   builder.SetProcessType(static_cast<int64_t>(
632       memory_instrumentation::mojom::ProcessType::UTILITY));
633   EmitProcessUmaAndUkm(pmd, ptype, uptime, record_uma, &builder);
634 
635   builder.Record(ukm_recorder);
636 }
637 
638 }  // namespace
639 
ProcessMemoryMetricsEmitter()640 ProcessMemoryMetricsEmitter::ProcessMemoryMetricsEmitter()
641     : pid_scope_(base::kNullProcessId) {}
642 
ProcessMemoryMetricsEmitter(base::ProcessId pid_scope)643 ProcessMemoryMetricsEmitter::ProcessMemoryMetricsEmitter(
644     base::ProcessId pid_scope)
645     : pid_scope_(pid_scope) {}
646 
FetchAndEmitProcessMemoryMetrics()647 void ProcessMemoryMetricsEmitter::FetchAndEmitProcessMemoryMetrics() {
648   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
649 
650   MarkServiceRequestsInProgress();
651 
652   // The callback keeps this object alive until the callback is invoked.
653   auto callback =
654       base::BindOnce(&ProcessMemoryMetricsEmitter::ReceivedMemoryDump, this);
655   std::vector<std::string> mad_list;
656   for (const auto& metric : kAllocatorDumpNamesForMetrics)
657     mad_list.push_back(metric.dump_name);
658   if (pid_scope_ != base::kNullProcessId) {
659     memory_instrumentation::MemoryInstrumentation::GetInstance()
660         ->RequestGlobalDumpForPid(pid_scope_, mad_list, std::move(callback));
661   } else {
662     memory_instrumentation::MemoryInstrumentation::GetInstance()
663         ->RequestGlobalDump(mad_list, std::move(callback));
664   }
665 
666   // Use a lambda adapter to post the results back to this sequence.
667   GetProcessToPageInfoMapCallback callback2 = base::BindOnce(
668       [](scoped_refptr<base::SequencedTaskRunner> task_runner,
669          scoped_refptr<ProcessMemoryMetricsEmitter> pmme,
670          std::vector<ProcessInfo> process_infos) -> void {
671         task_runner->PostTask(
672             FROM_HERE,
673             base::BindOnce(&ProcessMemoryMetricsEmitter::ReceivedProcessInfos,
674                            pmme, std::move(process_infos)));
675       },
676       base::SequencedTaskRunnerHandle::Get(),
677       scoped_refptr<ProcessMemoryMetricsEmitter>(this));
678 
679   performance_manager::PerformanceManager::CallOnGraph(
680       FROM_HERE,
681       base::BindOnce(&ProcessMemoryMetricsEmitter::GetProcessToPageInfoMap,
682                      std::move(callback2)));
683 }
684 
MarkServiceRequestsInProgress()685 void ProcessMemoryMetricsEmitter::MarkServiceRequestsInProgress() {
686   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
687 
688   memory_dump_in_progress_ = true;
689   get_process_urls_in_progress_ = true;
690 }
691 
~ProcessMemoryMetricsEmitter()692 ProcessMemoryMetricsEmitter::~ProcessMemoryMetricsEmitter() {}
693 
ReceivedMemoryDump(bool success,std::unique_ptr<GlobalMemoryDump> dump)694 void ProcessMemoryMetricsEmitter::ReceivedMemoryDump(
695     bool success,
696     std::unique_ptr<GlobalMemoryDump> dump) {
697   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
698 
699   memory_dump_in_progress_ = false;
700   if (!success)
701     return;
702   global_dump_ = std::move(dump);
703   CollateResults();
704 }
705 
ReceivedProcessInfos(std::vector<ProcessInfo> process_infos)706 void ProcessMemoryMetricsEmitter::ReceivedProcessInfos(
707     std::vector<ProcessInfo> process_infos) {
708   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
709 
710   get_process_urls_in_progress_ = false;
711   process_infos_.clear();
712   process_infos_.reserve(process_infos.size());
713 
714   // If there are duplicate pids, keep the latest ProcessInfoPtr.
715   for (ProcessInfo& process_info : process_infos) {
716     base::ProcessId pid = process_info.pid;
717     process_infos_[pid] = std::move(process_info);
718   }
719   CollateResults();
720 }
721 
GetUkmRecorder()722 ukm::UkmRecorder* ProcessMemoryMetricsEmitter::GetUkmRecorder() {
723   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
724 
725   return ukm::UkmRecorder::Get();
726 }
727 
GetNumberOfExtensions(base::ProcessId pid)728 int ProcessMemoryMetricsEmitter::GetNumberOfExtensions(base::ProcessId pid) {
729   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
730 
731   int number_of_extensions = 0;
732 #if BUILDFLAG(ENABLE_EXTENSIONS)
733   // Retrieve the renderer process host for the given pid.
734   int rph_id = -1;
735   bool found = false;
736   for (auto iter = content::RenderProcessHost::AllHostsIterator();
737        !iter.IsAtEnd(); iter.Advance()) {
738     if (!iter.GetCurrentValue()->GetProcess().IsValid())
739       continue;
740 
741     if (iter.GetCurrentValue()->GetProcess().Pid() == pid) {
742       found = true;
743       rph_id = iter.GetCurrentValue()->GetID();
744       break;
745     }
746   }
747   if (!found)
748     return 0;
749 
750   // Count the number of extensions associated with that renderer process host
751   // in all profiles.
752   for (Profile* profile :
753        g_browser_process->profile_manager()->GetLoadedProfiles()) {
754     extensions::ProcessMap* process_map = extensions::ProcessMap::Get(profile);
755     extensions::ExtensionRegistry* registry =
756         extensions::ExtensionRegistry::Get(profile);
757     std::set<std::string> extension_ids =
758         process_map->GetExtensionsInProcess(rph_id);
759     for (const std::string& extension_id : extension_ids) {
760       // Only count non hosted apps extensions.
761       const extensions::Extension* extension =
762           registry->enabled_extensions().GetByID(extension_id);
763       if (extension && !extension->is_hosted_app())
764         number_of_extensions++;
765     }
766   }
767 #endif
768   return number_of_extensions;
769 }
770 
GetProcessUptime(const base::Time & now,base::ProcessId pid)771 base::Optional<base::TimeDelta> ProcessMemoryMetricsEmitter::GetProcessUptime(
772     const base::Time& now,
773     base::ProcessId pid) {
774   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
775 
776   auto process_info = process_infos_.find(pid);
777   if (process_info != process_infos_.end()) {
778     if (!process_info->second.launch_time.is_null())
779       return now - process_info->second.launch_time;
780   }
781   return base::Optional<base::TimeDelta>();
782 }
783 
CollateResults()784 void ProcessMemoryMetricsEmitter::CollateResults() {
785   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
786 
787   if (memory_dump_in_progress_ || get_process_urls_in_progress_)
788     return;
789   // The memory dump can be done, yet |global_dump_| not set if:
790   // - Process metrics collection fails first.
791   // - Process Infos arrive later.
792   if (!global_dump_)
793     return;
794 
795   uint32_t private_footprint_total_kb = 0;
796   uint32_t renderer_private_footprint_total_kb = 0;
797   uint32_t shared_footprint_total_kb = 0;
798   uint32_t resident_set_total_kb = 0;
799   bool emit_metrics_for_all_processes = pid_scope_ == base::kNullProcessId;
800 
801   TabFootprintAggregator per_tab_metrics;
802 
803   base::Time now = base::Time::Now();
804   for (const auto& pmd : global_dump_->process_dumps()) {
805     uint32_t process_pmf_kb = pmd.os_dump().private_footprint_kb;
806     private_footprint_total_kb += process_pmf_kb;
807     shared_footprint_total_kb += pmd.os_dump().shared_footprint_kb;
808     resident_set_total_kb += pmd.os_dump().resident_set_kb;
809 
810     if (!emit_metrics_for_all_processes && pid_scope_ != pmd.pid())
811       continue;
812 
813     switch (pmd.process_type()) {
814       case memory_instrumentation::mojom::ProcessType::BROWSER: {
815         EmitBrowserMemoryMetrics(
816             pmd, ukm::UkmRecorder::GetNewSourceID(), GetUkmRecorder(),
817             GetProcessUptime(now, pmd.pid()), emit_metrics_for_all_processes);
818         break;
819       }
820       case memory_instrumentation::mojom::ProcessType::RENDERER: {
821         renderer_private_footprint_total_kb += process_pmf_kb;
822         const PageInfo* single_page_info = nullptr;
823         auto iter = process_infos_.find(pmd.pid());
824         if (iter != process_infos_.end()) {
825           const ProcessInfo& process_info = iter->second;
826 
827           if (emit_metrics_for_all_processes) {
828             // Renderer metrics-by-tab only make sense if we're visiting all
829             // render processes.
830             for (const PageInfo& page_info : process_info.page_infos) {
831               if (page_info.hosts_main_frame) {
832                 per_tab_metrics.AssociateMainFrame(page_info.ukm_source_id,
833                                                    pmd.pid(), page_info.tab_id,
834                                                    process_pmf_kb);
835               } else {
836                 per_tab_metrics.AssociateSubFrame(page_info.ukm_source_id,
837                                                   pmd.pid(), page_info.tab_id,
838                                                   process_pmf_kb);
839               }
840             }
841           }
842 
843           // If there is more than one tab being hosted in a renderer, don't
844           // emit certain data. This is not ideal, but UKM does not support
845           // multiple-URLs per entry, and we must have one entry per process.
846           if (process_info.page_infos.size() == 1) {
847             single_page_info = &process_info.page_infos[0];
848           }
849         }
850 
851         int number_of_extensions = GetNumberOfExtensions(pmd.pid());
852         EmitRendererMemoryMetrics(
853             pmd, single_page_info, GetUkmRecorder(), number_of_extensions,
854             GetProcessUptime(now, pmd.pid()), emit_metrics_for_all_processes);
855         break;
856       }
857       case memory_instrumentation::mojom::ProcessType::GPU: {
858         EmitGpuMemoryMetrics(pmd, ukm::UkmRecorder::GetNewSourceID(),
859                              GetUkmRecorder(), GetProcessUptime(now, pmd.pid()),
860                              emit_metrics_for_all_processes);
861         break;
862       }
863       case memory_instrumentation::mojom::ProcessType::UTILITY: {
864         HistogramProcessType ptype;
865         if (pmd.pid() == content::GetProcessIdForAudioService()) {
866           ptype = HistogramProcessType::kAudioService;
867         } else if (pmd.service_name() ==
868                    network::mojom::NetworkService::Name_) {
869           ptype = HistogramProcessType::kNetworkService;
870         } else if (pmd.service_name() ==
871                    paint_preview::mojom::PaintPreviewCompositorCollection::
872                        Name_) {
873           ptype = HistogramProcessType::kPaintPreviewCompositor;
874         } else {
875           ptype = HistogramProcessType::kUtility;
876         }
877         EmitUtilityMemoryMetrics(
878             ptype, pmd, ukm::UkmRecorder::GetNewSourceID(), GetUkmRecorder(),
879             GetProcessUptime(now, pmd.pid()), emit_metrics_for_all_processes);
880         break;
881       }
882       case memory_instrumentation::mojom::ProcessType::PLUGIN:
883         FALLTHROUGH;
884       case memory_instrumentation::mojom::ProcessType::ARC:
885         FALLTHROUGH;
886       case memory_instrumentation::mojom::ProcessType::OTHER:
887         break;
888     }
889   }
890 
891   if (emit_metrics_for_all_processes) {
892     const auto& metrics = global_dump_->aggregated_metrics();
893     int32_t native_resident_kb = metrics.native_library_resident_kb();
894     int32_t native_library_resident_not_ordered_kb =
895         metrics.native_library_resident_not_ordered_kb();
896     int32_t native_library_not_resident_ordered_kb =
897         metrics.native_library_not_resident_ordered_kb();
898 
899     // |native_resident_kb| is only calculated for android devices that support
900     // code ordering.
901     if (native_resident_kb != metrics.kInvalid) {
902       // Size of the native library on android is ~40MB.
903       // More precision is needed in the middle buckets, hence the range.
904       base::UmaHistogramCustomCounts(
905           "Memory.NativeLibrary.MappedAndResidentMemoryFootprint3",
906           native_resident_kb, 1000, 100000, 100);
907       if (native_library_not_resident_ordered_kb != metrics.kInvalid) {
908         base::UmaHistogramCustomCounts(
909             "Memory.NativeLibrary.NotResidentOrderedCodeMemoryFootprint",
910             native_library_not_resident_ordered_kb, 1000, 100000, 100);
911       }
912       if (native_library_resident_not_ordered_kb != metrics.kInvalid) {
913         base::UmaHistogramCustomCounts(
914             "Memory.NativeLibrary.ResidentNotOrderedCodeMemoryFootprint",
915             native_library_resident_not_ordered_kb, 1000, 100000, 100);
916       }
917     }
918 
919     UMA_HISTOGRAM_MEMORY_LARGE_MB(
920         "Memory.Experimental.Total2.PrivateMemoryFootprint",
921         private_footprint_total_kb / kKiB);
922 #if defined(OS_MAC)
923     // Resident set is not populated on Mac.
924     DCHECK_EQ(resident_set_total_kb, 0U);
925 #else
926     UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.ResidentSet",
927                                   resident_set_total_kb / kKiB);
928 
929 #endif
930     UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.PrivateMemoryFootprint",
931                                   private_footprint_total_kb / kKiB);
932     UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.RendererPrivateMemoryFootprint",
933                                   renderer_private_footprint_total_kb / kKiB);
934     UMA_HISTOGRAM_MEMORY_LARGE_MB("Memory.Total.SharedMemoryFootprint",
935                                   shared_footprint_total_kb / kKiB);
936 
937     Memory_Experimental(ukm::UkmRecorder::GetNewSourceID())
938         .SetTotal2_PrivateMemoryFootprint(private_footprint_total_kb / kKiB)
939         .SetTotal2_SharedMemoryFootprint(shared_footprint_total_kb / kKiB)
940         .Record(GetUkmRecorder());
941 
942     // Renderer metrics-by-tab only make sense if we're visiting all render
943     // processes.
944     per_tab_metrics.RecordPmfs(GetUkmRecorder());
945   }
946 
947   global_dump_ = nullptr;
948 }
949 
950 namespace {
951 
952 // Returns true iff the given |process| is responsible for hosting the
953 // main-frame of the given |page|.
HostsMainFrame(const performance_manager::ProcessNode * process,const performance_manager::PageNode * page)954 bool HostsMainFrame(const performance_manager::ProcessNode* process,
955                     const performance_manager::PageNode* page) {
956   const performance_manager::FrameNode* main_frame = page->GetMainFrameNode();
957   if (main_frame == nullptr) {
958     // |process| can't host a frame that doesn't exist.
959     return false;
960   }
961 
962   return main_frame->GetProcessNode() == process;
963 }
964 
965 }  // namespace
966 
GetProcessToPageInfoMap(GetProcessToPageInfoMapCallback callback,performance_manager::Graph * graph)967 void ProcessMemoryMetricsEmitter::GetProcessToPageInfoMap(
968     GetProcessToPageInfoMapCallback callback,
969     performance_manager::Graph* graph) {
970   std::vector<ProcessInfo> process_infos;
971   std::vector<const performance_manager::ProcessNode*> process_nodes =
972       graph->GetAllProcessNodes();
973   // Assign page nodes unique IDs within this lookup only.
974   base::flat_map<const performance_manager::PageNode*, uint64_t> page_id_map;
975   for (auto* process_node : process_nodes) {
976     if (process_node->GetProcessId() == base::kNullProcessId)
977       continue;
978 
979     ProcessInfo process_info;
980     process_info.pid = process_node->GetProcessId();
981     process_info.launch_time = process_node->GetLaunchTime();
982 
983     base::flat_set<const performance_manager::PageNode*> page_nodes =
984         performance_manager::GraphOperations::GetAssociatedPageNodes(
985             process_node);
986     for (const performance_manager::PageNode* page_node : page_nodes) {
987       if (page_node->GetUkmSourceID() == ukm::kInvalidSourceId)
988         continue;
989 
990       if (page_id_map.find(page_node) == page_id_map.end())
991         page_id_map.insert(std::make_pair(page_node, page_id_map.size() + 1));
992 
993       PageInfo page_info;
994       page_info.ukm_source_id = page_node->GetUkmSourceID();
995 
996       DCHECK(page_id_map.find(page_node) != page_id_map.end());
997       page_info.tab_id = page_id_map[page_node];
998       page_info.hosts_main_frame = HostsMainFrame(process_node, page_node);
999       page_info.is_visible = page_node->IsVisible();
1000       page_info.time_since_last_visibility_change =
1001           page_node->GetTimeSinceLastVisibilityChange();
1002       page_info.time_since_last_navigation =
1003           page_node->GetTimeSinceLastNavigation();
1004       process_info.page_infos.push_back(std::move(page_info));
1005     }
1006     process_infos.push_back(std::move(process_info));
1007   }
1008   std::move(callback).Run(std::move(process_infos));
1009 }
1010 
1011 ProcessMemoryMetricsEmitter::ProcessInfo::ProcessInfo() = default;
1012 
1013 ProcessMemoryMetricsEmitter::ProcessInfo::ProcessInfo(ProcessInfo&& other) =
1014     default;
1015 
1016 ProcessMemoryMetricsEmitter::ProcessInfo::~ProcessInfo() = default;
1017 
1018 ProcessMemoryMetricsEmitter::ProcessInfo&
1019 ProcessMemoryMetricsEmitter::ProcessInfo::operator=(const ProcessInfo& other) =
1020     default;
1021