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