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