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