blob: 2d29d08dc859b16b6e67e6bd68f1a4f924486e42 [file] [log] [blame]
// 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