diff options
author | Ulf Hermann <ulf.hermann@theqtcompany.com> | 2015-03-02 19:23:31 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@theqtcompany.com> | 2015-03-06 13:23:55 +0200 |
commit | 307d1080a4a409458f8b2cfb4e4fc94754d74ff0 (patch) | |
tree | d1a64bb70128dd2501d625770bb49f45257ed684 | |
parent | acea6db3eca27dc3a77f500d075313a89e446d00 (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.cpp | 20 | ||||
-rw-r--r-- | app/perfregisterinfo.h | 5 | ||||
-rw-r--r-- | app/perfunwind.cpp | 23 | ||||
-rw-r--r-- | app/perfunwind.h | 3 |
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, ¤tUnwind); + 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, ¤tUnwind); + } } 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(); |