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 | |
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>> = 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 | |
33 | void parseGoodMeta(StringRef Buf) { |
34 | Expected<std::unique_ptr<remarks::RemarkParser>> MaybeParser = |
35 | remarks::createRemarkParserFromMeta(ParserFormat: 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>> = 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> |
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>> = 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(OS&: Stream); }); |
64 | return StringRef(Stream.str()).contains(Other: Error); |
65 | } |
66 | |
67 | enum class CmpType { |
68 | Equal, |
69 | Contains |
70 | }; |
71 | |
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(ParserFormat: remarks::Format::YAML, Buf, |
80 | /*StrTab=*/std::nullopt, |
81 | ExternalFilePrependPath: std::move(ExternalFilePrependPath)); |
82 | handleAllErrors(E: MaybeParser.takeError(), |
83 | Handlers: [&](const ErrorInfoBase &EIB) { EIB.log(OS&: 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 | |
96 | TEST(YAMLRemarks, ParsingEmpty) { |
97 | EXPECT_TRUE(parseExpectError("\n\n" , "document root is not of mapping type." )); |
98 | } |
99 | |
100 | TEST(YAMLRemarks, ParsingNotYAML) { |
101 | EXPECT_TRUE( |
102 | parseExpectError("\x01\x02\x03\x04\x05\x06" , "Got empty plain scalar" )); |
103 | } |
104 | |
105 | TEST(YAMLRemarks, ParsingGood) { |
106 | parseGood(Buf: "\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(Buf: "\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(Buf: "\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(Buf: "\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(Buf: "\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 "\nPass: inline\nName: NoDefinition\nFunction: foo\n\n" |
171 | // Test all the types. |
172 | TEST(YAMLRemarks, ParsingTypes) { |
173 | // Type: Passed |
174 | parseGood(Buf: "--- !Passed" COMMON_REMARK); |
175 | // Type: Missed |
176 | parseGood(Buf: "--- !Missed" COMMON_REMARK); |
177 | // Type: Analysis |
178 | parseGood(Buf: "--- !Analysis" COMMON_REMARK); |
179 | // Type: AnalysisFPCommute |
180 | parseGood(Buf: "--- !AnalysisFPCommute" COMMON_REMARK); |
181 | // Type: AnalysisAliasing |
182 | parseGood(Buf: "--- !AnalysisAliasing" COMMON_REMARK); |
183 | // Type: Failure |
184 | parseGood(Buf: "--- !Failure" COMMON_REMARK); |
185 | } |
186 | #undef COMMON_REMARK |
187 | |
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 | |
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 | |
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 | |
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 | |
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(ParserFormat: 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>> = 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 & = **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 | |
479 | static inline StringRef (LLVMRemarkStringRef Str, |
480 | unsigned ExpectedLen) { |
481 | const char *StrData = LLVMRemarkStringGetData(String: Str); |
482 | unsigned StrLen = LLVMRemarkStringGetLen(String: Str); |
483 | EXPECT_EQ(StrLen, ExpectedLen); |
484 | return StringRef(StrData, StrLen); |
485 | } |
486 | |
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: Buf.data(), Size: Buf.size()); |
504 | LLVMRemarkEntryRef = 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(It: Arg, Remark))); |
552 | |
553 | LLVMRemarkEntryDispose(Remark); |
554 | |
555 | EXPECT_EQ(LLVMRemarkParserGetNext(Parser), nullptr); |
556 | |
557 | EXPECT_FALSE(LLVMRemarkParserHasError(Parser)); |
558 | LLVMRemarkParserDispose(Parser); |
559 | } |
560 | |
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(ParserFormat: remarks::Format::YAMLStrTab, Buf, |
586 | StrTab: 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>> = 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 & = **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 | |
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(ParserFormat: remarks::Format::YAMLStrTab, Buf, |
662 | StrTab: 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>> = Parser.next(); |
668 | EXPECT_FALSE(MaybeRemark); // Expect an error here. |
669 | |
670 | std::string ErrorStr; |
671 | raw_string_ostream Stream(ErrorStr); |
672 | handleAllErrors(E: MaybeRemark.takeError(), |
673 | Handlers: [&](const ErrorInfoBase &EIB) { EIB.log(OS&: Stream); }); |
674 | EXPECT_TRUE( |
675 | StringRef(Stream.str()) |
676 | .contains("String with index 50 is out of bounds (size = 1)." )); |
677 | } |
678 | |
679 | TEST(YAMLRemarks, ParsingGoodMeta) { |
680 | // No metadata should also work. |
681 | parseGoodMeta(Buf: "--- !Missed\n" |
682 | "Pass: inline\n" |
683 | "Name: NoDefinition\n" |
684 | "Function: foo\n" ); |
685 | |
686 | // No string table. |
687 | parseGoodMeta(Buf: 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(Buf: 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 | |
708 | TEST(YAMLRemarks, ParsingBadMeta) { |
709 | parseExpectErrorMeta(Buf: StringRef("REMARKSS" , 9), |
710 | Error: "Expecting \\0 after magic number." , Cmp: CmpType::Equal); |
711 | |
712 | parseExpectErrorMeta(Buf: StringRef("REMARKS\0" , 8), Error: "Expecting version number." , |
713 | Cmp: CmpType::Equal); |
714 | |
715 | parseExpectErrorMeta(Buf: StringRef("REMARKS\0" |
716 | "\x09\0\0\0\0\0\0\0" , |
717 | 16), |
718 | Error: "Mismatching remark version. Got 9, expected 0." , |
719 | Cmp: CmpType::Equal); |
720 | |
721 | parseExpectErrorMeta(Buf: StringRef("REMARKS\0" |
722 | "\0\0\0\0\0\0\0\0" , |
723 | 16), |
724 | Error: "Expecting string table size." , Cmp: CmpType::Equal); |
725 | |
726 | parseExpectErrorMeta(Buf: StringRef("REMARKS\0" |
727 | "\0\0\0\0\0\0\0\0" |
728 | "\x01\0\0\0\0\0\0\0" , |
729 | 24), |
730 | Error: "Expecting string table." , Cmp: CmpType::Equal); |
731 | |
732 | parseExpectErrorMeta(Buf: 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 | Error: "'/path/'" , Cmp: CmpType::Contains); |
738 | |
739 | parseExpectErrorMeta(Buf: 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 | Error: "'/baddir/path/'" , Cmp: CmpType::Contains, |
745 | ExternalFilePrependPath: StringRef("/baddir/" )); |
746 | } |
747 | |