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