1311120Sdim//===-- sanitizer_allocator_combined.h --------------------------*- C++ -*-===// 2311120Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6311120Sdim// 7311120Sdim//===----------------------------------------------------------------------===// 8311120Sdim// 9311120Sdim// Part of the Sanitizer Allocator. 10311120Sdim// 11311120Sdim//===----------------------------------------------------------------------===// 12311120Sdim#ifndef SANITIZER_ALLOCATOR_H 13311120Sdim#error This file must be included inside sanitizer_allocator.h 14311120Sdim#endif 15311120Sdim 16311120Sdim// This class implements a complete memory allocator by using two 17311120Sdim// internal allocators: 18311120Sdim// PrimaryAllocator is efficient, but may not allocate some sizes (alignments). 19311120Sdim// When allocating 2^x bytes it should return 2^x aligned chunk. 20311120Sdim// PrimaryAllocator is used via a local AllocatorCache. 21311120Sdim// SecondaryAllocator can allocate anything, but is not efficient. 22353358Sdimtemplate <class PrimaryAllocator, 23353358Sdim class LargeMmapAllocatorPtrArray = DefaultLargeMmapAllocatorPtrArray> 24311120Sdimclass CombinedAllocator { 25311120Sdim public: 26353358Sdim using AllocatorCache = typename PrimaryAllocator::AllocatorCache; 27353358Sdim using SecondaryAllocator = 28353358Sdim LargeMmapAllocator<typename PrimaryAllocator::MapUnmapCallback, 29353358Sdim LargeMmapAllocatorPtrArray, 30353358Sdim typename PrimaryAllocator::AddressSpaceView>; 31344779Sdim 32321369Sdim void InitLinkerInitialized(s32 release_to_os_interval_ms) { 33344779Sdim stats_.InitLinkerInitialized(); 34311120Sdim primary_.Init(release_to_os_interval_ms); 35321369Sdim secondary_.InitLinkerInitialized(); 36311120Sdim } 37311120Sdim 38321369Sdim void Init(s32 release_to_os_interval_ms) { 39344779Sdim stats_.Init(); 40321369Sdim primary_.Init(release_to_os_interval_ms); 41321369Sdim secondary_.Init(); 42311120Sdim } 43311120Sdim 44321369Sdim void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) { 45311120Sdim // Returning 0 on malloc(0) may break a lot of code. 46311120Sdim if (size == 0) 47311120Sdim size = 1; 48341825Sdim if (size + alignment < size) { 49341825Sdim Report("WARNING: %s: CombinedAllocator allocation overflow: " 50341825Sdim "0x%zx bytes with 0x%zx alignment requested\n", 51341825Sdim SanitizerToolName, size, alignment); 52341825Sdim return nullptr; 53341825Sdim } 54311120Sdim uptr original_size = size; 55311120Sdim // If alignment requirements are to be fulfilled by the frontend allocator 56311120Sdim // rather than by the primary or secondary, passing an alignment lower than 57311120Sdim // or equal to 8 will prevent any further rounding up, as well as the later 58311120Sdim // alignment check. 59311120Sdim if (alignment > 8) 60311120Sdim size = RoundUpTo(size, alignment); 61311120Sdim // The primary allocator should return a 2^x aligned allocation when 62311120Sdim // requested 2^x bytes, hence using the rounded up 'size' when being 63311120Sdim // serviced by the primary (this is no longer true when the primary is 64311120Sdim // using a non-fixed base address). The secondary takes care of the 65311120Sdim // alignment without such requirement, and allocating 'size' would use 66311120Sdim // extraneous memory, so we employ 'original_size'. 67321369Sdim void *res; 68321369Sdim if (primary_.CanAllocate(size, alignment)) 69311120Sdim res = cache->Allocate(&primary_, primary_.ClassID(size)); 70311120Sdim else 71311120Sdim res = secondary_.Allocate(&stats_, original_size, alignment); 72311120Sdim if (alignment > 8) 73311120Sdim CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0); 74311120Sdim return res; 75311120Sdim } 76311120Sdim 77311120Sdim s32 ReleaseToOSIntervalMs() const { 78311120Sdim return primary_.ReleaseToOSIntervalMs(); 79311120Sdim } 80311120Sdim 81311120Sdim void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) { 82311120Sdim primary_.SetReleaseToOSIntervalMs(release_to_os_interval_ms); 83311120Sdim } 84311120Sdim 85327952Sdim void ForceReleaseToOS() { 86327952Sdim primary_.ForceReleaseToOS(); 87327952Sdim } 88327952Sdim 89311120Sdim void Deallocate(AllocatorCache *cache, void *p) { 90311120Sdim if (!p) return; 91311120Sdim if (primary_.PointerIsMine(p)) 92311120Sdim cache->Deallocate(&primary_, primary_.GetSizeClass(p), p); 93311120Sdim else 94311120Sdim secondary_.Deallocate(&stats_, p); 95311120Sdim } 96311120Sdim 97311120Sdim void *Reallocate(AllocatorCache *cache, void *p, uptr new_size, 98311120Sdim uptr alignment) { 99311120Sdim if (!p) 100311120Sdim return Allocate(cache, new_size, alignment); 101311120Sdim if (!new_size) { 102311120Sdim Deallocate(cache, p); 103311120Sdim return nullptr; 104311120Sdim } 105311120Sdim CHECK(PointerIsMine(p)); 106311120Sdim uptr old_size = GetActuallyAllocatedSize(p); 107311120Sdim uptr memcpy_size = Min(new_size, old_size); 108311120Sdim void *new_p = Allocate(cache, new_size, alignment); 109311120Sdim if (new_p) 110311120Sdim internal_memcpy(new_p, p, memcpy_size); 111311120Sdim Deallocate(cache, p); 112311120Sdim return new_p; 113311120Sdim } 114311120Sdim 115311120Sdim bool PointerIsMine(void *p) { 116311120Sdim if (primary_.PointerIsMine(p)) 117311120Sdim return true; 118311120Sdim return secondary_.PointerIsMine(p); 119311120Sdim } 120311120Sdim 121311120Sdim bool FromPrimary(void *p) { 122311120Sdim return primary_.PointerIsMine(p); 123311120Sdim } 124311120Sdim 125311120Sdim void *GetMetaData(const void *p) { 126311120Sdim if (primary_.PointerIsMine(p)) 127311120Sdim return primary_.GetMetaData(p); 128311120Sdim return secondary_.GetMetaData(p); 129311120Sdim } 130311120Sdim 131311120Sdim void *GetBlockBegin(const void *p) { 132311120Sdim if (primary_.PointerIsMine(p)) 133311120Sdim return primary_.GetBlockBegin(p); 134311120Sdim return secondary_.GetBlockBegin(p); 135311120Sdim } 136311120Sdim 137311120Sdim // This function does the same as GetBlockBegin, but is much faster. 138311120Sdim // Must be called with the allocator locked. 139311120Sdim void *GetBlockBeginFastLocked(void *p) { 140311120Sdim if (primary_.PointerIsMine(p)) 141311120Sdim return primary_.GetBlockBegin(p); 142311120Sdim return secondary_.GetBlockBeginFastLocked(p); 143311120Sdim } 144311120Sdim 145311120Sdim uptr GetActuallyAllocatedSize(void *p) { 146311120Sdim if (primary_.PointerIsMine(p)) 147311120Sdim return primary_.GetActuallyAllocatedSize(p); 148311120Sdim return secondary_.GetActuallyAllocatedSize(p); 149311120Sdim } 150311120Sdim 151311120Sdim uptr TotalMemoryUsed() { 152311120Sdim return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed(); 153311120Sdim } 154311120Sdim 155311120Sdim void TestOnlyUnmap() { primary_.TestOnlyUnmap(); } 156311120Sdim 157311120Sdim void InitCache(AllocatorCache *cache) { 158311120Sdim cache->Init(&stats_); 159311120Sdim } 160311120Sdim 161311120Sdim void DestroyCache(AllocatorCache *cache) { 162311120Sdim cache->Destroy(&primary_, &stats_); 163311120Sdim } 164311120Sdim 165311120Sdim void SwallowCache(AllocatorCache *cache) { 166311120Sdim cache->Drain(&primary_); 167311120Sdim } 168311120Sdim 169311120Sdim void GetStats(AllocatorStatCounters s) const { 170311120Sdim stats_.Get(s); 171311120Sdim } 172311120Sdim 173311120Sdim void PrintStats() { 174311120Sdim primary_.PrintStats(); 175311120Sdim secondary_.PrintStats(); 176311120Sdim } 177311120Sdim 178311120Sdim // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone 179311120Sdim // introspection API. 180311120Sdim void ForceLock() { 181311120Sdim primary_.ForceLock(); 182311120Sdim secondary_.ForceLock(); 183311120Sdim } 184311120Sdim 185311120Sdim void ForceUnlock() { 186311120Sdim secondary_.ForceUnlock(); 187311120Sdim primary_.ForceUnlock(); 188311120Sdim } 189311120Sdim 190311120Sdim // Iterate over all existing chunks. 191311120Sdim // The allocator must be locked when calling this function. 192311120Sdim void ForEachChunk(ForEachChunkCallback callback, void *arg) { 193311120Sdim primary_.ForEachChunk(callback, arg); 194311120Sdim secondary_.ForEachChunk(callback, arg); 195311120Sdim } 196311120Sdim 197311120Sdim private: 198311120Sdim PrimaryAllocator primary_; 199311120Sdim SecondaryAllocator secondary_; 200311120Sdim AllocatorGlobalStats stats_; 201311120Sdim}; 202