//===- unittest/Support/RemarksLinkingTest.cpp - Linking 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/Bitcode/BitcodeAnalyzer.h" #include "llvm/Remarks/RemarkLinker.h" #include "llvm/Remarks/RemarkSerializer.h" #include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" #include using namespace llvm; static void serializeAndCheck(remarks::RemarkLinker &RL, remarks::Format OutputFormat, StringRef ExpectedOutput) { // 1. Create a serializer. // 2. Serialize all the remarks from the linker. // 3. Check that it matches the output. std::string Buf; raw_string_ostream OS(Buf); Error E = RL.serialize(OS, OutputFormat); EXPECT_FALSE(static_cast(E)); // For bitstream, run it through the analyzer. if (OutputFormat == remarks::Format::Bitstream) { std::string AnalyzeBuf; raw_string_ostream AnalyzeOS(AnalyzeBuf); BCDumpOptions O(AnalyzeOS); O.ShowBinaryBlobs = true; BitcodeAnalyzer BA(OS.str()); EXPECT_FALSE(BA.analyze(O)); // Expect no errors. EXPECT_EQ(AnalyzeOS.str(), ExpectedOutput); } else { EXPECT_EQ(OS.str(), ExpectedOutput); } } static void check(remarks::Format InputFormat, StringRef Input, remarks::Format OutputFormat, StringRef ExpectedOutput) { remarks::RemarkLinker RL; EXPECT_FALSE(RL.link(Input, InputFormat)); serializeAndCheck(RL, OutputFormat, ExpectedOutput); } static void check(remarks::Format InputFormat, StringRef Input, remarks::Format InputFormat2, StringRef Input2, remarks::Format OutputFormat, StringRef ExpectedOutput) { remarks::RemarkLinker RL; EXPECT_FALSE(RL.link(Input, InputFormat)); EXPECT_FALSE(RL.link(Input2, InputFormat2)); serializeAndCheck(RL, OutputFormat, ExpectedOutput); } TEST(Remarks, LinkingGoodYAML) { // One YAML remark. check(remarks::Format::YAML, "--- !Missed\n" "Pass: inline\n" "Name: NoDefinition\n" "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" "Function: foo\n" "...\n", remarks::Format::YAML, "--- !Missed\n" "Pass: inline\n" "Name: NoDefinition\n" "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" "Function: foo\n" "...\n"); // Check that we don't keep remarks without debug locations. check(remarks::Format::YAML, "--- !Missed\n" "Pass: inline\n" "Name: NoDefinition\n" "Function: foo\n" "...\n", remarks::Format::YAML, ""); // Check that we deduplicate remarks. check(remarks::Format::YAML, "--- !Missed\n" "Pass: inline\n" "Name: NoDefinition\n" "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" "Function: foo\n" "...\n" "--- !Missed\n" "Pass: inline\n" "Name: NoDefinition\n" "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" "Function: foo\n" "...\n", remarks::Format::YAML, "--- !Missed\n" "Pass: inline\n" "Name: NoDefinition\n" "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" "Function: foo\n" "...\n"); } TEST(Remarks, LinkingGoodBitstream) { // One YAML remark. check(remarks::Format::YAML, "--- !Missed\n" "Pass: inline\n" "Name: NoDefinition\n" "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" "Function: foo\n" "...\n", remarks::Format::Bitstream, "\n" "\n" " \n" " \n" " blob data = " "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n" "\n" "\n" " \n" " \n" "\n"); // Check that we deduplicate remarks. check(remarks::Format::YAML, "--- !Missed\n" "Pass: inline\n" "Name: NoDefinition\n" "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" "Function: foo\n" "...\n" "--- !Missed\n" "Pass: inline\n" "Name: NoDefinition\n" "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" "Function: foo\n" "...\n", remarks::Format::Bitstream, "\n" "\n" " \n" " \n" " blob data = " "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n" "\n" "\n" " \n" " \n" "\n"); } TEST(Remarks, LinkingGoodStrTab) { // Check that remarks from different entries use the same strtab. check(remarks::Format::YAML, "--- !Missed\n" "Pass: inline\n" "Name: NoDefinition\n" "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" "Function: foo\n" "...\n", remarks::Format::YAML, "--- !Passed\n" "Pass: inline\n" "Name: Ok\n" "DebugLoc: { File: file.c, Line: 3, Column: 12 }\n" "Function: foo\n" "...\n", remarks::Format::YAMLStrTab, StringRef("REMARKS\0\0\0\0\0\0\0\0\0\x22\0\0\0\0\0\0\0" "inline\0NoDefinition\0foo\0file.c\0Ok\0" "--- !Passed\n" "Pass: 0\n" "Name: 4\n" "DebugLoc: { File: 3, Line: 3, Column: 12 }\n" "Function: 2\n" "...\n" "--- !Missed\n" "Pass: 0\n" "Name: 1\n" "DebugLoc: { File: 3, Line: 3, Column: 12 }\n" "Function: 2\n" "...\n", 304)); } // Check that we propagate parsing errors. TEST(Remarks, LinkingError) { remarks::RemarkLinker RL; { Error E = RL.link("badyaml", remarks::Format::YAML); EXPECT_TRUE(static_cast(E)); EXPECT_EQ(toString(std::move(E)), "YAML:1:1: error: document root is not of mapping type.\n" "\n" "badyaml\n" "^~~~~~~\n" "\n"); } { // Check that the prepend path is propagated and fails with the full path. RL.setExternalFilePrependPath("/baddir/"); Error E = RL.link( StringRef("REMARKS\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0badfile.opt.yaml", 40), remarks::Format::YAMLStrTab); EXPECT_TRUE(static_cast(E)); std::string ErrorMessage = toString(std::move(E)); EXPECT_EQ(StringRef(ErrorMessage).lower(), StringRef("'/baddir/badfile.opt.yaml': No such file or directory") .lower()); } }