summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-03-02 19:23:31 +0100
committerUlf Hermann <ulf.hermann@theqtcompany.com>2015-03-06 13:23:55 +0200
commit307d1080a4a409458f8b2cfb4e4fc94754d74ff0 (patch)
treed1a64bb70128dd2501d625770bb49f45257ed684
parentacea6db3eca27dc3a77f500d075313a89e446d00 (diff)
Unwind from DWARF/THUMB interworking symbols
Those are generally only 1 instruction long and don't touch the stack. So, we can just replace IP with LR and get the rest of the stack. Change-Id: I9883ccfbbe522e665de073c5ed259db78f46c0a9 Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
-rw-r--r--app/perfregisterinfo.cpp20
-rw-r--r--app/perfregisterinfo.h5
-rw-r--r--app/perfunwind.cpp23
-rw-r--r--app/perfunwind.h3
4 files changed, 47 insertions, 4 deletions
diff --git a/app/perfregisterinfo.cpp b/app/perfregisterinfo.cpp
index 0703881..059079e 100644
--- a/app/perfregisterinfo.cpp
+++ b/app/perfregisterinfo.cpp
@@ -62,3 +62,23 @@ const uint PerfRegisterInfo::s_perfIp[ARCH_INVALID] = {
const uint PerfRegisterInfo::s_perfSp[ARCH_INVALID] = {
13, 31, 0xffff, 0xffff, 0xffff, 0xffff, 7
};
+
+const uint PerfRegisterInfo::s_dwarfLr[ARCH_INVALID][s_numAbis] = {
+ {14, 14},
+ {30, 30},
+ {0xffff, 0xffff},
+ {0xffff, 0xffff},
+ {0xffff, 0xffff},
+ {0xffff, 0xffff},
+ {0xffff, 0xffff}
+};
+
+const uint PerfRegisterInfo::s_dwarfIp[ARCH_INVALID][s_numAbis] = {
+ {15, 15},
+ {32, 32},
+ {0xffff, 0xffff},
+ {0xffff, 0xffff},
+ {0xffff, 0xffff},
+ {0xffff, 0xffff},
+ {8, 16}
+};
diff --git a/app/perfregisterinfo.h b/app/perfregisterinfo.h
index c834694..8897591 100644
--- a/app/perfregisterinfo.h
+++ b/app/perfregisterinfo.h
@@ -51,6 +51,11 @@ public:
static const uint s_perfIp[ARCH_INVALID];
// location of SP register or equivalent in perf register layout for each arch/abi
static const uint s_perfSp[ARCH_INVALID];
+
+ // location of LR register or equivalent in dwarf register layout for each arch/abi
+ static const uint s_dwarfLr[ARCH_INVALID][s_numAbis];
+ // location of IP register or equivalent in dwarf register layout for each arch/abi
+ static const uint s_dwarfIp[ARCH_INVALID][s_numAbis];
};
#endif // PERFREGISTERINFO_H
diff --git a/app/perfunwind.cpp b/app/perfunwind.cpp
index 1e3bd45..afee2fb 100644
--- a/app/perfunwind.cpp
+++ b/app/perfunwind.cpp
@@ -235,6 +235,10 @@ bool setInitialRegisters(Dwfl_Thread *thread, void *arg)
dwarfRegs[i] = ui->sample->registerValue(
PerfRegisterInfo::s_perfToDwarf[architecture][abi][i]);
+ if (ui->isInterworking) // Go one frame up to get the rest of the stack at interworking veneers.
+ dwarfRegs[PerfRegisterInfo::s_dwarfIp[architecture][abi]] =
+ dwarfRegs[PerfRegisterInfo::s_dwarfLr[architecture][abi]];
+
return dwfl_thread_state_registers(thread, 0, numRegs, dwarfRegs);
}
@@ -274,9 +278,11 @@ static PerfUnwind::Frame lookupSymbol(PerfUnwind::UnwindInfo *ui, Dwfl *dwfl, Dw
if (symname) {
char *demangled = NULL;
int status = -1;
- if (symname[0] == '_' && symname[1] == 'Z') {
+ if (symname[0] == '_' && symname[1] == 'Z')
demangled = abi::__cxa_demangle (symname, 0, 0, &status);
- }
+ else if (ui->unwind->architecture() == PerfRegisterInfo::ARCH_ARM && symname[0] == '$'
+ && (symname[1] == 'a' || symname[1] == 't') && symname[2] == '\0')
+ ui->isInterworking = true;
// Adjust it back. The symtab entries are 1 off for all practical purposes.
return PerfUnwind::Frame((do_adjust && (sym.st_value & 1)) ? sym.st_value - 1 :
@@ -296,8 +302,8 @@ static int frameCallback(Dwfl_Frame *state, void *arg)
bool isactivation;
if (!dwfl_frame_pc(state, &pc, &isactivation)) {
- qWarning() << dwfl_errmsg(dwfl_errno());
ui->broken = true;
+ qWarning() << dwfl_errmsg(dwfl_errno()) << ui->broken;
return DWARF_CB_ABORT;
}
@@ -315,6 +321,16 @@ static int frameCallback(Dwfl_Frame *state, void *arg)
void PerfUnwind::unwindStack()
{
dwfl_getthread_frames(dwfl, currentUnwind.sample->pid(), frameCallback, &currentUnwind);
+ if (currentUnwind.isInterworking && currentUnwind.frames.length() == 1) {
+ // If it's an ARM interworking veneer, we assume that we can find a return address in LR and
+ // no stack has been used for the veneer itself.
+ // The reasoning is that any symbol jumped to by the veneer has to work with or without
+ // using the veneer. It needs a valid return address and when it returns the stack pointer
+ // must be the same in both cases. Thus, the veneer cannot touch the stack pointer and there
+ // has to be a return address in LR, provided by the caller.
+ // So, just try again, and make setInitialRegisters use LR for IP.
+ dwfl_getthread_frames(dwfl, currentUnwind.sample->pid(), frameCallback, &currentUnwind);
+ }
}
void PerfUnwind::resolveCallchain()
@@ -378,6 +394,7 @@ void PerfUnwind::analyze(const PerfRecordSample &sample)
}
currentUnwind.broken = false;
+ currentUnwind.isInterworking = false;
currentUnwind.sample = &sample;
currentUnwind.frames.clear();
if (sample.callchain().length() > 0)
diff --git a/app/perfunwind.h b/app/perfunwind.h
index e954ee7..b87f357 100644
--- a/app/perfunwind.h
+++ b/app/perfunwind.h
@@ -55,11 +55,12 @@ public:
};
struct UnwindInfo {
- UnwindInfo() : frames(0), unwind(0), sample(0), broken(false) {}
+ UnwindInfo() : frames(0), unwind(0), sample(0), broken(false), isInterworking(false) {}
QVector<PerfUnwind::Frame> frames;
const PerfUnwind *unwind;
const PerfRecordSample *sample;
bool broken;
+ bool isInterworking;
};
static const quint32 s_kernelPid = std::numeric_limits<quint32>::max();