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