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
24namespace llvm {
25namespace xray {
26namespace {
27
28using ::testing::Eq;
29using ::testing::IsEmpty;
30using ::testing::Not;
31using ::testing::SizeIs;
32
33template <class RecordType> std::unique_ptr<Record> MakeRecord();
34
35template <> std::unique_ptr<Record> MakeRecord<NewBufferRecord>() {
36 return std::make_unique<NewBufferRecord>(args: 1);
37}
38
39template <> std::unique_ptr<Record> MakeRecord<NewCPUIDRecord>() {
40 return std::make_unique<NewCPUIDRecord>(args: 1, args: 2);
41}
42
43template <> std::unique_ptr<Record> MakeRecord<TSCWrapRecord>() {
44 return std::make_unique<TSCWrapRecord>(args: 1);
45}
46
47template <> std::unique_ptr<Record> MakeRecord<WallclockRecord>() {
48 return std::make_unique<WallclockRecord>(args: 1, args: 2);
49}
50
51template <> std::unique_ptr<Record> MakeRecord<CustomEventRecord>() {
52 return std::make_unique<CustomEventRecord>(args: 4, args: 1, args: 2, args: "data");
53}
54
55template <> std::unique_ptr<Record> MakeRecord<CallArgRecord>() {
56 return std::make_unique<CallArgRecord>(args: 1);
57}
58
59template <> std::unique_ptr<Record> MakeRecord<PIDRecord>() {
60 return std::make_unique<PIDRecord>(args: 1);
61}
62
63template <> std::unique_ptr<Record> MakeRecord<FunctionRecord>() {
64 return std::make_unique<FunctionRecord>(args: RecordTypes::ENTER, args: 1, args: 2);
65}
66
67template <> std::unique_ptr<Record> MakeRecord<CustomEventRecordV5>() {
68 return std::make_unique<CustomEventRecordV5>(args: 4, args: 1, args: "data");
69}
70
71template <> std::unique_ptr<Record> MakeRecord<TypedEventRecord>() {
72 return std::make_unique<TypedEventRecord>(args: 4, args: 1, args: 2, args: "data");
73}
74
75template <class T> class RoundTripTest : public ::testing::Test {
76public:
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>(args&: OS, args&: H);
85 Rec = MakeRecord<T>();
86 }
87
88protected:
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
96TYPED_TEST_SUITE_P(RoundTripTest);
97
98template <class T> class RoundTripTestV5 : public ::testing::Test {
99public:
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>(args&: OS, args&: H);
108 Rec = MakeRecord<T>();
109 }
110
111protected:
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
119TYPED_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.
123TYPED_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 this->OS.flush();
131
132 DataExtractor DE(this->Data, sys::IsLittleEndianHost, 8);
133 uint64_t OffsetPtr = 0;
134 auto HeaderOrErr = readBinaryFormatHeader(HeaderExtractor&: DE, OffsetPtr);
135 if (!HeaderOrErr)
136 FAIL() << HeaderOrErr.takeError();
137
138 FileBasedRecordProducer P(HeaderOrErr.get(), DE, OffsetPtr);
139 std::vector<std::unique_ptr<Record>> Records;
140 LogBuilderConsumer C(Records);
141 while (DE.isValidOffsetForDataOfSize(offset: OffsetPtr, length: 1)) {
142 auto R = P.produce();
143 if (!R)
144 FAIL() << R.takeError();
145 if (auto E = C.consume(R: std::move(R.get())))
146 FAIL() << E;
147 }
148 ASSERT_THAT(Records, Not(IsEmpty()));
149 std::string Data2;
150 raw_string_ostream OS2(Data2);
151 FDRTraceWriter Writer2(OS2, this->H);
152 for (auto &P : Records)
153 ASSERT_FALSE(errorToBool(P->apply(Writer2)));
154 OS2.flush();
155
156 EXPECT_EQ(Data2.substr(sizeof(XRayFileHeader)),
157 this->Data.substr(sizeof(XRayFileHeader)));
158 ASSERT_THAT(Records, SizeIs(2));
159 EXPECT_THAT(Records[1]->getRecordType(), Eq(R->getRecordType()));
160}
161
162REGISTER_TYPED_TEST_SUITE_P(RoundTripTest, RoundTripsSingleValue);
163
164// We duplicate the above case for the V5 version using different types and
165// encodings.
166TYPED_TEST_P(RoundTripTestV5, RoundTripsSingleValue) {
167 BufferExtents BE(200);
168 ASSERT_FALSE(errorToBool(BE.apply(*this->Writer)));
169 auto &R = this->Rec;
170 ASSERT_FALSE(errorToBool(R->apply(*this->Writer)));
171 this->OS.flush();
172
173 DataExtractor DE(this->Data, sys::IsLittleEndianHost, 8);
174 uint64_t OffsetPtr = 0;
175 auto HeaderOrErr = readBinaryFormatHeader(HeaderExtractor&: DE, OffsetPtr);
176 if (!HeaderOrErr)
177 FAIL() << HeaderOrErr.takeError();
178
179 FileBasedRecordProducer P(HeaderOrErr.get(), DE, OffsetPtr);
180 std::vector<std::unique_ptr<Record>> Records;
181 LogBuilderConsumer C(Records);
182 while (DE.isValidOffsetForDataOfSize(offset: OffsetPtr, length: 1)) {
183 auto R = P.produce();
184 if (!R)
185 FAIL() << R.takeError();
186 if (auto E = C.consume(R: std::move(R.get())))
187 FAIL() << E;
188 }
189 ASSERT_THAT(Records, Not(IsEmpty()));
190 std::string Data2;
191 raw_string_ostream OS2(Data2);
192 FDRTraceWriter Writer2(OS2, this->H);
193 for (auto &P : Records)
194 ASSERT_FALSE(errorToBool(P->apply(Writer2)));
195 OS2.flush();
196
197 EXPECT_EQ(Data2.substr(sizeof(XRayFileHeader)),
198 this->Data.substr(sizeof(XRayFileHeader)));
199 ASSERT_THAT(Records, SizeIs(2));
200 EXPECT_THAT(Records[1]->getRecordType(), Eq(R->getRecordType()));
201}
202
203REGISTER_TYPED_TEST_SUITE_P(RoundTripTestV5, RoundTripsSingleValue);
204
205// These are the record types we support for v4 and below.
206using RecordTypes =
207 ::testing::Types<NewBufferRecord, NewCPUIDRecord, TSCWrapRecord,
208 WallclockRecord, CustomEventRecord, CallArgRecord,
209 PIDRecord, FunctionRecord>;
210INSTANTIATE_TYPED_TEST_SUITE_P(Records, RoundTripTest, RecordTypes, );
211
212// For V5, we have two new types we're supporting.
213using RecordTypesV5 =
214 ::testing::Types<NewBufferRecord, NewCPUIDRecord, TSCWrapRecord,
215 WallclockRecord, CustomEventRecordV5, TypedEventRecord,
216 CallArgRecord, PIDRecord, FunctionRecord>;
217INSTANTIATE_TYPED_TEST_SUITE_P(Records, RoundTripTestV5, RecordTypesV5, );
218
219} // namespace
220} // namespace xray
221} // namespace llvm
222

source code of llvm/unittests/XRay/FDRProducerConsumerTest.cpp