1//===- unittests/Support/BitstreamRemarksParsingTest.cpp - Parsing tests --===//
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#include "llvm-c/Remarks.h"
10#include "llvm/Remarks/Remark.h"
11#include "llvm/Remarks/RemarkParser.h"
12#include "llvm/Remarks/RemarkSerializer.h"
13#include "gtest/gtest.h"
14
15using namespace llvm;
16
17template <size_t N> void parseGood(const char (&Buf)[N]) {
18 // 1. Parse the YAML remark -> FromYAMLRemark
19 // 2. Serialize it to bitstream -> BSStream
20 // 3. Parse it back -> FromBSRemark
21 // 4. Compare the remark objects
22 //
23 // This testing methodology has the drawback of relying on both the YAML
24 // remark parser and the bitstream remark serializer. It does simplify
25 // testing a lot, since working directly with bitstream is not that easy.
26
27 // 1.
28 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
29 remarks::createRemarkParser(remarks::Format::YAML, {Buf, N - 1});
30 EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
31 EXPECT_TRUE(*MaybeParser != nullptr);
32
33 std::unique_ptr<remarks::Remark> FromYAMLRemark = nullptr;
34 remarks::RemarkParser &Parser = **MaybeParser;
35 Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();
36 EXPECT_FALSE(errorToBool(Remark.takeError())); // Check for parsing errors.
37 EXPECT_TRUE(*Remark != nullptr); // At least one remark.
38 // Keep the previous remark around.
39 FromYAMLRemark = std::move(*Remark);
40 Remark = Parser.next();
41 Error E = Remark.takeError();
42 EXPECT_TRUE(E.isA<remarks::EndOfFileError>());
43 EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.
44
45 // 2.
46 remarks::StringTable BSStrTab;
47 BSStrTab.internalize(R&: *FromYAMLRemark);
48 std::string BSBuf;
49 raw_string_ostream BSStream(BSBuf);
50 Expected<std::unique_ptr<remarks::RemarkSerializer>> BSSerializer =
51 remarks::createRemarkSerializer(RemarksFormat: remarks::Format::Bitstream,
52 Mode: remarks::SerializerMode::Standalone,
53 OS&: BSStream, StrTab: std::move(BSStrTab));
54 EXPECT_FALSE(errorToBool(BSSerializer.takeError()));
55 (*BSSerializer)->emit(Remark: *FromYAMLRemark);
56
57 // 3.
58 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeBSParser =
59 remarks::createRemarkParser(ParserFormat: remarks::Format::Bitstream, Buf: BSStream.str());
60 EXPECT_FALSE(errorToBool(MaybeBSParser.takeError()));
61 EXPECT_TRUE(*MaybeBSParser != nullptr);
62
63 std::unique_ptr<remarks::Remark> FromBSRemark = nullptr;
64 remarks::RemarkParser &BSParser = **MaybeBSParser;
65 Expected<std::unique_ptr<remarks::Remark>> BSRemark = BSParser.next();
66 EXPECT_FALSE(errorToBool(BSRemark.takeError())); // Check for parsing errors.
67 EXPECT_TRUE(*BSRemark != nullptr); // At least one remark.
68 // Keep the previous remark around.
69 FromBSRemark = std::move(*BSRemark);
70 BSRemark = BSParser.next();
71 Error BSE = BSRemark.takeError();
72 EXPECT_TRUE(BSE.isA<remarks::EndOfFileError>());
73 EXPECT_TRUE(errorToBool(std::move(BSE))); // Check for parsing errors.
74
75 EXPECT_EQ(*FromYAMLRemark, *FromBSRemark);
76}
77
78TEST(BitstreamRemarks, ParsingGood) {
79 parseGood(Buf: "\n"
80 "--- !Missed\n"
81 "Pass: inline\n"
82 "Name: NoDefinition\n"
83 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
84 "Function: foo\n"
85 "Args:\n"
86 " - Callee: bar\n"
87 " - String: ' will not be inlined into '\n"
88 " - Caller: foo\n"
89 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
90 " - String: ' because its definition is unavailable'\n"
91 "");
92
93 // No debug loc should also pass.
94 parseGood(Buf: "\n"
95 "--- !Missed\n"
96 "Pass: inline\n"
97 "Name: NoDefinition\n"
98 "Function: foo\n"
99 "Args:\n"
100 " - Callee: bar\n"
101 " - String: ' will not be inlined into '\n"
102 " - Caller: foo\n"
103 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
104 " - String: ' because its definition is unavailable'\n"
105 "");
106
107 // No args is also ok.
108 parseGood(Buf: "\n"
109 "--- !Missed\n"
110 "Pass: inline\n"
111 "Name: NoDefinition\n"
112 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
113 "Function: foo\n"
114 "");
115}
116
117// Mandatory common part of a remark.
118#define COMMON_REMARK "\nPass: inline\nName: NoDefinition\nFunction: foo\n\n"
119// Test all the types.
120TEST(BitstreamRemarks, ParsingTypes) {
121 // Type: Passed
122 parseGood(Buf: "--- !Passed" COMMON_REMARK);
123 // Type: Missed
124 parseGood(Buf: "--- !Missed" COMMON_REMARK);
125 // Type: Analysis
126 parseGood(Buf: "--- !Analysis" COMMON_REMARK);
127 // Type: AnalysisFPCommute
128 parseGood(Buf: "--- !AnalysisFPCommute" COMMON_REMARK);
129 // Type: AnalysisAliasing
130 parseGood(Buf: "--- !AnalysisAliasing" COMMON_REMARK);
131 // Type: Failure
132 parseGood(Buf: "--- !Failure" COMMON_REMARK);
133}
134#undef COMMON_REMARK
135
136static inline StringRef checkStr(StringRef Str, unsigned ExpectedLen) {
137 const char *StrData = Str.data();
138 unsigned StrLen = Str.size();
139 EXPECT_EQ(StrLen, ExpectedLen);
140 return StringRef(StrData, StrLen);
141}
142
143TEST(BitstreamRemarks, Contents) {
144 StringRef Buf = "\n"
145 "--- !Missed\n"
146 "Pass: inline\n"
147 "Name: NoDefinition\n"
148 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
149 "Function: foo\n"
150 "Hotness: 4\n"
151 "Args:\n"
152 " - Callee: bar\n"
153 " - String: ' will not be inlined into '\n"
154 " - Caller: foo\n"
155 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
156 " - String: ' because its definition is unavailable'\n"
157 "\n";
158
159 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
160 remarks::createRemarkParser(ParserFormat: remarks::Format::YAML, Buf);
161 EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
162 EXPECT_TRUE(*MaybeParser != nullptr);
163
164 remarks::RemarkParser &Parser = **MaybeParser;
165 Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();
166 EXPECT_FALSE(
167 errorToBool(MaybeRemark.takeError())); // Check for parsing errors.
168 EXPECT_TRUE(*MaybeRemark != nullptr); // At least one remark.
169
170 const remarks::Remark &Remark = **MaybeRemark;
171 EXPECT_EQ(Remark.RemarkType, remarks::Type::Missed);
172 EXPECT_EQ(checkStr(Remark.PassName, 6), "inline");
173 EXPECT_EQ(checkStr(Remark.RemarkName, 12), "NoDefinition");
174 EXPECT_EQ(checkStr(Remark.FunctionName, 3), "foo");
175 EXPECT_TRUE(Remark.Loc);
176 const remarks::RemarkLocation &RL = *Remark.Loc;
177 EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
178 EXPECT_EQ(RL.SourceLine, 3U);
179 EXPECT_EQ(RL.SourceColumn, 12U);
180 EXPECT_TRUE(Remark.Hotness);
181 EXPECT_EQ(*Remark.Hotness, 4U);
182 EXPECT_EQ(Remark.Args.size(), 4U);
183
184 unsigned ArgID = 0;
185 for (const remarks::Argument &Arg : Remark.Args) {
186 switch (ArgID) {
187 case 0:
188 EXPECT_EQ(checkStr(Arg.Key, 6), "Callee");
189 EXPECT_EQ(checkStr(Arg.Val, 3), "bar");
190 EXPECT_FALSE(Arg.Loc);
191 break;
192 case 1:
193 EXPECT_EQ(checkStr(Arg.Key, 6), "String");
194 EXPECT_EQ(checkStr(Arg.Val, 26), " will not be inlined into ");
195 EXPECT_FALSE(Arg.Loc);
196 break;
197 case 2: {
198 EXPECT_EQ(checkStr(Arg.Key, 6), "Caller");
199 EXPECT_EQ(checkStr(Arg.Val, 3), "foo");
200 EXPECT_TRUE(Arg.Loc);
201 const remarks::RemarkLocation &RL = *Arg.Loc;
202 EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
203 EXPECT_EQ(RL.SourceLine, 2U);
204 EXPECT_EQ(RL.SourceColumn, 0U);
205 break;
206 }
207 case 3:
208 EXPECT_EQ(checkStr(Arg.Key, 6), "String");
209 EXPECT_EQ(checkStr(Arg.Val, 38),
210 " because its definition is unavailable");
211 EXPECT_FALSE(Arg.Loc);
212 break;
213 default:
214 break;
215 }
216 ++ArgID;
217 }
218
219 MaybeRemark = Parser.next();
220 Error E = MaybeRemark.takeError();
221 EXPECT_TRUE(E.isA<remarks::EndOfFileError>());
222 EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.
223}
224
225static inline StringRef checkStr(LLVMRemarkStringRef Str,
226 unsigned ExpectedLen) {
227 const char *StrData = LLVMRemarkStringGetData(String: Str);
228 unsigned StrLen = LLVMRemarkStringGetLen(String: Str);
229 EXPECT_EQ(StrLen, ExpectedLen);
230 return StringRef(StrData, StrLen);
231}
232
233TEST(BitstreamRemarks, ContentsCAPI) {
234 remarks::StringTable BSStrTab;
235 remarks::Remark ToSerializeRemark;
236 ToSerializeRemark.RemarkType = remarks::Type::Missed;
237 ToSerializeRemark.PassName = "inline";
238 ToSerializeRemark.RemarkName = "NoDefinition";
239 ToSerializeRemark.FunctionName = "foo";
240 ToSerializeRemark.Loc = remarks::RemarkLocation{.SourceFilePath: "file.c", .SourceLine: 3, .SourceColumn: 12};
241 ToSerializeRemark.Hotness = 0;
242 ToSerializeRemark.Args.emplace_back();
243 ToSerializeRemark.Args.back().Key = "Callee";
244 ToSerializeRemark.Args.back().Val = "bar";
245 ToSerializeRemark.Args.emplace_back();
246 ToSerializeRemark.Args.back().Key = "String";
247 ToSerializeRemark.Args.back().Val = " will not be inlined into ";
248 ToSerializeRemark.Args.emplace_back();
249 ToSerializeRemark.Args.back().Key = "Caller";
250 ToSerializeRemark.Args.back().Val = "foo";
251 ToSerializeRemark.Args.back().Loc = remarks::RemarkLocation{.SourceFilePath: "file.c", .SourceLine: 2, .SourceColumn: 0};
252 ToSerializeRemark.Args.emplace_back();
253 ToSerializeRemark.Args.back().Key = "String";
254 ToSerializeRemark.Args.back().Val = " because its definition is unavailable";
255 BSStrTab.internalize(R&: ToSerializeRemark);
256 std::string BSBuf;
257 raw_string_ostream BSStream(BSBuf);
258 Expected<std::unique_ptr<remarks::RemarkSerializer>> BSSerializer =
259 remarks::createRemarkSerializer(RemarksFormat: remarks::Format::Bitstream,
260 Mode: remarks::SerializerMode::Standalone,
261 OS&: BSStream, StrTab: std::move(BSStrTab));
262 EXPECT_FALSE(errorToBool(BSSerializer.takeError()));
263 (*BSSerializer)->emit(Remark: ToSerializeRemark);
264
265 StringRef Buf = BSStream.str();
266 LLVMRemarkParserRef Parser =
267 LLVMRemarkParserCreateBitstream(Buf: Buf.data(), Size: Buf.size());
268 LLVMRemarkEntryRef Remark = LLVMRemarkParserGetNext(Parser);
269 EXPECT_FALSE(Remark == nullptr);
270 EXPECT_EQ(LLVMRemarkEntryGetType(Remark), LLVMRemarkTypeMissed);
271 EXPECT_EQ(checkStr(LLVMRemarkEntryGetPassName(Remark), 6), "inline");
272 EXPECT_EQ(checkStr(LLVMRemarkEntryGetRemarkName(Remark), 12), "NoDefinition");
273 EXPECT_EQ(checkStr(LLVMRemarkEntryGetFunctionName(Remark), 3), "foo");
274 LLVMRemarkDebugLocRef DL = LLVMRemarkEntryGetDebugLoc(Remark);
275 EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");
276 EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 3U);
277 EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 12U);
278 EXPECT_EQ(LLVMRemarkEntryGetHotness(Remark), 0U);
279 EXPECT_EQ(LLVMRemarkEntryGetNumArgs(Remark), 4U);
280
281 unsigned ArgID = 0;
282 LLVMRemarkArgRef Arg = LLVMRemarkEntryGetFirstArg(Remark);
283 do {
284 switch (ArgID) {
285 case 0:
286 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Callee");
287 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "bar");
288 EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);
289 break;
290 case 1:
291 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");
292 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 26),
293 " will not be inlined into ");
294 EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);
295 break;
296 case 2: {
297 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Caller");
298 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "foo");
299 LLVMRemarkDebugLocRef DL = LLVMRemarkArgGetDebugLoc(Arg);
300 EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");
301 EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 2U);
302 EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 0U);
303 break;
304 }
305 case 3:
306 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");
307 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 38),
308 " because its definition is unavailable");
309 EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);
310 break;
311 default:
312 break;
313 }
314 ++ArgID;
315 } while ((Arg = LLVMRemarkEntryGetNextArg(It: Arg, Remark)));
316
317 LLVMRemarkEntryDispose(Remark);
318
319 EXPECT_EQ(LLVMRemarkParserGetNext(Parser), nullptr);
320
321 EXPECT_FALSE(LLVMRemarkParserHasError(Parser));
322 LLVMRemarkParserDispose(Parser);
323}
324
325static void parseBad(StringRef Input, const char *ErrorMsg) {
326 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeBSParser =
327 remarks::createRemarkParser(ParserFormat: remarks::Format::Bitstream, Buf: Input);
328 EXPECT_FALSE(errorToBool(MaybeBSParser.takeError()));
329 EXPECT_TRUE(*MaybeBSParser != nullptr);
330
331 remarks::RemarkParser &BSParser = **MaybeBSParser;
332 Expected<std::unique_ptr<remarks::Remark>> BSRemark = BSParser.next();
333 EXPECT_EQ(ErrorMsg, toString(BSRemark.takeError())); // Expect an error.
334}
335
336TEST(BitstreamRemarks, ParsingEmpty) {
337 parseBad(Input: StringRef(), ErrorMsg: "End of file reached.");
338}
339
340TEST(BitstreamRemarks, ParsingBadMagic) {
341 parseBad(Input: "KRMR", ErrorMsg: "Unknown magic number: expecting RMRK, got KRMR.");
342}
343
344// Testing malformed bitstream is not easy. We would need to replace bytes in
345// the stream to create malformed and unknown records and blocks. There is no
346// textual format for bitstream that can be decoded, modified and encoded
347// back.
348
349// FIXME: Add tests for the following error messages:
350// * Error while parsing META_BLOCK: malformed record entry
351// (RECORD_META_CONTAINER_INFO).
352// * Error while parsing META_BLOCK: malformed record entry
353// (RECORD_META_REMARK_VERSION).
354// * Error while parsing META_BLOCK: malformed record entry
355// (RECORD_META_STRTAB).
356// * Error while parsing META_BLOCK: malformed record entry
357// (RECORD_META_EXTERNAL_FILE).
358// * Error while parsing META_BLOCK: unknown record entry (NUM).
359// * Error while parsing REMARK_BLOCK: malformed record entry
360// (RECORD_REMARK_HEADER).
361// * Error while parsing REMARK_BLOCK: malformed record entry
362// (RECORD_REMARK_DEBUG_LOC).
363// * Error while parsing REMARK_BLOCK: malformed record entry
364// (RECORD_REMARK_HOTNESS).
365// * Error while parsing REMARK_BLOCK: malformed record entry
366// (RECORD_REMARK_ARG_WITH_DEBUGLOC).
367// * Error while parsing REMARK_BLOCK: malformed record entry
368// (RECORD_REMARK_ARG_WITHOUT_DEBUGLOC).
369// * Error while parsing REMARK_BLOCK: unknown record entry (NUM).
370// * Error while parsing META_BLOCK: expecting [ENTER_SUBBLOCO, META_BLOCK,
371// ...].
372// * Error while entering META_BLOCK.
373// * Error while parsing META_BLOCK: expecting records.
374// * Error while parsing META_BLOCK: unterminated block.
375// * Error while parsing REMARK_BLOCK: expecting [ENTER_SUBBLOCO, REMARK_BLOCK,
376// ...].
377// * Error while entering REMARK_BLOCK.
378// * Error while parsing REMARK_BLOCK: expecting records.
379// * Error while parsing REMARK_BLOCK: unterminated block.
380// * Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK,
381// BLOCKINFO_BLOCK, ...].
382// * Error while parsing BLOCKINFO_BLOCK.
383// * Unexpected error while parsing bitstream.
384// * Expecting META_BLOCK after the BLOCKINFO_BLOCK.
385// * Error while parsing BLOCK_META: missing container version.
386// * Error while parsing BLOCK_META: invalid container type.
387// * Error while parsing BLOCK_META: missing container type.
388// * Error while parsing BLOCK_META: missing string table.
389// * Error while parsing BLOCK_META: missing remark version.
390// * Error while parsing BLOCK_META: missing external file path.
391// * Error while parsing external file's BLOCK_META: wrong container type.
392// * Error while parsing external file's BLOCK_META: mismatching versions:
393// original meta: NUM, external file meta: NUM.
394// * Error while parsing BLOCK_REMARK: missing string table.
395// * Error while parsing BLOCK_REMARK: missing remark type.
396// * Error while parsing BLOCK_REMARK: unknown remark type.
397// * Error while parsing BLOCK_REMARK: missing remark name.
398// * Error while parsing BLOCK_REMARK: missing remark pass.
399// * Error while parsing BLOCK_REMARK: missing remark function name.
400// * Error while parsing BLOCK_REMARK: missing key in remark argument.
401// * Error while parsing BLOCK_REMARK: missing value in remark argument.
402

source code of llvm/unittests/Remarks/BitstreamRemarksParsingTest.cpp