mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-01-27 14:34:58 +00:00
This patch extends the libLLVMDebugInfo which contains a minimalistic DWARF parser:
1) DIContext is now able to return function name for a given instruction address (besides file/line info). 2) llvm-dwarfdump accepts flag --functions that prints the function name (if address is specified by --address flag). 3) test case that checks the basic functionality of llvm-dwarfdump added git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@159512 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
9c3d5a70f4
commit
3e25c4a1e3
@ -26,26 +26,49 @@ class raw_ostream;
|
||||
/// DILineInfo - a format-neutral container for source line information.
|
||||
class DILineInfo {
|
||||
const char *FileName;
|
||||
const char *FunctionName;
|
||||
uint32_t Line;
|
||||
uint32_t Column;
|
||||
public:
|
||||
DILineInfo() : FileName("<invalid>"), Line(0), Column(0) {}
|
||||
DILineInfo(const char *fileName, uint32_t line, uint32_t column)
|
||||
: FileName(fileName), Line(line), Column(column) {}
|
||||
DILineInfo()
|
||||
: FileName("<invalid>"), FunctionName("<invalid>"),
|
||||
Line(0), Column(0) {}
|
||||
DILineInfo(const char *fileName, const char *functionName,
|
||||
uint32_t line, uint32_t column)
|
||||
: FileName(fileName), FunctionName(functionName),
|
||||
Line(line), Column(column) {}
|
||||
|
||||
const char *getFileName() const { return FileName; }
|
||||
const char *getFunctionName() const { return FunctionName; }
|
||||
uint32_t getLine() const { return Line; }
|
||||
uint32_t getColumn() const { return Column; }
|
||||
|
||||
bool operator==(const DILineInfo &RHS) const {
|
||||
return Line == RHS.Line && Column == RHS.Column &&
|
||||
std::strcmp(FileName, RHS.FileName) == 0;
|
||||
std::strcmp(FileName, RHS.FileName) == 0 &&
|
||||
std::strcmp(FunctionName, RHS.FunctionName) == 0;
|
||||
}
|
||||
bool operator!=(const DILineInfo &RHS) const {
|
||||
return !(*this == RHS);
|
||||
}
|
||||
};
|
||||
|
||||
/// DILineInfoSpecifier - controls which fields of DILineInfo container
|
||||
/// should be filled with data.
|
||||
class DILineInfoSpecifier {
|
||||
const uint32_t Flags; // Or'ed flags that set the info we want to fetch.
|
||||
public:
|
||||
enum Specification {
|
||||
FileLineInfo = 1 << 0,
|
||||
FunctionName = 1 << 1
|
||||
};
|
||||
// Use file/line info by default.
|
||||
DILineInfoSpecifier(uint32_t flags = FileLineInfo) : Flags(flags) {}
|
||||
bool needs(Specification spec) const {
|
||||
return (Flags & spec) > 0;
|
||||
}
|
||||
};
|
||||
|
||||
class DIContext {
|
||||
public:
|
||||
virtual ~DIContext();
|
||||
@ -60,7 +83,8 @@ public:
|
||||
|
||||
virtual void dump(raw_ostream &OS) = 0;
|
||||
|
||||
virtual DILineInfo getLineInfoForAddress(uint64_t address) = 0;
|
||||
virtual DILineInfo getLineInfoForAddress(uint64_t address,
|
||||
DILineInfoSpecifier specifier = DILineInfoSpecifier()) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ void DWARFCompileUnit::clear() {
|
||||
Abbrevs = 0;
|
||||
AddrSize = 0;
|
||||
BaseAddr = 0;
|
||||
DieArray.clear();
|
||||
clearDIEs(false);
|
||||
}
|
||||
|
||||
void DWARFCompileUnit::dump(raw_ostream &OS) {
|
||||
@ -201,7 +201,7 @@ size_t DWARFCompileUnit::extractDIEsIfNeeded(bool cu_die_only) {
|
||||
}
|
||||
|
||||
void DWARFCompileUnit::clearDIEs(bool keep_compile_unit_die) {
|
||||
if (DieArray.size() > 1) {
|
||||
if (DieArray.size() > (unsigned)keep_compile_unit_die) {
|
||||
// std::vectors never get any smaller when resized to a smaller size,
|
||||
// or when clear() or erase() are called, the size will report that it
|
||||
// is smaller, but the memory allocated remains intact (call capacity()
|
||||
@ -227,8 +227,8 @@ DWARFCompileUnit::buildAddressRangeTable(DWARFDebugAranges *debug_aranges,
|
||||
// all compile units to stay loaded when they weren't needed. So we can end
|
||||
// up parsing the DWARF and then throwing them all away to keep memory usage
|
||||
// down.
|
||||
const bool clear_dies = extractDIEsIfNeeded(false) > 1;
|
||||
|
||||
const bool clear_dies = extractDIEsIfNeeded(false) > 1 &&
|
||||
clear_dies_if_already_not_parsed;
|
||||
DieArray[0].buildAddressRangeTable(this, debug_aranges);
|
||||
|
||||
// Keep memory down by clearing DIEs if this generate function
|
||||
@ -236,3 +236,13 @@ DWARFCompileUnit::buildAddressRangeTable(DWARFDebugAranges *debug_aranges,
|
||||
if (clear_dies)
|
||||
clearDIEs(true);
|
||||
}
|
||||
|
||||
const DWARFDebugInfoEntryMinimal*
|
||||
DWARFCompileUnit::getFunctionDIEForAddress(int64_t address) {
|
||||
size_t n = extractDIEsIfNeeded(false);
|
||||
for (size_t i = 0; i != n; i++) {
|
||||
if (DieArray[i].addressRangeContainsAddress(this, address))
|
||||
return &DieArray[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -104,6 +104,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);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -140,30 +140,42 @@ DWARFCompileUnit *DWARFContext::getCompileUnitForOffset(uint32_t offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DILineInfo DWARFContext::getLineInfoForAddress(uint64_t address) {
|
||||
DILineInfo DWARFContext::getLineInfoForAddress(uint64_t address,
|
||||
DILineInfoSpecifier specifier) {
|
||||
// First, get the offset of the compile unit.
|
||||
uint32_t cuOffset = getDebugAranges()->findAddress(address);
|
||||
// Retrieve the compile unit.
|
||||
DWARFCompileUnit *cu = getCompileUnitForOffset(cuOffset);
|
||||
if (!cu)
|
||||
return DILineInfo("<invalid>", 0, 0);
|
||||
// Get the line table for this compile unit.
|
||||
const DWARFDebugLine::LineTable *lineTable = getLineTableForCompileUnit(cu);
|
||||
if (!lineTable)
|
||||
return DILineInfo("<invalid>", 0, 0);
|
||||
// Get the index of the row we're looking for in the line table.
|
||||
uint64_t hiPC =
|
||||
cu->getCompileUnitDIE()->getAttributeValueAsUnsigned(cu, DW_AT_high_pc,
|
||||
-1ULL);
|
||||
uint32_t rowIndex = lineTable->lookupAddress(address, hiPC);
|
||||
if (rowIndex == -1U)
|
||||
return DILineInfo("<invalid>", 0, 0);
|
||||
|
||||
// From here, contruct the DILineInfo.
|
||||
const DWARFDebugLine::Row &row = lineTable->Rows[rowIndex];
|
||||
const std::string &fileName = lineTable->Prologue.FileNames[row.File-1].Name;
|
||||
|
||||
return DILineInfo(fileName.c_str(), row.Line, row.Column);
|
||||
return DILineInfo();
|
||||
const char *fileName = "<invalid>";
|
||||
const char *functionName = "<invalid>";
|
||||
uint32_t line = 0;
|
||||
uint32_t column = 0;
|
||||
if (specifier.needs(DILineInfoSpecifier::FunctionName)) {
|
||||
const DWARFDebugInfoEntryMinimal *function_die =
|
||||
cu->getFunctionDIEForAddress(address);
|
||||
if (function_die)
|
||||
functionName = function_die->getSubprogramName(cu);
|
||||
}
|
||||
if (specifier.needs(DILineInfoSpecifier::FileLineInfo)) {
|
||||
// Get the line table for this compile unit.
|
||||
const DWARFDebugLine::LineTable *lineTable = getLineTableForCompileUnit(cu);
|
||||
if (lineTable) {
|
||||
// Get the index of the row we're looking for in the line table.
|
||||
uint64_t hiPC = cu->getCompileUnitDIE()->getAttributeValueAsUnsigned(
|
||||
cu, DW_AT_high_pc, -1ULL);
|
||||
uint32_t rowIndex = lineTable->lookupAddress(address, hiPC);
|
||||
if (rowIndex != -1U) {
|
||||
const DWARFDebugLine::Row &row = lineTable->Rows[rowIndex];
|
||||
// Take file/line info from the line table.
|
||||
fileName = lineTable->Prologue.FileNames[row.File - 1].Name.c_str();
|
||||
line = row.Line;
|
||||
column = row.Column;
|
||||
}
|
||||
}
|
||||
}
|
||||
return DILineInfo(fileName, functionName, line, column);
|
||||
}
|
||||
|
||||
void DWARFContextInMemory::anchor() { }
|
||||
|
@ -66,7 +66,8 @@ public:
|
||||
const DWARFDebugLine::LineTable *
|
||||
getLineTableForCompileUnit(DWARFCompileUnit *cu);
|
||||
|
||||
virtual DILineInfo getLineInfoForAddress(uint64_t address);
|
||||
virtual DILineInfo getLineInfoForAddress(uint64_t address,
|
||||
DILineInfoSpecifier specifier = DILineInfoSpecifier());
|
||||
|
||||
bool isLittleEndian() const { return IsLittleEndian; }
|
||||
|
||||
|
@ -93,6 +93,7 @@ bool DWARFDebugAranges::generate(DWARFContext *ctx) {
|
||||
cu->buildAddressRangeTable(this, true);
|
||||
}
|
||||
}
|
||||
sort(true, /* overlap size */ 0);
|
||||
return !isEmpty();
|
||||
}
|
||||
|
||||
@ -221,4 +222,3 @@ bool DWARFDebugAranges::getMaxRange(uint64_t &LoPC, uint64_t &HiPC) const {
|
||||
HiPC = Aranges.back().HiPC();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -440,3 +440,51 @@ 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);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline const char*
|
||||
getSubprogramNameFromDie(const DWARFCompileUnit *cu,
|
||||
const DWARFDebugInfoEntryMinimal *die) {
|
||||
const char *result = 0;
|
||||
if (!die->isNULL() && die->getTag() == DW_TAG_subprogram) {
|
||||
// Try to get mangled name if possible.
|
||||
result = die->getAttributeValueAsString(cu, DW_AT_MIPS_linkage_name, 0);
|
||||
if (result == 0)
|
||||
result = die->getAttributeValueAsString(cu, DW_AT_linkage_name, 0);
|
||||
if (result == 0)
|
||||
result = die->getAttributeValueAsString(cu, DW_AT_name, 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const char*
|
||||
DWARFDebugInfoEntryMinimal::getSubprogramName(
|
||||
const DWARFCompileUnit *cu) const {
|
||||
if (isNULL() || getTag() != DW_TAG_subprogram)
|
||||
return 0;
|
||||
const char *name = getSubprogramNameFromDie(cu, this);
|
||||
if (name == 0) {
|
||||
// Try to get name from specification DIE.
|
||||
uint32_t ref = getAttributeValueAsReference(cu, DW_AT_specification, -1U);
|
||||
if (ref != -1U) {
|
||||
DWARFDebugInfoEntryMinimal spec_die;
|
||||
if (spec_die.extract(cu, &ref))
|
||||
name = getSubprogramNameFromDie(cu, &spec_die);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
@ -128,6 +128,13 @@ public:
|
||||
|
||||
void buildAddressRangeTable(const DWARFCompileUnit *cu,
|
||||
DWARFDebugAranges *debug_aranges) const;
|
||||
|
||||
bool addressRangeContainsAddress(const DWARFCompileUnit *cu,
|
||||
const uint64_t address) const;
|
||||
|
||||
// If a DIE represents a subroutine, returns its mangled name
|
||||
// (or short name, if mangled is missing). Otherwise returns null.
|
||||
const char* getSubprogramName(const DWARFCompileUnit *cu) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
BIN
test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64
Executable file
BIN
test/DebugInfo/Inputs/dwarfdump-test.elf-x86-64
Executable file
Binary file not shown.
BIN
test/DebugInfo/Inputs/dwarfdump-test2.elf-x86-64
Executable file
BIN
test/DebugInfo/Inputs/dwarfdump-test2.elf-x86-64
Executable file
Binary file not shown.
25
test/DebugInfo/dwarfdump-test.test
Normal file
25
test/DebugInfo/dwarfdump-test.test
Normal file
@ -0,0 +1,25 @@
|
||||
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test.elf-x86-64 \
|
||||
RUN: --address=0x400589 --functions | FileCheck %s -check-prefix MAIN
|
||||
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test.elf-x86-64 \
|
||||
RUN: --address=0x400558 --functions | FileCheck %s -check-prefix FUNCTION
|
||||
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test.elf-x86-64 \
|
||||
RUN: --address=0x4005b6 --functions | FileCheck %s -check-prefix CTOR_WITH_SPEC
|
||||
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test2.elf-x86-64 \
|
||||
RUN: --address=0x4004b8 --functions | FileCheck %s -check-prefix MANY_CU_1
|
||||
RUN: llvm-dwarfdump %p/Inputs/dwarfdump-test2.elf-x86-64 \
|
||||
RUN: --address=0x4004c4 --functions | FileCheck %s -check-prefix MANY_CU_2
|
||||
|
||||
MAIN: main
|
||||
MAIN-NEXT: dwarfdump-test.cc:16:10
|
||||
|
||||
FUNCTION: _Z1fii
|
||||
FUNCTION-NEXT: dwarfdump-test.cc:11:18
|
||||
|
||||
CTOR_WITH_SPEC: _ZN10DummyClassC1Ei
|
||||
CTOR_WITH_SPEC-NEXT: dwarfdump-test.cc:4:30
|
||||
|
||||
MANY_CU_1: a
|
||||
MANY_CU_1-NEXT: a.cc:2:0
|
||||
|
||||
MANY_CU_2: main
|
||||
MANY_CU_2-NEXT: main.cc:4:0
|
@ -39,6 +39,11 @@ static cl::opt<unsigned long long>
|
||||
Address("address", cl::init(-1ULL),
|
||||
cl::desc("Print line information for a given address"));
|
||||
|
||||
static cl::opt<bool>
|
||||
PrintFunctions("functions", cl::init(false),
|
||||
cl::desc("Print function names as well as line information "
|
||||
"for a given address"));
|
||||
|
||||
static void DumpInput(const StringRef &Filename) {
|
||||
OwningPtr<MemoryBuffer> Buff;
|
||||
|
||||
@ -92,7 +97,13 @@ static void DumpInput(const StringRef &Filename) {
|
||||
dictx->dump(outs());
|
||||
} else {
|
||||
// Print line info for the specified address.
|
||||
DILineInfo dli = dictx->getLineInfoForAddress(Address);
|
||||
int spec_flags = DILineInfoSpecifier::FileLineInfo;
|
||||
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';
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user