diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFFormValue.h @@ -119,6 +119,19 @@ Optional> getAsBlock() const; Optional getAsCStringOffset() const; Optional getAsReferenceUVal() const; + /// Correctly extract any file paths from a form value. + /// + /// These attributes can be in the from DW_AT_decl_file or DW_AT_call_file + /// attributes. We need to use the file index in the correct DWARFUnit's line + /// table prologue, and each DWARFFormValue has the DWARFUnit the form value + /// was extracted from. + /// + /// \param Kind The kind of path to extract. + /// + /// \returns A valid string value on success, or llvm::None if the form class + /// is not FC_Constant, or if the file index is not valid. + Optional + getAsFile(DILineInfoSpecifier::FileLineInfoKind Kind) const; /// Skip a form's value in \p DebugInfoData at the offset specified by /// \p OffsetPtr. diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -566,18 +566,10 @@ std::string DWARFDie::getDeclFile(DILineInfoSpecifier::FileLineInfoKind Kind) const { - auto D = getAttributeValueAsReferencedDie(DW_AT_abstract_origin); - if (!D) - D = *this; - std::string FileName; - if (auto DeclFile = toUnsigned(D.find(DW_AT_decl_file))) { - if (const auto *LineTable = - getDwarfUnit()->getContext().getLineTableForUnit( - D.getDwarfUnit()->getLinkedUnit())) - LineTable->getFileNameByIndex( - *DeclFile, D.getDwarfUnit()->getCompilationDir(), Kind, FileName); - } - return FileName; + if (auto FormValue = findRecursively(DW_AT_decl_file)) + if (auto OptString = FormValue->getAsFile(Kind)) + return *OptString; + return {}; } void DWARFDie::getCallerFrame(uint32_t &CallFile, uint32_t &CallLine, diff --git a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFFormValue.cpp @@ -690,7 +690,7 @@ return R->Unit ? R->Unit->getOffset() + R->Offset : R->Offset; return None; } - + Optional DWARFFormValue::getAsRelativeReference() const { if (!isFormClass(FC_Reference)) return None; @@ -762,3 +762,17 @@ return None; return Value.uval; } + +Optional +DWARFFormValue::getAsFile(DILineInfoSpecifier::FileLineInfoKind Kind) const { + if (U == nullptr || !isFormClass(FC_Constant)) + return None; + DWARFUnit *DU = const_cast(U); + if (auto *LT = U->getContext().getLineTableForUnit(DU->getLinkedUnit())) { + std::string FileName; + if (LT->getFileNameByIndex(Value.uval, DU->getCompilationDir(), Kind, + FileName)) + return FileName; + } + return None; +} diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp --- a/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp @@ -100,4 +100,878 @@ "No DW_AT_call_data_value"))); } +TEST(DWARFDie, getDeclFile) { + const char *yamldata = R"( + debug_str: + - '' + - '/tmp/main.cpp' + - main + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_language + Form: DW_FORM_udata + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + - Code: 0x2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + debug_info: + - Length: 0x29 + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x1 + - Value: 0x2 + - Value: 0x0 + - AbbrCode: 0x2 + Values: + - Value: 0x1000 + - Value: 0x1050 + - Value: 0xF + - Value: 0x1 + - Value: 0x14 + - AbbrCode: 0x0 + debug_line: + - Length: 80 + Version: 2 + PrologueLength: 36 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - '/tmp' + Files: + - Name: main.cpp + DirIdx: 1 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 4096 + - Opcode: DW_LNS_advance_line + SData: 9 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_advance_line + SData: 1 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_advance_line + SData: 1 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 18446744073709548538 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 0 + )"; + + // Given DWARF like this: + // + // 0x0000000b: DW_TAG_compile_unit + // DW_AT_name ("/tmp/main.cpp") + // DW_AT_language (DW_LANG_C) + // DW_AT_stmt_list (0x00000000) + // + // 0x00000015: DW_TAG_subprogram + // DW_AT_low_pc (0x0000000000001000) + // DW_AT_high_pc (0x0000000000001050) + // DW_AT_name ("main") + // DW_AT_decl_file ("/tmp/main.cpp") + // DW_AT_decl_line (20) + // + // 0x0000002c: NULL + // + // This tests that we can extract the right DW_AT_decl_file from a DIE that + // has a DW_AT_decl_file attribute. + + Expected>> Sections = + DWARFYAML::emitDebugSections(StringRef(yamldata), + /*IsLittleEndian=*/true, + /*Is64BitAddrSize=*/true); + ASSERT_THAT_EXPECTED(Sections, Succeeded()); + std::unique_ptr Ctx = + DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true); + DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0); + ASSERT_NE(nullptr, CU); + DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false); + ASSERT_TRUE(Die.isValid()); + + DWARFDie MainDie = Die.getFirstChild(); + ASSERT_TRUE(MainDie.isValid()); + + std::string DeclFile = MainDie.getDeclFile( + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath); + + EXPECT_EQ(DeclFile, "/tmp/main.cpp"); +} + +TEST(DWARFDie, getDeclFileAbstractOrigin) { + const char *yamldata = R"( + debug_str: + - '' + - '/tmp/main.cpp' + - main + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_language + Form: DW_FORM_udata + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + - Code: 0x2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Attribute: DW_AT_abstract_origin + Form: DW_FORM_ref4 + - Code: 0x3 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + debug_info: + - Length: 0x2E + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x1 + - Value: 0x2 + - Value: 0x0 + - AbbrCode: 0x2 + Values: + - Value: 0x1000 + - Value: 0x1050 + - Value: 0x2A + - AbbrCode: 0x3 + Values: + - Value: 0xF + - Value: 0x1 + - Value: 0x14 + - AbbrCode: 0x0 + debug_line: + - Length: 80 + Version: 2 + PrologueLength: 36 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - '/tmp' + Files: + - Name: main.cpp + DirIdx: 1 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 4096 + - Opcode: DW_LNS_advance_line + SData: 9 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_advance_line + SData: 1 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_advance_line + SData: 1 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 18446744073709548538 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 0 + )"; + + // Given DWARF like this: + // + // 0x0000000b: DW_TAG_compile_unit + // DW_AT_name ("/tmp/main.cpp") + // DW_AT_language (DW_LANG_C) + // DW_AT_stmt_list (0x00000000) + // + // 0x00000015: DW_TAG_subprogram + // DW_AT_low_pc (0x0000000000001000) + // DW_AT_high_pc (0x0000000000001050) + // DW_AT_abstract_origin (0x0000002a "main") + // + // 0x0000002a: DW_TAG_subprogram + // DW_AT_name ("main") + // DW_AT_decl_file ("/tmp/main.cpp") + // DW_AT_decl_line (20) + // + // 0x00000031: NULL + + // + // The DIE at 0x00000015 uses a DW_AT_abstract_origin to point to the DIE at + // 0x0000002a, make sure that DWARFDie::getDeclFile() succeeds by extracting + // the right file name of "/tmp/main.cpp". + // + // This tests that when we have a DW_AT_abstract_origin with a compile unit + // relative form (DW_FORM_ref4) to another DIE that we get the right + // DW_AT_decl_file value. + + Expected>> Sections = + DWARFYAML::emitDebugSections(StringRef(yamldata), + /*IsLittleEndian=*/true, + /*Is64BitAddrSize=*/true); + ASSERT_THAT_EXPECTED(Sections, Succeeded()); + std::unique_ptr Ctx = + DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true); + DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0); + ASSERT_NE(nullptr, CU); + DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false); + ASSERT_TRUE(Die.isValid()); + + DWARFDie MainDie = Die.getFirstChild(); + ASSERT_TRUE(MainDie.isValid()); + + std::string DeclFile = MainDie.getDeclFile( + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath); + + EXPECT_EQ(DeclFile, "/tmp/main.cpp"); +} + +TEST(DWARFDie, getDeclFileSpecification) { + const char *yamldata = R"( + debug_str: + - '' + - '/tmp/main.cpp' + - main + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_language + Form: DW_FORM_udata + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + - Code: 0x2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Attribute: DW_AT_specification + Form: DW_FORM_ref4 + - Code: 0x3 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + debug_info: + - Length: 0x2E + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x1 + - Value: 0x2 + - Value: 0x0 + - AbbrCode: 0x2 + Values: + - Value: 0x1000 + - Value: 0x1050 + - Value: 0x2A + - AbbrCode: 0x3 + Values: + - Value: 0xF + - Value: 0x1 + - Value: 0x14 + - AbbrCode: 0x0 + debug_line: + - Length: 80 + Version: 2 + PrologueLength: 36 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - '/tmp' + Files: + - Name: main.cpp + DirIdx: 1 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 4096 + - Opcode: DW_LNS_advance_line + SData: 9 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_advance_line + SData: 1 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_advance_line + SData: 1 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 18446744073709548538 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 0 + )"; + + // Given DWARF like this: + // + // 0x0000000b: DW_TAG_compile_unit + // DW_AT_name ("/tmp/main.cpp") + // DW_AT_language (DW_LANG_C) + // DW_AT_stmt_list (0x00000000) + // + // 0x00000015: DW_TAG_subprogram + // DW_AT_low_pc (0x0000000000001000) + // DW_AT_high_pc (0x0000000000001050) + // DW_AT_specification (0x0000002a "main") + // + // 0x0000002a: DW_TAG_subprogram + // DW_AT_name ("main") + // DW_AT_decl_file ("/tmp/main.cpp") + // DW_AT_decl_line (20) + // + // 0x00000031: NULL + + // + // The DIE at 0x00000015 uses a DW_AT_specification to point to the DIE at + // 0x0000002a, make sure that DWARFDie::getDeclFile() succeeds by extracting + // the right file name of "/tmp/main.cpp". + // + // This tests that when we have a DW_AT_specification with a compile unit + // relative form (DW_FORM_ref4) to another DIE that we get the right + // DW_AT_decl_file value. + + Expected>> Sections = + DWARFYAML::emitDebugSections(StringRef(yamldata), + /*IsLittleEndian=*/true, + /*Is64BitAddrSize=*/true); + ASSERT_THAT_EXPECTED(Sections, Succeeded()); + std::unique_ptr Ctx = + DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true); + DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0); + ASSERT_NE(nullptr, CU); + DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false); + ASSERT_TRUE(Die.isValid()); + + DWARFDie MainDie = Die.getFirstChild(); + ASSERT_TRUE(MainDie.isValid()); + + std::string DeclFile = MainDie.getDeclFile( + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath); + + EXPECT_EQ(DeclFile, "/tmp/main.cpp"); +} + +TEST(DWARFDie, getDeclFileAbstractOriginAcrossCUBoundary) { + const char *yamldata = R"( + debug_str: + - '' + - '/tmp/foo.cpp' + - '/tmp/main.cpp' + - main + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_language + Form: DW_FORM_udata + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + - Code: 0x2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_abstract_origin + Form: DW_FORM_ref_addr + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Code: 0x3 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + debug_info: + - Length: 0x27 + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x1 + - Value: 0x2 + - Value: 0x0 + - AbbrCode: 0x2 + Values: + - Value: 0x40 + - Value: 0x1000 + - Value: 0x1050 + - AbbrCode: 0x0 + - Length: 0x19 + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0xE + - Value: 0x2 + - Value: 0x53 + - AbbrCode: 0x3 + Values: + - Value: 0x1C + - Value: 0x1 + - Value: 0x14 + - AbbrCode: 0x0 + debug_line: + - Length: 79 + Version: 2 + PrologueLength: 35 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - '/tmp' + Files: + - Name: foo.cpp + DirIdx: 1 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 4096 + - Opcode: DW_LNS_advance_line + SData: 9 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_advance_line + SData: 1 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_advance_line + SData: 1 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 18446744073709548538 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 0 + - Length: 56 + Version: 2 + PrologueLength: 36 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - '/tmp' + Files: + - Name: main.cpp + DirIdx: 1 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 0 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 0 + )"; + + // Given DWARF like this: + // + // 0x0000000b: DW_TAG_compile_unit + // DW_AT_name ("/tmp/foo.cpp") + // DW_AT_language (DW_LANG_C) + // DW_AT_stmt_list (0x00000000) + // + // 0x00000015: DW_TAG_subprogram + // DW_AT_abstract_origin (0x0000000000000040 "main") + // DW_AT_low_pc (0x0000000000001000) + // DW_AT_high_pc (0x0000000000001050) + // + // 0x0000002a: NULL + // + // 0x00000036: DW_TAG_compile_unit + // DW_AT_name ("/tmp/main.cpp") + // DW_AT_language (DW_LANG_C) + // DW_AT_stmt_list (0x00000053) + // + // 0x00000040: DW_TAG_subprogram + // DW_AT_name ("main") + // DW_AT_decl_file ("/tmp/main.cpp") + // DW_AT_decl_line (20) + // + // 0x00000047: NULL + // + // This tests that when we have a DW_AT_abstract_origin with a + // DW_FORM_ref_addr to another DIE in another compile unit that we use the + // right file table when converting the file index of the DW_AT_decl_file. + // + // The DIE at 0x00000015 uses a DW_AT_abstract_origin to point to the DIE at + // 0x00000040, make sure that DWARFDie::getDeclFile() succeeds by extracting + // the right file name of "/tmp/main.cpp". The DW_AT_decl_file must grab the + // file from the line table prologue of the compile unit at offset + // 0x00000036. + + Expected>> Sections = + DWARFYAML::emitDebugSections(StringRef(yamldata), + /*IsLittleEndian=*/true, + /*Is64BitAddrSize=*/true); + ASSERT_THAT_EXPECTED(Sections, Succeeded()); + std::unique_ptr Ctx = + DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true); + DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0); + ASSERT_NE(nullptr, CU); + DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false); + ASSERT_TRUE(Die.isValid()); + + DWARFDie MainDie = Die.getFirstChild(); + ASSERT_TRUE(MainDie.isValid()); + + std::string DeclFile = MainDie.getDeclFile( + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath); + + EXPECT_EQ(DeclFile, "/tmp/main.cpp"); +} + +TEST(DWARFDie, getDeclFileSpecificationAcrossCUBoundary) { + const char *yamldata = R"( + debug_str: + - '' + - '/tmp/foo.cpp' + - '/tmp/main.cpp' + - main + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_language + Form: DW_FORM_udata + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + - Code: 0x2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_specification + Form: DW_FORM_ref_addr + - Attribute: DW_AT_low_pc + Form: DW_FORM_addr + - Attribute: DW_AT_high_pc + Form: DW_FORM_addr + - Code: 0x3 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_name + Form: DW_FORM_strp + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + - Attribute: DW_AT_decl_line + Form: DW_FORM_data1 + debug_info: + - Length: 0x27 + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x1 + - Value: 0x2 + - Value: 0x0 + - AbbrCode: 0x2 + Values: + - Value: 0x40 + - Value: 0x1000 + - Value: 0x1050 + - AbbrCode: 0x0 + - Length: 0x19 + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0xE + - Value: 0x2 + - Value: 0x53 + - AbbrCode: 0x3 + Values: + - Value: 0x1C + - Value: 0x1 + - Value: 0x14 + - AbbrCode: 0x0 + debug_line: + - Length: 79 + Version: 2 + PrologueLength: 35 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - '/tmp' + Files: + - Name: foo.cpp + DirIdx: 1 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 4096 + - Opcode: DW_LNS_advance_line + SData: 9 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_advance_line + SData: 1 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 16 + - Opcode: DW_LNS_advance_line + SData: 1 + Data: 0 + - Opcode: DW_LNS_copy + Data: 0 + - Opcode: DW_LNS_advance_pc + Data: 18446744073709548538 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 0 + - Length: 56 + Version: 2 + PrologueLength: 36 + MinInstLength: 1 + DefaultIsStmt: 1 + LineBase: 251 + LineRange: 14 + OpcodeBase: 13 + StandardOpcodeLengths: [ 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 ] + IncludeDirs: + - '/tmp' + Files: + - Name: main.cpp + DirIdx: 1 + ModTime: 0 + Length: 0 + Opcodes: + - Opcode: DW_LNS_extended_op + ExtLen: 9 + SubOpcode: DW_LNE_set_address + Data: 0 + - Opcode: DW_LNS_extended_op + ExtLen: 1 + SubOpcode: DW_LNE_end_sequence + Data: 0 + )"; + + // Given DWARF like this: + // + // 0x0000000b: DW_TAG_compile_unit + // DW_AT_name ("/tmp/foo.cpp") + // DW_AT_language (DW_LANG_C) + // DW_AT_stmt_list (0x00000000) + // + // 0x00000015: DW_TAG_subprogram + // DW_AT_specification (0x0000000000000040 "main") + // DW_AT_low_pc (0x0000000000001000) + // DW_AT_high_pc (0x0000000000001050) + // + // 0x0000002a: NULL + // + // 0x00000036: DW_TAG_compile_unit + // DW_AT_name ("/tmp/main.cpp") + // DW_AT_language (DW_LANG_C) + // DW_AT_stmt_list (0x00000053) + // + // 0x00000040: DW_TAG_subprogram + // DW_AT_name ("main") + // DW_AT_decl_file ("/tmp/main.cpp") + // DW_AT_decl_line (20) + // + // 0x00000047: NULL + // + // This tests that when we have a DW_AT_specification with a + // DW_FORM_ref_addr to another DIE in another compile unit that we use the + // right file table when converting the file index of the DW_AT_decl_file. + // + // The DIE at 0x00000015 uses a DW_AT_specification to point to the DIE at + // 0x00000040, make sure that DWARFDie::getDeclFile() succeeds by extracting + // the right file name of "/tmp/main.cpp". The DW_AT_decl_file must grab the + // file from the line table prologue of the compile unit at offset + // 0x00000036. + + Expected>> Sections = + DWARFYAML::emitDebugSections(StringRef(yamldata), + /*IsLittleEndian=*/true, + /*Is64BitAddrSize=*/true); + ASSERT_THAT_EXPECTED(Sections, Succeeded()); + std::unique_ptr Ctx = + DWARFContext::create(*Sections, 4, /*isLittleEndian=*/true); + DWARFCompileUnit *CU = Ctx->getCompileUnitForOffset(0); + ASSERT_NE(nullptr, CU); + DWARFDie Die = CU->getUnitDIE(/*ExtractUnitDIEOnly=*/false); + ASSERT_TRUE(Die.isValid()); + + DWARFDie MainDie = Die.getFirstChild(); + ASSERT_TRUE(MainDie.isValid()); + + std::string DeclFile = MainDie.getDeclFile( + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath); + + EXPECT_EQ(DeclFile, "/tmp/main.cpp"); +} + } // end anonymous namespace