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