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 | |
24 | namespace llvm { |
25 | namespace xray { |
26 | namespace { |
27 | |
28 | using ::testing::Eq; |
29 | using ::testing::IsEmpty; |
30 | using ::testing::Not; |
31 | using ::testing::SizeIs; |
32 | |
33 | template <class RecordType> std::unique_ptr<Record> MakeRecord(); |
34 | |
35 | template <> std::unique_ptr<Record> MakeRecord<NewBufferRecord>() { |
36 | return std::make_unique<NewBufferRecord>(args: 1); |
37 | } |
38 | |
39 | template <> std::unique_ptr<Record> MakeRecord<NewCPUIDRecord>() { |
40 | return std::make_unique<NewCPUIDRecord>(args: 1, args: 2); |
41 | } |
42 | |
43 | template <> std::unique_ptr<Record> MakeRecord<TSCWrapRecord>() { |
44 | return std::make_unique<TSCWrapRecord>(args: 1); |
45 | } |
46 | |
47 | template <> std::unique_ptr<Record> MakeRecord<WallclockRecord>() { |
48 | return std::make_unique<WallclockRecord>(args: 1, args: 2); |
49 | } |
50 | |
51 | template <> std::unique_ptr<Record> MakeRecord<CustomEventRecord>() { |
52 | return std::make_unique<CustomEventRecord>(args: 4, args: 1, args: 2, args: "data" ); |
53 | } |
54 | |
55 | template <> std::unique_ptr<Record> MakeRecord<CallArgRecord>() { |
56 | return std::make_unique<CallArgRecord>(args: 1); |
57 | } |
58 | |
59 | template <> std::unique_ptr<Record> MakeRecord<PIDRecord>() { |
60 | return std::make_unique<PIDRecord>(args: 1); |
61 | } |
62 | |
63 | template <> std::unique_ptr<Record> MakeRecord<FunctionRecord>() { |
64 | return std::make_unique<FunctionRecord>(args: RecordTypes::ENTER, args: 1, args: 2); |
65 | } |
66 | |
67 | template <> std::unique_ptr<Record> MakeRecord<CustomEventRecordV5>() { |
68 | return std::make_unique<CustomEventRecordV5>(args: 4, args: 1, args: "data" ); |
69 | } |
70 | |
71 | template <> std::unique_ptr<Record> MakeRecord<TypedEventRecord>() { |
72 | return std::make_unique<TypedEventRecord>(args: 4, args: 1, args: 2, args: "data" ); |
73 | } |
74 | |
75 | template <class T> class RoundTripTest : public ::testing::Test { |
76 | public: |
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 | |
88 | protected: |
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 | |
96 | TYPED_TEST_SUITE_P(RoundTripTest); |
97 | |
98 | template <class T> class RoundTripTestV5 : public ::testing::Test { |
99 | public: |
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 | |
111 | protected: |
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 | |
119 | TYPED_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. |
123 | TYPED_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 = 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 | |
162 | REGISTER_TYPED_TEST_SUITE_P(RoundTripTest, RoundTripsSingleValue); |
163 | |
164 | // We duplicate the above case for the V5 version using different types and |
165 | // encodings. |
166 | TYPED_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 = 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 | |
203 | REGISTER_TYPED_TEST_SUITE_P(RoundTripTestV5, RoundTripsSingleValue); |
204 | |
205 | // These are the record types we support for v4 and below. |
206 | using RecordTypes = |
207 | ::testing::Types<NewBufferRecord, NewCPUIDRecord, TSCWrapRecord, |
208 | WallclockRecord, CustomEventRecord, CallArgRecord, |
209 | PIDRecord, FunctionRecord>; |
210 | INSTANTIATE_TYPED_TEST_SUITE_P(Records, RoundTripTest, RecordTypes, ); |
211 | |
212 | // For V5, we have two new types we're supporting. |
213 | using RecordTypesV5 = |
214 | ::testing::Types<NewBufferRecord, NewCPUIDRecord, TSCWrapRecord, |
215 | WallclockRecord, CustomEventRecordV5, TypedEventRecord, |
216 | CallArgRecord, PIDRecord, FunctionRecord>; |
217 | INSTANTIATE_TYPED_TEST_SUITE_P(Records, RoundTripTestV5, RecordTypesV5, ); |
218 | |
219 | } // namespace |
220 | } // namespace xray |
221 | } // namespace llvm |
222 | |