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