//===- llvm/unittest/XRay/FDRTraceWriterTest.cpp ----------------*- C++ -*-===// // // 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 // //===----------------------------------------------------------------------===// // // Test a utility that can write out XRay FDR Mode formatted trace files. // //===----------------------------------------------------------------------===// #include "llvm/XRay/FDRTraceWriter.h" #include "llvm/Support/raw_ostream.h" #include "llvm/XRay/FDRLogBuilder.h" #include "llvm/XRay/FDRRecords.h" #include "llvm/XRay/Trace.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include namespace llvm { namespace xray { namespace { using testing::ElementsAre; using testing::Eq; using testing::Field; using testing::IsEmpty; using testing::Not; // We want to be able to create an instance of an FDRTraceWriter and associate // it with a stream, which could be loaded and turned into a Trace instance. // This test writes out version 3 trace logs. TEST(FDRTraceWriterTest, WriteToStringBufferVersion3) { std::string Data; raw_string_ostream OS(Data); XRayFileHeader H; H.Version = 3; H.Type = 1; H.ConstantTSC = true; H.NonstopTSC = true; H.CycleFrequency = 3e9; FDRTraceWriter Writer(OS, H); auto L = LogBuilder() .add(80) .add(1) .add(1, 1) .add(1) .add(1, 2) .add(RecordTypes::ENTER, 1, 1) .add(RecordTypes::EXIT, 1, 100) .consume(); for (auto &P : L) ASSERT_FALSE(errorToBool(P->apply(Writer))); OS.flush(); // Then from here we load the Trace file. DataExtractor DE(Data, sys::IsLittleEndianHost, 8); auto TraceOrErr = loadTrace(DE, true); if (!TraceOrErr) FAIL() << TraceOrErr.takeError(); auto &Trace = TraceOrErr.get(); ASSERT_THAT(Trace, Not(IsEmpty())); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)), Field(&XRayRecord::FuncId, Eq(1)))); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)), Field(&XRayRecord::TId, Eq(1u)))); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::PId, Eq(1u)), Field(&XRayRecord::PId, Eq(1u)))); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)), Field(&XRayRecord::CPU, Eq(1u)))); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)), Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))); } // This version is almost exactly the same as above, except writing version 2 // logs, without the PID records. TEST(FDRTraceWriterTest, WriteToStringBufferVersion2) { std::string Data; raw_string_ostream OS(Data); XRayFileHeader H; H.Version = 2; H.Type = 1; H.ConstantTSC = true; H.NonstopTSC = true; H.CycleFrequency = 3e9; FDRTraceWriter Writer(OS, H); auto L = LogBuilder() .add(64) .add(1) .add(1, 1) .add(1, 2) .add(RecordTypes::ENTER, 1, 1) .add(RecordTypes::EXIT, 1, 100) .consume(); for (auto &P : L) ASSERT_FALSE(errorToBool(P->apply(Writer))); OS.flush(); // Then from here we load the Trace file. DataExtractor DE(Data, sys::IsLittleEndianHost, 8); auto TraceOrErr = loadTrace(DE, true); if (!TraceOrErr) FAIL() << TraceOrErr.takeError(); auto &Trace = TraceOrErr.get(); ASSERT_THAT(Trace, Not(IsEmpty())); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)), Field(&XRayRecord::FuncId, Eq(1)))); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)), Field(&XRayRecord::TId, Eq(1u)))); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)), Field(&XRayRecord::CPU, Eq(1u)))); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)), Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))); } // This covers version 1 of the log, without a BufferExtents record but has an // explicit EndOfBuffer record. TEST(FDRTraceWriterTest, WriteToStringBufferVersion1) { std::string Data; raw_string_ostream OS(Data); XRayFileHeader H; H.Version = 1; H.Type = 1; H.ConstantTSC = true; H.NonstopTSC = true; H.CycleFrequency = 3e9; // Write the size of buffers out, arbitrarily it's 4k. constexpr uint64_t BufferSize = 4096; std::memcpy(H.FreeFormData, reinterpret_cast(&BufferSize), sizeof(BufferSize)); FDRTraceWriter Writer(OS, H); OS.flush(); // Ensure that at this point the Data buffer has the file header serialized // size. ASSERT_THAT(Data.size(), Eq(32u)); auto L = LogBuilder() .add(1) .add(1, 1) .add(1, 2) .add(RecordTypes::ENTER, 1, 1) .add(RecordTypes::EXIT, 1, 100) .add() .consume(); for (auto &P : L) ASSERT_FALSE(errorToBool(P->apply(Writer))); // We need to pad the buffer with 4016 (4096 - 80) bytes of zeros. OS.write_zeros(4016); OS.flush(); // For version 1 of the log, we need the whole buffer to be the size of the // file header plus 32. ASSERT_THAT(Data.size(), Eq(BufferSize + 32)); // Then from here we load the Trace file. DataExtractor DE(Data, sys::IsLittleEndianHost, 8); auto TraceOrErr = loadTrace(DE, true); if (!TraceOrErr) FAIL() << TraceOrErr.takeError(); auto &Trace = TraceOrErr.get(); ASSERT_THAT(Trace, Not(IsEmpty())); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)), Field(&XRayRecord::FuncId, Eq(1)))); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)), Field(&XRayRecord::TId, Eq(1u)))); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)), Field(&XRayRecord::CPU, Eq(1u)))); EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)), Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))); } } // namespace } // namespace xray } // namespace llvm