Add support for fetching inlining context (stack of source code locations)

by instruction address from DWARF.

Add --inlining flag to llvm-dwarfdump to demonstrate and test this functionality,
so that "llvm-dwarfdump --inlining --address=0x..." now works much like
"addr2line -i 0x...", provided that the binary has debug info
(Clang's -gline-tables-only *is* enough).


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@163128 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Alexey Samsonov 2012-09-04 08:12:33 +00:00
parent 2d5c28da0d
commit 5eae90d727
12 changed files with 359 additions and 88 deletions

View File

@ -15,6 +15,7 @@
#ifndef LLVM_DEBUGINFO_DICONTEXT_H #ifndef LLVM_DEBUGINFO_DICONTEXT_H
#define LLVM_DEBUGINFO_DICONTEXT_H #define LLVM_DEBUGINFO_DICONTEXT_H
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include "llvm/Support/DataTypes.h" #include "llvm/Support/DataTypes.h"
@ -54,6 +55,23 @@ public:
} }
}; };
/// DIInliningInfo - a format-neutral container for inlined code description.
class DIInliningInfo {
SmallVector<DILineInfo, 4> Frames;
public:
DIInliningInfo() {}
DILineInfo getFrame(unsigned Index) const {
assert(Index < Frames.size());
return Frames[Index];
}
uint32_t getNumberOfFrames() const {
return Frames.size();
}
void addFrame(const DILineInfo &Frame) {
Frames.push_back(Frame);
}
};
/// DILineInfoSpecifier - controls which fields of DILineInfo container /// DILineInfoSpecifier - controls which fields of DILineInfo container
/// should be filled with data. /// should be filled with data.
class DILineInfoSpecifier { class DILineInfoSpecifier {
@ -86,8 +104,10 @@ public:
virtual void dump(raw_ostream &OS) = 0; virtual void dump(raw_ostream &OS) = 0;
virtual DILineInfo getLineInfoForAddress(uint64_t address, virtual DILineInfo getLineInfoForAddress(uint64_t Address,
DILineInfoSpecifier specifier = DILineInfoSpecifier()) = 0; DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0;
virtual DIInliningInfo getInliningInfoForAddress(uint64_t Address,
DILineInfoSpecifier Specifier = DILineInfoSpecifier()) = 0;
}; };
} }

View File

@ -75,6 +75,15 @@ DWARFCompileUnit::extract(uint32_t offset, DataExtractor debug_info_data,
return 0; return 0;
} }
bool DWARFCompileUnit::extractRangeList(uint32_t RangeListOffset,
DWARFDebugRangeList &RangeList) const {
// Require that compile unit is extracted.
assert(DieArray.size() > 0);
DataExtractor RangesData(Context.getRangeSection(),
Context.isLittleEndian(), AddrSize);
return RangeList.extract(RangesData, &RangeListOffset);
}
void DWARFCompileUnit::clear() { void DWARFCompileUnit::clear() {
Offset = 0; Offset = 0;
Length = 0; Length = 0;
@ -246,12 +255,21 @@ DWARFCompileUnit::buildAddressRangeTable(DWARFDebugAranges *debug_aranges,
clearDIEs(true); clearDIEs(true);
} }
const DWARFDebugInfoEntryMinimal* DWARFDebugInfoEntryMinimal::InlinedChain
DWARFCompileUnit::getFunctionDIEForAddress(int64_t address) { DWARFCompileUnit::getInlinedChainForAddress(uint64_t Address) {
// First, find a subprogram that contains the given address (the root
// of inlined chain).
extractDIEsIfNeeded(false); extractDIEsIfNeeded(false);
const DWARFDebugInfoEntryMinimal *SubprogramDIE = 0;
for (size_t i = 0, n = DieArray.size(); i != n; i++) { for (size_t i = 0, n = DieArray.size(); i != n; i++) {
if (DieArray[i].addressRangeContainsAddress(this, address)) if (DieArray[i].isSubprogramDIE() &&
return &DieArray[i]; DieArray[i].addressRangeContainsAddress(this, Address)) {
SubprogramDIE = &DieArray[i];
break;
} }
return 0; }
// Get inlined chain rooted at this subprogram DIE.
if (!SubprogramDIE)
return DWARFDebugInfoEntryMinimal::InlinedChain();
return SubprogramDIE->getInlinedChainForAddress(this, Address);
} }

View File

@ -12,6 +12,7 @@
#include "DWARFDebugAbbrev.h" #include "DWARFDebugAbbrev.h"
#include "DWARFDebugInfoEntry.h" #include "DWARFDebugInfoEntry.h"
#include "DWARFDebugRangeList.h"
#include <vector> #include <vector>
namespace llvm { namespace llvm {
@ -45,6 +46,11 @@ public:
/// extractDIEsIfNeeded - Parses a compile unit and indexes its DIEs if it /// extractDIEsIfNeeded - Parses a compile unit and indexes its DIEs if it
/// hasn't already been done. Returns the number of DIEs parsed at this call. /// hasn't already been done. Returns the number of DIEs parsed at this call.
size_t extractDIEsIfNeeded(bool cu_die_only); size_t extractDIEsIfNeeded(bool cu_die_only);
/// extractRangeList - extracts the range list referenced by this compile
/// unit from .debug_ranges section. Returns true on success.
/// Requires that compile unit is already extracted.
bool extractRangeList(uint32_t RangeListOffset,
DWARFDebugRangeList &RangeList) const;
void clear(); void clear();
void dump(raw_ostream &OS); void dump(raw_ostream &OS);
uint32_t getOffset() const { return Offset; } uint32_t getOffset() const { return Offset; }
@ -106,11 +112,11 @@ public:
void buildAddressRangeTable(DWARFDebugAranges *debug_aranges, void buildAddressRangeTable(DWARFDebugAranges *debug_aranges,
bool clear_dies_if_already_not_parsed); bool clear_dies_if_already_not_parsed);
/// getFunctionDIEForAddress - Returns pointer to parsed subprogram DIE,
/// address ranges of which contain the provided address, /// getInlinedChainForAddress - fetches inlined chain for a given address.
/// or NULL if there is no such subprogram. The pointer /// Returns empty chain if there is no subprogram containing address.
/// is valid until DWARFCompileUnit::clear() or clearDIEs() is called. DWARFDebugInfoEntryMinimal::InlinedChain getInlinedChainForAddress(
const DWARFDebugInfoEntryMinimal *getFunctionDIEForAddress(int64_t address); uint64_t Address);
}; };
} }

View File

@ -183,15 +183,11 @@ static bool getFileNameForCompileUnit(
return true; return true;
} }
bool static bool getFileLineInfoForCompileUnit(
DWARFContext::getFileLineInfoForCompileUnit(DWARFCompileUnit *CU, DWARFCompileUnit *CU, const DWARFDebugLine::LineTable *LineTable,
uint64_t Address, uint64_t Address, bool NeedsAbsoluteFilePath, std::string &FileName,
bool NeedsAbsoluteFilePath,
std::string &FileName,
uint32_t &Line, uint32_t &Column) { uint32_t &Line, uint32_t &Column) {
// Get the line table for this compile unit. if (CU == 0 || LineTable == 0)
const DWARFDebugLine::LineTable *LineTable = getLineTableForCompileUnit(CU);
if (!LineTable)
return false; return false;
// Get the index of row we're looking for in the line table. // Get the index of row we're looking for in the line table.
uint32_t RowIndex = LineTable->lookupAddress(Address); uint32_t RowIndex = LineTable->lookupAddress(Address);
@ -217,21 +213,84 @@ DILineInfo DWARFContext::getLineInfoForAddress(uint64_t Address,
uint32_t Line = 0; uint32_t Line = 0;
uint32_t Column = 0; uint32_t Column = 0;
if (Specifier.needs(DILineInfoSpecifier::FunctionName)) { if (Specifier.needs(DILineInfoSpecifier::FunctionName)) {
const DWARFDebugInfoEntryMinimal *FunctionDIE = // The address may correspond to instruction in some inlined function,
CU->getFunctionDIEForAddress(Address); // so we have to build the chain of inlined functions and take the
if (FunctionDIE) { // name of the topmost function in it.
if (const char *Name = FunctionDIE->getSubprogramName(CU)) const DWARFDebugInfoEntryMinimal::InlinedChain &InlinedChain =
CU->getInlinedChainForAddress(Address);
if (InlinedChain.size() > 0) {
const DWARFDebugInfoEntryMinimal &TopFunctionDIE = InlinedChain[0];
if (const char *Name = TopFunctionDIE.getSubroutineName(CU))
FunctionName = Name; FunctionName = Name;
} }
} }
if (Specifier.needs(DILineInfoSpecifier::FileLineInfo)) { if (Specifier.needs(DILineInfoSpecifier::FileLineInfo)) {
const DWARFDebugLine::LineTable *LineTable =
getLineTableForCompileUnit(CU);
const bool NeedsAbsoluteFilePath = const bool NeedsAbsoluteFilePath =
Specifier.needs(DILineInfoSpecifier::AbsoluteFilePath); Specifier.needs(DILineInfoSpecifier::AbsoluteFilePath);
getFileLineInfoForCompileUnit(CU, Address, NeedsAbsoluteFilePath, getFileLineInfoForCompileUnit(CU, LineTable, Address,
NeedsAbsoluteFilePath,
FileName, Line, Column); FileName, Line, Column);
} }
return DILineInfo(StringRef(FileName), StringRef(FunctionName), return DILineInfo(StringRef(FileName), StringRef(FunctionName),
Line, Column); Line, Column);
} }
DIInliningInfo DWARFContext::getInliningInfoForAddress(uint64_t Address,
DILineInfoSpecifier Specifier) {
DWARFCompileUnit *CU = getCompileUnitForAddress(Address);
if (!CU)
return DIInliningInfo();
const DWARFDebugInfoEntryMinimal::InlinedChain &InlinedChain =
CU->getInlinedChainForAddress(Address);
if (InlinedChain.size() == 0)
return DIInliningInfo();
DIInliningInfo InliningInfo;
uint32_t CallFile = 0, CallLine = 0, CallColumn = 0;
const DWARFDebugLine::LineTable *LineTable = 0;
for (uint32_t i = 0, n = InlinedChain.size(); i != n; i++) {
const DWARFDebugInfoEntryMinimal &FunctionDIE = InlinedChain[i];
std::string FileName = "<invalid>";
std::string FunctionName = "<invalid>";
uint32_t Line = 0;
uint32_t Column = 0;
// Get function name if necessary.
if (Specifier.needs(DILineInfoSpecifier::FunctionName)) {
if (const char *Name = FunctionDIE.getSubroutineName(CU))
FunctionName = Name;
}
if (Specifier.needs(DILineInfoSpecifier::FileLineInfo)) {
const bool NeedsAbsoluteFilePath =
Specifier.needs(DILineInfoSpecifier::AbsoluteFilePath);
if (i == 0) {
// For the topmost frame, initialize the line table of this
// compile unit and fetch file/line info from it.
LineTable = getLineTableForCompileUnit(CU);
// For the topmost routine, get file/line info from line table.
getFileLineInfoForCompileUnit(CU, LineTable, Address,
NeedsAbsoluteFilePath,
FileName, Line, Column);
} else {
// Otherwise, use call file, call line and call column from
// previous DIE in inlined chain.
getFileNameForCompileUnit(CU, LineTable, CallFile,
NeedsAbsoluteFilePath, FileName);
Line = CallLine;
Column = CallColumn;
}
// Get call file/line/column of a current DIE.
if (i + 1 < n) {
FunctionDIE.getCallerFrame(CU, CallFile, CallLine, CallColumn);
}
}
DILineInfo Frame(StringRef(FileName), StringRef(FunctionName),
Line, Column);
InliningInfo.addFrame(Frame);
}
return InliningInfo;
}
void DWARFContextInMemory::anchor() { } void DWARFContextInMemory::anchor() { }

View File

@ -66,6 +66,8 @@ public:
virtual DILineInfo getLineInfoForAddress(uint64_t Address, virtual DILineInfo getLineInfoForAddress(uint64_t Address,
DILineInfoSpecifier Specifier = DILineInfoSpecifier()); DILineInfoSpecifier Specifier = DILineInfoSpecifier());
virtual DIInliningInfo getInliningInfoForAddress(uint64_t Address,
DILineInfoSpecifier Specifier = DILineInfoSpecifier());
bool isLittleEndian() const { return IsLittleEndian; } bool isLittleEndian() const { return IsLittleEndian; }
@ -86,14 +88,6 @@ private:
/// Return the compile unit which contains instruction with provided /// Return the compile unit which contains instruction with provided
/// address. /// address.
DWARFCompileUnit *getCompileUnitForAddress(uint64_t Address); DWARFCompileUnit *getCompileUnitForAddress(uint64_t Address);
/// Fetches filename, line and column number for given address and
/// compile unit. Returns true on success.
bool getFileLineInfoForCompileUnit(DWARFCompileUnit *CU,
uint64_t Address,
bool NeedsAbsoluteFilePath,
std::string &FileName,
uint32_t &Line, uint32_t &Column);
}; };
/// DWARFContextInMemory is the simplest possible implementation of a /// DWARFContextInMemory is the simplest possible implementation of a

View File

@ -364,6 +364,16 @@ DWARFDebugInfoEntryMinimal::extract(const DWARFCompileUnit *cu,
return false; return false;
} }
bool DWARFDebugInfoEntryMinimal::isSubprogramDIE() const {
return getTag() == DW_TAG_subprogram;
}
bool DWARFDebugInfoEntryMinimal::isSubroutineDIE() const {
uint32_t Tag = getTag();
return Tag == DW_TAG_subprogram ||
Tag == DW_TAG_inlined_subroutine;
}
uint32_t uint32_t
DWARFDebugInfoEntryMinimal::getAttributeValue(const DWARFCompileUnit *cu, DWARFDebugInfoEntryMinimal::getAttributeValue(const DWARFCompileUnit *cu,
const uint16_t attr, const uint16_t attr,
@ -446,24 +456,31 @@ DWARFDebugInfoEntryMinimal::getAttributeValueAsReference(
return fail_value; return fail_value;
} }
bool DWARFDebugInfoEntryMinimal::getLowAndHighPC(const DWARFCompileUnit *CU,
uint64_t &LowPC, uint64_t &HighPC) const {
HighPC = -1ULL;
LowPC = getAttributeValueAsUnsigned(CU, DW_AT_low_pc, -1ULL);
if (LowPC != -1ULL)
HighPC = getAttributeValueAsUnsigned(CU, DW_AT_high_pc, -1ULL);
return (HighPC != -1ULL);
}
void void
DWARFDebugInfoEntryMinimal::buildAddressRangeTable(const DWARFCompileUnit *cu, DWARFDebugInfoEntryMinimal::buildAddressRangeTable(const DWARFCompileUnit *CU,
DWARFDebugAranges *debug_aranges) DWARFDebugAranges *DebugAranges)
const { const {
if (AbbrevDecl) { if (AbbrevDecl) {
uint16_t tag = AbbrevDecl->getTag(); if (isSubprogramDIE()) {
if (tag == DW_TAG_subprogram) { uint64_t LowPC, HighPC;
uint64_t hi_pc = -1ULL; if (getLowAndHighPC(CU, LowPC, HighPC)) {
uint64_t lo_pc = getAttributeValueAsUnsigned(cu, DW_AT_low_pc, -1ULL); DebugAranges->appendRange(CU->getOffset(), LowPC, HighPC);
if (lo_pc != -1ULL) }
hi_pc = getAttributeValueAsUnsigned(cu, DW_AT_high_pc, -1ULL); // FIXME: try to append ranges from .debug_ranges section.
if (hi_pc != -1ULL)
debug_aranges->appendRange(cu->getOffset(), lo_pc, hi_pc);
} }
const DWARFDebugInfoEntryMinimal *child = getFirstChild(); const DWARFDebugInfoEntryMinimal *child = getFirstChild();
while (child) { while (child) {
child->buildAddressRangeTable(cu, debug_aranges); child->buildAddressRangeTable(CU, DebugAranges);
child = child->getSibling(); child = child->getSibling();
} }
} }
@ -471,51 +488,90 @@ DWARFDebugInfoEntryMinimal::buildAddressRangeTable(const DWARFCompileUnit *cu,
bool bool
DWARFDebugInfoEntryMinimal::addressRangeContainsAddress( DWARFDebugInfoEntryMinimal::addressRangeContainsAddress(
const DWARFCompileUnit *cu, const uint64_t address) const { const DWARFCompileUnit *CU, const uint64_t Address) const {
if (!isNULL() && getTag() == DW_TAG_subprogram) { if (isNULL())
uint64_t hi_pc = -1ULL; return false;
uint64_t lo_pc = getAttributeValueAsUnsigned(cu, DW_AT_low_pc, -1ULL); uint64_t LowPC, HighPC;
if (lo_pc != -1ULL) if (getLowAndHighPC(CU, LowPC, HighPC))
hi_pc = getAttributeValueAsUnsigned(cu, DW_AT_high_pc, -1ULL); return (LowPC <= Address && Address <= HighPC);
if (hi_pc != -1ULL) { // Try to get address ranges from .debug_ranges section.
return (lo_pc <= address && address < hi_pc); uint32_t RangesOffset = getAttributeValueAsReference(CU, DW_AT_ranges, -1U);
} if (RangesOffset != -1U) {
DWARFDebugRangeList RangeList;
if (CU->extractRangeList(RangesOffset, RangeList))
return RangeList.containsAddress(CU->getBaseAddress(), Address);
} }
return false; return false;
} }
const char* const char*
DWARFDebugInfoEntryMinimal::getSubprogramName( DWARFDebugInfoEntryMinimal::getSubroutineName(
const DWARFCompileUnit *cu) const { const DWARFCompileUnit *CU) const {
if (isNULL() || getTag() != DW_TAG_subprogram) if (!isSubroutineDIE())
return 0; return 0;
// Try to get mangled name if possible. // Try to get mangled name if possible.
if (const char *name = if (const char *name =
getAttributeValueAsString(cu, DW_AT_MIPS_linkage_name, 0)) getAttributeValueAsString(CU, DW_AT_MIPS_linkage_name, 0))
return name; return name;
if (const char *name = getAttributeValueAsString(cu, DW_AT_linkage_name, 0)) if (const char *name = getAttributeValueAsString(CU, DW_AT_linkage_name, 0))
return name; return name;
if (const char *name = getAttributeValueAsString(cu, DW_AT_name, 0)) if (const char *name = getAttributeValueAsString(CU, DW_AT_name, 0))
return name; return name;
// Try to get name from specification DIE. // Try to get name from specification DIE.
uint32_t spec_ref = uint32_t spec_ref =
getAttributeValueAsReference(cu, DW_AT_specification, -1U); getAttributeValueAsReference(CU, DW_AT_specification, -1U);
if (spec_ref != -1U) { if (spec_ref != -1U) {
DWARFDebugInfoEntryMinimal spec_die; DWARFDebugInfoEntryMinimal spec_die;
if (spec_die.extract(cu, &spec_ref)) { if (spec_die.extract(CU, &spec_ref)) {
if (const char *name = spec_die.getSubprogramName(cu)) if (const char *name = spec_die.getSubroutineName(CU))
return name; return name;
} }
} }
// Try to get name from abstract origin DIE. // Try to get name from abstract origin DIE.
uint32_t abs_origin_ref = uint32_t abs_origin_ref =
getAttributeValueAsReference(cu, DW_AT_abstract_origin, -1U); getAttributeValueAsReference(CU, DW_AT_abstract_origin, -1U);
if (abs_origin_ref != -1U) { if (abs_origin_ref != -1U) {
DWARFDebugInfoEntryMinimal abs_origin_die; DWARFDebugInfoEntryMinimal abs_origin_die;
if (abs_origin_die.extract(cu, &abs_origin_ref)) { if (abs_origin_die.extract(CU, &abs_origin_ref)) {
if (const char *name = abs_origin_die.getSubprogramName(cu)) if (const char *name = abs_origin_die.getSubroutineName(CU))
return name; return name;
} }
} }
return 0; return 0;
} }
void DWARFDebugInfoEntryMinimal::getCallerFrame(
const DWARFCompileUnit *CU, uint32_t &CallFile, uint32_t &CallLine,
uint32_t &CallColumn) const {
CallFile = getAttributeValueAsUnsigned(CU, DW_AT_call_file, 0);
CallLine = getAttributeValueAsUnsigned(CU, DW_AT_call_line, 0);
CallColumn = getAttributeValueAsUnsigned(CU, DW_AT_call_column, 0);
}
DWARFDebugInfoEntryMinimal::InlinedChain
DWARFDebugInfoEntryMinimal::getInlinedChainForAddress(
const DWARFCompileUnit *CU, const uint64_t Address) const {
DWARFDebugInfoEntryMinimal::InlinedChain InlinedChain;
if (isNULL())
return InlinedChain;
for (const DWARFDebugInfoEntryMinimal *DIE = this; DIE; ) {
// Append current DIE to inlined chain only if it has correct tag
// (e.g. it is not a lexical block).
if (DIE->isSubroutineDIE()) {
InlinedChain.push_back(*DIE);
}
// Try to get child which also contains provided address.
const DWARFDebugInfoEntryMinimal *Child = DIE->getFirstChild();
while (Child) {
if (Child->addressRangeContainsAddress(CU, Address)) {
// Assume there is only one such child.
break;
}
Child = Child->getSibling();
}
DIE = Child;
}
// Reverse the obtained chain to make the root of inlined chain last.
std::reverse(InlinedChain.begin(), InlinedChain.end());
return InlinedChain;
}

View File

@ -11,6 +11,7 @@
#define LLVM_DEBUGINFO_DWARFDEBUGINFOENTRY_H #define LLVM_DEBUGINFO_DWARFDEBUGINFOENTRY_H
#include "DWARFAbbreviationDeclaration.h" #include "DWARFAbbreviationDeclaration.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/DataTypes.h" #include "llvm/Support/DataTypes.h"
namespace llvm { namespace llvm {
@ -19,6 +20,7 @@ class DWARFDebugAranges;
class DWARFCompileUnit; class DWARFCompileUnit;
class DWARFContext; class DWARFContext;
class DWARFFormValue; class DWARFFormValue;
class DWARFInlinedSubroutineChain;
/// DWARFDebugInfoEntryMinimal - A DIE with only the minimum required data. /// DWARFDebugInfoEntryMinimal - A DIE with only the minimum required data.
class DWARFDebugInfoEntryMinimal { class DWARFDebugInfoEntryMinimal {
@ -52,6 +54,13 @@ public:
uint32_t getTag() const { return AbbrevDecl ? AbbrevDecl->getTag() : 0; } uint32_t getTag() const { return AbbrevDecl ? AbbrevDecl->getTag() : 0; }
bool isNULL() const { return AbbrevDecl == 0; } bool isNULL() const { return AbbrevDecl == 0; }
/// Returns true if DIE represents a subprogram (not inlined).
bool isSubprogramDIE() const;
/// Returns true if DIE represents a subprogram or an inlined
/// subroutine.
bool isSubroutineDIE() const;
uint32_t getOffset() const { return Offset; } uint32_t getOffset() const { return Offset; }
uint32_t getNumAttributes() const { uint32_t getNumAttributes() const {
return !isNULL() ? AbbrevDecl->getNumAttributes() : 0; return !isNULL() ? AbbrevDecl->getNumAttributes() : 0;
@ -126,17 +135,40 @@ public:
const uint16_t attr, const uint16_t attr,
int64_t fail_value) const; int64_t fail_value) const;
void buildAddressRangeTable(const DWARFCompileUnit *cu, /// Retrieves DW_AT_low_pc and DW_AT_high_pc from CU.
DWARFDebugAranges *debug_aranges) const; /// Returns true if both attributes are present.
bool getLowAndHighPC(const DWARFCompileUnit *CU,
uint64_t &LowPC, uint64_t &HighPC) const;
bool addressRangeContainsAddress(const DWARFCompileUnit *cu, void buildAddressRangeTable(const DWARFCompileUnit *CU,
const uint64_t address) const; DWARFDebugAranges *DebugAranges) const;
// If a DIE represents a subprogram, returns its mangled name bool addressRangeContainsAddress(const DWARFCompileUnit *CU,
// (or short name, if mangled is missing). This name may be fetched const uint64_t Address) const;
// from specification or abstract origin for this subprogram.
// Returns null if no name is found. /// If a DIE represents a subprogram (or inlined subroutine),
const char* getSubprogramName(const DWARFCompileUnit *cu) const; /// returns its mangled name (or short name, if mangled is missing).
/// This name may be fetched from specification or abstract origin
/// for this subprogram. Returns null if no name is found.
const char* getSubroutineName(const DWARFCompileUnit *CU) const;
/// Retrieves values of DW_AT_call_file, DW_AT_call_line and
/// DW_AT_call_column from DIE (or zeroes if they are missing).
void getCallerFrame(const DWARFCompileUnit *CU, uint32_t &CallFile,
uint32_t &CallLine, uint32_t &CallColumn) const;
/// InlinedChain - represents a chain of inlined_subroutine
/// DIEs, (possibly ending with subprogram DIE), all of which are contained
/// in some concrete inlined instance tree. Address range for each DIE
/// (except the last DIE) in this chain is contained in address
/// range for next DIE in the chain.
typedef SmallVector<DWARFDebugInfoEntryMinimal, 4> InlinedChain;
/// Get inlined chain for a given address, rooted at the current DIE.
/// Returns empty chain if address is not contained in address range
/// of current DIE.
InlinedChain getInlinedChainForAddress(const DWARFCompileUnit *CU,
const uint64_t Address) const;
}; };
} }

View File

@ -37,10 +37,7 @@ bool DWARFDebugRangeList::extract(DataExtractor data, uint32_t *offset_ptr) {
clear(); clear();
return false; return false;
} }
// The end of any given range list is marked by an end of list entry, if (entry.isEndOfListEntry())
// which consists of a 0 for the beginning address offset
// and a 0 for the ending address offset.
if (entry.StartAddress == 0 && entry.EndAddress == 0)
break; break;
Entries.push_back(entry); Entries.push_back(entry);
} }
@ -57,3 +54,14 @@ void DWARFDebugRangeList::dump(raw_ostream &OS) const {
} }
OS << format("%08x <End of list>\n", Offset); OS << format("%08x <End of list>\n", Offset);
} }
bool DWARFDebugRangeList::containsAddress(uint64_t BaseAddress,
uint64_t Address) const {
for (int i = 0, n = Entries.size(); i != n; ++i) {
if (Entries[i].isBaseAddressSelectionEntry(AddressSize))
BaseAddress = Entries[i].EndAddress;
else if (Entries[i].containsAddress(BaseAddress, Address))
return true;
}
return false;
}

View File

@ -31,6 +31,29 @@ public:
// address past the end of the address range. The ending address must // address past the end of the address range. The ending address must
// be greater than or equal to the beginning address. // be greater than or equal to the beginning address.
uint64_t EndAddress; uint64_t EndAddress;
// The end of any given range list is marked by an end of list entry,
// which consists of a 0 for the beginning address offset
// and a 0 for the ending address offset.
bool isEndOfListEntry() const {
return (StartAddress == 0) && (EndAddress == 0);
}
// A base address selection entry consists of:
// 1. The value of the largest representable address offset
// (for example, 0xffffffff when the size of an address is 32 bits).
// 2. An address, which defines the appropriate base address for
// use in interpreting the beginning and ending address offsets of
// subsequent entries of the location list.
bool isBaseAddressSelectionEntry(uint8_t AddressSize) const {
assert(AddressSize == 4 || AddressSize == 8);
if (AddressSize == 4)
return StartAddress == -1U;
else
return StartAddress == -1ULL;
}
bool containsAddress(uint64_t BaseAddress, uint64_t Address) const {
return (BaseAddress + StartAddress <= Address) &&
(Address < BaseAddress + EndAddress);
}
}; };
private: private:
@ -44,6 +67,10 @@ public:
void clear(); void clear();
void dump(raw_ostream &OS) const; void dump(raw_ostream &OS) const;
bool extract(DataExtractor data, uint32_t *offset_ptr); bool extract(DataExtractor data, uint32_t *offset_ptr);
/// containsAddress - Returns true if range list contains the given
/// address. Has to be passed base address of the compile unit that
/// references this range list.
bool containsAddress(uint64_t BaseAddress, uint64_t Address) const;
}; };
} // namespace llvm } // namespace llvm

Binary file not shown.

View File

@ -0,0 +1,28 @@
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-inl-test.elf-x86-64 --address=0x613 \
RUN: --inlining --functions | FileCheck %s -check-prefix DEEP_STACK
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-inl-test.elf-x86-64 --address=0x6de \
RUN: --inlining | FileCheck %s -check-prefix SHORTER_STACK
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-inl-test.elf-x86-64 --address=0x685 \
RUN: --inlining | FileCheck %s -check-prefix SHORT_STACK
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-inl-test.elf-x86-64 --address=0x640 \
RUN: --functions | FileCheck %s -check-prefix INL_FUNC_NAME
DEEP_STACK: inlined_h
DEEP_STACK-NEXT: header.h:2:21
DEEP_STACK-NEXT: inlined_g
DEEP_STACK-NEXT: header.h:7
DEEP_STACK-NEXT: inlined_f
DEEP_STACK-NEXT: main.cc:3
DEEP_STACK-NEXT: main
DEEP_STACK-NEXT: main.cc:8
SHORTER_STACK: header.h:7:20
SHORTER_STACK-NEXT: main.cc:3
SHORTER_STACK-NEXT: main.cc:8
SHORT_STACK: main.cc:3:20
SHORT_STACK-NEXT: main.cc:8
INL_FUNC_NAME: inlined_g
INL_FUNC_NAME-NEXT: header.h:7:20

View File

@ -44,6 +44,18 @@ PrintFunctions("functions", cl::init(false),
cl::desc("Print function names as well as line information " cl::desc("Print function names as well as line information "
"for a given address")); "for a given address"));
static cl::opt<bool>
PrintInlining("inlining", cl::init(false),
cl::desc("Print all inlined frames for a given address"));
static void PrintDILineInfo(DILineInfo dli) {
if (PrintFunctions)
outs() << (dli.getFunctionName() ? dli.getFunctionName() : "<unknown>")
<< "\n";
outs() << (dli.getFileName() ? dli.getFileName() : "<unknown>") << ':'
<< dli.getLine() << ':' << dli.getColumn() << '\n';
}
static void DumpInput(const StringRef &Filename) { static void DumpInput(const StringRef &Filename) {
OwningPtr<MemoryBuffer> Buff; OwningPtr<MemoryBuffer> Buff;
@ -101,16 +113,27 @@ static void DumpInput(const StringRef &Filename) {
dictx->dump(outs()); dictx->dump(outs());
} else { } else {
// Print line info for the specified address. // Print line info for the specified address.
int spec_flags = DILineInfoSpecifier::FileLineInfo | int SpecFlags = DILineInfoSpecifier::FileLineInfo |
DILineInfoSpecifier::AbsoluteFilePath; DILineInfoSpecifier::AbsoluteFilePath;
if (PrintFunctions) if (PrintFunctions)
spec_flags |= DILineInfoSpecifier::FunctionName; SpecFlags |= DILineInfoSpecifier::FunctionName;
DILineInfo dli = dictx->getLineInfoForAddress(Address, spec_flags); if (PrintInlining) {
if (PrintFunctions) DIInliningInfo InliningInfo = dictx->getInliningInfoForAddress(
outs() << (dli.getFunctionName() ? dli.getFunctionName() : "<unknown>") Address, SpecFlags);
<< "\n"; uint32_t n = InliningInfo.getNumberOfFrames();
outs() << (dli.getFileName() ? dli.getFileName() : "<unknown>") << ':' if (n == 0) {
<< dli.getLine() << ':' << dli.getColumn() << '\n'; // Print one empty debug line info in any case.
PrintDILineInfo(DILineInfo());
} else {
for (uint32_t i = 0; i < n; i++) {
DILineInfo dli = InliningInfo.getFrame(i);
PrintDILineInfo(dli);
}
}
} else {
DILineInfo dli = dictx->getLineInfoForAddress(Address, SpecFlags);
PrintDILineInfo(dli);
}
} }
} }