#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "base/trace_event/malloc_dump_provider.h"
#include <stddef.h>
#include <unordered_map>
#include "base/allocator/buildflags.h"
#include "base/debug/profiler.h"
#include "base/format_macros.h"
#include "base/metrics/histogram_functions.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/traced_value.h"
#include "build/build_config.h"
#include "partition_alloc/buildflags.h"
#include "partition_alloc/partition_alloc_config.h"
#include "partition_alloc/partition_bucket_lookup.h"
#if BUILDFLAG(IS_APPLE)
#include <malloc/malloc.h>
#else
#include <malloc.h>
#endif
#if BUILDFLAG(IS_WIN)
#include <windows.h>
#endif
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID)
#include <features.h>
#endif
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
#include "base/no_destructor.h"
#include "partition_alloc/shim/allocator_shim_default_dispatch_to_partition_alloc.h"
#endif
#if PA_CONFIG(THREAD_CACHE_ALLOC_STATS)
#include "partition_alloc/partition_alloc_constants.h"
#endif
namespace base {
namespace trace_event {
namespace {
#if BUILDFLAG(IS_WIN)
struct WinHeapInfo {
size_t committed_size;
size_t uncommitted_size;
size_t allocated_size;
size_t block_count;
};
void WinHeapMemoryDumpImpl(WinHeapInfo* crt_heap_info) {
HANDLE crt_heap = reinterpret_cast<HANDLE>(_get_heap_handle());
::HeapLock(crt_heap);
PROCESS_HEAP_ENTRY heap_entry;
heap_entry.lpData = nullptr;
while (::HeapWalk(crt_heap, &heap_entry) != FALSE) {
if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {
crt_heap_info->allocated_size += heap_entry.cbData;
crt_heap_info->block_count++;
} else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) {
crt_heap_info->committed_size += heap_entry.Region.dwCommittedSize;
crt_heap_info->uncommitted_size += heap_entry.Region.dwUnCommittedSize;
}
}
CHECK(::HeapUnlock(crt_heap) == TRUE);
}
void 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) {
if (level_of_detail == MemoryDumpLevelOfDetail::kDetailed) {
WinHeapInfo main_heap_info = {};
WinHeapMemoryDumpImpl(&main_heap_info);
*total_virtual_size +=
main_heap_info.committed_size + main_heap_info.uncommitted_size;
*resident_size += main_heap_info.committed_size;
*allocated_objects_size += main_heap_info.allocated_size;
*allocated_objects_count += main_heap_info.block_count;
if (pmd) {
MemoryAllocatorDump* win_heap_dump =
pmd->CreateAllocatorDump("malloc/win_heap");
win_heap_dump->AddScalar(MemoryAllocatorDump::kNameSize,
MemoryAllocatorDump::kUnitsBytes,
main_heap_info.allocated_size);
}
}
}
#endif
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
void 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) { … }
#endif
#if !PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_APPLE)
void ReportAppleAllocStats(size_t* total_virtual_size,
size_t* resident_size,
size_t* allocated_objects_size) {
malloc_statistics_t stats = {0};
malloc_zone_statistics(nullptr, &stats);
*total_virtual_size += stats.size_allocated;
*allocated_objects_size += stats.size_in_use;
*resident_size += stats.size_in_use;
}
#endif
#if (PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && BUILDFLAG(IS_ANDROID)) || \
(!PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && !BUILDFLAG(IS_WIN) && \
!BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_FUCHSIA))
void ReportMallinfoStats(ProcessMemoryDump* pmd,
size_t* total_virtual_size,
size_t* resident_size,
size_t* allocated_objects_size,
size_t* allocated_objects_count) {
#if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
#if __GLIBC_PREREQ(2, 33)
#define MALLINFO2_FOUND_IN_LIBC
struct mallinfo2 info = mallinfo2();
#endif
#endif
#if !defined(MALLINFO2_FOUND_IN_LIBC)
struct mallinfo info = mallinfo();
#endif
#undef MALLINFO2_FOUND_IN_LIBC
*total_virtual_size += checked_cast<size_t>(info.arena + info.hblkhd);
size_t total_allocated_size = checked_cast<size_t>(info.uordblks);
*resident_size += total_allocated_size;
*allocated_objects_size += total_allocated_size;
if (pmd) {
MemoryAllocatorDump* sys_alloc_dump =
pmd->CreateAllocatorDump("malloc/sys_malloc");
sys_alloc_dump->AddScalar(MemoryAllocatorDump::kNameSize,
MemoryAllocatorDump::kUnitsBytes,
total_allocated_size);
}
}
#endif
#if PA_BUILDFLAG(USE_PARTITION_ALLOC)
void ReportPartitionAllocThreadCacheStats(
ProcessMemoryDump* pmd,
MemoryAllocatorDump* dump,
const partition_alloc::ThreadCacheStats& stats,
const std::string& metrics_suffix,
bool detailed) { … }
void ReportPartitionAllocLightweightQuarantineStats(
MemoryAllocatorDump* dump,
const partition_alloc::LightweightQuarantineStats& stats) { … }
#endif
}
const char MallocDumpProvider::kAllocatedObjects[] = …;
MallocDumpProvider* MallocDumpProvider::GetInstance() { … }
#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
void MallocDumpProvider::SetExtremeLUDGetStatsCallback(
ExtremeLUDGetStatsCallback callback) { … }
MallocDumpProvider::ExtremeLUDGetStatsCallback&
MallocDumpProvider::GetExtremeLUDGetStatsCallback() { … }
#endif
MallocDumpProvider::MallocDumpProvider() = default;
MallocDumpProvider::~MallocDumpProvider() = default;
bool MallocDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
ProcessMemoryDump* pmd) { … }
void MallocDumpProvider::ReportPerMinuteStats(
uint64_t syscall_count,
size_t cumulative_brp_quarantined_bytes,
size_t cumulative_brp_quarantined_count,
const ExtremeLUDStats& elud_stats,
MemoryAllocatorDump* malloc_dump,
MemoryAllocatorDump* partition_alloc_dump,
MemoryAllocatorDump* elud_dump) { … }
#if PA_BUILDFLAG(USE_PARTITION_ALLOC)
std::string GetPartitionDumpName(const char* root_name,
const char* partition_name) { … }
MemoryDumpPartitionStatsDumper::MemoryDumpPartitionStatsDumper(
const char* root_name,
ProcessMemoryDump* memory_dump,
MemoryDumpLevelOfDetail level_of_detail)
: … { … }
void MemoryDumpPartitionStatsDumper::PartitionDumpTotals(
const char* partition_name,
const partition_alloc::PartitionMemoryStats* memory_stats) { … }
void MemoryDumpPartitionStatsDumper::PartitionsDumpBucketStats(
const char* partition_name,
const partition_alloc::PartitionBucketMemoryStats* memory_stats) { … }
#endif
}
}