diff options
author | Eike Ziller <eike.ziller@theqtcompany.com> | 2016-03-23 14:14:54 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@theqtcompany.com> | 2016-03-23 14:14:58 +0100 |
commit | 4d8fe1608b62e9e1697e91a21f5d42c3911cdc6c (patch) | |
tree | 715aa5e861fa96be95a6773935b0923fa485645b | |
parent | ce1c4cca73dfa8374df660e401015f44da509d8f (diff) | |
parent | cd37b339b6562ab67e5cc91009f603569599b177 (diff) |
Merge remote-tracking branch 'origin/4.0'
Change-Id: If61899521e38e744742711b84acbeb31aadd92f2
-rw-r--r-- | 3rdparty/elfutils/backends/aarch64/aarch64.pro | 3 | ||||
-rw-r--r-- | 3rdparty/elfutils/backends/aarch64_init.c | 1 | ||||
-rw-r--r-- | 3rdparty/elfutils/backends/aarch64_unwind.c | 78 | ||||
-rw-r--r-- | 3rdparty/elfutils/backends/arm_unwind.c | 8 | ||||
-rw-r--r-- | 3rdparty/elfutils/backends/x86_64_unwind.c | 2 | ||||
-rw-r--r-- | app/perfunwind.cpp | 74 | ||||
-rw-r--r-- | app/perfunwind.h | 6 |
7 files changed, 130 insertions, 42 deletions
diff --git a/3rdparty/elfutils/backends/aarch64/aarch64.pro b/3rdparty/elfutils/backends/aarch64/aarch64.pro index 5489b1d..a53be98 100644 --- a/3rdparty/elfutils/backends/aarch64/aarch64.pro +++ b/3rdparty/elfutils/backends/aarch64/aarch64.pro @@ -8,7 +8,8 @@ SOURCES += \ ../aarch64_initreg.c \ ../aarch64_regs.c \ ../aarch64_retval.c \ - ../aarch64_symbol.c + ../aarch64_symbol.c \ + ../aarch64_unwind.c HEADERS += \ ../aarch64_reloc.def diff --git a/3rdparty/elfutils/backends/aarch64_init.c b/3rdparty/elfutils/backends/aarch64_init.c index b0fd17a..8a96f73 100644 --- a/3rdparty/elfutils/backends/aarch64_init.c +++ b/3rdparty/elfutils/backends/aarch64_init.c @@ -64,6 +64,7 @@ aarch64_init (elf, machine, eh, ehlen) + ALT_FRAME_RETURN_COLUMN (used when LR isn't used) = 97 DWARF regs. */ eh->frame_nregs = 97; HOOK (eh, set_initial_registers_tid); + HOOK (eh, unwind); return MODVERSION; } diff --git a/3rdparty/elfutils/backends/aarch64_unwind.c b/3rdparty/elfutils/backends/aarch64_unwind.c new file mode 100644 index 0000000..5560c58 --- /dev/null +++ b/3rdparty/elfutils/backends/aarch64_unwind.c @@ -0,0 +1,78 @@ +/* Get previous frame state for an existing frame state. + Copyright (C) 2016 The Qt Company Ltd. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see <http://www.gnu.org/licenses/>. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdlib.h> +#include <assert.h> + +#define BACKEND aarch64_ +#include "libebl_CPU.h" +#include <stdio.h> + +/* There was no CFI. Maybe we happen to have a frame pointer and can unwind from that? */ + +bool +aarch64_unwind (Ebl *ebl __attribute__ ((unused)), Dwarf_Addr pc __attribute__ ((unused)), + ebl_tid_registers_t *setfunc, ebl_tid_registers_get_t *getfunc, + ebl_pid_memory_read_t *readfunc, void *arg, bool *signal_framep) +{ + const int fpReg = 29; + const int lrReg = 30; + const int spReg = 31; + + Dwarf_Word fp; + if (!getfunc(fpReg, 1, &fp, arg) || fp == 0) + return false; + + Dwarf_Word lr; + if (!getfunc(lrReg, 1, &lr, arg)) + return false; + + Dwarf_Word newFp, newLr; + if (!readfunc(fp, &newFp, arg)) + return false; + fp += 8; + if (!readfunc(fp, &newLr, arg)) + return false; + + fp += 8; + if (!setfunc(spReg, 1, &fp, arg)) + return false; + if (!setfunc(fpReg, 1, &newFp, arg)) + return false; + if (!setfunc(-1, 1, &newLr, arg)) + return false; + if (!setfunc(lrReg, 1, &newLr, arg)) + return false; + + *signal_framep = false; + return true; +} diff --git a/3rdparty/elfutils/backends/arm_unwind.c b/3rdparty/elfutils/backends/arm_unwind.c index 4d90db9..ea725cd 100644 --- a/3rdparty/elfutils/backends/arm_unwind.c +++ b/3rdparty/elfutils/backends/arm_unwind.c @@ -50,18 +50,18 @@ arm_unwind (Ebl *ebl __attribute__ ((unused)), Dwarf_Addr pc, const int lrReg = 14; const int spReg = 13; - Dwarf_Word fp = 0, lr = 0, sp = 0; - if (!getfunc(fpReg, 1, &fp, arg)) + Dwarf_Word fp = 0, lr = 0, sp = 0; // have to be initialized because registers are 32bit only + if (!getfunc(fpReg, 1, &fp, arg) || fp == 0) return false; if (!getfunc(lrReg, 1, &lr, arg)) return false; if (!getfunc(spReg, 1, &sp, arg)) return false; - Dwarf_Word newFp = 0, newLr = 0; + Dwarf_Word newFp; // no initialization needed because we read 64bit if (!readfunc(fp, &newFp, arg)) return false; - newLr = newFp >> 32; + Dwarf_Word newLr = newFp >> 32; newFp &= 0xffffffff; fp += 8; diff --git a/3rdparty/elfutils/backends/x86_64_unwind.c b/3rdparty/elfutils/backends/x86_64_unwind.c index 8b8b94a..2ad6102 100644 --- a/3rdparty/elfutils/backends/x86_64_unwind.c +++ b/3rdparty/elfutils/backends/x86_64_unwind.c @@ -48,7 +48,7 @@ x86_64_unwind (Ebl *ebl __attribute__ ((unused)), Dwarf_Addr pc __attribute__ (( const int spReg = 7; Dwarf_Word fp; - if (!getfunc(fpReg, 1, &fp, arg)) + if (!getfunc(fpReg, 1, &fp, arg) || fp == 0) return false; Dwarf_Word prev_fp; diff --git a/app/perfunwind.cpp b/app/perfunwind.cpp index ae13b92..4170a8d 100644 --- a/app/perfunwind.cpp +++ b/app/perfunwind.cpp @@ -318,38 +318,46 @@ static const Dwfl_Thread_Callbacks callbacks = { nextThread, NULL, memoryRead, setInitialRegisters, NULL, NULL }; -static PerfUnwind::Frame lookupSymbol(PerfUnwind::UnwindInfo *ui, Dwfl *dwfl, Dwarf_Addr ip, - bool isKernel) +PerfUnwind::Frame PerfUnwind::lookupSymbol(Dwarf_Addr ip, bool isKernel) { + quint32 pid = currentUnwind.sample->pid(); Dwfl_Module *mod = dwfl ? dwfl_addrmodule(dwfl, ip) : 0; - QByteArray symname; QByteArray elfFile; - QByteArray srcFile; - int line = 0; - int column = 0; - GElf_Sym sym; - GElf_Off off = 0; if (dwfl && !mod) { - const PerfUnwind::ElfInfo *elfInfo = 0; - mod = ui->unwind->reportElf(ip, isKernel ? PerfUnwind::s_kernelPid : ui->sample->pid(), - &elfInfo); + const ElfInfo *elfInfo = 0; + mod = reportElf(ip, isKernel ? s_kernelPid : pid, &elfInfo); if (!mod && elfInfo) elfFile = elfInfo->file.fileName().toLocal8Bit(); } - Dwarf_Addr adjusted = (ui->unwind->architecture() != PerfRegisterInfo::ARCH_ARM || (ip & 1)) ? - ip : ip + 1; + Dwarf_Addr adjusted = (architecture() != PerfRegisterInfo::ARCH_ARM || (ip & 1)) ? ip : ip + 1; + if (mod) + elfFile = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0); + + QHash<Dwarf_Addr, Frame> &processAddrCache = addrCache[pid]; + auto it = processAddrCache.constFind(ip); + // Check for elfFile as it might have loaded a different file to the same address in the mean + // time. We don't consider the case of loading the same file again at a different, overlapping + // offset. + if (it != processAddrCache.constEnd() && it->elfFile == elfFile) + return *it; + + QByteArray symname; + QByteArray srcFile; + int line = 0; + int column = 0; + GElf_Sym sym; + GElf_Off off = 0; + if (mod) { // For addrinfo we need the raw pointer into symtab, so we need to adjust ourselves. symname = dwfl_module_addrinfo(mod, adjusted, &off, &sym, 0, 0, 0); if (off == adjusted) // no symbol found off = 0; - else if (ui->unwind->granularity() == PerfUnwind::Function) + else if (granularity() == Function) adjusted -= off; - elfFile = dwfl_module_info(mod, 0, 0, 0, 0, 0, 0, 0); - Dwfl_Line *srcLine = dwfl_module_getsrc(mod, adjusted); if (srcLine) srcFile = dwfl_lineinfo(srcLine, NULL, &line, &column, NULL, NULL); @@ -360,20 +368,23 @@ static PerfUnwind::Frame lookupSymbol(PerfUnwind::UnwindInfo *ui, Dwfl *dwfl, Dw int status = -1; if (symname[0] == '_' && symname[1] == 'Z') demangled = abi::__cxa_demangle(symname, 0, 0, &status); - else if (ui->unwind->architecture() == PerfRegisterInfo::ARCH_ARM && symname[0] == '$' + else if (architecture() == PerfRegisterInfo::ARCH_ARM && symname[0] == '$' && (symname[1] == 'a' || symname[1] == 't') && symname[2] == '\0') - ui->isInterworking = true; + currentUnwind.isInterworking = true; // Adjust it back. The symtab entries are 1 off for all practical purposes. - PerfUnwind::Frame frame(adjusted, isKernel, status == 0 ? QByteArray(demangled) : symname, - elfFile, srcFile, line, column); + Frame frame(adjusted, isKernel, status == 0 ? QByteArray(demangled) : symname, elfFile, + srcFile, line, column); free(demangled); + processAddrCache.insert(ip, frame); return frame; } else { - symname = ui->unwind->symbolFromPerfMap(adjusted, ui->sample->pid(), &off); - if (ui->unwind->granularity() == PerfUnwind::Function) + symname = symbolFromPerfMap(adjusted, pid, &off); + if (granularity() == Function) adjusted -= off; - return PerfUnwind::Frame(adjusted, isKernel, symname, elfFile, srcFile, line, column); + Frame frame(adjusted, isKernel, symname, elfFile, srcFile, line, column); + processAddrCache.insert(ip, frame); + return frame; } } @@ -392,12 +403,8 @@ static int frameCallback(Dwfl_Frame *state, void *arg) Dwarf_Addr pc_adjusted = pc - (isactivation ? 0 : 1); - /* Get PC->SYMNAME. */ - Dwfl_Thread *thread = dwfl_frame_thread (state); - Dwfl *dwfl = dwfl_thread_dwfl (thread); - // isKernel = false as unwinding generally only works on user code - ui->frames.append(lookupSymbol(ui, dwfl, pc_adjusted, false)); + ui->frames.append(ui->unwind->lookupSymbol(pc_adjusted, false)); return DWARF_CB_OK; } @@ -438,11 +445,10 @@ void PerfUnwind::resolveCallchain() // sometimes it skips the first user frame. if (i == 0 && !isKernel && ip != currentUnwind.sample->ip()) - currentUnwind.frames.append(lookupSymbol(¤tUnwind, dwfl, - currentUnwind.sample->ip(), false)); + currentUnwind.frames.append(lookupSymbol(currentUnwind.sample->ip(), false)); if (ip <= PERF_CONTEXT_MAX) - currentUnwind.frames.append(lookupSymbol(¤tUnwind, dwfl, ip, isKernel)); + currentUnwind.frames.append(lookupSymbol(ip, isKernel)); } } @@ -491,10 +497,8 @@ void PerfUnwind::analyze(const PerfRecordSample &sample) unwindStack(); } // If nothing was found, at least look up the IP - if (currentUnwind.frames.isEmpty()) { - currentUnwind.frames.append(lookupSymbol(¤tUnwind, dwfl, sample.ip(), - ipIsInKernelSpace(sample.ip()))); - } + if (currentUnwind.frames.isEmpty()) + currentUnwind.frames.append(lookupSymbol(sample.ip(), ipIsInKernelSpace(sample.ip()))); QByteArray buffer; QDataStream(&buffer, QIODevice::WriteOnly) diff --git a/app/perfunwind.h b/app/perfunwind.h index f7a3520..2b07131 100644 --- a/app/perfunwind.h +++ b/app/perfunwind.h @@ -64,7 +64,7 @@ public: struct UnwindInfo { UnwindInfo() : frames(0), unwind(0), sample(0), broken(false), isInterworking(false) {} QVector<PerfUnwind::Frame> frames; - const PerfUnwind *unwind; + PerfUnwind *unwind; const PerfRecordSample *sample; bool broken; bool isInterworking; @@ -120,6 +120,8 @@ public: QByteArray symbolFromPerfMap(quint64 ip, quint32 pid, GElf_Off *offset) const; void updatePerfMap(quint32 pid); + Frame lookupSymbol(Dwarf_Addr ip, bool isKernel); + private: enum CallchainContext { @@ -159,6 +161,8 @@ private: QHash<quint32, QMap<quint64, ElfInfo> > elfs; // The inner map needs to be sorted QHash<quint32, PerfMap> perfMaps; QList<PerfRecordSample> sampleBuffer; + QHash<quint32, QHash<Dwarf_Addr, Frame> > addrCache; + uint sampleBufferSize; Granularity sampleGranularity; |