Home | History | Annotate | Download | only in tests
      1 //===-- sanitizer_allocator_test.cc ---------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file is a part of ThreadSanitizer/AddressSanitizer runtime.
     11 // Tests for sanitizer_allocator.h.
     12 //
     13 //===----------------------------------------------------------------------===//
     14 #include "sanitizer_common/sanitizer_allocator.h"
     15 #include "sanitizer_common/sanitizer_common.h"
     16 
     17 #include "sanitizer_test_utils.h"
     18 
     19 #include "gtest/gtest.h"
     20 
     21 #include <stdlib.h>
     22 #include <pthread.h>
     23 #include <algorithm>
     24 #include <vector>
     25 #include <set>
     26 
     27 // Too slow for debug build
     28 #if TSAN_DEBUG == 0
     29 
     30 #if SANITIZER_WORDSIZE == 64
     31 static const uptr kAllocatorSpace = 0x700000000000ULL;
     32 static const uptr kAllocatorSize  = 0x010000000000ULL;  // 1T.
     33 static const u64 kAddressSpaceSize = 1ULL << 47;
     34 
     35 typedef SizeClassAllocator64<
     36   kAllocatorSpace, kAllocatorSize, 16, DefaultSizeClassMap> Allocator64;
     37 
     38 typedef SizeClassAllocator64<
     39   kAllocatorSpace, kAllocatorSize, 16, CompactSizeClassMap> Allocator64Compact;
     40 #else
     41 static const u64 kAddressSpaceSize = 1ULL << 32;
     42 #endif
     43 
     44 typedef SizeClassAllocator32<
     45   0, kAddressSpaceSize, 16, CompactSizeClassMap> Allocator32Compact;
     46 
     47 template <class SizeClassMap>
     48 void TestSizeClassMap() {
     49   typedef SizeClassMap SCMap;
     50   // SCMap::Print();
     51   SCMap::Validate();
     52 }
     53 
     54 TEST(SanitizerCommon, DefaultSizeClassMap) {
     55   TestSizeClassMap<DefaultSizeClassMap>();
     56 }
     57 
     58 TEST(SanitizerCommon, CompactSizeClassMap) {
     59   TestSizeClassMap<CompactSizeClassMap>();
     60 }
     61 
     62 template <class Allocator>
     63 void TestSizeClassAllocator() {
     64   Allocator *a = new Allocator;
     65   a->Init();
     66   SizeClassAllocatorLocalCache<Allocator> cache;
     67   memset(&cache, 0, sizeof(cache));
     68   cache.Init(0);
     69 
     70   static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000,
     71     50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000};
     72 
     73   std::vector<void *> allocated;
     74 
     75   uptr last_total_allocated = 0;
     76   for (int i = 0; i < 3; i++) {
     77     // Allocate a bunch of chunks.
     78     for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) {
     79       uptr size = sizes[s];
     80       if (!a->CanAllocate(size, 1)) continue;
     81       // printf("s = %ld\n", size);
     82       uptr n_iter = std::max((uptr)6, 8000000 / size);
     83       // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter);
     84       for (uptr i = 0; i < n_iter; i++) {
     85         uptr class_id0 = Allocator::SizeClassMapT::ClassID(size);
     86         char *x = (char*)cache.Allocate(a, class_id0);
     87         x[0] = 0;
     88         x[size - 1] = 0;
     89         x[size / 2] = 0;
     90         allocated.push_back(x);
     91         CHECK_EQ(x, a->GetBlockBegin(x));
     92         CHECK_EQ(x, a->GetBlockBegin(x + size - 1));
     93         CHECK(a->PointerIsMine(x));
     94         CHECK(a->PointerIsMine(x + size - 1));
     95         CHECK(a->PointerIsMine(x + size / 2));
     96         CHECK_GE(a->GetActuallyAllocatedSize(x), size);
     97         uptr class_id = a->GetSizeClass(x);
     98         CHECK_EQ(class_id, Allocator::SizeClassMapT::ClassID(size));
     99         uptr *metadata = reinterpret_cast<uptr*>(a->GetMetaData(x));
    100         metadata[0] = reinterpret_cast<uptr>(x) + 1;
    101         metadata[1] = 0xABCD;
    102       }
    103     }
    104     // Deallocate all.
    105     for (uptr i = 0; i < allocated.size(); i++) {
    106       void *x = allocated[i];
    107       uptr *metadata = reinterpret_cast<uptr*>(a->GetMetaData(x));
    108       CHECK_EQ(metadata[0], reinterpret_cast<uptr>(x) + 1);
    109       CHECK_EQ(metadata[1], 0xABCD);
    110       cache.Deallocate(a, a->GetSizeClass(x), x);
    111     }
    112     allocated.clear();
    113     uptr total_allocated = a->TotalMemoryUsed();
    114     if (last_total_allocated == 0)
    115       last_total_allocated = total_allocated;
    116     CHECK_EQ(last_total_allocated, total_allocated);
    117   }
    118 
    119   // Check that GetBlockBegin never crashes.
    120   for (uptr x = 0, step = kAddressSpaceSize / 100000;
    121        x < kAddressSpaceSize - step; x += step)
    122     if (a->PointerIsMine(reinterpret_cast<void *>(x)))
    123       Ident(a->GetBlockBegin(reinterpret_cast<void *>(x)));
    124 
    125   a->TestOnlyUnmap();
    126   delete a;
    127 }
    128 
    129 #if SANITIZER_WORDSIZE == 64
    130 TEST(SanitizerCommon, SizeClassAllocator64) {
    131   TestSizeClassAllocator<Allocator64>();
    132 }
    133 
    134 TEST(SanitizerCommon, SizeClassAllocator64Compact) {
    135   TestSizeClassAllocator<Allocator64Compact>();
    136 }
    137 #endif
    138 
    139 TEST(SanitizerCommon, SizeClassAllocator32Compact) {
    140   TestSizeClassAllocator<Allocator32Compact>();
    141 }
    142 
    143 template <class Allocator>
    144 void SizeClassAllocatorMetadataStress() {
    145   Allocator *a = new Allocator;
    146   a->Init();
    147   SizeClassAllocatorLocalCache<Allocator> cache;
    148   memset(&cache, 0, sizeof(cache));
    149   cache.Init(0);
    150   static volatile void *sink;
    151 
    152   const uptr kNumAllocs = 10000;
    153   void *allocated[kNumAllocs];
    154   for (uptr i = 0; i < kNumAllocs; i++) {
    155     void *x = cache.Allocate(a, 1 + i % 50);
    156     allocated[i] = x;
    157   }
    158   // Get Metadata kNumAllocs^2 times.
    159   for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) {
    160     sink = a->GetMetaData(allocated[i % kNumAllocs]);
    161   }
    162   for (uptr i = 0; i < kNumAllocs; i++) {
    163     cache.Deallocate(a, 1 + i % 50, allocated[i]);
    164   }
    165 
    166   a->TestOnlyUnmap();
    167   (void)sink;
    168   delete a;
    169 }
    170 
    171 #if SANITIZER_WORDSIZE == 64
    172 TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) {
    173   SizeClassAllocatorMetadataStress<Allocator64>();
    174 }
    175 
    176 TEST(SanitizerCommon, SizeClassAllocator64CompactMetadataStress) {
    177   SizeClassAllocatorMetadataStress<Allocator64Compact>();
    178 }
    179 #endif
    180 TEST(SanitizerCommon, SizeClassAllocator32CompactMetadataStress) {
    181   SizeClassAllocatorMetadataStress<Allocator32Compact>();
    182 }
    183 
    184 struct TestMapUnmapCallback {
    185   static int map_count, unmap_count;
    186   void OnMap(uptr p, uptr size) const { map_count++; }
    187   void OnUnmap(uptr p, uptr size) const { unmap_count++; }
    188 };
    189 int TestMapUnmapCallback::map_count;
    190 int TestMapUnmapCallback::unmap_count;
    191 
    192 #if SANITIZER_WORDSIZE == 64
    193 TEST(SanitizerCommon, SizeClassAllocator64MapUnmapCallback) {
    194   TestMapUnmapCallback::map_count = 0;
    195   TestMapUnmapCallback::unmap_count = 0;
    196   typedef SizeClassAllocator64<
    197       kAllocatorSpace, kAllocatorSize, 16, DefaultSizeClassMap,
    198       TestMapUnmapCallback> Allocator64WithCallBack;
    199   Allocator64WithCallBack *a = new Allocator64WithCallBack;
    200   a->Init();
    201   EXPECT_EQ(TestMapUnmapCallback::map_count, 1);  // Allocator state.
    202   SizeClassAllocatorLocalCache<Allocator64WithCallBack> cache;
    203   memset(&cache, 0, sizeof(cache));
    204   cache.Init(0);
    205   AllocatorStats stats;
    206   stats.Init();
    207   a->AllocateBatch(&stats, &cache, 32);
    208   EXPECT_EQ(TestMapUnmapCallback::map_count, 3);  // State + alloc + metadata.
    209   a->TestOnlyUnmap();
    210   EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1);  // The whole thing.
    211   delete a;
    212 }
    213 #endif
    214 
    215 TEST(SanitizerCommon, SizeClassAllocator32MapUnmapCallback) {
    216   TestMapUnmapCallback::map_count = 0;
    217   TestMapUnmapCallback::unmap_count = 0;
    218   typedef SizeClassAllocator32<
    219       0, kAddressSpaceSize, 16, CompactSizeClassMap,
    220       TestMapUnmapCallback> Allocator32WithCallBack;
    221   Allocator32WithCallBack *a = new Allocator32WithCallBack;
    222   a->Init();
    223   EXPECT_EQ(TestMapUnmapCallback::map_count, 1);  // Allocator state.
    224   SizeClassAllocatorLocalCache<Allocator32WithCallBack>  cache;
    225   memset(&cache, 0, sizeof(cache));
    226   cache.Init(0);
    227   AllocatorStats stats;
    228   stats.Init();
    229   a->AllocateBatch(&stats, &cache, 32);
    230   EXPECT_EQ(TestMapUnmapCallback::map_count, 2);  // alloc.
    231   a->TestOnlyUnmap();
    232   EXPECT_EQ(TestMapUnmapCallback::unmap_count, 2);  // The whole thing + alloc.
    233   delete a;
    234   // fprintf(stderr, "Map: %d Unmap: %d\n",
    235   //         TestMapUnmapCallback::map_count,
    236   //         TestMapUnmapCallback::unmap_count);
    237 }
    238 
    239 TEST(SanitizerCommon, LargeMmapAllocatorMapUnmapCallback) {
    240   TestMapUnmapCallback::map_count = 0;
    241   TestMapUnmapCallback::unmap_count = 0;
    242   LargeMmapAllocator<TestMapUnmapCallback> a;
    243   a.Init();
    244   AllocatorStats stats;
    245   stats.Init();
    246   void *x = a.Allocate(&stats, 1 << 20, 1);
    247   EXPECT_EQ(TestMapUnmapCallback::map_count, 1);
    248   a.Deallocate(&stats, x);
    249   EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1);
    250 }
    251 
    252 template<class Allocator>
    253 void FailInAssertionOnOOM() {
    254   Allocator a;
    255   a.Init();
    256   SizeClassAllocatorLocalCache<Allocator> cache;
    257   memset(&cache, 0, sizeof(cache));
    258   cache.Init(0);
    259   AllocatorStats stats;
    260   stats.Init();
    261   for (int i = 0; i < 1000000; i++) {
    262     a.AllocateBatch(&stats, &cache, 52);
    263   }
    264 
    265   a.TestOnlyUnmap();
    266 }
    267 
    268 #if SANITIZER_WORDSIZE == 64
    269 TEST(SanitizerCommon, SizeClassAllocator64Overflow) {
    270   EXPECT_DEATH(FailInAssertionOnOOM<Allocator64>(), "Out of memory");
    271 }
    272 #endif
    273 
    274 TEST(SanitizerCommon, LargeMmapAllocator) {
    275   LargeMmapAllocator<> a;
    276   a.Init();
    277   AllocatorStats stats;
    278   stats.Init();
    279 
    280   static const int kNumAllocs = 1000;
    281   char *allocated[kNumAllocs];
    282   static const uptr size = 4000;
    283   // Allocate some.
    284   for (int i = 0; i < kNumAllocs; i++) {
    285     allocated[i] = (char *)a.Allocate(&stats, size, 1);
    286     CHECK(a.PointerIsMine(allocated[i]));
    287   }
    288   // Deallocate all.
    289   CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs);
    290   for (int i = 0; i < kNumAllocs; i++) {
    291     char *p = allocated[i];
    292     CHECK(a.PointerIsMine(p));
    293     a.Deallocate(&stats, p);
    294   }
    295   // Check that non left.
    296   CHECK_EQ(a.TotalMemoryUsed(), 0);
    297 
    298   // Allocate some more, also add metadata.
    299   for (int i = 0; i < kNumAllocs; i++) {
    300     char *x = (char *)a.Allocate(&stats, size, 1);
    301     CHECK_GE(a.GetActuallyAllocatedSize(x), size);
    302     uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(x));
    303     *meta = i;
    304     allocated[i] = x;
    305   }
    306   for (int i = 0; i < kNumAllocs * kNumAllocs; i++) {
    307     char *p = allocated[i % kNumAllocs];
    308     CHECK(a.PointerIsMine(p));
    309     CHECK(a.PointerIsMine(p + 2000));
    310   }
    311   CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs);
    312   // Deallocate all in reverse order.
    313   for (int i = 0; i < kNumAllocs; i++) {
    314     int idx = kNumAllocs - i - 1;
    315     char *p = allocated[idx];
    316     uptr *meta = reinterpret_cast<uptr*>(a.GetMetaData(p));
    317     CHECK_EQ(*meta, idx);
    318     CHECK(a.PointerIsMine(p));
    319     a.Deallocate(&stats, p);
    320   }
    321   CHECK_EQ(a.TotalMemoryUsed(), 0);
    322 
    323   // Test alignments.
    324   uptr max_alignment = SANITIZER_WORDSIZE == 64 ? (1 << 28) : (1 << 24);
    325   for (uptr alignment = 8; alignment <= max_alignment; alignment *= 2) {
    326     const uptr kNumAlignedAllocs = 100;
    327     for (uptr i = 0; i < kNumAlignedAllocs; i++) {
    328       uptr size = ((i % 10) + 1) * 4096;
    329       char *p = allocated[i] = (char *)a.Allocate(&stats, size, alignment);
    330       CHECK_EQ(p, a.GetBlockBegin(p));
    331       CHECK_EQ(p, a.GetBlockBegin(p + size - 1));
    332       CHECK_EQ(p, a.GetBlockBegin(p + size / 2));
    333       CHECK_EQ(0, (uptr)allocated[i] % alignment);
    334       p[0] = p[size - 1] = 0;
    335     }
    336     for (uptr i = 0; i < kNumAlignedAllocs; i++) {
    337       a.Deallocate(&stats, allocated[i]);
    338     }
    339   }
    340 }
    341 
    342 template
    343 <class PrimaryAllocator, class SecondaryAllocator, class AllocatorCache>
    344 void TestCombinedAllocator() {
    345   typedef
    346       CombinedAllocator<PrimaryAllocator, AllocatorCache, SecondaryAllocator>
    347       Allocator;
    348   Allocator *a = new Allocator;
    349   a->Init();
    350 
    351   AllocatorCache cache;
    352   memset(&cache, 0, sizeof(cache));
    353   a->InitCache(&cache);
    354 
    355   EXPECT_EQ(a->Allocate(&cache, -1, 1), (void*)0);
    356   EXPECT_EQ(a->Allocate(&cache, -1, 1024), (void*)0);
    357   EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1), (void*)0);
    358   EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1024), (void*)0);
    359   EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1023, 1024), (void*)0);
    360 
    361   const uptr kNumAllocs = 100000;
    362   const uptr kNumIter = 10;
    363   for (uptr iter = 0; iter < kNumIter; iter++) {
    364     std::vector<void*> allocated;
    365     for (uptr i = 0; i < kNumAllocs; i++) {
    366       uptr size = (i % (1 << 14)) + 1;
    367       if ((i % 1024) == 0)
    368         size = 1 << (10 + (i % 14));
    369       void *x = a->Allocate(&cache, size, 1);
    370       uptr *meta = reinterpret_cast<uptr*>(a->GetMetaData(x));
    371       CHECK_EQ(*meta, 0);
    372       *meta = size;
    373       allocated.push_back(x);
    374     }
    375 
    376     random_shuffle(allocated.begin(), allocated.end());
    377 
    378     for (uptr i = 0; i < kNumAllocs; i++) {
    379       void *x = allocated[i];
    380       uptr *meta = reinterpret_cast<uptr*>(a->GetMetaData(x));
    381       CHECK_NE(*meta, 0);
    382       CHECK(a->PointerIsMine(x));
    383       *meta = 0;
    384       a->Deallocate(&cache, x);
    385     }
    386     allocated.clear();
    387     a->SwallowCache(&cache);
    388   }
    389   a->DestroyCache(&cache);
    390   a->TestOnlyUnmap();
    391 }
    392 
    393 #if SANITIZER_WORDSIZE == 64
    394 TEST(SanitizerCommon, CombinedAllocator64) {
    395   TestCombinedAllocator<Allocator64,
    396       LargeMmapAllocator<>,
    397       SizeClassAllocatorLocalCache<Allocator64> > ();
    398 }
    399 
    400 TEST(SanitizerCommon, CombinedAllocator64Compact) {
    401   TestCombinedAllocator<Allocator64Compact,
    402       LargeMmapAllocator<>,
    403       SizeClassAllocatorLocalCache<Allocator64Compact> > ();
    404 }
    405 #endif
    406 
    407 TEST(SanitizerCommon, CombinedAllocator32Compact) {
    408   TestCombinedAllocator<Allocator32Compact,
    409       LargeMmapAllocator<>,
    410       SizeClassAllocatorLocalCache<Allocator32Compact> > ();
    411 }
    412 
    413 template <class AllocatorCache>
    414 void TestSizeClassAllocatorLocalCache() {
    415   AllocatorCache cache;
    416   typedef typename AllocatorCache::Allocator Allocator;
    417   Allocator *a = new Allocator();
    418 
    419   a->Init();
    420   memset(&cache, 0, sizeof(cache));
    421   cache.Init(0);
    422 
    423   const uptr kNumAllocs = 10000;
    424   const int kNumIter = 100;
    425   uptr saved_total = 0;
    426   for (int class_id = 1; class_id <= 5; class_id++) {
    427     for (int it = 0; it < kNumIter; it++) {
    428       void *allocated[kNumAllocs];
    429       for (uptr i = 0; i < kNumAllocs; i++) {
    430         allocated[i] = cache.Allocate(a, class_id);
    431       }
    432       for (uptr i = 0; i < kNumAllocs; i++) {
    433         cache.Deallocate(a, class_id, allocated[i]);
    434       }
    435       cache.Drain(a);
    436       uptr total_allocated = a->TotalMemoryUsed();
    437       if (it)
    438         CHECK_EQ(saved_total, total_allocated);
    439       saved_total = total_allocated;
    440     }
    441   }
    442 
    443   a->TestOnlyUnmap();
    444   delete a;
    445 }
    446 
    447 #if SANITIZER_WORDSIZE == 64
    448 TEST(SanitizerCommon, SizeClassAllocator64LocalCache) {
    449   TestSizeClassAllocatorLocalCache<
    450       SizeClassAllocatorLocalCache<Allocator64> >();
    451 }
    452 
    453 TEST(SanitizerCommon, SizeClassAllocator64CompactLocalCache) {
    454   TestSizeClassAllocatorLocalCache<
    455       SizeClassAllocatorLocalCache<Allocator64Compact> >();
    456 }
    457 #endif
    458 
    459 TEST(SanitizerCommon, SizeClassAllocator32CompactLocalCache) {
    460   TestSizeClassAllocatorLocalCache<
    461       SizeClassAllocatorLocalCache<Allocator32Compact> >();
    462 }
    463 
    464 #if SANITIZER_WORDSIZE == 64
    465 typedef SizeClassAllocatorLocalCache<Allocator64> AllocatorCache;
    466 static AllocatorCache static_allocator_cache;
    467 
    468 void *AllocatorLeakTestWorker(void *arg) {
    469   typedef AllocatorCache::Allocator Allocator;
    470   Allocator *a = (Allocator*)(arg);
    471   static_allocator_cache.Allocate(a, 10);
    472   static_allocator_cache.Drain(a);
    473   return 0;
    474 }
    475 
    476 TEST(SanitizerCommon, AllocatorLeakTest) {
    477   typedef AllocatorCache::Allocator Allocator;
    478   Allocator a;
    479   a.Init();
    480   uptr total_used_memory = 0;
    481   for (int i = 0; i < 100; i++) {
    482     pthread_t t;
    483     EXPECT_EQ(0, pthread_create(&t, 0, AllocatorLeakTestWorker, &a));
    484     EXPECT_EQ(0, pthread_join(t, 0));
    485     if (i == 0)
    486       total_used_memory = a.TotalMemoryUsed();
    487     EXPECT_EQ(a.TotalMemoryUsed(), total_used_memory);
    488   }
    489 
    490   a.TestOnlyUnmap();
    491 }
    492 
    493 // Struct which is allocated to pass info to new threads.  The new thread frees
    494 // it.
    495 struct NewThreadParams {
    496   AllocatorCache *thread_cache;
    497   AllocatorCache::Allocator *allocator;
    498   uptr class_id;
    499 };
    500 
    501 // Called in a new thread.  Just frees its argument.
    502 static void *DeallocNewThreadWorker(void *arg) {
    503   NewThreadParams *params = reinterpret_cast<NewThreadParams*>(arg);
    504   params->thread_cache->Deallocate(params->allocator, params->class_id, params);
    505   return NULL;
    506 }
    507 
    508 // The allocator cache is supposed to be POD and zero initialized.  We should be
    509 // able to call Deallocate on a zeroed cache, and it will self-initialize.
    510 TEST(Allocator, AllocatorCacheDeallocNewThread) {
    511   AllocatorCache::Allocator allocator;
    512   allocator.Init();
    513   AllocatorCache main_cache;
    514   AllocatorCache child_cache;
    515   memset(&main_cache, 0, sizeof(main_cache));
    516   memset(&child_cache, 0, sizeof(child_cache));
    517 
    518   uptr class_id = DefaultSizeClassMap::ClassID(sizeof(NewThreadParams));
    519   NewThreadParams *params = reinterpret_cast<NewThreadParams*>(
    520       main_cache.Allocate(&allocator, class_id));
    521   params->thread_cache = &child_cache;
    522   params->allocator = &allocator;
    523   params->class_id = class_id;
    524   pthread_t t;
    525   EXPECT_EQ(0, pthread_create(&t, 0, DeallocNewThreadWorker, params));
    526   EXPECT_EQ(0, pthread_join(t, 0));
    527 }
    528 #endif
    529 
    530 TEST(Allocator, Basic) {
    531   char *p = (char*)InternalAlloc(10);
    532   EXPECT_NE(p, (char*)0);
    533   char *p2 = (char*)InternalAlloc(20);
    534   EXPECT_NE(p2, (char*)0);
    535   EXPECT_NE(p2, p);
    536   InternalFree(p);
    537   InternalFree(p2);
    538 }
    539 
    540 TEST(Allocator, Stress) {
    541   const int kCount = 1000;
    542   char *ptrs[kCount];
    543   unsigned rnd = 42;
    544   for (int i = 0; i < kCount; i++) {
    545     uptr sz = my_rand_r(&rnd) % 1000;
    546     char *p = (char*)InternalAlloc(sz);
    547     EXPECT_NE(p, (char*)0);
    548     ptrs[i] = p;
    549   }
    550   for (int i = 0; i < kCount; i++) {
    551     InternalFree(ptrs[i]);
    552   }
    553 }
    554 
    555 TEST(Allocator, ScopedBuffer) {
    556   const int kSize = 512;
    557   {
    558     InternalScopedBuffer<int> int_buf(kSize);
    559     EXPECT_EQ(sizeof(int) * kSize, int_buf.size());  // NOLINT
    560   }
    561   InternalScopedBuffer<char> char_buf(kSize);
    562   EXPECT_EQ(sizeof(char) * kSize, char_buf.size());  // NOLINT
    563   internal_memset(char_buf.data(), 'c', kSize);
    564   for (int i = 0; i < kSize; i++) {
    565     EXPECT_EQ('c', char_buf[i]);
    566   }
    567 }
    568 
    569 class IterationTestCallback {
    570  public:
    571   explicit IterationTestCallback(std::set<void *> *chunks)
    572     : chunks_(chunks) {}
    573   void operator()(void *chunk) const {
    574     chunks_->insert(chunk);
    575   }
    576  private:
    577   std::set<void *> *chunks_;
    578 };
    579 
    580 template <class Allocator>
    581 void TestSizeClassAllocatorIteration() {
    582   Allocator *a = new Allocator;
    583   a->Init();
    584   SizeClassAllocatorLocalCache<Allocator> cache;
    585   memset(&cache, 0, sizeof(cache));
    586   cache.Init(0);
    587 
    588   static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000,
    589     50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000};
    590 
    591   std::vector<void *> allocated;
    592 
    593   // Allocate a bunch of chunks.
    594   for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) {
    595     uptr size = sizes[s];
    596     if (!a->CanAllocate(size, 1)) continue;
    597     // printf("s = %ld\n", size);
    598     uptr n_iter = std::max((uptr)6, 80000 / size);
    599     // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter);
    600     for (uptr j = 0; j < n_iter; j++) {
    601       uptr class_id0 = Allocator::SizeClassMapT::ClassID(size);
    602       void *x = cache.Allocate(a, class_id0);
    603       allocated.push_back(x);
    604     }
    605   }
    606 
    607   std::set<void *> reported_chunks;
    608   IterationTestCallback callback(&reported_chunks);
    609   a->ForceLock();
    610   a->ForEachChunk(callback);
    611   a->ForceUnlock();
    612 
    613   for (uptr i = 0; i < allocated.size(); i++) {
    614     // Don't use EXPECT_NE. Reporting the first mismatch is enough.
    615     ASSERT_NE(reported_chunks.find(allocated[i]), reported_chunks.end());
    616   }
    617 
    618   a->TestOnlyUnmap();
    619   delete a;
    620 }
    621 
    622 #if SANITIZER_WORDSIZE == 64
    623 TEST(SanitizerCommon, SizeClassAllocator64Iteration) {
    624   TestSizeClassAllocatorIteration<Allocator64>();
    625 }
    626 #endif
    627 
    628 TEST(SanitizerCommon, SizeClassAllocator32Iteration) {
    629   TestSizeClassAllocatorIteration<Allocator32Compact>();
    630 }
    631 
    632 
    633 TEST(SanitizerCommon, LargeMmapAllocatorIteration) {
    634   LargeMmapAllocator<> a;
    635   a.Init();
    636   AllocatorStats stats;
    637   stats.Init();
    638 
    639   static const uptr kNumAllocs = 1000;
    640   char *allocated[kNumAllocs];
    641   static const uptr size = 40;
    642   // Allocate some.
    643   for (uptr i = 0; i < kNumAllocs; i++) {
    644     allocated[i] = (char *)a.Allocate(&stats, size, 1);
    645   }
    646 
    647   std::set<void *> reported_chunks;
    648   IterationTestCallback callback(&reported_chunks);
    649   a.ForceLock();
    650   a.ForEachChunk(callback);
    651   a.ForceUnlock();
    652 
    653   for (uptr i = 0; i < kNumAllocs; i++) {
    654     // Don't use EXPECT_NE. Reporting the first mismatch is enough.
    655     ASSERT_NE(reported_chunks.find(allocated[i]), reported_chunks.end());
    656   }
    657 }
    658 
    659 #endif  // #if TSAN_DEBUG==0
    660