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