diff options
Diffstat (limited to 'standalone/tests/secondary_test.cpp')
-rw-r--r-- | standalone/tests/secondary_test.cpp | 189 |
1 files changed, 112 insertions, 77 deletions
diff --git a/standalone/tests/secondary_test.cpp b/standalone/tests/secondary_test.cpp index a55704297de..723679228cb 100644 --- a/standalone/tests/secondary_test.cpp +++ b/standalone/tests/secondary_test.cpp @@ -6,55 +6,79 @@ // //===----------------------------------------------------------------------===// +#include "memtag.h" #include "tests/scudo_unit_test.h" #include "allocator_config.h" #include "secondary.h" -#include <stdio.h> - #include <condition_variable> +#include <memory> #include <mutex> #include <random> +#include <stdio.h> #include <thread> #include <vector> +template <typename Config> static scudo::Options getOptionsForConfig() { + if (!Config::MaySupportMemoryTagging || !scudo::archSupportsMemoryTagging() || + !scudo::systemSupportsMemoryTagging()) + return {}; + scudo::AtomicOptions AO; + AO.set(scudo::OptionBit::UseMemoryTagging); + return AO.load(); +} + template <typename Config> static void testSecondaryBasic(void) { using SecondaryT = scudo::MapAllocator<Config>; + scudo::Options Options = getOptionsForConfig<Config>(); scudo::GlobalStats S; S.init(); std::unique_ptr<SecondaryT> L(new SecondaryT); L->init(&S); const scudo::uptr Size = 1U << 16; - void *P = L->allocate(scudo::Options{}, Size); + void *P = L->allocate(Options, Size); EXPECT_NE(P, nullptr); memset(P, 'A', Size); EXPECT_GE(SecondaryT::getBlockSize(P), Size); - L->deallocate(scudo::Options{}, P); + L->deallocate(Options, P); + // If the Secondary can't cache that pointer, it will be unmapped. - if (!L->canCache(Size)) - EXPECT_DEATH(memset(P, 'A', Size), ""); + if (!L->canCache(Size)) { + EXPECT_DEATH( + { + // Repeat few time to avoid missing crash if it's mmaped by unrelated + // code. + for (int i = 0; i < 10; ++i) { + P = L->allocate(Options, Size); + L->deallocate(Options, P); + memset(P, 'A', Size); + } + }, + ""); + } const scudo::uptr Align = 1U << 16; - P = L->allocate(scudo::Options{}, Size + Align, Align); + P = L->allocate(Options, Size + Align, Align); EXPECT_NE(P, nullptr); void *AlignedP = reinterpret_cast<void *>( scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align)); memset(AlignedP, 'A', Size); - L->deallocate(scudo::Options{}, P); + L->deallocate(Options, P); std::vector<void *> V; for (scudo::uptr I = 0; I < 32U; I++) - V.push_back(L->allocate(scudo::Options{}, Size)); + V.push_back(L->allocate(Options, Size)); std::shuffle(V.begin(), V.end(), std::mt19937(std::random_device()())); while (!V.empty()) { - L->deallocate(scudo::Options{}, V.back()); + L->deallocate(Options, V.back()); V.pop_back(); } - scudo::ScopedString Str(1024); + scudo::ScopedString Str; L->getStats(&Str); Str.output(); + L->unmapTestOnly(); } struct NoCacheConfig { @@ -79,16 +103,25 @@ TEST(ScudoSecondaryTest, SecondaryBasic) { testSecondaryBasic<TestConfig>(); } -using LargeAllocator = scudo::MapAllocator<scudo::DefaultConfig>; +struct MapAllocatorTest : public Test { + using Config = scudo::DefaultConfig; + using LargeAllocator = scudo::MapAllocator<Config>; + + void SetUp() override { Allocator->init(nullptr); } + + void TearDown() override { Allocator->unmapTestOnly(); } + + std::unique_ptr<LargeAllocator> Allocator = + std::make_unique<LargeAllocator>(); + scudo::Options Options = getOptionsForConfig<Config>(); +}; // This exercises a variety of combinations of size and alignment for the // MapAllocator. The size computation done here mimic the ones done by the // combined allocator. -TEST(ScudoSecondaryTest, SecondaryCombinations) { +TEST_F(MapAllocatorTest, SecondaryCombinations) { constexpr scudo::uptr MinAlign = FIRST_32_SECOND_64(8, 16); constexpr scudo::uptr HeaderSize = scudo::roundUpTo(8, MinAlign); - std::unique_ptr<LargeAllocator> L(new LargeAllocator); - L->init(nullptr); for (scudo::uptr SizeLog = 0; SizeLog <= 20; SizeLog++) { for (scudo::uptr AlignLog = FIRST_32_SECOND_64(3, 4); AlignLog <= 16; AlignLog++) { @@ -100,100 +133,102 @@ TEST(ScudoSecondaryTest, SecondaryCombinations) { scudo::roundUpTo((1U << SizeLog) + Delta, MinAlign); const scudo::uptr Size = HeaderSize + UserSize + (Align > MinAlign ? Align - HeaderSize : 0); - void *P = L->allocate(scudo::Options{}, Size, Align); + void *P = Allocator->allocate(Options, Size, Align); EXPECT_NE(P, nullptr); void *AlignedP = reinterpret_cast<void *>( scudo::roundUpTo(reinterpret_cast<scudo::uptr>(P), Align)); memset(AlignedP, 0xff, UserSize); - L->deallocate(scudo::Options{}, P); + Allocator->deallocate(Options, P); } } } - scudo::ScopedString Str(1024); - L->getStats(&Str); + scudo::ScopedString Str; + Allocator->getStats(&Str); Str.output(); } -TEST(ScudoSecondaryTest, SecondaryIterate) { - std::unique_ptr<LargeAllocator> L(new LargeAllocator); - L->init(nullptr); +TEST_F(MapAllocatorTest, SecondaryIterate) { std::vector<void *> V; const scudo::uptr PageSize = scudo::getPageSizeCached(); for (scudo::uptr I = 0; I < 32U; I++) - V.push_back(L->allocate(scudo::Options{}, (std::rand() % 16) * PageSize)); + V.push_back(Allocator->allocate(Options, (std::rand() % 16) * PageSize)); auto Lambda = [V](scudo::uptr Block) { EXPECT_NE(std::find(V.begin(), V.end(), reinterpret_cast<void *>(Block)), V.end()); }; - L->disable(); - L->iterateOverBlocks(Lambda); - L->enable(); + Allocator->disable(); + Allocator->iterateOverBlocks(Lambda); + Allocator->enable(); while (!V.empty()) { - L->deallocate(scudo::Options{}, V.back()); + Allocator->deallocate(Options, V.back()); V.pop_back(); } - scudo::ScopedString Str(1024); - L->getStats(&Str); + scudo::ScopedString Str; + Allocator->getStats(&Str); Str.output(); } -TEST(ScudoSecondaryTest, SecondaryOptions) { - std::unique_ptr<LargeAllocator> L(new LargeAllocator); - L->init(nullptr); +TEST_F(MapAllocatorTest, SecondaryOptions) { // Attempt to set a maximum number of entries higher than the array size. - EXPECT_FALSE(L->setOption(scudo::Option::MaxCacheEntriesCount, 4096U)); + EXPECT_FALSE( + Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4096U)); // A negative number will be cast to a scudo::u32, and fail. - EXPECT_FALSE(L->setOption(scudo::Option::MaxCacheEntriesCount, -1)); - if (L->canCache(0U)) { + EXPECT_FALSE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, -1)); + if (Allocator->canCache(0U)) { // Various valid combinations. - EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntriesCount, 4U)); - EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20)); - EXPECT_TRUE(L->canCache(1UL << 18)); - EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 17)); - EXPECT_FALSE(L->canCache(1UL << 18)); - EXPECT_TRUE(L->canCache(1UL << 16)); - EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntriesCount, 0U)); - EXPECT_FALSE(L->canCache(1UL << 16)); - EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntriesCount, 4U)); - EXPECT_TRUE(L->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20)); - EXPECT_TRUE(L->canCache(1UL << 16)); + EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4U)); + EXPECT_TRUE( + Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20)); + EXPECT_TRUE(Allocator->canCache(1UL << 18)); + EXPECT_TRUE( + Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 17)); + EXPECT_FALSE(Allocator->canCache(1UL << 18)); + EXPECT_TRUE(Allocator->canCache(1UL << 16)); + EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 0U)); + EXPECT_FALSE(Allocator->canCache(1UL << 16)); + EXPECT_TRUE(Allocator->setOption(scudo::Option::MaxCacheEntriesCount, 4U)); + EXPECT_TRUE( + Allocator->setOption(scudo::Option::MaxCacheEntrySize, 1UL << 20)); + EXPECT_TRUE(Allocator->canCache(1UL << 16)); } } -static std::mutex Mutex; -static std::condition_variable Cv; -static bool Ready; +struct MapAllocatorWithReleaseTest : public MapAllocatorTest { + void SetUp() override { Allocator->init(nullptr, /*ReleaseToOsInterval=*/0); } -static void performAllocations(LargeAllocator *L) { - std::vector<void *> V; - const scudo::uptr PageSize = scudo::getPageSizeCached(); - { - std::unique_lock<std::mutex> Lock(Mutex); - while (!Ready) - Cv.wait(Lock); - } - for (scudo::uptr I = 0; I < 128U; I++) { - // Deallocate 75% of the blocks. - const bool Deallocate = (rand() & 3) != 0; - void *P = L->allocate(scudo::Options{}, (std::rand() % 16) * PageSize); - if (Deallocate) - L->deallocate(scudo::Options{}, P); - else - V.push_back(P); - } - while (!V.empty()) { - L->deallocate(scudo::Options{}, V.back()); - V.pop_back(); + void performAllocations() { + std::vector<void *> V; + const scudo::uptr PageSize = scudo::getPageSizeCached(); + { + std::unique_lock<std::mutex> Lock(Mutex); + while (!Ready) + Cv.wait(Lock); + } + for (scudo::uptr I = 0; I < 128U; I++) { + // Deallocate 75% of the blocks. + const bool Deallocate = (rand() & 3) != 0; + void *P = Allocator->allocate(Options, (std::rand() % 16) * PageSize); + if (Deallocate) + Allocator->deallocate(Options, P); + else + V.push_back(P); + } + while (!V.empty()) { + Allocator->deallocate(Options, V.back()); + V.pop_back(); + } } -} -TEST(ScudoSecondaryTest, SecondaryThreadsRace) { - Ready = false; - std::unique_ptr<LargeAllocator> L(new LargeAllocator); - L->init(nullptr, /*ReleaseToOsInterval=*/0); + std::mutex Mutex; + std::condition_variable Cv; + bool Ready = false; +}; + +TEST_F(MapAllocatorWithReleaseTest, SecondaryThreadsRace) { std::thread Threads[16]; for (scudo::uptr I = 0; I < ARRAY_SIZE(Threads); I++) - Threads[I] = std::thread(performAllocations, L.get()); + Threads[I] = + std::thread(&MapAllocatorWithReleaseTest::performAllocations, this); { std::unique_lock<std::mutex> Lock(Mutex); Ready = true; @@ -201,7 +236,7 @@ TEST(ScudoSecondaryTest, SecondaryThreadsRace) { } for (auto &T : Threads) T.join(); - scudo::ScopedString Str(1024); - L->getStats(&Str); + scudo::ScopedString Str; + Allocator->getStats(&Str); Str.output(); } |