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 OS.flush(); 56 57 // Then from here we load the Trace file. 58 DataExtractor DE(Data, sys::IsLittleEndianHost, 8); 59 auto TraceOrErr = loadTrace(DE, true); 60 if (!TraceOrErr) 61 FAIL() << TraceOrErr.takeError(); 62 auto &Trace = TraceOrErr.get(); 63 64 ASSERT_THAT(Trace, Not(IsEmpty())); 65 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)), 66 Field(&XRayRecord::FuncId, Eq(1)))); 67 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)), 68 Field(&XRayRecord::TId, Eq(1u)))); 69 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::PId, Eq(1u)), 70 Field(&XRayRecord::PId, Eq(1u)))); 71 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)), 72 Field(&XRayRecord::CPU, Eq(1u)))); 73 EXPECT_THAT(Trace, 74 ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)), 75 Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))); 76 } 77 78 // This version is almost exactly the same as above, except writing version 2 79 // logs, without the PID records. 80 TEST(FDRTraceWriterTest, WriteToStringBufferVersion2) { 81 std::string Data; 82 raw_string_ostream OS(Data); 83 XRayFileHeader H; 84 H.Version = 2; 85 H.Type = 1; 86 H.ConstantTSC = true; 87 H.NonstopTSC = true; 88 H.CycleFrequency = 3e9; 89 FDRTraceWriter Writer(OS, H); 90 auto L = LogBuilder() 91 .add<BufferExtents>(64) 92 .add<NewBufferRecord>(1) 93 .add<WallclockRecord>(1, 1) 94 .add<NewCPUIDRecord>(1, 2) 95 .add<FunctionRecord>(RecordTypes::ENTER, 1, 1) 96 .add<FunctionRecord>(RecordTypes::EXIT, 1, 100) 97 .consume(); 98 for (auto &P : L) 99 ASSERT_FALSE(errorToBool(P->apply(Writer))); 100 OS.flush(); 101 102 // Then from here we load the Trace file. 103 DataExtractor DE(Data, sys::IsLittleEndianHost, 8); 104 auto TraceOrErr = loadTrace(DE, true); 105 if (!TraceOrErr) 106 FAIL() << TraceOrErr.takeError(); 107 auto &Trace = TraceOrErr.get(); 108 109 ASSERT_THAT(Trace, Not(IsEmpty())); 110 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)), 111 Field(&XRayRecord::FuncId, Eq(1)))); 112 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)), 113 Field(&XRayRecord::TId, Eq(1u)))); 114 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)), 115 Field(&XRayRecord::CPU, Eq(1u)))); 116 EXPECT_THAT(Trace, 117 ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)), 118 Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))); 119 } 120 121 // This covers version 1 of the log, without a BufferExtents record but has an 122 // explicit EndOfBuffer record. 123 TEST(FDRTraceWriterTest, WriteToStringBufferVersion1) { 124 std::string Data; 125 raw_string_ostream OS(Data); 126 XRayFileHeader H; 127 H.Version = 1; 128 H.Type = 1; 129 H.ConstantTSC = true; 130 H.NonstopTSC = true; 131 H.CycleFrequency = 3e9; 132 // Write the size of buffers out, arbitrarily it's 4k. 133 constexpr uint64_t BufferSize = 4096; 134 std::memcpy(H.FreeFormData, reinterpret_cast<const char *>(&BufferSize), 135 sizeof(BufferSize)); 136 FDRTraceWriter Writer(OS, H); 137 OS.flush(); 138 139 // Ensure that at this point the Data buffer has the file header serialized 140 // size. 141 ASSERT_THAT(Data.size(), Eq(32u)); 142 auto L = LogBuilder() 143 .add<NewBufferRecord>(1) 144 .add<WallclockRecord>(1, 1) 145 .add<NewCPUIDRecord>(1, 2) 146 .add<FunctionRecord>(RecordTypes::ENTER, 1, 1) 147 .add<FunctionRecord>(RecordTypes::EXIT, 1, 100) 148 .add<EndBufferRecord>() 149 .consume(); 150 for (auto &P : L) 151 ASSERT_FALSE(errorToBool(P->apply(Writer))); 152 153 // We need to pad the buffer with 4016 (4096 - 80) bytes of zeros. 154 OS.write_zeros(4016); 155 OS.flush(); 156 157 // For version 1 of the log, we need the whole buffer to be the size of the 158 // file header plus 32. 159 ASSERT_THAT(Data.size(), Eq(BufferSize + 32)); 160 161 // Then from here we load the Trace file. 162 DataExtractor DE(Data, sys::IsLittleEndianHost, 8); 163 auto TraceOrErr = loadTrace(DE, true); 164 if (!TraceOrErr) 165 FAIL() << TraceOrErr.takeError(); 166 auto &Trace = TraceOrErr.get(); 167 168 ASSERT_THAT(Trace, Not(IsEmpty())); 169 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::FuncId, Eq(1)), 170 Field(&XRayRecord::FuncId, Eq(1)))); 171 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::TId, Eq(1u)), 172 Field(&XRayRecord::TId, Eq(1u)))); 173 EXPECT_THAT(Trace, ElementsAre(Field(&XRayRecord::CPU, Eq(1u)), 174 Field(&XRayRecord::CPU, Eq(1u)))); 175 EXPECT_THAT(Trace, 176 ElementsAre(Field(&XRayRecord::Type, Eq(RecordTypes::ENTER)), 177 Field(&XRayRecord::Type, Eq(RecordTypes::EXIT)))); 178 } 179 180 } // namespace 181 } // namespace xray 182 } // namespace llvm 183