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,557 @@ "No DW_AT_call_data_value"))); } +TEST(DWARFDie, getDeclFile) { + const char *yamldata = R"( + debug_str: + - '' + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + - Code: 0x2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + debug_info: + - Length: 0xF + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x0 + - AbbrCode: 0x2 + Values: + - Value: 0x1 + - AbbrCode: 0x0 + debug_line: + - Length: 42 + 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 + )"; + + // Given DWARF like this: + // + // 0x0000000b: DW_TAG_compile_unit + // DW_AT_stmt_list (0x00000000) + // + // 0x00000010: DW_TAG_subprogram + // DW_AT_decl_file ("/tmp/main.cpp") + // + // 0x00000012: 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); + +#if defined(_WIN32) + EXPECT_EQ(DeclFile, "/tmp\\main.cpp"); +#else + EXPECT_EQ(DeclFile, "/tmp/main.cpp"); +#endif +} + +TEST(DWARFDie, getDeclFileAbstractOrigin) { + const char *yamldata = R"( + debug_str: + - '' + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - 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 + - Code: 0x3 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + debug_info: + - Length: 0x14 + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x0 + - AbbrCode: 0x2 + Values: + - Value: 0x15 + - AbbrCode: 0x3 + Values: + - Value: 0x1 + - AbbrCode: 0x0 + debug_line: + - Length: 42 + 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 + )"; + + // Given DWARF like this: + // + // 0x0000000b: DW_TAG_compile_unit + // DW_AT_stmt_list (0x00000000) + // + // 0x00000010: DW_TAG_subprogram + // DW_AT_abstract_origin (0x0000000000000015) + // + // 0x00000015: DW_TAG_subprogram + // DW_AT_decl_file ("/tmp/main.cpp") + // + // 0x00000017: NULL + // + // + // The DIE at 0x00000010 uses a DW_AT_abstract_origin to point to the DIE at + // 0x00000015, 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); + +#if defined(_WIN32) + EXPECT_EQ(DeclFile, "/tmp\\main.cpp"); +#else + EXPECT_EQ(DeclFile, "/tmp/main.cpp"); +#endif +} + +TEST(DWARFDie, getDeclFileSpecification) { + const char *yamldata = R"( + debug_str: + - '' + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - 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 + - Code: 0x3 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + debug_info: + - Length: 0x14 + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + Values: + - Value: 0x0 + - AbbrCode: 0x2 + Values: + - Value: 0x15 + - AbbrCode: 0x3 + Values: + - Value: 0x1 + - AbbrCode: 0x0 + debug_line: + - Length: 42 + 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 + )"; + + // Given DWARF like this: + // + // 0x0000000b: DW_TAG_compile_unit + // DW_AT_stmt_list (0x00000000) + // + // 0x00000010: DW_TAG_subprogram + // DW_AT_specification (0x0000000000000015) + // + // 0x00000015: DW_TAG_subprogram + // DW_AT_decl_file ("/tmp/main.cpp") + // + // 0x00000017: NULL + // + // The DIE at 0x00000010 uses a DW_AT_specification to point to the DIE at + // 0x00000015, 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); + +#if defined(_WIN32) + EXPECT_EQ(DeclFile, "/tmp\\main.cpp"); +#else + EXPECT_EQ(DeclFile, "/tmp/main.cpp"); +#endif +} + +TEST(DWARFDie, getDeclFileAbstractOriginAcrossCUBoundary) { + const char *yamldata = R"( + debug_str: + - '' + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + - Code: 0x2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_abstract_origin + Form: DW_FORM_ref_addr + - Code: 0x3 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + - Code: 0x4 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + debug_info: + - Length: 0xE + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + - AbbrCode: 0x2 + Values: + - Value: 0x22 + - AbbrCode: 0x0 + - Length: 0xF + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x3 + Values: + - Value: 0x0 + - AbbrCode: 0x4 + Values: + - Value: 0x1 + - AbbrCode: 0x0 + debug_line: + - Length: 42 + 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 + )"; + + // Given DWARF like this: + // + // 0x0000000b: DW_TAG_compile_unit + // + // 0x0000000c: DW_TAG_subprogram + // DW_AT_abstract_origin (0x0000000000000022) + // + // 0x00000011: NULL + // + // 0x0000001d: DW_TAG_compile_unit + // DW_AT_stmt_list (0x00000000) + // + // 0x00000022: DW_TAG_subprogram + // DW_AT_decl_file ("/tmp/main.cpp") + // + // 0x00000024: 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 0x0000000c uses a DW_AT_abstract_origin to point to the DIE at + // 0x00000022, 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 + // 0x0000001d. + + 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); + +#if defined(_WIN32) + EXPECT_EQ(DeclFile, "/tmp\\main.cpp"); +#else + EXPECT_EQ(DeclFile, "/tmp/main.cpp"); +#endif +} + +TEST(DWARFDie, getDeclFileSpecificationAcrossCUBoundary) { + const char *yamldata = R"( + debug_str: + - '' + debug_abbrev: + - ID: 0 + Table: + - Code: 0x1 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + - Code: 0x2 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_specification + Form: DW_FORM_ref_addr + - Code: 0x3 + Tag: DW_TAG_compile_unit + Children: DW_CHILDREN_yes + Attributes: + - Attribute: DW_AT_stmt_list + Form: DW_FORM_sec_offset + - Code: 0x4 + Tag: DW_TAG_subprogram + Children: DW_CHILDREN_no + Attributes: + - Attribute: DW_AT_decl_file + Form: DW_FORM_data1 + debug_info: + - Length: 0xE + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x1 + - AbbrCode: 0x2 + Values: + - Value: 0x22 + - AbbrCode: 0x0 + - Length: 0xF + Version: 4 + AbbrevTableID: 0 + AbbrOffset: 0x0 + AddrSize: 8 + Entries: + - AbbrCode: 0x3 + Values: + - Value: 0x0 + - AbbrCode: 0x4 + Values: + - Value: 0x1 + - AbbrCode: 0x0 + debug_line: + - Length: 42 + 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 + )"; + + // Given DWARF like this: + // + // 0x0000000b: DW_TAG_compile_unit + // + // 0x0000000c: DW_TAG_subprogram + // DW_AT_specification (0x0000000000000022) + // + // 0x00000011: NULL + // + // 0x0000001d: DW_TAG_compile_unit + // DW_AT_stmt_list (0x00000000) + // + // 0x00000022: DW_TAG_subprogram + // DW_AT_decl_file ("/tmp/main.cpp") + // + // 0x00000024: 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 0x0000000c uses a DW_AT_specification to point to the DIE at + // 0x00000022, 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 + // 0x0000001d. + + 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); + +#if defined(_WIN32) + EXPECT_EQ(DeclFile, "/tmp\\main.cpp"); +#else + EXPECT_EQ(DeclFile, "/tmp/main.cpp"); +#endif +} + } // end anonymous namespace