Index: compiler-rt/lib/xray/tests/CMakeLists.txt =================================================================== --- compiler-rt/lib/xray/tests/CMakeLists.txt +++ compiler-rt/lib/xray/tests/CMakeLists.txt @@ -68,7 +68,11 @@ endfunction() set(XRAY_TEST_ARCH ${XRAY_SUPPORTED_ARCH}) -set(XRAY_UNITTEST_LINK_FLAGS ${CMAKE_THREAD_LIBS_INIT}) +set(XRAY_UNITTEST_LINK_FLAGS + ${CMAKE_THREAD_LIBS_INIT} + -fxray-instrument + -lstdc++ # TODO: Detect the correct standard library used! + ) if (NOT APPLE) append_list_if(COMPILER_RT_HAS_LIBM -lm XRAY_UNITTEST_LINK_FLAGS) append_list_if(COMPILER_RT_HAS_LIBRT -lrt XRAY_UNITTEST_LINK_FLAGS) @@ -94,7 +98,7 @@ RUNTIME "${XRAY_RUNTIME_LIBS}" DEPS gtest xray CFLAGS ${XRAY_UNITTEST_CFLAGS} - LINK_FLAGS ${TARGET_LINK_FLAGS} ${XRAY_UNITTEST_LINK_FLAGS} -lstdc++) + LINK_FLAGS ${TARGET_LINK_FLAGS} ${XRAY_UNITTEST_LINK_FLAGS}) set_target_properties(XRayUnitTests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) endforeach() Index: compiler-rt/lib/xray/tests/unit/function_call_trie_test.cc =================================================================== --- compiler-rt/lib/xray/tests/unit/function_call_trie_test.cc +++ compiler-rt/lib/xray/tests/unit/function_call_trie_test.cc @@ -61,6 +61,17 @@ ASSERT_TRUE(R.empty()); } +TEST(FunctionCallTrieTest, NoMatchingEntersForExit) { + auto A = FunctionCallTrie::InitAllocators(); + FunctionCallTrie Trie(A); + Trie.enterFunction(2, 1); + Trie.enterFunction(3, 3); + Trie.exitFunction(1, 5); + const auto &R = Trie.getRoots(); + + ASSERT_TRUE(R.empty()); +} + TEST(FunctionCallTrieTest, MissingFunctionExit) { auto A = FunctionCallTrie::InitAllocators(); FunctionCallTrie Trie(A); Index: compiler-rt/lib/xray/tests/unit/segmented_array_test.cc =================================================================== --- compiler-rt/lib/xray/tests/unit/segmented_array_test.cc +++ compiler-rt/lib/xray/tests/unit/segmented_array_test.cc @@ -107,32 +107,49 @@ } TEST(SegmentedArrayTest, IteratorTrimBehaviour) { - Array data; - ASSERT_TRUE(data.empty()); - auto I0Begin = data.begin(), I0End = data.end(); - // Add enough elements in data to have more than one chunk. - constexpr auto ChunkX2 = Array::ChunkSize * 2; + using AllocatorType = typename Array::AllocatorType; + AllocatorType A(1 << 10, 0); + Array 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 Chunk = Array::ChunkSize; + constexpr auto ChunkX2 = Chunk * 2; for (auto i = ChunkX2; i > 0u; --i) { - data.Append({static_cast(i), static_cast(i)}); + Data.AppendEmplace(static_cast(i), static_cast(i)); + } + ASSERT_EQ(Data.size(), ChunkX2); + { + auto &Back = Data.back(); + ASSERT_EQ(Back.First, 1); + ASSERT_EQ(Back.Second, 1); } - ASSERT_EQ(data.size(), ChunkX2); + // Trim one chunk's elements worth. - data.trim(ChunkX2 / 2); - ASSERT_EQ(data.size(), ChunkX2 / 2); + Data.trim(ChunkX2 / 2); + ASSERT_EQ(Data.size(), ChunkX2 / 2); + + // Check that we are still able to access 'back' properly. + { + auto &Back = Data.back(); + ASSERT_EQ(Back.First, static_cast(Chunk + 1)); + ASSERT_EQ(Back.Second, static_cast(Chunk + 1)); + } + // Then trim until it's empty. - data.trim(ChunkX2 / 2); - ASSERT_TRUE(data.empty()); + Data.trim(ChunkX2 / 2); + ASSERT_TRUE(Data.empty()); // Here our iterators should be the same. - auto I1Begin = data.begin(), I1End = data.end(); + 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 = ChunkX2; i > 0u; --i) { - data.Append({static_cast(i), static_cast(i)}); + Data.Append({static_cast(i), static_cast(i)}); } - EXPECT_EQ(data.size(), ChunkX2); + EXPECT_EQ(Data.size(), ChunkX2); } } // namespace Index: compiler-rt/lib/xray/xray_segmented_array.h =================================================================== --- compiler-rt/lib/xray/xray_segmented_array.h +++ compiler-rt/lib/xray/xray_segmented_array.h @@ -18,6 +18,7 @@ #include "sanitizer_common/sanitizer_allocator.h" #include "xray_allocator.h" +#include #include #include @@ -85,6 +86,7 @@ auto *FreeChunk = Freelist; Freelist = FreeChunk->Next; FreeChunk->Next = &SentinelChunk; + Freelist->Prev = &SentinelChunk; return FreeChunk; } @@ -96,6 +98,8 @@ if (C == nullptr) return nullptr; C->Block = Block; + C->Prev = &SentinelChunk; + C->Next = &SentinelChunk; return C; } @@ -116,31 +120,38 @@ auto Chunk = NewChunk(); if (Chunk == nullptr) return nullptr; - Chunk->Prev = &SentinelChunk; - Chunk->Next = &SentinelChunk; - Head = Chunk; - Tail = Chunk; - return Chunk; + DCHECK_EQ(Chunk->Next, &SentinelChunk); + DCHECK_EQ(Chunk->Prev, &SentinelChunk); + return Head = Tail = Chunk; } Chunk *AppendNewChunk() { auto Chunk = NewChunk(); if (Chunk == nullptr) return nullptr; + DCHECK_NE(Tail, &SentinelChunk); + DCHECK_EQ(Tail->Next, &SentinelChunk); + DCHECK_EQ(Chunk->Prev, &SentinelChunk); + DCHECK_EQ(Chunk->Next, &SentinelChunk); Tail->Next = Chunk; Chunk->Prev = Tail; - Chunk->Next = &SentinelChunk; - Tail = Chunk; - return Chunk; + return Tail = Chunk; } // This Iterator models a BidirectionalIterator. template class Iterator { - Chunk *C = nullptr; + Chunk *C = &SentinelChunk; size_t Offset = 0; + size_t Size = 0; public: - Iterator(Chunk *IC, size_t Off) : C(IC), Offset(Off) {} + Iterator(Chunk *IC, size_t Off, size_t S) : C(IC), Offset(Off), Size(S) {} + Iterator(const Iterator &) noexcept = default; + Iterator() noexcept = default; + Iterator(Iterator &&) noexcept = default; + Iterator &operator=(const Iterator &) = default; + Iterator &operator=(Iterator &&) = default; + ~Iterator() = default; Iterator &operator++() { if (++Offset % N) @@ -159,10 +170,21 @@ DCHECK_NE(C, &SentinelChunk); DCHECK_GT(Offset, 0); - // We check whether the offset was on a boundary before decrement, to see - // whether we need to retreat to the previous chunk. - if ((Offset-- % N) == 0) + auto PreviousOffset = Offset--; + + // We check whether the offset was on a boundary before decrement, to + // see whether we need to retreat to the previous chunk. Here we only + // move to the previous chunk IFF: + // + // - The offset after the decrement is greater than the chunk size (this + // means we're not on the "head" of the list). + // + // - The offset previously was at a chunk-size multiple, which means it + // was on the first element of the "current" chunk the iterator refers to. + if (Offset > N && PreviousOffset != Size && PreviousOffset % N == 0) { + DCHECK_NE(C->Prev, &SentinelChunk); C = C->Prev; + } return *this; } @@ -273,13 +295,13 @@ T &front() const { DCHECK_NE(Head, &SentinelChunk); DCHECK_NE(Size, 0u); - return *reinterpret_cast(Head->Block.Data); + return *begin(); } T &back() const { DCHECK_NE(Tail, &SentinelChunk); - auto Offset = (Size - 1) % N; - return *(reinterpret_cast(Tail->Block.Data) + Offset); + DCHECK_NE(Size, 0u); + return *--end(); } template T *find_element(Predicate P) const { @@ -297,7 +319,7 @@ /// Remove N Elements from the end. This leaves the blocks behind, and not /// require allocation of new blocks for new elements added after trimming. void trim(size_t Elements) { - DCHECK_LE(Elements, Size); + assert(Elements <= Size); Size -= Elements; FreeElements += Elements; @@ -307,6 +329,8 @@ // elements still in the array. auto ChunksToTrim = FreeElements / N; for (size_t i = 0; i < ChunksToTrim; ++i, FreeElements -= N) { + assert(Head != &SentinelChunk); + assert(Tail != &SentinelChunk); // Put the tail into the Freelist. auto *FreeChunk = Tail; Tail = Tail->Prev; @@ -315,16 +339,18 @@ else Tail->Next = &SentinelChunk; FreeChunk->Next = Freelist; - FreeChunk->Prev = Freelist->Prev; + FreeChunk->Prev = &SentinelChunk; + if (Freelist != &SentinelChunk) + Freelist->Prev = FreeChunk; Freelist = FreeChunk; } } // Provide iterators. - Iterator begin() const { return Iterator(Head, 0); } - Iterator end() const { return Iterator(Tail, Size); } - Iterator cbegin() const { return Iterator(Head, 0); } - Iterator cend() const { return Iterator(Tail, Size); } + Iterator begin() const { return Iterator(Head, 0, Size); } + Iterator end() const { return Iterator(Tail, Size, Size); } + Iterator cbegin() const { return Iterator(Head, 0, Size); } + Iterator cend() const { return Iterator(Tail, Size, Size); } }; // We need to have this storage definition out-of-line so that the compiler can