llvm-project/llvm/unittests/Remarks/RemarksLinkingTest.cpp
Florian Hahn e1596d7d9b
[Remarks] Retain all remarks by default, add option to drop without DL.
At the moment, dsymutil drops all remarks without debug location.

There are many cases where debug location may be missing for remarks,
mostly due LLVM not preserving debug locations. When using bitstream
remarks for statistical analysis, those missed remarks mean we get an
incomplete picture.

The patch flips the default to keeping all remarks and leaving it to
tools that display remarks to filter out remarks without debug locations
as needed.

The new --remarks-drop-without-debug flag can be used to drop remarks
without debug locations, i.e. restore the previous behavior.

Reviewed By: thegameg

Differential Revision: https://reviews.llvm.org/D151089
2023-05-26 11:01:20 +01:00

256 lines
9.2 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::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<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.
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<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());
}
}