//===- 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); } bool setupGenerator(uint16_t Version = 4, uint8_t AddrSize = 8) { AddressSize = AddrSize; Triple T = getDefaultTargetTripleForAddrSize(AddressSize == 0 ? 8 : AddressSize); if (!isConfigurationSupported(T)) return false; auto ExpectedGenerator = Generator::create(T, Version); if (ExpectedGenerator) Gen.reset(ExpectedGenerator->release()); return true; } 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 createContext() { if (!Gen) return nullptr; 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 < = 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 <2 = 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 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 Gen; std::unique_ptr Context; DWARFDataExtractor LineData; DWARFDebugLine Line; Error Recoverable; std::function RecordRecoverable; Error Unrecoverable; std::function RecordUnrecoverable; SmallVector, 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>, 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 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(*Prologue.IncludeDirectories[0].getAsCString(), "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(*Prologue.FileNames[0].Name.getAsCString(), "a file"); } TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffset) { if (!setupGenerator()) return; 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")); } TEST_F(DebugLineBasicFixture, GetOrParseLineTableAtInvalidOffsetAfterData) { if (!setupGenerator()) return; LineTable < = 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")); } TEST_P(DebugLineParameterisedFixture, PrologueGetLength) { if (!setupGenerator(Version)) return; LineTable < = 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); } TEST_P(DebugLineParameterisedFixture, GetOrParseLineTableValidTable) { if (!setupGenerator(Version)) return; SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " + (Format == DWARF64 ? "DWARF64" : "DWARF32")); LineTable < = 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 <2 = 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. } TEST_F(DebugLineBasicFixture, ErrorForReservedLength) { if (!setupGenerator()) return; LineTable < = 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, public CommonFixture { void SetUp() override { Version = GetParam(); } uint16_t Version; }; TEST_P(DebugLineUnsupportedVersionFixture, ErrorForUnsupportedVersion) { if (!setupGenerator()) return; LineTable < = 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_CASE_P(UnsupportedVersionTestParams, DebugLineUnsupportedVersionFixture, Values(/*1 below min */ 1, /* 1 above max */ 6, /* Maximum possible */ 0xffff), ); TEST_F(DebugLineBasicFixture, ErrorForInvalidV5IncludeDirTable) { if (!setupGenerator(5)) return; LineTable < = 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")); } TEST_P(DebugLineParameterisedFixture, ErrorForTooLargePrologueLength) { if (!setupGenerator(Version)) return; SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " + (Format == DWARF64 ? "DWARF64" : "DWARF32")); LineTable < = 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())); } TEST_P(DebugLineParameterisedFixture, ErrorForTooShortPrologueLength) { if (!setupGenerator(Version)) return; SCOPED_TRACE("Checking Version " + std::to_string(Version) + ", Format " + (Format == DWARF64 ? "DWARF64" : "DWARF32")); LineTable < = 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 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_CASE_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)), ); TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthSmallerThanExpected) { if (!setupGenerator()) return; LineTable < = 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); } TEST_F(DebugLineBasicFixture, ErrorForExtendedOpcodeLengthLargerThanExpected) { if (!setupGenerator()) return; LineTable < = 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); } TEST_F(DebugLineBasicFixture, ErrorForUnitLengthTooLarge) { if (!setupGenerator()) return; LineTable &Padding = Gen->addLineTable(); // Add some padding to show that a non-zero offset is handled correctly. Padding.setCustomPrologue({{0, LineTable::Byte}}); LineTable < = 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); } TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSize) { if (!setupGenerator(4, 8)) return; LineTable < = 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); } TEST_F(DebugLineBasicFixture, ErrorForMismatchedAddressSizeUnsetInitialAddress) { if (!setupGenerator(4, 0)) return; LineTable < = 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); } TEST_F(DebugLineBasicFixture, ErrorForUnsupportedAddressSizeInSetAddressLength) { // 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)) return; LineTable < = 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); } TEST_F(DebugLineBasicFixture, ErrorForAddressSizeGreaterThanByteSize) { // 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)) return; LineTable < = 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 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()); } TEST_F(DebugLineBasicFixture, ErrorForUnsupportedAddressSizeDefinedInHeader) { // Use 0 for data extractor address size so that it does not clash with the // header address size. if (!setupGenerator(5, 0)) return; LineTable < = 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); } TEST_F(DebugLineBasicFixture, CallbackUsedForUnterminatedSequence) { if (!setupGenerator()) return; LineTable < = 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 <) = 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 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)) return; 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>, AdjustAddressFixtureBase { void SetUp() override { std::tie(Version, MaxOpsPerInst, IsErrorExpected) = GetParam(); } uint64_t editPrologue(LineTable <) override { DWARFDebugLine::Prologue Prologue = LT.createBasicPrologue(); Prologue.MaxOpsPerInst = MaxOpsPerInst; LT.setPrologue(Prologue); return Prologue.TotalLength + Prologue.sizeofTotalLength(); } uint8_t MaxOpsPerInst; }; TEST_P(MaxOpsPerInstFixture, MaxOpsPerInstProblemsReportedCorrectly) { 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_CASE_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>, AdjustAddressFixtureBase { void SetUp() override { std::tie(LineRange, IsErrorExpected) = GetParam(); } uint64_t editPrologue(LineTable <) 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; }; TEST_P(LineRangeFixture, LineRangeProblemsReportedCorrectly) { runTest(/*CheckAdvancePC=*/false, "but the prologue line_range value is 0. The address and line will " "not be adjusted"); } INSTANTIATE_TEST_CASE_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>, AdjustAddressFixtureBase { void SetUp() override { std::tie(MinInstLength, IsErrorExpected) = GetParam(); } uint64_t editPrologue(LineTable <) 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; }; TEST_P(BadMinInstLenFixture, MinInstLengthProblemsReportedCorrectly) { runTest(/*CheckAdvancePC=*/true, "but the prologue minimum_instruction_length value is 0, which " "prevents any address advancing"); } INSTANTIATE_TEST_CASE_P( BadMinInstLenParams, BadMinInstLenFixture, Values(std::make_tuple(0, true), // Test zero value (error). std::make_tuple(1, false)), ); // Test non-zero value (no error). TEST_F(DebugLineBasicFixture, ParserParsesCorrectly) { if (!setupGenerator()) return; 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); } TEST_F(DebugLineBasicFixture, ParserSkipsCorrectly) { if (!setupGenerator()) return; 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); } TEST_F(DebugLineBasicFixture, ParserAlwaysDoneForEmptySection) { if (!setupGenerator()) return; generate(); DWARFDebugLine::SectionParser Parser(LineData, *Context, Units); EXPECT_TRUE(Parser.done()); } TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenParsing) { if (!setupGenerator()) return; LineTable < = 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")); } TEST_F(DebugLineBasicFixture, ParserMarkedAsDoneForBadLengthWhenSkipping) { if (!setupGenerator()) return; LineTable < = 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")); } TEST_F(DebugLineBasicFixture, ParserReportsFirstErrorInEachTableWhenParsing) { if (!setupGenerator()) return; LineTable < = Gen->addLineTable(DWARF32); LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}}); LineTable <2 = 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")); } TEST_F(DebugLineBasicFixture, ParserReportsNonPrologueProblemsWhenParsing) { if (!setupGenerator()) return; LineTable < = Gen->addLineTable(DWARF32); LT.addExtendedOpcode(0x42, DW_LNE_end_sequence, {}); LineTable <2 = 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); } TEST_F(DebugLineBasicFixture, ParserReportsPrologueErrorsInEachTableWhenSkipping) { if (!setupGenerator()) return; LineTable < = Gen->addLineTable(DWARF32); LT.setCustomPrologue({{2, LineTable::Long}, {0, LineTable::Half}}); LineTable <2 = 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")); } TEST_F(DebugLineBasicFixture, ParserIgnoresNonPrologueErrorsWhenSkipping) { if (!setupGenerator()) return; LineTable < = 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); } TEST_F(DebugLineBasicFixture, VerboseOutput) { if (!setupGenerator(5)) return; LineTable < = 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>, 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; }; TEST_P(TruncatedPrologueFixture, ErrorForTruncatedPrologue) { if (!setupGenerator(Version)) return; 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_CASE_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; struct TruncatedOpcodeFixtureBase : public CommonFixture { LineTable &setupTable() { LineTable < = 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>, public TruncatedOpcodeFixtureBase { void SetUp() override { std::tie(BodyLength, Opcode, Operands, ExpectedOutput, ExpectedErr) = GetParam(); } }; struct TruncatedExtendedOpcodeFixture : public TestWithParam>, public TruncatedOpcodeFixtureBase { void SetUp() override { std::tie(BodyLength, OpcodeLength, Opcode, Operands, ExpectedOutput, ExpectedErr) = GetParam(); } uint64_t OpcodeLength; }; TEST_P(TruncatedExtendedOpcodeFixture, ErrorForTruncatedExtendedOpcode) { if (!setupGenerator()) return; LineTable < = setupTable(); LT.addExtendedOpcode(OpcodeLength, Opcode, Operands); runTest(0); EXPECT_THAT_ERROR(std::move(Recoverable), FailedWithMessage(ExpectedErr.str())); } INSTANTIATE_TEST_CASE_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 ( 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 ( 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 ( 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 ( 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 ( 12 34 34)", "unexpected end of data at offset 0x35 while reading [0x32, " "0x36)")), ); TEST_P(TruncatedStandardOpcodeFixture, ErrorForTruncatedStandardOpcode) { if (!setupGenerator()) return; LineTable < = setupTable(); LT.addStandardOpcode(Opcode, Operands); runTest(Opcode); EXPECT_THAT_ERROR(std::move(Unrecoverable), FailedWithMessage(ExpectedErr.str())); } INSTANTIATE_TEST_CASE_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")), ); TEST_F(DebugLineBasicFixture, PrintPathsProperly) { if (!setupGenerator(5)) return; LineTable < = 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