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
#define LLVM_DEBUGINFO_DICONTEXT_H
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.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
/// should be filled with data.
class DILineInfoSpecifier {
@ -86,8 +104,10 @@ public:
virtual void dump(raw_ostream &OS) = 0;
virtual DILineInfo getLineInfoForAddress(uint64_t address,
DILineInfoSpecifier specifier = DILineInfoSpecifier()) = 0;
virtual DILineInfo getLineInfoForAddress(uint64_t Address,
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;
}
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() {
Offset = 0;
Length = 0;
@ -246,12 +255,21 @@ DWARFCompileUnit::buildAddressRangeTable(DWARFDebugAranges *debug_aranges,
clearDIEs(true);
}
const DWARFDebugInfoEntryMinimal*
DWARFCompileUnit::getFunctionDIEForAddress(int64_t address) {
DWARFDebugInfoEntryMinimal::InlinedChain
DWARFCompileUnit::getInlinedChainForAddress(uint64_t Address) {
// First, find a subprogram that contains the given address (the root
// of inlined chain).
extractDIEsIfNeeded(false);
const DWARFDebugInfoEntryMinimal *SubprogramDIE = 0;
for (size_t i = 0, n = DieArray.size(); i != n; i++) {
if (DieArray[i].addressRangeContainsAddress(this, address))
return &DieArray[i];
if (DieArray[i].isSubprogramDIE() &&
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 "DWARFDebugInfoEntry.h"
#include "DWARFDebugRangeList.h"
#include <vector>
namespace llvm {
@ -45,6 +46,11 @@ public:
/// 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.
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 dump(raw_ostream &OS);
uint32_t getOffset() const { return Offset; }
@ -106,11 +112,11 @@ public:
void buildAddressRangeTable(DWARFDebugAranges *debug_aranges,
bool clear_dies_if_already_not_parsed);
/// getFunctionDIEForAddress - Returns pointer to parsed subprogram DIE,
/// address ranges of which contain the provided address,
/// or NULL if there is no such subprogram. The pointer
/// is valid until DWARFCompileUnit::clear() or clearDIEs() is called.
const DWARFDebugInfoEntryMinimal *getFunctionDIEForAddress(int64_t address);
/// getInlinedChainForAddress - fetches inlined chain for a given address.
/// Returns empty chain if there is no subprogram containing address.
DWARFDebugInfoEntryMinimal::InlinedChain getInlinedChainForAddress(
uint64_t Address);
};
}

View File

@ -183,15 +183,11 @@ static bool getFileNameForCompileUnit(
return true;
}
bool
DWARFContext::getFileLineInfoForCompileUnit(DWARFCompileUnit *CU,
uint64_t Address,
bool NeedsAbsoluteFilePath,
std::string &FileName,
uint32_t &Line, uint32_t &Column) {
// Get the line table for this compile unit.
const DWARFDebugLine::LineTable *LineTable = getLineTableForCompileUnit(CU);
if (!LineTable)
static bool getFileLineInfoForCompileUnit(
DWARFCompileUnit *CU, const DWARFDebugLine::LineTable *LineTable,
uint64_t Address, bool NeedsAbsoluteFilePath, std::string &FileName,
uint32_t &Line, uint32_t &Column) {
if (CU == 0 || LineTable == 0)
return false;
// Get the index of row we're looking for in the line table.
uint32_t RowIndex = LineTable->lookupAddress(Address);
@ -217,21 +213,84 @@ DILineInfo DWARFContext::getLineInfoForAddress(uint64_t Address,
uint32_t Line = 0;
uint32_t Column = 0;
if (Specifier.needs(DILineInfoSpecifier::FunctionName)) {
const DWARFDebugInfoEntryMinimal *FunctionDIE =
CU->getFunctionDIEForAddress(Address);
if (FunctionDIE) {
if (const char *Name = FunctionDIE->getSubprogramName(CU))
// The address may correspond to instruction in some inlined function,
// so we have to build the chain of inlined functions and take the
// name of the topmost function in it.
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;
}
}
if (Specifier.needs(DILineInfoSpecifier::FileLineInfo)) {
const DWARFDebugLine::LineTable *LineTable =
getLineTableForCompileUnit(CU);
const bool NeedsAbsoluteFilePath =
Specifier.needs(DILineInfoSpecifier::AbsoluteFilePath);
getFileLineInfoForCompileUnit(CU, Address, NeedsAbsoluteFilePath,
getFileLineInfoForCompileUnit(CU, LineTable, Address,
NeedsAbsoluteFilePath,
FileName, Line, Column);
}
return DILineInfo(StringRef(FileName), StringRef(FunctionName),
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() { }

View File

@ -66,6 +66,8 @@ public:
virtual DILineInfo getLineInfoForAddress(uint64_t Address,
DILineInfoSpecifier Specifier = DILineInfoSpecifier());
virtual DIInliningInfo getInliningInfoForAddress(uint64_t Address,
DILineInfoSpecifier Specifier = DILineInfoSpecifier());
bool isLittleEndian() const { return IsLittleEndian; }
@ -86,14 +88,6 @@ private:
/// Return the compile unit which contains instruction with provided
/// 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

View File

@ -364,6 +364,16 @@ DWARFDebugInfoEntryMinimal::extract(const DWARFCompileUnit *cu,
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
DWARFDebugInfoEntryMinimal::getAttributeValue(const DWARFCompileUnit *cu,
const uint16_t attr,
@ -446,24 +456,31 @@ DWARFDebugInfoEntryMinimal::getAttributeValueAsReference(
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
DWARFDebugInfoEntryMinimal::buildAddressRangeTable(const DWARFCompileUnit *cu,
DWARFDebugAranges *debug_aranges)
DWARFDebugInfoEntryMinimal::buildAddressRangeTable(const DWARFCompileUnit *CU,
DWARFDebugAranges *DebugAranges)
const {
if (AbbrevDecl) {
uint16_t tag = AbbrevDecl->getTag();
if (tag == DW_TAG_subprogram) {
uint64_t hi_pc = -1ULL;
uint64_t lo_pc = getAttributeValueAsUnsigned(cu, DW_AT_low_pc, -1ULL);
if (lo_pc != -1ULL)
hi_pc = getAttributeValueAsUnsigned(cu, DW_AT_high_pc, -1ULL);
if (hi_pc != -1ULL)
debug_aranges->appendRange(cu->getOffset(), lo_pc, hi_pc);
if (isSubprogramDIE()) {
uint64_t LowPC, HighPC;
if (getLowAndHighPC(CU, LowPC, HighPC)) {
DebugAranges->appendRange(CU->getOffset(), LowPC, HighPC);
}
// FIXME: try to append ranges from .debug_ranges section.
}
const DWARFDebugInfoEntryMinimal *child = getFirstChild();
while (child) {
child->buildAddressRangeTable(cu, debug_aranges);
child->buildAddressRangeTable(CU, DebugAranges);
child = child->getSibling();
}
}
@ -471,51 +488,90 @@ DWARFDebugInfoEntryMinimal::buildAddressRangeTable(const DWARFCompileUnit *cu,
bool
DWARFDebugInfoEntryMinimal::addressRangeContainsAddress(
const DWARFCompileUnit *cu, const uint64_t address) const {
if (!isNULL() && getTag() == DW_TAG_subprogram) {
uint64_t hi_pc = -1ULL;
uint64_t lo_pc = getAttributeValueAsUnsigned(cu, DW_AT_low_pc, -1ULL);
if (lo_pc != -1ULL)
hi_pc = getAttributeValueAsUnsigned(cu, DW_AT_high_pc, -1ULL);
if (hi_pc != -1ULL) {
return (lo_pc <= address && address < hi_pc);
}
const DWARFCompileUnit *CU, const uint64_t Address) const {
if (isNULL())
return false;
uint64_t LowPC, HighPC;
if (getLowAndHighPC(CU, LowPC, HighPC))
return (LowPC <= Address && Address <= HighPC);
// Try to get address ranges from .debug_ranges section.
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;
}
const char*
DWARFDebugInfoEntryMinimal::getSubprogramName(
const DWARFCompileUnit *cu) const {
if (isNULL() || getTag() != DW_TAG_subprogram)
DWARFDebugInfoEntryMinimal::getSubroutineName(
const DWARFCompileUnit *CU) const {
if (!isSubroutineDIE())
return 0;
// Try to get mangled name if possible.
if (const char *name =
getAttributeValueAsString(cu, DW_AT_MIPS_linkage_name, 0))
getAttributeValueAsString(CU, DW_AT_MIPS_linkage_name, 0))
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;
if (const char *name = getAttributeValueAsString(cu, DW_AT_name, 0))
if (const char *name = getAttributeValueAsString(CU, DW_AT_name, 0))
return name;
// Try to get name from specification DIE.
uint32_t spec_ref =
getAttributeValueAsReference(cu, DW_AT_specification, -1U);
getAttributeValueAsReference(CU, DW_AT_specification, -1U);
if (spec_ref != -1U) {
DWARFDebugInfoEntryMinimal spec_die;
if (spec_die.extract(cu, &spec_ref)) {
if (const char *name = spec_die.getSubprogramName(cu))
if (spec_die.extract(CU, &spec_ref)) {
if (const char *name = spec_die.getSubroutineName(CU))
return name;
}
}
// Try to get name from abstract origin DIE.
uint32_t abs_origin_ref =
getAttributeValueAsReference(cu, DW_AT_abstract_origin, -1U);
getAttributeValueAsReference(CU, DW_AT_abstract_origin, -1U);
if (abs_origin_ref != -1U) {
DWARFDebugInfoEntryMinimal abs_origin_die;
if (abs_origin_die.extract(cu, &abs_origin_ref)) {
if (const char *name = abs_origin_die.getSubprogramName(cu))
if (abs_origin_die.extract(CU, &abs_origin_ref)) {
if (const char *name = abs_origin_die.getSubroutineName(CU))
return name;
}
}
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
#include "DWARFAbbreviationDeclaration.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/DataTypes.h"
namespace llvm {
@ -19,6 +20,7 @@ class DWARFDebugAranges;
class DWARFCompileUnit;
class DWARFContext;
class DWARFFormValue;
class DWARFInlinedSubroutineChain;
/// DWARFDebugInfoEntryMinimal - A DIE with only the minimum required data.
class DWARFDebugInfoEntryMinimal {
@ -52,6 +54,13 @@ public:
uint32_t getTag() const { return AbbrevDecl ? AbbrevDecl->getTag() : 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 getNumAttributes() const {
return !isNULL() ? AbbrevDecl->getNumAttributes() : 0;
@ -126,17 +135,40 @@ public:
const uint16_t attr,
int64_t fail_value) const;
void buildAddressRangeTable(const DWARFCompileUnit *cu,
DWARFDebugAranges *debug_aranges) const;
/// Retrieves DW_AT_low_pc and DW_AT_high_pc from CU.
/// Returns true if both attributes are present.
bool getLowAndHighPC(const DWARFCompileUnit *CU,
uint64_t &LowPC, uint64_t &HighPC) const;
bool addressRangeContainsAddress(const DWARFCompileUnit *cu,
const uint64_t address) const;
void buildAddressRangeTable(const DWARFCompileUnit *CU,
DWARFDebugAranges *DebugAranges) const;
// If a DIE represents a subprogram, 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* getSubprogramName(const DWARFCompileUnit *cu) const;
bool addressRangeContainsAddress(const DWARFCompileUnit *CU,
const uint64_t Address) 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. 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();
return false;
}
// 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.
if (entry.StartAddress == 0 && entry.EndAddress == 0)
if (entry.isEndOfListEntry())
break;
Entries.push_back(entry);
}
@ -57,3 +54,14 @@ void DWARFDebugRangeList::dump(raw_ostream &OS) const {
}
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
// be greater than or equal to the beginning address.
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:
@ -44,6 +67,10 @@ public:
void clear();
void dump(raw_ostream &OS) const;
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

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 "
"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) {
OwningPtr<MemoryBuffer> Buff;
@ -101,16 +113,27 @@ static void DumpInput(const StringRef &Filename) {
dictx->dump(outs());
} else {
// Print line info for the specified address.
int spec_flags = DILineInfoSpecifier::FileLineInfo |
DILineInfoSpecifier::AbsoluteFilePath;
int SpecFlags = DILineInfoSpecifier::FileLineInfo |
DILineInfoSpecifier::AbsoluteFilePath;
if (PrintFunctions)
spec_flags |= DILineInfoSpecifier::FunctionName;
DILineInfo dli = dictx->getLineInfoForAddress(Address, spec_flags);
if (PrintFunctions)
outs() << (dli.getFunctionName() ? dli.getFunctionName() : "<unknown>")
<< "\n";
outs() << (dli.getFileName() ? dli.getFileName() : "<unknown>") << ':'
<< dli.getLine() << ':' << dli.getColumn() << '\n';
SpecFlags |= DILineInfoSpecifier::FunctionName;
if (PrintInlining) {
DIInliningInfo InliningInfo = dictx->getInliningInfoForAddress(
Address, SpecFlags);
uint32_t n = InliningInfo.getNumberOfFrames();
if (n == 0) {
// 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);
}
}
}