diff options
author | Milian Wolff <milian.wolff@kdab.com> | 2017-04-27 17:20:04 +0200 |
---|---|---|
committer | Milian Wolff <milian.wolff@kdab.com> | 2017-05-08 11:20:20 +0000 |
commit | cb6c08fa91cb992571a2baacf8bc7e3507c89c55 (patch) | |
tree | 40aab5c32aa9b7098fd8aa2815944ba6229cf10a | |
parent | a55e3a1dc93088ea25098ec1c70a2589e37a5dd9 (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.cpp | 121 | ||||
-rw-r--r-- | app/perfsymboltable.h | 9 | ||||
-rw-r--r-- | app/perfunwind.cpp | 18 | ||||
-rw-r--r-- | app/perfunwind.h | 5 |
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 { |