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>(A: 80) |
46 | .add<NewBufferRecord>(A: 1) |
47 | .add<WallclockRecord>(A: 1, A: 1) |
48 | .add<PIDRecord>(A: 1) |
49 | .add<NewCPUIDRecord>(A: 1, A: 2) |
50 | .add<FunctionRecord>(A: RecordTypes::ENTER, A: 1, A: 1) |
51 | .add<FunctionRecord>(A: RecordTypes::EXIT, A: 1, A: 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(Extractor: DE, Sort: 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>(A: 64) |
92 | .add<NewBufferRecord>(A: 1) |
93 | .add<WallclockRecord>(A: 1, A: 1) |
94 | .add<NewCPUIDRecord>(A: 1, A: 2) |
95 | .add<FunctionRecord>(A: RecordTypes::ENTER, A: 1, A: 1) |
96 | .add<FunctionRecord>(A: RecordTypes::EXIT, A: 1, A: 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(Extractor: DE, Sort: 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(dest: H.FreeFormData, src: reinterpret_cast<const char *>(&BufferSize), |
135 | n: 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>(A: 1) |
144 | .add<WallclockRecord>(A: 1, A: 1) |
145 | .add<NewCPUIDRecord>(A: 1, A: 2) |
146 | .add<FunctionRecord>(A: RecordTypes::ENTER, A: 1, A: 1) |
147 | .add<FunctionRecord>(A: RecordTypes::EXIT, A: 1, A: 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(NumZeros: 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(Extractor: DE, Sort: 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 | |