1 //===- llvm/unittest/XRay/FDRTraceWriterTest.cpp ----------------*- C++ -*-===// 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 // Test a utility that can write out XRay FDR Mode formatted trace files. 10 // 11 //===----------------------------------------------------------------------===// 12 #include "llvm/XRay/FDRTraceWriter.h" 13 #include "llvm/Support/raw_ostream.h" 14 #include "llvm/XRay/FDRLogBuilder.h" 15 #include "llvm/XRay/FDRRecords.h" 16 #include "llvm/XRay/Trace.h" 17 #include "gmock/gmock.h" 18 #include "gtest/gtest.h" 19 #include <string> 20 21 namespace llvm { 22 namespace xray { 23 namespace { 24 25 using testing::ElementsAre; 26 using testing::Eq; 27 using testing::Field; 28 using testing::IsEmpty; 29 using testing::Not; 30 31 // We want to be able to create an instance of an FDRTraceWriter and associate 32 // it with a stream, which could be loaded and turned into a Trace instance. 33 // This test writes out version 3 trace logs. 34 TEST(FDRTraceWriterTest, WriteToStringBufferVersion3) { 35 std::string Data; 36 raw_string_ostream OS(Data); 37 XRayFileHeader H; 38 H.Version = 3; 39 H.Type = 1; 40 H.ConstantTSC = true; 41 H.NonstopTSC = true; 42 H.CycleFrequency = 3e9; 43 FDRTraceWriter Writer(OS, H); 44 auto L = LogBuilder() 45 .add<BufferExtents>(80) 46 .add<NewBufferRecord>(1) 47 .add<WallclockRecord>(1, 1) 48 .add<PIDRecord>(1) 49 .add<NewCPUIDRecord>(1, 2) 50 .add<FunctionRecord>(RecordTypes::ENTER, 1, 1) 51 .add<FunctionRecord>(RecordTypes::EXIT, 1, 100) 52 .consume(); 53 for (auto &P : L) 54 ASSERT_FALSE(errorToBool(P->apply(Writer))); 55 56 // Then from here we load the Trace file. 57 DataExtractor DE(Data, sys::IsLittleEndianHost, 8); 58 auto TraceOrErr = loadTrace(DE, true); 59 if (!TraceOrErr) 60 FAIL() << TraceOrErr.takeError(); 61 auto &Trace = TraceOrErr.get(); 62 63 ASSERT_THAT(Trace, Not(IsEmpty())); 64 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)), 65 Field(&XRayRecord::FuncId, Eq(1)))); 66 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)), 67 Field(&XRayRecord::TId, Eq(1u)))); 68 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::PId, Eq(1u)), 69 Field(&XRayRecord::PId, Eq(1u)))); 70 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)), 71 Field(&XRayRecord::CPU, Eq(1u)))); 72 EXPECT_THAT(Trace, 73 ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)), 74 Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))); 75 } 76 77 // This version is almost exactly the same as above, except writing version 2 78 // logs, without the PID records. 79 TEST(FDRTraceWriterTest, WriteToStringBufferVersion2) { 80 std::string Data; 81 raw_string_ostream OS(Data); 82 XRayFileHeader H; 83 H.Version = 2; 84 H.Type = 1; 85 H.ConstantTSC = true; 86 H.NonstopTSC = true; 87 H.CycleFrequency = 3e9; 88 FDRTraceWriter Writer(OS, H); 89 auto L = LogBuilder() 90 .add<BufferExtents>(64) 91 .add<NewBufferRecord>(1) 92 .add<WallclockRecord>(1, 1) 93 .add<NewCPUIDRecord>(1, 2) 94 .add<FunctionRecord>(RecordTypes::ENTER, 1, 1) 95 .add<FunctionRecord>(RecordTypes::EXIT, 1, 100) 96 .consume(); 97 for (auto &P : L) 98 ASSERT_FALSE(errorToBool(P->apply(Writer))); 99 100 // Then from here we load the Trace file. 101 DataExtractor DE(Data, sys::IsLittleEndianHost, 8); 102 auto TraceOrErr = loadTrace(DE, true); 103 if (!TraceOrErr) 104 FAIL() << TraceOrErr.takeError(); 105 auto &Trace = TraceOrErr.get(); 106 107 ASSERT_THAT(Trace, Not(IsEmpty())); 108 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)), 109 Field(&XRayRecord::FuncId, Eq(1)))); 110 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)), 111 Field(&XRayRecord::TId, Eq(1u)))); 112 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)), 113 Field(&XRayRecord::CPU, Eq(1u)))); 114 EXPECT_THAT(Trace, 115 ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)), 116 Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))); 117 } 118 119 // This covers version 1 of the log, without a BufferExtents record but has an 120 // explicit EndOfBuffer record. 121 TEST(FDRTraceWriterTest, WriteToStringBufferVersion1) { 122 std::string Data; 123 raw_string_ostream OS(Data); 124 XRayFileHeader H; 125 H.Version = 1; 126 H.Type = 1; 127 H.ConstantTSC = true; 128 H.NonstopTSC = true; 129 H.CycleFrequency = 3e9; 130 // Write the size of buffers out, arbitrarily it's 4k. 131 constexpr uint64_t BufferSize = 4096; 132 std::memcpy(H.FreeFormData, reinterpret_cast<const char *>(&BufferSize), 133 sizeof(BufferSize)); 134 FDRTraceWriter Writer(OS, H); 135 136 // Ensure that at this point the Data buffer has the file header serialized 137 // size. 138 ASSERT_THAT(Data.size(), Eq(32u)); 139 auto L = LogBuilder() 140 .add<NewBufferRecord>(1) 141 .add<WallclockRecord>(1, 1) 142 .add<NewCPUIDRecord>(1, 2) 143 .add<FunctionRecord>(RecordTypes::ENTER, 1, 1) 144 .add<FunctionRecord>(RecordTypes::EXIT, 1, 100) 145 .add<EndBufferRecord>() 146 .consume(); 147 for (auto &P : L) 148 ASSERT_FALSE(errorToBool(P->apply(Writer))); 149 150 // We need to pad the buffer with 4016 (4096 - 80) bytes of zeros. 151 OS.write_zeros(4016); 152 153 // For version 1 of the log, we need the whole buffer to be the size of the 154 // file header plus 32. 155 ASSERT_THAT(Data.size(), Eq(BufferSize + 32)); 156 157 // Then from here we load the Trace file. 158 DataExtractor DE(Data, sys::IsLittleEndianHost, 8); 159 auto TraceOrErr = loadTrace(DE, true); 160 if (!TraceOrErr) 161 FAIL() << TraceOrErr.takeError(); 162 auto &Trace = TraceOrErr.get(); 163 164 ASSERT_THAT(Trace, Not(IsEmpty())); 165 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)), 166 Field(&XRayRecord::FuncId, Eq(1)))); 167 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)), 168 Field(&XRayRecord::TId, Eq(1u)))); 169 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)), 170 Field(&XRayRecord::CPU, Eq(1u)))); 171 EXPECT_THAT(Trace, 172 ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)), 173 Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))); 174 } 175 176 } // namespace 177 } // namespace xray 178 } // namespace llvm 179