diff options
author | Dean Michael Berris <dberris@google.com> | 2018-10-15 02:57:06 +0000 |
---|---|---|
committer | Dean Michael Berris <dberris@google.com> | 2018-10-15 02:57:06 +0000 |
commit | 3c01508409d3e9a7e43950318b626e2bd1bf7e78 (patch) | |
tree | f68110bd424d7adb2a28b98baaa234ca667c6270 /compiler-rt/lib/xray/tests | |
parent | 06aea1720a632634cd4bd0a718042e8507579f1d (diff) | |
download | bcm5719-llvm-3c01508409d3e9a7e43950318b626e2bd1bf7e78.tar.gz bcm5719-llvm-3c01508409d3e9a7e43950318b626e2bd1bf7e78.zip |
[XRay][compiler-rt] FDR Mode Controller
Summary:
This change implements a controller for abstracting away the details of
what happens when tracing with FDR mode. This controller type allows us
to test in isolation the various cases where we're encountering function
entry, exit, and other kinds of events we are handling when FDR mode is
enabled.
This change introduces a number of testing facilities we've needed to
better support expressing the conditions we need for the unit tests. We
leave some TODOs for moving those utilities into the LLVM project,
sitting in the `Testing` library, to make matching conditions on XRay
`Trace` instances through googlemock more manageable and declarative.
We don't wire in the controller right away, to allow us to incrementally
update the implementation(s) as we increase testing coverage of the
controller type. There's a need to re-think the way we're managing
buffers in a multi-threaded environment, which is more invasive than
this implementation.
This step in the process allows us to encode our assumptions in the
implementation of the controller, and then evolve the buffer queue
implementation to support generational buffer management to ensure we
can continue to support the cases we're already supporting with the
controller.
Reviewers: mboerger, eizan
Subscribers: mgorny, llvm-commits, jfb
Differential Revision: https://reviews.llvm.org/D52588
llvm-svn: 344488
Diffstat (limited to 'compiler-rt/lib/xray/tests')
-rw-r--r-- | compiler-rt/lib/xray/tests/unit/CMakeLists.txt | 8 | ||||
-rw-r--r-- | compiler-rt/lib/xray/tests/unit/fdr_controller_test.cc | 242 | ||||
-rw-r--r-- | compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cc | 70 | ||||
-rw-r--r-- | compiler-rt/lib/xray/tests/unit/test_helpers.cc | 91 | ||||
-rw-r--r-- | compiler-rt/lib/xray/tests/unit/test_helpers.h | 59 |
5 files changed, 441 insertions, 29 deletions
diff --git a/compiler-rt/lib/xray/tests/unit/CMakeLists.txt b/compiler-rt/lib/xray/tests/unit/CMakeLists.txt index d0ead947df2..42ea43750e3 100644 --- a/compiler-rt/lib/xray/tests/unit/CMakeLists.txt +++ b/compiler-rt/lib/xray/tests/unit/CMakeLists.txt @@ -1,8 +1,10 @@ add_xray_unittest(XRayTest SOURCES - buffer_queue_test.cc allocator_test.cc - segmented_array_test.cc + buffer_queue_test.cc + fdr_controller_test.cc + fdr_log_writer_test.cc function_call_trie_test.cc profile_collector_test.cc - fdr_log_writer_test.cc + segmented_array_test.cc + test_helpers.cc xray_unit_test_main.cc) diff --git a/compiler-rt/lib/xray/tests/unit/fdr_controller_test.cc b/compiler-rt/lib/xray/tests/unit/fdr_controller_test.cc new file mode 100644 index 00000000000..73666a76b3f --- /dev/null +++ b/compiler-rt/lib/xray/tests/unit/fdr_controller_test.cc @@ -0,0 +1,242 @@ +//===-- fdr_controller_test.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of XRay, a function call tracing system. +// +//===----------------------------------------------------------------------===// +#include <algorithm> +#include <memory> +#include <time.h> + +#include "test_helpers.h" +#include "xray/xray_records.h" +#include "xray_buffer_queue.h" +#include "xray_fdr_controller.h" +#include "xray_fdr_log_writer.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Testing/Support/Error.h" +#include "llvm/XRay/Trace.h" +#include "llvm/XRay/XRayRecord.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace __xray { +namespace { + +using ::llvm::HasValue; +using ::llvm::xray::testing::FuncId; +using ::llvm::xray::testing::HasArg; +using ::llvm::xray::testing::RecordType; +using ::testing::AllOf; +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::Field; +using ::testing::IsEmpty; +using ::testing::SizeIs; + +class FunctionSequenceTest : public ::testing::Test { +protected: + BufferQueue::Buffer B{}; + std::unique_ptr<BufferQueue> BQ; + std::unique_ptr<FDRLogWriter> W; + std::unique_ptr<FDRController<>> C; + +public: + void SetUp() override { + bool Success; + BQ = llvm::make_unique<BufferQueue>(4096, 1, Success); + ASSERT_TRUE(Success); + ASSERT_EQ(BQ->getBuffer(B), BufferQueue::ErrorCode::Ok); + W = llvm::make_unique<FDRLogWriter>(B); + C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 0); + } +}; + +TEST_F(FunctionSequenceTest, DefaultInitFinalizeFlush) { + ASSERT_TRUE(C->functionEnter(1, 2, 3)); + ASSERT_TRUE(C->functionExit(1, 2, 3)); + ASSERT_TRUE(C->flush()); + ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok); + + // Serialize the buffers then test to see we find the expected records. + std::string Serialized = serialize(*BQ, 3); + llvm::DataExtractor DE(Serialized, true, 8); + auto TraceOrErr = llvm::xray::loadTrace(DE); + EXPECT_THAT_EXPECTED( + TraceOrErr, + HasValue(ElementsAre( + AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)), + AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT))))); +} + +TEST_F(FunctionSequenceTest, ThresholdsAreEnforced) { + C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000); + ASSERT_TRUE(C->functionEnter(1, 2, 3)); + ASSERT_TRUE(C->functionExit(1, 2, 3)); + ASSERT_TRUE(C->flush()); + ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok); + + // Serialize the buffers then test to see we find the *no* records, because + // the function entry-exit comes under the cycle threshold. + std::string Serialized = serialize(*BQ, 3); + llvm::DataExtractor DE(Serialized, true, 8); + auto TraceOrErr = llvm::xray::loadTrace(DE); + EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty())); +} + +TEST_F(FunctionSequenceTest, ArgsAreHandledAndKept) { + C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000); + ASSERT_TRUE(C->functionEnterArg(1, 2, 3, 4)); + ASSERT_TRUE(C->functionExit(1, 2, 3)); + ASSERT_TRUE(C->flush()); + ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok); + + // Serialize the buffers then test to see we find the function enter arg + // record with the specified argument. + std::string Serialized = serialize(*BQ, 3); + llvm::DataExtractor DE(Serialized, true, 8); + auto TraceOrErr = llvm::xray::loadTrace(DE); + EXPECT_THAT_EXPECTED( + TraceOrErr, + HasValue(ElementsAre( + AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER_ARG), + HasArg(4)), + AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT))))); +} + +TEST_F(FunctionSequenceTest, RewindingMultipleCalls) { + C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000); + + // First we construct an arbitrarily deep function enter/call stack. + // We also ensure that we are in the same CPU. + uint64_t TSC = 1; + uint16_t CPU = 1; + ASSERT_TRUE(C->functionEnter(1, TSC++, CPU)); + ASSERT_TRUE(C->functionEnter(2, TSC++, CPU)); + ASSERT_TRUE(C->functionEnter(3, TSC++, CPU)); + + // Then we exit them one at a time, in reverse order of entry. + ASSERT_TRUE(C->functionExit(3, TSC++, CPU)); + ASSERT_TRUE(C->functionExit(2, TSC++, CPU)); + ASSERT_TRUE(C->functionExit(1, TSC++, CPU)); + + ASSERT_TRUE(C->flush()); + ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok); + + // Serialize the buffers then test to see we find that all the calls have been + // unwound because all of them are under the cycle counter threshold. + std::string Serialized = serialize(*BQ, 3); + llvm::DataExtractor DE(Serialized, true, 8); + auto TraceOrErr = llvm::xray::loadTrace(DE); + EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty())); +} + +TEST_F(FunctionSequenceTest, RewindingIntermediaryTailExits) { + C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000); + + // First we construct an arbitrarily deep function enter/call stack. + // We also ensure that we are in the same CPU. + uint64_t TSC = 1; + uint16_t CPU = 1; + ASSERT_TRUE(C->functionEnter(1, TSC++, CPU)); + ASSERT_TRUE(C->functionEnter(2, TSC++, CPU)); + ASSERT_TRUE(C->functionEnter(3, TSC++, CPU)); + + // Next we tail-exit into a new function multiple times. + ASSERT_TRUE(C->functionTailExit(3, TSC++, CPU)); + ASSERT_TRUE(C->functionEnter(4, TSC++, CPU)); + ASSERT_TRUE(C->functionTailExit(4, TSC++, CPU)); + ASSERT_TRUE(C->functionEnter(5, TSC++, CPU)); + ASSERT_TRUE(C->functionTailExit(5, TSC++, CPU)); + ASSERT_TRUE(C->functionEnter(6, TSC++, CPU)); + + // Then we exit them one at a time, in reverse order of entry. + ASSERT_TRUE(C->functionExit(6, TSC++, CPU)); + ASSERT_TRUE(C->functionExit(2, TSC++, CPU)); + ASSERT_TRUE(C->functionExit(1, TSC++, CPU)); + ASSERT_TRUE(C->flush()); + ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok); + + // Serialize the buffers then test to see we find that all the calls have been + // unwound because all of them are under the cycle counter threshold. + std::string Serialized = serialize(*BQ, 3); + llvm::DataExtractor DE(Serialized, true, 8); + auto TraceOrErr = llvm::xray::loadTrace(DE); + EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty())); +} + +class BufferManagementTest : public ::testing::Test { +protected: + BufferQueue::Buffer B{}; + std::unique_ptr<BufferQueue> BQ; + std::unique_ptr<FDRLogWriter> W; + std::unique_ptr<FDRController<>> C; + + static constexpr size_t kBuffers = 10; + +public: + void SetUp() override { + bool Success; + BQ = llvm::make_unique<BufferQueue>(sizeof(MetadataRecord) * 4 + + sizeof(FunctionRecord) * 2, + kBuffers, Success); + ASSERT_TRUE(Success); + ASSERT_EQ(BQ->getBuffer(B), BufferQueue::ErrorCode::Ok); + W = llvm::make_unique<FDRLogWriter>(B); + C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 0); + } +}; + +constexpr size_t BufferManagementTest::kBuffers; + +TEST_F(BufferManagementTest, HandlesOverflow) { + uint64_t TSC = 1; + uint16_t CPU = 1; + for (size_t I = 0; I < kBuffers; ++I) { + ASSERT_TRUE(C->functionEnter(1, TSC++, CPU)); + ASSERT_TRUE(C->functionExit(1, TSC++, CPU)); + } + C->flush(); + ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok); + + std::string Serialized = serialize(*BQ, 3); + llvm::DataExtractor DE(Serialized, true, 8); + auto TraceOrErr = llvm::xray::loadTrace(DE); + EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(SizeIs(kBuffers * 2))); +} + +TEST_F(BufferManagementTest, HandlesFinalizedBufferQueue) { + uint64_t TSC = 1; + uint16_t CPU = 1; + + // First write one function entry. + ASSERT_TRUE(C->functionEnter(1, TSC++, CPU)); + + // Then we finalize the buffer queue, simulating the case where the logging + // has been finalized. + ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok); + + // At this point further calls to the controller must fail. + ASSERT_FALSE(C->functionExit(1, TSC++, CPU)); + + // But flushing should succeed. + ASSERT_TRUE(C->flush()); + + // We expect that we'll only be able to find the function enter event, but not + // the function exit event. + std::string Serialized = serialize(*BQ, 3); + llvm::DataExtractor DE(Serialized, true, 8); + auto TraceOrErr = llvm::xray::loadTrace(DE); + EXPECT_THAT_EXPECTED( + TraceOrErr, HasValue(ElementsAre(AllOf( + FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER))))); +} + +} // namespace +} // namespace __xray diff --git a/compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cc b/compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cc index 3a2138cd821..1aeaf099d32 100644 --- a/compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cc +++ b/compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cc @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include <time.h> +#include "test_helpers.h" #include "xray/xray_records.h" #include "xray_fdr_log_writer.h" #include "llvm/Support/DataExtractor.h" @@ -26,8 +27,12 @@ namespace { static constexpr size_t kSize = 4096; using ::llvm::HasValue; +using ::llvm::xray::testing::FuncId; +using ::llvm::xray::testing::RecordType; using ::testing::Eq; -using ::testing::SizeIs; +using ::testing::AllOf; +using ::testing::IsEmpty; +using ::testing::ElementsAre; // Exercise the common code path where we initialize a buffer and are able to // write some records successfully. @@ -58,34 +63,47 @@ TEST(FdrLogWriterTest, WriteSomeRecords) { // We then need to go through each element of the Buffers, and re-create a // flat buffer that we would see if they were laid out in a file. This also // means we need to write out the header manually. - // TODO: Isolate the file header writing. - std::string Serialized; - std::aligned_storage<sizeof(XRayFileHeader), alignof(XRayFileHeader)>::type - HeaderStorage; - auto *Header = reinterpret_cast<XRayFileHeader *>(&HeaderStorage); - new (Header) XRayFileHeader(); - Header->Version = 3; - Header->Type = FileTypes::FDR_LOG; - Header->CycleFrequency = 3e9; - Header->ConstantTSC = 1; - Header->NonstopTSC = 1; - Serialized.append(reinterpret_cast<const char *>(&HeaderStorage), - sizeof(XRayFileHeader)); - size_t BufferCount = 0; - Buffers.apply([&](const BufferQueue::Buffer &B) { - ++BufferCount; - auto Size = atomic_load_relaxed(&B.Extents); - auto Extents = - createMetadataRecord<MetadataRecord::RecordKinds::BufferExtents>(Size); - Serialized.append(reinterpret_cast<const char *>(&Extents), - sizeof(Extents)); - Serialized.append(reinterpret_cast<const char *>(B.Data), Size); - }); - ASSERT_EQ(BufferCount, 1u); + std::string Serialized = serialize(Buffers, 3); + llvm::DataExtractor DE(Serialized, true, 8); + auto TraceOrErr = llvm::xray::loadTrace(DE); + EXPECT_THAT_EXPECTED( + TraceOrErr, + HasValue(ElementsAre( + AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)), + AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT))))); +} + +TEST(FdrLogWriterTest, UnwriteRecords) { + bool Success = false; + BufferQueue Buffers(kSize, 1, Success); + BufferQueue::Buffer B; + ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok); + + FDRLogWriter Writer(B); + MetadataRecord Preamble[] = { + createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(int32_t{1}), + createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>( + int64_t{1}, int32_t{2}), + createMetadataRecord<MetadataRecord::RecordKinds::Pid>(int32_t{1}), + }; + ASSERT_THAT(Writer.writeMetadataRecords(Preamble), + Eq(sizeof(MetadataRecord) * 3)); + ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(1)); + ASSERT_TRUE( + Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, 1)); + ASSERT_TRUE( + Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, 1, 1)); + Writer.undoWrites(sizeof(FunctionRecord) * 2); + ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok); + ASSERT_EQ(B.Data, nullptr); + ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok); + // We've un-done the two function records we've written, and now we expect + // that we don't have any function records in the trace. + std::string Serialized = serialize(Buffers, 3); llvm::DataExtractor DE(Serialized, true, 8); auto TraceOrErr = llvm::xray::loadTrace(DE); - EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(SizeIs(2))); + EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty())); } } // namespace diff --git a/compiler-rt/lib/xray/tests/unit/test_helpers.cc b/compiler-rt/lib/xray/tests/unit/test_helpers.cc new file mode 100644 index 00000000000..ebc40f88cde --- /dev/null +++ b/compiler-rt/lib/xray/tests/unit/test_helpers.cc @@ -0,0 +1,91 @@ +//===-- test_helpers.cc ---------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of XRay, a function call tracing system. +// +//===----------------------------------------------------------------------===// +#include "test_helpers.h" +#include "xray/xray_records.h" +#include "xray_buffer_queue.h" +#include "xray_fdr_log_writer.h" +#include <type_traits> + +// TODO: Move these to llvm/include/Testing/XRay/... +namespace llvm { +namespace xray { + +std::string RecordTypeAsString(RecordTypes T) { + switch (T) { + case RecordTypes::ENTER: + return "llvm::xray::RecordTypes::ENTER"; + case RecordTypes::EXIT: + return "llvm::xray::RecordTypes::EXIT"; + case RecordTypes::TAIL_EXIT: + return "llvm::xray::RecordTypes::TAIL_EXIT"; + case RecordTypes::ENTER_ARG: + return "llvm::xray::RecordTypes::ENTER_ARG"; + } + return "<UNKNOWN>"; +} + +void PrintTo(RecordTypes T, std::ostream *OS) { + *OS << RecordTypeAsString(T); +} + +void PrintTo(const XRayRecord &R, std::ostream *OS) { + *OS << "XRayRecord { CPU = " << R.CPU + << "; Type = " << RecordTypeAsString(R.Type) << "; FuncId = " << R.FuncId + << "; TSC = " << R.TSC << "; TId = " << R.TId << "; PId = " << R.PId + << " Args = " << ::testing::PrintToString(R.CallArgs) << " }"; +} + +void PrintTo(const Trace &T, std::ostream *OS) { + const auto &H = T.getFileHeader(); + *OS << "XRay Trace:\nHeader: { Version = " << H.Version + << "; Type = " << H.Type + << "; ConstantTSC = " << ::testing::PrintToString(H.ConstantTSC) + << "; NonstopTSC = " << ::testing::PrintToString(H.NonstopTSC) + << "; CycleFrequency = " << H.CycleFrequency << "; FreeFormData = '" + << ::testing::PrintToString(H.FreeFormData) << "' }\n"; + for (const auto &R : T) { + PrintTo(R, OS); + *OS << "\n"; + } +} + +} // namespace xray +} // namespace llvm + +namespace __xray { + +std::string serialize(BufferQueue &Buffers, int32_t Version) { + std::string Serialized; + std::aligned_storage<sizeof(XRayFileHeader), alignof(XRayFileHeader)>::type + HeaderStorage; + auto *Header = reinterpret_cast<XRayFileHeader *>(&HeaderStorage); + new (Header) XRayFileHeader(); + Header->Version = Version; + Header->Type = FileTypes::FDR_LOG; + Header->CycleFrequency = 3e9; + Header->ConstantTSC = 1; + Header->NonstopTSC = 1; + Serialized.append(reinterpret_cast<const char *>(&HeaderStorage), + sizeof(XRayFileHeader)); + Buffers.apply([&](const BufferQueue::Buffer &B) { + auto Size = atomic_load_relaxed(&B.Extents); + auto Extents = + createMetadataRecord<MetadataRecord::RecordKinds::BufferExtents>(Size); + Serialized.append(reinterpret_cast<const char *>(&Extents), + sizeof(Extents)); + Serialized.append(reinterpret_cast<const char *>(B.Data), Size); + }); + return Serialized; +} + +} // namespace __xray diff --git a/compiler-rt/lib/xray/tests/unit/test_helpers.h b/compiler-rt/lib/xray/tests/unit/test_helpers.h new file mode 100644 index 00000000000..6eca7795e31 --- /dev/null +++ b/compiler-rt/lib/xray/tests/unit/test_helpers.h @@ -0,0 +1,59 @@ +//===-- test_helpers.h ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of XRay, a function call tracing system. +// +//===----------------------------------------------------------------------===// +#ifndef COMPILER_RT_LIB_XRAY_TESTS_TEST_HELPERS_H_ +#define COMPILER_RT_LIB_XRAY_TESTS_TEST_HELPERS_H_ + +#include "xray_buffer_queue.h" +#include "llvm/XRay/XRayRecord.h" +#include "llvm/XRay/Trace.h" +#include "gmock/gmock.h" + +// TODO: Move these to llvm/include/Testing/XRay/... +namespace llvm { +namespace xray { + +std::string RecordTypeAsString(RecordTypes T); +void PrintTo(RecordTypes T, std::ostream *OS); +void PrintTo(const XRayRecord &R, std::ostream *OS); +void PrintTo(const Trace &T, std::ostream *OS); + +namespace testing { + +MATCHER_P(FuncId, F, "") { + *result_listener << "where the function id is " << F; + return arg.FuncId == F; +} + +MATCHER_P(RecordType, T, "") { + *result_listener << "where the record type is " << RecordTypeAsString(T); + return arg.Type == T; +} + +MATCHER_P(HasArg, A, "") { + *result_listener << "where args contains " << A; + return !arg.CallArgs.empty() && + std::any_of(arg.CallArgs.begin(), arg.CallArgs.end(), + [this](decltype(A) V) { return V == A; }); +} + +} // namespace testing +} // namespace xray +} // namespace llvm + +namespace __xray { + +std::string serialize(BufferQueue &Buffers, int32_t Version); + +} // namespace __xray + +#endif // COMPILER_RT_LIB_XRAY_TESTS_TEST_HELPERS_H_ |