1//===-- fdr_log_writer_test.cpp -------------------------------------------===//
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// This file is a part of XRay, a function call tracing system.
10//
11//===----------------------------------------------------------------------===//
12#include <time.h>
13
14#include "test_helpers.h"
15#include "xray/xray_records.h"
16#include "xray_fdr_log_writer.h"
17#include "llvm/Support/DataExtractor.h"
18#include "llvm/Testing/Support/Error.h"
19#include "llvm/XRay/Trace.h"
20#include "gmock/gmock.h"
21#include "gtest/gtest.h"
22
23namespace __xray {
24namespace {
25
26static constexpr size_t kSize = 4096;
27
28using ::llvm::HasValue;
29using ::llvm::xray::testing::FuncId;
30using ::llvm::xray::testing::RecordType;
31using ::testing::AllOf;
32using ::testing::ElementsAre;
33using ::testing::Eq;
34using ::testing::IsEmpty;
35using ::testing::IsNull;
36
37// Exercise the common code path where we initialize a buffer and are able to
38// write some records successfully.
39TEST(FdrLogWriterTest, WriteSomeRecords) {
40  bool Success = false;
41  BufferQueue Buffers(kSize, 1, Success);
42  BufferQueue::Buffer B;
43  ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok);
44
45  FDRLogWriter Writer(B);
46  MetadataRecord Preamble[] = {
47      createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(int32_t{1}),
48      createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(
49          int64_t{1}, int32_t{2}),
50      createMetadataRecord<MetadataRecord::RecordKinds::Pid>(int32_t{1}),
51  };
52  ASSERT_THAT(Writer.writeMetadataRecords(Preamble),
53              Eq(sizeof(MetadataRecord) * 3));
54  ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(1));
55  ASSERT_TRUE(
56      Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, 1));
57  ASSERT_TRUE(
58      Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, 1, 1));
59  ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
60  ASSERT_EQ(B.Data, nullptr);
61  ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
62
63  // We then need to go through each element of the Buffers, and re-create a
64  // flat buffer that we would see if they were laid out in a file. This also
65  // means we need to write out the header manually.
66  std::string Serialized = serialize(Buffers, 3);
67  llvm::DataExtractor DE(Serialized, true, 8);
68  auto TraceOrErr = llvm::xray::loadTrace(DE);
69  EXPECT_THAT_EXPECTED(
70      TraceOrErr,
71      HasValue(ElementsAre(
72          AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)),
73          AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT)))));
74}
75
76// Ensure that we can handle buffer re-use.
77TEST(FdrLogWriterTest, ReuseBuffers) {
78  bool Success = false;
79  BufferQueue Buffers(kSize, 1, Success);
80  BufferQueue::Buffer B;
81  ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok);
82
83  FDRLogWriter Writer(B);
84  MetadataRecord Preamble[] = {
85      createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(int32_t{1}),
86      createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(
87          int64_t{1}, int32_t{2}),
88      createMetadataRecord<MetadataRecord::RecordKinds::Pid>(int32_t{1}),
89  };
90
91  // First we write the first set of records into the single buffer in the
92  // queue which includes one enter and one exit record.
93  ASSERT_THAT(Writer.writeMetadataRecords(Preamble),
94              Eq(sizeof(MetadataRecord) * 3));
95  ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(
96      uint16_t{1}, uint64_t{1}));
97  uint64_t TSC = 1;
98  ASSERT_TRUE(
99      Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, TSC++));
100  ASSERT_TRUE(
101      Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, 1, TSC++));
102  ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
103  ASSERT_THAT(B.Data, IsNull());
104
105  // Then we re-use the buffer, but only write one record.
106  ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok);
107  Writer.resetRecord();
108  ASSERT_THAT(Writer.writeMetadataRecords(Preamble),
109              Eq(sizeof(MetadataRecord) * 3));
110  ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(
111      uint16_t{1}, uint64_t{1}));
112  ASSERT_TRUE(
113      Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, TSC++));
114  ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
115  ASSERT_THAT(B.Data, IsNull());
116  ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
117
118  // Then we validate that we only see the single enter record.
119  std::string Serialized = serialize(Buffers, 3);
120  llvm::DataExtractor DE(Serialized, true, 8);
121  auto TraceOrErr = llvm::xray::loadTrace(DE);
122  EXPECT_THAT_EXPECTED(
123      TraceOrErr, HasValue(ElementsAre(AllOf(
124                      FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)))));
125}
126
127TEST(FdrLogWriterTest, UnwriteRecords) {
128  bool Success = false;
129  BufferQueue Buffers(kSize, 1, Success);
130  BufferQueue::Buffer B;
131  ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok);
132
133  FDRLogWriter Writer(B);
134  MetadataRecord Preamble[] = {
135      createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(int32_t{1}),
136      createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(
137          int64_t{1}, int32_t{2}),
138      createMetadataRecord<MetadataRecord::RecordKinds::Pid>(int32_t{1}),
139  };
140  ASSERT_THAT(Writer.writeMetadataRecords(Preamble),
141              Eq(sizeof(MetadataRecord) * 3));
142  ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(1));
143  ASSERT_TRUE(
144      Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, 1));
145  ASSERT_TRUE(
146      Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, 1, 1));
147  Writer.undoWrites(sizeof(FunctionRecord) * 2);
148  ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok);
149  ASSERT_EQ(B.Data, nullptr);
150  ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok);
151
152  // We've un-done the two function records we've written, and now we expect
153  // that we don't have any function records in the trace.
154  std::string Serialized = serialize(Buffers, 3);
155  llvm::DataExtractor DE(Serialized, true, 8);
156  auto TraceOrErr = llvm::xray::loadTrace(DE);
157  EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty()));
158}
159
160} // namespace
161} // namespace __xray
162