1 //===- llvm/unittest/XRay/FDRProducerConsumerTest.cpp -----------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Test for round-trip record writing and reading. 10 // 11 //===----------------------------------------------------------------------===// 12 #include "llvm/Support/DataExtractor.h" 13 #include "llvm/Support/raw_ostream.h" 14 #include "llvm/XRay/FDRLogBuilder.h" 15 #include "llvm/XRay/FDRRecordConsumer.h" 16 #include "llvm/XRay/FDRRecordProducer.h" 17 #include "llvm/XRay/FDRRecords.h" 18 #include "llvm/XRay/FDRTraceWriter.h" 19 #include "llvm/XRay/FileHeaderReader.h" 20 #include "gmock/gmock.h" 21 #include "gtest/gtest.h" 22 #include <string> 23 24 namespace llvm { 25 namespace xray { 26 namespace { 27 28 using ::testing::Eq; 29 using ::testing::IsEmpty; 30 using ::testing::Not; 31 using ::testing::SizeIs; 32 33 template <class RecordType> std::unique_ptr<Record> MakeRecord(); 34 35 template <> std::unique_ptr<Record> MakeRecord<NewBufferRecord>() { 36 return std::make_unique<NewBufferRecord>(1); 37 } 38 39 template <> std::unique_ptr<Record> MakeRecord<NewCPUIDRecord>() { 40 return std::make_unique<NewCPUIDRecord>(1, 2); 41 } 42 43 template <> std::unique_ptr<Record> MakeRecord<TSCWrapRecord>() { 44 return std::make_unique<TSCWrapRecord>(1); 45 } 46 47 template <> std::unique_ptr<Record> MakeRecord<WallclockRecord>() { 48 return std::make_unique<WallclockRecord>(1, 2); 49 } 50 51 template <> std::unique_ptr<Record> MakeRecord<CustomEventRecord>() { 52 return std::make_unique<CustomEventRecord>(4, 1, 2, "data"); 53 } 54 55 template <> std::unique_ptr<Record> MakeRecord<CallArgRecord>() { 56 return std::make_unique<CallArgRecord>(1); 57 } 58 59 template <> std::unique_ptr<Record> MakeRecord<PIDRecord>() { 60 return std::make_unique<PIDRecord>(1); 61 } 62 63 template <> std::unique_ptr<Record> MakeRecord<FunctionRecord>() { 64 return std::make_unique<FunctionRecord>(RecordTypes::ENTER, 1, 2); 65 } 66 67 template <> std::unique_ptr<Record> MakeRecord<CustomEventRecordV5>() { 68 return std::make_unique<CustomEventRecordV5>(4, 1, "data"); 69 } 70 71 template <> std::unique_ptr<Record> MakeRecord<TypedEventRecord>() { 72 return std::make_unique<TypedEventRecord>(4, 1, 2, "data"); 73 } 74 75 template <class T> class RoundTripTest : public ::testing::Test { 76 public: 77 RoundTripTest() : Data(), OS(Data) { 78 H.Version = 4; 79 H.Type = 1; 80 H.ConstantTSC = true; 81 H.NonstopTSC = true; 82 H.CycleFrequency = 3e9; 83 84 Writer = std::make_unique<FDRTraceWriter>(OS, H); 85 Rec = MakeRecord<T>(); 86 } 87 88 protected: 89 std::string Data; 90 raw_string_ostream OS; 91 XRayFileHeader H; 92 std::unique_ptr<FDRTraceWriter> Writer; 93 std::unique_ptr<Record> Rec; 94 }; 95 96 TYPED_TEST_SUITE_P(RoundTripTest); 97 98 template <class T> class RoundTripTestV5 : public ::testing::Test { 99 public: 100 RoundTripTestV5() : Data(), OS(Data) { 101 H.Version = 5; 102 H.Type = 1; 103 H.ConstantTSC = true; 104 H.NonstopTSC = true; 105 H.CycleFrequency = 3e9; 106 107 Writer = std::make_unique<FDRTraceWriter>(OS, H); 108 Rec = MakeRecord<T>(); 109 } 110 111 protected: 112 std::string Data; 113 raw_string_ostream OS; 114 XRayFileHeader H; 115 std::unique_ptr<FDRTraceWriter> Writer; 116 std::unique_ptr<Record> Rec; 117 }; 118 119 TYPED_TEST_SUITE_P(RoundTripTestV5); 120 121 // This test ensures that the writing and reading implementations are in sync -- 122 // that given write(read(write(R))) == R. 123 TYPED_TEST_P(RoundTripTest, RoundTripsSingleValue) { 124 // Always write a buffer extents record which will cover the correct size of 125 // the record, for version 3 and up. 126 BufferExtents BE(200); 127 ASSERT_FALSE(errorToBool(BE.apply(*this->Writer))); 128 auto &R = this->Rec; 129 ASSERT_FALSE(errorToBool(R->apply(*this->Writer))); 130 131 DataExtractor DE(this->Data, sys::IsLittleEndianHost, 8); 132 uint64_t OffsetPtr = 0; 133 auto HeaderOrErr = readBinaryFormatHeader(DE, OffsetPtr); 134 if (!HeaderOrErr) 135 FAIL() << HeaderOrErr.takeError(); 136 137 FileBasedRecordProducer P(HeaderOrErr.get(), DE, OffsetPtr); 138 std::vector<std::unique_ptr<Record>> Records; 139 LogBuilderConsumer C(Records); 140 while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) { 141 auto R = P.produce(); 142 if (!R) 143 FAIL() << R.takeError(); 144 if (auto E = C.consume(std::move(R.get()))) 145 FAIL() << E; 146 } 147 ASSERT_THAT(Records, Not(IsEmpty())); 148 std::string Data2; 149 raw_string_ostream OS2(Data2); 150 FDRTraceWriter Writer2(OS2, this->H); 151 for (auto &P : Records) 152 ASSERT_FALSE(errorToBool(P->apply(Writer2))); 153 154 EXPECT_EQ(Data2.substr(sizeof(XRayFileHeader)), 155 this->Data.substr(sizeof(XRayFileHeader))); 156 ASSERT_THAT(Records, SizeIs(2)); 157 EXPECT_THAT(Records[1]->getRecordType(), Eq(R->getRecordType())); 158 } 159 160 REGISTER_TYPED_TEST_SUITE_P(RoundTripTest, RoundTripsSingleValue); 161 162 // We duplicate the above case for the V5 version using different types and 163 // encodings. 164 TYPED_TEST_P(RoundTripTestV5, RoundTripsSingleValue) { 165 BufferExtents BE(200); 166 ASSERT_FALSE(errorToBool(BE.apply(*this->Writer))); 167 auto &R = this->Rec; 168 ASSERT_FALSE(errorToBool(R->apply(*this->Writer))); 169 170 DataExtractor DE(this->Data, sys::IsLittleEndianHost, 8); 171 uint64_t OffsetPtr = 0; 172 auto HeaderOrErr = readBinaryFormatHeader(DE, OffsetPtr); 173 if (!HeaderOrErr) 174 FAIL() << HeaderOrErr.takeError(); 175 176 FileBasedRecordProducer P(HeaderOrErr.get(), DE, OffsetPtr); 177 std::vector<std::unique_ptr<Record>> Records; 178 LogBuilderConsumer C(Records); 179 while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) { 180 auto R = P.produce(); 181 if (!R) 182 FAIL() << R.takeError(); 183 if (auto E = C.consume(std::move(R.get()))) 184 FAIL() << E; 185 } 186 ASSERT_THAT(Records, Not(IsEmpty())); 187 std::string Data2; 188 raw_string_ostream OS2(Data2); 189 FDRTraceWriter Writer2(OS2, this->H); 190 for (auto &P : Records) 191 ASSERT_FALSE(errorToBool(P->apply(Writer2))); 192 193 EXPECT_EQ(Data2.substr(sizeof(XRayFileHeader)), 194 this->Data.substr(sizeof(XRayFileHeader))); 195 ASSERT_THAT(Records, SizeIs(2)); 196 EXPECT_THAT(Records[1]->getRecordType(), Eq(R->getRecordType())); 197 } 198 199 REGISTER_TYPED_TEST_SUITE_P(RoundTripTestV5, RoundTripsSingleValue); 200 201 // These are the record types we support for v4 and below. 202 using RecordTypes = 203 ::testing::Types<NewBufferRecord, NewCPUIDRecord, TSCWrapRecord, 204 WallclockRecord, CustomEventRecord, CallArgRecord, 205 PIDRecord, FunctionRecord>; 206 INSTANTIATE_TYPED_TEST_SUITE_P(Records, RoundTripTest, RecordTypes, ); 207 208 // For V5, we have two new types we're supporting. 209 using RecordTypesV5 = 210 ::testing::Types<NewBufferRecord, NewCPUIDRecord, TSCWrapRecord, 211 WallclockRecord, CustomEventRecordV5, TypedEventRecord, 212 CallArgRecord, PIDRecord, FunctionRecord>; 213 INSTANTIATE_TYPED_TEST_SUITE_P(Records, RoundTripTestV5, RecordTypesV5, ); 214 215 } // namespace 216 } // namespace xray 217 } // namespace llvm 218