
Add remark format 'Auto', which performs automatic detection of the remark format using the magic numbers at the beginning of the remarks files. The RemarkLinker already did something similar, so we streamlined this and exposed this to llvm-remarkutil.
255 lines
9.3 KiB
C++
255 lines
9.3 KiB
C++
//===- 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 <string>
|
|
|
|
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<bool>(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,
|
|
std::optional<bool> KeepAllRemarks = {}) {
|
|
remarks::RemarkLinker RL;
|
|
if (KeepAllRemarks)
|
|
RL.setKeepAllRemarks(*KeepAllRemarks);
|
|
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, unless
|
|
// KeepAllRemarks is set.
|
|
check(remarks::Format::YAML,
|
|
"--- !Missed\n"
|
|
"Pass: inline\n"
|
|
"Name: NoDefinition\n"
|
|
"Function: foo\n"
|
|
"...\n",
|
|
remarks::Format::YAML, "",
|
|
/*KeepAllRemarks=*/false);
|
|
check(remarks::Format::YAML,
|
|
"--- !Missed\n"
|
|
"Pass: inline\n"
|
|
"Name: NoDefinition\n"
|
|
"Function: foo\n"
|
|
"...\n",
|
|
remarks::Format::YAML,
|
|
"--- !Missed\n"
|
|
"Pass: inline\n"
|
|
"Name: NoDefinition\n"
|
|
"Function: foo\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::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,
|
|
"<BLOCKINFO_BLOCK/>\n"
|
|
"<Meta BlockID=8 NumWords=12 BlockCodeSize=3>\n"
|
|
" <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
|
|
" <Remark version codeid=2 abbrevid=5 op0=0/>\n"
|
|
" <String table codeid=3 abbrevid=6/> blob data = "
|
|
"'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n"
|
|
"</Meta>\n"
|
|
"<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
|
|
" <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
|
|
" <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n"
|
|
"</Remark>\n");
|
|
|
|
// Check that we keep remarks without debug info.
|
|
check(remarks::Format::YAML,
|
|
"--- !Missed\n"
|
|
"Pass: inline\n"
|
|
"Name: NoDefinition\n"
|
|
"Function: foo\n"
|
|
"...\n",
|
|
remarks::Format::Bitstream,
|
|
"<BLOCKINFO_BLOCK/>\n"
|
|
"<Meta BlockID=8 NumWords=10 BlockCodeSize=3>\n"
|
|
" <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
|
|
" <Remark version codeid=2 abbrevid=5 op0=0/>\n"
|
|
" <String table codeid=3 abbrevid=6/> blob data = "
|
|
"'inline\\x00NoDefinition\\x00foo\\x00'\n"
|
|
"</Meta>\n"
|
|
"<Remark BlockID=9 NumWords=1 BlockCodeSize=4>\n"
|
|
" <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
|
|
"</Remark>\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,
|
|
"<BLOCKINFO_BLOCK/>\n"
|
|
"<Meta BlockID=8 NumWords=12 BlockCodeSize=3>\n"
|
|
" <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
|
|
" <Remark version codeid=2 abbrevid=5 op0=0/>\n"
|
|
" <String table codeid=3 abbrevid=6/> blob data = "
|
|
"'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n"
|
|
"</Meta>\n"
|
|
"<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
|
|
" <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
|
|
" <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n"
|
|
"</Remark>\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::Bitstream,
|
|
"<BLOCKINFO_BLOCK/>\n"
|
|
"<Meta BlockID=8 NumWords=13 BlockCodeSize=3>\n"
|
|
" <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
|
|
" <Remark version codeid=2 abbrevid=5 op0=0/>\n"
|
|
" <String table codeid=3 abbrevid=6/> blob data = "
|
|
"'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00Ok\\x00'\n"
|
|
"</Meta>\n"
|
|
"<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
|
|
" <Remark header codeid=5 abbrevid=4 op0=1 op1=4 op2=0 op3=2/>\n"
|
|
" <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n"
|
|
"</Remark>\n"
|
|
"<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
|
|
" <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
|
|
" <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n"
|
|
"</Remark>\n");
|
|
}
|
|
|
|
// Check that we propagate parsing errors.
|
|
TEST(Remarks, LinkingError) {
|
|
remarks::RemarkLinker RL;
|
|
{
|
|
Error E = RL.link("badyaml", remarks::Format::YAML);
|
|
EXPECT_TRUE(static_cast<bool>(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.
|
|
// Also ensures that the remark format is correctly auto-detected.
|
|
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));
|
|
EXPECT_TRUE(static_cast<bool>(E));
|
|
std::string ErrorMessage = toString(std::move(E));
|
|
EXPECT_EQ(StringRef(ErrorMessage).lower(),
|
|
StringRef("'/baddir/badfile.opt.yaml': No such file or directory")
|
|
.lower());
|
|
}
|
|
}
|