mirror of
https://github.com/RPCS3/llvm.git
synced 2025-04-10 18:11:26 +00:00
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:
parent
2d5c28da0d
commit
5eae90d727
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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() { }
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
BIN
test/DebugInfo/Inputs/dwarfdump-inl-test.elf-x86-64
Executable file
BIN
test/DebugInfo/Inputs/dwarfdump-inl-test.elf-x86-64
Executable file
Binary file not shown.
28
test/DebugInfo/dwarfdump-inlining.test
Normal file
28
test/DebugInfo/dwarfdump-inlining.test
Normal 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
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user