diff options
author | Ulf Hermann <ulf.hermann@theqtcompany.com> | 2015-11-03 13:17:09 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@theqtcompany.com> | 2016-03-04 12:21:52 +0000 |
commit | f53e88918713437d84a145649dc4be671ff78fe1 (patch) | |
tree | 2843e483bdf6039b17c6403212a522bad0296d88 | |
parent | 65f15b411e1125c1acfd919915e3c41d5e8b8a0c (diff) |
Resolve symbols from perf.map files
perf.map files are a simple way of mapping names to addresses if
proper debug information is absent. perf itself parses them. Some
languages (Java, QML if given the QV4_PROFILE_WRITE_PERF_MAP
environment variable, possibly others) create them.
This change is of somewhat limited utility, for two reasons:
1. We can only read the files if the process is running on the same
machine.
2. If running in dwarf mode (ie most of the time) we won't be able
to unwind any further from such symbols. So they can only appear
at the "bottom" of the stack.
Still, it might be helpful to some.
Change-Id: I7126ce7db06e56df82d60122a5c78abab9dcfcbb
Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
-rw-r--r-- | app/perfunwind.cpp | 66 | ||||
-rw-r--r-- | app/perfunwind.h | 18 |
2 files changed, 79 insertions, 5 deletions
diff --git a/app/perfunwind.cpp b/app/perfunwind.cpp index 441b456..139b5f4 100644 --- a/app/perfunwind.cpp +++ b/app/perfunwind.cpp @@ -296,9 +296,9 @@ static PerfUnwind::Frame lookupSymbol(PerfUnwind::UnwindInfo *ui, Dwfl *dwfl, Dw bool isKernel) { Dwfl_Module *mod = dwfl ? dwfl_addrmodule(dwfl, ip) : 0; - const char *symname = NULL; - const char *elfFile = NULL; - const char *srcFile = NULL; + QByteArray symname; + QByteArray elfFile; + QByteArray srcFile; int line = 0; int column = 0; GElf_Sym sym; @@ -329,7 +329,7 @@ static PerfUnwind::Frame lookupSymbol(PerfUnwind::UnwindInfo *ui, Dwfl *dwfl, Dw srcFile = dwfl_lineinfo(srcLine, NULL, &line, &column, NULL, NULL); } - if (symname) { + if (!symname.isEmpty()) { char *demangled = NULL; int status = -1; if (symname[0] == '_' && symname[1] == 'Z') @@ -339,11 +339,14 @@ static PerfUnwind::Frame lookupSymbol(PerfUnwind::UnwindInfo *ui, Dwfl *dwfl, Dw ui->isInterworking = true; // Adjust it back. The symtab entries are 1 off for all practical purposes. - PerfUnwind::Frame frame(adjusted, isKernel, status == 0 ? demangled : symname, + PerfUnwind::Frame frame(adjusted, isKernel, status == 0 ? QByteArray(demangled) : symname, elfFile, srcFile, line, column); free(demangled); return frame; } else { + symname = ui->unwind->symbolFromPerfMap(adjusted, ui->sample->pid(), &off); + if (ui->unwind->granularity() == PerfUnwind::Function) + adjusted -= off; return PerfUnwind::Frame(adjusted, isKernel, symname, elfFile, srcFile, line, column); } } @@ -453,6 +456,7 @@ void PerfUnwind::analyze(const PerfRecordSample &sample) if (!dwfl_addrmodule (dwfl, sample.ip())) reportElf(sample.ip(), sample.pid()); } + updatePerfMap(sample.pid()); if (sample.callchain().length() > 0) resolveCallchain(); @@ -490,3 +494,55 @@ void PerfUnwind::exit(const PerfRecordExit &sample) << QVector<PerfUnwind::Frame>(); sendBuffer(output, buffer); } + +bool operator<(const PerfUnwind::PerfMap::Symbol &a, const PerfUnwind::PerfMap::Symbol &b) +{ + return a.start < b.start; +} + +QByteArray PerfUnwind::symbolFromPerfMap(quint64 ip, quint32 pid, GElf_Off *offset) const +{ + const PerfMap &map = perfMaps[pid]; + + QVector<PerfMap::Symbol>::ConstIterator sym = + std::upper_bound(map.symbols.begin(), map.symbols.end(), PerfMap::Symbol(ip)); + if (sym == map.symbols.begin()) + return QByteArray(); + --sym; + + if (sym != map.symbols.end() && sym->start <= ip && sym->start + sym->length > ip) { + *offset = ip - sym->start; + return sym->name; + } + *offset = 0; + return QByteArray(); +} + +void PerfUnwind::updatePerfMap(quint32 pid) +{ + PerfMap &map = perfMaps[pid]; + if (!map.file) { + map.file.reset(new QFile(QString::fromLatin1("/tmp/perf-%1.map").arg(pid))); + map.file->open(QIODevice::ReadOnly); + } + + bool readLine = false; + while (!map.file->atEnd()) { + QByteArrayList line = map.file->readLine().split(' '); + if (line.length() >= 3) { + bool ok = false; + quint64 start = line.takeFirst().toULongLong(&ok, 16); + if (!ok) + continue; + quint64 length = line.takeFirst().toULongLong(&ok, 16); + if (!ok) + continue; + QByteArray name = line.join(' ').trimmed(); + map.symbols.append(PerfMap::Symbol(start, length, name)); + readLine = true; + } + } + + if (readLine) + std::sort(map.symbols.begin(), map.symbols.end()); +} diff --git a/app/perfunwind.h b/app/perfunwind.h index d68d16f..b2601b6 100644 --- a/app/perfunwind.h +++ b/app/perfunwind.h @@ -27,6 +27,7 @@ #include <QFileInfo> #include <QMap> #include <QHash> +#include <QSharedPointer> class PerfUnwind { @@ -77,6 +78,19 @@ public: bool found; }; + struct PerfMap { + struct Symbol { + Symbol(quint64 start = 0, quint64 length = 0, QByteArray name = QByteArray()) : + start(start), length(length), name(name) {} + quint64 start; + quint64 length; + QByteArray name; + }; + + QSharedPointer<QFile> file; + QVector<Symbol> symbols; + }; + static const quint32 s_kernelPid = std::numeric_limits<quint32>::max(); static const int s_maxFrames = 64; @@ -102,6 +116,9 @@ public: void fork(const PerfRecordFork &sample); void exit(const PerfRecordExit &sample); + QByteArray symbolFromPerfMap(quint64 ip, quint32 pid, GElf_Off *offset) const; + void updatePerfMap(quint32 pid); + private: enum CallchainContext { @@ -139,6 +156,7 @@ private: QString appPath; QHash<quint32, QMap<quint64, ElfInfo> > elfs; // The inner map needs to be sorted + QHash<quint32, PerfMap> perfMaps; QList<PerfRecordSample> sampleBuffer; uint sampleBufferSize; |