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
MakeRecord()36 template <> std::unique_ptr<Record> MakeRecord<NewBufferRecord>() {
37 return std::make_unique<NewBufferRecord>(1);
38 }
39
MakeRecord()40 template <> std::unique_ptr<Record> MakeRecord<NewCPUIDRecord>() {
41 return std::make_unique<NewCPUIDRecord>(1, 2);
42 }
43
MakeRecord()44 template <> std::unique_ptr<Record> MakeRecord<TSCWrapRecord>() {
45 return std::make_unique<TSCWrapRecord>(1);
46 }
47
MakeRecord()48 template <> std::unique_ptr<Record> MakeRecord<WallclockRecord>() {
49 return std::make_unique<WallclockRecord>(1, 2);
50 }
51
MakeRecord()52 template <> std::unique_ptr<Record> MakeRecord<CustomEventRecord>() {
53 return std::make_unique<CustomEventRecord>(4, 1, 2, "data");
54 }
55
MakeRecord()56 template <> std::unique_ptr<Record> MakeRecord<CallArgRecord>() {
57 return std::make_unique<CallArgRecord>(1);
58 }
59
MakeRecord()60 template <> std::unique_ptr<Record> MakeRecord<PIDRecord>() {
61 return std::make_unique<PIDRecord>(1);
62 }
63
MakeRecord()64 template <> std::unique_ptr<Record> MakeRecord<FunctionRecord>() {
65 return std::make_unique<FunctionRecord>(RecordTypes::ENTER, 1, 2);
66 }
67
MakeRecord()68 template <> std::unique_ptr<Record> MakeRecord<CustomEventRecordV5>() {
69 return std::make_unique<CustomEventRecordV5>(4, 1, "data");
70 }
71
MakeRecord()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:
RoundTripTest()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:
RoundTripTestV5()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.
TYPED_TEST_P(RoundTripTest,RoundTripsSingleValue)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.
TYPED_TEST_P(RoundTripTestV5,RoundTripsSingleValue)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