diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFAddressRange.h @@ -49,11 +49,16 @@ const DWARFObject *Obj = nullptr) const; }; -static inline bool operator<(const DWARFAddressRange &LHS, - const DWARFAddressRange &RHS) { +inline bool operator<(const DWARFAddressRange &LHS, + const DWARFAddressRange &RHS) { return std::tie(LHS.LowPC, LHS.HighPC) < std::tie(RHS.LowPC, RHS.HighPC); } +inline bool operator==(const DWARFAddressRange &LHS, + const DWARFAddressRange &RHS) { + return std::tie(LHS.LowPC, LHS.HighPC) == std::tie(RHS.LowPC, RHS.HighPC); +} + raw_ostream &operator<<(raw_ostream &OS, const DWARFAddressRange &R); /// DWARFAddressRangesVector - represents a set of absolute address ranges. diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugLoc.h @@ -63,6 +63,11 @@ const MCRegisterInfo *MRI, DWARFUnit *U, DIDumpOptions DumpOpts, unsigned Indent) const; + Error visitAbsoluteLocationList( + uint64_t Offset, Optional BaseAddr, + std::function(uint32_t)> LookupAddr, + function_ref)> Callback) const; + protected: DWARFDataExtractor Data; diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDie.h @@ -18,6 +18,7 @@ #include "llvm/DebugInfo/DWARF/DWARFAddressRange.h" #include "llvm/DebugInfo/DWARF/DWARFAttribute.h" #include "llvm/DebugInfo/DWARF/DWARFDebugInfoEntry.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" #include #include #include @@ -231,6 +232,9 @@ bool addressRangeContainsAddress(const uint64_t Address) const; + Expected> + getLocations(dwarf::Attribute Attr) const; + /// If a DIE represents a subprogram (or inlined subroutine), returns its /// mangled name (or short name, if mangled is missing). This name may be /// fetched from specification or abstract origin for this subprogram. diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFLocationExpression.h @@ -14,6 +14,8 @@ namespace llvm { +class raw_ostream; + /// Represents a single DWARF expression, whose value is location-dependent. /// Typically used in DW_AT_location attributes to describe the location of /// objects. @@ -27,6 +29,13 @@ SmallVector Expr; }; +inline bool operator==(const DWARFLocationExpression &L, + const DWARFLocationExpression &R) { + return L.Range == R.Range && L.Expr == R.Expr; +} + +raw_ostream &operator<<(raw_ostream &OS, const DWARFLocationExpression &Loc); + } // end namespace llvm #endif // LLVM_DEBUGINFO_DWARF_DWARFLOCATIONEXPRESSION_H diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFUnit.h @@ -439,6 +439,9 @@ } Expected collectAddressRanges(); + Expected> + findLoclistFromOffset(uint64_t Offset); + /// Returns subprogram DIE with address range encompassing the provided /// address. The pointer is alive as long as parsed compile unit DIEs are not /// cleared. diff --git a/llvm/include/llvm/Object/ObjectFile.h b/llvm/include/llvm/Object/ObjectFile.h --- a/llvm/include/llvm/Object/ObjectFile.h +++ b/llvm/include/llvm/Object/ObjectFile.h @@ -155,6 +155,8 @@ std::tie(RHS.SectionIndex, RHS.Address); } +raw_ostream &operator<<(raw_ostream &OS, const SectionedAddress &Addr); + /// This is a value type class that represents a single symbol in the list of /// symbols in the object file. class SymbolRef : public BasicSymbolRef { diff --git a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt --- a/llvm/lib/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/lib/DebugInfo/DWARF/CMakeLists.txt @@ -22,6 +22,7 @@ DWARFFormValue.cpp DWARFGdbIndex.cpp DWARFListTable.cpp + DWARFLocationExpression.cpp DWARFTypeUnit.cpp DWARFUnitIndex.cpp DWARFUnit.cpp diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -40,6 +40,21 @@ }; } // namespace +Error DWARFLocationTable::visitAbsoluteLocationList( + uint64_t Offset, Optional BaseAddr, + std::function(uint32_t)> LookupAddr, + function_ref)> Callback) const { + DWARFLocationInterpreter Interp(BaseAddr, std::move(LookupAddr)); + return visitLocationList(&Offset, [&](const DWARFLocationEntry &E) { + Expected> Loc = Interp.Interpret(E); + if (!Loc) + return Callback(Loc.takeError()); + if (*Loc) + return Callback(**Loc); + return true; + }); +} + static Error createResolverError(uint32_t Index, unsigned Kind) { return createStringError(errc::invalid_argument, "Unable to resolve indirect address %u for: %s", 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 @@ -486,6 +486,27 @@ return false; } +Expected> +DWARFDie::getLocations(dwarf::Attribute Attr) const { + Optional Location = find(Attr); + if (!Location) + return createStringError(inconvertibleErrorCode(), "No %s", + dwarf::AttributeString(Attr).data()); + + if (Optional Off = Location->getAsSectionOffset()) + return U->findLoclistFromOffset(*Off); + + if (Optional> Expr = Location->getAsBlock()) { + return std::vector{ + DWARFLocationExpression{None, to_vector<4>(*Expr)}}; + } + + return createStringError( + inconvertibleErrorCode(), "Unsupported %s encoding: %s", + dwarf::AttributeString(Attr).data(), + dwarf::FormEncodingString(Location->getForm()).data()); +} + const char *DWARFDie::getSubroutineName(DINameKind Kind) const { if (!isSubroutineDIE()) return nullptr; diff --git a/llvm/lib/DebugInfo/DWARF/DWARFLocationExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFLocationExpression.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/DebugInfo/DWARF/DWARFLocationExpression.cpp @@ -0,0 +1,19 @@ +//===- DWARFLocationExpression.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/DebugInfo/DWARF/DWARFLocationExpression.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; + +raw_ostream &llvm::operator<<(raw_ostream &OS, + const DWARFLocationExpression &Loc) { + return OS << Loc.Range << ": " + << formatv("{0}", make_range(Loc.Expr.begin(), Loc.Expr.end())); +} diff --git a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp --- a/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFUnit.cpp @@ -637,6 +637,30 @@ return *CUDIERangesOrError; } +Expected> +DWARFUnit::findLoclistFromOffset(uint64_t Offset) { + std::vector Result; + + Error InterpretationError = Error::success(); + + Error ParseError = getLocationTable().visitAbsoluteLocationList( + Offset, getBaseAddress(), + [this](uint32_t Index) { return getAddrOffsetSectionItem(Index); }, + [&](Expected L) { + if (L) + Result.push_back(std::move(*L)); + else + InterpretationError = + joinErrors(L.takeError(), std::move(InterpretationError)); + return !InterpretationError; + }); + + if (ParseError || InterpretationError) + return joinErrors(std::move(ParseError), std::move(InterpretationError)); + + return Result; +} + void DWARFUnit::updateAddressDieMap(DWARFDie Die) { if (Die.isSubroutineDIE()) { auto DIERangesOrError = Die.getAddressRanges(); diff --git a/llvm/lib/Object/ObjectFile.cpp b/llvm/lib/Object/ObjectFile.cpp --- a/llvm/lib/Object/ObjectFile.cpp +++ b/llvm/lib/Object/ObjectFile.cpp @@ -32,6 +32,13 @@ using namespace llvm; using namespace object; +raw_ostream &object::operator<<(raw_ostream &OS, const SectionedAddress &Addr) { + OS << "SectionedAddress{" << format_hex(Addr.Address, 10); + if (Addr.SectionIndex != SectionedAddress::UndefSection) + OS << ", " << Addr.SectionIndex; + return OS << "}"; +} + void ObjectFile::anchor() {} ObjectFile::ObjectFile(unsigned int Type, MemoryBufferRef Source) diff --git a/llvm/lib/ObjectYAML/DWARFEmitter.cpp b/llvm/lib/ObjectYAML/DWARFEmitter.cpp --- a/llvm/lib/ObjectYAML/DWARFEmitter.cpp +++ b/llvm/lib/ObjectYAML/DWARFEmitter.cpp @@ -314,7 +314,9 @@ DIEFixupVisitor(DWARFYAML::Data &DI) : DWARFYAML::Visitor(DI){}; private: - virtual void onStartCompileUnit(DWARFYAML::Unit &CU) { Length = 7; } + virtual void onStartCompileUnit(DWARFYAML::Unit &CU) { + Length = CU.Version >= 5 ? 8 : 7; + } virtual void onEndCompileUnit(DWARFYAML::Unit &CU) { CU.Length.setLength(Length); diff --git a/llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test b/llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test --- a/llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test +++ b/llvm/test/tools/llvm-dwarfdump/X86/statistics-dwo.test @@ -80,8 +80,8 @@ # Ideally the value below would be 33 but currently it's not. CHECK: "variables with location":22 CHECK: "call site entries":7 -CHECK: "scope bytes total":2817 -CHECK: "scope bytes covered":506 +CHECK: "scope bytes total":2702 +CHECK: "scope bytes covered":1160 CHECK: "total function size":594 CHECK: "total inlined function size":345 CHECK: "total formal params":12 diff --git a/llvm/tools/llvm-dwarfdump/Statistics.cpp b/llvm/tools/llvm-dwarfdump/Statistics.cpp --- a/llvm/tools/llvm-dwarfdump/Statistics.cpp +++ b/llvm/tools/llvm-dwarfdump/Statistics.cpp @@ -243,43 +243,35 @@ return; } // Handle variables and function arguments. - auto FormValue = Die.find(dwarf::DW_AT_location); - HasLoc = FormValue.hasValue(); - if (HasLoc) { + if (Expected> Loc = + Die.getLocations(dwarf::DW_AT_location)) { + HasLoc = true; // Get PC coverage. - if (auto DebugLocOffset = FormValue->getAsSectionOffset()) { - auto *DebugLoc = Die.getDwarfUnit()->getContext().getDebugLoc(); - // TODO: This code does not handle DWARF5 nor DWARF4 base address - // selection entries. This should use a higher-level API which abstracts - // these away. - if (auto List = DebugLoc->getLocationListAtOffset(*DebugLocOffset)) { - ArrayRef Entries = List->Entries; - // Ignore end-of-list entries - Entries = Entries.drop_back(); - - for (auto Entry : Entries) { - uint64_t BytesEntryCovered = Entry.Value1 - Entry.Value0; - BytesCovered += BytesEntryCovered; - if (IsEntryValue(Entry.Loc)) - BytesEntryValuesCovered += BytesEntryCovered; - } - if (Entries.size()) { - uint64_t FirstDef = Entries[0].Value0; - uint64_t UnitOfs = UnitLowPC; - // Ranges sometimes start before the lexical scope. - if (UnitOfs + FirstDef >= ScopeLowPC) - OffsetToFirstDefinition = UnitOfs + FirstDef - ScopeLowPC; - // Or even after it. Count that as a failure. - if (OffsetToFirstDefinition > BytesInScope) - OffsetToFirstDefinition = 0; - } - } - assert(BytesInScope); - } else { + auto Default = find_if( + *Loc, [](const DWARFLocationExpression &L) { return !L.Range; }); + if (Default != Loc->end()) { // Assume the entire range is covered by a single location. BytesCovered = BytesInScope; + } else { + for (auto Entry : *Loc) { + uint64_t BytesEntryCovered = Entry.Range->HighPC - Entry.Range->LowPC; + BytesCovered += BytesEntryCovered; + if (IsEntryValue(Entry.Expr)) + BytesEntryValuesCovered += BytesEntryCovered; + } + if (!Loc->empty()) { + uint64_t FirstDef = Loc->front().Range->LowPC; + // Ranges sometimes start before the lexical scope. + if (FirstDef >= ScopeLowPC) + OffsetToFirstDefinition = FirstDef - ScopeLowPC; + // Or even after it. Count that as a failure. + if (OffsetToFirstDefinition > BytesInScope) + OffsetToFirstDefinition = 0; + } + assert(BytesInScope); } - } + } else + consumeError(Loc.takeError()); } // Calculate the debug location statistics. diff --git a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt --- a/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/DWARF/CMakeLists.txt @@ -13,6 +13,7 @@ DwarfUtils.cpp DWARFDebugInfoTest.cpp DWARFDebugLineTest.cpp + DWARFDieTest.cpp DWARFFormValueTest.cpp ) diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDieTest.cpp @@ -0,0 +1,103 @@ +//===- 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/DWARFContext.h" +#include "llvm/ObjectYAML/DWARFEmitter.h" +#include "llvm/Testing/Support/Error.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::dwarf; +using object::SectionedAddress; + +namespace { + +TEST(DWARFLocationTable, getLocations) { + const char *yamldata = R"( + debug_abbrev: + - 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: + - Length: + TotalLength: 0 + Version: 5 + UnitType: DW_UT_compile + AbbrOffset: 0 + AddrSize: 4 + Entries: + - AbbrCode: 0x00000001 + Values: + - Value: 12 + - Value: 0x0000000000000001 + BlockData: [ 0x47 ] + - Value: 20 + - Value: 25 + )"; + Expected>> Sections = + DWARFYAML::EmitDebugSections(StringRef(yamldata), + /*IsLittleEndian=*/true); + ASSERT_THAT_EXPECTED(Sections, Succeeded()); + std::vector Loclists{ + 0, 0, 0, 0, 5, 0, 4, 0, 0, 0, 0, 0, // Header + // First location list. + DW_LLE_start_length, 1, 0, 0, 0, 2, 0, // First entry. + DW_LLE_end_of_list, + // Second location list. + DW_LLE_startx_length, 1, 2, 0, // First entry. + DW_LLE_end_of_list, + // Third location list. + DW_LLE_start_length, 1, 0, 0, 0, 2, 0, // First entry. + // No end of list. + }; + Loclists[0] = Loclists.size() - 4; + Sections->try_emplace( + "debug_loclists", + MemoryBuffer::getMemBuffer(toStringRef(Loclists), "debug_loclists", + /*RequiresNullTerminator=*/false)); + std::unique_ptr Ctx = DWARFContext::create(*Sections, 8); + 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{None, {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), + Failed(testing::Property( + &ErrorInfoBase::message, "unexpected end of data"))); + + EXPECT_THAT_EXPECTED( + Die.getLocations(DW_AT_call_data_value), + Failed(testing::Property(&ErrorInfoBase::message, + "No DW_AT_call_data_value"))); +} + +} // end anonymous namespace