blob: 94d5a1f52a4d52a5a55395f851a43e1f88263db7 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif
#ifndef BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_
#define BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_
#include "base/allocator/dispatcher/configuration.h"
#include "base/allocator/dispatcher/internal/dispatch_data.h"
#include "base/allocator/dispatcher/internal/tools.h"
#include "base/allocator/dispatcher/memory_tagging.h"
#include "base/allocator/dispatcher/notification_data.h"
#include "base/allocator/dispatcher/subsystem.h"
#include "base/check.h"
#include "base/compiler_specific.h"
#include "partition_alloc/buildflags.h"
#if PA_BUILDFLAG(USE_PARTITION_ALLOC)
#include "partition_alloc/partition_alloc_allocation_data.h" // nogncheck
#endif
#if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
#include "partition_alloc/shim/allocator_shim.h"
#endif
#include <tuple>
namespace base::allocator::dispatcher::internal {
#if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
using allocator_shim::AllocatorDispatch;
#endif
template <typename CheckObserverPredicate,
typename... ObserverTypes,
size_t... Indices>
void inline PerformObserverCheck(const std::tuple<ObserverTypes...>& observers,
std::index_sequence<Indices...>,
CheckObserverPredicate check_observer) {
([](bool b) { DCHECK(b); }(check_observer(std::get<Indices>(observers))),
...);
}
template <typename... ObserverTypes, size_t... Indices>
ALWAYS_INLINE void PerformAllocationNotification(
const std::tuple<ObserverTypes...>& observers,
std::index_sequence<Indices...>,
const AllocationNotificationData& notification_data) {
((std::get<Indices>(observers)->OnAllocation(notification_data)), ...);
}
template <typename... ObserverTypes, size_t... Indices>
ALWAYS_INLINE void PerformFreeNotification(
const std::tuple<ObserverTypes...>& observers,
std::index_sequence<Indices...>,
const FreeNotificationData& notification_data) {
((std::get<Indices>(observers)->OnFree(notification_data)), ...);
}
// DispatcherImpl provides hooks into the various memory subsystems. These hooks
// are responsible for dispatching any notification to the observers.
// In order to provide as many information on the exact type of the observer and
// prevent any conditional jumps in the hot allocation path, observers are
// stored in a std::tuple. DispatcherImpl performs a CHECK at initialization
// time to ensure they are valid.
template <typename... ObserverTypes>
struct DispatcherImpl {
using AllObservers = std::index_sequence_for<ObserverTypes...>;
template <std::enable_if_t<
internal::LessEqual(sizeof...(ObserverTypes),
configuration::kMaximumNumberOfObservers),
bool> = true>
static DispatchData GetNotificationHooks(
std::tuple<ObserverTypes*...> observers) {
s_observers = std::move(observers);
PerformObserverCheck(s_observers, AllObservers{}, IsValidObserver{});
return CreateDispatchData();
}
private:
static DispatchData CreateDispatchData() {
return DispatchData()
#if PA_BUILDFLAG(USE_PARTITION_ALLOC)
.SetAllocationObserverHooks(&PartitionAllocatorAllocationHook,
&PartitionAllocatorFreeHook)
#endif
#if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
.SetAllocatorDispatch(&allocator_dispatch_)
#endif
;
}
#if PA_BUILDFLAG(USE_PARTITION_ALLOC)
static void PartitionAllocatorAllocationHook(
const partition_alloc::AllocationNotificationData& pa_notification_data) {
AllocationNotificationData dispatcher_notification_data(
pa_notification_data.address(), pa_notification_data.size(),
pa_notification_data.type_name(),
AllocationSubsystem::kPartitionAllocator);
#if PA_BUILDFLAG(HAS_MEMORY_TAGGING)
dispatcher_notification_data.SetMteReportingMode(
ConvertToMTEMode(pa_notification_data.mte_reporting_mode()));
#endif
DoNotifyAllocation(dispatcher_notification_data);
}
static void PartitionAllocatorFreeHook(
const partition_alloc::FreeNotificationData& pa_notification_data) {
FreeNotificationData dispatcher_notification_data(
pa_notification_data.address(),
AllocationSubsystem::kPartitionAllocator);
#if PA_BUILDFLAG(HAS_MEMORY_TAGGING)
dispatcher_notification_data.SetMteReportingMode(
ConvertToMTEMode(pa_notification_data.mte_reporting_mode()));
#endif
DoNotifyFree(dispatcher_notification_data);
}
#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC)
#if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
static void* AllocFn(size_t size, void* context) {
void* const address =
allocator_dispatch_.next->alloc_function(size, context);
DoNotifyAllocationForShim(address, size);
return address;
}
static void* AllocUncheckedFn(size_t size, void* context) {
void* const address =
allocator_dispatch_.next->alloc_unchecked_function(size, context);
DoNotifyAllocationForShim(address, size);
return address;
}
static void* AllocZeroInitializedFn(size_t n, size_t size, void* context) {
void* const address =
allocator_dispatch_.next->alloc_zero_initialized_function(n, size,
context);
DoNotifyAllocationForShim(address, n * size);
return address;
}
static void* AllocZeroInitializedUncheckedFn(size_t n,
size_t size,
void* context) {
void* const address =
allocator_dispatch_.next->alloc_zero_initialized_unchecked_function(
n, size, context);
DoNotifyAllocationForShim(address, n * size);
return address;
}
static void* AllocAlignedFn(size_t alignment, size_t size, void* context) {
void* const address = allocator_dispatch_.next->alloc_aligned_function(
alignment, size, context);
DoNotifyAllocationForShim(address, size);
return address;
}
static void* ReallocFn(void* address, size_t size, void* context) {
// Note: size == 0 actually performs free.
DoNotifyFreeForShim(address);
void* const reallocated_address =
allocator_dispatch_.next->realloc_function(address, size, context);
DoNotifyAllocationForShim(reallocated_address, size);
return reallocated_address;
}
static void* ReallocUncheckedFn(void* address, size_t size, void* context) {
// Note: size == 0 actually performs free.
DoNotifyFreeForShim(address);
void* const reallocated_address =
allocator_dispatch_.next->realloc_unchecked_function(address, size,
context);
DoNotifyAllocationForShim(reallocated_address, size);
return reallocated_address;
}
static void FreeFn(void* address, void* context) {
// Note: DoNotifyFree should be called before free_function (here and in
// other places). That is because observers need to handle the allocation
// being freed before calling free_function, as once the latter is executed
// the address becomes available and can be allocated by another thread.
// That would be racy otherwise.
DoNotifyFreeForShim(address);
MUSTTAIL return allocator_dispatch_.next->free_function(address, context);
}
static void FreeWithSizeFn(void* address, size_t size, void* context) {
DoNotifyFreeForShim(address);
MUSTTAIL return allocator_dispatch_.next->free_with_size_function(
address, size, context);
}
static void FreeWithAlignmentFn(void* address,
size_t alignment,
void* context) {
DoNotifyFreeForShim(address);
MUSTTAIL return allocator_dispatch_.next->free_with_alignment_function(
address, alignment, context);
}
static void FreeWithSizeAndAlignmentFn(void* address,
size_t size,
size_t alignment,
void* context) {
DoNotifyFreeForShim(address);
MUSTTAIL return allocator_dispatch_.next
->free_with_size_and_alignment_function(address, size, alignment,
context);
}
static unsigned BatchMallocFn(size_t size,
void** results,
unsigned num_requested,
void* context) {
unsigned const num_allocated =
allocator_dispatch_.next->batch_malloc_function(size, results,
num_requested, context);
for (unsigned i = 0; i < num_allocated; ++i) {
DoNotifyAllocationForShim(results[i], size);
}
return num_allocated;
}
static void BatchFreeFn(void** to_be_freed,
unsigned num_to_be_freed,
void* context) {
for (unsigned i = 0; i < num_to_be_freed; ++i) {
DoNotifyFreeForShim(to_be_freed[i]);
}
MUSTTAIL return allocator_dispatch_.next->batch_free_function(
to_be_freed, num_to_be_freed, context);
}
static void TryFreeDefaultFn(void* address, void* context) {
DoNotifyFreeForShim(address);
MUSTTAIL return allocator_dispatch_.next->try_free_default_function(
address, context);
}
static void* AlignedMallocFn(size_t size, size_t alignment, void* context) {
void* const address = allocator_dispatch_.next->aligned_malloc_function(
size, alignment, context);
DoNotifyAllocationForShim(address, size);
return address;
}
static void* AlignedMallocUncheckedFn(size_t size,
size_t alignment,
void* context) {
void* const address =
allocator_dispatch_.next->aligned_malloc_unchecked_function(
size, alignment, context);
DoNotifyAllocationForShim(address, size);
return address;
}
static void* AlignedReallocFn(void* address,
size_t size,
size_t alignment,
void* context) {
// Note: size == 0 actually performs free.
DoNotifyFreeForShim(address);
address = allocator_dispatch_.next->aligned_realloc_function(
address, size, alignment, context);
DoNotifyAllocationForShim(address, size);
return address;
}
static void* AlignedReallocUncheckedFn(void* address,
size_t size,
size_t alignment,
void* context) {
// Note: size == 0 actually performs free.
DoNotifyFreeForShim(address);
address = allocator_dispatch_.next->aligned_realloc_unchecked_function(
address, size, alignment, context);
DoNotifyAllocationForShim(address, size);
return address;
}
static void AlignedFreeFn(void* address, void* context) {
DoNotifyFreeForShim(address);
MUSTTAIL return allocator_dispatch_.next->aligned_free_function(address,
context);
}
ALWAYS_INLINE static void DoNotifyAllocationForShim(void* address,
size_t size) {
AllocationNotificationData notification_data(
address, size, nullptr, AllocationSubsystem::kAllocatorShim);
DoNotifyAllocation(notification_data);
}
ALWAYS_INLINE static void DoNotifyFreeForShim(void* address) {
FreeNotificationData notification_data(address,
AllocationSubsystem::kAllocatorShim);
DoNotifyFree(notification_data);
}
static AllocatorDispatch allocator_dispatch_;
#endif // PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
ALWAYS_INLINE static void DoNotifyAllocation(
const AllocationNotificationData& notification_data) {
PerformAllocationNotification(s_observers, AllObservers{},
notification_data);
}
ALWAYS_INLINE static void DoNotifyFree(
const FreeNotificationData& notification_data) {
PerformFreeNotification(s_observers, AllObservers{}, notification_data);
}
static std::tuple<ObserverTypes*...> s_observers;
};
template <typename... ObserverTypes>
std::tuple<ObserverTypes*...> DispatcherImpl<ObserverTypes...>::s_observers;
#if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
template <typename... ObserverTypes>
AllocatorDispatch DispatcherImpl<ObserverTypes...>::allocator_dispatch_ = {
.alloc_function = AllocFn,
.alloc_unchecked_function = AllocUncheckedFn,
.alloc_zero_initialized_function = AllocZeroInitializedFn,
.alloc_zero_initialized_unchecked_function =
AllocZeroInitializedUncheckedFn,
.alloc_aligned_function = AllocAlignedFn,
.realloc_function = ReallocFn,
.realloc_unchecked_function = ReallocUncheckedFn,
.free_function = FreeFn,
.free_with_size_function = FreeWithSizeFn,
.free_with_alignment_function = FreeWithAlignmentFn,
.free_with_size_and_alignment_function = FreeWithSizeAndAlignmentFn,
.get_size_estimate_function = nullptr,
.good_size_function = nullptr,
.claimed_address_function = nullptr,
.batch_malloc_function = BatchMallocFn,
.batch_free_function = BatchFreeFn,
.try_free_default_function = TryFreeDefaultFn,
.aligned_malloc_function = AlignedMallocFn,
.aligned_malloc_unchecked_function = AlignedMallocUncheckedFn,
.aligned_realloc_function = AlignedReallocFn,
.aligned_realloc_unchecked_function = AlignedReallocUncheckedFn,
.aligned_free_function = AlignedFreeFn,
.next = nullptr,
};
#endif // PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
// Specialization of DispatcherImpl in case we have no observers to notify. In
// this special case we return a set of null pointers as the Dispatcher must not
// install any hooks at all.
template <>
struct DispatcherImpl<> {
static DispatchData GetNotificationHooks(std::tuple<> /*observers*/) {
return DispatchData()
#if PA_BUILDFLAG(USE_PARTITION_ALLOC)
.SetAllocationObserverHooks(nullptr, nullptr)
#endif
#if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
.SetAllocatorDispatch(nullptr)
#endif
;
}
};
// A little utility function that helps using DispatcherImpl by providing
// automated type deduction for templates.
template <typename... ObserverTypes>
inline DispatchData GetNotificationHooks(
std::tuple<ObserverTypes*...> observers) {
return DispatcherImpl<ObserverTypes...>::GetNotificationHooks(
std::move(observers));
}
} // namespace base::allocator::dispatcher::internal
#endif // BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_