blob: e3376e746ebde61d3f5b9425092a012b87a939a4 [file] [log] [blame]
Dynamic Tools Team517193e2019-09-11 14:48:41 +00001//===-- primary32.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#ifndef SCUDO_PRIMARY32_H_
10#define SCUDO_PRIMARY32_H_
11
12#include "bytemap.h"
13#include "common.h"
14#include "list.h"
15#include "local_cache.h"
16#include "release.h"
17#include "report.h"
18#include "stats.h"
19#include "string_utils.h"
20
21namespace scudo {
22
23// SizeClassAllocator32 is an allocator for 32 or 64-bit address space.
24//
25// It maps Regions of 2^RegionSizeLog bytes aligned on a 2^RegionSizeLog bytes
26// boundary, and keeps a bytemap of the mappable address space to track the size
27// class they are associated with.
28//
29// Mapped regions are split into equally sized Blocks according to the size
30// class they belong to, and the associated pointers are shuffled to prevent any
31// predictable address pattern (the predictability increases with the block
32// size).
33//
34// Regions for size class 0 are special and used to hold TransferBatches, which
35// allow to transfer arrays of pointers from the global size class freelist to
36// the thread specific freelist for said class, and back.
37//
38// Memory used by this allocator is never unmapped but can be partially
39// reclaimed if the platform allows for it.
40
Dynamic Tools Team26387462020-02-14 12:24:03 -080041template <class SizeClassMapT, uptr RegionSizeLog,
42 s32 MinReleaseToOsIntervalMs = INT32_MIN,
Dynamic Tools Team2e7fec22020-02-16 15:29:46 -080043 s32 MaxReleaseToOsIntervalMs = INT32_MAX>
44class SizeClassAllocator32 {
Dynamic Tools Team517193e2019-09-11 14:48:41 +000045public:
46 typedef SizeClassMapT SizeClassMap;
Dynamic Tools Teamac403052020-02-06 15:46:05 -080047 // The bytemap can only track UINT8_MAX - 1 classes.
48 static_assert(SizeClassMap::LargestClassId <= (UINT8_MAX - 1), "");
Dynamic Tools Team517193e2019-09-11 14:48:41 +000049 // Regions should be large enough to hold the largest Block.
Dynamic Tools Team09e6d482019-11-26 18:18:14 -080050 static_assert((1UL << RegionSizeLog) >= SizeClassMap::MaxSize, "");
Dynamic Tools Team26387462020-02-14 12:24:03 -080051 typedef SizeClassAllocator32<SizeClassMapT, RegionSizeLog,
52 MinReleaseToOsIntervalMs,
Dynamic Tools Team2e7fec22020-02-16 15:29:46 -080053 MaxReleaseToOsIntervalMs>
54 ThisT;
Dynamic Tools Team517193e2019-09-11 14:48:41 +000055 typedef SizeClassAllocatorLocalCache<ThisT> CacheT;
56 typedef typename CacheT::TransferBatch TransferBatch;
Dynamic Tools Team48429c72019-12-04 17:46:15 -080057 static const bool SupportsMemoryTagging = false;
Dynamic Tools Team517193e2019-09-11 14:48:41 +000058
59 static uptr getSizeByClassId(uptr ClassId) {
60 return (ClassId == SizeClassMap::BatchClassId)
61 ? sizeof(TransferBatch)
62 : SizeClassMap::getSizeByClassId(ClassId);
63 }
64
65 static bool canAllocate(uptr Size) { return Size <= SizeClassMap::MaxSize; }
66
67 void initLinkerInitialized(s32 ReleaseToOsInterval) {
68 if (SCUDO_FUCHSIA)
69 reportError("SizeClassAllocator32 is not supported on Fuchsia");
70
71 PossibleRegions.initLinkerInitialized();
72 MinRegionIndex = NumRegions; // MaxRegionIndex is already initialized to 0.
73
74 u32 Seed;
75 if (UNLIKELY(!getRandom(reinterpret_cast<void *>(&Seed), sizeof(Seed))))
76 Seed =
77 static_cast<u32>(getMonotonicTime() ^
78 (reinterpret_cast<uptr>(SizeClassInfoArray) >> 6));
79 const uptr PageSize = getPageSizeCached();
80 for (uptr I = 0; I < NumClasses; I++) {
81 SizeClassInfo *Sci = getSizeClassInfo(I);
82 Sci->RandState = getRandomU32(&Seed);
83 // See comment in the 64-bit primary about releasing smaller size classes.
Dynamic Tools Teamf6d5c5d2020-01-30 11:06:49 -080084 Sci->CanRelease = (I != SizeClassMap::BatchClassId) &&
Dynamic Tools Team70cfbf12020-01-29 12:35:44 -080085 (getSizeByClassId(I) >= (PageSize / 32));
Dynamic Tools Team517193e2019-09-11 14:48:41 +000086 }
Dynamic Tools Team26387462020-02-14 12:24:03 -080087 setReleaseToOsIntervalMs(ReleaseToOsInterval);
Dynamic Tools Team517193e2019-09-11 14:48:41 +000088 }
89 void init(s32 ReleaseToOsInterval) {
90 memset(this, 0, sizeof(*this));
91 initLinkerInitialized(ReleaseToOsInterval);
92 }
93
94 void unmapTestOnly() {
95 while (NumberOfStashedRegions > 0)
96 unmap(reinterpret_cast<void *>(RegionsStash[--NumberOfStashedRegions]),
97 RegionSize);
Dynamic Tools Teamac403052020-02-06 15:46:05 -080098 for (uptr I = MinRegionIndex; I <= MaxRegionIndex; I++)
Dynamic Tools Team517193e2019-09-11 14:48:41 +000099 if (PossibleRegions[I])
100 unmap(reinterpret_cast<void *>(I * RegionSize), RegionSize);
101 PossibleRegions.unmapTestOnly();
102 }
103
104 TransferBatch *popBatch(CacheT *C, uptr ClassId) {
105 DCHECK_LT(ClassId, NumClasses);
106 SizeClassInfo *Sci = getSizeClassInfo(ClassId);
107 ScopedLock L(Sci->Mutex);
108 TransferBatch *B = Sci->FreeList.front();
109 if (B) {
110 Sci->FreeList.pop_front();
111 } else {
112 B = populateFreeList(C, ClassId, Sci);
113 if (UNLIKELY(!B))
114 return nullptr;
115 }
116 DCHECK_GT(B->getCount(), 0);
117 Sci->Stats.PoppedBlocks += B->getCount();
118 return B;
119 }
120
121 void pushBatch(uptr ClassId, TransferBatch *B) {
122 DCHECK_LT(ClassId, NumClasses);
123 DCHECK_GT(B->getCount(), 0);
124 SizeClassInfo *Sci = getSizeClassInfo(ClassId);
125 ScopedLock L(Sci->Mutex);
126 Sci->FreeList.push_front(B);
127 Sci->Stats.PushedBlocks += B->getCount();
128 if (Sci->CanRelease)
129 releaseToOSMaybe(Sci, ClassId);
130 }
131
132 void disable() {
Dynamic Tools Team83eaa512020-01-09 11:43:16 -0800133 // The BatchClassId must be locked last since other classes can use it.
134 for (sptr I = static_cast<sptr>(NumClasses) - 1; I >= 0; I--) {
135 if (static_cast<uptr>(I) == SizeClassMap::BatchClassId)
136 continue;
137 getSizeClassInfo(static_cast<uptr>(I))->Mutex.lock();
138 }
139 getSizeClassInfo(SizeClassMap::BatchClassId)->Mutex.lock();
140 RegionsStashMutex.lock();
141 PossibleRegions.disable();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000142 }
143
144 void enable() {
Dynamic Tools Team83eaa512020-01-09 11:43:16 -0800145 PossibleRegions.enable();
146 RegionsStashMutex.unlock();
147 getSizeClassInfo(SizeClassMap::BatchClassId)->Mutex.unlock();
148 for (uptr I = 0; I < NumClasses; I++) {
149 if (I == SizeClassMap::BatchClassId)
150 continue;
151 getSizeClassInfo(I)->Mutex.unlock();
152 }
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000153 }
154
155 template <typename F> void iterateOverBlocks(F Callback) {
156 for (uptr I = MinRegionIndex; I <= MaxRegionIndex; I++)
Dynamic Tools Teamac403052020-02-06 15:46:05 -0800157 if (PossibleRegions[I] &&
158 (PossibleRegions[I] - 1U) != SizeClassMap::BatchClassId) {
159 const uptr BlockSize = getSizeByClassId(PossibleRegions[I] - 1U);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000160 const uptr From = I * RegionSize;
161 const uptr To = From + (RegionSize / BlockSize) * BlockSize;
162 for (uptr Block = From; Block < To; Block += BlockSize)
163 Callback(Block);
164 }
165 }
166
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000167 void getStats(ScopedString *Str) {
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000168 // TODO(kostyak): get the RSS per region.
169 uptr TotalMapped = 0;
170 uptr PoppedBlocks = 0;
171 uptr PushedBlocks = 0;
172 for (uptr I = 0; I < NumClasses; I++) {
173 SizeClassInfo *Sci = getSizeClassInfo(I);
174 TotalMapped += Sci->AllocatedUser;
175 PoppedBlocks += Sci->Stats.PoppedBlocks;
176 PushedBlocks += Sci->Stats.PushedBlocks;
177 }
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000178 Str->append("Stats: SizeClassAllocator32: %zuM mapped in %zu allocations; "
179 "remains %zu\n",
180 TotalMapped >> 20, PoppedBlocks, PoppedBlocks - PushedBlocks);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000181 for (uptr I = 0; I < NumClasses; I++)
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000182 getStats(Str, I, 0);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000183 }
184
Dynamic Tools Team26387462020-02-14 12:24:03 -0800185 void setReleaseToOsIntervalMs(s32 Interval) {
186 if (Interval >= MaxReleaseToOsIntervalMs) {
187 Interval = MaxReleaseToOsIntervalMs;
188 } else if (Interval <= MinReleaseToOsIntervalMs) {
189 Interval = MinReleaseToOsIntervalMs;
190 }
191 atomic_store(&ReleaseToOsIntervalMs, Interval, memory_order_relaxed);
192 }
193
Dynamic Tools Teamc283bab2019-10-07 17:37:39 +0000194 uptr releaseToOS() {
195 uptr TotalReleasedBytes = 0;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000196 for (uptr I = 0; I < NumClasses; I++) {
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000197 SizeClassInfo *Sci = getSizeClassInfo(I);
198 ScopedLock L(Sci->Mutex);
Dynamic Tools Teamc283bab2019-10-07 17:37:39 +0000199 TotalReleasedBytes += releaseToOSMaybe(Sci, I, /*Force=*/true);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000200 }
Dynamic Tools Teamc283bab2019-10-07 17:37:39 +0000201 return TotalReleasedBytes;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000202 }
203
Dynamic Tools Team48429c72019-12-04 17:46:15 -0800204 bool useMemoryTagging() { return false; }
205 void disableMemoryTagging() {}
206
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000207private:
208 static const uptr NumClasses = SizeClassMap::NumClasses;
209 static const uptr RegionSize = 1UL << RegionSizeLog;
210 static const uptr NumRegions = SCUDO_MMAP_RANGE_SIZE >> RegionSizeLog;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000211 typedef FlatByteMap<NumRegions> ByteMap;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000212
213 struct SizeClassStats {
214 uptr PoppedBlocks;
215 uptr PushedBlocks;
216 };
217
218 struct ReleaseToOsInfo {
219 uptr PushedBlocksAtLastRelease;
220 uptr RangesReleased;
221 uptr LastReleasedBytes;
222 u64 LastReleaseAtNs;
223 };
224
225 struct ALIGNED(SCUDO_CACHE_LINE_SIZE) SizeClassInfo {
226 HybridMutex Mutex;
Dynamic Tools Team35997702019-10-28 15:06:10 -0700227 SinglyLinkedList<TransferBatch> FreeList;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000228 SizeClassStats Stats;
229 bool CanRelease;
230 u32 RandState;
231 uptr AllocatedUser;
232 ReleaseToOsInfo ReleaseInfo;
233 };
Dynamic Tools Team09e6d482019-11-26 18:18:14 -0800234 static_assert(sizeof(SizeClassInfo) % SCUDO_CACHE_LINE_SIZE == 0, "");
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000235
236 uptr computeRegionId(uptr Mem) {
237 const uptr Id = Mem >> RegionSizeLog;
238 CHECK_LT(Id, NumRegions);
239 return Id;
240 }
241
242 uptr allocateRegionSlow() {
243 uptr MapSize = 2 * RegionSize;
244 const uptr MapBase = reinterpret_cast<uptr>(
245 map(nullptr, MapSize, "scudo:primary", MAP_ALLOWNOMEM));
246 if (UNLIKELY(!MapBase))
247 return 0;
248 const uptr MapEnd = MapBase + MapSize;
249 uptr Region = MapBase;
250 if (isAligned(Region, RegionSize)) {
251 ScopedLock L(RegionsStashMutex);
252 if (NumberOfStashedRegions < MaxStashedRegions)
253 RegionsStash[NumberOfStashedRegions++] = MapBase + RegionSize;
254 else
255 MapSize = RegionSize;
256 } else {
257 Region = roundUpTo(MapBase, RegionSize);
258 unmap(reinterpret_cast<void *>(MapBase), Region - MapBase);
259 MapSize = RegionSize;
260 }
261 const uptr End = Region + MapSize;
262 if (End != MapEnd)
263 unmap(reinterpret_cast<void *>(End), MapEnd - End);
264 return Region;
265 }
266
267 uptr allocateRegion(uptr ClassId) {
268 DCHECK_LT(ClassId, NumClasses);
269 uptr Region = 0;
270 {
271 ScopedLock L(RegionsStashMutex);
272 if (NumberOfStashedRegions > 0)
273 Region = RegionsStash[--NumberOfStashedRegions];
274 }
275 if (!Region)
276 Region = allocateRegionSlow();
277 if (LIKELY(Region)) {
Dynamic Tools Teamac403052020-02-06 15:46:05 -0800278 const uptr RegionIndex = computeRegionId(Region);
279 if (RegionIndex < MinRegionIndex)
280 MinRegionIndex = RegionIndex;
281 if (RegionIndex > MaxRegionIndex)
282 MaxRegionIndex = RegionIndex;
283 PossibleRegions.set(RegionIndex, static_cast<u8>(ClassId + 1U));
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000284 }
285 return Region;
286 }
287
288 SizeClassInfo *getSizeClassInfo(uptr ClassId) {
289 DCHECK_LT(ClassId, NumClasses);
290 return &SizeClassInfoArray[ClassId];
291 }
292
293 bool populateBatches(CacheT *C, SizeClassInfo *Sci, uptr ClassId,
294 TransferBatch **CurrentBatch, u32 MaxCount,
295 void **PointersArray, u32 Count) {
296 if (ClassId != SizeClassMap::BatchClassId)
297 shuffle(PointersArray, Count, &Sci->RandState);
298 TransferBatch *B = *CurrentBatch;
299 for (uptr I = 0; I < Count; I++) {
300 if (B && B->getCount() == MaxCount) {
301 Sci->FreeList.push_back(B);
302 B = nullptr;
303 }
304 if (!B) {
305 B = C->createBatch(ClassId, PointersArray[I]);
306 if (UNLIKELY(!B))
307 return false;
308 B->clear();
309 }
310 B->add(PointersArray[I]);
311 }
312 *CurrentBatch = B;
313 return true;
314 }
315
316 NOINLINE TransferBatch *populateFreeList(CacheT *C, uptr ClassId,
317 SizeClassInfo *Sci) {
318 const uptr Region = allocateRegion(ClassId);
319 if (UNLIKELY(!Region))
320 return nullptr;
321 C->getStats().add(StatMapped, RegionSize);
322 const uptr Size = getSizeByClassId(ClassId);
323 const u32 MaxCount = TransferBatch::getMaxCached(Size);
324 DCHECK_GT(MaxCount, 0);
325 const uptr NumberOfBlocks = RegionSize / Size;
326 DCHECK_GT(NumberOfBlocks, 0);
327 TransferBatch *B = nullptr;
Dynamic Tools Teamaa152462019-11-19 10:18:38 -0800328 constexpr u32 ShuffleArraySize = 8U * TransferBatch::MaxNumCached;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000329 void *ShuffleArray[ShuffleArraySize];
330 u32 Count = 0;
Dynamic Tools Teamaa152462019-11-19 10:18:38 -0800331 const uptr AllocatedUser = Size * NumberOfBlocks;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000332 for (uptr I = Region; I < Region + AllocatedUser; I += Size) {
333 ShuffleArray[Count++] = reinterpret_cast<void *>(I);
334 if (Count == ShuffleArraySize) {
335 if (UNLIKELY(!populateBatches(C, Sci, ClassId, &B, MaxCount,
336 ShuffleArray, Count)))
337 return nullptr;
338 Count = 0;
339 }
340 }
341 if (Count) {
342 if (UNLIKELY(!populateBatches(C, Sci, ClassId, &B, MaxCount, ShuffleArray,
343 Count)))
344 return nullptr;
345 }
346 DCHECK(B);
Dynamic Tools Teamaa152462019-11-19 10:18:38 -0800347 if (!Sci->FreeList.empty()) {
348 Sci->FreeList.push_back(B);
349 B = Sci->FreeList.front();
350 Sci->FreeList.pop_front();
351 }
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000352 DCHECK_GT(B->getCount(), 0);
353
354 C->getStats().add(StatFree, AllocatedUser);
355 Sci->AllocatedUser += AllocatedUser;
356 if (Sci->CanRelease)
357 Sci->ReleaseInfo.LastReleaseAtNs = getMonotonicTime();
358 return B;
359 }
360
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000361 void getStats(ScopedString *Str, uptr ClassId, uptr Rss) {
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000362 SizeClassInfo *Sci = getSizeClassInfo(ClassId);
363 if (Sci->AllocatedUser == 0)
364 return;
365 const uptr InUse = Sci->Stats.PoppedBlocks - Sci->Stats.PushedBlocks;
366 const uptr AvailableChunks = Sci->AllocatedUser / getSizeByClassId(ClassId);
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000367 Str->append(" %02zu (%6zu): mapped: %6zuK popped: %7zu pushed: %7zu "
Dynamic Tools Teamac403052020-02-06 15:46:05 -0800368 "inuse: %6zu avail: %6zu rss: %6zuK releases: %6zu\n",
Dynamic Tools Team3e8c65b2019-10-18 20:00:32 +0000369 ClassId, getSizeByClassId(ClassId), Sci->AllocatedUser >> 10,
370 Sci->Stats.PoppedBlocks, Sci->Stats.PushedBlocks, InUse,
Dynamic Tools Teamac403052020-02-06 15:46:05 -0800371 AvailableChunks, Rss >> 10, Sci->ReleaseInfo.RangesReleased);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000372 }
373
Dynamic Tools Team26387462020-02-14 12:24:03 -0800374 s32 getReleaseToOsIntervalMs() {
375 return atomic_load(&ReleaseToOsIntervalMs, memory_order_relaxed);
376 }
377
Dynamic Tools Teamc283bab2019-10-07 17:37:39 +0000378 NOINLINE uptr releaseToOSMaybe(SizeClassInfo *Sci, uptr ClassId,
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000379 bool Force = false) {
380 const uptr BlockSize = getSizeByClassId(ClassId);
381 const uptr PageSize = getPageSizeCached();
382
383 CHECK_GE(Sci->Stats.PoppedBlocks, Sci->Stats.PushedBlocks);
Dynamic Tools Teamc283bab2019-10-07 17:37:39 +0000384 const uptr BytesInFreeList =
385 Sci->AllocatedUser -
386 (Sci->Stats.PoppedBlocks - Sci->Stats.PushedBlocks) * BlockSize;
387 if (BytesInFreeList < PageSize)
388 return 0; // No chance to release anything.
Dynamic Tools Teamebcf82d2020-02-25 14:23:34 -0800389 const uptr BytesPushed =
390 (Sci->Stats.PushedBlocks - Sci->ReleaseInfo.PushedBlocksAtLastRelease) *
391 BlockSize;
392 if (BytesPushed < PageSize)
Dynamic Tools Teamc283bab2019-10-07 17:37:39 +0000393 return 0; // Nothing new to release.
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000394
395 if (!Force) {
Dynamic Tools Team26387462020-02-14 12:24:03 -0800396 const s32 IntervalMs = getReleaseToOsIntervalMs();
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000397 if (IntervalMs < 0)
Dynamic Tools Teamc283bab2019-10-07 17:37:39 +0000398 return 0;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000399 if (Sci->ReleaseInfo.LastReleaseAtNs +
Dynamic Tools Teamc5d5abc2020-01-27 14:03:21 -0800400 static_cast<u64>(IntervalMs) * 1000000 >
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000401 getMonotonicTime()) {
Dynamic Tools Teamc283bab2019-10-07 17:37:39 +0000402 return 0; // Memory was returned recently.
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000403 }
404 }
405
406 // TODO(kostyak): currently not ideal as we loop over all regions and
407 // iterate multiple times over the same freelist if a ClassId spans multiple
408 // regions. But it will have to do for now.
Dynamic Tools Teamc283bab2019-10-07 17:37:39 +0000409 uptr TotalReleasedBytes = 0;
Dynamic Tools Teamebcf82d2020-02-25 14:23:34 -0800410 const uptr Size = (RegionSize / BlockSize) * BlockSize;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000411 for (uptr I = MinRegionIndex; I <= MaxRegionIndex; I++) {
Dynamic Tools Teamac403052020-02-06 15:46:05 -0800412 if (PossibleRegions[I] - 1U == ClassId) {
413 const uptr Region = I * RegionSize;
414 ReleaseRecorder Recorder(Region);
Dynamic Tools Teamebcf82d2020-02-25 14:23:34 -0800415 releaseFreeMemoryToOS(Sci->FreeList, Region, Size, BlockSize,
416 &Recorder);
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000417 if (Recorder.getReleasedRangesCount() > 0) {
418 Sci->ReleaseInfo.PushedBlocksAtLastRelease = Sci->Stats.PushedBlocks;
419 Sci->ReleaseInfo.RangesReleased += Recorder.getReleasedRangesCount();
420 Sci->ReleaseInfo.LastReleasedBytes = Recorder.getReleasedBytes();
Dynamic Tools Teamc283bab2019-10-07 17:37:39 +0000421 TotalReleasedBytes += Sci->ReleaseInfo.LastReleasedBytes;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000422 }
423 }
424 }
425 Sci->ReleaseInfo.LastReleaseAtNs = getMonotonicTime();
Dynamic Tools Teamc283bab2019-10-07 17:37:39 +0000426 return TotalReleasedBytes;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000427 }
428
429 SizeClassInfo SizeClassInfoArray[NumClasses];
430
Dynamic Tools Teamac403052020-02-06 15:46:05 -0800431 // Track the regions in use, 0 is unused, otherwise store ClassId + 1.
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000432 ByteMap PossibleRegions;
433 // Keep track of the lowest & highest regions allocated to avoid looping
434 // through the whole NumRegions.
435 uptr MinRegionIndex;
436 uptr MaxRegionIndex;
Dynamic Tools Team26387462020-02-14 12:24:03 -0800437 atomic_s32 ReleaseToOsIntervalMs;
Dynamic Tools Team517193e2019-09-11 14:48:41 +0000438 // Unless several threads request regions simultaneously from different size
439 // classes, the stash rarely contains more than 1 entry.
440 static constexpr uptr MaxStashedRegions = 4;
441 HybridMutex RegionsStashMutex;
442 uptr NumberOfStashedRegions;
443 uptr RegionsStash[MaxStashedRegions];
444};
445
446} // namespace scudo
447
448#endif // SCUDO_PRIMARY32_H_