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 23 namespace __xray { 24 namespace { 25 26 static constexpr size_t kSize = 4096; 27 28 using ::llvm::HasValue; 29 using ::llvm::xray::testing::FuncId; 30 using ::llvm::xray::testing::RecordType; 31 using ::testing::AllOf; 32 using ::testing::ElementsAre; 33 using ::testing::Eq; 34 using ::testing::IsEmpty; 35 using ::testing::IsNull; 36 37 // Exercise the common code path where we initialize a buffer and are able to 38 // write some records successfully. 39 TEST(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. 77 TEST(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 127 TEST(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