xref: /llvm-project/llvm/unittests/Remarks/YAMLRemarksParsingTest.cpp (revision e800967eb1105177f561732814297bfecfaf63c2)
1 //===- unittest/Support/YAMLRemarksParsingTest.cpp - OptTable 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 "gtest/gtest.h"
13 #include <optional>
14 
15 using namespace llvm;
16 
parseGood(const char (& Buf)[N])17 template <size_t N> void parseGood(const char (&Buf)[N]) {
18   Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
19       remarks::createRemarkParser(remarks::Format::YAML, {Buf, N - 1});
20   EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
21   EXPECT_TRUE(*MaybeParser != nullptr);
22 
23   remarks::RemarkParser &Parser = **MaybeParser;
24   Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();
25   EXPECT_FALSE(errorToBool(Remark.takeError())); // Check for parsing errors.
26   EXPECT_TRUE(*Remark != nullptr);               // At least one remark.
27   Remark = Parser.next();
28   Error E = Remark.takeError();
29   EXPECT_TRUE(E.isA<remarks::EndOfFileError>());
30   EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.
31 }
32 
parseGoodMeta(StringRef Buf)33 void parseGoodMeta(StringRef Buf) {
34   Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
35       remarks::createRemarkParserFromMeta(remarks::Format::YAML, Buf);
36   EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
37   EXPECT_TRUE(*MaybeParser != nullptr);
38 
39   remarks::RemarkParser &Parser = **MaybeParser;
40   Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();
41   EXPECT_FALSE(errorToBool(Remark.takeError())); // Check for parsing errors.
42   EXPECT_TRUE(*Remark != nullptr);               // At least one remark.
43   Remark = Parser.next();
44   Error E = Remark.takeError();
45   EXPECT_TRUE(E.isA<remarks::EndOfFileError>());
46   EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.
47 }
48 
49 template <size_t N>
parseExpectError(const char (& Buf)[N],const char * Error)50 bool parseExpectError(const char (&Buf)[N], const char *Error) {
51   Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
52       remarks::createRemarkParser(remarks::Format::YAML, {Buf, N - 1});
53   EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
54   EXPECT_TRUE(*MaybeParser != nullptr);
55 
56   remarks::RemarkParser &Parser = **MaybeParser;
57   Expected<std::unique_ptr<remarks::Remark>> Remark = Parser.next();
58   EXPECT_FALSE(Remark); // Check for parsing errors.
59 
60   std::string ErrorStr;
61   raw_string_ostream Stream(ErrorStr);
62   handleAllErrors(Remark.takeError(),
63                   [&](const ErrorInfoBase &EIB) { EIB.log(Stream); });
64   return StringRef(Stream.str()).contains(Error);
65 }
66 
67 enum class CmpType {
68   Equal,
69   Contains
70 };
71 
parseExpectErrorMeta(StringRef Buf,const char * Error,CmpType Cmp,std::optional<StringRef> ExternalFilePrependPath=std::nullopt)72 void parseExpectErrorMeta(
73     StringRef Buf, const char *Error, CmpType Cmp,
74     std::optional<StringRef> ExternalFilePrependPath = std::nullopt) {
75   std::string ErrorStr;
76   raw_string_ostream Stream(ErrorStr);
77 
78   Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
79       remarks::createRemarkParserFromMeta(remarks::Format::YAML, Buf,
80                                           /*StrTab=*/std::nullopt,
81                                           std::move(ExternalFilePrependPath));
82   handleAllErrors(MaybeParser.takeError(),
83                   [&](const ErrorInfoBase &EIB) { EIB.log(Stream); });
84 
85   // Use a case insensitive comparision due to case differences in error strings
86   // for different OSs.
87   if (Cmp == CmpType::Equal) {
88     EXPECT_EQ(StringRef(Stream.str()).lower(), StringRef(Error).lower());
89   }
90 
91   if (Cmp == CmpType::Contains) {
92     EXPECT_TRUE(StringRef(Stream.str()).contains(StringRef(Error)));
93   }
94 }
95 
TEST(YAMLRemarks,ParsingEmpty)96 TEST(YAMLRemarks, ParsingEmpty) {
97   EXPECT_TRUE(parseExpectError("\n\n", "document root is not of mapping type."));
98 }
99 
TEST(YAMLRemarks,ParsingNotYAML)100 TEST(YAMLRemarks, ParsingNotYAML) {
101   EXPECT_TRUE(
102       parseExpectError("\x01\x02\x03\x04\x05\x06", "Got empty plain scalar"));
103 }
104 
TEST(YAMLRemarks,ParsingGood)105 TEST(YAMLRemarks, ParsingGood) {
106   parseGood("\n"
107             "--- !Missed\n"
108             "Pass: inline\n"
109             "Name: NoDefinition\n"
110             "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
111             "Function: foo\n"
112             "Args:\n"
113             "  - Callee: bar\n"
114             "  - String: ' will not be inlined into '\n"
115             "  - Caller: foo\n"
116             "    DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
117             "  - String: ' because its definition is unavailable'\n"
118             "");
119 
120   // No debug loc should also pass.
121   parseGood("\n"
122             "--- !Missed\n"
123             "Pass: inline\n"
124             "Name: NoDefinition\n"
125             "Function: foo\n"
126             "Args:\n"
127             "  - Callee: bar\n"
128             "  - String: ' will not be inlined into '\n"
129             "  - Caller: foo\n"
130             "    DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
131             "  - String: ' because its definition is unavailable'\n"
132             "");
133 
134   // No args is also ok.
135   parseGood("\n"
136             "--- !Missed\n"
137             "Pass: inline\n"
138             "Name: NoDefinition\n"
139             "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
140             "Function: foo\n"
141             "");
142 
143   // Different order.
144   parseGood("\n"
145             "--- !Missed\n"
146             "DebugLoc: { Line: 3, Column: 12, File: file.c }\n"
147             "Function: foo\n"
148             "Name: NoDefinition\n"
149             "Args:\n"
150             "  - Callee: bar\n"
151             "  - String: ' will not be inlined into '\n"
152             "  - Caller: foo\n"
153             "    DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
154             "  - String: ' because its definition is unavailable'\n"
155             "Pass: inline\n"
156             "");
157 
158   // Block Remark.
159   parseGood("\n"
160             "--- !Missed\n"
161             "Function: foo\n"
162             "Name: NoDefinition\n"
163             "Args:\n"
164             "  - String:           |\n      \n      \n      blocks\n"
165             "Pass: inline\n"
166             "");
167 }
168 
169 // Mandatory common part of a remark.
170 #define COMMON_REMARK "\nPass: inline\nName: NoDefinition\nFunction: foo\n\n"
171 // Test all the types.
TEST(YAMLRemarks,ParsingTypes)172 TEST(YAMLRemarks, ParsingTypes) {
173   // Type: Passed
174   parseGood("--- !Passed" COMMON_REMARK);
175   // Type: Missed
176   parseGood("--- !Missed" COMMON_REMARK);
177   // Type: Analysis
178   parseGood("--- !Analysis" COMMON_REMARK);
179   // Type: AnalysisFPCommute
180   parseGood("--- !AnalysisFPCommute" COMMON_REMARK);
181   // Type: AnalysisAliasing
182   parseGood("--- !AnalysisAliasing" COMMON_REMARK);
183   // Type: Failure
184   parseGood("--- !Failure" COMMON_REMARK);
185 }
186 #undef COMMON_REMARK
187 
TEST(YAMLRemarks,ParsingMissingFields)188 TEST(YAMLRemarks, ParsingMissingFields) {
189   // No type.
190   EXPECT_TRUE(parseExpectError("\n"
191                    "---\n"
192                    "Pass: inline\n"
193                    "Name: NoDefinition\n"
194                    "Function: foo\n"
195                    "",
196                    "expected a remark tag."));
197   // No pass.
198   EXPECT_TRUE(parseExpectError("\n"
199                    "--- !Missed\n"
200                    "Name: NoDefinition\n"
201                    "Function: foo\n"
202                    "",
203                    "Type, Pass, Name or Function missing."));
204   // No name.
205   EXPECT_TRUE(parseExpectError("\n"
206                    "--- !Missed\n"
207                    "Pass: inline\n"
208                    "Function: foo\n"
209                    "",
210                    "Type, Pass, Name or Function missing."));
211   // No function.
212   EXPECT_TRUE(parseExpectError("\n"
213                    "--- !Missed\n"
214                    "Pass: inline\n"
215                    "Name: NoDefinition\n"
216                    "",
217                    "Type, Pass, Name or Function missing."));
218   // Debug loc but no file.
219   EXPECT_TRUE(parseExpectError("\n"
220                    "--- !Missed\n"
221                    "Pass: inline\n"
222                    "Name: NoDefinition\n"
223                    "Function: foo\n"
224                    "DebugLoc: { Line: 3, Column: 12 }\n"
225                    "",
226                    "DebugLoc node incomplete."));
227   // Debug loc but no line.
228   EXPECT_TRUE(parseExpectError("\n"
229                    "--- !Missed\n"
230                    "Pass: inline\n"
231                    "Name: NoDefinition\n"
232                    "Function: foo\n"
233                    "DebugLoc: { File: file.c, Column: 12 }\n"
234                    "",
235                    "DebugLoc node incomplete."));
236   // Debug loc but no column.
237   EXPECT_TRUE(parseExpectError("\n"
238                    "--- !Missed\n"
239                    "Pass: inline\n"
240                    "Name: NoDefinition\n"
241                    "Function: foo\n"
242                    "DebugLoc: { File: file.c, Line: 3 }\n"
243                    "",
244                    "DebugLoc node incomplete."));
245 }
246 
TEST(YAMLRemarks,ParsingWrongTypes)247 TEST(YAMLRemarks, ParsingWrongTypes) {
248   // Wrong debug loc type.
249   EXPECT_TRUE(parseExpectError("\n"
250                    "--- !Missed\n"
251                    "Pass: inline\n"
252                    "Name: NoDefinition\n"
253                    "Function: foo\n"
254                    "DebugLoc: foo\n"
255                    "",
256                    "expected a value of mapping type."));
257   // Wrong line type.
258   EXPECT_TRUE(parseExpectError("\n"
259                    "--- !Missed\n"
260                    "Pass: inline\n"
261                    "Name: NoDefinition\n"
262                    "Function: foo\n"
263                    "DebugLoc: { File: file.c, Line: b, Column: 12 }\n"
264                    "",
265                    "expected a value of integer type."));
266   // Wrong column type.
267   EXPECT_TRUE(parseExpectError("\n"
268                    "--- !Missed\n"
269                    "Pass: inline\n"
270                    "Name: NoDefinition\n"
271                    "Function: foo\n"
272                    "DebugLoc: { File: file.c, Line: 3, Column: c }\n"
273                    "",
274                    "expected a value of integer type."));
275   // Wrong args type.
276   EXPECT_TRUE(parseExpectError("\n"
277                    "--- !Missed\n"
278                    "Pass: inline\n"
279                    "Name: NoDefinition\n"
280                    "Function: foo\n"
281                    "Args: foo\n"
282                    "",
283                    "wrong value type for key."));
284   // Wrong key type.
285   EXPECT_TRUE(parseExpectError("\n"
286                    "--- !Missed\n"
287                    "{ A: a }: inline\n"
288                    "Name: NoDefinition\n"
289                    "Function: foo\n"
290                    "",
291                    "key is not a string."));
292   // Debug loc with unknown entry.
293   EXPECT_TRUE(parseExpectError("\n"
294                    "--- !Missed\n"
295                    "Pass: inline\n"
296                    "Name: NoDefinition\n"
297                    "Function: foo\n"
298                    "DebugLoc: { File: file.c, Column: 12, Unknown: 12 }\n"
299                    "",
300                    "unknown entry in DebugLoc map."));
301   // Unknown entry.
302   EXPECT_TRUE(parseExpectError("\n"
303                    "--- !Missed\n"
304                    "Unknown: inline\n"
305                    "",
306                    "unknown key."));
307   // Not a scalar.
308   EXPECT_TRUE(parseExpectError("\n"
309                    "--- !Missed\n"
310                    "Pass: { File: a, Line: 1, Column: 2 }\n"
311                    "Name: NoDefinition\n"
312                    "Function: foo\n"
313                    "",
314                    "expected a value of scalar type."));
315   // Not a string file in debug loc.
316   EXPECT_TRUE(parseExpectError("\n"
317                    "--- !Missed\n"
318                    "Pass: inline\n"
319                    "Name: NoDefinition\n"
320                    "Function: foo\n"
321                    "DebugLoc: { File: { a: b }, Column: 12, Line: 12 }\n"
322                    "",
323                    "expected a value of scalar type."));
324   // Not a integer column in debug loc.
325   EXPECT_TRUE(parseExpectError("\n"
326                    "--- !Missed\n"
327                    "Pass: inline\n"
328                    "Name: NoDefinition\n"
329                    "Function: foo\n"
330                    "DebugLoc: { File: file.c, Column: { a: b }, Line: 12 }\n"
331                    "",
332                    "expected a value of scalar type."));
333   // Not a integer line in debug loc.
334   EXPECT_TRUE(parseExpectError("\n"
335                    "--- !Missed\n"
336                    "Pass: inline\n"
337                    "Name: NoDefinition\n"
338                    "Function: foo\n"
339                    "DebugLoc: { File: file.c, Column: 12, Line: { a: b } }\n"
340                    "",
341                    "expected a value of scalar type."));
342   // Not a mapping type value for args.
343   EXPECT_TRUE(parseExpectError("\n"
344                    "--- !Missed\n"
345                    "Pass: inline\n"
346                    "Name: NoDefinition\n"
347                    "Function: foo\n"
348                    "DebugLoc: { File: file.c, Column: 12, Line: { a: b } }\n"
349                    "",
350                    "expected a value of scalar type."));
351 }
352 
TEST(YAMLRemarks,ParsingWrongArgs)353 TEST(YAMLRemarks, ParsingWrongArgs) {
354   // Multiple debug locs per arg.
355   EXPECT_TRUE(parseExpectError("\n"
356                    "--- !Missed\n"
357                    "Pass: inline\n"
358                    "Name: NoDefinition\n"
359                    "Function: foo\n"
360                    "Args:\n"
361                    "  - Str: string\n"
362                    "    DebugLoc: { File: a, Line: 1, Column: 2 }\n"
363                    "    DebugLoc: { File: a, Line: 1, Column: 2 }\n"
364                    "",
365                    "only one DebugLoc entry is allowed per argument."));
366   // Multiple strings per arg.
367   EXPECT_TRUE(parseExpectError("\n"
368                    "--- !Missed\n"
369                    "Pass: inline\n"
370                    "Name: NoDefinition\n"
371                    "Function: foo\n"
372                    "Args:\n"
373                    "  - Str: string\n"
374                    "    Str2: string\n"
375                    "    DebugLoc: { File: a, Line: 1, Column: 2 }\n"
376                    "",
377                    "only one string entry is allowed per argument."));
378   // No arg value.
379   EXPECT_TRUE(parseExpectError("\n"
380                    "--- !Missed\n"
381                    "Pass: inline\n"
382                    "Name: NoDefinition\n"
383                    "Function: foo\n"
384                    "Args:\n"
385                    "  - DebugLoc: { File: a, Line: 1, Column: 2 }\n"
386                    "",
387                    "argument key is missing."));
388 }
389 
checkStr(StringRef Str,unsigned ExpectedLen)390 static inline StringRef checkStr(StringRef Str, unsigned ExpectedLen) {
391   const char *StrData = Str.data();
392   unsigned StrLen = Str.size();
393   EXPECT_EQ(StrLen, ExpectedLen);
394   return StringRef(StrData, StrLen);
395 }
396 
TEST(YAMLRemarks,Contents)397 TEST(YAMLRemarks, Contents) {
398   StringRef Buf = "\n"
399                   "--- !Missed\n"
400                   "Pass: inline\n"
401                   "Name: NoDefinition\n"
402                   "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
403                   "Function: foo\n"
404                   "Hotness: 4\n"
405                   "Args:\n"
406                   "  - Callee: bar\n"
407                   "  - String: ' will not be inlined into '\n"
408                   "  - Caller: foo\n"
409                   "    DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
410                   "  - String: ' because its definition is unavailable'\n"
411                   "\n";
412 
413   Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
414       remarks::createRemarkParser(remarks::Format::YAML, Buf);
415   EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
416   EXPECT_TRUE(*MaybeParser != nullptr);
417 
418   remarks::RemarkParser &Parser = **MaybeParser;
419   Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();
420   EXPECT_FALSE(
421       errorToBool(MaybeRemark.takeError())); // Check for parsing errors.
422   EXPECT_TRUE(*MaybeRemark != nullptr);      // At least one remark.
423 
424   const remarks::Remark &Remark = **MaybeRemark;
425   EXPECT_EQ(Remark.RemarkType, remarks::Type::Missed);
426   EXPECT_EQ(checkStr(Remark.PassName, 6), "inline");
427   EXPECT_EQ(checkStr(Remark.RemarkName, 12), "NoDefinition");
428   EXPECT_EQ(checkStr(Remark.FunctionName, 3), "foo");
429   EXPECT_TRUE(Remark.Loc);
430   const remarks::RemarkLocation &RL = *Remark.Loc;
431   EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
432   EXPECT_EQ(RL.SourceLine, 3U);
433   EXPECT_EQ(RL.SourceColumn, 12U);
434   EXPECT_TRUE(Remark.Hotness);
435   EXPECT_EQ(*Remark.Hotness, 4U);
436   EXPECT_EQ(Remark.Args.size(), 4U);
437 
438   unsigned ArgID = 0;
439   for (const remarks::Argument &Arg : Remark.Args) {
440     switch (ArgID) {
441     case 0:
442       EXPECT_EQ(checkStr(Arg.Key, 6), "Callee");
443       EXPECT_EQ(checkStr(Arg.Val, 3), "bar");
444       EXPECT_FALSE(Arg.Loc);
445       break;
446     case 1:
447       EXPECT_EQ(checkStr(Arg.Key, 6), "String");
448       EXPECT_EQ(checkStr(Arg.Val, 26), " will not be inlined into ");
449       EXPECT_FALSE(Arg.Loc);
450       break;
451     case 2: {
452       EXPECT_EQ(checkStr(Arg.Key, 6), "Caller");
453       EXPECT_EQ(checkStr(Arg.Val, 3), "foo");
454       EXPECT_TRUE(Arg.Loc);
455       const remarks::RemarkLocation &RL = *Arg.Loc;
456       EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
457       EXPECT_EQ(RL.SourceLine, 2U);
458       EXPECT_EQ(RL.SourceColumn, 0U);
459       break;
460     }
461     case 3:
462       EXPECT_EQ(checkStr(Arg.Key, 6), "String");
463       EXPECT_EQ(checkStr(Arg.Val, 38),
464                 " because its definition is unavailable");
465       EXPECT_FALSE(Arg.Loc);
466       break;
467     default:
468       break;
469     }
470     ++ArgID;
471   }
472 
473   MaybeRemark = Parser.next();
474   Error E = MaybeRemark.takeError();
475   EXPECT_TRUE(E.isA<remarks::EndOfFileError>());
476   EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.
477 }
478 
checkStr(LLVMRemarkStringRef Str,unsigned ExpectedLen)479 static inline StringRef checkStr(LLVMRemarkStringRef Str,
480                                  unsigned ExpectedLen) {
481   const char *StrData = LLVMRemarkStringGetData(Str);
482   unsigned StrLen = LLVMRemarkStringGetLen(Str);
483   EXPECT_EQ(StrLen, ExpectedLen);
484   return StringRef(StrData, StrLen);
485 }
486 
TEST(YAMLRemarks,ContentsCAPI)487 TEST(YAMLRemarks, ContentsCAPI) {
488   StringRef Buf = "\n"
489                   "--- !Missed\n"
490                   "Pass: inline\n"
491                   "Name: NoDefinition\n"
492                   "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n"
493                   "Function: foo\n"
494                   "Args:\n"
495                   "  - Callee: bar\n"
496                   "  - String: ' will not be inlined into '\n"
497                   "  - Caller: foo\n"
498                   "    DebugLoc: { File: file.c, Line: 2, Column: 0 }\n"
499                   "  - String: ' because its definition is unavailable'\n"
500                   "\n";
501 
502   LLVMRemarkParserRef Parser =
503       LLVMRemarkParserCreateYAML(Buf.data(), Buf.size());
504   LLVMRemarkEntryRef Remark = LLVMRemarkParserGetNext(Parser);
505   EXPECT_FALSE(Remark == nullptr);
506   EXPECT_EQ(LLVMRemarkEntryGetType(Remark), LLVMRemarkTypeMissed);
507   EXPECT_EQ(checkStr(LLVMRemarkEntryGetPassName(Remark), 6), "inline");
508   EXPECT_EQ(checkStr(LLVMRemarkEntryGetRemarkName(Remark), 12), "NoDefinition");
509   EXPECT_EQ(checkStr(LLVMRemarkEntryGetFunctionName(Remark), 3), "foo");
510   LLVMRemarkDebugLocRef DL = LLVMRemarkEntryGetDebugLoc(Remark);
511   EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");
512   EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 3U);
513   EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 12U);
514   EXPECT_EQ(LLVMRemarkEntryGetHotness(Remark), 0U);
515   EXPECT_EQ(LLVMRemarkEntryGetNumArgs(Remark), 4U);
516 
517   unsigned ArgID = 0;
518   LLVMRemarkArgRef Arg = LLVMRemarkEntryGetFirstArg(Remark);
519   do {
520     switch (ArgID) {
521     case 0:
522       EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Callee");
523       EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "bar");
524       EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);
525       break;
526     case 1:
527       EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");
528       EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 26),
529                 " will not be inlined into ");
530       EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);
531       break;
532     case 2: {
533       EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "Caller");
534       EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 3), "foo");
535       LLVMRemarkDebugLocRef DL = LLVMRemarkArgGetDebugLoc(Arg);
536       EXPECT_EQ(checkStr(LLVMRemarkDebugLocGetSourceFilePath(DL), 6), "file.c");
537       EXPECT_EQ(LLVMRemarkDebugLocGetSourceLine(DL), 2U);
538       EXPECT_EQ(LLVMRemarkDebugLocGetSourceColumn(DL), 0U);
539       break;
540     }
541     case 3:
542       EXPECT_EQ(checkStr(LLVMRemarkArgGetKey(Arg), 6), "String");
543       EXPECT_EQ(checkStr(LLVMRemarkArgGetValue(Arg), 38),
544                 " because its definition is unavailable");
545       EXPECT_EQ(LLVMRemarkArgGetDebugLoc(Arg), nullptr);
546       break;
547     default:
548       break;
549     }
550     ++ArgID;
551   } while ((Arg = LLVMRemarkEntryGetNextArg(Arg, Remark)));
552 
553   LLVMRemarkEntryDispose(Remark);
554 
555   EXPECT_EQ(LLVMRemarkParserGetNext(Parser), nullptr);
556 
557   EXPECT_FALSE(LLVMRemarkParserHasError(Parser));
558   LLVMRemarkParserDispose(Parser);
559 }
560 
TEST(YAMLRemarks,ContentsStrTab)561 TEST(YAMLRemarks, ContentsStrTab) {
562   StringRef Buf = "\n"
563                   "--- !Missed\n"
564                   "Pass: 0\n"
565                   "Name: 1\n"
566                   "DebugLoc: { File: 2, Line: 3, Column: 12 }\n"
567                   "Function: 3\n"
568                   "Hotness: 4\n"
569                   "Args:\n"
570                   "  - Callee: 5\n"
571                   "  - String: 7\n"
572                   "  - Caller: 3\n"
573                   "    DebugLoc: { File: 2, Line: 2, Column: 0 }\n"
574                   "  - String: 8\n"
575                   "\n";
576 
577   StringRef StrTabBuf =
578       StringRef("inline\0NoDefinition\0file.c\0foo\0Callee\0bar\0String\0 "
579                 "will not be inlined into \0 because its definition is "
580                 "unavailable",
581                 115);
582 
583   remarks::ParsedStringTable StrTab(StrTabBuf);
584   Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
585       remarks::createRemarkParser(remarks::Format::YAMLStrTab, Buf,
586                                   std::move(StrTab));
587   EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
588   EXPECT_TRUE(*MaybeParser != nullptr);
589 
590   remarks::RemarkParser &Parser = **MaybeParser;
591   Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();
592   EXPECT_FALSE(
593       errorToBool(MaybeRemark.takeError())); // Check for parsing errors.
594   EXPECT_TRUE(*MaybeRemark != nullptr);      // At least one remark.
595 
596   const remarks::Remark &Remark = **MaybeRemark;
597   EXPECT_EQ(Remark.RemarkType, remarks::Type::Missed);
598   EXPECT_EQ(checkStr(Remark.PassName, 6), "inline");
599   EXPECT_EQ(checkStr(Remark.RemarkName, 12), "NoDefinition");
600   EXPECT_EQ(checkStr(Remark.FunctionName, 3), "foo");
601   EXPECT_TRUE(Remark.Loc);
602   const remarks::RemarkLocation &RL = *Remark.Loc;
603   EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
604   EXPECT_EQ(RL.SourceLine, 3U);
605   EXPECT_EQ(RL.SourceColumn, 12U);
606   EXPECT_TRUE(Remark.Hotness);
607   EXPECT_EQ(*Remark.Hotness, 4U);
608   EXPECT_EQ(Remark.Args.size(), 4U);
609 
610   unsigned ArgID = 0;
611   for (const remarks::Argument &Arg : Remark.Args) {
612     switch (ArgID) {
613     case 0:
614       EXPECT_EQ(checkStr(Arg.Key, 6), "Callee");
615       EXPECT_EQ(checkStr(Arg.Val, 3), "bar");
616       EXPECT_FALSE(Arg.Loc);
617       break;
618     case 1:
619       EXPECT_EQ(checkStr(Arg.Key, 6), "String");
620       EXPECT_EQ(checkStr(Arg.Val, 26), " will not be inlined into ");
621       EXPECT_FALSE(Arg.Loc);
622       break;
623     case 2: {
624       EXPECT_EQ(checkStr(Arg.Key, 6), "Caller");
625       EXPECT_EQ(checkStr(Arg.Val, 3), "foo");
626       EXPECT_TRUE(Arg.Loc);
627       const remarks::RemarkLocation &RL = *Arg.Loc;
628       EXPECT_EQ(checkStr(RL.SourceFilePath, 6), "file.c");
629       EXPECT_EQ(RL.SourceLine, 2U);
630       EXPECT_EQ(RL.SourceColumn, 0U);
631       break;
632     }
633     case 3:
634       EXPECT_EQ(checkStr(Arg.Key, 6), "String");
635       EXPECT_EQ(checkStr(Arg.Val, 38),
636                 " because its definition is unavailable");
637       EXPECT_FALSE(Arg.Loc);
638       break;
639     default:
640       break;
641     }
642     ++ArgID;
643   }
644 
645   MaybeRemark = Parser.next();
646   Error E = MaybeRemark.takeError();
647   EXPECT_TRUE(E.isA<remarks::EndOfFileError>());
648   EXPECT_TRUE(errorToBool(std::move(E))); // Check for parsing errors.
649 }
650 
TEST(YAMLRemarks,ParsingBadStringTableIndex)651 TEST(YAMLRemarks, ParsingBadStringTableIndex) {
652   StringRef Buf = "\n"
653                   "--- !Missed\n"
654                   "Pass: 50\n"
655                   "\n";
656 
657   StringRef StrTabBuf = StringRef("inline");
658 
659   remarks::ParsedStringTable StrTab(StrTabBuf);
660   Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser =
661       remarks::createRemarkParser(remarks::Format::YAMLStrTab, Buf,
662                                   std::move(StrTab));
663   EXPECT_FALSE(errorToBool(MaybeParser.takeError()));
664   EXPECT_TRUE(*MaybeParser != nullptr);
665 
666   remarks::RemarkParser &Parser = **MaybeParser;
667   Expected<std::unique_ptr<remarks::Remark>> MaybeRemark = Parser.next();
668   EXPECT_FALSE(MaybeRemark); // Expect an error here.
669 
670   std::string ErrorStr;
671   raw_string_ostream Stream(ErrorStr);
672   handleAllErrors(MaybeRemark.takeError(),
673                   [&](const ErrorInfoBase &EIB) { EIB.log(Stream); });
674   EXPECT_TRUE(
675       StringRef(Stream.str())
676           .contains("String with index 50 is out of bounds (size = 1)."));
677 }
678 
TEST(YAMLRemarks,ParsingGoodMeta)679 TEST(YAMLRemarks, ParsingGoodMeta) {
680   // No metadata should also work.
681   parseGoodMeta("--- !Missed\n"
682                 "Pass: inline\n"
683                 "Name: NoDefinition\n"
684                 "Function: foo\n");
685 
686   // No string table.
687   parseGoodMeta(StringRef("REMARKS\0"
688                           "\0\0\0\0\0\0\0\0"
689                           "\0\0\0\0\0\0\0\0"
690                           "--- !Missed\n"
691                           "Pass: inline\n"
692                           "Name: NoDefinition\n"
693                           "Function: foo\n",
694                           82));
695 
696   // Use the string table from the metadata.
697   parseGoodMeta(StringRef("REMARKS\0"
698                           "\0\0\0\0\0\0\0\0"
699                           "\x02\0\0\0\0\0\0\0"
700                           "a\0"
701                           "--- !Missed\n"
702                           "Pass: 0\n"
703                           "Name: 0\n"
704                           "Function: 0\n",
705                           66));
706 }
707 
TEST(YAMLRemarks,ParsingBadMeta)708 TEST(YAMLRemarks, ParsingBadMeta) {
709   parseExpectErrorMeta(StringRef("REMARKSS", 9),
710                        "Expecting \\0 after magic number.", CmpType::Equal);
711 
712   parseExpectErrorMeta(StringRef("REMARKS\0", 8), "Expecting version number.",
713                        CmpType::Equal);
714 
715   parseExpectErrorMeta(StringRef("REMARKS\0"
716                                  "\x09\0\0\0\0\0\0\0",
717                                  16),
718                        "Mismatching remark version. Got 9, expected 0.",
719                        CmpType::Equal);
720 
721   parseExpectErrorMeta(StringRef("REMARKS\0"
722                                  "\0\0\0\0\0\0\0\0",
723                                  16),
724                        "Expecting string table size.", CmpType::Equal);
725 
726   parseExpectErrorMeta(StringRef("REMARKS\0"
727                                  "\0\0\0\0\0\0\0\0"
728                                  "\x01\0\0\0\0\0\0\0",
729                                  24),
730                        "Expecting string table.", CmpType::Equal);
731 
732   parseExpectErrorMeta(StringRef("REMARKS\0"
733                                  "\0\0\0\0\0\0\0\0"
734                                  "\0\0\0\0\0\0\0\0"
735                                  "/path/",
736                                  30),
737                        "'/path/'", CmpType::Contains);
738 
739   parseExpectErrorMeta(StringRef("REMARKS\0"
740                                  "\0\0\0\0\0\0\0\0"
741                                  "\0\0\0\0\0\0\0\0"
742                                  "/path/",
743                                  30),
744                        "'/baddir/path/'", CmpType::Contains,
745                        StringRef("/baddir/"));
746 }
747