//===- llvm/unittest/XRay/FDRProducerConsumerTest.cpp -----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Test for round-trip record writing and reading. // //===----------------------------------------------------------------------===// #include "llvm/Support/DataExtractor.h" #include "llvm/Support/raw_ostream.h" #include "llvm/XRay/FDRLogBuilder.h" #include "llvm/XRay/FDRRecordConsumer.h" #include "llvm/XRay/FDRRecordProducer.h" #include "llvm/XRay/FDRRecords.h" #include "llvm/XRay/FDRTraceWriter.h" #include "llvm/XRay/FileHeaderReader.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include #include namespace llvm { namespace xray { namespace { using ::testing::Eq; using ::testing::IsEmpty; using ::testing::Not; using ::testing::SizeIs; template std::unique_ptr MakeRecord(); template <> std::unique_ptr MakeRecord() { return std::make_unique(1); } template <> std::unique_ptr MakeRecord() { return std::make_unique(1, 2); } template <> std::unique_ptr MakeRecord() { return std::make_unique(1); } template <> std::unique_ptr MakeRecord() { return std::make_unique(1, 2); } template <> std::unique_ptr MakeRecord() { return std::make_unique(4, 1, 2, "data"); } template <> std::unique_ptr MakeRecord() { return std::make_unique(1); } template <> std::unique_ptr MakeRecord() { return std::make_unique(1); } template <> std::unique_ptr MakeRecord() { return std::make_unique(RecordTypes::ENTER, 1, 2); } template <> std::unique_ptr MakeRecord() { return std::make_unique(4, 1, "data"); } template <> std::unique_ptr MakeRecord() { return std::make_unique(4, 1, 2, "data"); } template class RoundTripTest : public ::testing::Test { public: RoundTripTest() : Data(), OS(Data) { H.Version = 4; H.Type = 1; H.ConstantTSC = true; H.NonstopTSC = true; H.CycleFrequency = 3e9; Writer = std::make_unique(OS, H); Rec = MakeRecord(); } protected: std::string Data; raw_string_ostream OS; XRayFileHeader H; std::unique_ptr Writer; std::unique_ptr Rec; }; TYPED_TEST_CASE_P(RoundTripTest); template class RoundTripTestV5 : public ::testing::Test { public: RoundTripTestV5() : Data(), OS(Data) { H.Version = 5; H.Type = 1; H.ConstantTSC = true; H.NonstopTSC = true; H.CycleFrequency = 3e9; Writer = std::make_unique(OS, H); Rec = MakeRecord(); } protected: std::string Data; raw_string_ostream OS; XRayFileHeader H; std::unique_ptr Writer; std::unique_ptr Rec; }; TYPED_TEST_CASE_P(RoundTripTestV5); // This test ensures that the writing and reading implementations are in sync -- // that given write(read(write(R))) == R. TYPED_TEST_P(RoundTripTest, RoundTripsSingleValue) { // Always write a buffer extents record which will cover the correct size of // the record, for version 3 and up. BufferExtents BE(200); ASSERT_FALSE(errorToBool(BE.apply(*this->Writer))); auto &R = this->Rec; ASSERT_FALSE(errorToBool(R->apply(*this->Writer))); this->OS.flush(); DataExtractor DE(this->Data, sys::IsLittleEndianHost, 8); uint64_t OffsetPtr = 0; auto HeaderOrErr = readBinaryFormatHeader(DE, OffsetPtr); if (!HeaderOrErr) FAIL() << HeaderOrErr.takeError(); FileBasedRecordProducer P(HeaderOrErr.get(), DE, OffsetPtr); std::vector> Records; LogBuilderConsumer C(Records); while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) { auto R = P.produce(); if (!R) FAIL() << R.takeError(); if (auto E = C.consume(std::move(R.get()))) FAIL() << E; } ASSERT_THAT(Records, Not(IsEmpty())); std::string Data2; raw_string_ostream OS2(Data2); FDRTraceWriter Writer2(OS2, this->H); for (auto &P : Records) ASSERT_FALSE(errorToBool(P->apply(Writer2))); OS2.flush(); EXPECT_EQ(Data2.substr(sizeof(XRayFileHeader)), this->Data.substr(sizeof(XRayFileHeader))); ASSERT_THAT(Records, SizeIs(2)); EXPECT_THAT(Records[1]->getRecordType(), Eq(R->getRecordType())); } REGISTER_TYPED_TEST_CASE_P(RoundTripTest, RoundTripsSingleValue); // We duplicate the above case for the V5 version using different types and // encodings. TYPED_TEST_P(RoundTripTestV5, RoundTripsSingleValue) { BufferExtents BE(200); ASSERT_FALSE(errorToBool(BE.apply(*this->Writer))); auto &R = this->Rec; ASSERT_FALSE(errorToBool(R->apply(*this->Writer))); this->OS.flush(); DataExtractor DE(this->Data, sys::IsLittleEndianHost, 8); uint64_t OffsetPtr = 0; auto HeaderOrErr = readBinaryFormatHeader(DE, OffsetPtr); if (!HeaderOrErr) FAIL() << HeaderOrErr.takeError(); FileBasedRecordProducer P(HeaderOrErr.get(), DE, OffsetPtr); std::vector> Records; LogBuilderConsumer C(Records); while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) { auto R = P.produce(); if (!R) FAIL() << R.takeError(); if (auto E = C.consume(std::move(R.get()))) FAIL() << E; } ASSERT_THAT(Records, Not(IsEmpty())); std::string Data2; raw_string_ostream OS2(Data2); FDRTraceWriter Writer2(OS2, this->H); for (auto &P : Records) ASSERT_FALSE(errorToBool(P->apply(Writer2))); OS2.flush(); EXPECT_EQ(Data2.substr(sizeof(XRayFileHeader)), this->Data.substr(sizeof(XRayFileHeader))); ASSERT_THAT(Records, SizeIs(2)); EXPECT_THAT(Records[1]->getRecordType(), Eq(R->getRecordType())); } REGISTER_TYPED_TEST_CASE_P(RoundTripTestV5, RoundTripsSingleValue); // These are the record types we support for v4 and below. using RecordTypes = ::testing::Types; INSTANTIATE_TYPED_TEST_CASE_P(Records, RoundTripTest, RecordTypes); // For V5, we have two new types we're supporting. using RecordTypesV5 = ::testing::Types; INSTANTIATE_TYPED_TEST_CASE_P(Records, RoundTripTestV5, RecordTypesV5); } // namespace } // namespace xray } // namespace llvm