1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/allocator/dispatcher/dispatcher.h"
6
7 #include "base/allocator/dispatcher/configuration.h"
8 #include "base/allocator/dispatcher/notification_data.h"
9 #include "base/allocator/dispatcher/testing/dispatcher_test.h"
10 #include "base/allocator/dispatcher/testing/tools.h"
11 #include "build/build_config.h"
12 #include "partition_alloc/buildflags.h"
13 #include "partition_alloc/partition_root.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 #if PA_BUILDFLAG(USE_PARTITION_ALLOC)
17 #include "partition_alloc/partition_alloc_for_testing.h"
18 #endif
19
20 #if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
21 #include "partition_alloc/shim/allocator_shim.h"
22 #endif
23
24 #include <tuple>
25
26 namespace base::allocator::dispatcher {
27 namespace {
28 using configuration::kMaximumNumberOfObservers;
29 using configuration::kMaximumNumberOfOptionalObservers;
30 using partition_alloc::PartitionOptions;
31 using partition_alloc::PartitionRoot;
32 using testing::DispatcherTest;
33
34 // A simple observer implementation. Since these tests plug in to Partition
35 // Allocator and Allocator Shim, implementing an observer with Google Mock
36 // results in endless recursion.
37 struct ObserverMock {
OnAllocationbase::allocator::dispatcher::__anond3ae0b310111::ObserverMock38 void OnAllocation(const AllocationNotificationData& notification_data) {
39 ++on_allocation_calls_;
40 }
OnFreebase::allocator::dispatcher::__anond3ae0b310111::ObserverMock41 void OnFree(const FreeNotificationData& notification_data) {
42 ++on_free_calls_;
43 }
44
Resetbase::allocator::dispatcher::__anond3ae0b310111::ObserverMock45 void Reset() {
46 on_allocation_calls_ = 0;
47 on_free_calls_ = 0;
48 }
49
GetNumberOnAllocationCallsbase::allocator::dispatcher::__anond3ae0b310111::ObserverMock50 size_t GetNumberOnAllocationCalls() const { return on_allocation_calls_; }
GetNumberOnFreeCallsbase::allocator::dispatcher::__anond3ae0b310111::ObserverMock51 size_t GetNumberOnFreeCalls() const { return on_free_calls_; }
52
53 private:
54 size_t on_allocation_calls_ = 0;
55 size_t on_free_calls_ = 0;
56 };
57
58 struct DispatcherInitializerGuard {
59 template <typename... Observers>
DispatcherInitializerGuardbase::allocator::dispatcher::__anond3ae0b310111::DispatcherInitializerGuard60 explicit DispatcherInitializerGuard(std::tuple<Observers*...> observers) {
61 Dispatcher::GetInstance().Initialize(observers);
62 }
63
~DispatcherInitializerGuardbase::allocator::dispatcher::__anond3ae0b310111::DispatcherInitializerGuard64 ~DispatcherInitializerGuard() { Dispatcher::GetInstance().ResetForTesting(); }
65 };
66
67 struct BaseAllocatorDispatcherTest : public DispatcherTest {};
68
69 template <typename A>
DoBasicTest(A & allocator)70 void DoBasicTest(A& allocator) {
71 // All we want to verify is that the Dispatcher correctly hooks into the
72 // passed allocator. Therefore, we do not perform an exhaustive test but
73 // just check some basics.
74 std::array<ObserverMock, kMaximumNumberOfObservers> observers;
75
76 {
77 DispatcherInitializerGuard const g(
78 testing::CreateTupleOfPointers(observers));
79
80 constexpr size_t size_to_allocate = 1024;
81 void* const ptr = allocator.Alloc(size_to_allocate);
82 allocator.Free(ptr);
83 }
84
85 for (const auto& mock : observers) {
86 EXPECT_GE(mock.GetNumberOnAllocationCalls(), 1u);
87 EXPECT_GE(mock.GetNumberOnFreeCalls(), 1u);
88 }
89 }
90
TEST_F(BaseAllocatorDispatcherTest,VerifyInitialization)91 TEST_F(BaseAllocatorDispatcherTest, VerifyInitialization) {
92 std::array<ObserverMock, kMaximumNumberOfObservers> observers;
93
94 DispatcherInitializerGuard g(testing::CreateTupleOfPointers(observers));
95 }
96
97 #if PA_BUILDFLAG(USE_PARTITION_ALLOC) && \
98 !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
99 // Don't enable this test when MEMORY_TOOL_REPLACES_ALLOCATOR is defined,
100 // because it makes PartitionAlloc take a different path that doesn't provide
101 // notifications to observer hooks.
102 struct PartitionAllocator {
Allocbase::allocator::dispatcher::__anond3ae0b310111::PartitionAllocator103 void* Alloc(size_t size) { return alloc_.AllocInline(size); }
Freebase::allocator::dispatcher::__anond3ae0b310111::PartitionAllocator104 void Free(void* data) { alloc_.Free(data); }
~PartitionAllocatorbase::allocator::dispatcher::__anond3ae0b310111::PartitionAllocator105 ~PartitionAllocator() {
106 // Use |DisallowLeaks| to confirm that there is no memory allocated and
107 // not yet freed.
108 alloc_.ResetForTesting(::partition_alloc::internal::DisallowLeaks);
109 }
110
111 private:
112 PartitionRoot alloc_{PartitionOptions{}};
113 };
114
TEST_F(BaseAllocatorDispatcherTest,VerifyNotificationUsingPartitionAllocator)115 TEST_F(BaseAllocatorDispatcherTest, VerifyNotificationUsingPartitionAllocator) {
116 PartitionAllocator allocator;
117 DoBasicTest(allocator);
118 }
119 #endif
120
121 #if PA_BUILDFLAG(USE_ALLOCATOR_SHIM)
122 struct AllocatorShimAllocator {
Allocbase::allocator::dispatcher::__anond3ae0b310111::AllocatorShimAllocator123 void* Alloc(size_t size) { return allocator_shim::UncheckedAlloc(size); }
Freebase::allocator::dispatcher::__anond3ae0b310111::AllocatorShimAllocator124 void Free(void* data) { allocator_shim::UncheckedFree(data); }
125 };
126
127 #if BUILDFLAG(IS_APPLE) && !PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
128 // Disable the test when running on any of Apple's OSs without PartitionAlloc
129 // being the default allocator. In this case, all allocations are routed to
130 // MallocImpl, which then causes the test to terminate unexpectedly.
131 #define MAYBE_VerifyNotificationUsingAllocatorShim \
132 DISABLED_VerifyNotificationUsingAllocatorShim
133 #else
134 #define MAYBE_VerifyNotificationUsingAllocatorShim \
135 VerifyNotificationUsingAllocatorShim
136 #endif
137
TEST_F(BaseAllocatorDispatcherTest,MAYBE_VerifyNotificationUsingAllocatorShim)138 TEST_F(BaseAllocatorDispatcherTest, MAYBE_VerifyNotificationUsingAllocatorShim) {
139 AllocatorShimAllocator allocator;
140 DoBasicTest(allocator);
141 }
142 #endif
143
144 } // namespace
145 } // namespace base::allocator::dispatcher
146