1*bc0d697dSNico Weber //===-- fdr_controller_test.cpp -------------------------------------------===//
2*bc0d697dSNico Weber //
3*bc0d697dSNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*bc0d697dSNico Weber // See https://llvm.org/LICENSE.txt for license information.
5*bc0d697dSNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*bc0d697dSNico Weber //
7*bc0d697dSNico Weber //===----------------------------------------------------------------------===//
8*bc0d697dSNico Weber //
9*bc0d697dSNico Weber // This file is a part of XRay, a function call tracing system.
10*bc0d697dSNico Weber //
11*bc0d697dSNico Weber //===----------------------------------------------------------------------===//
12*bc0d697dSNico Weber #include <algorithm>
13*bc0d697dSNico Weber #include <memory>
14*bc0d697dSNico Weber #include <time.h>
15*bc0d697dSNico Weber 
16*bc0d697dSNico Weber #include "test_helpers.h"
17*bc0d697dSNico Weber #include "xray/xray_records.h"
18*bc0d697dSNico Weber #include "xray_buffer_queue.h"
19*bc0d697dSNico Weber #include "xray_fdr_controller.h"
20*bc0d697dSNico Weber #include "xray_fdr_log_writer.h"
21*bc0d697dSNico Weber #include "llvm/Support/DataExtractor.h"
22*bc0d697dSNico Weber #include "llvm/Testing/Support/Error.h"
23*bc0d697dSNico Weber #include "llvm/XRay/Trace.h"
24*bc0d697dSNico Weber #include "llvm/XRay/XRayRecord.h"
25*bc0d697dSNico Weber #include "gmock/gmock.h"
26*bc0d697dSNico Weber #include "gtest/gtest.h"
27*bc0d697dSNico Weber 
28*bc0d697dSNico Weber namespace __xray {
29*bc0d697dSNico Weber namespace {
30*bc0d697dSNico Weber 
31*bc0d697dSNico Weber using ::llvm::HasValue;
32*bc0d697dSNico Weber using ::llvm::xray::testing::FuncId;
33*bc0d697dSNico Weber using ::llvm::xray::testing::HasArg;
34*bc0d697dSNico Weber using ::llvm::xray::testing::RecordType;
35*bc0d697dSNico Weber using ::llvm::xray::testing::TSCIs;
36*bc0d697dSNico Weber using ::testing::AllOf;
37*bc0d697dSNico Weber using ::testing::ElementsAre;
38*bc0d697dSNico Weber using ::testing::Eq;
39*bc0d697dSNico Weber using ::testing::Field;
40*bc0d697dSNico Weber using ::testing::Gt;
41*bc0d697dSNico Weber using ::testing::IsEmpty;
42*bc0d697dSNico Weber using ::testing::SizeIs;
43*bc0d697dSNico Weber 
44*bc0d697dSNico Weber class FunctionSequenceTest : public ::testing::Test {
45*bc0d697dSNico Weber protected:
46*bc0d697dSNico Weber   BufferQueue::Buffer B{};
47*bc0d697dSNico Weber   std::unique_ptr<BufferQueue> BQ;
48*bc0d697dSNico Weber   std::unique_ptr<FDRLogWriter> W;
49*bc0d697dSNico Weber   std::unique_ptr<FDRController<>> C;
50*bc0d697dSNico Weber 
51*bc0d697dSNico Weber public:
52*bc0d697dSNico Weber   void SetUp() override {
53*bc0d697dSNico Weber     bool Success;
54*bc0d697dSNico Weber     BQ = llvm::make_unique<BufferQueue>(4096, 1, Success);
55*bc0d697dSNico Weber     ASSERT_TRUE(Success);
56*bc0d697dSNico Weber     ASSERT_EQ(BQ->getBuffer(B), BufferQueue::ErrorCode::Ok);
57*bc0d697dSNico Weber     W = llvm::make_unique<FDRLogWriter>(B);
58*bc0d697dSNico Weber     C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 0);
59*bc0d697dSNico Weber   }
60*bc0d697dSNico Weber };
61*bc0d697dSNico Weber 
62*bc0d697dSNico Weber TEST_F(FunctionSequenceTest, DefaultInitFinalizeFlush) {
63*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(1, 2, 3));
64*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(1, 2, 3));
65*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
66*bc0d697dSNico Weber   ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
67*bc0d697dSNico Weber 
68*bc0d697dSNico Weber   // Serialize the buffers then test to see we find the expected records.
69*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
70*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
71*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
72*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(
73*bc0d697dSNico Weber       TraceOrErr,
74*bc0d697dSNico Weber       HasValue(ElementsAre(
75*bc0d697dSNico Weber           AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)),
76*bc0d697dSNico Weber           AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT)))));
77*bc0d697dSNico Weber }
78*bc0d697dSNico Weber 
79*bc0d697dSNico Weber TEST_F(FunctionSequenceTest, BoundaryFuncIdEncoding) {
80*bc0d697dSNico Weber   // We ensure that we can write function id's that are at the boundary of the
81*bc0d697dSNico Weber   // acceptable function ids.
82*bc0d697dSNico Weber   int32_t FId = (1 << 28) - 1;
83*bc0d697dSNico Weber   uint64_t TSC = 2;
84*bc0d697dSNico Weber   uint16_t CPU = 1;
85*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(FId, TSC++, CPU));
86*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(FId, TSC++, CPU));
87*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnterArg(FId, TSC++, CPU, 1));
88*bc0d697dSNico Weber   ASSERT_TRUE(C->functionTailExit(FId, TSC++, CPU));
89*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
90*bc0d697dSNico Weber   ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
91*bc0d697dSNico Weber 
92*bc0d697dSNico Weber   // Serialize the buffers then test to see we find the expected records.
93*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
94*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
95*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
96*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(
97*bc0d697dSNico Weber       TraceOrErr,
98*bc0d697dSNico Weber       HasValue(ElementsAre(
99*bc0d697dSNico Weber           AllOf(FuncId(FId), RecordType(llvm::xray::RecordTypes::ENTER)),
100*bc0d697dSNico Weber           AllOf(FuncId(FId), RecordType(llvm::xray::RecordTypes::EXIT)),
101*bc0d697dSNico Weber           AllOf(FuncId(FId), RecordType(llvm::xray::RecordTypes::ENTER_ARG)),
102*bc0d697dSNico Weber           AllOf(FuncId(FId), RecordType(llvm::xray::RecordTypes::TAIL_EXIT)))));
103*bc0d697dSNico Weber }
104*bc0d697dSNico Weber 
105*bc0d697dSNico Weber TEST_F(FunctionSequenceTest, ThresholdsAreEnforced) {
106*bc0d697dSNico Weber   C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
107*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(1, 2, 3));
108*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(1, 2, 3));
109*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
110*bc0d697dSNico Weber   ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
111*bc0d697dSNico Weber 
112*bc0d697dSNico Weber   // Serialize the buffers then test to see we find the *no* records, because
113*bc0d697dSNico Weber   // the function entry-exit comes under the cycle threshold.
114*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
115*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
116*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
117*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty()));
118*bc0d697dSNico Weber }
119*bc0d697dSNico Weber 
120*bc0d697dSNico Weber TEST_F(FunctionSequenceTest, ArgsAreHandledAndKept) {
121*bc0d697dSNico Weber   C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
122*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnterArg(1, 2, 3, 4));
123*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(1, 2, 3));
124*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
125*bc0d697dSNico Weber   ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
126*bc0d697dSNico Weber 
127*bc0d697dSNico Weber   // Serialize the buffers then test to see we find the function enter arg
128*bc0d697dSNico Weber   // record with the specified argument.
129*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
130*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
131*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
132*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(
133*bc0d697dSNico Weber       TraceOrErr,
134*bc0d697dSNico Weber       HasValue(ElementsAre(
135*bc0d697dSNico Weber           AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER_ARG),
136*bc0d697dSNico Weber                 HasArg(4)),
137*bc0d697dSNico Weber           AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT)))));
138*bc0d697dSNico Weber }
139*bc0d697dSNico Weber 
140*bc0d697dSNico Weber TEST_F(FunctionSequenceTest, PreservedCallsHaveCorrectTSC) {
141*bc0d697dSNico Weber   C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
142*bc0d697dSNico Weber   uint64_t TSC = 1;
143*bc0d697dSNico Weber   uint16_t CPU = 0;
144*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
145*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(2, TSC++, CPU));
146*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(2, TSC++, CPU));
147*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(1, TSC += 1000, CPU));
148*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
149*bc0d697dSNico Weber   ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
150*bc0d697dSNico Weber 
151*bc0d697dSNico Weber   // Serialize the buffers then test to see if we find the remaining records,
152*bc0d697dSNico Weber   // because the function entry-exit comes under the cycle threshold.
153*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
154*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
155*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
156*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(
157*bc0d697dSNico Weber       TraceOrErr,
158*bc0d697dSNico Weber       HasValue(ElementsAre(
159*bc0d697dSNico Weber           AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER),
160*bc0d697dSNico Weber                 TSCIs(Eq(1uL))),
161*bc0d697dSNico Weber           AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT),
162*bc0d697dSNico Weber                 TSCIs(Gt(1000uL))))));
163*bc0d697dSNico Weber }
164*bc0d697dSNico Weber 
165*bc0d697dSNico Weber TEST_F(FunctionSequenceTest, PreservedCallsSupportLargeDeltas) {
166*bc0d697dSNico Weber   C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
167*bc0d697dSNico Weber   uint64_t TSC = 1;
168*bc0d697dSNico Weber   uint16_t CPU = 0;
169*bc0d697dSNico Weber   const auto LargeDelta = uint64_t{std::numeric_limits<int32_t>::max()};
170*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
171*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(1, TSC += LargeDelta, CPU));
172*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
173*bc0d697dSNico Weber   ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
174*bc0d697dSNico Weber 
175*bc0d697dSNico Weber   // Serialize the buffer then test to see if we find the right TSC with a large
176*bc0d697dSNico Weber   // delta.
177*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
178*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
179*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
180*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(
181*bc0d697dSNico Weber       TraceOrErr,
182*bc0d697dSNico Weber       HasValue(ElementsAre(
183*bc0d697dSNico Weber           AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER),
184*bc0d697dSNico Weber                 TSCIs(Eq(1uL))),
185*bc0d697dSNico Weber           AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT),
186*bc0d697dSNico Weber                 TSCIs(Gt(LargeDelta))))));
187*bc0d697dSNico Weber }
188*bc0d697dSNico Weber 
189*bc0d697dSNico Weber TEST_F(FunctionSequenceTest, RewindingMultipleCalls) {
190*bc0d697dSNico Weber   C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
191*bc0d697dSNico Weber 
192*bc0d697dSNico Weber   // First we construct an arbitrarily deep function enter/call stack.
193*bc0d697dSNico Weber   // We also ensure that we are in the same CPU.
194*bc0d697dSNico Weber   uint64_t TSC = 1;
195*bc0d697dSNico Weber   uint16_t CPU = 1;
196*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
197*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(2, TSC++, CPU));
198*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(3, TSC++, CPU));
199*bc0d697dSNico Weber 
200*bc0d697dSNico Weber   // Then we exit them one at a time, in reverse order of entry.
201*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(3, TSC++, CPU));
202*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(2, TSC++, CPU));
203*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
204*bc0d697dSNico Weber 
205*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
206*bc0d697dSNico Weber   ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
207*bc0d697dSNico Weber 
208*bc0d697dSNico Weber   // Serialize the buffers then test to see we find that all the calls have been
209*bc0d697dSNico Weber   // unwound because all of them are under the cycle counter threshold.
210*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
211*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
212*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
213*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty()));
214*bc0d697dSNico Weber }
215*bc0d697dSNico Weber 
216*bc0d697dSNico Weber TEST_F(FunctionSequenceTest, RewindingIntermediaryTailExits) {
217*bc0d697dSNico Weber   C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
218*bc0d697dSNico Weber 
219*bc0d697dSNico Weber   // First we construct an arbitrarily deep function enter/call stack.
220*bc0d697dSNico Weber   // We also ensure that we are in the same CPU.
221*bc0d697dSNico Weber   uint64_t TSC = 1;
222*bc0d697dSNico Weber   uint16_t CPU = 1;
223*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
224*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(2, TSC++, CPU));
225*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(3, TSC++, CPU));
226*bc0d697dSNico Weber 
227*bc0d697dSNico Weber   // Next we tail-exit into a new function multiple times.
228*bc0d697dSNico Weber   ASSERT_TRUE(C->functionTailExit(3, TSC++, CPU));
229*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(4, TSC++, CPU));
230*bc0d697dSNico Weber   ASSERT_TRUE(C->functionTailExit(4, TSC++, CPU));
231*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(5, TSC++, CPU));
232*bc0d697dSNico Weber   ASSERT_TRUE(C->functionTailExit(5, TSC++, CPU));
233*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(6, TSC++, CPU));
234*bc0d697dSNico Weber 
235*bc0d697dSNico Weber   // Then we exit them one at a time, in reverse order of entry.
236*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(6, TSC++, CPU));
237*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(2, TSC++, CPU));
238*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
239*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
240*bc0d697dSNico Weber   ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
241*bc0d697dSNico Weber 
242*bc0d697dSNico Weber   // Serialize the buffers then test to see we find that all the calls have been
243*bc0d697dSNico Weber   // unwound because all of them are under the cycle counter threshold.
244*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
245*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
246*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
247*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty()));
248*bc0d697dSNico Weber }
249*bc0d697dSNico Weber 
250*bc0d697dSNico Weber TEST_F(FunctionSequenceTest, RewindingAfterMigration) {
251*bc0d697dSNico Weber   C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 1000);
252*bc0d697dSNico Weber 
253*bc0d697dSNico Weber   // First we construct an arbitrarily deep function enter/call stack.
254*bc0d697dSNico Weber   // We also ensure that we are in the same CPU.
255*bc0d697dSNico Weber   uint64_t TSC = 1;
256*bc0d697dSNico Weber   uint16_t CPU = 1;
257*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
258*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(2, TSC++, CPU));
259*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(3, TSC++, CPU));
260*bc0d697dSNico Weber 
261*bc0d697dSNico Weber   // Next we tail-exit into a new function multiple times.
262*bc0d697dSNico Weber   ASSERT_TRUE(C->functionTailExit(3, TSC++, CPU));
263*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(4, TSC++, CPU));
264*bc0d697dSNico Weber   ASSERT_TRUE(C->functionTailExit(4, TSC++, CPU));
265*bc0d697dSNico Weber 
266*bc0d697dSNico Weber   // But before we enter the next function, we migrate to a different CPU.
267*bc0d697dSNico Weber   CPU = 2;
268*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(5, TSC++, CPU));
269*bc0d697dSNico Weber   ASSERT_TRUE(C->functionTailExit(5, TSC++, CPU));
270*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(6, TSC++, CPU));
271*bc0d697dSNico Weber 
272*bc0d697dSNico Weber   // Then we exit them one at a time, in reverse order of entry.
273*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(6, TSC++, CPU));
274*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(2, TSC++, CPU));
275*bc0d697dSNico Weber   ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
276*bc0d697dSNico Weber 
277*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
278*bc0d697dSNico Weber   ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
279*bc0d697dSNico Weber 
280*bc0d697dSNico Weber   // Serialize buffers then test that we can find all the events that span the
281*bc0d697dSNico Weber   // CPU migration.
282*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
283*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
284*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
285*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(
286*bc0d697dSNico Weber       TraceOrErr,
287*bc0d697dSNico Weber       HasValue(ElementsAre(
288*bc0d697dSNico Weber           AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)),
289*bc0d697dSNico Weber           AllOf(FuncId(2), RecordType(llvm::xray::RecordTypes::ENTER)),
290*bc0d697dSNico Weber           AllOf(FuncId(2), RecordType(llvm::xray::RecordTypes::EXIT)),
291*bc0d697dSNico Weber           AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT)))));
292*bc0d697dSNico Weber }
293*bc0d697dSNico Weber 
294*bc0d697dSNico Weber class BufferManagementTest : public ::testing::Test {
295*bc0d697dSNico Weber protected:
296*bc0d697dSNico Weber   BufferQueue::Buffer B{};
297*bc0d697dSNico Weber   std::unique_ptr<BufferQueue> BQ;
298*bc0d697dSNico Weber   std::unique_ptr<FDRLogWriter> W;
299*bc0d697dSNico Weber   std::unique_ptr<FDRController<>> C;
300*bc0d697dSNico Weber 
301*bc0d697dSNico Weber   static constexpr size_t kBuffers = 10;
302*bc0d697dSNico Weber 
303*bc0d697dSNico Weber public:
304*bc0d697dSNico Weber   void SetUp() override {
305*bc0d697dSNico Weber     bool Success;
306*bc0d697dSNico Weber     BQ = llvm::make_unique<BufferQueue>(sizeof(MetadataRecord) * 5 +
307*bc0d697dSNico Weber                                             sizeof(FunctionRecord) * 2,
308*bc0d697dSNico Weber                                         kBuffers, Success);
309*bc0d697dSNico Weber     ASSERT_TRUE(Success);
310*bc0d697dSNico Weber     ASSERT_EQ(BQ->getBuffer(B), BufferQueue::ErrorCode::Ok);
311*bc0d697dSNico Weber     W = llvm::make_unique<FDRLogWriter>(B);
312*bc0d697dSNico Weber     C = llvm::make_unique<FDRController<>>(BQ.get(), B, *W, clock_gettime, 0);
313*bc0d697dSNico Weber   }
314*bc0d697dSNico Weber };
315*bc0d697dSNico Weber 
316*bc0d697dSNico Weber constexpr size_t BufferManagementTest::kBuffers;
317*bc0d697dSNico Weber 
318*bc0d697dSNico Weber TEST_F(BufferManagementTest, HandlesOverflow) {
319*bc0d697dSNico Weber   uint64_t TSC = 1;
320*bc0d697dSNico Weber   uint16_t CPU = 1;
321*bc0d697dSNico Weber   for (size_t I = 0; I < kBuffers + 1; ++I) {
322*bc0d697dSNico Weber     ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
323*bc0d697dSNico Weber     ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
324*bc0d697dSNico Weber   }
325*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
326*bc0d697dSNico Weber   ASSERT_THAT(BQ->finalize(), Eq(BufferQueue::ErrorCode::Ok));
327*bc0d697dSNico Weber 
328*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
329*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
330*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
331*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(SizeIs(kBuffers * 2)));
332*bc0d697dSNico Weber }
333*bc0d697dSNico Weber 
334*bc0d697dSNico Weber TEST_F(BufferManagementTest, HandlesOverflowWithArgs) {
335*bc0d697dSNico Weber   uint64_t TSC = 1;
336*bc0d697dSNico Weber   uint16_t CPU = 1;
337*bc0d697dSNico Weber   uint64_t ARG = 1;
338*bc0d697dSNico Weber   for (size_t I = 0; I < kBuffers + 1; ++I) {
339*bc0d697dSNico Weber     ASSERT_TRUE(C->functionEnterArg(1, TSC++, CPU, ARG++));
340*bc0d697dSNico Weber     ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
341*bc0d697dSNico Weber   }
342*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
343*bc0d697dSNico Weber   ASSERT_THAT(BQ->finalize(), Eq(BufferQueue::ErrorCode::Ok));
344*bc0d697dSNico Weber 
345*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
346*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
347*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
348*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(SizeIs(kBuffers)));
349*bc0d697dSNico Weber }
350*bc0d697dSNico Weber 
351*bc0d697dSNico Weber TEST_F(BufferManagementTest, HandlesOverflowWithCustomEvents) {
352*bc0d697dSNico Weber   uint64_t TSC = 1;
353*bc0d697dSNico Weber   uint16_t CPU = 1;
354*bc0d697dSNico Weber   int32_t D = 0x9009;
355*bc0d697dSNico Weber   for (size_t I = 0; I < kBuffers; ++I) {
356*bc0d697dSNico Weber     ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
357*bc0d697dSNico Weber     ASSERT_TRUE(C->functionExit(1, TSC++, CPU));
358*bc0d697dSNico Weber     ASSERT_TRUE(C->customEvent(TSC++, CPU, &D, sizeof(D)));
359*bc0d697dSNico Weber   }
360*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
361*bc0d697dSNico Weber   ASSERT_THAT(BQ->finalize(), Eq(BufferQueue::ErrorCode::Ok));
362*bc0d697dSNico Weber 
363*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
364*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
365*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
366*bc0d697dSNico Weber 
367*bc0d697dSNico Weber   // We expect to also now count the kBuffers/2 custom event records showing up
368*bc0d697dSNico Weber   // in the Trace.
369*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(SizeIs(kBuffers + (kBuffers / 2))));
370*bc0d697dSNico Weber }
371*bc0d697dSNico Weber 
372*bc0d697dSNico Weber TEST_F(BufferManagementTest, HandlesFinalizedBufferQueue) {
373*bc0d697dSNico Weber   uint64_t TSC = 1;
374*bc0d697dSNico Weber   uint16_t CPU = 1;
375*bc0d697dSNico Weber 
376*bc0d697dSNico Weber   // First write one function entry.
377*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
378*bc0d697dSNico Weber 
379*bc0d697dSNico Weber   // Then we finalize the buffer queue, simulating the case where the logging
380*bc0d697dSNico Weber   // has been finalized.
381*bc0d697dSNico Weber   ASSERT_EQ(BQ->finalize(), BufferQueue::ErrorCode::Ok);
382*bc0d697dSNico Weber 
383*bc0d697dSNico Weber   // At this point further calls to the controller must fail.
384*bc0d697dSNico Weber   ASSERT_FALSE(C->functionExit(1, TSC++, CPU));
385*bc0d697dSNico Weber 
386*bc0d697dSNico Weber   // But flushing should succeed.
387*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
388*bc0d697dSNico Weber 
389*bc0d697dSNico Weber   // We expect that we'll only be able to find the function enter event, but not
390*bc0d697dSNico Weber   // the function exit event.
391*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
392*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
393*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
394*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(
395*bc0d697dSNico Weber       TraceOrErr, HasValue(ElementsAre(AllOf(
396*bc0d697dSNico Weber                       FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)))));
397*bc0d697dSNico Weber }
398*bc0d697dSNico Weber 
399*bc0d697dSNico Weber TEST_F(BufferManagementTest, HandlesGenerationalBufferQueue) {
400*bc0d697dSNico Weber   uint64_t TSC = 1;
401*bc0d697dSNico Weber   uint16_t CPU = 1;
402*bc0d697dSNico Weber 
403*bc0d697dSNico Weber   ASSERT_TRUE(C->functionEnter(1, TSC++, CPU));
404*bc0d697dSNico Weber   ASSERT_THAT(BQ->finalize(), Eq(BufferQueue::ErrorCode::Ok));
405*bc0d697dSNico Weber   ASSERT_THAT(BQ->init(sizeof(MetadataRecord) * 4 + sizeof(FunctionRecord) * 2,
406*bc0d697dSNico Weber                        kBuffers),
407*bc0d697dSNico Weber               Eq(BufferQueue::ErrorCode::Ok));
408*bc0d697dSNico Weber   EXPECT_TRUE(C->functionExit(1, TSC++, CPU));
409*bc0d697dSNico Weber   ASSERT_TRUE(C->flush());
410*bc0d697dSNico Weber 
411*bc0d697dSNico Weber   // We expect that we will only be able to find the function exit event, but
412*bc0d697dSNico Weber   // not the function enter event, since we only have information about the new
413*bc0d697dSNico Weber   // generation of the buffers.
414*bc0d697dSNico Weber   std::string Serialized = serialize(*BQ, 3);
415*bc0d697dSNico Weber   llvm::DataExtractor DE(Serialized, true, 8);
416*bc0d697dSNico Weber   auto TraceOrErr = llvm::xray::loadTrace(DE);
417*bc0d697dSNico Weber   EXPECT_THAT_EXPECTED(
418*bc0d697dSNico Weber       TraceOrErr, HasValue(ElementsAre(AllOf(
419*bc0d697dSNico Weber                       FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT)))));
420*bc0d697dSNico Weber }
421*bc0d697dSNico Weber 
422*bc0d697dSNico Weber } // namespace
423*bc0d697dSNico Weber } // namespace __xray
424