|  | //===- 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 <string> | 
|  |  | 
|  | 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<BufferExtents>(80) | 
|  | .add<NewBufferRecord>(1) | 
|  | .add<WallclockRecord>(1, 1) | 
|  | .add<PIDRecord>(1) | 
|  | .add<NewCPUIDRecord>(1, 2) | 
|  | .add<FunctionRecord>(RecordTypes::ENTER, 1, 1) | 
|  | .add<FunctionRecord>(RecordTypes::EXIT, 1, 100) | 
|  | .consume(); | 
|  | for (auto &P : L) | 
|  | ASSERT_FALSE(errorToBool(P->apply(Writer))); | 
|  |  | 
|  | // 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<BufferExtents>(64) | 
|  | .add<NewBufferRecord>(1) | 
|  | .add<WallclockRecord>(1, 1) | 
|  | .add<NewCPUIDRecord>(1, 2) | 
|  | .add<FunctionRecord>(RecordTypes::ENTER, 1, 1) | 
|  | .add<FunctionRecord>(RecordTypes::EXIT, 1, 100) | 
|  | .consume(); | 
|  | for (auto &P : L) | 
|  | ASSERT_FALSE(errorToBool(P->apply(Writer))); | 
|  |  | 
|  | // 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<const char *>(&BufferSize), | 
|  | sizeof(BufferSize)); | 
|  | FDRTraceWriter Writer(OS, H); | 
|  |  | 
|  | // Ensure that at this point the Data buffer has the file header serialized | 
|  | // size. | 
|  | ASSERT_THAT(Data.size(), Eq(32u)); | 
|  | auto L = LogBuilder() | 
|  | .add<NewBufferRecord>(1) | 
|  | .add<WallclockRecord>(1, 1) | 
|  | .add<NewCPUIDRecord>(1, 2) | 
|  | .add<FunctionRecord>(RecordTypes::ENTER, 1, 1) | 
|  | .add<FunctionRecord>(RecordTypes::EXIT, 1, 100) | 
|  | .add<EndBufferRecord>() | 
|  | .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); | 
|  |  | 
|  | // 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 |