summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMilian Wolff <milian.wolff@kdab.com>2017-03-14 23:20:27 +0100
committerMilian Wolff <milian.wolff@kdab.com>2017-03-29 14:54:00 +0000
commit79ae8427155c6124fc23a9ab0bbed0a5ce1ec457 (patch)
treecdb1bafd0465fbb201f1e4ef33182ec03d4036ec
parent17a5d15977856617979e08e68985123af3f7229a (diff)
Try to find elfs in debug path, when build id is available
This enables us to parse perf.data files when the corresponding elf objects do not exist anymore in the version that was used at perf record time. This also enables us to use `perf archive` to evaluate perf.data files with perfparser on different machines. Change-Id: Id7ac1af125dd3818dc86880f25a0f74d8d09bfc1 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--app/perfelfmap.cpp15
-rw-r--r--app/perfelfmap.h20
-rw-r--r--app/perfsymboltable.cpp43
-rw-r--r--app/perfsymboltable.h5
-rw-r--r--app/perfunwind.cpp15
-rw-r--r--app/perfunwind.h4
-rw-r--r--tests/auto/elfmap/tst_elfmap.cpp3
7 files changed, 72 insertions, 33 deletions
diff --git a/app/perfelfmap.cpp b/app/perfelfmap.cpp
index 7a138cc..4fb4248 100644
--- a/app/perfelfmap.cpp
+++ b/app/perfelfmap.cpp
@@ -26,8 +26,9 @@
QDebug operator<<(QDebug stream, const PerfElfMap::ElfInfo& info)
{
stream.nospace() << "ElfInfo{"
- << "file=" << info.file.fileName() << ", "
+ << "localFile=" << info.localFile.fileName() << ", "
<< "isFile=" << info.isFile() << ", "
+ << "originalFileName=" << info.originalFileName << ", "
<< "addr=" << info.addr << ", "
<< "len=" << info.length << ", "
<< "pgoff=" << info.pgoff << ", "
@@ -51,7 +52,7 @@ struct SortByAddr
}
bool PerfElfMap::registerElf(const quint64 addr, const quint64 len, quint64 pgoff,
- const QFileInfo &fullPath)
+ const QFileInfo &fullPath, const QByteArray &originalFileName)
{
bool cacheInvalid = false;
const quint64 addrEnd = addr + len;
@@ -68,12 +69,12 @@ bool PerfElfMap::registerElf(const quint64 addr, const quint64 len, quint64 pgof
// reinsert any fragments of it that remain.
if (i->addr < addr) {
- newElfs.push_back(ElfInfo(i->file, i->addr, addr - i->addr,
- i->pgoff));
+ newElfs.push_back(ElfInfo(i->localFile, i->addr, addr - i->addr,
+ i->pgoff, i->originalFileName));
}
if (iEnd > addrEnd) {
- newElfs.push_back(ElfInfo(i->file, addrEnd, iEnd - addrEnd,
- i->pgoff + addrEnd - i->addr));
+ newElfs.push_back(ElfInfo(i->localFile, addrEnd, iEnd - addrEnd,
+ i->pgoff + addrEnd - i->addr, i->originalFileName));
}
removedElfs.push_back(std::distance(m_elfs.begin(), i));
@@ -89,7 +90,7 @@ bool PerfElfMap::registerElf(const quint64 addr, const quint64 len, quint64 pgof
for (auto it = removedElfs.rbegin(), end = removedElfs.rend(); it != end; ++it)
m_elfs.remove(*it);
- newElfs.push_back(ElfInfo(fullPath, addr, len, pgoff));
+ newElfs.push_back(ElfInfo(fullPath, addr, len, pgoff, originalFileName));
for (const auto &elf : newElfs) {
auto it = std::lower_bound(m_elfs.begin(), m_elfs.end(),
diff --git a/app/perfelfmap.h b/app/perfelfmap.h
index 936858c..0355039 100644
--- a/app/perfelfmap.h
+++ b/app/perfelfmap.h
@@ -28,9 +28,13 @@ class PerfElfMap
{
public:
struct ElfInfo {
- ElfInfo(const QFileInfo &file = QFileInfo(), quint64 addr = 0, quint64 length = 0,
- quint64 pgoff = 0) :
- file(file), addr(addr), length(length), pgoff(pgoff)
+ ElfInfo(const QFileInfo &localFile = QFileInfo(), quint64 addr = 0, quint64 length = 0,
+ quint64 pgoff = 0, const QByteArray &originalFileName = {}) :
+ localFile(localFile),
+ originalFileName(originalFileName.isEmpty()
+ ? localFile.fileName().toLocal8Bit()
+ : originalFileName),
+ addr(addr), length(length), pgoff(pgoff)
{}
bool isValid() const
@@ -40,26 +44,28 @@ public:
bool isFile() const
{
- return file.isFile();
+ return localFile.isFile();
}
bool operator==(const ElfInfo& rhs) const
{
return isFile() == rhs.isFile()
- && (!isFile() || file == rhs.file)
+ && (!isFile() || localFile == rhs.localFile)
+ && originalFileName == rhs.originalFileName
&& addr == rhs.addr
&& length == rhs.length
&& pgoff == rhs.pgoff;
}
- QFileInfo file;
+ QFileInfo localFile;
+ QByteArray originalFileName;
quint64 addr;
quint64 length;
quint64 pgoff;
};
bool registerElf(quint64 addr, quint64 len, quint64 pgoff,
- const QFileInfo &fullPath);
+ const QFileInfo &fullPath, const QByteArray &originalFileName = {});
ElfInfo findElf(quint64 ip) const;
bool isEmpty() const
diff --git a/app/perfsymboltable.cpp b/app/perfsymboltable.cpp
index 41f9c1e..bc3f18c 100644
--- a/app/perfsymboltable.cpp
+++ b/app/perfsymboltable.cpp
@@ -172,8 +172,9 @@ static bool findInExtraPath(QFileInfo &path, const QString &fileName)
return false;
}
-void PerfSymbolTable::registerElf(const PerfRecordMmap &mmap, const QString &appPath,
- const QString &systemRoot, const QString &extraLibsPath)
+void PerfSymbolTable::registerElf(const PerfRecordMmap &mmap, const QByteArray &buildId,
+ const QString &appPath, const QString &systemRoot,
+ const QString &extraLibsPath, const QString &debugInfoPath)
{
QLatin1String filePath(mmap.filename());
// special regions, such as [heap], [vdso], [stack], ... as well as //anon
@@ -184,9 +185,26 @@ void PerfSymbolTable::registerElf(const PerfRecordMmap &mmap, const QString &app
if (isSpecialRegion) {
// don not set fullPath, these regions don't represent a real file
} else if (mmap.pid() != PerfUnwind::s_kernelPid) {
- fullPath.setFile(appPath);
- if (!findInExtraPath(fullPath, fileInfo.fileName())) {
- bool found = false;
+ bool found = false;
+ // first try to find the debug information via build id, if available
+ if (!buildId.isEmpty()) {
+ const QString buildIdPath = QString::fromUtf8(mmap.filename() + '/'
+ + buildId.toHex() + "/elf");
+ foreach (const QString &extraPath, debugInfoPath.split(QLatin1Char(':'))) {
+ fullPath.setFile(extraPath);
+ if (findInExtraPath(fullPath, buildIdPath)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ // try to find the file in the app path
+ fullPath.setFile(appPath);
+ found = findInExtraPath(fullPath, fileInfo.fileName());
+ }
+ if (!found) {
+ // try to find the file in the extra libs path
foreach (const QString &extraPath, extraLibsPath.split(QLatin1Char(':'))) {
fullPath.setFile(extraPath);
if (findInExtraPath(fullPath, fileInfo.fileName())) {
@@ -194,9 +212,9 @@ void PerfSymbolTable::registerElf(const PerfRecordMmap &mmap, const QString &app
break;
}
}
- if (!found)
- fullPath.setFile(systemRoot + filePath);
}
+ if (!found) // last fall-back, try the system root
+ fullPath.setFile(systemRoot + filePath);
if (!m_firstElfFile.isOpen() && fullPath.exists()) {
m_firstElfFile.setFileName(fullPath.absoluteFilePath());
@@ -218,7 +236,8 @@ void PerfSymbolTable::registerElf(const PerfRecordMmap &mmap, const QString &app
fullPath.setFile(systemRoot + filePath);
}
- bool cacheInvalid = m_elfs.registerElf(mmap.addr(), mmap.len(), mmap.pgoff(), fullPath);
+ bool cacheInvalid = m_elfs.registerElf(mmap.addr(), mmap.len(), mmap.pgoff(), fullPath,
+ fileInfo.fileName().toUtf8());
// There is no need to clear the symbol or location caches in PerfUnwind. Some locations become
// stale this way, but we still need to keep their IDs, as the receiver might still use them for
@@ -356,7 +375,7 @@ void PerfSymbolTable::parseDwarf(Dwarf_Die *cudie, Dwarf_Addr bias, qint32 binar
static void reportError(const PerfElfMap::ElfInfo& info, const char *message)
{
- qWarning() << "failed to report" << info.file.absoluteFilePath() << "for"
+ qWarning() << "failed to report" << info.localFile.absoluteFilePath() << "for"
<< hex << info.addr << dec << ":" << message;
}
@@ -371,8 +390,8 @@ Dwfl_Module *PerfSymbolTable::reportElf(const PerfElfMap::ElfInfo& info)
}
Dwfl_Module *ret = dwfl_report_elf(
- m_dwfl, info.file.fileName().toLocal8Bit().constData(),
- info.file.absoluteFilePath().toLocal8Bit().constData(), -1, info.addr,
+ m_dwfl, info.originalFileName.constData(),
+ info.localFile.absoluteFilePath().toLocal8Bit().constData(), -1, info.addr,
false);
if (!ret)
reportError(info, dwfl_errmsg(dwfl_errno()));
@@ -401,7 +420,7 @@ int PerfSymbolTable::lookupFrame(Dwarf_Addr ip, bool isKernel,
const auto& elf = findElf(ip);
if (elf.isValid()) {
- binaryId = m_unwind->resolveString(elf.file.fileName().toLocal8Bit());
+ binaryId = m_unwind->resolveString(elf.originalFileName);
elfStart = elf.addr;
if (m_dwfl && !mod)
mod = reportElf(elf);
diff --git a/app/perfsymboltable.h b/app/perfsymboltable.h
index 167489a..840fa93 100644
--- a/app/perfsymboltable.h
+++ b/app/perfsymboltable.h
@@ -53,8 +53,9 @@ public:
// Announce an mmap. Invalidate the symbol and address cache and clear the dwfl if it overlaps
// with an existing one.
- void registerElf(const PerfRecordMmap &mmap, const QString &appPath,
- const QString &systemRoot, const QString &extraLibsPath);
+ void registerElf(const PerfRecordMmap &mmap, const QByteArray &buildId,
+ const QString &appPath, const QString &systemRoot,
+ const QString &extraLibsPath, const QString &debugInfoPath);
PerfElfMap::ElfInfo findElf(quint64 ip) const;
diff --git a/app/perfunwind.cpp b/app/perfunwind.cpp
index 155cad7..5975968 100644
--- a/app/perfunwind.cpp
+++ b/app/perfunwind.cpp
@@ -78,7 +78,7 @@ PerfUnwind::PerfUnwind(QIODevice *output, const QString &systemRoot, const QStri
const QString &extraLibsPath, const QString &appPath,
const QString &kallsymsPath, bool printStats, uint maxEventBufferSize) :
m_output(output), m_architecture(PerfRegisterInfo::ARCH_INVALID), m_systemRoot(systemRoot),
- m_extraLibsPath(extraLibsPath), m_appPath(appPath), m_kallsyms(kallsymsPath),
+ m_extraLibsPath(extraLibsPath), m_appPath(appPath), m_debugPath(debugPath), m_kallsyms(kallsymsPath),
m_maxEventBufferSize(maxEventBufferSize), m_eventBufferSize(0), m_lastFlushMaxTime(0)
{
m_stats.enabled = printStats;
@@ -247,6 +247,12 @@ void PerfUnwind::features(const PerfFeatures &features)
<< features.pmuMappings()
<< features.groupDesc();
sendBuffer(buffer);
+
+ const auto &buildIds = features.buildId().buildIds;
+ m_buildIds.reserve(buildIds.size());
+ for (const auto &buildId : buildIds) {
+ m_buildIds[buildId.fileName] = buildId.id;
+ }
}
Dwfl_Module *PerfUnwind::reportElf(quint64 ip, quint32 pid)
@@ -597,9 +603,10 @@ void PerfUnwind::flushEventBuffer(uint desiredBufferSize)
for (; mmapIt != mmapEnd && mmapIt->time() <= sample.time(); ++mmapIt) {
if (!m_stats.enabled) {
- symbolTable(mmapIt->pid())->registerElf(*mmapIt, m_appPath,
- m_systemRoot,
- m_extraLibsPath);
+ symbolTable(mmapIt->pid())->registerElf(*mmapIt,
+ m_buildIds.value(mmapIt->filename()),
+ m_appPath, m_systemRoot,
+ m_extraLibsPath, m_debugPath);
}
m_eventBufferSize -= mmapIt->size();
}
diff --git a/app/perfunwind.h b/app/perfunwind.h
index 6617a2e..c80fb47 100644
--- a/app/perfunwind.h
+++ b/app/perfunwind.h
@@ -179,6 +179,9 @@ private:
// binaries and debug symbols.
QString m_appPath;
+ // Path to debug information, e.g. ~/.debug and /usr/local/debug
+ QString m_debugPath;
+
QList<PerfRecordSample> m_sampleBuffer;
QList<PerfRecordMmap> m_mmapBuffer;
QHash<quint32, PerfSymbolTable *> m_symbolTables;
@@ -189,6 +192,7 @@ private:
QHash<qint32, Symbol> m_symbols;
QHash<quint64, qint32> m_attributeIds;
QHash<PerfEventAttributes, qint32> m_attributes;
+ QHash<QByteArray, QByteArray> m_buildIds;
uint m_maxEventBufferSize;
uint m_eventBufferSize;
diff --git a/tests/auto/elfmap/tst_elfmap.cpp b/tests/auto/elfmap/tst_elfmap.cpp
index 313c1b7..d827826 100644
--- a/tests/auto/elfmap/tst_elfmap.cpp
+++ b/tests/auto/elfmap/tst_elfmap.cpp
@@ -27,7 +27,8 @@
namespace {
bool registerElf(PerfElfMap *map, const PerfElfMap::ElfInfo &info)
{
- return map->registerElf(info.addr, info.length, info.pgoff, info.file);
+ return map->registerElf(info.addr, info.length, info.pgoff,
+ info.localFile, info.originalFileName);
}
}