summaryrefslogtreecommitdiffstats
path: root/standalone/tests/secondary_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'standalone/tests/secondary_test.cpp')
-rw-r--r--standalone/tests/secondary_test.cpp189
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();
}