summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Ziller <eike.ziller@theqtcompany.com>2016-03-23 14:14:54 +0100
committerEike Ziller <eike.ziller@theqtcompany.com>2016-03-23 14:14:58 +0100
commit4d8fe1608b62e9e1697e91a21f5d42c3911cdc6c (patch)
tree715aa5e861fa96be95a6773935b0923fa485645b
parentce1c4cca73dfa8374df660e401015f44da509d8f (diff)
parentcd37b339b6562ab67e5cc91009f603569599b177 (diff)
Merge remote-tracking branch 'origin/4.0'
-rw-r--r--3rdparty/elfutils/backends/aarch64/aarch64.pro3
-rw-r--r--3rdparty/elfutils/backends/aarch64_init.c1
-rw-r--r--3rdparty/elfutils/backends/aarch64_unwind.c78
-rw-r--r--3rdparty/elfutils/backends/arm_unwind.c8
-rw-r--r--3rdparty/elfutils/backends/x86_64_unwind.c2
-rw-r--r--app/perfunwind.cpp74
-rw-r--r--app/perfunwind.h6
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(&currentUnwind, dwfl,
- currentUnwind.sample->ip(), false));
+ currentUnwind.frames.append(lookupSymbol(currentUnwind.sample->ip(), false));
if (ip <= PERF_CONTEXT_MAX)
- currentUnwind.frames.append(lookupSymbol(&currentUnwind, 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(&currentUnwind, 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;