// Copyright 2023 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/allocator/dispatcher/standard_hooks.h" #include "base/allocator/buildflags.h" #include "base/allocator/partition_allocator/partition_alloc.h" #include "base/allocator/partition_allocator/partition_alloc_buildflags.h" #include "base/allocator/partition_allocator/shim/allocator_shim.h" #include "base/sampling_heap_profiler/poisson_allocation_sampler.h" #if !BUILDFLAG(USE_ALLOCATION_EVENT_DISPATCHER) #if BUILDFLAG(USE_ALLOCATOR_SHIM) namespace base::allocator::dispatcher::allocator_shim_details { namespace { using allocator_shim::AllocatorDispatch; void* AllocFn(const AllocatorDispatch* self, size_t size, void* context) { void* address = self->next->alloc_function(self->next, size, context); PoissonAllocationSampler::RecordAlloc( address, size, AllocationSubsystem::kAllocatorShim, nullptr); return address; } void* AllocUncheckedFn(const AllocatorDispatch* self, size_t size, void* context) { void* address = self->next->alloc_unchecked_function(self->next, size, context); PoissonAllocationSampler::RecordAlloc( address, size, AllocationSubsystem::kAllocatorShim, nullptr); return address; } void* AllocZeroInitializedFn(const AllocatorDispatch* self, size_t n, size_t size, void* context) { void* address = self->next->alloc_zero_initialized_function(self->next, n, size, context); PoissonAllocationSampler::RecordAlloc( address, n * size, AllocationSubsystem::kAllocatorShim, nullptr); return address; } void* AllocAlignedFn(const AllocatorDispatch* self, size_t alignment, size_t size, void* context) { void* address = self->next->alloc_aligned_function(self->next, alignment, size, context); PoissonAllocationSampler::RecordAlloc( address, size, AllocationSubsystem::kAllocatorShim, nullptr); return address; } void* ReallocFn(const AllocatorDispatch* self, void* address, size_t size, void* context) { // Note: size == 0 actually performs free. PoissonAllocationSampler::RecordFree(address); address = self->next->realloc_function(self->next, address, size, context); PoissonAllocationSampler::RecordAlloc( address, size, AllocationSubsystem::kAllocatorShim, nullptr); return address; } void FreeFn(const AllocatorDispatch* self, void* address, void* context) { // Note: The RecordFree should be called before free_function // (here and in other places). // That is because we need to remove the recorded allocation sample before // free_function, as once the latter is executed the address becomes available // and can be allocated by another thread. That would be racy otherwise. PoissonAllocationSampler::RecordFree(address); self->next->free_function(self->next, address, context); } size_t GetSizeEstimateFn(const AllocatorDispatch* self, void* address, void* context) { return self->next->get_size_estimate_function(self->next, address, context); } bool ClaimedAddressFn(const AllocatorDispatch* self, void* address, void* context) { return self->next->claimed_address_function(self->next, address, context); } unsigned BatchMallocFn(const AllocatorDispatch* self, size_t size, void** results, unsigned num_requested, void* context) { unsigned num_allocated = self->next->batch_malloc_function( self->next, size, results, num_requested, context); for (unsigned i = 0; i < num_allocated; ++i) { PoissonAllocationSampler::RecordAlloc( results[i], size, AllocationSubsystem::kAllocatorShim, nullptr); } return num_allocated; } void BatchFreeFn(const AllocatorDispatch* self, void** to_be_freed, unsigned num_to_be_freed, void* context) { for (unsigned i = 0; i < num_to_be_freed; ++i) { PoissonAllocationSampler::RecordFree(to_be_freed[i]); } self->next->batch_free_function(self->next, to_be_freed, num_to_be_freed, context); } void FreeDefiniteSizeFn(const AllocatorDispatch* self, void* address, size_t size, void* context) { PoissonAllocationSampler::RecordFree(address); self->next->free_definite_size_function(self->next, address, size, context); } void TryFreeDefaultFn(const AllocatorDispatch* self, void* address, void* context) { PoissonAllocationSampler::RecordFree(address); self->next->try_free_default_function(self->next, address, context); } static void* AlignedMallocFn(const AllocatorDispatch* self, size_t size, size_t alignment, void* context) { void* address = self->next->aligned_malloc_function(self->next, size, alignment, context); PoissonAllocationSampler::RecordAlloc( address, size, AllocationSubsystem::kAllocatorShim, nullptr); return address; } static void* AlignedReallocFn(const AllocatorDispatch* self, void* address, size_t size, size_t alignment, void* context) { // Note: size == 0 actually performs free. PoissonAllocationSampler::RecordFree(address); address = self->next->aligned_realloc_function(self->next, address, size, alignment, context); PoissonAllocationSampler::RecordAlloc( address, size, AllocationSubsystem::kAllocatorShim, nullptr); return address; } static void AlignedFreeFn(const AllocatorDispatch* self, void* address, void* context) { PoissonAllocationSampler::RecordFree(address); self->next->aligned_free_function(self->next, address, context); } AllocatorDispatch g_allocator_dispatch = {&AllocFn, &AllocUncheckedFn, &AllocZeroInitializedFn, &AllocAlignedFn, &ReallocFn, &FreeFn, &GetSizeEstimateFn, &ClaimedAddressFn, &BatchMallocFn, &BatchFreeFn, &FreeDefiniteSizeFn, &TryFreeDefaultFn, &AlignedMallocFn, &AlignedReallocFn, &AlignedFreeFn, nullptr}; } // namespace } // namespace base::allocator::dispatcher::allocator_shim_details #endif // BUILDFLAG(USE_ALLOCATOR_SHIM) #if BUILDFLAG(USE_PARTITION_ALLOC) namespace base::allocator::dispatcher::partition_allocator_details { namespace { void PartitionAllocHook(void* address, size_t size, const char* type) { PoissonAllocationSampler::RecordAlloc( address, size, AllocationSubsystem::kPartitionAllocator, type); } void PartitionFreeHook(void* address) { PoissonAllocationSampler::RecordFree(address); } } // namespace } // namespace base::allocator::dispatcher::partition_allocator_details #endif // BUILDFLAG(USE_PARTITION_ALLOC) #endif // !BUILDFLAG(USE_ALLOCATION_EVENT_DISPATCHER) namespace base::allocator::dispatcher { #if !BUILDFLAG(USE_ALLOCATION_EVENT_DISPATCHER) void InstallStandardAllocatorHooks() { #if BUILDFLAG(USE_ALLOCATOR_SHIM) allocator_shim::InsertAllocatorDispatch( &allocator_shim_details::g_allocator_dispatch); #else // If the allocator shim isn't available, then we don't install any hooks. // There's no point in printing an error message, since this can regularly // happen for tests. #endif // BUILDFLAG(USE_ALLOCATOR_SHIM) #if BUILDFLAG(USE_PARTITION_ALLOC) partition_alloc::PartitionAllocHooks::SetObserverHooks( &partition_allocator_details::PartitionAllocHook, &partition_allocator_details::PartitionFreeHook); #endif // BUILDFLAG(USE_PARTITION_ALLOC) } #endif // !BUILDFLAG(USE_ALLOCATION_EVENT_DISPATCHER) } // namespace base::allocator::dispatcher