Files
clang-p2996/llvm/unittests/DebugInfo/DWARF/DWARFDebugLineTest.cpp
Hyoun Kyu Cho 6c12ae8163 Exposes interface to free up caching data structure in DWARFDebugLine and DWARFUnit for memory management
This is minimum changes extracted from https://reviews.llvm.org/D78950. The old patch tried to add LRU eviction of caching data structure. Due to multiple layers of interfaces that users could be using, it was not clear where to put the functionality. While we work out on where to put that functionality, it'll be great to add this minimum interface change so that the user could implement their own memory management. More specifically:

* Add a clearLineTable method for DWARFDebugLine which erases the given offset from the LineTableMap.
* DWARFDebugContext adds the clearLineTableForUnit method that leverages clearLineTable to remove the object corresponding to a given compile unit, for memory management purposes. When it is referred to again, the line table object will be repopulated.

Reviewed By: dblaikie

Differential Revision: https://reviews.llvm.org/D90006
2022-05-24 03:23:24 +00:00

1998 lines
77 KiB
C++

//===- DWARFDebugLineTest.cpp ---------------------------------------------===//
//
// 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 "DwarfGenerator.h"
#include "DwarfUtils.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace dwarf;
using namespace dwarfgen;
using namespace object;
using namespace utils;
using namespace testing;
namespace {
struct CommonFixture {
CommonFixture()
: LineData("", true, 0), Recoverable(Error::success()),
RecordRecoverable(std::bind(&CommonFixture::recordRecoverable, this,
std::placeholders::_1)),
Unrecoverable(Error::success()),
RecordUnrecoverable(std::bind(&CommonFixture::recordUnrecoverable, this,
std::placeholders::_1)){};
~CommonFixture() {
EXPECT_FALSE(Recoverable);
EXPECT_FALSE(Unrecoverable);
}
// Note: ASSERT_THAT_EXPECTED cannot be used in a non-void function, so
// setupGenerator() is split into two.
void setupGeneratorImpl(uint16_t Version, uint8_t AddrSize) {
AddressSize = AddrSize;
Triple T = getDefaultTargetTripleForAddrSize(AddressSize ? AddressSize : 8);
if (!isConfigurationSupported(T))
return;
auto ExpectedGenerator = Generator::create(T, Version);
ASSERT_THAT_EXPECTED(ExpectedGenerator, Succeeded());
Gen = std::move(*ExpectedGenerator);
}
bool setupGenerator(uint16_t Version = 4, uint8_t AddrSize = 8) {
setupGeneratorImpl(Version, AddrSize);
return Gen != nullptr;
}
void generate() {
Context = createContext();
assert(Context != nullptr && "test state is not valid");
const DWARFObject &Obj = Context->getDWARFObj();
uint8_t TargetAddrSize = AddressSize == 0 ? 8 : AddressSize;
LineData = DWARFDataExtractor(
Obj, Obj.getLineSection(),
getDefaultTargetTripleForAddrSize(TargetAddrSize).isLittleEndian(),
AddressSize);
}
std::unique_ptr<DWARFContext> createContext() {
assert(Gen != nullptr && "Generator is not set up");
StringRef FileBytes = Gen->generate();
MemoryBufferRef FileBuffer(FileBytes, "dwarf");
auto Obj = object::ObjectFile::createObjectFile(FileBuffer);
if (Obj)
return DWARFContext::create(**Obj);
return nullptr;
}
DWARFDebugLine::SectionParser setupParser() {
LineTable &LT = Gen->addLineTable(DWARF32);
LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addByte(0xaa);
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
LineTable &LT2 = Gen->addLineTable(DWARF64);
LT2.addExtendedOpcode(9, DW_LNE_set_address,
{{0x11223344, LineTable::Quad}});
LT2.addStandardOpcode(DW_LNS_copy, {});
LT2.addByte(0xbb);
LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
return DWARFDebugLine::SectionParser(LineData, *Context, Units);
}
void recordRecoverable(Error Err) {
Recoverable = joinErrors(std::move(Recoverable), std::move(Err));
}
void recordUnrecoverable(Error Err) {
Unrecoverable = joinErrors(std::move(Unrecoverable), std::move(Err));
}
Expected<const DWARFDebugLine::LineTable *>
getOrParseLineTableFatalErrors(uint64_t Offset = 0) {
auto ExpectedLineTable = Line.getOrParseLineTable(
LineData, Offset, *Context, nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());
return ExpectedLineTable;
}
uint8_t AddressSize;
std::unique_ptr<Generator> Gen;
std::unique_ptr<DWARFContext> Context;
DWARFDataExtractor LineData;
DWARFDebugLine Line;
Error Recoverable;
std::function<void(Error)> RecordRecoverable;
Error Unrecoverable;
std::function<void(Error)> RecordUnrecoverable;
SmallVector<std::unique_ptr<DWARFUnit>, 2> Units;
};
// Fixtures must derive from "Test", but parameterised fixtures from
// "TestWithParam". It does not seem possible to inherit from both, so we share
// the common state in a separate class, inherited by the two fixture classes.
struct DebugLineBasicFixture : public Test, public CommonFixture {};
struct DebugLineParameterisedFixture
: public TestWithParam<std::pair<uint16_t, DwarfFormat>>,
public CommonFixture {
void SetUp() override { std::tie(Version, Format) = GetParam(); }
uint16_t Version;
DwarfFormat Format;
};
void checkDefaultPrologue(uint16_t Version, DwarfFormat Format,
DWARFDebugLine::Prologue Prologue,
uint64_t BodyLength) {
// Check version specific fields and values.
uint64_t UnitLength;
uint64_t PrologueLength;
switch (Version) {
case 4:
PrologueLength = 36;
UnitLength = PrologueLength + 2;
EXPECT_EQ(Prologue.MaxOpsPerInst, 1u);
break;
case 2:
case 3:
PrologueLength = 35;
UnitLength = PrologueLength + 2;
break;
case 5:
PrologueLength = 42;
UnitLength = PrologueLength + 4;
EXPECT_EQ(Prologue.getAddressSize(), 8u);
EXPECT_EQ(Prologue.SegSelectorSize, 0u);
break;
default:
llvm_unreachable("unsupported DWARF version");
}
UnitLength += BodyLength + (Format == DWARF32 ? 4 : 8);
EXPECT_EQ(Prologue.TotalLength, UnitLength);
EXPECT_EQ(Prologue.PrologueLength, PrologueLength);
EXPECT_EQ(Prologue.MinInstLength, 1u);
EXPECT_EQ(Prologue.DefaultIsStmt, 1u);
EXPECT_EQ(Prologue.LineBase, -5);
EXPECT_EQ(Prologue.LineRange, 14u);
EXPECT_EQ(Prologue.OpcodeBase, 13u);
std::vector<uint8_t> ExpectedLengths = {0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1};
EXPECT_EQ(Prologue.StandardOpcodeLengths, ExpectedLengths);
ASSERT_EQ(Prologue.IncludeDirectories.size(), 1u);
ASSERT_EQ(Prologue.IncludeDirectories[0].getForm(), DW_FORM_string);
EXPECT_STREQ(*toString(Prologue.IncludeDirectories[0]), "a dir");
ASSERT_EQ(Prologue.FileNames.size(), 1u);
ASSERT_EQ(Prologue.FileNames[0].Name.getForm(), DW_FORM_string);
ASSERT_EQ(Prologue.FileNames[0].DirIdx, 0u);
EXPECT_STREQ(*toString(Prologue.FileNames[0].Name), "a file");
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_GetOrParseLineTableAtInvalidOffset) {
#else
TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) {
#endif
if (!setupGenerator())
GTEST_SKIP();
generate();
EXPECT_THAT_EXPECTED(
getOrParseLineTableFatalErrors(0),
FailedWithMessage(
"offset 0x00000000 is not a valid debug line section offset"));
// Repeat to show that an error is reported each time.
EXPECT_THAT_EXPECTED(
getOrParseLineTableFatalErrors(0),
FailedWithMessage(
"offset 0x00000000 is not a valid debug line section offset"));
// Show that an error is reported for later offsets too.
EXPECT_THAT_EXPECTED(
getOrParseLineTableFatalErrors(1),
FailedWithMessage(
"offset 0x00000001 is not a valid debug line section offset"));
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_GetOrParseLineTableAtInvalidOffsetAfterData) {
#else
TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue({{0, LineTable::Byte}});
generate();
EXPECT_THAT_EXPECTED(
getOrParseLineTableFatalErrors(0),
FailedWithMessage(
"parsing line table prologue at offset 0x00000000: "
"unexpected end of data at offset 0x1 while reading [0x0, 0x4)"));
EXPECT_THAT_EXPECTED(
getOrParseLineTableFatalErrors(1),
FailedWithMessage(
"offset 0x00000001 is not a valid debug line section offset"));
}
#ifdef _AIX
TEST_P(DebugLineParameterisedFixture, DISABLED_PrologueGetLength) {
#else
TEST_P(DebugLineParameterisedFixture, PrologueGetLength) {
#endif
if (!setupGenerator(Version))
GTEST_SKIP();
LineTable &LT = Gen->addLineTable(Format);
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
LT.setPrologue(Prologue);
generate();
// + 10 for sizes of DWARF-32 unit length, version, prologue length.
uint64_t ExpectedLength = Prologue.PrologueLength + 10;
if (Version == 5)
// Add address and segment selector size fields.
ExpectedLength += 2;
if (Format == DWARF64)
// Unit length grows by 8, prologue length by 4.
ExpectedLength += 12;
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
EXPECT_EQ((*ExpectedLineTable)->Prologue.getLength(), ExpectedLength);
}
#ifdef _AIX
TEST_P(DebugLineParameterisedFixture, DISABLED_GetOrParseLineTableValidTable) {
#else
TEST_P(DebugLineParameterisedFixture, GetOrParseLineTableValidTable) {
#endif
if (!setupGenerator(Version))
GTEST_SKIP();
SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
(Format == DWARF64 ? "DWARF64" : "DWARF32"));
LineTable &LT = Gen->addLineTable(Format);
LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addByte(0xaa);
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
LineTable &LT2 = Gen->addLineTable(Format);
LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x11223344, LineTable::Quad}});
LT2.addStandardOpcode(DW_LNS_copy, {});
LT2.addByte(0xbb);
LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x55667788, LineTable::Quad}});
LT2.addStandardOpcode(DW_LNS_copy, {});
LT2.addByte(0xcc);
LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
ASSERT_TRUE(ExpectedLineTable.operator bool());
EXPECT_FALSE(Recoverable);
const DWARFDebugLine::LineTable *Expected = *ExpectedLineTable;
checkDefaultPrologue(Version, Format, Expected->Prologue, 16);
EXPECT_EQ(Expected->Sequences.size(), 1u);
uint64_t SecondOffset =
Expected->Prologue.sizeofTotalLength() + Expected->Prologue.TotalLength;
Recoverable = Error::success();
auto ExpectedLineTable2 = Line.getOrParseLineTable(
LineData, SecondOffset, *Context, nullptr, RecordRecoverable);
ASSERT_TRUE(ExpectedLineTable2.operator bool());
EXPECT_FALSE(Recoverable);
const DWARFDebugLine::LineTable *Expected2 = *ExpectedLineTable2;
checkDefaultPrologue(Version, Format, Expected2->Prologue, 32);
EXPECT_EQ(Expected2->Sequences.size(), 2u);
EXPECT_NE(Expected, Expected2);
// Check that if the same offset is requested, the exact same pointer is
// returned.
Recoverable = Error::success();
auto ExpectedLineTable3 = Line.getOrParseLineTable(
LineData, 0, *Context, nullptr, RecordRecoverable);
ASSERT_TRUE(ExpectedLineTable3.operator bool());
EXPECT_FALSE(Recoverable);
EXPECT_EQ(Expected, *ExpectedLineTable3);
Recoverable = Error::success();
auto ExpectedLineTable4 = Line.getOrParseLineTable(
LineData, SecondOffset, *Context, nullptr, RecordRecoverable);
ASSERT_TRUE(ExpectedLineTable4.operator bool());
EXPECT_FALSE(Recoverable);
EXPECT_EQ(Expected2, *ExpectedLineTable4);
// TODO: Add tests that show that the body of the programs have been read
// correctly.
}
#ifdef _AIX
TEST_P(DebugLineParameterisedFixture, DISABLED_ClearLineValidTable) {
#else
TEST_P(DebugLineParameterisedFixture, ClearLineValidTable) {
#endif
if (!setupGenerator(Version))
GTEST_SKIP();
SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
(Format == DWARF64 ? "DWARF64" : "DWARF32"));
LineTable &LT = Gen->addLineTable(Format);
LT.addExtendedOpcode(9, DW_LNE_set_address, {{0xadd4e55, LineTable::Quad}});
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addByte(0xaa);
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
LineTable &LT2 = Gen->addLineTable(Format);
LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x11223344, LineTable::Quad}});
LT2.addStandardOpcode(DW_LNS_copy, {});
LT2.addByte(0xbb);
LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
LT2.addExtendedOpcode(9, DW_LNE_set_address, {{0x55667788, LineTable::Quad}});
LT2.addStandardOpcode(DW_LNS_copy, {});
LT2.addByte(0xcc);
LT2.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
// Check that we have what we expect before calling clearLineTable().
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
ASSERT_TRUE((bool)ExpectedLineTable);
EXPECT_FALSE(Recoverable);
const DWARFDebugLine::LineTable *Expected = *ExpectedLineTable;
checkDefaultPrologue(Version, Format, Expected->Prologue, 16);
EXPECT_EQ(Expected->Sequences.size(), 1u);
uint64_t SecondOffset =
Expected->Prologue.sizeofTotalLength() + Expected->Prologue.TotalLength;
Recoverable = Error::success();
auto ExpectedLineTable2 = Line.getOrParseLineTable(
LineData, SecondOffset, *Context, nullptr, RecordRecoverable);
ASSERT_TRUE((bool)ExpectedLineTable2);
EXPECT_FALSE(Recoverable);
const DWARFDebugLine::LineTable *Expected2 = *ExpectedLineTable2;
checkDefaultPrologue(Version, Format, Expected2->Prologue, 32);
EXPECT_EQ(Expected2->Sequences.size(), 2u);
// Check that we no longer get the line tables after clearLineTable().
Line.clearLineTable(0);
Line.clearLineTable(SecondOffset);
EXPECT_EQ(Line.getLineTable(0), nullptr);
EXPECT_EQ(Line.getLineTable(SecondOffset), nullptr);
// Check that if the same offset is requested, the contents match what we
// had before.
Recoverable = Error::success();
auto ExpectedLineTable3 = Line.getOrParseLineTable(
LineData, 0, *Context, nullptr, RecordRecoverable);
ASSERT_TRUE((bool)ExpectedLineTable3);
EXPECT_FALSE(Recoverable);
const DWARFDebugLine::LineTable *Expected3 = *ExpectedLineTable3;
checkDefaultPrologue(Version, Format, Expected3->Prologue, 16);
EXPECT_EQ(Expected3->Sequences.size(), 1u);
Recoverable = Error::success();
auto ExpectedLineTable4 = Line.getOrParseLineTable(
LineData, SecondOffset, *Context, nullptr, RecordRecoverable);
ASSERT_TRUE((bool)ExpectedLineTable4);
EXPECT_FALSE(Recoverable);
const DWARFDebugLine::LineTable *Expected4 = *ExpectedLineTable4;
checkDefaultPrologue(Version, Format, Expected4->Prologue, 32);
EXPECT_EQ(Expected4->Sequences.size(), 2u);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_ErrorForReservedLength) {
#else
TEST_F(DebugLineBasicFixture, ErrorForReservedLength) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue({{0xfffffff0, LineTable::Long}});
generate();
EXPECT_THAT_EXPECTED(
getOrParseLineTableFatalErrors(),
FailedWithMessage(
"parsing line table prologue at offset 0x00000000: unsupported "
"reserved unit length of value 0xfffffff0"));
}
struct DebugLineUnsupportedVersionFixture : public TestWithParam<uint16_t>,
public CommonFixture {
void SetUp() override { Version = GetParam(); }
uint16_t Version;
};
#ifdef _AIX
TEST_P(DebugLineUnsupportedVersionFixture,
DISABLED_ErrorForUnsupportedVersion) {
#else
TEST_P(DebugLineUnsupportedVersionFixture, ErrorForUnsupportedVersion) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue(
{{LineTable::Half, LineTable::Long}, {Version, LineTable::Half}});
generate();
EXPECT_THAT_EXPECTED(
getOrParseLineTableFatalErrors(),
FailedWithMessage("parsing line table prologue at offset 0x00000000: "
"unsupported version " +
std::to_string(Version)));
}
INSTANTIATE_TEST_SUITE_P(UnsupportedVersionTestParams,
DebugLineUnsupportedVersionFixture,
Values(/*1 below min */ 1, /* 1 above max */ 6,
/* Maximum possible */ 0xffff));
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_ErrorForInvalidV5IncludeDirTable) {
#else
TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) {
#endif
if (!setupGenerator(5))
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue({
{19, LineTable::Long}, // unit length
{5, LineTable::Half}, // version
{8, LineTable::Byte}, // addr size
{0, LineTable::Byte}, // segment selector size
{11, LineTable::Long}, // prologue length
{1, LineTable::Byte}, // min instruction length
{1, LineTable::Byte}, // max ops per instruction
{1, LineTable::Byte}, // default is_stmt
{0, LineTable::Byte}, // line base
{14, LineTable::Byte}, // line range
{2, LineTable::Byte}, // opcode base (small to reduce the amount of
// setup required).
{0, LineTable::Byte}, // standard opcode lengths
{0, LineTable::Byte}, // directory entry format count (should not be
// zero).
{0, LineTable::ULEB}, // directories count
{0, LineTable::Byte}, // file name entry format count
{0, LineTable::ULEB} // file name entry count
});
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
EXPECT_THAT_ERROR(
std::move(Recoverable),
FailedWithMessage(
"parsing line table prologue at 0x00000000 found an invalid "
"directory or file table description at 0x00000014",
"failed to parse entry content descriptions because no path was "
"found"));
}
#ifdef _AIX
TEST_P(DebugLineParameterisedFixture, DISABLED_ErrorForTooLargePrologueLength) {
#else
TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) {
#endif
if (!setupGenerator(Version))
GTEST_SKIP();
SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
(Format == DWARF64 ? "DWARF64" : "DWARF32"));
LineTable &LT = Gen->addLineTable(Format);
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
++Prologue.PrologueLength;
LT.setPrologue(Prologue);
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
DWARFDebugLine::LineTable Result(**ExpectedLineTable);
// Undo the earlier modification so that it can be compared against a
// "default" prologue.
--Result.Prologue.PrologueLength;
checkDefaultPrologue(Version, Format, Result.Prologue, 0);
uint64_t ExpectedEnd =
Prologue.TotalLength + 1 + Prologue.sizeofTotalLength();
EXPECT_THAT_ERROR(
std::move(Recoverable),
FailedWithMessage(
("unknown data in line table prologue at offset 0x00000000: "
"parsing ended (at offset 0x000000" +
Twine::utohexstr(ExpectedEnd - 1) +
") before reaching the prologue end at offset 0x000000" +
Twine::utohexstr(ExpectedEnd))
.str()));
}
#ifdef _AIX
TEST_P(DebugLineParameterisedFixture, DISABLED_ErrorForTooShortPrologueLength) {
#else
TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) {
#endif
if (!setupGenerator(Version))
GTEST_SKIP();
SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " +
(Format == DWARF64 ? "DWARF64" : "DWARF32"));
LineTable &LT = Gen->addLineTable(Format);
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
Prologue.PrologueLength -= 2;
LT.setPrologue(Prologue);
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
DWARFDebugLine::LineTable Result(**ExpectedLineTable);
// Parsing will stop before reading a complete file entry.
ASSERT_EQ(Result.Prologue.IncludeDirectories.size(), 1u);
EXPECT_EQ(toStringRef(Result.Prologue.IncludeDirectories[0]), "a dir");
EXPECT_EQ(Result.Prologue.FileNames.size(), 0u);
// The exact place where the parsing will stop depends on the structure of the
// prologue and the last complete field we are able to read. Before V5 we stop
// before reading the file length. In V5, we stop before the filename.
uint64_t ExpectedEnd = Prologue.TotalLength + Prologue.sizeofTotalLength() -
(Version < 5 ? 2 : 8);
std::vector<std::string> Errs;
Errs.emplace_back(
(Twine("parsing line table prologue at 0x00000000 found an invalid "
"directory or file table description at 0x000000") +
Twine::utohexstr(ExpectedEnd))
.str());
if (Version < 5) {
Errs.emplace_back("file names table was not null terminated before the end "
"of the prologue");
} else {
Errs.emplace_back(
"failed to parse file entry because extracting the form value failed");
}
EXPECT_THAT_ERROR(std::move(Recoverable),
FailedWithMessageArray(testing::ElementsAreArray(Errs)));
}
INSTANTIATE_TEST_SUITE_P(
LineTableTestParams, DebugLineParameterisedFixture,
Values(std::make_pair(
2, DWARF32), // Test lower-bound of v2-3 fields and DWARF32.
std::make_pair(3, DWARF32), // Test upper-bound of v2-3 fields.
std::make_pair(4, DWARF64), // Test v4 fields and DWARF64.
std::make_pair(5, DWARF32), std::make_pair(5, DWARF64)));
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_ErrorForExtendedOpcodeLengthSmallerThanExpected) {
#else
TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthSmallerThanExpected) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
LT.addByte(0xaa);
// The Length should be 1 + sizeof(ULEB) for a set discriminator opcode.
// The operand will be read for both the discriminator opcode and then parsed
// again as DW_LNS_negate_stmt, to respect the claimed length.
LT.addExtendedOpcode(1, DW_LNE_set_discriminator,
{{DW_LNS_negate_stmt, LineTable::ULEB}});
LT.addByte(0xbb);
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(std::move(Recoverable),
FailedWithMessage("unexpected line op length at offset "
"0x00000031 expected 0x01 found 0x02"));
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u);
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
EXPECT_EQ((*ExpectedLineTable)->Rows[1].IsStmt, 0u);
EXPECT_EQ((*ExpectedLineTable)->Rows[1].Discriminator, DW_LNS_negate_stmt);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_ErrorForExtendedOpcodeLengthLargerThanExpected) {
#else
TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthLargerThanExpected) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
LT.addByte(0xaa);
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
// The Length should be 1 for an end sequence opcode.
LT.addExtendedOpcode(2, DW_LNE_end_sequence, {});
// The negate statement opcode will be skipped.
LT.addStandardOpcode(DW_LNS_negate_stmt, {});
LT.addByte(0xbb);
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(std::move(Recoverable),
FailedWithMessage("unexpected line op length at offset "
"0x00000032 expected 0x02 found 0x01"));
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 4u);
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 2u);
ASSERT_EQ((*ExpectedLineTable)->Sequences[1].FirstRowIndex, 2u);
EXPECT_EQ((*ExpectedLineTable)->Rows[2].IsStmt, 1u);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_ErrorForUnitLengthTooLarge) {
#else
TEST_F(DebugLineBasicFixture, ErrorForUnitLengthTooLarge) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &Padding = Gen->addLineTable();
// Add some padding to show that a non-zero offset is handled correctly.
Padding.setCustomPrologue({{0, LineTable::Byte}});
LineTable &LT = Gen->addLineTable();
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
// Set the total length to 1 higher than the actual length.
++Prologue.TotalLength;
LT.setPrologue(Prologue);
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 1, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(
std::move(Recoverable),
FailedWithMessage("line table program with offset 0x00000001 has length "
"0x00000034 but only 0x00000033 bytes are available"));
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
EXPECT_EQ((*ExpectedLineTable)->Rows.size(), 2u);
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_ErrorForMismatchedAddressSize) {
#else
TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) {
#endif
if (!setupGenerator(4, 8))
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
// The line data extractor expects size 8 (Quad) addresses.
uint64_t Addr1 = 0x11223344;
LT.addExtendedOpcode(5, DW_LNE_set_address, {{Addr1, LineTable::Long}});
LT.addStandardOpcode(DW_LNS_copy, {});
// Show that the expected address size is unchanged, so later valid lines
// don't cause a problem.
uint64_t Addr2 = 0x1122334455667788;
LT.addExtendedOpcode(9, DW_LNE_set_address, {{Addr2, LineTable::Quad}});
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(std::move(Recoverable),
FailedWithMessage("mismatching address size at offset "
"0x00000030 expected 0x08 found 0x04"));
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 2u);
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, Addr1);
EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_ErrorForMismatchedAddressSizeUnsetInitialAddress) {
#else
TEST_F(DebugLineBasicFixture,
ErrorForMismatchedAddressSizeUnsetInitialAddress) {
#endif
if (!setupGenerator(4, 0))
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
uint64_t Addr1 = 0x11223344;
LT.addExtendedOpcode(5, DW_LNE_set_address, {{Addr1, LineTable::Long}});
LT.addStandardOpcode(DW_LNS_copy, {});
uint64_t Addr2 = 0x1122334455667788;
LT.addExtendedOpcode(9, DW_LNE_set_address, {{Addr2, LineTable::Quad}});
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(std::move(Recoverable),
FailedWithMessage("mismatching address size at offset "
"0x00000038 expected 0x04 found 0x08"));
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 2u);
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, Addr1);
EXPECT_EQ((*ExpectedLineTable)->Rows[1].Address.Address, Addr2);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_ErrorForUnsupportedAddressSizeInSetAddressLength) {
#else
TEST_F(DebugLineBasicFixture,
ErrorForUnsupportedAddressSizeInSetAddressLength) {
#endif
// Use DWARF v4, and 0 for data extractor address size so that the address
// size is derived from the opcode length.
if (!setupGenerator(4, 0))
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
// 4 == length of the extended opcode, i.e. 1 for the opcode itself and 3 for
// the Half (2) + Byte (1) operand, representing the unsupported address size.
LT.addExtendedOpcode(4, DW_LNE_set_address,
{{0x1234, LineTable::Half}, {0x56, LineTable::Byte}});
LT.addStandardOpcode(DW_LNS_copy, {});
// Special opcode to ensure the address has changed between the first and last
// row in the sequence. Without this, the sequence will not be recorded.
LT.addByte(0xaa);
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(
std::move(Recoverable),
FailedWithMessage("address size 0x03 of DW_LNE_set_address opcode at "
"offset 0x00000030 is unsupported"));
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u);
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
// Show that the set address opcode is ignored in this case.
EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, 0u);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_ErrorForAddressSizeGreaterThanByteSize) {
#else
TEST_F(DebugLineBasicFixture, ErrorForAddressSizeGreaterThanByteSize) {
#endif
// Use DWARF v4, and 0 for data extractor address size so that the address
// size is derived from the opcode length.
if (!setupGenerator(4, 0))
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
// Specifically use an operand size that has a trailing byte of a supported
// size (8), so that any potential truncation would result in a valid size.
std::vector<LineTable::ValueAndLength> Operands(0x108);
LT.addExtendedOpcode(Operands.size() + 1, DW_LNE_set_address, Operands);
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(
std::move(Recoverable),
FailedWithMessage("address size 0x108 of DW_LNE_set_address opcode at "
"offset 0x00000031 is unsupported"));
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_ErrorForUnsupportedAddressSizeDefinedInHeader) {
#else
TEST_F(DebugLineBasicFixture, ErrorForUnsupportedAddressSizeDefinedInHeader) {
#endif
// Use 0 for data extractor address size so that it does not clash with the
// header address size.
if (!setupGenerator(5, 0))
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
// AddressSize + 1 == length of the extended opcode, i.e. 1 for the opcode
// itself and 9 for the Quad (8) + Byte (1) operand representing the
// unsupported address size.
uint8_t AddressSize = 9;
LT.addExtendedOpcode(AddressSize + 1, DW_LNE_set_address,
{{0x12345678, LineTable::Quad}, {0, LineTable::Byte}});
LT.addStandardOpcode(DW_LNS_copy, {});
// Special opcode to ensure the address has changed between the first and last
// row in the sequence. Without this, the sequence will not be recorded.
LT.addByte(0xaa);
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
Prologue.FormParams.AddrSize = AddressSize;
LT.setPrologue(Prologue);
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(
std::move(Recoverable),
FailedWithMessage("address size 0x09 of DW_LNE_set_address opcode at "
"offset 0x00000038 is unsupported"));
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
ASSERT_EQ((*ExpectedLineTable)->Rows.size(), 3u);
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
// Show that the set address opcode is ignored in this case.
EXPECT_EQ((*ExpectedLineTable)->Rows[0].Address.Address, 0u);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_CallbackUsedForUnterminatedSequence) {
#else
TEST_F(DebugLineBasicFixture, CallbackUsedForUnterminatedSequence) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
LT.addExtendedOpcode(9, DW_LNE_set_address,
{{0x1122334455667788, LineTable::Quad}});
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addByte(0xaa);
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
LT.addExtendedOpcode(9, DW_LNE_set_address,
{{0x99aabbccddeeff00, LineTable::Quad}});
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addByte(0xbb);
LT.addByte(0xcc);
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(std::move(Recoverable),
FailedWithMessage("last sequence in debug line table at "
"offset 0x00000000 is not terminated"));
ASSERT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
EXPECT_EQ((*ExpectedLineTable)->Rows.size(), 6u);
// The unterminated sequence is not added to the sequence list.
EXPECT_EQ((*ExpectedLineTable)->Sequences.size(), 1u);
}
struct AdjustAddressFixtureBase : public CommonFixture {
virtual ~AdjustAddressFixtureBase() {}
// Create and update the prologue as specified by the subclass, then return
// the length of the table.
virtual uint64_t editPrologue(LineTable &LT) = 0;
virtual uint64_t getAdjustedAddr(uint64_t Base, uint64_t ConstIncrs,
uint64_t SpecialIncrs,
uint64_t AdvanceIncrs) {
return Base + ConstIncrs + SpecialIncrs + AdvanceIncrs;
}
virtual uint64_t getAdjustedLine(uint64_t Base, uint64_t Incr) {
return Base + Incr;
}
uint64_t setupNoProblemTable() {
LineTable &NoProblem = Gen->addLineTable();
NoProblem.addExtendedOpcode(9, DW_LNE_set_address,
{{0xabcd, LineTable::Quad}});
NoProblem.addExtendedOpcode(1, DW_LNE_end_sequence, {});
return editPrologue(NoProblem);
}
uint64_t setupConstAddPcFirstTable() {
LineTable &ConstAddPCFirst = Gen->addLineTable();
ConstAddPCFirst.addExtendedOpcode(9, DW_LNE_set_address,
{{ConstAddPCAddr, LineTable::Quad}});
ConstAddPCFirst.addStandardOpcode(DW_LNS_const_add_pc, {});
ConstAddPCFirst.addStandardOpcode(DW_LNS_const_add_pc, {});
ConstAddPCFirst.addStandardOpcode(DW_LNS_advance_pc,
{{0x10, LineTable::ULEB}});
ConstAddPCFirst.addByte(0x21); // Special opcode, +1 op, +1 line.
ConstAddPCFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {});
return editPrologue(ConstAddPCFirst);
}
uint64_t setupSpecialFirstTable() {
LineTable &SpecialFirst = Gen->addLineTable();
SpecialFirst.addExtendedOpcode(9, DW_LNE_set_address,
{{SpecialAddr, LineTable::Quad}});
SpecialFirst.addByte(0x22); // Special opcode, +1 op, +2 line.
SpecialFirst.addStandardOpcode(DW_LNS_const_add_pc, {});
SpecialFirst.addStandardOpcode(DW_LNS_advance_pc,
{{0x20, LineTable::ULEB}});
SpecialFirst.addByte(0x23); // Special opcode, +1 op, +3 line.
SpecialFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {});
return editPrologue(SpecialFirst);
}
uint64_t setupAdvancePcFirstTable() {
LineTable &AdvancePCFirst = Gen->addLineTable();
AdvancePCFirst.addExtendedOpcode(9, DW_LNE_set_address,
{{AdvancePCAddr, LineTable::Quad}});
AdvancePCFirst.addStandardOpcode(DW_LNS_advance_pc,
{{0x30, LineTable::ULEB}});
AdvancePCFirst.addStandardOpcode(DW_LNS_const_add_pc, {});
AdvancePCFirst.addStandardOpcode(DW_LNS_advance_pc,
{{0x40, LineTable::ULEB}});
AdvancePCFirst.addByte(0x24); // Special opcode, +1 op, +4 line.
AdvancePCFirst.addExtendedOpcode(1, DW_LNE_end_sequence, {});
return editPrologue(AdvancePCFirst);
}
void setupTables(bool AddAdvancePCFirstTable) {
LineTable &Padding = Gen->addLineTable();
Padding.setCustomPrologue({{0, LineTable::Byte}});
NoProblemOffset = 1;
// Show that no warning is generated for the case where no
// DW_LNS_const_add_pc or special opcode is used.
ConstAddPCOffset = setupNoProblemTable() + NoProblemOffset;
// Show that the warning is emitted for the first DW_LNS_const_add_pc opcode
// and then not again.
SpecialOffset = setupConstAddPcFirstTable() + ConstAddPCOffset;
// Show that the warning is emitted for the first special opcode and then
// not again.
AdvancePCOffset = setupSpecialFirstTable() + SpecialOffset;
// Show that the warning is emitted for the first DW_LNS_advance_pc opcode
// (if requested) and then not again.
if (AddAdvancePCFirstTable)
setupAdvancePcFirstTable();
}
Expected<const DWARFDebugLine::LineTable *>
checkTable(uint64_t Offset, StringRef OpcodeType, const Twine &MsgSuffix) {
auto ExpectedTable = Line.getOrParseLineTable(LineData, Offset, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
if (!IsErrorExpected) {
EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());
} else {
if (!ExpectedTable)
return ExpectedTable;
uint64_t ExpectedOffset = Offset +
(*ExpectedTable)->Prologue.getLength() +
11; // 11 == size of DW_LNE_set_address.
std::string OffsetHex = Twine::utohexstr(Offset).str();
std::string OffsetZeroes = std::string(8 - OffsetHex.size(), '0');
std::string ExpectedHex = Twine::utohexstr(ExpectedOffset).str();
std::string ExpectedZeroes = std::string(8 - ExpectedHex.size(), '0');
EXPECT_THAT_ERROR(
std::move(Recoverable),
FailedWithMessage(("line table program at offset 0x" + OffsetZeroes +
OffsetHex + " contains a " + OpcodeType +
" opcode at offset 0x" + ExpectedZeroes +
ExpectedHex + ", " + MsgSuffix)
.str()));
}
return ExpectedTable;
}
void runTest(bool CheckAdvancePC, Twine MsgSuffix) {
if (!setupGenerator(Version))
GTEST_SKIP();
setupTables(/*AddAdvancePCFirstTable=*/CheckAdvancePC);
generate();
auto ExpectedNoProblem = Line.getOrParseLineTable(
LineData, NoProblemOffset, *Context, nullptr, RecordRecoverable);
EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());
EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
ASSERT_THAT_EXPECTED(ExpectedNoProblem, Succeeded());
auto ExpectedConstAddPC =
checkTable(ConstAddPCOffset, "DW_LNS_const_add_pc", MsgSuffix);
ASSERT_THAT_EXPECTED(ExpectedConstAddPC, Succeeded());
ASSERT_EQ((*ExpectedConstAddPC)->Rows.size(), 2u);
EXPECT_EQ((*ExpectedConstAddPC)->Rows[0].Address.Address,
getAdjustedAddr(ConstAddPCAddr, ConstIncr * 2, 0x1, 0x10));
EXPECT_EQ((*ExpectedConstAddPC)->Rows[0].Line, getAdjustedLine(1, 1));
EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
auto ExpectedSpecial = checkTable(SpecialOffset, "special", MsgSuffix);
ASSERT_THAT_EXPECTED(ExpectedSpecial, Succeeded());
ASSERT_EQ((*ExpectedSpecial)->Rows.size(), 3u);
EXPECT_EQ((*ExpectedSpecial)->Rows[0].Address.Address,
getAdjustedAddr(SpecialAddr, 0, 1, 0));
EXPECT_EQ((*ExpectedSpecial)->Rows[0].Line, getAdjustedLine(1, 2));
EXPECT_EQ((*ExpectedSpecial)->Rows[1].Address.Address,
getAdjustedAddr(SpecialAddr, ConstIncr, 0x2, 0x20));
EXPECT_EQ((*ExpectedSpecial)->Rows[1].Line, getAdjustedLine(1, 5));
EXPECT_THAT_ERROR(std::move(Unrecoverable), Succeeded());
if (!CheckAdvancePC)
return;
auto ExpectedAdvancePC =
checkTable(AdvancePCOffset, "DW_LNS_advance_pc", MsgSuffix);
ASSERT_THAT_EXPECTED(ExpectedAdvancePC, Succeeded());
ASSERT_EQ((*ExpectedAdvancePC)->Rows.size(), 2u);
EXPECT_EQ((*ExpectedAdvancePC)->Rows[0].Address.Address,
getAdjustedAddr(AdvancePCAddr, ConstIncr, 0x1, 0x70));
EXPECT_EQ((*ExpectedAdvancePC)->Rows[0].Line, getAdjustedLine(1, 4));
}
uint64_t ConstIncr = 0x11;
uint64_t ConstAddPCAddr = 0x1234;
uint64_t SpecialAddr = 0x5678;
uint64_t AdvancePCAddr = 0xabcd;
uint64_t NoProblemOffset;
uint64_t ConstAddPCOffset;
uint64_t SpecialOffset;
uint64_t AdvancePCOffset;
uint16_t Version = 4;
bool IsErrorExpected;
};
struct MaxOpsPerInstFixture
: TestWithParam<std::tuple<uint16_t, uint8_t, bool>>,
AdjustAddressFixtureBase {
void SetUp() override {
std::tie(Version, MaxOpsPerInst, IsErrorExpected) = GetParam();
}
uint64_t editPrologue(LineTable &LT) override {
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
Prologue.MaxOpsPerInst = MaxOpsPerInst;
LT.setPrologue(Prologue);
return Prologue.TotalLength + Prologue.sizeofTotalLength();
}
uint8_t MaxOpsPerInst;
};
#ifdef _AIX
TEST_P(MaxOpsPerInstFixture, DISABLED_MaxOpsPerInstProblemsReportedCorrectly) {
#else
TEST_P(MaxOpsPerInstFixture, MaxOpsPerInstProblemsReportedCorrectly) {
#endif
runTest(/*CheckAdvancePC=*/true,
"but the prologue maximum_operations_per_instruction value is " +
Twine(unsigned(MaxOpsPerInst)) +
", which is unsupported. Assuming a value of 1 instead");
}
INSTANTIATE_TEST_SUITE_P(
MaxOpsPerInstParams, MaxOpsPerInstFixture,
Values(std::make_tuple(3, 0, false), // Test for version < 4 (no error).
std::make_tuple(4, 0, true), // Test zero value for V4 (error).
std::make_tuple(4, 1, false), // Test good value for V4 (no error).
std::make_tuple(
4, 2, true))); // Test one higher than permitted V4 (error).
struct LineRangeFixture : TestWithParam<std::tuple<uint8_t, bool>>,
AdjustAddressFixtureBase {
void SetUp() override { std::tie(LineRange, IsErrorExpected) = GetParam(); }
uint64_t editPrologue(LineTable &LT) override {
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
Prologue.LineRange = LineRange;
LT.setPrologue(Prologue);
return Prologue.TotalLength + Prologue.sizeofTotalLength();
}
uint64_t getAdjustedAddr(uint64_t Base, uint64_t ConstIncr,
uint64_t SpecialIncr,
uint64_t AdvanceIncr) override {
if (LineRange == 0)
return Base + AdvanceIncr;
return AdjustAddressFixtureBase::getAdjustedAddr(Base, ConstIncr,
SpecialIncr, AdvanceIncr);
}
uint64_t getAdjustedLine(uint64_t Base, uint64_t Incr) override {
return LineRange != 0
? AdjustAddressFixtureBase::getAdjustedLine(Base, Incr)
: Base;
}
uint8_t LineRange;
};
#ifdef _AIX
TEST_P(LineRangeFixture, DISABLED_LineRangeProblemsReportedCorrectly) {
#else
TEST_P(LineRangeFixture, LineRangeProblemsReportedCorrectly) {
#endif
runTest(/*CheckAdvancePC=*/false,
"but the prologue line_range value is 0. The address and line will "
"not be adjusted");
}
INSTANTIATE_TEST_SUITE_P(
LineRangeParams, LineRangeFixture,
Values(std::make_tuple(0, true), // Test zero value (error).
std::make_tuple(14, false))); // Test non-zero value (no error).
struct BadMinInstLenFixture : TestWithParam<std::tuple<uint8_t, bool>>,
AdjustAddressFixtureBase {
void SetUp() override {
std::tie(MinInstLength, IsErrorExpected) = GetParam();
}
uint64_t editPrologue(LineTable &LT) override {
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
Prologue.MinInstLength = MinInstLength;
LT.setPrologue(Prologue);
return Prologue.TotalLength + Prologue.sizeofTotalLength();
}
uint64_t getAdjustedAddr(uint64_t Base, uint64_t ConstIncr,
uint64_t SpecialIncr,
uint64_t AdvanceIncr) override {
return MinInstLength != 0 ? AdjustAddressFixtureBase::getAdjustedAddr(
Base, ConstIncr, SpecialIncr, AdvanceIncr)
: Base;
}
uint8_t MinInstLength;
};
#ifdef _AIX
TEST_P(BadMinInstLenFixture, DISABLED_MinInstLengthProblemsReportedCorrectly) {
#else
TEST_P(BadMinInstLenFixture, MinInstLengthProblemsReportedCorrectly) {
#endif
runTest(/*CheckAdvancePC=*/true,
"but the prologue minimum_instruction_length value is 0, which "
"prevents any address advancing");
}
INSTANTIATE_TEST_SUITE_P(
BadMinInstLenParams, BadMinInstLenFixture,
Values(std::make_tuple(0, true), // Test zero value (error).
std::make_tuple(1, false))); // Test non-zero value (no error).
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_ParserParsesCorrectly) {
#else
TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) {
#endif
if (!setupGenerator())
GTEST_SKIP();
DWARFDebugLine::SectionParser Parser = setupParser();
EXPECT_EQ(Parser.getOffset(), 0u);
ASSERT_FALSE(Parser.done());
DWARFDebugLine::LineTable Parsed =
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
checkDefaultPrologue(4, DWARF32, Parsed.Prologue, 16);
EXPECT_EQ(Parsed.Sequences.size(), 1u);
EXPECT_EQ(Parser.getOffset(), 62u);
ASSERT_FALSE(Parser.done());
DWARFDebugLine::LineTable Parsed2 =
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
checkDefaultPrologue(4, DWARF64, Parsed2.Prologue, 16);
EXPECT_EQ(Parsed2.Sequences.size(), 1u);
EXPECT_EQ(Parser.getOffset(), 136u);
EXPECT_TRUE(Parser.done());
EXPECT_FALSE(Recoverable);
EXPECT_FALSE(Unrecoverable);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_ParserSkipsCorrectly) {
#else
TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) {
#endif
if (!setupGenerator())
GTEST_SKIP();
DWARFDebugLine::SectionParser Parser = setupParser();
EXPECT_EQ(Parser.getOffset(), 0u);
ASSERT_FALSE(Parser.done());
Parser.skip(RecordRecoverable, RecordUnrecoverable);
EXPECT_EQ(Parser.getOffset(), 62u);
ASSERT_FALSE(Parser.done());
Parser.skip(RecordRecoverable, RecordUnrecoverable);
EXPECT_EQ(Parser.getOffset(), 136u);
EXPECT_TRUE(Parser.done());
EXPECT_FALSE(Recoverable);
EXPECT_FALSE(Unrecoverable);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_ParserAlwaysDoneForEmptySection) {
#else
TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) {
#endif
if (!setupGenerator())
GTEST_SKIP();
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
EXPECT_TRUE(Parser.done());
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_ParserMarkedAsDoneForBadLengthWhenParsing) {
#else
TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenParsing) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue({{0xfffffff0, LineTable::Long}});
Gen->addLineTable();
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
EXPECT_EQ(Parser.getOffset(), 0u);
EXPECT_TRUE(Parser.done());
EXPECT_FALSE(Recoverable);
EXPECT_THAT_ERROR(
std::move(Unrecoverable),
FailedWithMessage(
"parsing line table prologue at offset 0x00000000: unsupported "
"reserved unit length of value 0xfffffff0"));
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_ParserMarkedAsDoneForBadLengthWhenSkipping) {
#else
TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenSkipping) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
LT.setCustomPrologue({{0xfffffff0, LineTable::Long}});
Gen->addLineTable();
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
Parser.skip(RecordRecoverable, RecordUnrecoverable);
EXPECT_EQ(Parser.getOffset(), 0u);
EXPECT_TRUE(Parser.done());
EXPECT_FALSE(Recoverable);
EXPECT_THAT_ERROR(
std::move(Unrecoverable),
FailedWithMessage(
"parsing line table prologue at offset 0x00000000: unsupported "
"reserved unit length of value 0xfffffff0"));
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_ParserReportsFirstErrorInEachTableWhenParsing) {
#else
TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable(DWARF32);
LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
LineTable &LT2 = Gen->addLineTable(DWARF32);
LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
ASSERT_FALSE(Parser.done());
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
EXPECT_TRUE(Parser.done());
EXPECT_THAT_ERROR(std::move(Recoverable), Succeeded());
EXPECT_THAT_ERROR(
std::move(Unrecoverable),
FailedWithMessage("parsing line table prologue at offset 0x00000000: "
"unsupported version 0",
"parsing line table prologue at offset 0x00000006: "
"unsupported version 1"));
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_ParserReportsNonPrologueProblemsWhenParsing) {
#else
TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable(DWARF32);
LT.addExtendedOpcode(0x42, DW_LNE_end_sequence, {});
LineTable &LT2 = Gen->addLineTable(DWARF32);
LT2.addExtendedOpcode(9, DW_LNE_set_address,
{{0x1234567890abcdef, LineTable::Quad}});
LT2.addStandardOpcode(DW_LNS_copy, {});
LT2.addByte(0xbb);
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
EXPECT_FALSE(Unrecoverable);
ASSERT_FALSE(Parser.done());
EXPECT_THAT_ERROR(std::move(Recoverable),
FailedWithMessage("unexpected line op length at offset "
"0x00000030 expected 0x42 found 0x01"));
// Reset the error state so that it does not confuse the next set of checks.
Unrecoverable = Error::success();
Parser.parseNext(RecordRecoverable, RecordUnrecoverable);
EXPECT_TRUE(Parser.done());
EXPECT_THAT_ERROR(std::move(Recoverable),
FailedWithMessage("last sequence in debug line table at "
"offset 0x00000031 is not terminated"));
EXPECT_FALSE(Unrecoverable);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_ParserReportsPrologueErrorsInEachTableWhenSkipping) {
#else
TEST_F(DebugLineBasicFixture,
ParserReportsPrologueErrorsInEachTableWhenSkipping) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable(DWARF32);
LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}});
LineTable &LT2 = Gen->addLineTable(DWARF32);
LT2.setCustomPrologue({{2, LineTable::Long}, {1, LineTable::Half}});
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
Parser.skip(RecordRecoverable, RecordUnrecoverable);
ASSERT_FALSE(Parser.done());
Parser.skip(RecordRecoverable, RecordUnrecoverable);
EXPECT_TRUE(Parser.done());
EXPECT_FALSE(Recoverable);
EXPECT_THAT_ERROR(
std::move(Unrecoverable),
FailedWithMessage("parsing line table prologue at offset 0x00000000: "
"unsupported version 0",
"parsing line table prologue at offset 0x00000006: "
"unsupported version 1"));
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture,
DISABLED_ParserIgnoresNonPrologueErrorsWhenSkipping) {
#else
TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = Gen->addLineTable(DWARF32);
LT.addExtendedOpcode(42, DW_LNE_end_sequence, {});
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
Parser.skip(RecordRecoverable, RecordUnrecoverable);
EXPECT_TRUE(Parser.done());
EXPECT_FALSE(Recoverable);
EXPECT_FALSE(Unrecoverable);
}
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_VerboseOutput) {
#else
TEST_F(DebugLineBasicFixture, VerboseOutput) {
#endif
if (!setupGenerator(5))
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
LT.addByte(0); // Extended opcode with zero length.
LT.addByte(0);
// Zero-value extended opcode.
LT.addExtendedOpcode(2, 0, {{1, LineTable::Byte}});
// Unknown extended opcode.
LT.addExtendedOpcode(2, 0x42, {{1, LineTable::Byte}});
LT.addExtendedOpcode(9, DW_LNE_set_address,
{{0x123456789abcdef, LineTable::Quad}});
LT.addExtendedOpcode(6, DW_LNE_define_file,
{{'a', LineTable::Byte},
{'\0', LineTable::Byte},
{2, LineTable::ULEB},
{3, LineTable::ULEB},
{4, LineTable::ULEB}});
LT.addExtendedOpcode(2, DW_LNE_set_discriminator, {{0x7f, LineTable::ULEB}});
LT.addStandardOpcode(DW_LNS_copy, {});
LT.addStandardOpcode(DW_LNS_advance_pc, {{11, LineTable::ULEB}});
LT.addStandardOpcode(DW_LNS_advance_line, {{22, LineTable::SLEB}});
LT.addStandardOpcode(DW_LNS_set_file, {{33, LineTable::ULEB}});
LT.addStandardOpcode(DW_LNS_set_column, {{44, LineTable::ULEB}});
LT.addStandardOpcode(DW_LNS_negate_stmt, {});
LT.addStandardOpcode(DW_LNS_set_basic_block, {});
LT.addStandardOpcode(DW_LNS_const_add_pc, {});
LT.addStandardOpcode(DW_LNS_fixed_advance_pc, {{55, LineTable::Half}});
LT.addStandardOpcode(DW_LNS_set_prologue_end, {});
LT.addStandardOpcode(DW_LNS_set_epilogue_begin, {});
LT.addStandardOpcode(DW_LNS_set_isa, {{66, LineTable::ULEB}});
// Add unknown standard opcode with operands.
LT.addStandardOpcode(
0xd, {{1, LineTable::ULEB}, {0x123456789abcdef, LineTable::ULEB}});
// Add unknown standard opcode without operands.
LT.addStandardOpcode(0xe, {});
LT.addByte(0xff); // Special opcode.
LT.addExtendedOpcode(1, DW_LNE_end_sequence, {});
// Adjust the prologue to account for the extra standard opcode.
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
Prologue.TotalLength += 2;
Prologue.PrologueLength += 2;
Prologue.OpcodeBase += 2;
Prologue.StandardOpcodeLengths.push_back(2);
Prologue.StandardOpcodeLengths.push_back(0);
LT.setPrologue(Prologue);
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
std::string Output;
raw_string_ostream OS(Output);
Parser.parseNext(RecordRecoverable, RecordUnrecoverable, &OS,
/*Verbose=*/true);
OS.flush();
StringRef OutputRef(Output);
size_t Pos = 0;
auto NextLine = [&Pos, &OutputRef]() {
size_t EOL = OutputRef.find_first_of('\n', Pos);
StringRef Line = OutputRef.substr(Pos, EOL - Pos);
Pos = EOL + 1;
return Line;
};
EXPECT_EQ(NextLine(), "Line table prologue:");
EXPECT_EQ(NextLine(), " total_length: 0x00000078");
EXPECT_EQ(NextLine(), " format: DWARF32");
EXPECT_EQ(NextLine(), " version: 5");
EXPECT_EQ(NextLine(), " address_size: 8");
EXPECT_EQ(NextLine(), " seg_select_size: 0");
EXPECT_EQ(NextLine(), " prologue_length: 0x0000002c");
EXPECT_EQ(NextLine(), " min_inst_length: 1");
EXPECT_EQ(NextLine(), "max_ops_per_inst: 1");
EXPECT_EQ(NextLine(), " default_is_stmt: 1");
EXPECT_EQ(NextLine(), " line_base: -5");
EXPECT_EQ(NextLine(), " line_range: 14");
EXPECT_EQ(NextLine(), " opcode_base: 15");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_copy] = 0");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_advance_pc] = 1");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_advance_line] = 1");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_file] = 1");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_column] = 1");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_negate_stmt] = 0");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_basic_block] = 0");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_const_add_pc] = 0");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_fixed_advance_pc] = 1");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_prologue_end] = 0");
EXPECT_EQ(NextLine(),
"standard_opcode_lengths[DW_LNS_set_epilogue_begin] = 0");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_set_isa] = 1");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_unknown_d] = 2");
EXPECT_EQ(NextLine(), "standard_opcode_lengths[DW_LNS_unknown_e] = 0");
EXPECT_EQ(NextLine(), "include_directories[ 0] = \"a dir\"");
EXPECT_EQ(NextLine(), "file_names[ 0]:");
EXPECT_EQ(NextLine(), " name: \"a file\"");
EXPECT_EQ(NextLine(), " dir_index: 0");
EXPECT_EQ(NextLine(), "");
EXPECT_EQ(NextLine(), " Address Line Column File ISA Discriminator Flags");
EXPECT_EQ(NextLine(), " ------------------ ------ ------ ------ --- ------------- -------------");
EXPECT_EQ(NextLine(),
"0x00000038: 00 Badly formed extended line op (length 0)");
EXPECT_EQ(NextLine(),
"0x0000003a: 00 Unrecognized extended op 0x00 length 2");
EXPECT_EQ(NextLine(),
"0x0000003e: 00 Unrecognized extended op 0x42 length 2");
EXPECT_EQ(NextLine(),
"0x00000042: 00 DW_LNE_set_address (0x0123456789abcdef)");
EXPECT_EQ(NextLine(), "0x0000004d: 00 DW_LNE_define_file (a, dir=2, "
"mod_time=(0x0000000000000003), length=4)");
EXPECT_EQ(NextLine(), "0x00000055: 00 DW_LNE_set_discriminator (127)");
EXPECT_EQ(NextLine(), "0x00000059: 01 DW_LNS_copy");
EXPECT_EQ(NextLine(), " 0x0123456789abcdef 1 0 1 "
"0 127 is_stmt");
EXPECT_EQ(NextLine(), "0x0000005a: 02 DW_LNS_advance_pc (11)");
EXPECT_EQ(NextLine(), "0x0000005c: 03 DW_LNS_advance_line (23)");
EXPECT_EQ(NextLine(), "0x0000005e: 04 DW_LNS_set_file (33)");
EXPECT_EQ(NextLine(), "0x00000060: 05 DW_LNS_set_column (44)");
EXPECT_EQ(NextLine(), "0x00000062: 06 DW_LNS_negate_stmt");
EXPECT_EQ(NextLine(), "0x00000063: 07 DW_LNS_set_basic_block");
EXPECT_EQ(NextLine(),
"0x00000064: 08 DW_LNS_const_add_pc (0x0000000000000011)");
EXPECT_EQ(NextLine(), "0x00000065: 09 DW_LNS_fixed_advance_pc (0x0037)");
EXPECT_EQ(NextLine(), "0x00000068: 0a DW_LNS_set_prologue_end");
EXPECT_EQ(NextLine(), "0x00000069: 0b DW_LNS_set_epilogue_begin");
EXPECT_EQ(NextLine(), "0x0000006a: 0c DW_LNS_set_isa (66)");
EXPECT_EQ(NextLine(), "0x0000006c: 0d Unrecognized standard opcode "
"(operands: 0x0000000000000001, 0x0123456789abcdef)");
EXPECT_EQ(NextLine(), "0x00000077: 0e Unrecognized standard opcode");
EXPECT_EQ(NextLine(), "0x00000078: ff address += 17, line += -3");
EXPECT_EQ(NextLine(),
" 0x0123456789abce53 20 44 33 66 "
" 0 basic_block prologue_end epilogue_begin");
EXPECT_EQ(NextLine(), "0x00000079: 00 DW_LNE_end_sequence");
EXPECT_EQ(NextLine(), " 0x0123456789abce53 20 44 33 "
"66 0 end_sequence");
EXPECT_EQ(NextLine(), "");
EXPECT_EQ(Output.size(), Pos);
}
struct TruncatedPrologueFixture
: public TestWithParam<
std::tuple<uint64_t, uint64_t, uint16_t, DwarfFormat, StringRef>>,
public CommonFixture {
void SetUp() override {
std::tie(Length, ExpectedOffset, Version, Format, ExpectedErr) = GetParam();
}
uint64_t Length;
uint64_t ExpectedOffset;
uint16_t Version;
DwarfFormat Format;
StringRef ExpectedErr;
};
#ifdef _AIX
TEST_P(TruncatedPrologueFixture, DISABLED_ErrorForTruncatedPrologue) {
#else
TEST_P(TruncatedPrologueFixture, ErrorForTruncatedPrologue) {
#endif
if (!setupGenerator(Version))
GTEST_SKIP();
LineTable &Padding = Gen->addLineTable();
// Add some padding to show that a non-zero offset is handled correctly.
Padding.setCustomPrologue({{0, LineTable::Byte}});
// Add a table with only two standard opcodes - we don't need to test the full
// set.
LineTable &Table = Gen->addLineTable(Format);
DWARFDebugLine::Prologue InputPrologue = Table.createBasicPrologue();
InputPrologue.OpcodeBase = 3;
InputPrologue.StandardOpcodeLengths.resize(2);
Table.setPrologue(InputPrologue);
generate();
// Truncate the data extractor to the specified length.
LineData = DWARFDataExtractor(LineData, Length);
DWARFDebugLine::Prologue Prologue;
uint64_t Offset = 1;
Error Err = Prologue.parse(LineData, &Offset, RecordRecoverable, *Context);
EXPECT_THAT_ERROR(std::move(Err), FailedWithMessage(ExpectedErr.str()));
EXPECT_EQ(Offset, ExpectedOffset);
}
INSTANTIATE_TEST_SUITE_P(
TruncatedPrologueParams, TruncatedPrologueFixture,
Values(
// Truncated length:
std::make_tuple(
4, 1, 4, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0x4 while reading [0x1, 0x5)"),
std::make_tuple(
4, 1, 4, DWARF64,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0x4 while reading [0x1, 0x5)"),
std::make_tuple(
0xc, 1, 4, DWARF64,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0xc while reading [0x5, 0xd)"),
// Truncated version:
std::make_tuple(
6, 5, 4, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0x6 while reading [0x5, 0x7)"),
// Truncated address size:
std::make_tuple(
7, 7, 5, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0x7 while reading [0x7, 0x8)"),
// Truncated segment selector size:
std::make_tuple(
8, 8, 5, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0x8 while reading [0x8, 0x9)"),
// Truncated prologue length:
std::make_tuple(
0xa, 7, 4, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0xa while reading [0x7, 0xb)"),
std::make_tuple(
0x16, 0xf, 4, DWARF64,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0x16 while reading [0xf, 0x17)"),
// Truncated min instruction length:
std::make_tuple(
0xb, 0xb, 4, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0xb while reading [0xb, 0xc)"),
// Truncated max ops per inst:
std::make_tuple(
0xc, 0xc, 4, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0xc while reading [0xc, 0xd)"),
// Truncated default is stmt:
std::make_tuple(
0xd, 0xd, 4, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0xd while reading [0xd, 0xe)"),
// Truncated line base:
std::make_tuple(
0xe, 0xe, 4, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0xe while reading [0xe, 0xf)"),
// Truncated line range:
std::make_tuple(
0xf, 0xf, 4, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0xf while reading [0xf, 0x10)"),
// Truncated opcode base:
std::make_tuple(
0x10, 0x10, 4, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0x10 while reading [0x10, 0x11)"),
// Truncated first standard opcode:
std::make_tuple(
0x11, 0x11, 4, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0x11 while reading [0x11, 0x12)"),
// Truncated second standard opcode:
std::make_tuple(
0x12, 0x12, 4, DWARF32,
"parsing line table prologue at offset 0x00000001: unexpected end "
"of data at offset 0x12 while reading [0x12, 0x13)")));
using ValueAndLengths = std::vector<LineTable::ValueAndLength>;
struct TruncatedOpcodeFixtureBase : public CommonFixture {
LineTable &setupTable() {
LineTable &LT = Gen->addLineTable();
// Creating the prologue before adding any opcodes ensures that the unit
// length does not include the table body.
DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue();
// Add an unrecognised standard opcode, and adjust prologue properties
// accordingly.
Prologue.TotalLength += BodyLength + 1;
++Prologue.PrologueLength;
++Prologue.OpcodeBase;
Prologue.StandardOpcodeLengths.push_back(2);
LT.setPrologue(Prologue);
return LT;
}
void runTest(uint8_t OpcodeValue) {
generate();
DWARFDebugLine::SectionParser Parser(LineData, *Context, Units);
std::string Output;
raw_string_ostream OS(Output);
Parser.parseNext(RecordRecoverable, RecordUnrecoverable, &OS,
/*Verbose=*/true);
OS.flush();
std::string LinePrefix =
("0x0000002f: 0" + Twine::utohexstr(OpcodeValue) + " ").str();
StringRef OutputRef(Output);
StringRef OutputToCheck = OutputRef.split(LinePrefix).second;
// Each extended opcode ends with a new line and then the table ends with an
// additional blank line.
EXPECT_EQ((ExpectedOutput + "\n\n").str(), OutputToCheck);
}
uint64_t BodyLength;
uint8_t Opcode;
ValueAndLengths Operands;
StringRef ExpectedOutput;
StringRef ExpectedErr;
};
struct TruncatedStandardOpcodeFixture
: public TestWithParam<
std::tuple<uint64_t, uint8_t, ValueAndLengths, StringRef, StringRef>>,
public TruncatedOpcodeFixtureBase {
void SetUp() override {
std::tie(BodyLength, Opcode, Operands, ExpectedOutput, ExpectedErr) =
GetParam();
}
};
struct TruncatedExtendedOpcodeFixture
: public TestWithParam<std::tuple<uint64_t, uint64_t, uint8_t,
ValueAndLengths, StringRef, StringRef>>,
public TruncatedOpcodeFixtureBase {
void SetUp() override {
std::tie(BodyLength, OpcodeLength, Opcode, Operands, ExpectedOutput,
ExpectedErr) = GetParam();
}
uint64_t OpcodeLength;
};
#ifdef _AIX
TEST_P(TruncatedExtendedOpcodeFixture,
DISABLED_ErrorForTruncatedExtendedOpcode) {
#else
TEST_P(TruncatedExtendedOpcodeFixture, ErrorForTruncatedExtendedOpcode) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = setupTable();
LT.addExtendedOpcode(OpcodeLength, Opcode, Operands);
runTest(0);
EXPECT_THAT_ERROR(std::move(Recoverable),
FailedWithMessage(ExpectedErr.str()));
}
INSTANTIATE_TEST_SUITE_P(
TruncatedExtendedOpcodeParams, TruncatedExtendedOpcodeFixture,
Values(
// Truncated length:
std::make_tuple(1, 1, /*ArbitraryOpcode=*/0x7f, ValueAndLengths(), "",
"unable to decode LEB128 at offset 0x00000030: "
"malformed uleb128, extends past end"),
// Truncated opcode:
std::make_tuple(
2, 9, /*ArbitraryOpcode=*/0x7f, ValueAndLengths(), "",
"unexpected end of data at offset 0x31 while reading [0x31, 0x32)"),
// Truncated operands:
std::make_tuple(
3, 9, DW_LNE_set_address,
ValueAndLengths{{0x1234567890abcdef, LineTable::Quad}},
"DW_LNE_set_address",
"unexpected end of data at offset 0x32 while reading [0x32, 0x3a)"),
std::make_tuple(
10, 9, DW_LNE_set_address,
ValueAndLengths{{0x1234567878563412, LineTable::Quad}},
"DW_LNE_set_address (<parsing error> 12 34 56 78 78 56 34)",
"unexpected end of data at offset 0x39 while reading [0x32, 0x3a)"),
std::make_tuple(3, 6, DW_LNE_define_file,
ValueAndLengths{{'a', LineTable::Byte},
{'\0', LineTable::Byte},
{1, LineTable::ULEB},
{1, LineTable::ULEB},
{1, LineTable::ULEB}},
"DW_LNE_define_file",
"no null terminated string at offset 0x32"),
std::make_tuple(5, 6, DW_LNE_define_file,
ValueAndLengths{{'a', LineTable::Byte},
{'\0', LineTable::Byte},
{1, LineTable::ULEB},
{1, LineTable::ULEB},
{1, LineTable::ULEB}},
"DW_LNE_define_file (<parsing error> 61 00)",
"unable to decode LEB128 at offset 0x00000034: "
"malformed uleb128, extends past end"),
std::make_tuple(6, 6, DW_LNE_define_file,
ValueAndLengths{{'a', LineTable::Byte},
{'\0', LineTable::Byte},
{1, LineTable::ULEB},
{1, LineTable::ULEB},
{1, LineTable::ULEB}},
"DW_LNE_define_file (<parsing error> 61 00 01)",
"unable to decode LEB128 at offset 0x00000035: "
"malformed uleb128, extends past end"),
std::make_tuple(7, 6, DW_LNE_define_file,
ValueAndLengths{{'a', LineTable::Byte},
{'\0', LineTable::Byte},
{1, LineTable::ULEB},
{1, LineTable::ULEB},
{1, LineTable::ULEB}},
"DW_LNE_define_file (<parsing error> 61 00 01 01)",
"unable to decode LEB128 at offset 0x00000036: "
"malformed uleb128, extends past end"),
std::make_tuple(3, 2, DW_LNE_set_discriminator,
ValueAndLengths{{1, LineTable::ULEB}},
"DW_LNE_set_discriminator",
"unable to decode LEB128 at offset 0x00000032: "
"malformed uleb128, extends past end"),
std::make_tuple(
6, 5, /*Unknown=*/0x7f,
ValueAndLengths{{0x12343412, LineTable::Long}},
"Unrecognized extended op 0x7f length 5 (<parsing error> 12 34 34)",
"unexpected end of data at offset 0x35 while reading [0x32, "
"0x36)")));
#ifdef _AIX
TEST_P(TruncatedStandardOpcodeFixture,
DISABLED_ErrorForTruncatedStandardOpcode) {
#else
TEST_P(TruncatedStandardOpcodeFixture, ErrorForTruncatedStandardOpcode) {
#endif
if (!setupGenerator())
GTEST_SKIP();
LineTable &LT = setupTable();
LT.addStandardOpcode(Opcode, Operands);
runTest(Opcode);
EXPECT_THAT_ERROR(std::move(Unrecoverable),
FailedWithMessage(ExpectedErr.str()));
}
INSTANTIATE_TEST_SUITE_P(
TruncatedStandardOpcodeParams, TruncatedStandardOpcodeFixture,
Values(
std::make_tuple(2, DW_LNS_advance_pc,
ValueAndLengths{{0x100, LineTable::ULEB}},
"DW_LNS_advance_pc",
"unable to decode LEB128 at offset 0x00000030: "
"malformed uleb128, extends past end"),
std::make_tuple(2, DW_LNS_advance_line,
ValueAndLengths{{0x200, LineTable::SLEB}},
"DW_LNS_advance_line",
"unable to decode LEB128 at offset 0x00000030: "
"malformed sleb128, extends past end"),
std::make_tuple(2, DW_LNS_set_file,
ValueAndLengths{{0x300, LineTable::ULEB}},
"DW_LNS_set_file",
"unable to decode LEB128 at offset 0x00000030: "
"malformed uleb128, extends past end"),
std::make_tuple(2, DW_LNS_set_column,
ValueAndLengths{{0x400, LineTable::ULEB}},
"DW_LNS_set_column",
"unable to decode LEB128 at offset 0x00000030: "
"malformed uleb128, extends past end"),
std::make_tuple(
2, DW_LNS_fixed_advance_pc,
ValueAndLengths{{0x500, LineTable::Half}},
"DW_LNS_fixed_advance_pc",
"unexpected end of data at offset 0x31 while reading [0x30, 0x32)"),
std::make_tuple(2, DW_LNS_set_isa,
ValueAndLengths{{0x600, LineTable::ULEB}},
"DW_LNS_set_isa",
"unable to decode LEB128 at offset 0x00000030: "
"malformed uleb128, extends past end"),
std::make_tuple(2, 0xd,
ValueAndLengths{{0x700, LineTable::ULEB},
{0x800, LineTable::ULEB}},
"Unrecognized standard opcode",
"unable to decode LEB128 at offset 0x00000030: "
"malformed uleb128, extends past end"),
std::make_tuple(
4, 0xd,
ValueAndLengths{{0x900, LineTable::ULEB}, {0xa00, LineTable::ULEB}},
"Unrecognized standard opcode (operands: 0x0000000000000900)",
"unable to decode LEB128 at offset 0x00000032: "
"malformed uleb128, extends past end")));
#ifdef _AIX
TEST_F(DebugLineBasicFixture, DISABLED_PrintPathsProperly) {
#else
TEST_F(DebugLineBasicFixture, PrintPathsProperly) {
#endif
if (!setupGenerator(5))
GTEST_SKIP();
LineTable &LT = Gen->addLineTable();
DWARFDebugLine::Prologue P = LT.createBasicPrologue();
P.IncludeDirectories.push_back(
DWARFFormValue::createFromPValue(DW_FORM_string, "b dir"));
P.FileNames.push_back(DWARFDebugLine::FileNameEntry());
P.FileNames.back().Name =
DWARFFormValue::createFromPValue(DW_FORM_string, "b file");
P.FileNames.back().DirIdx = 1;
P.TotalLength += 14;
P.PrologueLength += 14;
LT.setPrologue(P);
generate();
auto ExpectedLineTable = Line.getOrParseLineTable(LineData, 0, *Context,
nullptr, RecordRecoverable);
EXPECT_THAT_EXPECTED(ExpectedLineTable, Succeeded());
std::string Result;
// DWARF 5 stores the compilation directory in two places: the Compilation
// Unit and the directory table entry 0, and implementations are free to use
// one or the other. This copy serves as the one stored in the CU.
StringRef CompDir = "a dir";
EXPECT_FALSE(
(*ExpectedLineTable)
->Prologue.getFileNameByIndex(
1, CompDir, DILineInfoSpecifier::FileLineInfoKind::None, Result));
EXPECT_TRUE((*ExpectedLineTable)
->Prologue.getFileNameByIndex(
1, CompDir,
DILineInfoSpecifier::FileLineInfoKind::RawValue, Result));
EXPECT_TRUE((*ExpectedLineTable)
->Prologue.getFileNameByIndex(
1, CompDir,
DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly,
Result));
EXPECT_STREQ(Result.c_str(), "b file");
EXPECT_TRUE((*ExpectedLineTable)
->Prologue.getFileNameByIndex(
1, CompDir,
DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath,
Result));
EXPECT_THAT(Result.c_str(), MatchesRegex("b dir.b file"));
EXPECT_TRUE((*ExpectedLineTable)
->Prologue.getFileNameByIndex(
1, CompDir,
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
Result));
EXPECT_THAT(Result.c_str(), MatchesRegex("a dir.b dir.b file"));
}
} // end anonymous namespace