//===-- fdr_log_writer_test.cpp -------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file is a part of XRay, a function call tracing system. // //===----------------------------------------------------------------------===// #include #include "test_helpers.h" #include "xray/xray_records.h" #include "xray_fdr_log_writer.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Testing/Support/Error.h" #include "llvm/XRay/Trace.h" #include "gmock/gmock.h" #include "gtest/gtest.h" namespace __xray { namespace { static constexpr size_t kSize = 4096; using ::llvm::HasValue; using ::llvm::xray::testing::FuncId; using ::llvm::xray::testing::RecordType; using ::testing::AllOf; using ::testing::ElementsAre; using ::testing::Eq; using ::testing::IsEmpty; using ::testing::IsNull; // Exercise the common code path where we initialize a buffer and are able to // write some records successfully. TEST(FdrLogWriterTest, WriteSomeRecords) { bool Success = false; BufferQueue Buffers(kSize, 1, Success); BufferQueue::Buffer B; ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok); FDRLogWriter Writer(B); MetadataRecord Preamble[] = { createMetadataRecord(int32_t{1}), createMetadataRecord( int64_t{1}, int32_t{2}), createMetadataRecord(int32_t{1}), }; ASSERT_THAT(Writer.writeMetadataRecords(Preamble), Eq(sizeof(MetadataRecord) * 3)); ASSERT_TRUE(Writer.writeMetadata(1)); ASSERT_TRUE( Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, 1)); ASSERT_TRUE( Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, 1, 1)); ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok); ASSERT_EQ(B.Data, nullptr); ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok); // We then need to go through each element of the Buffers, and re-create a // flat buffer that we would see if they were laid out in a file. This also // means we need to write out the header manually. std::string Serialized = serialize(Buffers, 3); llvm::DataExtractor DE(Serialized, true, 8); auto TraceOrErr = llvm::xray::loadTrace(DE); EXPECT_THAT_EXPECTED( TraceOrErr, HasValue(ElementsAre( AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER)), AllOf(FuncId(1), RecordType(llvm::xray::RecordTypes::EXIT))))); } // Ensure that we can handle buffer re-use. TEST(FdrLogWriterTest, ReuseBuffers) { bool Success = false; BufferQueue Buffers(kSize, 1, Success); BufferQueue::Buffer B; ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok); FDRLogWriter Writer(B); MetadataRecord Preamble[] = { createMetadataRecord(int32_t{1}), createMetadataRecord( int64_t{1}, int32_t{2}), createMetadataRecord(int32_t{1}), }; // First we write the first set of records into the single buffer in the // queue which includes one enter and one exit record. ASSERT_THAT(Writer.writeMetadataRecords(Preamble), Eq(sizeof(MetadataRecord) * 3)); ASSERT_TRUE(Writer.writeMetadata( uint16_t{1}, uint64_t{1})); uint64_t TSC = 1; ASSERT_TRUE( Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, TSC++)); ASSERT_TRUE( Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, 1, TSC++)); ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok); ASSERT_THAT(B.Data, IsNull()); // Then we re-use the buffer, but only write one record. ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok); Writer.resetRecord(); ASSERT_THAT(Writer.writeMetadataRecords(Preamble), Eq(sizeof(MetadataRecord) * 3)); ASSERT_TRUE(Writer.writeMetadata( uint16_t{1}, uint64_t{1})); ASSERT_TRUE( Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, TSC++)); ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok); ASSERT_THAT(B.Data, IsNull()); ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok); // Then we validate that we only see the single enter record. std::string Serialized = serialize(Buffers, 3); llvm::DataExtractor DE(Serialized, true, 8); auto TraceOrErr = llvm::xray::loadTrace(DE); EXPECT_THAT_EXPECTED( TraceOrErr, HasValue(ElementsAre(AllOf( FuncId(1), RecordType(llvm::xray::RecordTypes::ENTER))))); } TEST(FdrLogWriterTest, UnwriteRecords) { bool Success = false; BufferQueue Buffers(kSize, 1, Success); BufferQueue::Buffer B; ASSERT_EQ(Buffers.getBuffer(B), BufferQueue::ErrorCode::Ok); FDRLogWriter Writer(B); MetadataRecord Preamble[] = { createMetadataRecord(int32_t{1}), createMetadataRecord( int64_t{1}, int32_t{2}), createMetadataRecord(int32_t{1}), }; ASSERT_THAT(Writer.writeMetadataRecords(Preamble), Eq(sizeof(MetadataRecord) * 3)); ASSERT_TRUE(Writer.writeMetadata(1)); ASSERT_TRUE( Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Enter, 1, 1)); ASSERT_TRUE( Writer.writeFunction(FDRLogWriter::FunctionRecordKind::Exit, 1, 1)); Writer.undoWrites(sizeof(FunctionRecord) * 2); ASSERT_EQ(Buffers.releaseBuffer(B), BufferQueue::ErrorCode::Ok); ASSERT_EQ(B.Data, nullptr); ASSERT_EQ(Buffers.finalize(), BufferQueue::ErrorCode::Ok); // We've un-done the two function records we've written, and now we expect // that we don't have any function records in the trace. std::string Serialized = serialize(Buffers, 3); llvm::DataExtractor DE(Serialized, true, 8); auto TraceOrErr = llvm::xray::loadTrace(DE); EXPECT_THAT_EXPECTED(TraceOrErr, HasValue(IsEmpty())); } } // namespace } // namespace __xray