1 //===- unittests/Support/BitstreamRemarksParsingTest.cpp - Parsing tests --===//
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
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"
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.
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.
46 remarks::StringTable BSStrTab
;
47 BSStrTab
.internalize(*FromYAMLRemark
);
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
);
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
) {
82 "Name: NoDefinition\n"
83 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
87 " - String: ' will not be inlined into '\n"
89 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
90 " - String: ' because its definition is unavailable'\n"
93 // No debug loc should also pass.
97 "Name: NoDefinition\n"
101 " - String: ' will not be inlined into '\n"
103 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
104 " - String: ' because its definition is unavailable'\n"
107 // No args is also ok.
111 "Name: NoDefinition\n"
112 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
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
) {
122 parseGood("--- !Passed" COMMON_REMARK
);
124 parseGood("--- !Missed" COMMON_REMARK
);
126 parseGood("--- !Analysis" COMMON_REMARK
);
127 // Type: AnalysisFPCommute
128 parseGood("--- !AnalysisFPCommute" COMMON_REMARK
);
129 // Type: AnalysisAliasing
130 parseGood("--- !AnalysisAliasing" COMMON_REMARK
);
132 parseGood("--- !Failure" 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
) {
147 "Name: NoDefinition\n"
148 "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
153 " - String: ' will not be inlined into '\n"
155 " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
156 " - String: ' because its definition is unavailable'\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();
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);
185 for (const remarks::Argument
&Arg
: Remark
.Args
) {
188 EXPECT_EQ(checkStr(Arg
.Key
, 6), "Callee");
189 EXPECT_EQ(checkStr(Arg
.Val
, 3), "bar");
190 EXPECT_FALSE(Arg
.Loc
);
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
);
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);
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
);
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
);
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);
282 LLVMRemarkArgRef Arg
= LLVMRemarkEntryGetFirstArg(Remark
);
286 EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg
), 6), "Callee");
287 EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg
), 3), "bar");
288 EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg
), nullptr);
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);
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);
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);
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
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,
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,
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.