1 //===-- secondary_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 //===----------------------------------------------------------------------===// 8 9 #include "tests/scudo_unit_test.h" 10 11 #include "secondary.h" 12 13 #include <stdio.h> 14 15 #include <condition_variable> 16 #include <mutex> 17 #include <random> 18 #include <thread> 19 #include <vector> 20 21 template <class SecondaryT> static void testSecondaryBasic(void) { 22 scudo::GlobalStats S; 23 S.init(); 24 SecondaryT *L = new SecondaryT; 25 L->init(&S); 26 const scudo::uptr Size = 1U << 16; 27 void *P = L->allocate(Size); 28 EXPECT_NE(P, nullptr); 29 memset(P, 'A', Size); 30 EXPECT_GE(SecondaryT::getBlockSize(P), Size); 31 L->deallocate(P); 32 // If we are not using a free list, blocks are unmapped on deallocation. 33 if (SecondaryT::getMaxFreeListSize() == 0U) 34 EXPECT_DEATH(memset(P, 'A', Size), ""); 35 36 const scudo::uptr Align = 1U << 16; 37 P = L->allocate(Size + Align, Align); 38 EXPECT_NE(P, nullptr); 39 void *AlignedP = reinterpret_cast<void *>( 40 scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align)); 41 memset(AlignedP, 'A', Size); 42 L->deallocate(P); 43 44 std::vector<void *> V; 45 for (scudo::uptr I = 0; I < 32U; I++) 46 V.push_back(L->allocate(Size)); 47 std::shuffle(V.begin(), V.end(), std::mt19937(std::random_device()())); 48 while (!V.empty()) { 49 L->deallocate(V.back()); 50 V.pop_back(); 51 } 52 scudo::ScopedString Str(1024); 53 L->getStats(&Str); 54 Str.output(); 55 } 56 57 TEST(ScudoSecondaryTest, SecondaryBasic) { 58 testSecondaryBasic<scudo::MapAllocator<0U>>(); 59 #if !SCUDO_FUCHSIA 60 testSecondaryBasic<scudo::MapAllocator<>>(); 61 testSecondaryBasic<scudo::MapAllocator<64U>>(); 62 #endif 63 } 64 65 #if SCUDO_FUCHSIA 66 using LargeAllocator = scudo::MapAllocator<0U>; 67 #else 68 using LargeAllocator = scudo::MapAllocator<>; 69 #endif 70 71 // This exercises a variety of combinations of size and alignment for the 72 // MapAllocator. The size computation done here mimic the ones done by the 73 // combined allocator. 74 TEST(ScudoSecondaryTest, SecondaryCombinations) { 75 constexpr scudo::uptr MinAlign = FIRST_32_SECOND_64(8, 16); 76 constexpr scudo::uptr HeaderSize = scudo::roundUpTo(8, MinAlign); 77 LargeAllocator *L = new LargeAllocator; 78 L->init(nullptr); 79 for (scudo::uptr SizeLog = 0; SizeLog <= 20; SizeLog++) { 80 for (scudo::uptr AlignLog = FIRST_32_SECOND_64(3, 4); AlignLog <= 16; 81 AlignLog++) { 82 const scudo::uptr Align = 1U << AlignLog; 83 for (scudo::sptr Delta = -128; Delta <= 128; Delta += 8) { 84 if (static_cast<scudo::sptr>(1U << SizeLog) + Delta <= 0) 85 continue; 86 const scudo::uptr UserSize = 87 scudo::roundUpTo((1U << SizeLog) + Delta, MinAlign); 88 const scudo::uptr Size = 89 HeaderSize + UserSize + (Align > MinAlign ? Align - HeaderSize : 0); 90 void *P = L->allocate(Size, Align); 91 EXPECT_NE(P, nullptr); 92 void *AlignedP = reinterpret_cast<void *>( 93 scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align)); 94 memset(AlignedP, 0xff, UserSize); 95 L->deallocate(P); 96 } 97 } 98 } 99 scudo::ScopedString Str(1024); 100 L->getStats(&Str); 101 Str.output(); 102 } 103 104 TEST(ScudoSecondaryTest, SecondaryIterate) { 105 LargeAllocator *L = new LargeAllocator; 106 L->init(nullptr); 107 std::vector<void *> V; 108 const scudo::uptr PageSize = scudo::getPageSizeCached(); 109 for (scudo::uptr I = 0; I < 32U; I++) 110 V.push_back(L->allocate((std::rand() % 16) * PageSize)); 111 auto Lambda = [V](scudo::uptr Block) { 112 EXPECT_NE(std::find(V.begin(), V.end(), reinterpret_cast<void *>(Block)), 113 V.end()); 114 }; 115 L->disable(); 116 L->iterateOverBlocks(Lambda); 117 L->enable(); 118 while (!V.empty()) { 119 L->deallocate(V.back()); 120 V.pop_back(); 121 } 122 scudo::ScopedString Str(1024); 123 L->getStats(&Str); 124 Str.output(); 125 } 126 127 static std::mutex Mutex; 128 static std::condition_variable Cv; 129 static bool Ready = false; 130 131 static void performAllocations(LargeAllocator *L) { 132 std::vector<void *> V; 133 const scudo::uptr PageSize = scudo::getPageSizeCached(); 134 { 135 std::unique_lock<std::mutex> Lock(Mutex); 136 while (!Ready) 137 Cv.wait(Lock); 138 } 139 for (scudo::uptr I = 0; I < 32U; I++) 140 V.push_back(L->allocate((std::rand() % 16) * PageSize)); 141 while (!V.empty()) { 142 L->deallocate(V.back()); 143 V.pop_back(); 144 } 145 } 146 147 TEST(ScudoSecondaryTest, SecondaryThreadsRace) { 148 LargeAllocator *L = new LargeAllocator; 149 L->init(nullptr); 150 std::thread Threads[10]; 151 for (scudo::uptr I = 0; I < 10U; I++) 152 Threads[I] = std::thread(performAllocations, L); 153 { 154 std::unique_lock<std::mutex> Lock(Mutex); 155 Ready = true; 156 Cv.notify_all(); 157 } 158 for (auto &T : Threads) 159 T.join(); 160 scudo::ScopedString Str(1024); 161 L->getStats(&Str); 162 Str.output(); 163 } 164