| //===- unittest/Support/OptRemarksParsingTest.cpp - OptTable tests --------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm-c/OptRemarks.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| |
| template <size_t N> bool tryParse(const char (&Buf)[N]) { |
| LLVMOptRemarkParserRef Parser = LLVMOptRemarkParserCreate(Buf, N - 1); |
| LLVMOptRemarkEntry *Remark = nullptr; |
| while (LLVMOptRemarkEntry *NewRemark = LLVMOptRemarkParserGetNext(Parser)) { |
| EXPECT_TRUE(Remark == nullptr); // Only one remark per test. |
| Remark = NewRemark; |
| } |
| EXPECT_TRUE(Remark != nullptr); // We need *exactly* one remark per test. |
| bool HasError = LLVMOptRemarkParserHasError(Parser); |
| LLVMOptRemarkParserDispose(Parser); |
| return !HasError; |
| } |
| |
| template <size_t N> |
| bool parseExpectError(const char (&Buf)[N], const char *Error) { |
| LLVMOptRemarkParserRef Parser = LLVMOptRemarkParserCreate(Buf, N - 1); |
| LLVMOptRemarkEntry *Remark = nullptr; |
| while (LLVMOptRemarkEntry *NewRemark = LLVMOptRemarkParserGetNext(Parser)) { |
| EXPECT_FALSE(NewRemark); |
| } |
| EXPECT_TRUE(Remark == nullptr); // We are parsing only one malformed remark. |
| EXPECT_TRUE(LLVMOptRemarkParserHasError(Parser)); |
| bool MatchesError = |
| StringRef(LLVMOptRemarkParserGetErrorMessage(Parser)).contains(Error); |
| LLVMOptRemarkParserDispose(Parser); |
| |
| return MatchesError; |
| } |
| |
| TEST(OptRemarks, OptRemarksParsingEmpty) { |
| StringRef Buf = "\n" |
| "\n"; |
| LLVMOptRemarkParserRef Parser = |
| LLVMOptRemarkParserCreate(Buf.data(), Buf.size()); |
| LLVMOptRemarkEntry *NewRemark = LLVMOptRemarkParserGetNext(Parser); |
| EXPECT_TRUE(NewRemark == nullptr); // No remark expected. |
| EXPECT_TRUE(LLVMOptRemarkParserHasError(Parser)); |
| EXPECT_TRUE(StringRef(LLVMOptRemarkParserGetErrorMessage(Parser)) |
| .contains("document root is not of mapping type.")); |
| LLVMOptRemarkParserDispose(Parser); |
| } |
| |
| TEST(OptRemarks, OptRemarksParsingGood) { |
| EXPECT_TRUE(tryParse("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" |
| "Function: foo\n" |
| "Args:\n" |
| " - Callee: bar\n" |
| " - String: ' will not be inlined into '\n" |
| " - Caller: foo\n" |
| " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n" |
| " - String: ' because its definition is unavailable'\n" |
| "")); |
| |
| // No debug loc should also pass. |
| EXPECT_TRUE(tryParse("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "Args:\n" |
| " - Callee: bar\n" |
| " - String: ' will not be inlined into '\n" |
| " - Caller: foo\n" |
| " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n" |
| " - String: ' because its definition is unavailable'\n" |
| "")); |
| |
| // No args is also ok. |
| EXPECT_TRUE(tryParse("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" |
| "Function: foo\n" |
| "")); |
| |
| // Different order. |
| EXPECT_TRUE(tryParse("\n" |
| "--- !Missed\n" |
| "DebugLoc: { Line: 3, Column: 12, File: file.c }\n" |
| "Function: foo\n" |
| "Name: NoDefinition\n" |
| "Args:\n" |
| " - Callee: bar\n" |
| " - String: ' will not be inlined into '\n" |
| " - Caller: foo\n" |
| " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n" |
| " - String: ' because its definition is unavailable'\n" |
| "Pass: inline\n" |
| "")); |
| } |
| |
| // Mandatory common part of a remark. |
| #define COMMON_REMARK "\nPass: inline\nName: NoDefinition\nFunction: foo\n\n" |
| // Test all the types. |
| TEST(OptRemarks, OptRemarksParsingTypes) { |
| // Type: Passed |
| EXPECT_TRUE(tryParse("--- !Passed" COMMON_REMARK)); |
| // Type: Missed |
| EXPECT_TRUE(tryParse("--- !Missed" COMMON_REMARK)); |
| // Type: Analysis |
| EXPECT_TRUE(tryParse("--- !Analysis" COMMON_REMARK)); |
| // Type: AnalysisFPCompute |
| EXPECT_TRUE(tryParse("--- !AnalysisFPCompute" COMMON_REMARK)); |
| // Type: AnalysisAliasing |
| EXPECT_TRUE(tryParse("--- !AnalysisAliasing" COMMON_REMARK)); |
| // Type: Failure |
| EXPECT_TRUE(tryParse("--- !Failure" COMMON_REMARK)); |
| } |
| #undef COMMON_REMARK |
| |
| TEST(OptRemarks, OptRemarksParsingMissingFields) { |
| // No type. |
| EXPECT_TRUE(parseExpectError("\n" |
| "---\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "", |
| "error: Type, Pass, Name or Function missing.")); |
| // No pass. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "", |
| "error: Type, Pass, Name or Function missing.")); |
| // No name. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Function: foo\n" |
| "", |
| "error: Type, Pass, Name or Function missing.")); |
| // No function. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "", |
| "error: Type, Pass, Name or Function missing.")); |
| // Debug loc but no file. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "DebugLoc: { Line: 3, Column: 12 }\n" |
| "", |
| "DebugLoc node incomplete.")); |
| // Debug loc but no line. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "DebugLoc: { File: file.c, Column: 12 }\n" |
| "", |
| "DebugLoc node incomplete.")); |
| // Debug loc but no column. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "DebugLoc: { File: file.c, Line: 3 }\n" |
| "", |
| "DebugLoc node incomplete.")); |
| } |
| |
| TEST(OptRemarks, OptRemarksParsingWrongTypes) { |
| // Wrong debug loc type. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "DebugLoc: foo\n" |
| "", |
| "expected a value of mapping type.")); |
| // Wrong line type. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "DebugLoc: { File: file.c, Line: b, Column: 12 }\n" |
| "", |
| "expected a value of integer type.")); |
| // Wrong column type. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "DebugLoc: { File: file.c, Line: 3, Column: c }\n" |
| "", |
| "expected a value of integer type.")); |
| // Wrong args type. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "Args: foo\n" |
| "", |
| "wrong value type for key.")); |
| // Wrong key type. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "{ A: a }: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "", |
| "key is not a string.")); |
| // Debug loc with unknown entry. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "DebugLoc: { File: file.c, Column: 12, Unknown: 12 }\n" |
| "", |
| "unknown entry in DebugLoc map.")); |
| // Unknown entry. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Unknown: inline\n" |
| "", |
| "unknown key.")); |
| // Not a scalar. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: { File: a, Line: 1, Column: 2 }\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "", |
| "expected a value of scalar type.")); |
| // Not a string file in debug loc. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "DebugLoc: { File: { a: b }, Column: 12, Line: 12 }\n" |
| "", |
| "expected a value of scalar type.")); |
| // Not a integer column in debug loc. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "DebugLoc: { File: file.c, Column: { a: b }, Line: 12 }\n" |
| "", |
| "expected a value of scalar type.")); |
| // Not a integer line in debug loc. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "DebugLoc: { File: file.c, Column: 12, Line: { a: b } }\n" |
| "", |
| "expected a value of scalar type.")); |
| // Not a mapping type value for args. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "DebugLoc: { File: file.c, Column: 12, Line: { a: b } }\n" |
| "", |
| "expected a value of scalar type.")); |
| } |
| |
| TEST(OptRemarks, OptRemarksParsingWrongArgs) { |
| // Multiple debug locs per arg. |
| EXPECT_TRUE( |
| parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "Args:\n" |
| " - Str: string\n" |
| " DebugLoc: { File: a, Line: 1, Column: 2 }\n" |
| " DebugLoc: { File: a, Line: 1, Column: 2 }\n" |
| "", |
| "only one DebugLoc entry is allowed per argument.")); |
| // Multiple strings per arg. |
| EXPECT_TRUE( |
| parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "Args:\n" |
| " - Str: string\n" |
| " Str2: string\n" |
| " DebugLoc: { File: a, Line: 1, Column: 2 }\n" |
| "", |
| "only one string entry is allowed per argument.")); |
| // No arg value. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "Args:\n" |
| " - Callee: ''\n" |
| " - DebugLoc: { File: a, Line: 1, Column: 2 }\n" |
| "", |
| "argument value is missing.")); |
| // No arg value. |
| EXPECT_TRUE(parseExpectError("\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "Function: foo\n" |
| "Args:\n" |
| " - DebugLoc: { File: a, Line: 1, Column: 2 }\n" |
| "", |
| "argument key is missing.")); |
| |
| } |
| |
| TEST(OptRemarks, OptRemarksGoodStruct) { |
| StringRef Buf = "\n" |
| "--- !Missed\n" |
| "Pass: inline\n" |
| "Name: NoDefinition\n" |
| "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" |
| "Function: foo\n" |
| "Args:\n" |
| " - Callee: bar\n" |
| " - String: ' will not be inlined into '\n" |
| " - Caller: foo\n" |
| " DebugLoc: { File: file.c, Line: 2, Column: 0 }\n" |
| " - String: ' because its definition is unavailable'\n" |
| "\n"; |
| |
| LLVMOptRemarkParserRef Parser = |
| LLVMOptRemarkParserCreate(Buf.data(), Buf.size()); |
| LLVMOptRemarkEntry *Remark = LLVMOptRemarkParserGetNext(Parser); |
| EXPECT_FALSE(Remark == nullptr); |
| EXPECT_EQ(StringRef(Remark->RemarkType.Str, 7), "!Missed"); |
| EXPECT_EQ(Remark->RemarkType.Len, 7U); |
| EXPECT_EQ(StringRef(Remark->PassName.Str, 6), "inline"); |
| EXPECT_EQ(Remark->PassName.Len, 6U); |
| EXPECT_EQ(StringRef(Remark->RemarkName.Str, 12), "NoDefinition"); |
| EXPECT_EQ(Remark->RemarkName.Len, 12U); |
| EXPECT_EQ(StringRef(Remark->FunctionName.Str, 3), "foo"); |
| EXPECT_EQ(Remark->FunctionName.Len, 3U); |
| EXPECT_EQ(StringRef(Remark->DebugLoc.SourceFile.Str, 6), "file.c"); |
| EXPECT_EQ(Remark->DebugLoc.SourceFile.Len, 6U); |
| EXPECT_EQ(Remark->DebugLoc.SourceLineNumber, 3U); |
| EXPECT_EQ(Remark->DebugLoc.SourceColumnNumber, 12U); |
| EXPECT_EQ(Remark->Hotness, 0U); |
| EXPECT_EQ(Remark->NumArgs, 4U); |
| // Arg 0 |
| { |
| LLVMOptRemarkArg &Arg = Remark->Args[0]; |
| EXPECT_EQ(StringRef(Arg.Key.Str, 6), "Callee"); |
| EXPECT_EQ(Arg.Key.Len, 6U); |
| EXPECT_EQ(StringRef(Arg.Value.Str, 3), "bar"); |
| EXPECT_EQ(Arg.Value.Len, 3U); |
| EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 0), ""); |
| EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 0U); |
| EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 0U); |
| EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U); |
| } |
| // Arg 1 |
| { |
| LLVMOptRemarkArg &Arg = Remark->Args[1]; |
| EXPECT_EQ(StringRef(Arg.Key.Str, 6), "String"); |
| EXPECT_EQ(Arg.Key.Len, 6U); |
| EXPECT_EQ(StringRef(Arg.Value.Str, 26), " will not be inlined into "); |
| EXPECT_EQ(Arg.Value.Len, 26U); |
| EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 0), ""); |
| EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 0U); |
| EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 0U); |
| EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U); |
| } |
| // Arg 2 |
| { |
| LLVMOptRemarkArg &Arg = Remark->Args[2]; |
| EXPECT_EQ(StringRef(Arg.Key.Str, 6), "Caller"); |
| EXPECT_EQ(Arg.Key.Len, 6U); |
| EXPECT_EQ(StringRef(Arg.Value.Str, 3), "foo"); |
| EXPECT_EQ(Arg.Value.Len, 3U); |
| EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 6), "file.c"); |
| EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 6U); |
| EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 2U); |
| EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U); |
| } |
| // Arg 3 |
| { |
| LLVMOptRemarkArg &Arg = Remark->Args[3]; |
| EXPECT_EQ(StringRef(Arg.Key.Str, 6), "String"); |
| EXPECT_EQ(Arg.Key.Len, 6U); |
| EXPECT_EQ(StringRef(Arg.Value.Str, 38), |
| " because its definition is unavailable"); |
| EXPECT_EQ(Arg.Value.Len, 38U); |
| EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 0), ""); |
| EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 0U); |
| EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 0U); |
| EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U); |
| } |
| |
| EXPECT_EQ(LLVMOptRemarkParserGetNext(Parser), nullptr); |
| |
| EXPECT_FALSE(LLVMOptRemarkParserHasError(Parser)); |
| LLVMOptRemarkParserDispose(Parser); |
| } |