blob: 4119b22b44cc9bda1d5a3c17f4b310805488eaff [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.
#include "base/allocator/dispatcher/internal/dispatcher_internal.h"
#include "base/allocator/buildflags.h"
#include "base/allocator/dispatcher/testing/dispatcher_test.h"
#include "base/allocator/dispatcher/testing/observer_mock.h"
#include "base/allocator/dispatcher/testing/tools.h"
#include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
#include "base/dcheck_is_on.h"
#include "testing/gtest/include/gtest/gtest.h"
#include <tuple>
#include <utility>
using ::base::allocator::dispatcher::configuration::kMaximumNumberOfObservers;
using ::base::allocator::dispatcher::testing::CreateTupleOfPointers;
using ::base::allocator::dispatcher::testing::DispatcherTest;
using ::testing::_;
using ::testing::InSequence;
namespace base::allocator::dispatcher::internal {
namespace {
struct AllocationEventDispatcherInternalTest : public DispatcherTest {
static void* GetAllocatedAddress() {
return reinterpret_cast<void*>(0x12345678);
}
static unsigned int GetAllocatedSize() { return 35; }
static unsigned int GetEstimatedSize() { return 77; }
static void* GetFreedAddress() {
return reinterpret_cast<void*>(0x876543210);
}
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
AllocatorDispatch* GetNextAllocatorDispatch() { return &allocator_dispatch_; }
static void* alloc_function(const AllocatorDispatch*, size_t, void*) {
return GetAllocatedAddress();
}
static void* alloc_unchecked_function(const AllocatorDispatch*,
size_t,
void*) {
return GetAllocatedAddress();
}
static void* alloc_zero_initialized_function(const AllocatorDispatch*,
size_t,
size_t,
void*) {
return GetAllocatedAddress();
}
static void* alloc_aligned_function(const AllocatorDispatch*,
size_t,
size_t,
void*) {
return GetAllocatedAddress();
}
static void* realloc_function(const AllocatorDispatch*,
void*,
size_t,
void*) {
return GetAllocatedAddress();
}
static size_t get_size_estimate_function(const AllocatorDispatch*,
void*,
void*) {
return GetEstimatedSize();
}
static bool claimed_address_function(const AllocatorDispatch*, void*, void*) {
return GetEstimatedSize();
}
static unsigned batch_malloc_function(const AllocatorDispatch*,
size_t,
void**,
unsigned num_requested,
void*) {
return num_requested;
}
static void* aligned_malloc_function(const AllocatorDispatch*,
size_t,
size_t,
void*) {
return GetAllocatedAddress();
}
static void* aligned_realloc_function(const AllocatorDispatch*,
void*,
size_t,
size_t,
void*) {
return GetAllocatedAddress();
}
AllocatorDispatch allocator_dispatch_ = {
&alloc_function,
&alloc_unchecked_function,
&alloc_zero_initialized_function,
&alloc_aligned_function,
&realloc_function,
[](const AllocatorDispatch*, void*, void*) {},
&get_size_estimate_function,
&claimed_address_function,
&batch_malloc_function,
[](const AllocatorDispatch*, void**, unsigned, void*) {},
[](const AllocatorDispatch*, void*, size_t, void*) {},
[](const AllocatorDispatch*, void*, void*) {},
&aligned_malloc_function,
&aligned_realloc_function,
[](const AllocatorDispatch*, void*, void*) {}};
#endif
};
} // namespace
using ::testing::NaggyMock;
using ::testing::StrictMock;
using ObserverMock = StrictMock<testing::ObserverMock<>>;
#if defined(GTEST_HAS_DEATH_TEST) && GTEST_HAS_DEATH_TEST && DCHECK_IS_ON()
TEST(AllocationEventDispatcherInternalDeathTest,
VerifyDeathWhenObserverIsNull) {
testing::ObserverMock<int> observer_1;
testing::ObserverMock<float> observer_2;
testing::ObserverMock<size_t>* null_observer = nullptr;
testing::ObserverMock<double> observer_3;
const auto observer_ptrs =
std::make_tuple(&observer_1, &observer_2, null_observer, &observer_3);
EXPECT_DEATH({ GetNotificationHooks(observer_ptrs); }, "");
}
#endif // defined(GTEST_HAS_DEATH_TEST) && GTEST_HAS_DEATH_TEST &&
// DCHECK_IS_ON()
#if BUILDFLAG(USE_PARTITION_ALLOC)
TEST_F(AllocationEventDispatcherInternalTest,
VerifyPartitionAllocatorHooksAreSet) {
std::array<ObserverMock, 1> observers;
const auto dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
EXPECT_NE(nullptr, dispatch_data.GetAllocationObserverHook());
EXPECT_NE(nullptr, dispatch_data.GetFreeObserverHook());
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyPartitionAllocatorHooksAreNullWhenNoObservers) {
const auto dispatch_data = GetNotificationHooks(std::make_tuple());
EXPECT_EQ(nullptr, dispatch_data.GetAllocationObserverHook());
EXPECT_EQ(nullptr, dispatch_data.GetFreeObserverHook());
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyPartitionAllocatorAllocationHooksTriggerCorrectly) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
EXPECT_CALL(mock, OnAllocation(this, sizeof(*this), _, _)).Times(1);
EXPECT_CALL(mock, OnFree(_)).Times(0);
}
const auto dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
dispatch_data.GetAllocationObserverHook()(this, sizeof(*this), nullptr);
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyPartitionAllocatorFreeHooksTriggerCorrectly) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
EXPECT_CALL(mock, OnFree(_)).Times(0);
EXPECT_CALL(mock, OnFree(this)).Times(1);
}
const auto dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
dispatch_data.GetFreeObserverHook()(this);
}
#endif
#if BUILDFLAG(USE_ALLOCATOR_SHIM)
TEST_F(AllocationEventDispatcherInternalTest, VerifyAllocatorShimDataIsSet) {
std::array<ObserverMock, 1> observers;
const auto dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
const auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
EXPECT_NE(nullptr, allocator_dispatch);
EXPECT_NE(nullptr, allocator_dispatch->alloc_function);
EXPECT_NE(nullptr, allocator_dispatch->alloc_unchecked_function);
EXPECT_NE(nullptr, allocator_dispatch->alloc_zero_initialized_function);
EXPECT_NE(nullptr, allocator_dispatch->alloc_aligned_function);
EXPECT_NE(nullptr, allocator_dispatch->realloc_function);
EXPECT_NE(nullptr, allocator_dispatch->free_function);
EXPECT_NE(nullptr, allocator_dispatch->get_size_estimate_function);
EXPECT_NE(nullptr, allocator_dispatch->claimed_address_function);
EXPECT_NE(nullptr, allocator_dispatch->batch_malloc_function);
EXPECT_NE(nullptr, allocator_dispatch->batch_free_function);
EXPECT_NE(nullptr, allocator_dispatch->free_definite_size_function);
EXPECT_NE(nullptr, allocator_dispatch->try_free_default_function);
EXPECT_NE(nullptr, allocator_dispatch->aligned_malloc_function);
EXPECT_NE(nullptr, allocator_dispatch->aligned_realloc_function);
EXPECT_NE(nullptr, allocator_dispatch->aligned_free_function);
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimDataIsNullWhenNoObservers) {
const auto dispatch_data = GetNotificationHooks(std::make_tuple());
EXPECT_EQ(nullptr, dispatch_data.GetAllocatorDispatch());
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_alloc_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(),
AllocationSubsystem::kAllocatorShim, _))
.Times(1);
EXPECT_CALL(mock, OnFree(_)).Times(0);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
allocator_dispatch->next = GetNextAllocatorDispatch();
auto* const allocated_address = allocator_dispatch->alloc_function(
allocator_dispatch, GetAllocatedSize(), nullptr);
EXPECT_EQ(allocated_address, GetAllocatedAddress());
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_alloc_unchecked_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(),
AllocationSubsystem::kAllocatorShim, _))
.Times(1);
EXPECT_CALL(mock, OnFree(_)).Times(0);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
allocator_dispatch->next = GetNextAllocatorDispatch();
auto* const allocated_address = allocator_dispatch->alloc_unchecked_function(
allocator_dispatch, GetAllocatedSize(), nullptr);
EXPECT_EQ(allocated_address, GetAllocatedAddress());
}
TEST_F(
AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_alloc_zero_initialized_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
constexpr int n = 8;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
EXPECT_CALL(mock,
OnAllocation(GetAllocatedAddress(), n * GetAllocatedSize(),
AllocationSubsystem::kAllocatorShim, _))
.Times(1);
EXPECT_CALL(mock, OnFree(_)).Times(0);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
allocator_dispatch->next = GetNextAllocatorDispatch();
auto* const allocated_address =
allocator_dispatch->alloc_zero_initialized_function(
allocator_dispatch, n, GetAllocatedSize(), nullptr);
EXPECT_EQ(allocated_address, GetAllocatedAddress());
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_alloc_aligned_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(),
AllocationSubsystem::kAllocatorShim, _))
.Times(1);
EXPECT_CALL(mock, OnFree(_)).Times(0);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
allocator_dispatch->next = GetNextAllocatorDispatch();
auto* const allocated_address = allocator_dispatch->alloc_aligned_function(
allocator_dispatch, 2048, GetAllocatedSize(), nullptr);
EXPECT_EQ(allocated_address, GetAllocatedAddress());
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_realloc_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
InSequence execution_order;
EXPECT_CALL(mock, OnFree(GetFreedAddress())).Times(1);
EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(),
AllocationSubsystem::kAllocatorShim, _))
.Times(1);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
allocator_dispatch->next = GetNextAllocatorDispatch();
auto* const allocated_address = allocator_dispatch->realloc_function(
allocator_dispatch, GetFreedAddress(), GetAllocatedSize(), nullptr);
EXPECT_EQ(allocated_address, GetAllocatedAddress());
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_free_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnFree(GetFreedAddress())).Times(1);
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
allocator_dispatch->next = GetNextAllocatorDispatch();
allocator_dispatch->free_function(allocator_dispatch, GetFreedAddress(),
nullptr);
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_get_size_estimate_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnFree(_)).Times(0);
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
allocator_dispatch->next = GetNextAllocatorDispatch();
auto const estimated_size = allocator_dispatch->get_size_estimate_function(
allocator_dispatch, GetAllocatedAddress(), nullptr);
EXPECT_EQ(estimated_size, GetEstimatedSize());
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_batch_malloc_function) {
constexpr size_t allocation_batch_size = 10;
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
std::array<void*, allocation_batch_size> allocation_batch = {nullptr};
for (auto& mock : observers) {
EXPECT_CALL(mock, OnFree(_)).Times(0);
EXPECT_CALL(mock, OnAllocation(nullptr, GetAllocatedSize(),
AllocationSubsystem::kAllocatorShim, _))
.Times(allocation_batch_size);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
EXPECT_NE(allocator_dispatch->batch_malloc_function, nullptr);
allocator_dispatch->next = GetNextAllocatorDispatch();
auto const number_allocated = allocator_dispatch->batch_malloc_function(
allocator_dispatch, GetAllocatedSize(), allocation_batch.data(),
allocation_batch_size, nullptr);
EXPECT_EQ(number_allocated, allocation_batch_size);
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_batch_free_function) {
constexpr size_t allocation_batch_size = 10;
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
std::array<void*, allocation_batch_size> allocation_batch;
allocation_batch.fill(GetFreedAddress());
for (auto& mock : observers) {
EXPECT_CALL(mock, OnFree(GetFreedAddress())).Times(allocation_batch_size);
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
EXPECT_NE(allocator_dispatch->batch_free_function, nullptr);
allocator_dispatch->next = GetNextAllocatorDispatch();
allocator_dispatch->batch_free_function(allocator_dispatch,
allocation_batch.data(),
allocation_batch_size, nullptr);
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_free_definite_size_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnFree(GetAllocatedAddress())).Times(1);
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
}
DispatchData const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
EXPECT_NE(allocator_dispatch->free_definite_size_function, nullptr);
allocator_dispatch->next = GetNextAllocatorDispatch();
allocator_dispatch->free_definite_size_function(
allocator_dispatch, GetAllocatedAddress(), GetAllocatedSize(), nullptr);
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_try_free_default_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnFree(GetAllocatedAddress())).Times(1);
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
}
DispatchData const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
EXPECT_NE(allocator_dispatch->try_free_default_function, nullptr);
allocator_dispatch->next = GetNextAllocatorDispatch();
allocator_dispatch->try_free_default_function(allocator_dispatch,
GetAllocatedAddress(), nullptr);
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_aligned_malloc_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(),
AllocationSubsystem::kAllocatorShim, _))
.Times(1);
EXPECT_CALL(mock, OnFree(_)).Times(0);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
allocator_dispatch->next = GetNextAllocatorDispatch();
auto* const allocated_address = allocator_dispatch->aligned_malloc_function(
allocator_dispatch, GetAllocatedSize(), 2048, nullptr);
EXPECT_EQ(allocated_address, GetAllocatedAddress());
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_aligned_realloc_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
InSequence execution_order;
EXPECT_CALL(mock, OnFree(GetFreedAddress())).Times(1);
EXPECT_CALL(mock, OnAllocation(GetAllocatedAddress(), GetAllocatedSize(),
AllocationSubsystem::kAllocatorShim, _))
.Times(1);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
allocator_dispatch->next = GetNextAllocatorDispatch();
auto* const allocated_address = allocator_dispatch->aligned_realloc_function(
allocator_dispatch, GetFreedAddress(), GetAllocatedSize(), 2048, nullptr);
EXPECT_EQ(allocated_address, GetAllocatedAddress());
}
TEST_F(AllocationEventDispatcherInternalTest,
VerifyAllocatorShimHooksTriggerCorrectly_aligned_free_function) {
std::array<ObserverMock, kMaximumNumberOfObservers> observers;
for (auto& mock : observers) {
EXPECT_CALL(mock, OnFree(GetFreedAddress())).Times(1);
EXPECT_CALL(mock, OnAllocation(_, _, _, _)).Times(0);
}
auto const dispatch_data =
GetNotificationHooks(CreateTupleOfPointers(observers));
auto* const allocator_dispatch = dispatch_data.GetAllocatorDispatch();
allocator_dispatch->next = GetNextAllocatorDispatch();
allocator_dispatch->aligned_free_function(allocator_dispatch,
GetFreedAddress(), nullptr);
}
#endif
} // namespace base::allocator::dispatcher::internal