Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / compiler-rt / lib / scudo / standalone / tests / quarantine_test.cpp
blob972c98d510a90f9416d6d41b73cc81abe9d1adcd
1 //===-- quarantine_test.cpp -------------------------------------*- 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 //===----------------------------------------------------------------------===//
9 #include "tests/scudo_unit_test.h"
11 #include "quarantine.h"
13 #include <pthread.h>
14 #include <stdlib.h>
16 static void *FakePtr = reinterpret_cast<void *>(0xFA83FA83);
17 static const scudo::uptr BlockSize = 8UL;
18 static const scudo::uptr LargeBlockSize = 16384UL;
20 struct QuarantineCallback {
21 void recycle(void *P) { EXPECT_EQ(P, FakePtr); }
22 void *allocate(scudo::uptr Size) { return malloc(Size); }
23 void deallocate(void *P) { free(P); }
26 typedef scudo::GlobalQuarantine<QuarantineCallback, void> QuarantineT;
27 typedef typename QuarantineT::CacheT CacheT;
29 static QuarantineCallback Cb;
31 static void deallocateCache(CacheT *Cache) {
32 while (scudo::QuarantineBatch *Batch = Cache->dequeueBatch())
33 Cb.deallocate(Batch);
36 TEST(ScudoQuarantineTest, QuarantineBatchMerge) {
37 // Verify the trivial case.
38 scudo::QuarantineBatch Into;
39 Into.init(FakePtr, 4UL);
40 scudo::QuarantineBatch From;
41 From.init(FakePtr, 8UL);
43 Into.merge(&From);
45 EXPECT_EQ(Into.Count, 2UL);
46 EXPECT_EQ(Into.Batch[0], FakePtr);
47 EXPECT_EQ(Into.Batch[1], FakePtr);
48 EXPECT_EQ(Into.Size, 12UL + sizeof(scudo::QuarantineBatch));
49 EXPECT_EQ(Into.getQuarantinedSize(), 12UL);
51 EXPECT_EQ(From.Count, 0UL);
52 EXPECT_EQ(From.Size, sizeof(scudo::QuarantineBatch));
53 EXPECT_EQ(From.getQuarantinedSize(), 0UL);
55 // Merge the batch to the limit.
56 for (scudo::uptr I = 2; I < scudo::QuarantineBatch::MaxCount; ++I)
57 From.push_back(FakePtr, 8UL);
58 EXPECT_TRUE(Into.Count + From.Count == scudo::QuarantineBatch::MaxCount);
59 EXPECT_TRUE(Into.canMerge(&From));
61 Into.merge(&From);
62 EXPECT_TRUE(Into.Count == scudo::QuarantineBatch::MaxCount);
64 // No more space, not even for one element.
65 From.init(FakePtr, 8UL);
67 EXPECT_FALSE(Into.canMerge(&From));
70 TEST(ScudoQuarantineTest, QuarantineCacheMergeBatchesEmpty) {
71 CacheT Cache;
72 CacheT ToDeallocate;
73 Cache.init();
74 ToDeallocate.init();
75 Cache.mergeBatches(&ToDeallocate);
77 EXPECT_EQ(ToDeallocate.getSize(), 0UL);
78 EXPECT_EQ(ToDeallocate.dequeueBatch(), nullptr);
81 TEST(SanitizerCommon, QuarantineCacheMergeBatchesOneBatch) {
82 CacheT Cache;
83 Cache.init();
84 Cache.enqueue(Cb, FakePtr, BlockSize);
85 EXPECT_EQ(BlockSize + sizeof(scudo::QuarantineBatch), Cache.getSize());
87 CacheT ToDeallocate;
88 ToDeallocate.init();
89 Cache.mergeBatches(&ToDeallocate);
91 // Nothing to merge, nothing to deallocate.
92 EXPECT_EQ(BlockSize + sizeof(scudo::QuarantineBatch), Cache.getSize());
94 EXPECT_EQ(ToDeallocate.getSize(), 0UL);
95 EXPECT_EQ(ToDeallocate.dequeueBatch(), nullptr);
97 deallocateCache(&Cache);
100 TEST(ScudoQuarantineTest, QuarantineCacheMergeBatchesSmallBatches) {
101 // Make a Cache with two batches small enough to merge.
102 CacheT From;
103 From.init();
104 From.enqueue(Cb, FakePtr, BlockSize);
105 CacheT Cache;
106 Cache.init();
107 Cache.enqueue(Cb, FakePtr, BlockSize);
109 Cache.transfer(&From);
110 EXPECT_EQ(BlockSize * 2 + sizeof(scudo::QuarantineBatch) * 2,
111 Cache.getSize());
113 CacheT ToDeallocate;
114 ToDeallocate.init();
115 Cache.mergeBatches(&ToDeallocate);
117 // Batches merged, one batch to deallocate.
118 EXPECT_EQ(BlockSize * 2 + sizeof(scudo::QuarantineBatch), Cache.getSize());
119 EXPECT_EQ(ToDeallocate.getSize(), sizeof(scudo::QuarantineBatch));
121 deallocateCache(&Cache);
122 deallocateCache(&ToDeallocate);
125 TEST(ScudoQuarantineTest, QuarantineCacheMergeBatchesTooBigToMerge) {
126 const scudo::uptr NumBlocks = scudo::QuarantineBatch::MaxCount - 1;
128 // Make a Cache with two batches small enough to merge.
129 CacheT From;
130 CacheT Cache;
131 From.init();
132 Cache.init();
133 for (scudo::uptr I = 0; I < NumBlocks; ++I) {
134 From.enqueue(Cb, FakePtr, BlockSize);
135 Cache.enqueue(Cb, FakePtr, BlockSize);
137 Cache.transfer(&From);
138 EXPECT_EQ(BlockSize * NumBlocks * 2 + sizeof(scudo::QuarantineBatch) * 2,
139 Cache.getSize());
141 CacheT ToDeallocate;
142 ToDeallocate.init();
143 Cache.mergeBatches(&ToDeallocate);
145 // Batches cannot be merged.
146 EXPECT_EQ(BlockSize * NumBlocks * 2 + sizeof(scudo::QuarantineBatch) * 2,
147 Cache.getSize());
148 EXPECT_EQ(ToDeallocate.getSize(), 0UL);
150 deallocateCache(&Cache);
153 TEST(ScudoQuarantineTest, QuarantineCacheMergeBatchesALotOfBatches) {
154 const scudo::uptr NumBatchesAfterMerge = 3;
155 const scudo::uptr NumBlocks =
156 scudo::QuarantineBatch::MaxCount * NumBatchesAfterMerge;
157 const scudo::uptr NumBatchesBeforeMerge = NumBlocks;
159 // Make a Cache with many small batches.
160 CacheT Cache;
161 Cache.init();
162 for (scudo::uptr I = 0; I < NumBlocks; ++I) {
163 CacheT From;
164 From.init();
165 From.enqueue(Cb, FakePtr, BlockSize);
166 Cache.transfer(&From);
169 EXPECT_EQ(BlockSize * NumBlocks +
170 sizeof(scudo::QuarantineBatch) * NumBatchesBeforeMerge,
171 Cache.getSize());
173 CacheT ToDeallocate;
174 ToDeallocate.init();
175 Cache.mergeBatches(&ToDeallocate);
177 // All blocks should fit Into 3 batches.
178 EXPECT_EQ(BlockSize * NumBlocks +
179 sizeof(scudo::QuarantineBatch) * NumBatchesAfterMerge,
180 Cache.getSize());
182 EXPECT_EQ(ToDeallocate.getSize(),
183 sizeof(scudo::QuarantineBatch) *
184 (NumBatchesBeforeMerge - NumBatchesAfterMerge));
186 deallocateCache(&Cache);
187 deallocateCache(&ToDeallocate);
190 static const scudo::uptr MaxQuarantineSize = 1024UL << 10; // 1MB
191 static const scudo::uptr MaxCacheSize = 256UL << 10; // 256KB
193 TEST(ScudoQuarantineTest, GlobalQuarantine) {
194 QuarantineT Quarantine;
195 CacheT Cache;
196 Cache.init();
197 Quarantine.init(MaxQuarantineSize, MaxCacheSize);
198 EXPECT_EQ(Quarantine.getMaxSize(), MaxQuarantineSize);
199 EXPECT_EQ(Quarantine.getCacheSize(), MaxCacheSize);
201 bool DrainOccurred = false;
202 scudo::uptr CacheSize = Cache.getSize();
203 EXPECT_EQ(Cache.getSize(), 0UL);
204 // We quarantine enough blocks that a drain has to occur. Verify this by
205 // looking for a decrease of the size of the cache.
206 for (scudo::uptr I = 0; I < 128UL; I++) {
207 Quarantine.put(&Cache, Cb, FakePtr, LargeBlockSize);
208 if (!DrainOccurred && Cache.getSize() < CacheSize)
209 DrainOccurred = true;
210 CacheSize = Cache.getSize();
212 EXPECT_TRUE(DrainOccurred);
214 Quarantine.drainAndRecycle(&Cache, Cb);
215 EXPECT_EQ(Cache.getSize(), 0UL);
217 scudo::ScopedString Str;
218 Quarantine.getStats(&Str);
219 Str.output();
222 struct PopulateQuarantineThread {
223 pthread_t Thread;
224 QuarantineT *Quarantine;
225 CacheT Cache;
228 void *populateQuarantine(void *Param) {
229 PopulateQuarantineThread *P = static_cast<PopulateQuarantineThread *>(Param);
230 P->Cache.init();
231 for (scudo::uptr I = 0; I < 128UL; I++)
232 P->Quarantine->put(&P->Cache, Cb, FakePtr, LargeBlockSize);
233 return 0;
236 TEST(ScudoQuarantineTest, ThreadedGlobalQuarantine) {
237 QuarantineT Quarantine;
238 Quarantine.init(MaxQuarantineSize, MaxCacheSize);
240 const scudo::uptr NumberOfThreads = 32U;
241 PopulateQuarantineThread T[NumberOfThreads];
242 for (scudo::uptr I = 0; I < NumberOfThreads; I++) {
243 T[I].Quarantine = &Quarantine;
244 pthread_create(&T[I].Thread, 0, populateQuarantine, &T[I]);
246 for (scudo::uptr I = 0; I < NumberOfThreads; I++)
247 pthread_join(T[I].Thread, 0);
249 scudo::ScopedString Str;
250 Quarantine.getStats(&Str);
251 Str.output();
253 for (scudo::uptr I = 0; I < NumberOfThreads; I++)
254 Quarantine.drainAndRecycle(&T[I].Cache, Cb);