• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_
6 #define BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_
7 
8 #include "base/allocator/dispatcher/configuration.h"
9 #include "base/allocator/dispatcher/internal/dispatch_data.h"
10 #include "base/allocator/dispatcher/internal/tools.h"
11 #include "base/allocator/dispatcher/subsystem.h"
12 #include "base/allocator/partition_allocator/src/partition_alloc/partition_alloc_allocation_data.h"
13 #include "base/allocator/partition_allocator/src/partition_alloc/partition_alloc_buildflags.h"
14 #include "base/check.h"
15 #include "base/compiler_specific.h"
16 #include "build/build_config.h"
17 
18 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
19 #include "base/allocator/partition_allocator/src/partition_alloc/shim/allocator_shim.h"
20 #endif
21 
22 #include <tuple>
23 
24 namespace base::allocator::dispatcher::internal {
25 
26 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
27 using allocator_shim::AllocatorDispatch;
28 #endif
29 
30 template <typename CheckObserverPredicate,
31           typename... ObserverTypes,
32           size_t... Indices>
PerformObserverCheck(const std::tuple<ObserverTypes...> & observers,std::index_sequence<Indices...>,CheckObserverPredicate check_observer)33 void inline PerformObserverCheck(const std::tuple<ObserverTypes...>& observers,
34                                  std::index_sequence<Indices...>,
35                                  CheckObserverPredicate check_observer) {
36   ([](bool b) { DCHECK(b); }(check_observer(std::get<Indices>(observers))),
37    ...);
38 }
39 
40 template <typename... ObserverTypes, size_t... Indices>
PerformAllocationNotification(const std::tuple<ObserverTypes...> & observers,std::index_sequence<Indices...>,const partition_alloc::AllocationNotificationData & notification_data,AllocationSubsystem sub_system)41 ALWAYS_INLINE void PerformAllocationNotification(
42     const std::tuple<ObserverTypes...>& observers,
43     std::index_sequence<Indices...>,
44     const partition_alloc::AllocationNotificationData& notification_data,
45     AllocationSubsystem sub_system) {
46   ((std::get<Indices>(observers)->OnAllocation(
47        notification_data.address(), notification_data.size(), sub_system,
48        notification_data.type_name())),
49    ...);
50 }
51 
52 template <typename... ObserverTypes, size_t... Indices>
PerformFreeNotification(const std::tuple<ObserverTypes...> & observers,std::index_sequence<Indices...>,const partition_alloc::FreeNotificationData & notification_data)53 ALWAYS_INLINE void PerformFreeNotification(
54     const std::tuple<ObserverTypes...>& observers,
55     std::index_sequence<Indices...>,
56     const partition_alloc::FreeNotificationData& notification_data) {
57   ((std::get<Indices>(observers)->OnFree(notification_data.address())), ...);
58 }
59 
60 // DispatcherImpl provides hooks into the various memory subsystems. These hooks
61 // are responsible for dispatching any notification to the observers.
62 // In order to provide as many information on the exact type of the observer and
63 // prevent any conditional jumps in the hot allocation path, observers are
64 // stored in a std::tuple. DispatcherImpl performs a CHECK at initialization
65 // time to ensure they are valid.
66 template <typename... ObserverTypes>
67 struct DispatcherImpl {
68   using AllObservers = std::index_sequence_for<ObserverTypes...>;
69 
70   template <std::enable_if_t<
71                 internal::LessEqual(sizeof...(ObserverTypes),
72                                     configuration::kMaximumNumberOfObservers),
73                 bool> = true>
GetNotificationHooksDispatcherImpl74   static DispatchData GetNotificationHooks(
75       std::tuple<ObserverTypes*...> observers) {
76     s_observers = std::move(observers);
77 
78     PerformObserverCheck(s_observers, AllObservers{}, IsValidObserver{});
79 
80     return CreateDispatchData();
81   }
82 
83  private:
CreateDispatchDataDispatcherImpl84   static DispatchData CreateDispatchData() {
85     return DispatchData()
86 #if BUILDFLAG(USE_PARTITION_ALLOC)
87         .SetAllocationObserverHooks(&PartitionAllocatorAllocationHook,
88                                     &PartitionAllocatorFreeHook)
89 #endif
90 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
91         .SetAllocatorDispatch(&allocator_dispatch_)
92 #endif
93         ;
94   }
95 
96 #if BUILDFLAG(USE_PARTITION_ALLOC)
PartitionAllocatorAllocationHookDispatcherImpl97   static void PartitionAllocatorAllocationHook(
98       const partition_alloc::AllocationNotificationData& notification_data) {
99     DoNotifyAllocation(notification_data,
100                        AllocationSubsystem::kPartitionAllocator);
101   }
102 
PartitionAllocatorFreeHookDispatcherImpl103   static void PartitionAllocatorFreeHook(
104       const partition_alloc::FreeNotificationData& notification_data) {
105     DoNotifyFree(notification_data);
106   }
107 #endif
108 
109 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
AllocFnDispatcherImpl110   static void* AllocFn(const AllocatorDispatch* self,
111                        size_t size,
112                        void* context) {
113     void* const address = self->next->alloc_function(self->next, size, context);
114 
115     DoNotifyAllocation(
116         partition_alloc::AllocationNotificationData(address, size, nullptr),
117         AllocationSubsystem::kAllocatorShim);
118 
119     return address;
120   }
121 
AllocUncheckedFnDispatcherImpl122   static void* AllocUncheckedFn(const AllocatorDispatch* self,
123                                 size_t size,
124                                 void* context) {
125     void* const address =
126         self->next->alloc_unchecked_function(self->next, size, context);
127 
128     DoNotifyAllocation(
129         partition_alloc::AllocationNotificationData(address, size, nullptr),
130         AllocationSubsystem::kAllocatorShim);
131 
132     return address;
133   }
134 
AllocZeroInitializedFnDispatcherImpl135   static void* AllocZeroInitializedFn(const AllocatorDispatch* self,
136                                       size_t n,
137                                       size_t size,
138                                       void* context) {
139     void* const address = self->next->alloc_zero_initialized_function(
140         self->next, n, size, context);
141 
142     DoNotifyAllocation(
143         partition_alloc::AllocationNotificationData(address, n * size, nullptr),
144         AllocationSubsystem::kAllocatorShim);
145 
146     return address;
147   }
148 
AllocAlignedFnDispatcherImpl149   static void* AllocAlignedFn(const AllocatorDispatch* self,
150                               size_t alignment,
151                               size_t size,
152                               void* context) {
153     void* const address = self->next->alloc_aligned_function(
154         self->next, alignment, size, context);
155 
156     DoNotifyAllocation(
157         partition_alloc::AllocationNotificationData(address, size, nullptr),
158         AllocationSubsystem::kAllocatorShim);
159 
160     return address;
161   }
162 
ReallocFnDispatcherImpl163   static void* ReallocFn(const AllocatorDispatch* self,
164                          void* address,
165                          size_t size,
166                          void* context) {
167     // Note: size == 0 actually performs free.
168     DoNotifyFree(partition_alloc::FreeNotificationData(address));
169     void* const reallocated_address =
170         self->next->realloc_function(self->next, address, size, context);
171 
172     DoNotifyAllocation(partition_alloc::AllocationNotificationData(
173                            reallocated_address, size, nullptr),
174                        AllocationSubsystem::kAllocatorShim);
175 
176     return reallocated_address;
177   }
178 
FreeFnDispatcherImpl179   static void FreeFn(const AllocatorDispatch* self,
180                      void* address,
181                      void* context) {
182     // Note: The RecordFree should be called before free_function (here and in
183     // other places). That is because observers need to handle the allocation
184     // being freed before calling free_function, as once the latter is executed
185     // the address becomes available and can be allocated by another thread.
186     // That would be racy otherwise.
187     DoNotifyFree(partition_alloc::FreeNotificationData(address));
188     self->next->free_function(self->next, address, context);
189   }
190 
GetSizeEstimateFnDispatcherImpl191   static size_t GetSizeEstimateFn(const AllocatorDispatch* self,
192                                   void* address,
193                                   void* context) {
194     return self->next->get_size_estimate_function(self->next, address, context);
195   }
196 
GoodSizeFnDispatcherImpl197   static size_t GoodSizeFn(const AllocatorDispatch* self,
198                            size_t size,
199                            void* context) {
200     return self->next->good_size_function(self->next, size, context);
201   }
202 
ClaimedAddressFnDispatcherImpl203   static bool ClaimedAddressFn(const AllocatorDispatch* self,
204                                void* address,
205                                void* context) {
206     return self->next->claimed_address_function(self->next, address, context);
207   }
208 
BatchMallocFnDispatcherImpl209   static unsigned BatchMallocFn(const AllocatorDispatch* self,
210                                 size_t size,
211                                 void** results,
212                                 unsigned num_requested,
213                                 void* context) {
214     unsigned const num_allocated = self->next->batch_malloc_function(
215         self->next, size, results, num_requested, context);
216     for (unsigned i = 0; i < num_allocated; ++i) {
217       DoNotifyAllocation(partition_alloc::AllocationNotificationData(
218                              results[i], size, nullptr),
219                          AllocationSubsystem::kAllocatorShim);
220     }
221     return num_allocated;
222   }
223 
BatchFreeFnDispatcherImpl224   static void BatchFreeFn(const AllocatorDispatch* self,
225                           void** to_be_freed,
226                           unsigned num_to_be_freed,
227                           void* context) {
228     for (unsigned i = 0; i < num_to_be_freed; ++i) {
229       DoNotifyFree(partition_alloc::FreeNotificationData(to_be_freed[i]));
230     }
231 
232     self->next->batch_free_function(self->next, to_be_freed, num_to_be_freed,
233                                     context);
234   }
235 
FreeDefiniteSizeFnDispatcherImpl236   static void FreeDefiniteSizeFn(const AllocatorDispatch* self,
237                                  void* address,
238                                  size_t size,
239                                  void* context) {
240     DoNotifyFree(partition_alloc::FreeNotificationData(address));
241     self->next->free_definite_size_function(self->next, address, size, context);
242   }
243 
TryFreeDefaultFnDispatcherImpl244   static void TryFreeDefaultFn(const AllocatorDispatch* self,
245                                void* address,
246                                void* context) {
247     DoNotifyFree(partition_alloc::FreeNotificationData(address));
248     self->next->try_free_default_function(self->next, address, context);
249   }
250 
AlignedMallocFnDispatcherImpl251   static void* AlignedMallocFn(const AllocatorDispatch* self,
252                                size_t size,
253                                size_t alignment,
254                                void* context) {
255     void* const address = self->next->aligned_malloc_function(
256         self->next, size, alignment, context);
257 
258     DoNotifyAllocation(
259         partition_alloc::AllocationNotificationData(address, size, nullptr),
260         AllocationSubsystem::kAllocatorShim);
261 
262     return address;
263   }
264 
AlignedReallocFnDispatcherImpl265   static void* AlignedReallocFn(const AllocatorDispatch* self,
266                                 void* address,
267                                 size_t size,
268                                 size_t alignment,
269                                 void* context) {
270     // Note: size == 0 actually performs free.
271     DoNotifyFree(partition_alloc::FreeNotificationData(address));
272     address = self->next->aligned_realloc_function(self->next, address, size,
273                                                    alignment, context);
274 
275     DoNotifyAllocation(
276         partition_alloc::AllocationNotificationData(address, size, nullptr),
277         AllocationSubsystem::kAllocatorShim);
278 
279     return address;
280   }
281 
AlignedFreeFnDispatcherImpl282   static void AlignedFreeFn(const AllocatorDispatch* self,
283                             void* address,
284                             void* context) {
285     DoNotifyFree(partition_alloc::FreeNotificationData(address));
286     self->next->aligned_free_function(self->next, address, context);
287   }
288 
289   static AllocatorDispatch allocator_dispatch_;
290 #endif
291 
DoNotifyAllocationDispatcherImpl292   ALWAYS_INLINE static void DoNotifyAllocation(
293       const partition_alloc::AllocationNotificationData& notification_data,
294       AllocationSubsystem sub_system) {
295     PerformAllocationNotification(s_observers, AllObservers{},
296                                   notification_data, sub_system);
297   }
298 
DoNotifyFreeDispatcherImpl299   ALWAYS_INLINE static void DoNotifyFree(
300       const partition_alloc::FreeNotificationData& notification_data) {
301     PerformFreeNotification(s_observers, AllObservers{}, notification_data);
302   }
303 
304   static std::tuple<ObserverTypes*...> s_observers;
305 };
306 
307 template <typename... ObserverTypes>
308 std::tuple<ObserverTypes*...> DispatcherImpl<ObserverTypes...>::s_observers;
309 
310 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
311 template <typename... ObserverTypes>
312 AllocatorDispatch DispatcherImpl<ObserverTypes...>::allocator_dispatch_ = {
313     &AllocFn,
314     &AllocUncheckedFn,
315     &AllocZeroInitializedFn,
316     &AllocAlignedFn,
317     &ReallocFn,
318     &FreeFn,
319     &GetSizeEstimateFn,
320     &GoodSizeFn,
321     &ClaimedAddressFn,
322     &BatchMallocFn,
323     &BatchFreeFn,
324     &FreeDefiniteSizeFn,
325     &TryFreeDefaultFn,
326     &AlignedMallocFn,
327     &AlignedReallocFn,
328     &AlignedFreeFn,
329     nullptr};
330 #endif
331 
332 // Specialization of DispatcherImpl in case we have no observers to notify. In
333 // this special case we return a set of null pointers as the Dispatcher must not
334 // install any hooks at all.
335 template <>
336 struct DispatcherImpl<> {
337   static DispatchData GetNotificationHooks(std::tuple<> /*observers*/) {
338     return DispatchData()
339 #if BUILDFLAG(USE_PARTITION_ALLOC)
340         .SetAllocationObserverHooks(nullptr, nullptr)
341 #endif
342 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
343         .SetAllocatorDispatch(nullptr)
344 #endif
345         ;
346   }
347 };
348 
349 // A little utility function that helps using DispatcherImpl by providing
350 // automated type deduction for templates.
351 template <typename... ObserverTypes>
352 inline DispatchData GetNotificationHooks(
353     std::tuple<ObserverTypes*...> observers) {
354   return DispatcherImpl<ObserverTypes...>::GetNotificationHooks(
355       std::move(observers));
356 }
357 
358 }  // namespace base::allocator::dispatcher::internal
359 
360 #endif  // BASE_ALLOCATOR_DISPATCHER_INTERNAL_DISPATCHER_INTERNAL_H_
361