master
  1//===-- sanitizer_allocator_combined.h --------------------------*- C++ -*-===//
  2//
  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
  6//
  7//===----------------------------------------------------------------------===//
  8//
  9// Part of the Sanitizer Allocator.
 10//
 11//===----------------------------------------------------------------------===//
 12#ifndef SANITIZER_ALLOCATOR_H
 13#error This file must be included inside sanitizer_allocator.h
 14#endif
 15
 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.
 22template <class PrimaryAllocator,
 23          class LargeMmapAllocatorPtrArray = DefaultLargeMmapAllocatorPtrArray>
 24class CombinedAllocator {
 25 public:
 26  using AllocatorCache = typename PrimaryAllocator::AllocatorCache;
 27  using SecondaryAllocator =
 28      LargeMmapAllocator<typename PrimaryAllocator::MapUnmapCallback,
 29                         LargeMmapAllocatorPtrArray,
 30                         typename PrimaryAllocator::AddressSpaceView>;
 31
 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();
 36  }
 37
 38  void Init(s32 release_to_os_interval_ms, uptr heap_start = 0) {
 39    stats_.Init();
 40    primary_.Init(release_to_os_interval_ms, heap_start);
 41    secondary_.Init();
 42  }
 43
 44  void *Allocate(AllocatorCache *cache, uptr size, uptr alignment) {
 45    // Returning 0 on malloc(0) may break a lot of code.
 46    if (size == 0)
 47      size = 1;
 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);
 52      return nullptr;
 53    }
 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
 58    // alignment check.
 59    if (alignment > 8)
 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'.
 67    void *res;
 68    if (primary_.CanAllocate(size, alignment))
 69      res = cache->Allocate(&primary_, primary_.ClassID(size));
 70    else
 71      res = secondary_.Allocate(&stats_, original_size, alignment);
 72    if (alignment > 8)
 73      CHECK_EQ(reinterpret_cast<uptr>(res) & (alignment - 1), 0);
 74    return res;
 75  }
 76
 77  s32 ReleaseToOSIntervalMs() const {
 78    return primary_.ReleaseToOSIntervalMs();
 79  }
 80
 81  void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) {
 82    primary_.SetReleaseToOSIntervalMs(release_to_os_interval_ms);
 83  }
 84
 85  void ForceReleaseToOS() {
 86    primary_.ForceReleaseToOS();
 87  }
 88
 89  void Deallocate(AllocatorCache *cache, void *p) {
 90    if (!p) return;
 91    if (primary_.PointerIsMine(p))
 92      cache->Deallocate(&primary_, primary_.GetSizeClass(p), p);
 93    else
 94      secondary_.Deallocate(&stats_, p);
 95  }
 96
 97  void *Reallocate(AllocatorCache *cache, void *p, uptr new_size,
 98                   uptr alignment) {
 99    if (!p)
100      return Allocate(cache, new_size, alignment);
101    if (!new_size) {
102      Deallocate(cache, p);
103      return nullptr;
104    }
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);
109    if (new_p)
110      internal_memcpy(new_p, p, memcpy_size);
111    Deallocate(cache, p);
112    return new_p;
113  }
114
115  bool PointerIsMine(const void *p) const {
116    if (primary_.PointerIsMine(p))
117      return true;
118    return secondary_.PointerIsMine(p);
119  }
120
121  bool FromPrimary(const void *p) const { return primary_.PointerIsMine(p); }
122
123  void *GetMetaData(const void *p) {
124    if (primary_.PointerIsMine(p))
125      return primary_.GetMetaData(p);
126    return secondary_.GetMetaData(p);
127  }
128
129  void *GetBlockBegin(const void *p) {
130    if (primary_.PointerIsMine(p))
131      return primary_.GetBlockBegin(p);
132    return secondary_.GetBlockBegin(p);
133  }
134
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);
141  }
142
143  uptr GetActuallyAllocatedSize(void *p) {
144    if (primary_.PointerIsMine(p))
145      return primary_.GetActuallyAllocatedSize(p);
146    return secondary_.GetActuallyAllocatedSize(p);
147  }
148
149  uptr TotalMemoryUsed() {
150    return primary_.TotalMemoryUsed() + secondary_.TotalMemoryUsed();
151  }
152
153  void TestOnlyUnmap() { primary_.TestOnlyUnmap(); }
154
155  void InitCache(AllocatorCache *cache) {
156    cache->Init(&stats_);
157  }
158
159  void DestroyCache(AllocatorCache *cache) {
160    cache->Destroy(&primary_, &stats_);
161  }
162
163  void SwallowCache(AllocatorCache *cache) {
164    cache->Drain(&primary_);
165  }
166
167  void GetStats(AllocatorStatCounters s) const {
168    stats_.Get(s);
169  }
170
171  void PrintStats() {
172    primary_.PrintStats();
173    secondary_.PrintStats();
174  }
175
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();
181  }
182
183  void ForceUnlock() SANITIZER_NO_THREAD_SAFETY_ANALYSIS {
184    secondary_.ForceUnlock();
185    primary_.ForceUnlock();
186  }
187
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);
193  }
194
195 private:
196  PrimaryAllocator primary_;
197  SecondaryAllocator secondary_;
198  AllocatorGlobalStats stats_;
199};