1 //===-- quarantine_test.cpp -------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "tests/scudo_unit_test.h"
11 #include "quarantine.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())
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);
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
));
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
) {
75 Cache
.mergeBatches(&ToDeallocate
);
77 EXPECT_EQ(ToDeallocate
.getSize(), 0UL);
78 EXPECT_EQ(ToDeallocate
.dequeueBatch(), nullptr);
81 TEST(SanitizerCommon
, QuarantineCacheMergeBatchesOneBatch
) {
84 Cache
.enqueue(Cb
, FakePtr
, BlockSize
);
85 EXPECT_EQ(BlockSize
+ sizeof(scudo::QuarantineBatch
), Cache
.getSize());
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.
104 From
.enqueue(Cb
, FakePtr
, BlockSize
);
107 Cache
.enqueue(Cb
, FakePtr
, BlockSize
);
109 Cache
.transfer(&From
);
110 EXPECT_EQ(BlockSize
* 2 + sizeof(scudo::QuarantineBatch
) * 2,
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.
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,
143 Cache
.mergeBatches(&ToDeallocate
);
145 // Batches cannot be merged.
146 EXPECT_EQ(BlockSize
* NumBlocks
* 2 + sizeof(scudo::QuarantineBatch
) * 2,
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.
162 for (scudo::uptr I
= 0; I
< NumBlocks
; ++I
) {
165 From
.enqueue(Cb
, FakePtr
, BlockSize
);
166 Cache
.transfer(&From
);
169 EXPECT_EQ(BlockSize
* NumBlocks
+
170 sizeof(scudo::QuarantineBatch
) * NumBatchesBeforeMerge
,
175 Cache
.mergeBatches(&ToDeallocate
);
177 // All blocks should fit Into 3 batches.
178 EXPECT_EQ(BlockSize
* NumBlocks
+
179 sizeof(scudo::QuarantineBatch
) * NumBatchesAfterMerge
,
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
;
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
);
222 struct PopulateQuarantineThread
{
224 QuarantineT
*Quarantine
;
228 void *populateQuarantine(void *Param
) {
229 PopulateQuarantineThread
*P
= static_cast<PopulateQuarantineThread
*>(Param
);
231 for (scudo::uptr I
= 0; I
< 128UL; I
++)
232 P
->Quarantine
->put(&P
->Cache
, Cb
, FakePtr
, LargeBlockSize
);
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
);
253 for (scudo::uptr I
= 0; I
< NumberOfThreads
; I
++)
254 Quarantine
.drainAndRecycle(&T
[I
].Cache
, Cb
);