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