summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/perfsymboltable.cpp118
1 files changed, 85 insertions, 33 deletions
diff --git a/app/perfsymboltable.cpp b/app/perfsymboltable.cpp
index 01f698b..445528c 100644
--- a/app/perfsymboltable.cpp
+++ b/app/perfsymboltable.cpp
@@ -771,6 +771,77 @@ static QByteArray fakeSymbolFromSection(Dwfl_Module *mod, Dwarf_Addr addr)
return sym;
}
+// based on MIT licensed https://github.com/bombela/backward-cpp
+static bool die_has_pc(Dwarf_Die* die, Dwarf_Addr pc)
+{
+ Dwarf_Addr low, high;
+
+ // continuous range
+ if (dwarf_hasattr(die, DW_AT_low_pc) && dwarf_hasattr(die, DW_AT_high_pc)) {
+ if (dwarf_lowpc(die, &low) != 0)
+ return false;
+ if (dwarf_highpc(die, &high) != 0) {
+ Dwarf_Attribute attr_mem;
+ Dwarf_Attribute* attr = dwarf_attr(die, DW_AT_high_pc, &attr_mem);
+ Dwarf_Word value;
+ if (dwarf_formudata(attr, &value) != 0)
+ return false;
+ high = low + value;
+ }
+ return pc >= low && pc < high;
+ }
+
+ // non-continuous range.
+ Dwarf_Addr base;
+ ptrdiff_t offset = 0;
+ while ((offset = dwarf_ranges(die, offset, &base, &low, &high)) > 0) {
+ if (pc >= low && pc < high)
+ return true;
+ }
+ return false;
+}
+
+static bool find_fundie_by_pc(Dwarf_Die* parent_die, Dwarf_Addr pc)
+{
+ Dwarf_Die die;
+ if (dwarf_child(parent_die, &die) != 0)
+ return false;
+
+ do {
+ switch (dwarf_tag(&die)) {
+ case DW_TAG_subprogram:
+ case DW_TAG_inlined_subroutine:
+ if (die_has_pc(&die, pc))
+ return true;
+ };
+ bool declaration = false;
+ Dwarf_Attribute attr_mem;
+ dwarf_formflag(dwarf_attr(&die, DW_AT_declaration, &attr_mem), &declaration);
+ if (!declaration) {
+ // let's be curious and look deeper in the tree,
+ // function are not necessarily at the first level, but
+ // might be nested inside a namespace, structure etc.
+ if (find_fundie_by_pc(&die, pc))
+ return true;
+ }
+ } while (dwarf_siblingof(&die, &die) == 0);
+ return false;
+}
+
+Dwarf_Die *find_die(Dwfl_Module *mod, Dwarf_Addr addr, Dwarf_Addr *bias)
+{
+ auto die = dwfl_module_addrdie(mod, addr, bias);
+ if (die)
+ return die;
+
+ while ((die = dwfl_module_nextcu(mod, die, bias))) {
+ if (find_fundie_by_pc(die, addr - *bias))
+ return die;
+ }
+
+ return nullptr;
+}
+
int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel,
bool *isInterworking)
{
@@ -805,38 +876,6 @@ int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel,
// For addrinfo we need the raw pointer into symtab, so we need to adjust ourselves.
symname = dwfl_module_addrinfo(mod, addressLocation.address, &off, &sym, nullptr, nullptr,
nullptr);
- Dwfl_Line *srcLine = dwfl_module_getsrc(mod, addressLocation.address);
- if (srcLine) {
- const QByteArray file = dwfl_lineinfo(srcLine, nullptr, &addressLocation.line,
- &addressLocation.column, nullptr, nullptr);
- addressLocation.file = m_unwind->resolveString(file);
- } else {
- Dwarf_Addr bias = 0;
- if (Dwarf *dwarf = dwfl_module_getdwarf(mod, &bias)) {
- const quint64 adjusted = addressLocation.address - bias;
- size_t headerSize = 0;
- Dwarf_Off nextOffset = 0;
- for (Dwarf_Off offset = 0;
- dwarf_nextcu(dwarf, offset, &nextOffset, &headerSize,
- nullptr, nullptr, nullptr) == 0;
- offset = nextOffset) {
- Dwarf_Die cudieMemory;
- Dwarf_Die *cudie = dwarf_offdie(dwarf, offset + headerSize, &cudieMemory);
-
- if (!cudie || !dwarf_haspc(cudie, adjusted))
- continue;
-
- if (Dwarf_Line *line = dwarf_getsrc_die(cudie, adjusted)) {
- const QByteArray file = dwarf_linesrc(line, nullptr, nullptr);
- addressLocation.file = m_unwind->resolveString(file);
- dwarf_lineno(line, &addressLocation.line);
- dwarf_linecol(line, &addressLocation.column);
- }
-
- break;
- }
- }
- }
if (off == addressLocation.address) {// no symbol found
symname = fakeSymbolFromSection(mod, addressLocation.address);
@@ -844,7 +883,20 @@ int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel,
} else {
Dwarf_Addr bias = 0;
functionLocation.address -= off; // in case we don't find anything better
- Dwarf_Die *die = dwfl_module_addrdie(mod, addressLocation.address, &bias);
+ Dwarf_Die *die = find_die(mod, addressLocation.address, &bias);
+
+ if (die) {
+ auto srcloc = dwarf_getsrc_die(die, addressLocation.address - bias);
+ if (srcloc) {
+ const char* srcfile = dwarf_linesrc(srcloc, nullptr, nullptr);
+ if (srcfile) {
+ const QByteArray file = srcfile;
+ addressLocation.file = m_unwind->resolveString(file);
+ dwarf_lineno(srcloc, &addressLocation.line);
+ dwarf_linecol(srcloc, &addressLocation.column);
+ }
+ }
+ }
Dwarf_Die *subroutine = nullptr;
Dwarf_Die *scopes = nullptr;