summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-11-03 13:17:09 +0100
committerUlf Hermann <ulf.hermann@theqtcompany.com>2016-03-04 12:21:52 +0000
commitf53e88918713437d84a145649dc4be671ff78fe1 (patch)
tree2843e483bdf6039b17c6403212a522bad0296d88
parent65f15b411e1125c1acfd919915e3c41d5e8b8a0c (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.cpp66
-rw-r--r--app/perfunwind.h18
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;