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