xref: /aosp_15_r20/external/cronet/base/trace_event/malloc_dump_provider.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2015 The Chromium Authors
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 "base/trace_event/malloc_dump_provider.h"
6 
7 #include <stddef.h>
8 
9 #include <unordered_map>
10 
11 #include "base/allocator/buildflags.h"
12 #include "base/debug/profiler.h"
13 #include "base/format_macros.h"
14 #include "base/metrics/histogram_functions.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/trace_event/process_memory_dump.h"
18 #include "base/trace_event/traced_value.h"
19 #include "build/build_config.h"
20 #include "partition_alloc/partition_alloc_buildflags.h"
21 #include "partition_alloc/partition_alloc_config.h"
22 #include "partition_alloc/partition_bucket_lookup.h"
23 #include "partition_alloc/shim/nonscannable_allocator.h"
24 
25 #if BUILDFLAG(IS_APPLE)
26 #include <malloc/malloc.h>
27 #else
28 #include <malloc.h>
29 #endif
30 #if BUILDFLAG(IS_WIN)
31 #include <windows.h>
32 #endif
33 
34 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
35 #include <features.h>
36 #endif
37 
38 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
39 #include "partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h"
40 #endif
41 
42 #if PA_CONFIG(THREAD_CACHE_ALLOC_STATS)
43 #include "partition_alloc/partition_alloc_constants.h"
44 #endif
45 
46 namespace base {
47 namespace trace_event {
48 
49 namespace {
50 #if BUILDFLAG(IS_WIN)
51 // A structure containing some information about a given heap.
52 struct WinHeapInfo {
53   size_t committed_size;
54   size_t uncommitted_size;
55   size_t allocated_size;
56   size_t block_count;
57 };
58 
59 // NOTE: crbug.com/665516
60 // Unfortunately, there is no safe way to collect information from secondary
61 // heaps due to limitations and racy nature of this piece of WinAPI.
WinHeapMemoryDumpImpl(WinHeapInfo * crt_heap_info)62 void WinHeapMemoryDumpImpl(WinHeapInfo* crt_heap_info) {
63   // Iterate through whichever heap our CRT is using.
64   HANDLE crt_heap = reinterpret_cast<HANDLE>(_get_heap_handle());
65   ::HeapLock(crt_heap);
66   PROCESS_HEAP_ENTRY heap_entry;
67   heap_entry.lpData = nullptr;
68   // Walk over all the entries in the main heap.
69   while (::HeapWalk(crt_heap, &heap_entry) != FALSE) {
70     if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {
71       crt_heap_info->allocated_size += heap_entry.cbData;
72       crt_heap_info->block_count++;
73     } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) {
74       crt_heap_info->committed_size += heap_entry.Region.dwCommittedSize;
75       crt_heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize;
76     }
77   }
78   CHECK(::HeapUnlock(crt_heap) == TRUE);
79 }
80 
ReportWinHeapStats(MemoryDumpLevelOfDetail level_of_detail,ProcessMemoryDump * pmd,size_t * total_virtual_size,size_t * resident_size,size_t * allocated_objects_size,size_t * allocated_objects_count)81 void ReportWinHeapStats(MemoryDumpLevelOfDetail level_of_detail,
82                         ProcessMemoryDump* pmd,
83                         size_t* total_virtual_size,
84                         size_t* resident_size,
85                         size_t* allocated_objects_size,
86                         size_t* allocated_objects_count) {
87   // This is too expensive on Windows, crbug.com/780735.
88   if (level_of_detail == MemoryDumpLevelOfDetail::kDetailed) {
89     WinHeapInfo main_heap_info = {};
90     WinHeapMemoryDumpImpl(&main_heap_info);
91     *total_virtual_size +=
92         main_heap_info.committed_size + main_heap_info.uncommitted_size;
93     // Resident size is approximated with committed heap size. Note that it is
94     // possible to do this with better accuracy on windows by intersecting the
95     // working set with the virtual memory ranges occuipied by the heap. It's
96     // not clear that this is worth it, as it's fairly expensive to do.
97     *resident_size += main_heap_info.committed_size;
98     *allocated_objects_size += main_heap_info.allocated_size;
99     *allocated_objects_count += main_heap_info.block_count;
100 
101     if (pmd) {
102       MemoryAllocatorDump* win_heap_dump =
103           pmd->CreateAllocatorDump("malloc/win_heap");
104       win_heap_dump->AddScalar(MemoryAllocatorDump::kNameSize,
105                                MemoryAllocatorDump::kUnitsBytes,
106                                main_heap_info.allocated_size);
107     }
108   }
109 }
110 #endif  // BUILDFLAG(IS_WIN)
111 
112 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
ReportPartitionAllocStats(ProcessMemoryDump * pmd,MemoryDumpLevelOfDetail level_of_detail,size_t * total_virtual_size,size_t * resident_size,size_t * allocated_objects_size,size_t * allocated_objects_count,uint64_t * syscall_count,size_t * cumulative_brp_quarantined_size,size_t * cumulative_brp_quarantined_count)113 void ReportPartitionAllocStats(ProcessMemoryDump* pmd,
114                                MemoryDumpLevelOfDetail level_of_detail,
115                                size_t* total_virtual_size,
116                                size_t* resident_size,
117                                size_t* allocated_objects_size,
118                                size_t* allocated_objects_count,
119                                uint64_t* syscall_count,
120                                size_t* cumulative_brp_quarantined_size,
121                                size_t* cumulative_brp_quarantined_count) {
122   MemoryDumpPartitionStatsDumper partition_stats_dumper("malloc", pmd,
123                                                         level_of_detail);
124   bool is_light_dump = level_of_detail == MemoryDumpLevelOfDetail::kBackground;
125 
126   auto* allocator = allocator_shim::internal::PartitionAllocMalloc::Allocator();
127   allocator->DumpStats("allocator", is_light_dump, &partition_stats_dumper);
128 
129   auto* original_allocator =
130       allocator_shim::internal::PartitionAllocMalloc::OriginalAllocator();
131   if (original_allocator) {
132     original_allocator->DumpStats("original", is_light_dump,
133                                   &partition_stats_dumper);
134   }
135   auto& nonscannable_allocator =
136       allocator_shim::NonScannableAllocator::Instance();
137   if (auto* root = nonscannable_allocator.root())
138     root->DumpStats("nonscannable", is_light_dump, &partition_stats_dumper);
139   auto& nonquarantinable_allocator =
140       allocator_shim::NonQuarantinableAllocator::Instance();
141   if (auto* root = nonquarantinable_allocator.root())
142     root->DumpStats("nonquarantinable", is_light_dump, &partition_stats_dumper);
143 
144   *total_virtual_size += partition_stats_dumper.total_resident_bytes();
145   *resident_size += partition_stats_dumper.total_resident_bytes();
146   *allocated_objects_size += partition_stats_dumper.total_active_bytes();
147   *allocated_objects_count += partition_stats_dumper.total_active_count();
148   *syscall_count += partition_stats_dumper.syscall_count();
149 #if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
150   *cumulative_brp_quarantined_size +=
151       partition_stats_dumper.cumulative_brp_quarantined_bytes();
152   *cumulative_brp_quarantined_count +=
153       partition_stats_dumper.cumulative_brp_quarantined_count();
154 #endif  // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
155 }
156 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
157 
158 #if !BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_APPLE)
ReportAppleAllocStats(size_t * total_virtual_size,size_t * resident_size,size_t * allocated_objects_size)159 void ReportAppleAllocStats(size_t* total_virtual_size,
160                            size_t* resident_size,
161                            size_t* allocated_objects_size) {
162   malloc_statistics_t stats = {0};
163   malloc_zone_statistics(nullptr, &stats);
164   *total_virtual_size += stats.size_allocated;
165   *allocated_objects_size += stats.size_in_use;
166 
167   // Resident size is approximated pretty well by stats.max_size_in_use.
168   // However, on macOS, freed blocks are both resident and reusable, which is
169   // semantically equivalent to deallocated. The implementation of libmalloc
170   // will also only hold a fixed number of freed regions before actually
171   // starting to deallocate them, so stats.max_size_in_use is also not
172   // representative of the peak size. As a result, stats.max_size_in_use is
173   // typically somewhere between actually resident [non-reusable] pages, and
174   // peak size. This is not very useful, so we just use stats.size_in_use for
175   // resident_size, even though it's an underestimate and fails to account for
176   // fragmentation. See
177   // https://bugs.chromium.org/p/chromium/issues/detail?id=695263#c1.
178   *resident_size += stats.size_in_use;
179 }
180 #endif
181 
182 #if (BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_ANDROID)) || \
183     (!BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && !BUILDFLAG(IS_WIN) &&    \
184      !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_FUCHSIA))
ReportMallinfoStats(ProcessMemoryDump * pmd,size_t * total_virtual_size,size_t * resident_size,size_t * allocated_objects_size,size_t * allocated_objects_count)185 void ReportMallinfoStats(ProcessMemoryDump* pmd,
186                          size_t* total_virtual_size,
187                          size_t* resident_size,
188                          size_t* allocated_objects_size,
189                          size_t* allocated_objects_count) {
190 #if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
191 #if __GLIBC_PREREQ(2, 33)
192 #define MALLINFO2_FOUND_IN_LIBC
193   struct mallinfo2 info = mallinfo2();
194 #endif
195 #endif  // defined(__GLIBC__) && defined(__GLIBC_PREREQ)
196 #if !defined(MALLINFO2_FOUND_IN_LIBC)
197   struct mallinfo info = mallinfo();
198 #endif
199 #undef MALLINFO2_FOUND_IN_LIBC
200   // In case of Android's jemalloc |arena| is 0 and the outer pages size is
201   // reported by |hblkhd|. In case of dlmalloc the total is given by
202   // |arena| + |hblkhd|. For more details see link: http://goo.gl/fMR8lF.
203   *total_virtual_size += checked_cast<size_t>(info.arena + info.hblkhd);
204   size_t total_allocated_size = checked_cast<size_t>(info.uordblks);
205   *resident_size += total_allocated_size;
206 
207   // Total allocated space is given by |uordblks|.
208   *allocated_objects_size += total_allocated_size;
209 
210   if (pmd) {
211     MemoryAllocatorDump* sys_alloc_dump =
212         pmd->CreateAllocatorDump("malloc/sys_malloc");
213     sys_alloc_dump->AddScalar(MemoryAllocatorDump::kNameSize,
214                               MemoryAllocatorDump::kUnitsBytes,
215                               total_allocated_size);
216   }
217 }
218 #endif
219 
220 #if BUILDFLAG(USE_PARTITION_ALLOC)
ReportPartitionAllocThreadCacheStats(ProcessMemoryDump * pmd,MemoryAllocatorDump * dump,const partition_alloc::ThreadCacheStats & stats,const std::string & metrics_suffix,bool detailed)221 void ReportPartitionAllocThreadCacheStats(
222     ProcessMemoryDump* pmd,
223     MemoryAllocatorDump* dump,
224     const partition_alloc::ThreadCacheStats& stats,
225     const std::string& metrics_suffix,
226     bool detailed) {
227   dump->AddScalar("alloc_count", MemoryAllocatorDump::kTypeScalar,
228                   stats.alloc_count);
229   dump->AddScalar("alloc_hits", MemoryAllocatorDump::kTypeScalar,
230                   stats.alloc_hits);
231   dump->AddScalar("alloc_misses", MemoryAllocatorDump::kTypeScalar,
232                   stats.alloc_misses);
233 
234   dump->AddScalar("alloc_miss_empty", MemoryAllocatorDump::kTypeScalar,
235                   stats.alloc_miss_empty);
236   dump->AddScalar("alloc_miss_too_large", MemoryAllocatorDump::kTypeScalar,
237                   stats.alloc_miss_too_large);
238 
239   dump->AddScalar("cache_fill_count", MemoryAllocatorDump::kTypeScalar,
240                   stats.cache_fill_count);
241   dump->AddScalar("cache_fill_hits", MemoryAllocatorDump::kTypeScalar,
242                   stats.cache_fill_hits);
243   dump->AddScalar("cache_fill_misses", MemoryAllocatorDump::kTypeScalar,
244                   stats.cache_fill_misses);
245 
246   dump->AddScalar("batch_fill_count", MemoryAllocatorDump::kTypeScalar,
247                   stats.batch_fill_count);
248 
249   dump->AddScalar(MemoryAllocatorDump::kNameSize,
250                   MemoryAllocatorDump::kUnitsBytes, stats.bucket_total_memory);
251   dump->AddScalar("metadata_overhead", MemoryAllocatorDump::kUnitsBytes,
252                   stats.metadata_overhead);
253 
254   if (stats.alloc_count) {
255     int hit_rate_percent =
256         static_cast<int>((100 * stats.alloc_hits) / stats.alloc_count);
257     base::UmaHistogramPercentage(
258         "Memory.PartitionAlloc.ThreadCache.HitRate" + metrics_suffix,
259         hit_rate_percent);
260     int batch_fill_rate_percent =
261         static_cast<int>((100 * stats.batch_fill_count) / stats.alloc_count);
262     base::UmaHistogramPercentage(
263         "Memory.PartitionAlloc.ThreadCache.BatchFillRate" + metrics_suffix,
264         batch_fill_rate_percent);
265 
266 #if PA_CONFIG(THREAD_CACHE_ALLOC_STATS)
267     if (detailed) {
268       partition_alloc::internal::BucketIndexLookup lookup{};
269       std::string name = dump->absolute_name();
270       for (size_t i = 0; i < partition_alloc::kNumBuckets; i++) {
271         size_t bucket_size = lookup.bucket_sizes()[i];
272         if (bucket_size == partition_alloc::kInvalidBucketSize)
273           continue;
274         // Covers all normal buckets, that is up to ~1MiB, so 7 digits.
275         std::string dump_name =
276             base::StringPrintf("%s/buckets_alloc/%07d", name.c_str(),
277                                static_cast<int>(bucket_size));
278         auto* buckets_alloc_dump = pmd->CreateAllocatorDump(dump_name);
279         buckets_alloc_dump->AddScalar("count",
280                                       MemoryAllocatorDump::kUnitsObjects,
281                                       stats.allocs_per_bucket_[i]);
282       }
283     }
284 #endif  // PA_CONFIG(THREAD_CACHE_ALLOC_STATS)
285   }
286 }
287 
ReportPartitionAllocLightweightQuarantineStats(MemoryAllocatorDump * dump,const partition_alloc::LightweightQuarantineStats & stats)288 void ReportPartitionAllocLightweightQuarantineStats(
289     MemoryAllocatorDump* dump,
290     const partition_alloc::LightweightQuarantineStats& stats) {
291   dump->AddScalar("count", MemoryAllocatorDump::kUnitsObjects, stats.count);
292   dump->AddScalar("size_in_bytes", MemoryAllocatorDump::kUnitsBytes,
293                   stats.size_in_bytes);
294   dump->AddScalar("cumulative_count", MemoryAllocatorDump::kUnitsObjects,
295                   stats.cumulative_count);
296   dump->AddScalar("cumulative_size_in_bytes", MemoryAllocatorDump::kUnitsBytes,
297                   stats.cumulative_size_in_bytes);
298   dump->AddScalar("quarantine_miss_count", MemoryAllocatorDump::kUnitsObjects,
299                   stats.quarantine_miss_count);
300 }
301 #endif  // BUILDFLAG(USE_PARTITION_ALLOC)
302 
303 }  // namespace
304 
305 // static
306 const char MallocDumpProvider::kAllocatedObjects[] = "malloc/allocated_objects";
307 
308 // static
GetInstance()309 MallocDumpProvider* MallocDumpProvider::GetInstance() {
310   return Singleton<MallocDumpProvider,
311                    LeakySingletonTraits<MallocDumpProvider>>::get();
312 }
313 
314 MallocDumpProvider::MallocDumpProvider() = default;
315 MallocDumpProvider::~MallocDumpProvider() = default;
316 
317 // Called at trace dump point time. Creates a snapshot the memory counters for
318 // the current process.
OnMemoryDump(const MemoryDumpArgs & args,ProcessMemoryDump * pmd)319 bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
320                                       ProcessMemoryDump* pmd) {
321   {
322     base::AutoLock auto_lock(emit_metrics_on_memory_dump_lock_);
323     if (!emit_metrics_on_memory_dump_) {
324       return true;
325     }
326   }
327 
328   size_t total_virtual_size = 0;
329   size_t resident_size = 0;
330   size_t allocated_objects_size = 0;
331   size_t allocated_objects_count = 0;
332   uint64_t syscall_count = 0;
333   size_t cumulative_brp_quarantined_size = 0;
334   size_t cumulative_brp_quarantined_count = 0;
335 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
336   uint64_t pa_only_resident_size;
337   uint64_t pa_only_allocated_objects_size;
338 #endif
339 
340 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
341   ReportPartitionAllocStats(
342       pmd, args.level_of_detail, &total_virtual_size, &resident_size,
343       &allocated_objects_size, &allocated_objects_count, &syscall_count,
344       &cumulative_brp_quarantined_size, &cumulative_brp_quarantined_count);
345 
346   pa_only_resident_size = resident_size;
347   pa_only_allocated_objects_size = allocated_objects_size;
348 
349   // Even when PartitionAlloc is used, WinHeap / System malloc is still used as
350   // well, report its statistics.
351 #if BUILDFLAG(IS_ANDROID)
352   ReportMallinfoStats(pmd, &total_virtual_size, &resident_size,
353                       &allocated_objects_size, &allocated_objects_count);
354 #elif BUILDFLAG(IS_WIN)
355   ReportWinHeapStats(args.level_of_detail, pmd, &total_virtual_size,
356                      &resident_size, &allocated_objects_size,
357                      &allocated_objects_count);
358 #endif  // BUILDFLAG(IS_ANDROID), BUILDFLAG(IS_WIN)
359 
360 #elif BUILDFLAG(IS_APPLE)
361   ReportAppleAllocStats(&total_virtual_size, &resident_size,
362                         &allocated_objects_size);
363 #elif BUILDFLAG(IS_WIN)
364   ReportWinHeapStats(args.level_of_detail, nullptr, &total_virtual_size,
365                      &resident_size, &allocated_objects_size,
366                      &allocated_objects_count);
367 #elif BUILDFLAG(IS_FUCHSIA)
368 // TODO(fuchsia): Port, see https://crbug.com/706592.
369 #else
370   ReportMallinfoStats(/*pmd=*/nullptr, &total_virtual_size, &resident_size,
371                       &allocated_objects_size, &allocated_objects_count);
372 #endif
373 
374   MemoryAllocatorDump* outer_dump = pmd->CreateAllocatorDump("malloc");
375   outer_dump->AddScalar("virtual_size", MemoryAllocatorDump::kUnitsBytes,
376                         total_virtual_size);
377   outer_dump->AddScalar(MemoryAllocatorDump::kNameSize,
378                         MemoryAllocatorDump::kUnitsBytes, resident_size);
379 
380   MemoryAllocatorDump* inner_dump = pmd->CreateAllocatorDump(kAllocatedObjects);
381   inner_dump->AddScalar(MemoryAllocatorDump::kNameSize,
382                         MemoryAllocatorDump::kUnitsBytes,
383                         allocated_objects_size);
384   if (allocated_objects_count != 0) {
385     inner_dump->AddScalar(MemoryAllocatorDump::kNameObjectCount,
386                           MemoryAllocatorDump::kUnitsObjects,
387                           allocated_objects_count);
388   }
389 
390 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
391   base::trace_event::MemoryAllocatorDump* partitions_dump =
392       pmd->CreateAllocatorDump("malloc/partitions");
393   pmd->AddOwnershipEdge(inner_dump->guid(), partitions_dump->guid());
394 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
395 
396   int64_t waste = static_cast<int64_t>(resident_size - allocated_objects_size);
397 
398   // With PartitionAlloc, reported size under malloc/partitions is the resident
399   // size, so it already includes fragmentation. Meaning that "malloc/"'s size
400   // would double-count fragmentation if we report it under
401   // "malloc/metadata_fragmentation_caches" as well.
402   //
403   // Still report waste, as on some platforms, PartitionAlloc doesn't capture
404   // all of malloc()'s memory footprint.
405 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
406   int64_t pa_waste = static_cast<int64_t>(pa_only_resident_size -
407                                           pa_only_allocated_objects_size);
408   waste -= pa_waste;
409 #endif
410 
411   if (waste > 0) {
412     // Explicitly specify why is extra memory resident. In mac and ios it
413     // accounts for the fragmentation and metadata.
414     MemoryAllocatorDump* other_dump =
415         pmd->CreateAllocatorDump("malloc/metadata_fragmentation_caches");
416     other_dump->AddScalar(MemoryAllocatorDump::kNameSize,
417                           MemoryAllocatorDump::kUnitsBytes,
418                           static_cast<uint64_t>(waste));
419   }
420 
421   ReportPerMinuteStats(syscall_count, cumulative_brp_quarantined_size,
422                        cumulative_brp_quarantined_count, outer_dump,
423 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
424                        partitions_dump
425 #else
426                        nullptr
427 #endif
428   );
429 
430   return true;
431 }
432 
ReportPerMinuteStats(uint64_t syscall_count,size_t cumulative_brp_quarantined_bytes,size_t cumulative_brp_quarantined_count,MemoryAllocatorDump * malloc_dump,MemoryAllocatorDump * partition_alloc_dump)433 void MallocDumpProvider::ReportPerMinuteStats(
434     uint64_t syscall_count,
435     size_t cumulative_brp_quarantined_bytes,
436     size_t cumulative_brp_quarantined_count,
437     MemoryAllocatorDump* malloc_dump,
438     MemoryAllocatorDump* partition_alloc_dump) {
439 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
440   uint64_t new_syscalls = syscall_count - last_syscall_count_;
441   size_t new_brp_quarantined_bytes =
442       cumulative_brp_quarantined_bytes - last_cumulative_brp_quarantined_bytes_;
443   size_t new_brp_quarantined_count =
444       cumulative_brp_quarantined_count - last_cumulative_brp_quarantined_count_;
445   base::TimeDelta time_since_last_dump =
446       base::TimeTicks::Now() - last_memory_dump_time_;
447   uint64_t syscalls_per_minute = static_cast<uint64_t>(
448       (60 * new_syscalls) / time_since_last_dump.InSecondsF());
449   malloc_dump->AddScalar("syscalls_per_minute", "count", syscalls_per_minute);
450   if (partition_alloc_dump) {
451     size_t brp_quarantined_bytes_per_minute =
452         (60 * new_brp_quarantined_bytes) / time_since_last_dump.InSecondsF();
453     size_t brp_quarantined_count_per_minute =
454         (60 * new_brp_quarantined_count) / time_since_last_dump.InSecondsF();
455     partition_alloc_dump->AddScalar("brp_quarantined_bytes_per_minute",
456                                     MemoryAllocatorDump::kUnitsBytes,
457                                     brp_quarantined_bytes_per_minute);
458     partition_alloc_dump->AddScalar("brp_quarantined_count_per_minute",
459                                     MemoryAllocatorDump::kNameObjectCount,
460                                     brp_quarantined_count_per_minute);
461   }
462 
463   last_memory_dump_time_ = base::TimeTicks::Now();
464   last_syscall_count_ = syscall_count;
465   last_cumulative_brp_quarantined_bytes_ = cumulative_brp_quarantined_bytes;
466   last_cumulative_brp_quarantined_count_ = cumulative_brp_quarantined_count;
467 #endif  // BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
468 }
469 
470 #if BUILDFLAG(USE_PARTITION_ALLOC)
GetPartitionDumpName(const char * root_name,const char * partition_name)471 std::string GetPartitionDumpName(const char* root_name,
472                                  const char* partition_name) {
473   return base::StringPrintf("%s/%s/%s", root_name,
474                             MemoryDumpPartitionStatsDumper::kPartitionsDumpName,
475                             partition_name);
476 }
477 
MemoryDumpPartitionStatsDumper(const char * root_name,ProcessMemoryDump * memory_dump,MemoryDumpLevelOfDetail level_of_detail)478 MemoryDumpPartitionStatsDumper::MemoryDumpPartitionStatsDumper(
479     const char* root_name,
480     ProcessMemoryDump* memory_dump,
481     MemoryDumpLevelOfDetail level_of_detail)
482     : root_name_(root_name),
483       memory_dump_(memory_dump),
484       detailed_(level_of_detail != MemoryDumpLevelOfDetail::kBackground) {}
485 
PartitionDumpTotals(const char * partition_name,const partition_alloc::PartitionMemoryStats * memory_stats)486 void MemoryDumpPartitionStatsDumper::PartitionDumpTotals(
487     const char* partition_name,
488     const partition_alloc::PartitionMemoryStats* memory_stats) {
489   total_mmapped_bytes_ += memory_stats->total_mmapped_bytes;
490   total_resident_bytes_ += memory_stats->total_resident_bytes;
491   total_active_bytes_ += memory_stats->total_active_bytes;
492   total_active_count_ += memory_stats->total_active_count;
493   syscall_count_ += memory_stats->syscall_count;
494 #if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
495   cumulative_brp_quarantined_bytes_ +=
496       memory_stats->cumulative_brp_quarantined_bytes;
497   cumulative_brp_quarantined_count_ +=
498       memory_stats->cumulative_brp_quarantined_count;
499 #endif  // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
500 
501   std::string dump_name = GetPartitionDumpName(root_name_, partition_name);
502   MemoryAllocatorDump* allocator_dump =
503       memory_dump_->CreateAllocatorDump(dump_name);
504 
505   auto total_committed_bytes = memory_stats->total_committed_bytes;
506   auto total_active_bytes = memory_stats->total_active_bytes;
507   size_t wasted = total_committed_bytes - total_active_bytes;
508   DCHECK_GE(total_committed_bytes, total_active_bytes);
509   size_t fragmentation =
510       total_committed_bytes == 0 ? 0 : 100 * wasted / total_committed_bytes;
511 
512   allocator_dump->AddScalar(MemoryAllocatorDump::kNameSize,
513                             MemoryAllocatorDump::kUnitsBytes,
514                             memory_stats->total_resident_bytes);
515   allocator_dump->AddScalar("allocated_objects_size",
516                             MemoryAllocatorDump::kUnitsBytes,
517                             memory_stats->total_active_bytes);
518   allocator_dump->AddScalar("allocated_objects_count", "count",
519                             memory_stats->total_active_count);
520   allocator_dump->AddScalar("virtual_size", MemoryAllocatorDump::kUnitsBytes,
521                             memory_stats->total_mmapped_bytes);
522   allocator_dump->AddScalar("virtual_committed_size",
523                             MemoryAllocatorDump::kUnitsBytes,
524                             memory_stats->total_committed_bytes);
525   allocator_dump->AddScalar("max_committed_size",
526                             MemoryAllocatorDump::kUnitsBytes,
527                             memory_stats->max_committed_bytes);
528   allocator_dump->AddScalar("allocated_size", MemoryAllocatorDump::kUnitsBytes,
529                             memory_stats->total_allocated_bytes);
530   allocator_dump->AddScalar("max_allocated_size",
531                             MemoryAllocatorDump::kUnitsBytes,
532                             memory_stats->max_allocated_bytes);
533   allocator_dump->AddScalar("decommittable_size",
534                             MemoryAllocatorDump::kUnitsBytes,
535                             memory_stats->total_decommittable_bytes);
536   allocator_dump->AddScalar("discardable_size",
537                             MemoryAllocatorDump::kUnitsBytes,
538                             memory_stats->total_discardable_bytes);
539 #if BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
540   allocator_dump->AddScalar("brp_quarantined_size",
541                             MemoryAllocatorDump::kUnitsBytes,
542                             memory_stats->total_brp_quarantined_bytes);
543   allocator_dump->AddScalar("brp_quarantined_count", "count",
544                             memory_stats->total_brp_quarantined_count);
545 #endif  // BUILDFLAG(ENABLE_BACKUP_REF_PTR_SUPPORT)
546   allocator_dump->AddScalar("syscall_count", "count",
547                             memory_stats->syscall_count);
548   allocator_dump->AddScalar("syscall_total_time_ms", "ms",
549                             memory_stats->syscall_total_time_ns / 1e6);
550   allocator_dump->AddScalar("fragmentation", "percent", fragmentation);
551   allocator_dump->AddScalar("wasted", MemoryAllocatorDump::kUnitsBytes, wasted);
552 
553   if (memory_stats->has_thread_cache) {
554     const auto& thread_cache_stats = memory_stats->current_thread_cache_stats;
555     auto* thread_cache_dump = memory_dump_->CreateAllocatorDump(
556         dump_name + "/thread_cache/main_thread");
557     ReportPartitionAllocThreadCacheStats(memory_dump_, thread_cache_dump,
558                                          thread_cache_stats, ".MainThread",
559                                          detailed_);
560 
561     const auto& all_thread_caches_stats = memory_stats->all_thread_caches_stats;
562     auto* all_thread_caches_dump =
563         memory_dump_->CreateAllocatorDump(dump_name + "/thread_cache");
564     ReportPartitionAllocThreadCacheStats(memory_dump_, all_thread_caches_dump,
565                                          all_thread_caches_stats, "",
566                                          detailed_);
567   }
568 
569   if (memory_stats->has_scheduler_loop_quarantine) {
570     MemoryAllocatorDump* quarantine_dump_total =
571         memory_dump_->CreateAllocatorDump(dump_name +
572                                           "/scheduler_loop_quarantine");
573     ReportPartitionAllocLightweightQuarantineStats(
574         quarantine_dump_total,
575         memory_stats->scheduler_loop_quarantine_stats_total);
576   }
577 }
578 
PartitionsDumpBucketStats(const char * partition_name,const partition_alloc::PartitionBucketMemoryStats * memory_stats)579 void MemoryDumpPartitionStatsDumper::PartitionsDumpBucketStats(
580     const char* partition_name,
581     const partition_alloc::PartitionBucketMemoryStats* memory_stats) {
582   DCHECK(memory_stats->is_valid);
583   std::string dump_name = GetPartitionDumpName(root_name_, partition_name);
584   if (memory_stats->is_direct_map) {
585     dump_name.append(base::StringPrintf("/buckets/directMap_%" PRIu64, ++uid_));
586   } else {
587     // Normal buckets go up to ~1MiB, 7 digits.
588     dump_name.append(base::StringPrintf("/buckets/bucket_%07" PRIu32,
589                                         memory_stats->bucket_slot_size));
590   }
591 
592   MemoryAllocatorDump* allocator_dump =
593       memory_dump_->CreateAllocatorDump(dump_name);
594   allocator_dump->AddScalar(MemoryAllocatorDump::kNameSize,
595                             MemoryAllocatorDump::kUnitsBytes,
596                             memory_stats->resident_bytes);
597   allocator_dump->AddScalar("allocated_objects_size",
598                             MemoryAllocatorDump::kUnitsBytes,
599                             memory_stats->active_bytes);
600   allocator_dump->AddScalar("slot_size", MemoryAllocatorDump::kUnitsBytes,
601                             memory_stats->bucket_slot_size);
602   allocator_dump->AddScalar("decommittable_size",
603                             MemoryAllocatorDump::kUnitsBytes,
604                             memory_stats->decommittable_bytes);
605   allocator_dump->AddScalar("discardable_size",
606                             MemoryAllocatorDump::kUnitsBytes,
607                             memory_stats->discardable_bytes);
608   // TODO(bartekn): Rename the scalar names.
609   allocator_dump->AddScalar("total_slot_span_size",
610                             MemoryAllocatorDump::kUnitsBytes,
611                             memory_stats->allocated_slot_span_size);
612   allocator_dump->AddScalar("active_slot_spans",
613                             MemoryAllocatorDump::kUnitsObjects,
614                             memory_stats->num_active_slot_spans);
615   allocator_dump->AddScalar("full_slot_spans",
616                             MemoryAllocatorDump::kUnitsObjects,
617                             memory_stats->num_full_slot_spans);
618   allocator_dump->AddScalar("empty_slot_spans",
619                             MemoryAllocatorDump::kUnitsObjects,
620                             memory_stats->num_empty_slot_spans);
621   allocator_dump->AddScalar("decommitted_slot_spans",
622                             MemoryAllocatorDump::kUnitsObjects,
623                             memory_stats->num_decommitted_slot_spans);
624 }
625 #endif  // BUILDFLAG(USE_PARTITION_ALLOC)
626 
627 }  // namespace trace_event
628 }  // namespace base
629