1 //===-- sanitizer_allocator_test.cpp --------------------------------------===//
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 // This file is a part of ThreadSanitizer/AddressSanitizer runtime.
10 // Tests for sanitizer_allocator.h.
12 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_allocator.h"
23 #include "gtest/gtest.h"
24 #include "sanitizer_common/sanitizer_allocator_internal.h"
25 #include "sanitizer_common/sanitizer_common.h"
26 #include "sanitizer_pthread_wrappers.h"
27 #include "sanitizer_test_utils.h"
29 using namespace __sanitizer
;
31 #if defined(__sparcv9)
32 // FIXME: These tests probably fail because Solaris/sparcv9 uses the full
33 // 64-bit address space. Same on Linux/sparc64, so probably a general SPARC
34 // issue. Needs more investigation
35 # define SKIP_ON_SPARCV9(x) DISABLED_##x
37 # define SKIP_ON_SPARCV9(x) x
40 // On 64-bit systems with small virtual address spaces (e.g. 39-bit) we can't
41 // use size class maps with a large number of classes, as that will make the
42 // SizeClassAllocator64 region size too small (< 2^32).
43 #if SANITIZER_ANDROID && defined(__aarch64__)
44 #define ALLOCATOR64_SMALL_SIZE 1
45 #elif SANITIZER_RISCV64
46 #define ALLOCATOR64_SMALL_SIZE 1
48 #define ALLOCATOR64_SMALL_SIZE 0
51 // Too slow for debug build
54 #if SANITIZER_CAN_USE_ALLOCATOR64
56 // On Windows 64-bit there is no easy way to find a large enough fixed address
57 // space that is always available. Thus, a dynamically allocated address space
58 // is used instead (i.e. ~(uptr)0).
59 static const uptr kAllocatorSpace
= ~(uptr
)0;
60 static const uptr kAllocatorSize
= 0x8000000000ULL
; // 500G
61 static const u64 kAddressSpaceSize
= 1ULL << 47;
62 typedef DefaultSizeClassMap SizeClassMap
;
63 #elif SANITIZER_ANDROID && defined(__aarch64__)
64 static const uptr kAllocatorSpace
= 0x3000000000ULL
;
65 static const uptr kAllocatorSize
= 0x2000000000ULL
;
66 static const u64 kAddressSpaceSize
= 1ULL << 39;
67 typedef VeryCompactSizeClassMap SizeClassMap
;
68 #elif SANITIZER_RISCV64
69 const uptr kAllocatorSpace
= ~(uptr
)0;
70 const uptr kAllocatorSize
= 0x2000000000ULL
; // 128G.
71 static const u64 kAddressSpaceSize
= 1ULL << 38;
72 typedef VeryDenseSizeClassMap SizeClassMap
;
73 # elif SANITIZER_APPLE
74 static const uptr kAllocatorSpace
= 0x700000000000ULL
;
75 static const uptr kAllocatorSize
= 0x010000000000ULL
; // 1T.
76 static const u64 kAddressSpaceSize
= 1ULL << 47;
77 typedef DefaultSizeClassMap SizeClassMap
;
79 static const uptr kAllocatorSpace
= 0x500000000000ULL
;
80 static const uptr kAllocatorSize
= 0x010000000000ULL
; // 1T.
81 static const u64 kAddressSpaceSize
= 1ULL << 47;
82 typedef DefaultSizeClassMap SizeClassMap
;
85 template <typename AddressSpaceViewTy
>
86 struct AP64
{ // Allocator Params. Short name for shorter demangled names..
87 static const uptr kSpaceBeg
= kAllocatorSpace
;
88 static const uptr kSpaceSize
= kAllocatorSize
;
89 static const uptr kMetadataSize
= 16;
90 typedef ::SizeClassMap SizeClassMap
;
91 typedef NoOpMapUnmapCallback MapUnmapCallback
;
92 static const uptr kFlags
= 0;
93 using AddressSpaceView
= AddressSpaceViewTy
;
96 template <typename AddressSpaceViewTy
>
98 static const uptr kSpaceBeg
= ~(uptr
)0;
99 static const uptr kSpaceSize
= kAllocatorSize
;
100 static const uptr kMetadataSize
= 16;
101 typedef ::SizeClassMap SizeClassMap
;
102 typedef NoOpMapUnmapCallback MapUnmapCallback
;
103 static const uptr kFlags
= 0;
104 using AddressSpaceView
= AddressSpaceViewTy
;
107 template <typename AddressSpaceViewTy
>
109 static const uptr kSpaceBeg
= ~(uptr
)0;
110 static const uptr kSpaceSize
= kAllocatorSize
;
111 static const uptr kMetadataSize
= 16;
112 typedef CompactSizeClassMap SizeClassMap
;
113 typedef NoOpMapUnmapCallback MapUnmapCallback
;
114 static const uptr kFlags
= 0;
115 using AddressSpaceView
= AddressSpaceViewTy
;
118 template <typename AddressSpaceViewTy
>
119 struct AP64VeryCompact
{
120 static const uptr kSpaceBeg
= ~(uptr
)0;
121 static const uptr kSpaceSize
= 1ULL << 37;
122 static const uptr kMetadataSize
= 16;
123 typedef VeryCompactSizeClassMap SizeClassMap
;
124 typedef NoOpMapUnmapCallback MapUnmapCallback
;
125 static const uptr kFlags
= 0;
126 using AddressSpaceView
= AddressSpaceViewTy
;
129 template <typename AddressSpaceViewTy
>
131 static const uptr kSpaceBeg
= kAllocatorSpace
;
132 static const uptr kSpaceSize
= kAllocatorSize
;
133 static const uptr kMetadataSize
= 16;
134 typedef DenseSizeClassMap SizeClassMap
;
135 typedef NoOpMapUnmapCallback MapUnmapCallback
;
136 static const uptr kFlags
= 0;
137 using AddressSpaceView
= AddressSpaceViewTy
;
140 template <typename AddressSpaceView
>
141 using Allocator64ASVT
= SizeClassAllocator64
<AP64
<AddressSpaceView
>>;
142 using Allocator64
= Allocator64ASVT
<LocalAddressSpaceView
>;
144 template <typename AddressSpaceView
>
145 using Allocator64DynamicASVT
= SizeClassAllocator64
<AP64Dyn
<AddressSpaceView
>>;
146 using Allocator64Dynamic
= Allocator64DynamicASVT
<LocalAddressSpaceView
>;
148 template <typename AddressSpaceView
>
149 using Allocator64CompactASVT
=
150 SizeClassAllocator64
<AP64Compact
<AddressSpaceView
>>;
151 using Allocator64Compact
= Allocator64CompactASVT
<LocalAddressSpaceView
>;
153 template <typename AddressSpaceView
>
154 using Allocator64VeryCompactASVT
=
155 SizeClassAllocator64
<AP64VeryCompact
<AddressSpaceView
>>;
156 using Allocator64VeryCompact
=
157 Allocator64VeryCompactASVT
<LocalAddressSpaceView
>;
159 template <typename AddressSpaceView
>
160 using Allocator64DenseASVT
= SizeClassAllocator64
<AP64Dense
<AddressSpaceView
>>;
161 using Allocator64Dense
= Allocator64DenseASVT
<LocalAddressSpaceView
>;
163 #elif defined(__mips64)
164 static const u64 kAddressSpaceSize
= 1ULL << 40;
165 #elif defined(__aarch64__)
166 static const u64 kAddressSpaceSize
= 1ULL << 39;
167 #elif defined(__s390x__)
168 static const u64 kAddressSpaceSize
= 1ULL << 53;
169 #elif defined(__s390__)
170 static const u64 kAddressSpaceSize
= 1ULL << 31;
172 static const u64 kAddressSpaceSize
= 1ULL << 32;
175 static const uptr kRegionSizeLog
= FIRST_32_SECOND_64(20, 24);
177 template <typename AddressSpaceViewTy
>
179 static const uptr kSpaceBeg
= 0;
180 static const u64 kSpaceSize
= kAddressSpaceSize
;
181 static const uptr kMetadataSize
= 16;
182 typedef CompactSizeClassMap SizeClassMap
;
183 static const uptr kRegionSizeLog
= ::kRegionSizeLog
;
184 using AddressSpaceView
= AddressSpaceViewTy
;
185 typedef NoOpMapUnmapCallback MapUnmapCallback
;
186 static const uptr kFlags
= 0;
188 template <typename AddressSpaceView
>
189 using Allocator32CompactASVT
=
190 SizeClassAllocator32
<AP32Compact
<AddressSpaceView
>>;
191 using Allocator32Compact
= Allocator32CompactASVT
<LocalAddressSpaceView
>;
193 template <class SizeClassMap
>
194 void TestSizeClassMap() {
195 typedef SizeClassMap SCMap
;
200 TEST(SanitizerCommon
, DefaultSizeClassMap
) {
201 TestSizeClassMap
<DefaultSizeClassMap
>();
204 TEST(SanitizerCommon
, CompactSizeClassMap
) {
205 TestSizeClassMap
<CompactSizeClassMap
>();
208 TEST(SanitizerCommon
, VeryCompactSizeClassMap
) {
209 TestSizeClassMap
<VeryCompactSizeClassMap
>();
212 TEST(SanitizerCommon
, InternalSizeClassMap
) {
213 TestSizeClassMap
<InternalSizeClassMap
>();
216 TEST(SanitizerCommon
, DenseSizeClassMap
) {
217 TestSizeClassMap
<VeryCompactSizeClassMap
>();
220 template <class Allocator
>
221 void TestSizeClassAllocator(uptr premapped_heap
= 0) {
222 Allocator
*a
= new Allocator
;
223 a
->Init(kReleaseToOSIntervalNever
, premapped_heap
);
224 typename
Allocator::AllocatorCache cache
;
225 memset(&cache
, 0, sizeof(cache
));
228 static const uptr sizes
[] = {
229 1, 16, 30, 40, 100, 1000, 10000,
230 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000
233 std::vector
<void *> allocated
;
235 uptr last_total_allocated
= 0;
236 for (int i
= 0; i
< 3; i
++) {
237 // Allocate a bunch of chunks.
238 for (uptr s
= 0; s
< ARRAY_SIZE(sizes
); s
++) {
239 uptr size
= sizes
[s
];
240 if (!a
->CanAllocate(size
, 1)) continue;
241 // printf("s = %ld\n", size);
242 uptr n_iter
= std::max((uptr
)6, 4000000 / size
);
243 // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter);
244 for (uptr i
= 0; i
< n_iter
; i
++) {
245 uptr class_id0
= Allocator::SizeClassMapT::ClassID(size
);
246 char *x
= (char*)cache
.Allocate(a
, class_id0
);
250 allocated
.push_back(x
);
251 CHECK_EQ(x
, a
->GetBlockBegin(x
));
252 CHECK_EQ(x
, a
->GetBlockBegin(x
+ size
- 1));
253 CHECK(a
->PointerIsMine(x
));
254 CHECK(a
->PointerIsMine(x
+ size
- 1));
255 CHECK(a
->PointerIsMine(x
+ size
/ 2));
256 CHECK_GE(a
->GetActuallyAllocatedSize(x
), size
);
257 uptr class_id
= a
->GetSizeClass(x
);
258 CHECK_EQ(class_id
, Allocator::SizeClassMapT::ClassID(size
));
259 uptr
*metadata
= reinterpret_cast<uptr
*>(a
->GetMetaData(x
));
260 metadata
[0] = reinterpret_cast<uptr
>(x
) + 1;
261 metadata
[1] = 0xABCD;
265 for (uptr i
= 0; i
< allocated
.size(); i
++) {
266 void *x
= allocated
[i
];
267 uptr
*metadata
= reinterpret_cast<uptr
*>(a
->GetMetaData(x
));
268 CHECK_EQ(metadata
[0], reinterpret_cast<uptr
>(x
) + 1);
269 CHECK_EQ(metadata
[1], 0xABCD);
270 cache
.Deallocate(a
, a
->GetSizeClass(x
), x
);
273 uptr total_allocated
= a
->TotalMemoryUsed();
274 if (last_total_allocated
== 0)
275 last_total_allocated
= total_allocated
;
276 CHECK_EQ(last_total_allocated
, total_allocated
);
279 // Check that GetBlockBegin never crashes.
280 for (uptr x
= 0, step
= kAddressSpaceSize
/ 100000;
281 x
< kAddressSpaceSize
- step
; x
+= step
)
282 if (a
->PointerIsMine(reinterpret_cast<void *>(x
)))
283 Ident(a
->GetBlockBegin(reinterpret_cast<void *>(x
)));
289 #if SANITIZER_CAN_USE_ALLOCATOR64
291 // Allocates kAllocatorSize aligned bytes on construction and frees it on
293 class ScopedPremappedHeap
{
295 ScopedPremappedHeap() {
296 BasePtr
= MmapNoReserveOrDie(2 * kAllocatorSize
, "preallocated heap");
297 AlignedAddr
= RoundUpTo(reinterpret_cast<uptr
>(BasePtr
), kAllocatorSize
);
300 ~ScopedPremappedHeap() { UnmapOrDie(BasePtr
, kAllocatorSize
); }
302 uptr
Addr() { return AlignedAddr
; }
309 // These tests can fail on Windows if memory is somewhat full and lit happens
310 // to run them all at the same time. FIXME: Make them not flaky and reenable.
311 #if !SANITIZER_WINDOWS
312 TEST(SanitizerCommon
, SizeClassAllocator64
) {
313 TestSizeClassAllocator
<Allocator64
>();
316 TEST(SanitizerCommon
, SizeClassAllocator64Dynamic
) {
317 TestSizeClassAllocator
<Allocator64Dynamic
>();
320 #if !ALLOCATOR64_SMALL_SIZE
321 // Android only has 39-bit address space, so mapping 2 * kAllocatorSize
323 TEST(SanitizerCommon
, SizeClassAllocator64DynamicPremapped
) {
324 ScopedPremappedHeap h
;
325 TestSizeClassAllocator
<Allocator64Dynamic
>(h
.Addr());
328 TEST(SanitizerCommon
, SizeClassAllocator64Compact
) {
329 TestSizeClassAllocator
<Allocator64Compact
>();
332 TEST(SanitizerCommon
, SizeClassAllocator64Dense
) {
333 TestSizeClassAllocator
<Allocator64Dense
>();
337 TEST(SanitizerCommon
, SizeClassAllocator64VeryCompact
) {
338 TestSizeClassAllocator
<Allocator64VeryCompact
>();
343 TEST(SanitizerCommon
, SizeClassAllocator32Compact
) {
344 TestSizeClassAllocator
<Allocator32Compact
>();
347 template <typename AddressSpaceViewTy
>
348 struct AP32SeparateBatches
{
349 static const uptr kSpaceBeg
= 0;
350 static const u64 kSpaceSize
= kAddressSpaceSize
;
351 static const uptr kMetadataSize
= 16;
352 typedef DefaultSizeClassMap SizeClassMap
;
353 static const uptr kRegionSizeLog
= ::kRegionSizeLog
;
354 using AddressSpaceView
= AddressSpaceViewTy
;
355 typedef NoOpMapUnmapCallback MapUnmapCallback
;
356 static const uptr kFlags
=
357 SizeClassAllocator32FlagMasks::kUseSeparateSizeClassForBatch
;
359 template <typename AddressSpaceView
>
360 using Allocator32SeparateBatchesASVT
=
361 SizeClassAllocator32
<AP32SeparateBatches
<AddressSpaceView
>>;
362 using Allocator32SeparateBatches
=
363 Allocator32SeparateBatchesASVT
<LocalAddressSpaceView
>;
365 TEST(SanitizerCommon
, SizeClassAllocator32SeparateBatches
) {
366 TestSizeClassAllocator
<Allocator32SeparateBatches
>();
369 template <class Allocator
>
370 void SizeClassAllocatorMetadataStress(uptr premapped_heap
= 0) {
371 Allocator
*a
= new Allocator
;
372 a
->Init(kReleaseToOSIntervalNever
, premapped_heap
);
373 typename
Allocator::AllocatorCache cache
;
374 memset(&cache
, 0, sizeof(cache
));
377 const uptr kNumAllocs
= 1 << 13;
378 void *allocated
[kNumAllocs
];
379 void *meta
[kNumAllocs
];
380 for (uptr i
= 0; i
< kNumAllocs
; i
++) {
381 void *x
= cache
.Allocate(a
, 1 + i
% (Allocator::kNumClasses
- 1));
383 meta
[i
] = a
->GetMetaData(x
);
385 // Get Metadata kNumAllocs^2 times.
386 for (uptr i
= 0; i
< kNumAllocs
* kNumAllocs
; i
++) {
387 uptr idx
= i
% kNumAllocs
;
388 void *m
= a
->GetMetaData(allocated
[idx
]);
389 EXPECT_EQ(m
, meta
[idx
]);
391 for (uptr i
= 0; i
< kNumAllocs
; i
++) {
392 cache
.Deallocate(a
, 1 + i
% (Allocator::kNumClasses
- 1), allocated
[i
]);
399 #if SANITIZER_CAN_USE_ALLOCATOR64
400 // These tests can fail on Windows if memory is somewhat full and lit happens
401 // to run them all at the same time. FIXME: Make them not flaky and reenable.
402 #if !SANITIZER_WINDOWS
403 TEST(SanitizerCommon
, SizeClassAllocator64MetadataStress
) {
404 SizeClassAllocatorMetadataStress
<Allocator64
>();
407 TEST(SanitizerCommon
, SizeClassAllocator64DynamicMetadataStress
) {
408 SizeClassAllocatorMetadataStress
<Allocator64Dynamic
>();
411 #if !ALLOCATOR64_SMALL_SIZE
412 TEST(SanitizerCommon
, SizeClassAllocator64DynamicPremappedMetadataStress
) {
413 ScopedPremappedHeap h
;
414 SizeClassAllocatorMetadataStress
<Allocator64Dynamic
>(h
.Addr());
417 TEST(SanitizerCommon
, SizeClassAllocator64CompactMetadataStress
) {
418 SizeClassAllocatorMetadataStress
<Allocator64Compact
>();
423 #endif // SANITIZER_CAN_USE_ALLOCATOR64
424 TEST(SanitizerCommon
, SizeClassAllocator32CompactMetadataStress
) {
425 SizeClassAllocatorMetadataStress
<Allocator32Compact
>();
428 template <class Allocator
>
429 void SizeClassAllocatorGetBlockBeginStress(u64 TotalSize
,
430 uptr premapped_heap
= 0) {
431 Allocator
*a
= new Allocator
;
432 a
->Init(kReleaseToOSIntervalNever
, premapped_heap
);
433 typename
Allocator::AllocatorCache cache
;
434 memset(&cache
, 0, sizeof(cache
));
437 uptr max_size_class
= Allocator::SizeClassMapT::kLargestClassID
;
438 uptr size
= Allocator::SizeClassMapT::Size(max_size_class
);
439 // Make sure we correctly compute GetBlockBegin() w/o overflow.
440 for (size_t i
= 0; i
<= TotalSize
/ size
; i
++) {
441 void *x
= cache
.Allocate(a
, max_size_class
);
442 void *beg
= a
->GetBlockBegin(x
);
443 // if ((i & (i - 1)) == 0)
444 // fprintf(stderr, "[%zd] %p %p\n", i, x, beg);
452 #if SANITIZER_CAN_USE_ALLOCATOR64
453 // These tests can fail on Windows if memory is somewhat full and lit happens
454 // to run them all at the same time. FIXME: Make them not flaky and reenable.
455 #if !SANITIZER_WINDOWS
456 TEST(SanitizerCommon
, SizeClassAllocator64GetBlockBegin
) {
457 SizeClassAllocatorGetBlockBeginStress
<Allocator64
>(
458 1ULL << (SANITIZER_ANDROID
? 31 : 33));
460 TEST(SanitizerCommon
, SizeClassAllocator64DynamicGetBlockBegin
) {
461 SizeClassAllocatorGetBlockBeginStress
<Allocator64Dynamic
>(
462 1ULL << (SANITIZER_ANDROID
? 31 : 33));
464 #if !ALLOCATOR64_SMALL_SIZE
465 TEST(SanitizerCommon
, SizeClassAllocator64DynamicPremappedGetBlockBegin
) {
466 ScopedPremappedHeap h
;
467 SizeClassAllocatorGetBlockBeginStress
<Allocator64Dynamic
>(
468 1ULL << (SANITIZER_ANDROID
? 31 : 33), h
.Addr());
470 TEST(SanitizerCommon
, SizeClassAllocator64CompactGetBlockBegin
) {
471 SizeClassAllocatorGetBlockBeginStress
<Allocator64Compact
>(1ULL << 33);
474 TEST(SanitizerCommon
, SizeClassAllocator64VeryCompactGetBlockBegin
) {
475 // Does not have > 4Gb for each class.
476 SizeClassAllocatorGetBlockBeginStress
<Allocator64VeryCompact
>(1ULL << 31);
478 TEST(SanitizerCommon
, SizeClassAllocator32CompactGetBlockBegin
) {
479 SizeClassAllocatorGetBlockBeginStress
<Allocator32Compact
>(1ULL << 33);
482 #endif // SANITIZER_CAN_USE_ALLOCATOR64
484 struct TestMapUnmapCallback
{
485 static int map_count
, map_secondary_count
, unmap_count
;
486 void OnMap(uptr p
, uptr size
) const { map_count
++; }
487 void OnMapSecondary(uptr p
, uptr size
, uptr user_begin
,
488 uptr user_size
) const {
489 map_secondary_count
++;
491 void OnUnmap(uptr p
, uptr size
) const { unmap_count
++; }
493 static void Reset() { map_count
= map_secondary_count
= unmap_count
= 0; }
495 int TestMapUnmapCallback::map_count
;
496 int TestMapUnmapCallback::map_secondary_count
;
497 int TestMapUnmapCallback::unmap_count
;
499 #if SANITIZER_CAN_USE_ALLOCATOR64
500 // These tests can fail on Windows if memory is somewhat full and lit happens
501 // to run them all at the same time. FIXME: Make them not flaky and reenable.
502 #if !SANITIZER_WINDOWS
504 template <typename AddressSpaceViewTy
= LocalAddressSpaceView
>
505 struct AP64WithCallback
{
506 static const uptr kSpaceBeg
= kAllocatorSpace
;
507 static const uptr kSpaceSize
= kAllocatorSize
;
508 static const uptr kMetadataSize
= 16;
509 typedef ::SizeClassMap SizeClassMap
;
510 typedef TestMapUnmapCallback MapUnmapCallback
;
511 static const uptr kFlags
= 0;
512 using AddressSpaceView
= AddressSpaceViewTy
;
515 TEST(SanitizerCommon
, SizeClassAllocator64MapUnmapCallback
) {
516 TestMapUnmapCallback::Reset();
517 typedef SizeClassAllocator64
<AP64WithCallback
<>> Allocator64WithCallBack
;
518 Allocator64WithCallBack
*a
= new Allocator64WithCallBack
;
519 a
->Init(kReleaseToOSIntervalNever
);
520 EXPECT_EQ(TestMapUnmapCallback::map_count
, 1); // Allocator state.
521 EXPECT_EQ(TestMapUnmapCallback::map_secondary_count
, 0);
522 typename
Allocator64WithCallBack::AllocatorCache cache
;
523 memset(&cache
, 0, sizeof(cache
));
525 AllocatorStats stats
;
527 const size_t kNumChunks
= 128;
528 uint32_t chunks
[kNumChunks
];
529 a
->GetFromAllocator(&stats
, 30, chunks
, kNumChunks
);
530 // State + alloc + metadata + freearray.
531 EXPECT_EQ(TestMapUnmapCallback::map_count
, 4);
532 EXPECT_EQ(TestMapUnmapCallback::map_secondary_count
, 0);
534 EXPECT_EQ(TestMapUnmapCallback::unmap_count
, 1); // The whole thing.
540 template <typename AddressSpaceViewTy
= LocalAddressSpaceView
>
541 struct AP32WithCallback
{
542 static const uptr kSpaceBeg
= 0;
543 static const u64 kSpaceSize
= kAddressSpaceSize
;
544 static const uptr kMetadataSize
= 16;
545 typedef CompactSizeClassMap SizeClassMap
;
546 static const uptr kRegionSizeLog
= ::kRegionSizeLog
;
547 using AddressSpaceView
= AddressSpaceViewTy
;
548 typedef TestMapUnmapCallback MapUnmapCallback
;
549 static const uptr kFlags
= 0;
552 TEST(SanitizerCommon
, SizeClassAllocator32MapUnmapCallback
) {
553 TestMapUnmapCallback::Reset();
554 typedef SizeClassAllocator32
<AP32WithCallback
<>> Allocator32WithCallBack
;
555 Allocator32WithCallBack
*a
= new Allocator32WithCallBack
;
556 a
->Init(kReleaseToOSIntervalNever
);
557 EXPECT_EQ(TestMapUnmapCallback::map_count
, 0);
558 EXPECT_EQ(TestMapUnmapCallback::map_secondary_count
, 0);
559 Allocator32WithCallBack::AllocatorCache cache
;
560 memset(&cache
, 0, sizeof(cache
));
562 AllocatorStats stats
;
564 a
->AllocateBatch(&stats
, &cache
, 32);
565 EXPECT_EQ(TestMapUnmapCallback::map_count
, 1);
566 EXPECT_EQ(TestMapUnmapCallback::map_secondary_count
, 0);
568 EXPECT_EQ(TestMapUnmapCallback::unmap_count
, 1);
572 TEST(SanitizerCommon
, LargeMmapAllocatorMapUnmapCallback
) {
573 TestMapUnmapCallback::Reset();
574 LargeMmapAllocator
<TestMapUnmapCallback
> a
;
576 AllocatorStats stats
;
578 void *x
= a
.Allocate(&stats
, 1 << 20, 1);
579 EXPECT_EQ(TestMapUnmapCallback::map_count
, 0);
580 EXPECT_EQ(TestMapUnmapCallback::map_secondary_count
, 1);
581 a
.Deallocate(&stats
, x
);
582 EXPECT_EQ(TestMapUnmapCallback::unmap_count
, 1);
585 // Don't test OOM conditions on Win64 because it causes other tests on the same
587 #if SANITIZER_CAN_USE_ALLOCATOR64 && !SANITIZER_WINDOWS64
588 TEST(SanitizerCommon
, SizeClassAllocator64Overflow
) {
590 a
.Init(kReleaseToOSIntervalNever
);
591 Allocator64::AllocatorCache cache
;
592 memset(&cache
, 0, sizeof(cache
));
594 AllocatorStats stats
;
597 const size_t kNumChunks
= 128;
598 uint32_t chunks
[kNumChunks
];
599 bool allocation_failed
= false;
600 for (int i
= 0; i
< 1000000; i
++) {
601 uptr class_id
= a
.kNumClasses
- 1;
602 if (!a
.GetFromAllocator(&stats
, class_id
, chunks
, kNumChunks
)) {
603 allocation_failed
= true;
607 EXPECT_EQ(allocation_failed
, true);
613 TEST(SanitizerCommon
, LargeMmapAllocator
) {
614 LargeMmapAllocator
<NoOpMapUnmapCallback
> a
;
616 AllocatorStats stats
;
619 static const int kNumAllocs
= 1000;
620 char *allocated
[kNumAllocs
];
621 static const uptr size
= 4000;
623 for (int i
= 0; i
< kNumAllocs
; i
++) {
624 allocated
[i
] = (char *)a
.Allocate(&stats
, size
, 1);
625 CHECK(a
.PointerIsMine(allocated
[i
]));
628 CHECK_GT(a
.TotalMemoryUsed(), size
* kNumAllocs
);
629 for (int i
= 0; i
< kNumAllocs
; i
++) {
630 char *p
= allocated
[i
];
631 CHECK(a
.PointerIsMine(p
));
632 a
.Deallocate(&stats
, p
);
634 // Check that non left.
635 CHECK_EQ(a
.TotalMemoryUsed(), 0);
637 // Allocate some more, also add metadata.
638 for (int i
= 0; i
< kNumAllocs
; i
++) {
639 char *x
= (char *)a
.Allocate(&stats
, size
, 1);
640 CHECK_GE(a
.GetActuallyAllocatedSize(x
), size
);
641 uptr
*meta
= reinterpret_cast<uptr
*>(a
.GetMetaData(x
));
645 for (int i
= 0; i
< kNumAllocs
* kNumAllocs
; i
++) {
646 char *p
= allocated
[i
% kNumAllocs
];
647 CHECK(a
.PointerIsMine(p
));
648 CHECK(a
.PointerIsMine(p
+ 2000));
650 CHECK_GT(a
.TotalMemoryUsed(), size
* kNumAllocs
);
651 // Deallocate all in reverse order.
652 for (int i
= 0; i
< kNumAllocs
; i
++) {
653 int idx
= kNumAllocs
- i
- 1;
654 char *p
= allocated
[idx
];
655 uptr
*meta
= reinterpret_cast<uptr
*>(a
.GetMetaData(p
));
656 CHECK_EQ(*meta
, idx
);
657 CHECK(a
.PointerIsMine(p
));
658 a
.Deallocate(&stats
, p
);
660 CHECK_EQ(a
.TotalMemoryUsed(), 0);
662 // Test alignments. Test with 512MB alignment on x64 non-Windows machines.
663 // Windows doesn't overcommit, and many machines do not have 51.2GB of swap.
665 (SANITIZER_WORDSIZE
== 64 && !SANITIZER_WINDOWS
) ? (1 << 28) : (1 << 24);
666 for (uptr alignment
= 8; alignment
<= max_alignment
; alignment
*= 2) {
667 const uptr kNumAlignedAllocs
= 100;
668 for (uptr i
= 0; i
< kNumAlignedAllocs
; i
++) {
669 uptr size
= ((i
% 10) + 1) * 4096;
670 char *p
= allocated
[i
] = (char *)a
.Allocate(&stats
, size
, alignment
);
671 CHECK_EQ(p
, a
.GetBlockBegin(p
));
672 CHECK_EQ(p
, a
.GetBlockBegin(p
+ size
- 1));
673 CHECK_EQ(p
, a
.GetBlockBegin(p
+ size
/ 2));
674 CHECK_EQ(0, (uptr
)allocated
[i
] % alignment
);
675 p
[0] = p
[size
- 1] = 0;
677 for (uptr i
= 0; i
< kNumAlignedAllocs
; i
++) {
678 a
.Deallocate(&stats
, allocated
[i
]);
682 // Regression test for boundary condition in GetBlockBegin().
683 uptr page_size
= GetPageSizeCached();
684 char *p
= (char *)a
.Allocate(&stats
, page_size
, 1);
685 CHECK_EQ(p
, a
.GetBlockBegin(p
));
686 CHECK_EQ(p
, (char *)a
.GetBlockBegin(p
+ page_size
- 1));
687 CHECK_NE(p
, (char *)a
.GetBlockBegin(p
+ page_size
));
688 a
.Deallocate(&stats
, p
);
691 template <class PrimaryAllocator
>
692 void TestCombinedAllocator(uptr premapped_heap
= 0) {
693 typedef CombinedAllocator
<PrimaryAllocator
> Allocator
;
694 Allocator
*a
= new Allocator
;
695 a
->Init(kReleaseToOSIntervalNever
, premapped_heap
);
698 typename
Allocator::AllocatorCache cache
;
699 memset(&cache
, 0, sizeof(cache
));
700 a
->InitCache(&cache
);
702 EXPECT_EQ(a
->Allocate(&cache
, -1, 1), (void*)0);
703 EXPECT_EQ(a
->Allocate(&cache
, -1, 1024), (void*)0);
704 EXPECT_EQ(a
->Allocate(&cache
, (uptr
)-1 - 1024, 1), (void*)0);
705 EXPECT_EQ(a
->Allocate(&cache
, (uptr
)-1 - 1024, 1024), (void*)0);
706 EXPECT_EQ(a
->Allocate(&cache
, (uptr
)-1 - 1023, 1024), (void*)0);
707 EXPECT_EQ(a
->Allocate(&cache
, -1, 1), (void*)0);
709 const uptr kNumAllocs
= 100000;
710 const uptr kNumIter
= 10;
711 for (uptr iter
= 0; iter
< kNumIter
; iter
++) {
712 std::vector
<void*> allocated
;
713 for (uptr i
= 0; i
< kNumAllocs
; i
++) {
714 uptr size
= (i
% (1 << 14)) + 1;
716 size
= 1 << (10 + (i
% 14));
717 void *x
= a
->Allocate(&cache
, size
, 1);
718 uptr
*meta
= reinterpret_cast<uptr
*>(a
->GetMetaData(x
));
721 allocated
.push_back(x
);
724 std::shuffle(allocated
.begin(), allocated
.end(), r
);
726 // Test ForEachChunk(...)
728 std::set
<void *> reported_chunks
;
729 auto cb
= [](uptr chunk
, void *arg
) {
730 auto reported_chunks_ptr
= reinterpret_cast<std::set
<void *> *>(arg
);
732 reported_chunks_ptr
->insert(reinterpret_cast<void *>(chunk
));
733 // Check chunk is never reported more than once.
734 ASSERT_TRUE(pair
.second
);
736 a
->ForEachChunk(cb
, reinterpret_cast<void *>(&reported_chunks
));
737 for (const auto &allocated_ptr
: allocated
) {
738 ASSERT_NE(reported_chunks
.find(allocated_ptr
), reported_chunks
.end());
742 for (uptr i
= 0; i
< kNumAllocs
; i
++) {
743 void *x
= allocated
[i
];
744 uptr
*meta
= reinterpret_cast<uptr
*>(a
->GetMetaData(x
));
746 CHECK(a
->PointerIsMine(x
));
748 a
->Deallocate(&cache
, x
);
751 a
->SwallowCache(&cache
);
753 a
->DestroyCache(&cache
);
757 #if SANITIZER_CAN_USE_ALLOCATOR64
758 TEST(SanitizerCommon
, CombinedAllocator64
) {
759 TestCombinedAllocator
<Allocator64
>();
762 TEST(SanitizerCommon
, CombinedAllocator64Dynamic
) {
763 TestCombinedAllocator
<Allocator64Dynamic
>();
766 #if !ALLOCATOR64_SMALL_SIZE
767 #if !SANITIZER_WINDOWS
768 // Windows fails to map 1TB, so disable this test.
769 TEST(SanitizerCommon
, CombinedAllocator64DynamicPremapped
) {
770 ScopedPremappedHeap h
;
771 TestCombinedAllocator
<Allocator64Dynamic
>(h
.Addr());
775 TEST(SanitizerCommon
, CombinedAllocator64Compact
) {
776 TestCombinedAllocator
<Allocator64Compact
>();
780 TEST(SanitizerCommon
, CombinedAllocator64VeryCompact
) {
781 TestCombinedAllocator
<Allocator64VeryCompact
>();
785 TEST(SanitizerCommon
, SKIP_ON_SPARCV9(CombinedAllocator32Compact
)) {
786 TestCombinedAllocator
<Allocator32Compact
>();
789 template <class Allocator
>
790 void TestSizeClassAllocatorLocalCache(uptr premapped_heap
= 0) {
791 using AllocatorCache
= typename
Allocator::AllocatorCache
;
792 AllocatorCache cache
;
793 Allocator
*a
= new Allocator();
795 a
->Init(kReleaseToOSIntervalNever
, premapped_heap
);
796 memset(&cache
, 0, sizeof(cache
));
799 const uptr kNumAllocs
= 10000;
800 const int kNumIter
= 100;
801 uptr saved_total
= 0;
802 for (int class_id
= 1; class_id
<= 5; class_id
++) {
803 for (int it
= 0; it
< kNumIter
; it
++) {
804 void *allocated
[kNumAllocs
];
805 for (uptr i
= 0; i
< kNumAllocs
; i
++) {
806 allocated
[i
] = cache
.Allocate(a
, class_id
);
808 for (uptr i
= 0; i
< kNumAllocs
; i
++) {
809 cache
.Deallocate(a
, class_id
, allocated
[i
]);
812 uptr total_allocated
= a
->TotalMemoryUsed();
814 CHECK_EQ(saved_total
, total_allocated
);
815 saved_total
= total_allocated
;
823 #if SANITIZER_CAN_USE_ALLOCATOR64
824 // These tests can fail on Windows if memory is somewhat full and lit happens
825 // to run them all at the same time. FIXME: Make them not flaky and reenable.
826 #if !SANITIZER_WINDOWS
827 TEST(SanitizerCommon
, SizeClassAllocator64LocalCache
) {
828 TestSizeClassAllocatorLocalCache
<Allocator64
>();
831 TEST(SanitizerCommon
, SizeClassAllocator64DynamicLocalCache
) {
832 TestSizeClassAllocatorLocalCache
<Allocator64Dynamic
>();
835 #if !ALLOCATOR64_SMALL_SIZE
836 TEST(SanitizerCommon
, SizeClassAllocator64DynamicPremappedLocalCache
) {
837 ScopedPremappedHeap h
;
838 TestSizeClassAllocatorLocalCache
<Allocator64Dynamic
>(h
.Addr());
841 TEST(SanitizerCommon
, SizeClassAllocator64CompactLocalCache
) {
842 TestSizeClassAllocatorLocalCache
<Allocator64Compact
>();
845 TEST(SanitizerCommon
, SizeClassAllocator64VeryCompactLocalCache
) {
846 TestSizeClassAllocatorLocalCache
<Allocator64VeryCompact
>();
851 TEST(SanitizerCommon
, SizeClassAllocator32CompactLocalCache
) {
852 TestSizeClassAllocatorLocalCache
<Allocator32Compact
>();
855 #if SANITIZER_CAN_USE_ALLOCATOR64
856 typedef Allocator64::AllocatorCache AllocatorCache
;
857 static AllocatorCache static_allocator_cache
;
859 void *AllocatorLeakTestWorker(void *arg
) {
860 typedef AllocatorCache::Allocator Allocator
;
861 Allocator
*a
= (Allocator
*)(arg
);
862 static_allocator_cache
.Allocate(a
, 10);
863 static_allocator_cache
.Drain(a
);
867 TEST(SanitizerCommon
, AllocatorLeakTest
) {
868 typedef AllocatorCache::Allocator Allocator
;
870 a
.Init(kReleaseToOSIntervalNever
);
871 uptr total_used_memory
= 0;
872 for (int i
= 0; i
< 100; i
++) {
874 PTHREAD_CREATE(&t
, 0, AllocatorLeakTestWorker
, &a
);
877 total_used_memory
= a
.TotalMemoryUsed();
878 EXPECT_EQ(a
.TotalMemoryUsed(), total_used_memory
);
884 // Struct which is allocated to pass info to new threads. The new thread frees
886 struct NewThreadParams
{
887 AllocatorCache
*thread_cache
;
888 AllocatorCache::Allocator
*allocator
;
892 // Called in a new thread. Just frees its argument.
893 static void *DeallocNewThreadWorker(void *arg
) {
894 NewThreadParams
*params
= reinterpret_cast<NewThreadParams
*>(arg
);
895 params
->thread_cache
->Deallocate(params
->allocator
, params
->class_id
, params
);
899 // The allocator cache is supposed to be POD and zero initialized. We should be
900 // able to call Deallocate on a zeroed cache, and it will self-initialize.
901 TEST(Allocator
, AllocatorCacheDeallocNewThread
) {
902 AllocatorCache::Allocator allocator
;
903 allocator
.Init(kReleaseToOSIntervalNever
);
904 AllocatorCache main_cache
;
905 AllocatorCache child_cache
;
906 memset(&main_cache
, 0, sizeof(main_cache
));
907 memset(&child_cache
, 0, sizeof(child_cache
));
909 uptr class_id
= DefaultSizeClassMap::ClassID(sizeof(NewThreadParams
));
910 NewThreadParams
*params
= reinterpret_cast<NewThreadParams
*>(
911 main_cache
.Allocate(&allocator
, class_id
));
912 params
->thread_cache
= &child_cache
;
913 params
->allocator
= &allocator
;
914 params
->class_id
= class_id
;
916 PTHREAD_CREATE(&t
, 0, DeallocNewThreadWorker
, params
);
919 allocator
.TestOnlyUnmap();
923 TEST(Allocator
, Basic
) {
924 char *p
= (char*)InternalAlloc(10);
925 EXPECT_NE(p
, (char*)0);
926 char *p2
= (char*)InternalAlloc(20);
927 EXPECT_NE(p2
, (char*)0);
933 TEST(Allocator
, Stress
) {
934 const int kCount
= 1000;
937 for (int i
= 0; i
< kCount
; i
++) {
938 uptr sz
= my_rand_r(&rnd
) % 1000;
939 char *p
= (char*)InternalAlloc(sz
);
940 EXPECT_NE(p
, (char*)0);
943 for (int i
= 0; i
< kCount
; i
++) {
944 InternalFree(ptrs
[i
]);
948 TEST(Allocator
, LargeAlloc
) {
949 void *p
= InternalAlloc(10 << 20);
953 TEST(Allocator
, ScopedBuffer
) {
954 const int kSize
= 512;
956 InternalMmapVector
<int> int_buf(kSize
);
957 EXPECT_EQ((uptr
)kSize
, int_buf
.size());
959 InternalMmapVector
<char> char_buf(kSize
);
960 EXPECT_EQ((uptr
)kSize
, char_buf
.size());
961 internal_memset(char_buf
.data(), 'c', kSize
);
962 for (int i
= 0; i
< kSize
; i
++) {
963 EXPECT_EQ('c', char_buf
[i
]);
967 void IterationTestCallback(uptr chunk
, void *arg
) {
968 reinterpret_cast<std::set
<uptr
> *>(arg
)->insert(chunk
);
971 template <class Allocator
>
972 void TestSizeClassAllocatorIteration(uptr premapped_heap
= 0) {
973 Allocator
*a
= new Allocator
;
974 a
->Init(kReleaseToOSIntervalNever
, premapped_heap
);
975 typename
Allocator::AllocatorCache cache
;
976 memset(&cache
, 0, sizeof(cache
));
979 static const uptr sizes
[] = {1, 16, 30, 40, 100, 1000, 10000,
980 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000};
982 std::vector
<void *> allocated
;
984 // Allocate a bunch of chunks.
985 for (uptr s
= 0; s
< ARRAY_SIZE(sizes
); s
++) {
986 uptr size
= sizes
[s
];
987 if (!a
->CanAllocate(size
, 1)) continue;
988 // printf("s = %ld\n", size);
989 uptr n_iter
= std::max((uptr
)6, 80000 / size
);
990 // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter);
991 for (uptr j
= 0; j
< n_iter
; j
++) {
992 uptr class_id0
= Allocator::SizeClassMapT::ClassID(size
);
993 void *x
= cache
.Allocate(a
, class_id0
);
994 allocated
.push_back(x
);
998 std::set
<uptr
> reported_chunks
;
1000 a
->ForEachChunk(IterationTestCallback
, &reported_chunks
);
1003 for (uptr i
= 0; i
< allocated
.size(); i
++) {
1004 // Don't use EXPECT_NE. Reporting the first mismatch is enough.
1005 ASSERT_NE(reported_chunks
.find(reinterpret_cast<uptr
>(allocated
[i
])),
1006 reported_chunks
.end());
1013 #if SANITIZER_CAN_USE_ALLOCATOR64
1014 // These tests can fail on Windows if memory is somewhat full and lit happens
1015 // to run them all at the same time. FIXME: Make them not flaky and reenable.
1016 #if !SANITIZER_WINDOWS
1017 TEST(SanitizerCommon
, SizeClassAllocator64Iteration
) {
1018 TestSizeClassAllocatorIteration
<Allocator64
>();
1020 TEST(SanitizerCommon
, SizeClassAllocator64DynamicIteration
) {
1021 TestSizeClassAllocatorIteration
<Allocator64Dynamic
>();
1023 #if !ALLOCATOR64_SMALL_SIZE
1024 TEST(SanitizerCommon
, SizeClassAllocator64DynamicPremappedIteration
) {
1025 ScopedPremappedHeap h
;
1026 TestSizeClassAllocatorIteration
<Allocator64Dynamic
>(h
.Addr());
1032 TEST(SanitizerCommon
, SKIP_ON_SPARCV9(SizeClassAllocator32Iteration
)) {
1033 TestSizeClassAllocatorIteration
<Allocator32Compact
>();
1036 TEST(SanitizerCommon
, LargeMmapAllocatorIteration
) {
1037 LargeMmapAllocator
<NoOpMapUnmapCallback
> a
;
1039 AllocatorStats stats
;
1042 static const uptr kNumAllocs
= 1000;
1043 char *allocated
[kNumAllocs
];
1044 static const uptr size
= 40;
1046 for (uptr i
= 0; i
< kNumAllocs
; i
++)
1047 allocated
[i
] = (char *)a
.Allocate(&stats
, size
, 1);
1049 std::set
<uptr
> reported_chunks
;
1051 a
.ForEachChunk(IterationTestCallback
, &reported_chunks
);
1054 for (uptr i
= 0; i
< kNumAllocs
; i
++) {
1055 // Don't use EXPECT_NE. Reporting the first mismatch is enough.
1056 ASSERT_NE(reported_chunks
.find(reinterpret_cast<uptr
>(allocated
[i
])),
1057 reported_chunks
.end());
1059 for (uptr i
= 0; i
< kNumAllocs
; i
++)
1060 a
.Deallocate(&stats
, allocated
[i
]);
1063 TEST(SanitizerCommon
, LargeMmapAllocatorBlockBegin
) {
1064 LargeMmapAllocator
<NoOpMapUnmapCallback
> a
;
1066 AllocatorStats stats
;
1069 static const uptr kNumAllocs
= 1024;
1070 static const uptr kNumExpectedFalseLookups
= 10000000;
1071 char *allocated
[kNumAllocs
];
1072 static const uptr size
= 4096;
1074 for (uptr i
= 0; i
< kNumAllocs
; i
++) {
1075 allocated
[i
] = (char *)a
.Allocate(&stats
, size
, 1);
1079 for (uptr i
= 0; i
< kNumAllocs
* kNumAllocs
; i
++) {
1080 // if ((i & (i - 1)) == 0) fprintf(stderr, "[%zd]\n", i);
1081 char *p1
= allocated
[i
% kNumAllocs
];
1082 EXPECT_EQ(p1
, a
.GetBlockBeginFastLocked(p1
));
1083 EXPECT_EQ(p1
, a
.GetBlockBeginFastLocked(p1
+ size
/ 2));
1084 EXPECT_EQ(p1
, a
.GetBlockBeginFastLocked(p1
+ size
- 1));
1085 EXPECT_EQ(p1
, a
.GetBlockBeginFastLocked(p1
- 100));
1088 for (uptr i
= 0; i
< kNumExpectedFalseLookups
; i
++) {
1089 void *p
= reinterpret_cast<void *>(i
% 1024);
1090 EXPECT_EQ((void *)0, a
.GetBlockBeginFastLocked(p
));
1091 p
= reinterpret_cast<void *>(~0L - (i
% 1024));
1092 EXPECT_EQ((void *)0, a
.GetBlockBeginFastLocked(p
));
1096 for (uptr i
= 0; i
< kNumAllocs
; i
++)
1097 a
.Deallocate(&stats
, allocated
[i
]);
1101 // Don't test OOM conditions on Win64 because it causes other tests on the same
1103 #if SANITIZER_CAN_USE_ALLOCATOR64 && !SANITIZER_WINDOWS64 && !ALLOCATOR64_SMALL_SIZE
1104 typedef __sanitizer::SizeClassMap
<2, 22, 22, 34, 128, 16> SpecialSizeClassMap
;
1105 template <typename AddressSpaceViewTy
= LocalAddressSpaceView
>
1106 struct AP64_SpecialSizeClassMap
{
1107 static const uptr kSpaceBeg
= kAllocatorSpace
;
1108 static const uptr kSpaceSize
= kAllocatorSize
;
1109 static const uptr kMetadataSize
= 0;
1110 typedef SpecialSizeClassMap SizeClassMap
;
1111 typedef NoOpMapUnmapCallback MapUnmapCallback
;
1112 static const uptr kFlags
= 0;
1113 using AddressSpaceView
= AddressSpaceViewTy
;
1116 // Regression test for out-of-memory condition in PopulateFreeList().
1117 TEST(SanitizerCommon
, SizeClassAllocator64PopulateFreeListOOM
) {
1118 // In a world where regions are small and chunks are huge...
1119 typedef SizeClassAllocator64
<AP64_SpecialSizeClassMap
<>> SpecialAllocator64
;
1120 const uptr kRegionSize
=
1121 kAllocatorSize
/ SpecialSizeClassMap::kNumClassesRounded
;
1122 SpecialAllocator64
*a
= new SpecialAllocator64
;
1123 a
->Init(kReleaseToOSIntervalNever
);
1124 SpecialAllocator64::AllocatorCache cache
;
1125 memset(&cache
, 0, sizeof(cache
));
1128 // ...one man is on a mission to overflow a region with a series of
1129 // successive allocations.
1131 const uptr kClassID
= 24;
1132 const uptr kAllocationSize
= SpecialSizeClassMap::Size(kClassID
);
1133 ASSERT_LT(2 * kAllocationSize
, kRegionSize
);
1134 ASSERT_GT(3 * kAllocationSize
, kRegionSize
);
1135 EXPECT_NE(cache
.Allocate(a
, kClassID
), nullptr);
1136 EXPECT_NE(cache
.Allocate(a
, kClassID
), nullptr);
1137 EXPECT_EQ(cache
.Allocate(a
, kClassID
), nullptr);
1139 const uptr Class2
= 21;
1140 const uptr Size2
= SpecialSizeClassMap::Size(Class2
);
1141 ASSERT_EQ(Size2
* 8, kRegionSize
);
1143 for (int i
= 0; i
< 7; i
++) {
1144 p
[i
] = (char*)cache
.Allocate(a
, Class2
);
1145 EXPECT_NE(p
[i
], nullptr);
1146 fprintf(stderr
, "p[%d] %p s = %lx\n", i
, (void*)p
[i
], Size2
);
1147 p
[i
][Size2
- 1] = 42;
1148 if (i
) ASSERT_LT(p
[i
- 1], p
[i
]);
1150 EXPECT_EQ(cache
.Allocate(a
, Class2
), nullptr);
1151 cache
.Deallocate(a
, Class2
, p
[0]);
1153 ASSERT_EQ(p
[6][Size2
- 1], 42);
1160 #if SANITIZER_CAN_USE_ALLOCATOR64
1162 class NoMemoryMapper
{
1164 uptr last_request_buffer_size
= 0;
1166 u64
*MapPackedCounterArrayBuffer(uptr buffer_size
) {
1167 last_request_buffer_size
= buffer_size
* sizeof(u64
);
1172 class RedZoneMemoryMapper
{
1174 RedZoneMemoryMapper() {
1175 const auto page_size
= GetPageSize();
1176 buffer
= MmapOrDie(3ULL * page_size
, "");
1177 MprotectNoAccess(reinterpret_cast<uptr
>(buffer
), page_size
);
1178 MprotectNoAccess(reinterpret_cast<uptr
>(buffer
) + page_size
* 2, page_size
);
1180 ~RedZoneMemoryMapper() { UnmapOrDie(buffer
, 3 * GetPageSize()); }
1182 u64
*MapPackedCounterArrayBuffer(uptr buffer_size
) {
1183 buffer_size
*= sizeof(u64
);
1184 const auto page_size
= GetPageSize();
1185 CHECK_EQ(buffer_size
, page_size
);
1187 reinterpret_cast<u64
*>(reinterpret_cast<uptr
>(buffer
) + page_size
);
1188 memset(p
, 0, page_size
);
1196 TEST(SanitizerCommon
, SizeClassAllocator64PackedCounterArray
) {
1197 NoMemoryMapper no_memory_mapper
;
1198 for (int i
= 0; i
< 64; i
++) {
1199 // Various valid counter's max values packed into one word.
1200 Allocator64::PackedCounterArray
counters_2n(1, 1ULL << i
,
1202 EXPECT_EQ(8ULL, no_memory_mapper
.last_request_buffer_size
);
1204 // Check the "all bit set" values too.
1205 Allocator64::PackedCounterArray
counters_2n1_1(1, ~0ULL >> i
,
1207 EXPECT_EQ(8ULL, no_memory_mapper
.last_request_buffer_size
);
1209 // Verify the packing ratio, the counter is expected to be packed into the
1210 // closest power of 2 bits.
1211 Allocator64::PackedCounterArray
counters(64, 1ULL << i
, &no_memory_mapper
);
1212 EXPECT_EQ(8ULL * RoundUpToPowerOfTwo(i
+ 1),
1213 no_memory_mapper
.last_request_buffer_size
);
1216 RedZoneMemoryMapper memory_mapper
;
1217 // Go through 1, 2, 4, 8, .. 64 bits per counter.
1218 for (int i
= 0; i
< 7; i
++) {
1219 // Make sure counters request one memory page for the buffer.
1220 const u64 kNumCounters
= (GetPageSize() / 8) * (64 >> i
);
1221 Allocator64::PackedCounterArray
counters(
1222 kNumCounters
, 1ULL << ((1 << i
) - 1), &memory_mapper
);
1224 for (u64 c
= 1; c
< kNumCounters
- 1; c
++) {
1225 ASSERT_EQ(0ULL, counters
.Get(c
));
1227 ASSERT_EQ(1ULL, counters
.Get(c
- 1));
1229 ASSERT_EQ(0ULL, counters
.Get(kNumCounters
- 1));
1230 counters
.Inc(kNumCounters
- 1);
1233 counters
.IncRange(0, kNumCounters
- 1);
1234 for (u64 c
= 0; c
< kNumCounters
; c
++)
1235 ASSERT_EQ(2ULL, counters
.Get(c
));
1240 class RangeRecorder
{
1242 std::string reported_pages
;
1245 : page_size_scaled_log(
1246 Log2(GetPageSizeCached() >> Allocator64::kCompactPtrScale
)),
1247 last_page_reported(0) {}
1249 void ReleasePageRangeToOS(u32 class_id
, u32 from
, u32 to
) {
1250 from
>>= page_size_scaled_log
;
1251 to
>>= page_size_scaled_log
;
1252 ASSERT_LT(from
, to
);
1253 if (!reported_pages
.empty())
1254 ASSERT_LT(last_page_reported
, from
);
1255 reported_pages
.append(from
- last_page_reported
, '.');
1256 reported_pages
.append(to
- from
, 'x');
1257 last_page_reported
= to
;
1261 const uptr page_size_scaled_log
;
1262 u32 last_page_reported
;
1265 TEST(SanitizerCommon
, SizeClassAllocator64FreePagesRangeTracker
) {
1266 typedef Allocator64::FreePagesRangeTracker
<RangeRecorder
> RangeTracker
;
1268 // 'x' denotes a page to be released, '.' denotes a page to be kept around.
1269 const char* test_cases
[] = {
1275 "..............xxxxx",
1276 "xxxxxxxxxxxxxxxxxx.....",
1277 "......xxxxxxxx........",
1278 "xxx..........xxxxxxxxxxxxxxx",
1279 "......xxxx....xxxx........",
1280 "xxx..........xxxxxxxx....xxxxxxx",
1281 "x.x.x.x.x.x.x.x.x.x.x.x.",
1282 ".x.x.x.x.x.x.x.x.x.x.x.x",
1283 ".x.x.x.x.x.x.x.x.x.x.x.x.",
1284 "x.x.x.x.x.x.x.x.x.x.x.x.x",
1287 for (auto test_case
: test_cases
) {
1288 RangeRecorder range_recorder
;
1289 RangeTracker
tracker(&range_recorder
, 1);
1290 for (int i
= 0; test_case
[i
] != 0; i
++)
1291 tracker
.NextPage(test_case
[i
] == 'x');
1293 // Strip trailing '.'-pages before comparing the results as they are not
1294 // going to be reported to range_recorder anyway.
1295 const char* last_x
= strrchr(test_case
, 'x');
1296 std::string
expected(
1298 last_x
== nullptr ? 0 : (last_x
- test_case
+ 1));
1299 EXPECT_STREQ(expected
.c_str(), range_recorder
.reported_pages
.c_str());
1303 class ReleasedPagesTrackingMemoryMapper
{
1305 std::set
<u32
> reported_pages
;
1306 std::vector
<u64
> buffer
;
1308 u64
*MapPackedCounterArrayBuffer(uptr buffer_size
) {
1309 reported_pages
.clear();
1310 buffer
.assign(buffer_size
, 0);
1311 return buffer
.data();
1313 void ReleasePageRangeToOS(u32 class_id
, u32 from
, u32 to
) {
1314 uptr page_size_scaled
=
1315 GetPageSizeCached() >> Allocator64::kCompactPtrScale
;
1316 for (u32 i
= from
; i
< to
; i
+= page_size_scaled
)
1317 reported_pages
.insert(i
);
1321 template <class Allocator
>
1322 void TestReleaseFreeMemoryToOS() {
1323 ReleasedPagesTrackingMemoryMapper memory_mapper
;
1324 const uptr kAllocatedPagesCount
= 1024;
1325 const uptr page_size
= GetPageSizeCached();
1326 const uptr page_size_scaled
= page_size
>> Allocator::kCompactPtrScale
;
1328 uint32_t rnd_state
= 42;
1330 for (uptr class_id
= 1; class_id
<= Allocator::SizeClassMapT::kLargestClassID
;
1332 const uptr chunk_size
= Allocator::SizeClassMapT::Size(class_id
);
1333 const uptr chunk_size_scaled
= chunk_size
>> Allocator::kCompactPtrScale
;
1334 const uptr max_chunks
=
1335 kAllocatedPagesCount
* GetPageSizeCached() / chunk_size
;
1337 // Generate the random free list.
1338 std::vector
<u32
> free_array
;
1339 bool in_free_range
= false;
1340 uptr current_range_end
= 0;
1341 for (uptr i
= 0; i
< max_chunks
; i
++) {
1342 if (i
== current_range_end
) {
1343 in_free_range
= (my_rand_r(&rnd_state
) & 1U) == 1;
1344 current_range_end
+= my_rand_r(&rnd_state
) % 100 + 1;
1347 free_array
.push_back(i
* chunk_size_scaled
);
1349 if (free_array
.empty())
1351 // Shuffle free_list to verify that ReleaseFreeMemoryToOS does not depend on
1352 // the list ordering.
1353 std::shuffle(free_array
.begin(), free_array
.end(), r
);
1355 Allocator::ReleaseFreeMemoryToOS(&free_array
[0], free_array
.size(),
1356 chunk_size
, kAllocatedPagesCount
,
1357 &memory_mapper
, class_id
);
1359 // Verify that there are no released pages touched by used chunks and all
1360 // ranges of free chunks big enough to contain the entire memory pages had
1361 // these pages released.
1362 uptr verified_released_pages
= 0;
1363 std::set
<u32
> free_chunks(free_array
.begin(), free_array
.end());
1365 u32 current_chunk
= 0;
1366 in_free_range
= false;
1367 u32 current_free_range_start
= 0;
1368 for (uptr i
= 0; i
<= max_chunks
; i
++) {
1369 bool is_free_chunk
= free_chunks
.find(current_chunk
) != free_chunks
.end();
1371 if (is_free_chunk
) {
1372 if (!in_free_range
) {
1373 in_free_range
= true;
1374 current_free_range_start
= current_chunk
;
1377 // Verify that this used chunk does not touch any released page.
1378 for (uptr i_page
= current_chunk
/ page_size_scaled
;
1379 i_page
<= (current_chunk
+ chunk_size_scaled
- 1) /
1382 bool page_released
=
1383 memory_mapper
.reported_pages
.find(i_page
* page_size_scaled
) !=
1384 memory_mapper
.reported_pages
.end();
1385 ASSERT_EQ(false, page_released
);
1388 if (in_free_range
) {
1389 in_free_range
= false;
1390 // Verify that all entire memory pages covered by this range of free
1391 // chunks were released.
1392 u32 page
= RoundUpTo(current_free_range_start
, page_size_scaled
);
1393 while (page
+ page_size_scaled
<= current_chunk
) {
1394 bool page_released
=
1395 memory_mapper
.reported_pages
.find(page
) !=
1396 memory_mapper
.reported_pages
.end();
1397 ASSERT_EQ(true, page_released
);
1398 verified_released_pages
++;
1399 page
+= page_size_scaled
;
1404 current_chunk
+= chunk_size_scaled
;
1407 ASSERT_EQ(memory_mapper
.reported_pages
.size(), verified_released_pages
);
1411 TEST(SanitizerCommon
, SizeClassAllocator64ReleaseFreeMemoryToOS
) {
1412 TestReleaseFreeMemoryToOS
<Allocator64
>();
1415 #if !ALLOCATOR64_SMALL_SIZE
1416 TEST(SanitizerCommon
, SizeClassAllocator64CompactReleaseFreeMemoryToOS
) {
1417 TestReleaseFreeMemoryToOS
<Allocator64Compact
>();
1420 TEST(SanitizerCommon
, SizeClassAllocator64VeryCompactReleaseFreeMemoryToOS
) {
1421 TestReleaseFreeMemoryToOS
<Allocator64VeryCompact
>();
1423 #endif // !ALLOCATOR64_SMALL_SIZE
1425 #endif // SANITIZER_CAN_USE_ALLOCATOR64
1427 TEST(SanitizerCommon
, LowLevelAllocatorShouldRoundUpSizeOnAlloc
) {
1428 // When allocating a memory block slightly bigger than a memory page and
1429 // LowLevelAllocator calls MmapOrDie for the internal buffer, it should round
1430 // the size up to the page size, so that subsequent calls to the allocator
1431 // can use the remaining space in the last allocated page.
1432 static LowLevelAllocator allocator
;
1433 char *ptr1
= (char *)allocator
.Allocate(GetPageSizeCached() + 16);
1434 char *ptr2
= (char *)allocator
.Allocate(16);
1435 EXPECT_EQ(ptr2
, ptr1
+ GetPageSizeCached() + 16);
1438 #endif // #if !SANITIZER_DEBUG