summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-03-04 14:55:46 +0100
committerUlf Hermann <ulf.hermann@theqtcompany.com>2015-04-02 17:50:50 +0300
commitddc660b4a29c7960a18f94d8bb967e41643db8bb (patch)
tree9f7c7bbcb98c3e0c2469f3e74089a1f7f3a9cebd
parent652561a29f37970722d2231f759631e0932e0288 (diff)
Split mmaps when they get overridden and report files we don't find
The application can mmap() multiple elf files on top of each other. The effect is that the new mmaps replace those parts of the old ones of which the address ranges overlap. We have to replay this in the perfparser to find the right symbols. Change-Id: I283f6a789a7818b50a865e0a0815fdb9bfb7ff42 Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
-rw-r--r--app/perfunwind.cpp50
-rw-r--r--app/perfunwind.h17
2 files changed, 50 insertions, 17 deletions
diff --git a/app/perfunwind.cpp b/app/perfunwind.cpp
index 2afc124..6d20838 100644
--- a/app/perfunwind.cpp
+++ b/app/perfunwind.cpp
@@ -70,9 +70,33 @@ void PerfUnwind::registerElf(const PerfRecordMmap &mmap)
{
QMap<quint64, ElfInfo> &procElfs = elfs[mmap.pid()];
- // No point in mapping the same file twice
- if (procElfs.find(mmap.addr()) != procElfs.end())
- return;
+ auto i = procElfs.upperBound(mmap.addr());
+ if (i != procElfs.begin())
+ --i;
+ while (i != procElfs.end() && i.key() < mmap.addr() + mmap.len()) {
+ if (i.key() + i.value().length <= mmap.addr()) {
+ ++i;
+ continue;
+ }
+
+ if (i.key() + i.value().length > mmap.addr() + mmap.len()) {
+ // Move or copy the original mmap to the part after the new mmap. The new length is the
+ // difference between the end points (begin + length) of the two. The original mmap
+ // is either removed or shortened by the following if/else construct.
+ procElfs.insert(mmap.addr() + mmap.len(),
+ ElfInfo(i.value().file,
+ i.key() + i.value().length - mmap.addr() - mmap.len()));
+ }
+
+ if (i.key() >= mmap.addr()) {
+ i = procElfs.erase(i);
+ } else {
+ i.value().length = mmap.addr() - i.key();
+ ++i;
+ }
+
+ lastPid = -1; // Throw out the dwfl state
+ }
QString fileName = QFileInfo(mmap.filename()).fileName();
QFileInfo path;
@@ -97,8 +121,7 @@ void PerfUnwind::registerElf(const PerfRecordMmap &mmap)
if (path.isFile())
procElfs[mmap.addr()] = ElfInfo(path, mmap.len());
else
- qWarning() << "cannot find file to report for" << QString::fromLocal8Bit(mmap.filename());
-
+ procElfs[mmap.addr()] = ElfInfo(QFileInfo(mmap.filename()), mmap.len(), false);
}
void sendBuffer(QIODevice *output, const QByteArray &buffer)
@@ -119,7 +142,7 @@ void PerfUnwind::comm(PerfRecordComm &comm)
sendBuffer(output, buffer);
}
-Dwfl_Module *PerfUnwind::reportElf(quint64 ip, quint32 pid) const
+Dwfl_Module *PerfUnwind::reportElf(quint64 ip, quint32 pid, const ElfInfo **info) const
{
QHash<quint32, QMap<quint64, ElfInfo> >::ConstIterator elfsIt = elfs.find(pid);
if (elfsIt == elfs.end()) {
@@ -145,6 +168,10 @@ Dwfl_Module *PerfUnwind::reportElf(quint64 ip, quint32 pid) const
// ^ We don't have to do this here as libdw is supposed to handle it from version 0.160.
if (i != procElfs.end() && i.key() + i.value().length > ip) {
+ if (info)
+ *info = &i.value();
+ if (!i.value().found)
+ return 0;
Dwfl_Module *ret = dwfl_report_elf(
dwfl, i.value().file.fileName().toLocal8Bit().constData(),
i.value().file.absoluteFilePath().toLocal8Bit().constData(), -1, i.key(),
@@ -264,9 +291,6 @@ static PerfUnwind::Frame lookupSymbol(PerfUnwind::UnwindInfo *ui, Dwfl *dwfl, Dw
{
Dwfl_Module *mod = dwfl_addrmodule (dwfl, ip);
const char *symname = NULL;
- if (!mod)
- mod = ui->unwind->reportElf(ip, isKernel ? PerfUnwind::s_kernelPid : ui->unwind->pid());
-
const char *elfFile = NULL;
const char *srcFile = NULL;
int line = 0;
@@ -274,6 +298,14 @@ static PerfUnwind::Frame lookupSymbol(PerfUnwind::UnwindInfo *ui, Dwfl *dwfl, Dw
GElf_Sym sym;
GElf_Off off;
+ if (!mod) {
+ const PerfUnwind::ElfInfo *elfInfo = 0;
+ mod = ui->unwind->reportElf(ip, isKernel ? PerfUnwind::s_kernelPid : ui->unwind->pid(),
+ &elfInfo);
+ if (!mod && elfInfo)
+ elfFile = elfInfo->file.fileName().toLocal8Bit();
+ }
+
bool do_adjust = (ui->unwind->architecture() == PerfRegisterInfo::ARCH_ARM);
if (mod) {
Dwarf_Addr adjusted = (!do_adjust || (ip & 1)) ? ip : ip + 1;
diff --git a/app/perfunwind.h b/app/perfunwind.h
index f84623b..3a013a5 100644
--- a/app/perfunwind.h
+++ b/app/perfunwind.h
@@ -64,6 +64,14 @@ public:
bool isInterworking;
};
+ struct ElfInfo {
+ ElfInfo(const QFileInfo &file = QFileInfo(), quint64 length = 0, bool found = true) :
+ file(file), length(length), found(found) {}
+ QFileInfo file;
+ quint64 length;
+ bool found;
+ };
+
static const quint32 s_kernelPid = std::numeric_limits<quint32>::max();
static const int s_maxFrames = 64;
@@ -81,7 +89,7 @@ public:
void comm(PerfRecordComm &comm);
quint32 pid() const { return lastPid; }
- Dwfl_Module *reportElf(quint64 ip, quint32 pid) const;
+ Dwfl_Module *reportElf(quint64 ip, quint32 pid, const ElfInfo **info = 0) const;
void analyze(const PerfRecordSample &sample);
void fork(const PerfRecordFork &sample);
@@ -124,13 +132,6 @@ private:
// binaries and debug symbols.
QString appPath;
- struct ElfInfo {
- ElfInfo(const QFileInfo &file = QFileInfo(), quint64 length = 0) :
- file(file), length(length) {}
- QFileInfo file;
- quint64 length;
- };
-
QHash<quint32, QMap<quint64, ElfInfo> > elfs; // The inner map needs to be sorted
void unwindStack();