summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMilian Wolff <milian.wolff@kdab.com>2017-04-27 17:20:04 +0200
committerMilian Wolff <milian.wolff@kdab.com>2017-05-08 11:20:20 +0000
commitcb6c08fa91cb992571a2baacf8bc7e3507c89c55 (patch)
tree40aab5c32aa9b7098fd8aa2815944ba6229cf10a
parenta55e3a1dc93088ea25098ec1c70a2589e37a5dd9 (diff)
Fall-back to global search for debug-link files
When elfutils fails to find a debug link file, try to find it in all of our search paths like we do for the DSO itself. This fixes situations where we report the DSO in the ~/.debug path as identified by its build-id. Change-Id: If5931b5705e318806bf7d8cc8a63e252ba344a36 Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
-rw-r--r--app/perfsymboltable.cpp121
-rw-r--r--app/perfsymboltable.h9
-rw-r--r--app/perfunwind.cpp18
-rw-r--r--app/perfunwind.h5
4 files changed, 105 insertions, 48 deletions
diff --git a/app/perfsymboltable.cpp b/app/perfsymboltable.cpp
index b9b2bbb..8aad5f9 100644
--- a/app/perfsymboltable.cpp
+++ b/app/perfsymboltable.cpp
@@ -41,6 +41,13 @@ extern "C" {
extern int eu_compat_close(int);
extern void *eu_compat_malloc(size_t);
extern void eu_compat_free(void *);
+ static char* eu_compat_strdup(const char* string)
+ {
+ const size_t length = strlen(string) + 1; // include null char
+ char* ret = reinterpret_cast<char*>(eu_compat_malloc(length));
+ std::memcpy(ret, string, length);
+ return ret;
+ }
}
#else
#include <cxxabi.h>
@@ -50,6 +57,7 @@ extern "C" {
#define eu_compat_malloc malloc
#define eu_compat_free free
#define eu_compat_demangle abi::__cxa_demangle
+#define eu_compat_strdup strdup
#define O_BINARY 0
#endif
@@ -239,54 +247,54 @@ static QStringList splitPath(const QString &path)
return path.split(QDir::listSeparator(), QString::SkipEmptyParts);
}
-void PerfSymbolTable::registerElf(const PerfRecordMmap &mmap, const QByteArray &buildId,
- const QString &appPath, const QString &systemRoot,
- const QString &extraLibsPath, const QString &debugInfoPath)
+QFileInfo PerfSymbolTable::findFile(const char *path, const QString &fileName,
+ const QByteArray &buildId) const
+{
+ QFileInfo fullPath;
+ // first try to find the debug information via build id, if available
+ if (!buildId.isEmpty()) {
+ const QString buildIdPath = QString::fromUtf8(path) + QDir::separator()
+ + QString::fromUtf8(buildId.toHex());
+ foreach (const QString &extraPath, splitPath(m_unwind->debugPath())) {
+ fullPath.setFile(extraPath);
+ if (findBuildIdPath(fullPath, buildIdPath))
+ return fullPath;
+ }
+ }
+
+ if (!m_unwind->appPath().isEmpty()) {
+ // try to find the file in the app path
+ fullPath.setFile(m_unwind->appPath());
+ if (findInExtraPath(fullPath, fileName))
+ return fullPath;
+ }
+
+ // try to find the file in the extra libs path
+ foreach (const QString &extraPath, splitPath(m_unwind->extraLibsPath())) {
+ fullPath.setFile(extraPath);
+ if (findInExtraPath(fullPath, fileName))
+ return fullPath;
+ }
+
+ // last fall-back, try the system root
+ fullPath.setFile(m_unwind->systemRoot() + QString::fromUtf8(path));
+ return fullPath;
+}
+
+void PerfSymbolTable::registerElf(const PerfRecordMmap &mmap, const QByteArray &buildId)
{
QLatin1String filePath(mmap.filename());
// special regions, such as [heap], [vdso], [stack], ... as well as //anon
const bool isSpecialRegion = (mmap.filename().startsWith('[') && mmap.filename().endsWith(']'))
|| filePath == QLatin1String("//anon");
- QFileInfo fileInfo(filePath);
+ const auto fileName = QFileInfo(filePath).fileName();
QFileInfo fullPath;
if (isSpecialRegion) {
// don not set fullPath, these regions don't represent a real file
} else if (mmap.pid() != PerfUnwind::s_kernelPid) {
- 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()) + QDir::separator()
- + QString::fromUtf8(buildId.toHex());
- foreach (const QString &extraPath, splitPath(debugInfoPath)) {
- fullPath.setFile(extraPath);
- if (findBuildIdPath(fullPath, buildIdPath)) {
- found = true;
- break;
- }
- }
- }
- if (!found && !appPath.isEmpty()) {
- // 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, splitPath(extraLibsPath)) {
- fullPath.setFile(extraPath);
- if (findInExtraPath(fullPath, fileInfo.fileName())) {
- found = true;
- break;
- }
- }
- }
- if (!found) {
- // last fall-back, try the system root
- fullPath.setFile(systemRoot + filePath);
- found = fullPath.exists();
- }
+ fullPath = findFile(mmap.filename(), fileName, buildId);
- if (!found) {
+ if (!fullPath.isFile()) {
m_unwind->sendError(PerfUnwind::MissingElfFile,
PerfUnwind::tr("Could not find ELF file for %1. "
"This can break stack unwinding "
@@ -312,11 +320,11 @@ void PerfSymbolTable::registerElf(const PerfRecordMmap &mmap, const QByteArray &
}
}
} else { // kernel
- fullPath.setFile(systemRoot + filePath);
+ fullPath.setFile(m_unwind->systemRoot() + filePath);
}
bool cacheInvalid = m_elfs.registerElf(mmap.addr(), mmap.len(), mmap.pgoff(), fullPath,
- fileInfo.fileName().toUtf8());
+ 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
@@ -475,11 +483,44 @@ Dwfl_Module *PerfSymbolTable::reportElf(const PerfElfMap::ElfInfo& info)
if (!ret) {
reportError(info, dwfl_errmsg(dwfl_errno()));
m_cacheIsDirty = true;
+ } else {
+ // set symbol table as user data, cf. find_debuginfo callback in perfunwind.cpp
+ void** userData;
+ dwfl_module_info(ret, &userData, nullptr, nullptr, nullptr, nullptr,
+ nullptr, nullptr);
+ *userData = this;
}
return ret;
}
+int PerfSymbolTable::findDebugInfo(Dwfl_Module *module, const char *moduleName, Dwarf_Addr base,
+ const char *file, const char *debugLink,
+ GElf_Word crc, char **debugInfoFilename)
+{
+ int ret = dwfl_standard_find_debuginfo(module, nullptr, moduleName, base, file,
+ debugLink, crc, debugInfoFilename);
+ if (ret >= 0 || !debugLink || strlen(debugLink) == 0)
+ return ret;
+
+ // fall-back, mostly for situations where we loaded a file via it's build-id.
+ // search all known paths for the debug link in that case
+ const auto debugLinkFile = findFile(debugLink, QFile(debugLink).fileName());
+ if (!debugLinkFile.isFile())
+ return ret;
+
+ const auto path = eu_compat_strdup(debugLinkFile.absoluteFilePath().toUtf8().constData());
+ // ugh, nasty - we have to return a fd here :-/
+ ret = eu_compat_open(path, O_RDONLY | O_BINARY);
+ if (ret < 0 ) {
+ qWarning() << "Failed to open debug info file" << path;
+ eu_compat_free(path);
+ } else {
+ *debugInfoFilename = path;
+ }
+ return ret;
+}
+
PerfElfMap::ElfInfo PerfSymbolTable::findElf(quint64 ip) const
{
return m_elfs.findElf(ip);
diff --git a/app/perfsymboltable.h b/app/perfsymboltable.h
index 9478d1c..27e7127 100644
--- a/app/perfsymboltable.h
+++ b/app/perfsymboltable.h
@@ -53,15 +53,16 @@ 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 QByteArray &buildId,
- const QString &appPath, const QString &systemRoot,
- const QString &extraLibsPath, const QString &debugInfoPath);
+ void registerElf(const PerfRecordMmap &mmap, const QByteArray &buildId);
PerfElfMap::ElfInfo findElf(quint64 ip) const;
// Report an mmap to dwfl and parse it for symbols and inlines, or simply return it if dwfl has
// it already
Dwfl_Module *reportElf(const PerfElfMap::ElfInfo& elf);
+ int findDebugInfo(Dwfl_Module *module, const char *moduleName, Dwarf_Addr base,
+ const char *file, const char *debugLink,
+ GElf_Word crc, char **debugInfoFilename);
// Look up a frame and all its inline parents and append them to the given vector.
// If the frame hits an elf that hasn't been reported, yet, report it.
@@ -75,6 +76,8 @@ public:
bool cacheIsDirty() const { return m_cacheIsDirty; }
private:
+ QFileInfo findFile(const char *path, const QString &fileName,
+ const QByteArray &buildId = QByteArray()) const;
struct AddressCacheEntry {
int locationId;
diff --git a/app/perfunwind.cpp b/app/perfunwind.cpp
index 9c84811..12ccfef 100644
--- a/app/perfunwind.cpp
+++ b/app/perfunwind.cpp
@@ -75,6 +75,16 @@ void PerfUnwind::Stats::finishedRound()
lastRoundTime = maxTime;
}
+static int find_debuginfo(Dwfl_Module *module, void **userData, const char *moduleName,
+ Dwarf_Addr base, const char *file, const char *debugLink,
+ GElf_Word crc, char **debugInfoFilename)
+{
+ // data should have been set from PerfSymbolTable::reportElf
+ Q_ASSERT(*userData);
+ auto* symbolTable = reinterpret_cast<PerfSymbolTable*>(*userData);
+ return symbolTable->findDebugInfo(module, moduleName, base, file, debugLink, crc, debugInfoFilename);
+}
+
PerfUnwind::PerfUnwind(QIODevice *output, const QString &systemRoot, const QString &debugPath,
const QString &extraLibsPath, const QString &appPath,
const QString &kallsymsPath, bool printStats, uint maxEventBufferSize,
@@ -87,7 +97,7 @@ PerfUnwind::PerfUnwind(QIODevice *output, const QString &systemRoot, const QStri
m_currentUnwind.unwind = this;
m_currentUnwind.maxFrames = maxFrames;
m_offlineCallbacks.find_elf = dwfl_build_id_find_elf;
- m_offlineCallbacks.find_debuginfo = dwfl_standard_find_debuginfo;
+ m_offlineCallbacks.find_debuginfo = find_debuginfo;
m_offlineCallbacks.section_address = dwfl_offline_section_address;
const QChar separator = QDir::listSeparator();
QByteArray newDebugInfo = (separator + debugPath + separator + appPath + separator
@@ -641,10 +651,8 @@ void PerfUnwind::flushEventBuffer(uint desiredBufferSize)
for (; mmapIt != mmapEnd && mmapIt->time() <= sample.time(); ++mmapIt) {
if (!m_stats.enabled) {
- symbolTable(mmapIt->pid())->registerElf(*mmapIt,
- m_buildIds.value(mmapIt->filename()),
- m_appPath, m_systemRoot,
- m_extraLibsPath, m_debugPath);
+ const auto &buildId = m_buildIds.value(mmapIt->filename());
+ symbolTable(mmapIt->pid())->registerElf(*mmapIt, buildId);
}
m_eventBufferSize -= mmapIt->size();
}
diff --git a/app/perfunwind.h b/app/perfunwind.h
index 2402cc7..209e863 100644
--- a/app/perfunwind.h
+++ b/app/perfunwind.h
@@ -150,6 +150,11 @@ public:
void sendError(ErrorCode error, const QString &message);
void sendProgress(float percent);
+ QString systemRoot() const { return m_systemRoot; }
+ QString extraLibsPath() const { return m_extraLibsPath; }
+ QString appPath() const { return m_appPath; }
+ QString debugPath() const { return m_debugPath; }
+
private:
enum CallchainContext {