//===- llvm/unittest/DebugInfo/DWARFDieTest.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 "llvm/BinaryFormat/Dwarf.h" #include "llvm/DebugInfo/DWARF/DWARFCompileUnit.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/ObjectYAML/DWARFEmitter.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace llvm; using namespace llvm::dwarf; namespace { TEST(DWARFDie, getLocations) { const char *yamldata = R"( debug_abbrev: - Table: - Code: 0x00000001 Tag: DW_TAG_compile_unit Children: DW_CHILDREN_no Attributes: - Attribute: DW_AT_location Form: DW_FORM_sec_offset - Attribute: DW_AT_data_member_location Form: DW_FORM_exprloc - Attribute: DW_AT_vtable_elem_location Form: DW_FORM_sec_offset - Attribute: DW_AT_call_data_location Form: DW_FORM_sec_offset debug_info: - Version: 5 UnitType: DW_UT_compile AddrSize: 4 Entries: - AbbrCode: 0x00000001 Values: - Value: 12 - Value: 0x0000000000000001 BlockData: [ 0x47 ] - Value: 20 - Value: 25 debug_loclists: - AddressSize: 4 OffsetEntryCount: 0 Lists: - Entries: - Operator: DW_LLE_start_length Values: [ 0x01, 0x02 ] - Operator: DW_LLE_end_of_list - Entries: - Operator: DW_LLE_startx_length Values: [ 0x01, 0x02 ] - Operator: DW_LLE_end_of_list - Entries: - Operator: DW_LLE_start_length Values: [ 0x01, 0x02 ] ## end_of_list intentionally missing. )"; Expected>> Sections = DWARFYAML::emitDebugSections(StringRef(yamldata), /*IsLittleEndian=*/true, /*Is64BitAddrSize=*/false); 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(); ASSERT_TRUE(Die.isValid()); EXPECT_THAT_EXPECTED(Die.getLocations(DW_AT_location), HasValue(testing::ElementsAre(DWARFLocationExpression{ DWARFAddressRange{1, 3}, {}}))); EXPECT_THAT_EXPECTED(Die.getLocations(DW_AT_data_member_location), HasValue(testing::ElementsAre( DWARFLocationExpression{std::nullopt, {0x47}}))); EXPECT_THAT_EXPECTED( Die.getLocations(DW_AT_vtable_elem_location), Failed(testing::Property( &ErrorInfoBase::message, "unable to resolve indirect address 1 for: DW_LLE_startx_length"))); EXPECT_THAT_EXPECTED( Die.getLocations(DW_AT_call_data_location), FailedWithMessage( "unexpected end of data at offset 0x20 while reading [0x20, 0x21)")); EXPECT_THAT_EXPECTED( Die.getLocations(DW_AT_call_data_value), Failed(testing::Property(&ErrorInfoBase::message, "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); std::string Ref = ("/tmp" + llvm::sys::path::get_separator() + "main.cpp").str(); EXPECT_EQ(DeclFile, Ref); } 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); std::string Ref = ("/tmp" + llvm::sys::path::get_separator() + "main.cpp").str(); EXPECT_EQ(DeclFile, Ref); } 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); std::string Ref = ("/tmp" + llvm::sys::path::get_separator() + "main.cpp").str(); EXPECT_EQ(DeclFile, Ref); } 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); std::string Ref = ("/tmp" + llvm::sys::path::get_separator() + "main.cpp").str(); EXPECT_EQ(DeclFile, Ref); } 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); std::string Ref = ("/tmp" + llvm::sys::path::get_separator() + "main.cpp").str(); EXPECT_EQ(DeclFile, Ref); } } // end anonymous namespace