1 //===- llvm/unittest/XRay/FDRProducerConsumerTest.cpp -----------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // Test for round-trip record writing and reading.
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"
30 using ::testing::IsEmpty
;
32 using ::testing::SizeIs
;
34 template <class RecordType
> std::unique_ptr
<Record
> MakeRecord();
36 template <> std::unique_ptr
<Record
> MakeRecord
<NewBufferRecord
>() {
37 return std::make_unique
<NewBufferRecord
>(1);
40 template <> std::unique_ptr
<Record
> MakeRecord
<NewCPUIDRecord
>() {
41 return std::make_unique
<NewCPUIDRecord
>(1, 2);
44 template <> std::unique_ptr
<Record
> MakeRecord
<TSCWrapRecord
>() {
45 return std::make_unique
<TSCWrapRecord
>(1);
48 template <> std::unique_ptr
<Record
> MakeRecord
<WallclockRecord
>() {
49 return std::make_unique
<WallclockRecord
>(1, 2);
52 template <> std::unique_ptr
<Record
> MakeRecord
<CustomEventRecord
>() {
53 return std::make_unique
<CustomEventRecord
>(4, 1, 2, "data");
56 template <> std::unique_ptr
<Record
> MakeRecord
<CallArgRecord
>() {
57 return std::make_unique
<CallArgRecord
>(1);
60 template <> std::unique_ptr
<Record
> MakeRecord
<PIDRecord
>() {
61 return std::make_unique
<PIDRecord
>(1);
64 template <> std::unique_ptr
<Record
> MakeRecord
<FunctionRecord
>() {
65 return std::make_unique
<FunctionRecord
>(RecordTypes::ENTER
, 1, 2);
68 template <> std::unique_ptr
<Record
> MakeRecord
<CustomEventRecordV5
>() {
69 return std::make_unique
<CustomEventRecordV5
>(4, 1, "data");
72 template <> std::unique_ptr
<Record
> MakeRecord
<TypedEventRecord
>() {
73 return std::make_unique
<TypedEventRecord
>(4, 1, 2, "data");
76 template <class T
> class RoundTripTest
: public ::testing::Test
{
78 RoundTripTest() : Data(), OS(Data
) {
83 H
.CycleFrequency
= 3e9
;
85 Writer
= std::make_unique
<FDRTraceWriter
>(OS
, H
);
86 Rec
= MakeRecord
<T
>();
91 raw_string_ostream OS
;
93 std::unique_ptr
<FDRTraceWriter
> Writer
;
94 std::unique_ptr
<Record
> Rec
;
97 TYPED_TEST_CASE_P(RoundTripTest
);
99 template <class T
> class RoundTripTestV5
: public ::testing::Test
{
101 RoundTripTestV5() : Data(), OS(Data
) {
104 H
.ConstantTSC
= true;
106 H
.CycleFrequency
= 3e9
;
108 Writer
= std::make_unique
<FDRTraceWriter
>(OS
, H
);
109 Rec
= MakeRecord
<T
>();
114 raw_string_ostream OS
;
116 std::unique_ptr
<FDRTraceWriter
> Writer
;
117 std::unique_ptr
<Record
> Rec
;
120 TYPED_TEST_CASE_P(RoundTripTestV5
);
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
)));
130 ASSERT_FALSE(errorToBool(R
->apply(*this->Writer
)));
133 DataExtractor
DE(this->Data
, sys::IsLittleEndianHost
, 8);
134 uint64_t OffsetPtr
= 0;
135 auto HeaderOrErr
= readBinaryFormatHeader(DE
, OffsetPtr
);
137 FAIL() << HeaderOrErr
.takeError();
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();
145 FAIL() << R
.takeError();
146 if (auto E
= C
.consume(std::move(R
.get())))
149 ASSERT_THAT(Records
, Not(IsEmpty()));
151 raw_string_ostream
OS2(Data2
);
152 FDRTraceWriter
Writer2(OS2
, this->H
);
153 for (auto &P
: Records
)
154 ASSERT_FALSE(errorToBool(P
->apply(Writer2
)));
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()));
163 REGISTER_TYPED_TEST_CASE_P(RoundTripTest
, RoundTripsSingleValue
);
165 // We duplicate the above case for the V5 version using different types and
167 TYPED_TEST_P(RoundTripTestV5
, RoundTripsSingleValue
) {
168 BufferExtents
BE(200);
169 ASSERT_FALSE(errorToBool(BE
.apply(*this->Writer
)));
171 ASSERT_FALSE(errorToBool(R
->apply(*this->Writer
)));
174 DataExtractor
DE(this->Data
, sys::IsLittleEndianHost
, 8);
175 uint64_t OffsetPtr
= 0;
176 auto HeaderOrErr
= readBinaryFormatHeader(DE
, OffsetPtr
);
178 FAIL() << HeaderOrErr
.takeError();
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();
186 FAIL() << R
.takeError();
187 if (auto E
= C
.consume(std::move(R
.get())))
190 ASSERT_THAT(Records
, Not(IsEmpty()));
192 raw_string_ostream
OS2(Data2
);
193 FDRTraceWriter
Writer2(OS2
, this->H
);
194 for (auto &P
: Records
)
195 ASSERT_FALSE(errorToBool(P
->apply(Writer2
)));
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()));
204 REGISTER_TYPED_TEST_CASE_P(RoundTripTestV5
, RoundTripsSingleValue
);
206 // These are the record types we support for v4 and below.
208 ::testing::Types
<NewBufferRecord
, NewCPUIDRecord
, TSCWrapRecord
,
209 WallclockRecord
, CustomEventRecord
, CallArgRecord
,
210 PIDRecord
, FunctionRecord
>;
211 INSTANTIATE_TYPED_TEST_CASE_P(Records
, RoundTripTest
, RecordTypes
);
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_CASE_P(Records
, RoundTripTestV5
, RecordTypesV5
);