[docs] Fix build-docs.sh
[llvm-project.git] / llvm / unittests / Remarks / BitstreamRemarksParsingTest.cpp
blob6234931b3bece626145553f8827bb3dbe5b3ee35
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 //===----------------------------------------------------------------------===//
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"
15 using namespace llvm;
17 template <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
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.
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);
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.
45 // 2.
46 remarks::StringTable BSStrTab;
47 BSStrTab.internalize(*FromYAMLRemark);
48 std::string BSBuf;
49 raw_string_ostream BSStream(BSBuf);
50 Expected<std::unique_ptr<remarks::RemarkSerializer>> BSSerializer =
51 remarks::createRemarkSerializer(remarks::Format::Bitstream,
52 remarks::SerializerMode::Standalone,
53 BSStream, std::move(BSStrTab));
54 EXPECT_FALSE(errorToBool(BSSerializer.takeError()));
55 (*BSSerializer)->emit(*FromYAMLRemark);
57 // 3.
58 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeBSParser =
59 remarks::createRemarkParser(remarks::Format::Bitstream, BSStream.str());
60 EXPECT_FALSE(errorToBool(MaybeBSParser.takeError()));
61 EXPECT_TRUE(*MaybeBSParser != nullptr);
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.
75 EXPECT_EQ(*FromYAMLRemark, *FromBSRemark);
78 TEST(BitstreamRemarks, ParsingGood) {
79 parseGood("\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 "");
93 // No debug loc should also pass.
94 parseGood("\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 "");
107 // No args is also ok.
108 parseGood("\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 "");
117 // Mandatory common part of a remark.
118 #define COMMON_REMARK "\nPass: inline\nName: NoDefinition\nFunction: foo\n\n"
119 // Test all the types.
120 TEST(BitstreamRemarks, ParsingTypes) {
121 // Type: Passed
122 parseGood("--- !Passed" COMMON_REMARK);
123 // Type: Missed
124 parseGood("--- !Missed" COMMON_REMARK);
125 // Type: Analysis
126 parseGood("--- !Analysis" COMMON_REMARK);
127 // Type: AnalysisFPCommute
128 parseGood("--- !AnalysisFPCommute" COMMON_REMARK);
129 // Type: AnalysisAliasing
130 parseGood("--- !AnalysisAliasing" COMMON_REMARK);
131 // Type: Failure
132 parseGood("--- !Failure" COMMON_REMARK);
134 #undef COMMON_REMARK
136 static 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);
143 TEST(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";
159 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
160 remarks::createRemarkParser(remarks::Format::YAML, Buf);
161 EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
162 EXPECT_TRUE(*MaybeParser != nullptr);
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.
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);
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;
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;
216 ++ArgID;
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.
225 static inline StringRef checkStr(LLVMRemarkStringRef Str,
226 unsigned ExpectedLen) {
227 const char *StrData = LLVMRemarkStringGetData(Str);
228 unsigned StrLen = LLVMRemarkStringGetLen(Str);
229 EXPECT_EQ(StrLen, ExpectedLen);
230 return StringRef(StrData, StrLen);
233 TEST(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{"file.c", 3, 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{"file.c", 2, 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(ToSerializeRemark);
256 std::string BSBuf;
257 raw_string_ostream BSStream(BSBuf);
258 Expected<std::unique_ptr<remarks::RemarkSerializer>> BSSerializer =
259 remarks::createRemarkSerializer(remarks::Format::Bitstream,
260 remarks::SerializerMode::Standalone,
261 BSStream, std::move(BSStrTab));
262 EXPECT_FALSE(errorToBool(BSSerializer.takeError()));
263 (*BSSerializer)->emit(ToSerializeRemark);
265 StringRef Buf = BSStream.str();
266 LLVMRemarkParserRef Parser =
267 LLVMRemarkParserCreateBitstream(Buf.data(), 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);
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;
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;
314 ++ArgID;
315 } while ((Arg = LLVMRemarkEntryGetNextArg(Arg, Remark)));
317 LLVMRemarkEntryDispose(Remark);
319 EXPECT_EQ(LLVMRemarkParserGetNext(Parser), nullptr);
321 EXPECT_FALSE(LLVMRemarkParserHasError(Parser));
322 LLVMRemarkParserDispose(Parser);
325 static void parseBad(StringRef Input, const char *ErrorMsg) {
326 Expected<std::unique_ptr<remarks::RemarkParser>> MaybeBSParser =
327 remarks::createRemarkParser(remarks::Format::Bitstream, Input);
328 EXPECT_FALSE(errorToBool(MaybeBSParser.takeError()));
329 EXPECT_TRUE(*MaybeBSParser != nullptr);
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.
336 TEST(BitstreamRemarks, ParsingEmpty) {
337 parseBad(StringRef(), "End of file reached.");
340 TEST(BitstreamRemarks, ParsingBadMagic) {
341 parseBad("KRMR", "Unknown magic number: expecting RMRK, got KRMR.");
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.
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.