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(Buf&: B), BufferQueue::ErrorCode::Ok);
44
45 FDRLogWriter Writer(B);
46 MetadataRecord Preamble[] = {
47 createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(Ds: int32_t{1}),
48 createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(
49 Ds: int64_t{1}, Ds: int32_t{2}),
50 createMetadataRecord<MetadataRecord::RecordKinds::Pid>(Ds: int32_t{1}),
51 };
52 ASSERT_THAT(Writer.writeMetadataRecords(Recs&: Preamble),
53 Eq(sizeof(MetadataRecord) * 3));
54 ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(Ds: 1));
55 ASSERT_TRUE(
56 Writer.writeFunction(Kind: FDRLogWriter::FunctionRecordKind::Enter, FuncId: 1, Delta: 1));
57 ASSERT_TRUE(
58 Writer.writeFunction(Kind: FDRLogWriter::FunctionRecordKind::Exit, FuncId: 1, Delta: 1));
59 ASSERT_EQ(Buffers.releaseBuffer(Buf&: 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, Version: 3);
67 llvm::DataExtractor DE(Serialized, true, 8);
68 auto TraceOrErr = llvm::xray::loadTrace(Extractor: 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(Buf&: B), BufferQueue::ErrorCode::Ok);
82
83 FDRLogWriter Writer(B);
84 MetadataRecord Preamble[] = {
85 createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(Ds: int32_t{1}),
86 createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(
87 Ds: int64_t{1}, Ds: int32_t{2}),
88 createMetadataRecord<MetadataRecord::RecordKinds::Pid>(Ds: 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(Recs&: Preamble),
94 Eq(sizeof(MetadataRecord) * 3));
95 ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(
96 Ds: uint16_t{1}, Ds: uint64_t{1}));
97 uint64_t TSC = 1;
98 ASSERT_TRUE(
99 Writer.writeFunction(Kind: FDRLogWriter::FunctionRecordKind::Enter, FuncId: 1, Delta: TSC++));
100 ASSERT_TRUE(
101 Writer.writeFunction(Kind: FDRLogWriter::FunctionRecordKind::Exit, FuncId: 1, Delta: TSC++));
102 ASSERT_EQ(Buffers.releaseBuffer(Buf&: 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(Buf&: B), BufferQueue::ErrorCode::Ok);
107 Writer.resetRecord();
108 ASSERT_THAT(Writer.writeMetadataRecords(Recs&: Preamble),
109 Eq(sizeof(MetadataRecord) * 3));
110 ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(
111 Ds: uint16_t{1}, Ds: uint64_t{1}));
112 ASSERT_TRUE(
113 Writer.writeFunction(Kind: FDRLogWriter::FunctionRecordKind::Enter, FuncId: 1, Delta: TSC++));
114 ASSERT_EQ(Buffers.releaseBuffer(Buf&: 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, Version: 3);
120 llvm::DataExtractor DE(Serialized, true, 8);
121 auto TraceOrErr = llvm::xray::loadTrace(Extractor: 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(Buf&: B), BufferQueue::ErrorCode::Ok);
132
133 FDRLogWriter Writer(B);
134 MetadataRecord Preamble[] = {
135 createMetadataRecord<MetadataRecord::RecordKinds::NewBuffer>(Ds: int32_t{1}),
136 createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>(
137 Ds: int64_t{1}, Ds: int32_t{2}),
138 createMetadataRecord<MetadataRecord::RecordKinds::Pid>(Ds: int32_t{1}),
139 };
140 ASSERT_THAT(Writer.writeMetadataRecords(Recs&: Preamble),
141 Eq(sizeof(MetadataRecord) * 3));
142 ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(Ds: 1));
143 ASSERT_TRUE(
144 Writer.writeFunction(Kind: FDRLogWriter::FunctionRecordKind::Enter, FuncId: 1, Delta: 1));
145 ASSERT_TRUE(
146 Writer.writeFunction(Kind: FDRLogWriter::FunctionRecordKind::Exit, FuncId: 1, Delta: 1));
147 Writer.undoWrites(B: sizeof(FunctionRecord) * 2);
148 ASSERT_EQ(Buffers.releaseBuffer(Buf&: 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, Version: 3);
155 llvm::DataExtractor DE(Serialized, true, 8);
156 auto TraceOrErr = llvm::xray::loadTrace(Extractor: DE);
157 EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty()));
158}
159
160} // namespace
161} // namespace __xray
162

source code of compiler-rt/lib/xray/tests/unit/fdr_log_writer_test.cpp