1 //===-- sanitizer_allocator_combined.h --------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Part of the Sanitizer Allocator.
11 //===----------------------------------------------------------------------===//
12 #ifndef SANITIZER_ALLOCATOR_H
13 #error This file must be included inside sanitizer_allocator.h
16 // This class implements a complete memory allocator by using two
17 // internal allocators:
18 // PrimaryAllocator is efficient, but may not allocate some sizes (alignments).
19 // When allocating 2^x bytes it should return 2^x aligned chunk.
20 // PrimaryAllocator is used via a local AllocatorCache.
21 // SecondaryAllocator can allocate anything, but is not efficient.
22 template <class PrimaryAllocator
,
23 class LargeMmapAllocatorPtrArray
= DefaultLargeMmapAllocatorPtrArray
>
24 class CombinedAllocator
{
26 using AllocatorCache
= typename
PrimaryAllocator::AllocatorCache
;
27 using SecondaryAllocator
=
28 LargeMmapAllocator
<typename
PrimaryAllocator::MapUnmapCallback
,
29 LargeMmapAllocatorPtrArray
,
30 typename
PrimaryAllocator::AddressSpaceView
>;
32 void InitLinkerInitialized(s32 release_to_os_interval_ms
,
33 uptr heap_start
= 0) {
34 primary_
.Init(release_to_os_interval_ms
, heap_start
);
35 secondary_
.InitLinkerInitialized();
38 void Init(s32 release_to_os_interval_ms
, uptr heap_start
= 0) {
40 primary_
.Init(release_to_os_interval_ms
, heap_start
);
44 void *Allocate(AllocatorCache
*cache
, uptr size
, uptr alignment
) {
45 // Returning 0 on malloc(0) may break a lot of code.
48 if (size
+ alignment
< size
) {
49 Report("WARNING: %s: CombinedAllocator allocation overflow: "
50 "0x%zx bytes with 0x%zx alignment requested\n",
51 SanitizerToolName
, size
, alignment
);
54 uptr original_size
= size
;
55 // If alignment requirements are to be fulfilled by the frontend allocator
56 // rather than by the primary or secondary, passing an alignment lower than
57 // or equal to 8 will prevent any further rounding up, as well as the later
60 size
= RoundUpTo(size
, alignment
);
61 // The primary allocator should return a 2^x aligned allocation when
62 // requested 2^x bytes, hence using the rounded up 'size' when being
63 // serviced by the primary (this is no longer true when the primary is
64 // using a non-fixed base address). The secondary takes care of the
65 // alignment without such requirement, and allocating 'size' would use
66 // extraneous memory, so we employ 'original_size'.
68 if (primary_
.CanAllocate(size
, alignment
))
69 res
= cache
->Allocate(&primary_
, primary_
.ClassID(size
));
71 res
= secondary_
.Allocate(&stats_
, original_size
, alignment
);
73 CHECK_EQ(reinterpret_cast<uptr
>(res
) & (alignment
- 1), 0);
77 s32
ReleaseToOSIntervalMs() const {
78 return primary_
.ReleaseToOSIntervalMs();
81 void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms
) {
82 primary_
.SetReleaseToOSIntervalMs(release_to_os_interval_ms
);
85 void ForceReleaseToOS() {
86 primary_
.ForceReleaseToOS();
89 void Deallocate(AllocatorCache
*cache
, void *p
) {
91 if (primary_
.PointerIsMine(p
))
92 cache
->Deallocate(&primary_
, primary_
.GetSizeClass(p
), p
);
94 secondary_
.Deallocate(&stats_
, p
);
97 void *Reallocate(AllocatorCache
*cache
, void *p
, uptr new_size
,
100 return Allocate(cache
, new_size
, alignment
);
102 Deallocate(cache
, p
);
105 CHECK(PointerIsMine(p
));
106 uptr old_size
= GetActuallyAllocatedSize(p
);
107 uptr memcpy_size
= Min(new_size
, old_size
);
108 void *new_p
= Allocate(cache
, new_size
, alignment
);
110 internal_memcpy(new_p
, p
, memcpy_size
);
111 Deallocate(cache
, p
);
115 bool PointerIsMine(const void *p
) const {
116 if (primary_
.PointerIsMine(p
))
118 return secondary_
.PointerIsMine(p
);
121 bool FromPrimary(const void *p
) const { return primary_
.PointerIsMine(p
); }
123 void *GetMetaData(const void *p
) {
124 if (primary_
.PointerIsMine(p
))
125 return primary_
.GetMetaData(p
);
126 return secondary_
.GetMetaData(p
);
129 void *GetBlockBegin(const void *p
) {
130 if (primary_
.PointerIsMine(p
))
131 return primary_
.GetBlockBegin(p
);
132 return secondary_
.GetBlockBegin(p
);
135 // This function does the same as GetBlockBegin, but is much faster.
136 // Must be called with the allocator locked.
137 void *GetBlockBeginFastLocked(const void *p
) {
138 if (primary_
.PointerIsMine(p
))
139 return primary_
.GetBlockBegin(p
);
140 return secondary_
.GetBlockBeginFastLocked(p
);
143 uptr
GetActuallyAllocatedSize(void *p
) {
144 if (primary_
.PointerIsMine(p
))
145 return primary_
.GetActuallyAllocatedSize(p
);
146 return secondary_
.GetActuallyAllocatedSize(p
);
149 uptr
TotalMemoryUsed() {
150 return primary_
.TotalMemoryUsed() + secondary_
.TotalMemoryUsed();
153 void TestOnlyUnmap() { primary_
.TestOnlyUnmap(); }
155 void InitCache(AllocatorCache
*cache
) {
156 cache
->Init(&stats_
);
159 void DestroyCache(AllocatorCache
*cache
) {
160 cache
->Destroy(&primary_
, &stats_
);
163 void SwallowCache(AllocatorCache
*cache
) {
164 cache
->Drain(&primary_
);
167 void GetStats(AllocatorStatCounters s
) const {
172 primary_
.PrintStats();
173 secondary_
.PrintStats();
176 // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
177 // introspection API.
178 void ForceLock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS
{
179 primary_
.ForceLock();
180 secondary_
.ForceLock();
183 void ForceUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS
{
184 secondary_
.ForceUnlock();
185 primary_
.ForceUnlock();
188 // Iterate over all existing chunks.
189 // The allocator must be locked when calling this function.
190 void ForEachChunk(ForEachChunkCallback callback
, void *arg
) {
191 primary_
.ForEachChunk(callback
, arg
);
192 secondary_
.ForEachChunk(callback
, arg
);
196 PrimaryAllocator primary_
;
197 SecondaryAllocator secondary_
;
198 AllocatorGlobalStats stats_
;