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