| //===-- 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 <time.h> |
| |
| #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<MetadataRecord::RecordKinds::NewBuffer>(int32_t{1}), |
| createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>( |
| int64_t{1}, int32_t{2}), |
| createMetadataRecord<MetadataRecord::RecordKinds::Pid>(int32_t{1}), |
| }; |
| ASSERT_THAT(Writer.writeMetadataRecords(Preamble), |
| Eq(sizeof(MetadataRecord) * 3)); |
| ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(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<MetadataRecord::RecordKinds::NewBuffer>(int32_t{1}), |
| createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>( |
| int64_t{1}, int32_t{2}), |
| createMetadataRecord<MetadataRecord::RecordKinds::Pid>(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<MetadataRecord::RecordKinds::NewCPUId>( |
| 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<MetadataRecord::RecordKinds::NewCPUId>( |
| 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<MetadataRecord::RecordKinds::NewBuffer>(int32_t{1}), |
| createMetadataRecord<MetadataRecord::RecordKinds::WalltimeMarker>( |
| int64_t{1}, int32_t{2}), |
| createMetadataRecord<MetadataRecord::RecordKinds::Pid>(int32_t{1}), |
| }; |
| ASSERT_THAT(Writer.writeMetadataRecords(Preamble), |
| Eq(sizeof(MetadataRecord) * 3)); |
| ASSERT_TRUE(Writer.writeMetadata<MetadataRecord::RecordKinds::NewCPUId>(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 |