]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/sanitizer_common/sanitizer_allocator_combined.h
Merge clang 7.0.1 and several follow-up changes
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / sanitizer_common / sanitizer_allocator_combined.h
1 //===-- sanitizer_allocator_combined.h --------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Part of the Sanitizer Allocator.
11 //
12 //===----------------------------------------------------------------------===//
13 #ifndef SANITIZER_ALLOCATOR_H
14 #error This file must be included inside sanitizer_allocator.h
15 #endif
16
17 // This class implements a complete memory allocator by using two
18 // internal allocators:
19 // PrimaryAllocator is efficient, but may not allocate some sizes (alignments).
20 //  When allocating 2^x bytes it should return 2^x aligned chunk.
21 // PrimaryAllocator is used via a local AllocatorCache.
22 // SecondaryAllocator can allocate anything, but is not efficient.
23 template <class PrimaryAllocator, class AllocatorCache,
24           class SecondaryAllocator>  // NOLINT
25 class CombinedAllocator {
26  public:
27   void InitLinkerInitialized(s32 release_to_os_interval_ms) {
28     primary_.Init(release_to_os_interval_ms);
29     secondary_.InitLinkerInitialized();
30     stats_.InitLinkerInitialized();
31   }
32
33   void Init(s32 release_to_os_interval_ms) {
34     primary_.Init(release_to_os_interval_ms);
35     secondary_.Init();
36     stats_.Init();
37   }
38
39   void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) {
40     // Returning 0 on malloc(0) may break a lot of code.
41     if (size == 0)
42       size = 1;
43     if (size + alignment < size) {
44       Report("WARNING: %s: CombinedAllocator allocation overflow: "
45              "0x%zx bytes with 0x%zx alignment requested\n",
46              SanitizerToolName, size, alignment);
47       return nullptr;
48     }
49     uptr original_size = size;
50     // If alignment requirements are to be fulfilled by the frontend allocator
51     // rather than by the primary or secondary, passing an alignment lower than
52     // or equal to 8 will prevent any further rounding up, as well as the later
53     // alignment check.
54     if (alignment > 8)
55       size = RoundUpTo(size, alignment);
56     // The primary allocator should return a 2^x aligned allocation when
57     // requested 2^x bytes, hence using the rounded up 'size' when being
58     // serviced by the primary (this is no longer true when the primary is
59     // using a non-fixed base address). The secondary takes care of the
60     // alignment without such requirement, and allocating 'size' would use
61     // extraneous memory, so we employ 'original_size'.
62     void *res;
63     if (primary_.CanAllocate(size, alignment))
64       res = cache->Allocate(&primary_, primary_.ClassID(size));
65     else
66       res = secondary_.Allocate(&stats_, original_size, alignment);
67     if (alignment > 8)
68       CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
69     return res;
70   }
71
72   s32 ReleaseToOSIntervalMs() const {
73     return primary_.ReleaseToOSIntervalMs();
74   }
75
76   void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) {
77     primary_.SetReleaseToOSIntervalMs(release_to_os_interval_ms);
78   }
79
80   void ForceReleaseToOS() {
81     primary_.ForceReleaseToOS();
82   }
83
84   void Deallocate(AllocatorCache *cache, void *p) {
85     if (!p) return;
86     if (primary_.PointerIsMine(p))
87       cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
88     else
89       secondary_.Deallocate(&stats_, p);
90   }
91
92   void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
93                    uptr alignment) {
94     if (!p)
95       return Allocate(cache, new_size, alignment);
96     if (!new_size) {
97       Deallocate(cache, p);
98       return nullptr;
99     }
100     CHECK(PointerIsMine(p));
101     uptr old_size = GetActuallyAllocatedSize(p);
102     uptr memcpy_size = Min(new_size, old_size);
103     void *new_p = Allocate(cache, new_size, alignment);
104     if (new_p)
105       internal_memcpy(new_p, p, memcpy_size);
106     Deallocate(cache, p);
107     return new_p;
108   }
109
110   bool PointerIsMine(void *p) {
111     if (primary_.PointerIsMine(p))
112       return true;
113     return secondary_.PointerIsMine(p);
114   }
115
116   bool FromPrimary(void *p) {
117     return primary_.PointerIsMine(p);
118   }
119
120   void *GetMetaData(const void *p) {
121     if (primary_.PointerIsMine(p))
122       return primary_.GetMetaData(p);
123     return secondary_.GetMetaData(p);
124   }
125
126   void *GetBlockBegin(const void *p) {
127     if (primary_.PointerIsMine(p))
128       return primary_.GetBlockBegin(p);
129     return secondary_.GetBlockBegin(p);
130   }
131
132   // This function does the same as GetBlockBegin, but is much faster.
133   // Must be called with the allocator locked.
134   void *GetBlockBeginFastLocked(void *p) {
135     if (primary_.PointerIsMine(p))
136       return primary_.GetBlockBegin(p);
137     return secondary_.GetBlockBeginFastLocked(p);
138   }
139
140   uptr GetActuallyAllocatedSize(void *p) {
141     if (primary_.PointerIsMine(p))
142       return primary_.GetActuallyAllocatedSize(p);
143     return secondary_.GetActuallyAllocatedSize(p);
144   }
145
146   uptr TotalMemoryUsed() {
147     return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed();
148   }
149
150   void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
151
152   void InitCache(AllocatorCache *cache) {
153     cache->Init(&stats_);
154   }
155
156   void DestroyCache(AllocatorCache *cache) {
157     cache->Destroy(&primary_, &stats_);
158   }
159
160   void SwallowCache(AllocatorCache *cache) {
161     cache->Drain(&primary_);
162   }
163
164   void GetStats(AllocatorStatCounters s) const {
165     stats_.Get(s);
166   }
167
168   void PrintStats() {
169     primary_.PrintStats();
170     secondary_.PrintStats();
171   }
172
173   // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone
174   // introspection API.
175   void ForceLock() {
176     primary_.ForceLock();
177     secondary_.ForceLock();
178   }
179
180   void ForceUnlock() {
181     secondary_.ForceUnlock();
182     primary_.ForceUnlock();
183   }
184
185   // Iterate over all existing chunks.
186   // The allocator must be locked when calling this function.
187   void ForEachChunk(ForEachChunkCallback callback, void *arg) {
188     primary_.ForEachChunk(callback, arg);
189     secondary_.ForEachChunk(callback, arg);
190   }
191
192  private:
193   PrimaryAllocator primary_;
194   SecondaryAllocator secondary_;
195   AllocatorGlobalStats stats_;
196 };
197