From 186e784d89aca5fc181b716bc1af7cc8ff413a59 Mon Sep 17 00:00:00 2001 From: Dale Glass Date: Thu, 1 Jul 2021 01:43:06 +0200 Subject: [PATCH] Rework crashpad initialization * Don't abort on startup if we can't find our own path * Try to locate our own path by looking in /proc in Linux -- should fix AppImage issues * Add extensive logging --- interface/src/CrashHandler.h | 5 +- interface/src/CrashHandler_Crashpad.cpp | 64 +++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/interface/src/CrashHandler.h b/interface/src/CrashHandler.h index 5b7e3b4438..27c5d76e1c 100644 --- a/interface/src/CrashHandler.h +++ b/interface/src/CrashHandler.h @@ -13,7 +13,10 @@ #define hifi_CrashHandler_h #include -class QCoreApplication; +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(crash_handler) bool startCrashHandler(std::string appPath); void setCrashAnnotation(std::string name, std::string value); diff --git a/interface/src/CrashHandler_Crashpad.cpp b/interface/src/CrashHandler_Crashpad.cpp index a8195ce6e8..f2ec0c2ff8 100644 --- a/interface/src/CrashHandler_Crashpad.cpp +++ b/interface/src/CrashHandler_Crashpad.cpp @@ -13,6 +13,8 @@ #include "CrashHandler.h" +Q_LOGGING_CATEGORY(crash_handler, "vircadia.crash_handler") + #include #include @@ -141,6 +143,10 @@ static const QString CRASHPAD_HANDLER_NAME{ "crashpad_handler.exe" }; static const QString CRASHPAD_HANDLER_NAME{ "crashpad_handler" }; #endif +#ifdef Q_OS_LINUX +#include +#endif + #ifdef Q_OS_WIN // ------------------------------------------------------------------------------------------------ // The area within this #ifdef is specific to the Microsoft C++ compiler @@ -311,8 +317,38 @@ void checkUnhandledExceptionHook() { // ------------------------------------------------------------------------------------------------ #endif // Q_OS_WIN +// Locate the full path to the binary's directory +static QString findBinaryDir() { + // Normally we'd just use QCoreApplication::applicationDirPath(), but we can't. + // That function needs the QApplication to be created first, and Crashpad is initialized as early as possible, + // which is well before QApplication, so that function throws out a warning and returns ".". + // + // So we must do things the hard way here. In particular this is needed to correctly handle things in AppImage + // on Linux. On Windows and MacOS falling back to argv[0] should be fine. + +#ifdef Q_OS_LINUX + // Find outselves by looking at /proc//exe + pid_t ourPid = getpid(); + QString exeLink = QString("/proc/%1/exe").arg(ourPid); + qCDebug(crash_handler) << "Looking at" << exeLink; + + QFileInfo exeLinkInfo(exeLink); + if (exeLinkInfo.isSymLink()) { + QFileInfo exeInfo( exeLinkInfo.symLinkTarget() ); + qCDebug(crash_handler) << "exe symlink points at" << exeInfo; + return exeInfo.absoluteDir().absolutePath(); + } else { + qCWarning(crash_handler) << exeLink << "isn't a symlink. /proc not mounted?"; + } + +#endif + + return QString(); +} + bool startCrashHandler(std::string appPath) { if (BACKTRACE_URL.empty() || BACKTRACE_TOKEN.empty()) { + qCCritical(crash_handler) << "Backtrace URL or token not set, crash handler disabled."; return false; } @@ -338,11 +374,29 @@ bool startCrashHandler(std::string appPath) { const auto crashpadDbPath = crashpadDbDir.toStdString() + "/" + crashpadDbName; // Locate Crashpad handler - const QFileInfo interfaceBinary{ QString::fromStdString(appPath) }; - const QDir interfaceDir = interfaceBinary.dir(); - assert(interfaceDir.exists(CRASHPAD_HANDLER_NAME)); + QString binaryDir = findBinaryDir(); + QDir interfaceDir; + + if ( !binaryDir.isEmpty() ) { + // Locating ourselves by argv[0] fails in the case of AppImage on Linux, as we get the AppImage + // itself in there. If we have a platform-specific method, and it succeeds, we use that instead + // of argv. + qCDebug(crash_handler) << "Locating own directory by platform-specific method"; + interfaceDir.setPath(binaryDir); + } else { + qCDebug(crash_handler) << "Locating own directory by argv[0]"; + interfaceDir.setPath(QString::fromStdString(appPath)); + } + + if (!interfaceDir.exists(CRASHPAD_HANDLER_NAME)) { + qCCritical(crash_handler) << "Failed to find" << CRASHPAD_HANDLER_NAME << "in" << interfaceDir << ", can't start crash handler"; + return false; + } + const std::string CRASHPAD_HANDLER_PATH = interfaceDir.filePath(CRASHPAD_HANDLER_NAME).toStdString(); + qCDebug(crash_handler) << "Crashpad handler found at" << QString::fromStdString(CRASHPAD_HANDLER_PATH); + // Setup different file paths base::FilePath::StringType dbPath; base::FilePath::StringType handlerPath; @@ -352,8 +406,10 @@ bool startCrashHandler(std::string appPath) { base::FilePath db(dbPath); base::FilePath handler(handlerPath); + qCDebug(crash_handler) << "Opening crashpad database" << QString::fromStdString(crashpadDbPath); auto database = crashpad::CrashReportDatabase::Initialize(db); if (database == nullptr || database->GetSettings() == nullptr) { + qCCritical(crash_handler) << "Failed to open crashpad database" << QString::fromStdString(crashpadDbPath); return false; } @@ -361,6 +417,7 @@ bool startCrashHandler(std::string appPath) { database->GetSettings()->SetUploadsEnabled(true); if (!client->StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true)) { + qCCritical(crash_handler) << "Failed to start crashpad handler"; return false; } @@ -369,6 +426,7 @@ bool startCrashHandler(std::string appPath) { gl_crashpadUnhandledExceptionFilter = SetUnhandledExceptionFilter(unhandledExceptionHandler); #endif + qCInfo(crash_handler) << "Crashpad initialized"; return true; }