diff options
author | 2020-08-10 21:03:40 +0000 | |
---|---|---|
committer | 2020-08-10 21:03:40 +0000 | |
commit | 3cab2bb3f667058bece8e38b12449a63a9d73c4b (patch) | |
tree | 732ebd68e507f798225d97f4f2dd7ff8468d3e17 /gnu/llvm/compiler-rt/lib/xray/tests/unit/segmented_array_test.cpp | |
parent | Reduce log spam. (diff) | |
download | wireguard-openbsd-3cab2bb3f667058bece8e38b12449a63a9d73c4b.tar.xz wireguard-openbsd-3cab2bb3f667058bece8e38b12449a63a9d73c4b.zip |
Import compiler-rt 10.0.1 release.
ok kettenis@
Diffstat (limited to 'gnu/llvm/compiler-rt/lib/xray/tests/unit/segmented_array_test.cpp')
-rw-r--r-- | gnu/llvm/compiler-rt/lib/xray/tests/unit/segmented_array_test.cpp | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/gnu/llvm/compiler-rt/lib/xray/tests/unit/segmented_array_test.cpp b/gnu/llvm/compiler-rt/lib/xray/tests/unit/segmented_array_test.cpp new file mode 100644 index 00000000000..46aeb88f71b --- /dev/null +++ b/gnu/llvm/compiler-rt/lib/xray/tests/unit/segmented_array_test.cpp @@ -0,0 +1,349 @@ +#include "test_helpers.h" +#include "xray_segmented_array.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include <algorithm> +#include <numeric> +#include <vector> + +namespace __xray { +namespace { + +using ::testing::SizeIs; + +struct TestData { + s64 First; + s64 Second; + + // Need a constructor for emplace operations. + TestData(s64 F, s64 S) : First(F), Second(S) {} +}; + +void PrintTo(const TestData &D, std::ostream *OS) { + *OS << "{ " << D.First << ", " << D.Second << " }"; +} + +TEST(SegmentedArrayTest, ConstructWithAllocators) { + using AllocatorType = typename Array<TestData>::AllocatorType; + AllocatorType A(1 << 4); + Array<TestData> Data(A); + (void)Data; +} + +TEST(SegmentedArrayTest, ConstructAndPopulate) { + using AllocatorType = typename Array<TestData>::AllocatorType; + AllocatorType A(1 << 4); + Array<TestData> data(A); + ASSERT_NE(data.Append(TestData{0, 0}), nullptr); + ASSERT_NE(data.Append(TestData{1, 1}), nullptr); + ASSERT_EQ(data.size(), 2u); +} + +TEST(SegmentedArrayTest, ConstructPopulateAndLookup) { + using AllocatorType = typename Array<TestData>::AllocatorType; + AllocatorType A(1 << 4); + Array<TestData> data(A); + ASSERT_NE(data.Append(TestData{0, 1}), nullptr); + ASSERT_EQ(data.size(), 1u); + ASSERT_EQ(data[0].First, 0); + ASSERT_EQ(data[0].Second, 1); +} + +TEST(SegmentedArrayTest, PopulateWithMoreElements) { + using AllocatorType = typename Array<TestData>::AllocatorType; + AllocatorType A(1 << 24); + Array<TestData> data(A); + static const auto kMaxElements = 100u; + for (auto I = 0u; I < kMaxElements; ++I) { + ASSERT_NE(data.Append(TestData{I, I + 1}), nullptr); + } + ASSERT_EQ(data.size(), kMaxElements); + for (auto I = 0u; I < kMaxElements; ++I) { + ASSERT_EQ(data[I].First, I); + ASSERT_EQ(data[I].Second, I + 1); + } +} + +TEST(SegmentedArrayTest, AppendEmplace) { + using AllocatorType = typename Array<TestData>::AllocatorType; + AllocatorType A(1 << 4); + Array<TestData> data(A); + ASSERT_NE(data.AppendEmplace(1, 1), nullptr); + ASSERT_EQ(data[0].First, 1); + ASSERT_EQ(data[0].Second, 1); +} + +TEST(SegmentedArrayTest, AppendAndTrim) { + using AllocatorType = typename Array<TestData>::AllocatorType; + AllocatorType A(1 << 4); + Array<TestData> data(A); + ASSERT_NE(data.AppendEmplace(1, 1), nullptr); + ASSERT_EQ(data.size(), 1u); + data.trim(1); + ASSERT_EQ(data.size(), 0u); + ASSERT_TRUE(data.empty()); +} + +TEST(SegmentedArrayTest, IteratorAdvance) { + using AllocatorType = typename Array<TestData>::AllocatorType; + AllocatorType A(1 << 4); + Array<TestData> data(A); + ASSERT_TRUE(data.empty()); + ASSERT_EQ(data.begin(), data.end()); + auto I0 = data.begin(); + ASSERT_EQ(I0++, data.begin()); + ASSERT_NE(I0, data.begin()); + for (const auto &D : data) { + (void)D; + FAIL(); + } + ASSERT_NE(data.AppendEmplace(1, 1), nullptr); + ASSERT_EQ(data.size(), 1u); + ASSERT_NE(data.begin(), data.end()); + auto &D0 = *data.begin(); + ASSERT_EQ(D0.First, 1); + ASSERT_EQ(D0.Second, 1); +} + +TEST(SegmentedArrayTest, IteratorRetreat) { + using AllocatorType = typename Array<TestData>::AllocatorType; + AllocatorType A(1 << 4); + Array<TestData> data(A); + ASSERT_TRUE(data.empty()); + ASSERT_EQ(data.begin(), data.end()); + ASSERT_NE(data.AppendEmplace(1, 1), nullptr); + ASSERT_EQ(data.size(), 1u); + ASSERT_NE(data.begin(), data.end()); + auto &D0 = *data.begin(); + ASSERT_EQ(D0.First, 1); + ASSERT_EQ(D0.Second, 1); + + auto I0 = data.end(); + ASSERT_EQ(I0--, data.end()); + ASSERT_NE(I0, data.end()); + ASSERT_EQ(I0, data.begin()); + ASSERT_EQ(I0->First, 1); + ASSERT_EQ(I0->Second, 1); +} + +TEST(SegmentedArrayTest, IteratorTrimBehaviour) { + using AllocatorType = typename Array<TestData>::AllocatorType; + AllocatorType A(1 << 20); + Array<TestData> Data(A); + ASSERT_TRUE(Data.empty()); + auto I0Begin = Data.begin(), I0End = Data.end(); + // Add enough elements in Data to have more than one chunk. + constexpr auto Segment = Array<TestData>::SegmentSize; + constexpr auto SegmentX2 = Segment * 2; + for (auto i = SegmentX2; i > 0u; --i) { + Data.AppendEmplace(static_cast<s64>(i), static_cast<s64>(i)); + } + ASSERT_EQ(Data.size(), SegmentX2); + { + auto &Back = Data.back(); + ASSERT_EQ(Back.First, 1); + ASSERT_EQ(Back.Second, 1); + } + + // Trim one chunk's elements worth. + Data.trim(Segment); + ASSERT_EQ(Data.size(), Segment); + + // Check that we are still able to access 'back' properly. + { + auto &Back = Data.back(); + ASSERT_EQ(Back.First, static_cast<s64>(Segment + 1)); + ASSERT_EQ(Back.Second, static_cast<s64>(Segment + 1)); + } + + // Then trim until it's empty. + Data.trim(Segment); + ASSERT_TRUE(Data.empty()); + + // Here our iterators should be the same. + auto I1Begin = Data.begin(), I1End = Data.end(); + EXPECT_EQ(I0Begin, I1Begin); + EXPECT_EQ(I0End, I1End); + + // Then we ensure that adding elements back works just fine. + for (auto i = SegmentX2; i > 0u; --i) { + Data.AppendEmplace(static_cast<s64>(i), static_cast<s64>(i)); + } + EXPECT_EQ(Data.size(), SegmentX2); +} + +TEST(SegmentedArrayTest, HandleExhaustedAllocator) { + using AllocatorType = typename Array<TestData>::AllocatorType; + constexpr auto Segment = Array<TestData>::SegmentSize; + constexpr auto MaxElements = Array<TestData>::ElementsPerSegment; + AllocatorType A(Segment); + Array<TestData> Data(A); + for (auto i = MaxElements; i > 0u; --i) + EXPECT_NE(Data.AppendEmplace(static_cast<s64>(i), static_cast<s64>(i)), + nullptr); + EXPECT_EQ(Data.AppendEmplace(0, 0), nullptr); + EXPECT_THAT(Data, SizeIs(MaxElements)); + + // Trimming more elements than there are in the container should be fine. + Data.trim(MaxElements + 1); + EXPECT_THAT(Data, SizeIs(0u)); +} + +struct ShadowStackEntry { + uint64_t EntryTSC = 0; + uint64_t *NodePtr = nullptr; + ShadowStackEntry(uint64_t T, uint64_t *N) : EntryTSC(T), NodePtr(N) {} +}; + +TEST(SegmentedArrayTest, SimulateStackBehaviour) { + using AllocatorType = typename Array<ShadowStackEntry>::AllocatorType; + AllocatorType A(1 << 10); + Array<ShadowStackEntry> Data(A); + static uint64_t Dummy = 0; + constexpr uint64_t Max = 9; + + for (uint64_t i = 0; i < Max; ++i) { + auto P = Data.Append({i, &Dummy}); + ASSERT_NE(P, nullptr); + ASSERT_EQ(P->NodePtr, &Dummy); + auto &Back = Data.back(); + ASSERT_EQ(Back.NodePtr, &Dummy); + ASSERT_EQ(Back.EntryTSC, i); + } + + // Simulate a stack by checking the data from the end as we're trimming. + auto Counter = Max; + ASSERT_EQ(Data.size(), size_t(Max)); + while (!Data.empty()) { + const auto &Top = Data.back(); + uint64_t *TopNode = Top.NodePtr; + EXPECT_EQ(TopNode, &Dummy) << "Counter = " << Counter; + Data.trim(1); + --Counter; + ASSERT_EQ(Data.size(), size_t(Counter)); + } +} + +TEST(SegmentedArrayTest, PlacementNewOnAlignedStorage) { + using AllocatorType = typename Array<ShadowStackEntry>::AllocatorType; + typename std::aligned_storage<sizeof(AllocatorType), + alignof(AllocatorType)>::type AllocatorStorage; + new (&AllocatorStorage) AllocatorType(1 << 10); + auto *A = reinterpret_cast<AllocatorType *>(&AllocatorStorage); + typename std::aligned_storage<sizeof(Array<ShadowStackEntry>), + alignof(Array<ShadowStackEntry>)>::type + ArrayStorage; + new (&ArrayStorage) Array<ShadowStackEntry>(*A); + auto *Data = reinterpret_cast<Array<ShadowStackEntry> *>(&ArrayStorage); + + static uint64_t Dummy = 0; + constexpr uint64_t Max = 9; + + for (uint64_t i = 0; i < Max; ++i) { + auto P = Data->Append({i, &Dummy}); + ASSERT_NE(P, nullptr); + ASSERT_EQ(P->NodePtr, &Dummy); + auto &Back = Data->back(); + ASSERT_EQ(Back.NodePtr, &Dummy); + ASSERT_EQ(Back.EntryTSC, i); + } + + // Simulate a stack by checking the data from the end as we're trimming. + auto Counter = Max; + ASSERT_EQ(Data->size(), size_t(Max)); + while (!Data->empty()) { + const auto &Top = Data->back(); + uint64_t *TopNode = Top.NodePtr; + EXPECT_EQ(TopNode, &Dummy) << "Counter = " << Counter; + Data->trim(1); + --Counter; + ASSERT_EQ(Data->size(), size_t(Counter)); + } + + // Once the stack is exhausted, we re-use the storage. + for (uint64_t i = 0; i < Max; ++i) { + auto P = Data->Append({i, &Dummy}); + ASSERT_NE(P, nullptr); + ASSERT_EQ(P->NodePtr, &Dummy); + auto &Back = Data->back(); + ASSERT_EQ(Back.NodePtr, &Dummy); + ASSERT_EQ(Back.EntryTSC, i); + } + + // We re-initialize the storage, by calling the destructor and + // placement-new'ing again. + Data->~Array(); + A->~AllocatorType(); + new (A) AllocatorType(1 << 10); + new (Data) Array<ShadowStackEntry>(*A); + + // Then re-do the test. + for (uint64_t i = 0; i < Max; ++i) { + auto P = Data->Append({i, &Dummy}); + ASSERT_NE(P, nullptr); + ASSERT_EQ(P->NodePtr, &Dummy); + auto &Back = Data->back(); + ASSERT_EQ(Back.NodePtr, &Dummy); + ASSERT_EQ(Back.EntryTSC, i); + } + + // Simulate a stack by checking the data from the end as we're trimming. + Counter = Max; + ASSERT_EQ(Data->size(), size_t(Max)); + while (!Data->empty()) { + const auto &Top = Data->back(); + uint64_t *TopNode = Top.NodePtr; + EXPECT_EQ(TopNode, &Dummy) << "Counter = " << Counter; + Data->trim(1); + --Counter; + ASSERT_EQ(Data->size(), size_t(Counter)); + } + + // Once the stack is exhausted, we re-use the storage. + for (uint64_t i = 0; i < Max; ++i) { + auto P = Data->Append({i, &Dummy}); + ASSERT_NE(P, nullptr); + ASSERT_EQ(P->NodePtr, &Dummy); + auto &Back = Data->back(); + ASSERT_EQ(Back.NodePtr, &Dummy); + ASSERT_EQ(Back.EntryTSC, i); + } +} + +TEST(SegmentedArrayTest, ArrayOfPointersIteratorAccess) { + using PtrArray = Array<int *>; + PtrArray::AllocatorType Alloc(16384); + Array<int *> A(Alloc); + static constexpr size_t Count = 100; + std::vector<int> Integers(Count); + std::iota(Integers.begin(), Integers.end(), 0); + for (auto &I : Integers) + ASSERT_NE(A.Append(&I), nullptr); + int V = 0; + ASSERT_EQ(A.size(), Count); + for (auto P : A) { + ASSERT_NE(P, nullptr); + ASSERT_EQ(*P, V++); + } +} + +TEST(SegmentedArrayTest, ArrayOfPointersIteratorAccessExhaustion) { + using PtrArray = Array<int *>; + PtrArray::AllocatorType Alloc(4096); + Array<int *> A(Alloc); + static constexpr size_t Count = 1000; + std::vector<int> Integers(Count); + std::iota(Integers.begin(), Integers.end(), 0); + for (auto &I : Integers) + if (A.Append(&I) == nullptr) + break; + int V = 0; + ASSERT_LT(A.size(), Count); + for (auto P : A) { + ASSERT_NE(P, nullptr); + ASSERT_EQ(*P, V++); + } +} + +} // namespace +} // namespace __xray |