From a7509669f9bda56fdc4436ee1129b779a9c14ee1 Mon Sep 17 00:00:00 2001 From: Roland Rossgotterer Date: Fri, 3 May 2019 15:08:39 +0200 Subject: [PATCH] Refactor crashpad initialization Crashpad handler was initialized quite late since we were waiting for settings. However the settings in crashpad are persistent itself, hence we can start it at the earliest possible time and change the upload settings independently later. The only prerequisite is to have QCoreApplication constructed in order for the application name to be available. ref T652 --- src/blackcore/application.cpp | 165 +-------------- src/blackcore/application.h | 47 +---- src/blackcore/blackcore.pro | 10 - src/blackcore/data/globalsetup.cpp | 3 +- src/blackcore/simulator.cpp | 7 +- src/blackgui/components/dblogincomponent.cpp | 5 +- .../components/internalscomponent.cpp | 5 +- src/blackgui/components/logincomponent.cpp | 9 +- src/blackmisc/appstarttime.cpp | 18 ++ src/blackmisc/appstarttime.h | 24 +++ src/blackmisc/blackmisc.pro | 11 + src/blackmisc/crashhandler.cpp | 189 ++++++++++++++++++ src/blackmisc/crashhandler.h | 92 +++++++++ src/blackmisc/filelogger.cpp | 61 +++--- src/blackmisc/filelogger.h | 22 +- src/swiftcore/main.cpp | 4 + src/swiftdata/main.cpp | 4 + src/swiftguistandard/main.cpp | 4 + 18 files changed, 407 insertions(+), 273 deletions(-) create mode 100644 src/blackmisc/appstarttime.cpp create mode 100644 src/blackmisc/appstarttime.h create mode 100644 src/blackmisc/crashhandler.cpp create mode 100644 src/blackmisc/crashhandler.h diff --git a/src/blackcore/application.cpp b/src/blackcore/application.cpp index 95f125236..7a28fb99d 100644 --- a/src/blackcore/application.cpp +++ b/src/blackcore/application.cpp @@ -21,6 +21,7 @@ #include "blackcore/inputmanager.h" #include "blackmisc/atomicfile.h" #include "blackmisc/applicationinfo.h" +#include "blackmisc/crashhandler.h" #include "blackmisc/datacache.h" #include "blackmisc/dbusserver.h" #include "blackmisc/directoryutils.h" @@ -68,16 +69,6 @@ #include #include -#ifdef BLACK_USE_CRASHPAD -#if defined(Q_OS_WIN) && !defined(NOMINMAX) -#define NOMINMAX -#endif -#include "crashpad/client/crashpad_client.h" -#include "crashpad/client/crash_report_database.h" -#include "crashpad/client/settings.h" -#include "crashpad/client/simulate_crash.h" -#endif - using namespace BlackConfig; using namespace BlackMisc; using namespace BlackMisc::Db; @@ -90,7 +81,6 @@ using namespace BlackCore::Context; using namespace BlackCore::Vatsim; using namespace BlackCore::Data; using namespace BlackCore::Db; -using namespace crashpad; BlackCore::CApplication *sApp = nullptr; // set by constructor @@ -382,9 +372,6 @@ namespace BlackCore // Settings if not already initialized msgs.push_back(this->initLocalSettings()); if (msgs.isFailure()) { break; } - - // we have settings, so enable crash handler. - msgs.push_back(this->initCrashHandler()); } while (false); @@ -959,7 +946,7 @@ namespace BlackCore CLogHandler::instance()->install(); // make sure we have a log handler! // File logger - m_fileLogger.reset(new CFileLogger(executable(), CDirectoryUtils::logDirectory())); + m_fileLogger.reset(new CFileLogger(this)); m_fileLogger->changeLogPattern(CLogPattern().withSeverityAtOrAbove(CStatusMessage::SeverityDebug)); } @@ -1636,164 +1623,20 @@ namespace BlackCore return CUrlList(); } -#ifdef BLACK_USE_CRASHPAD - //! Convert to file path - base::FilePath qstringToFilePath(const QString &str) - { -# ifdef Q_OS_WIN - return base::FilePath(str.toStdWString()); -# else - return base::FilePath(str.toStdString()); -# endif - } -#endif - - BlackMisc::CStatusMessageList CApplication::initCrashHandler() - { -#ifdef BLACK_USE_CRASHPAD - // No crash handling for unit tests - if (this->getApplicationInfo().isUnitTest()) { return CStatusMessage(this).info(u"No crash handler for unit tests"); } - - static const QString crashpadHandler(CBuildConfig::isRunningOnWindowsNtPlatform() ? "swift_crashpad_handler.exe" : "swift_crashpad_handler"); - static const QString handler = CFileUtils::appendFilePaths(CDirectoryUtils::binDirectory(), crashpadHandler); - // const QString crashpadPath = CDirectoryUtils::crashpadDirectory(); - const QString database = CDirectoryUtils::crashpadDatabaseDirectory(); - const QString metrics = CDirectoryUtils::crashpadMetricsDirectory(); - - if (!QFileInfo::exists(handler)) - { - return CStatusMessage(this).warning(u"Crashpad handler '%1' not found. Cannot init handler!") << handler; - } - - const CUrl serverUrl = this->getGlobalSetup().getCrashReportServerUrl(); - std::map annotations; - - // Backtrace annotations - annotations["token"] = "b15efd93e290be3cf5d39750cadc092b651327ff0c027b80abd75e0ee50df1da"; - annotations["format"] = "minidump"; - annotations["version"] = CBuildConfig::getVersionString().toStdString(); - annotations["platform"] = CBuildConfig::getPlatformString().toStdString(); - annotations["qtversion"] = QT_VERSION_STR; - - // add our logfile - const QString logFilePath = m_fileLogger->getLogFilePath(); // file and path - const QString logFileName = m_fileLogger->getLogFileName(); - const QString logAttachment = QStringLiteral("--attachment=attachment_%1=%2").arg(logFileName, logFilePath); - - std::vector arguments; - arguments.push_back(logAttachment.toStdString()); - - // and the simplified crash info if any - const QString crashInfoFileName("swiftcrashinfo.txt"); - const QString crashInfoFilePath(CFileUtils::appendFilePaths(CFileUtils::stripFileFromPath(logFilePath), crashInfoFileName)); - m_crashAndLogInfo.setLogPathAndFileName(crashInfoFilePath); - const QString crashAttachment = QStringLiteral("--attachment=attachment_%1=%2").arg(crashInfoFileName, crashInfoFilePath); - arguments.push_back(crashAttachment.toStdString()); - - // for testing purposes - if (CBuildConfig::isLocalDeveloperDebugBuild()) - { - arguments.push_back("--no-rate-limit"); - } - - QDir().mkpath(database); - m_crashReportDatabase = CrashReportDatabase::Initialize(qstringToFilePath(database)); - this->onCrashDumpUploadEnabledChanged(); // settings for crashpad uploads - m_crashpadClient = std::make_unique(); - m_crashpadClient->StartHandler(qstringToFilePath(handler), - qstringToFilePath(database), - qstringToFilePath(metrics), - serverUrl.getFullUrl().toStdString(), - annotations, - arguments, - false, true); - - this->crashAndLogAppendInfo(u"Init crash info at " % QDateTime::currentDateTimeUtc().toString()); - return CStatusMessage(this).info(u"Using crash handler"); -#else - return CStatusMessage(this).info(u"Not using crash handler"); -#endif - } - void CApplication::onCrashDumpUploadEnabledChanged() { - #ifdef BLACK_USE_CRASHPAD const bool enabled = CBuildConfig::isReleaseBuild() && m_crashDumpSettings.getThreadLocal().isEnabled(); this->enableCrashDumpUpload(enabled); - #endif - } - - void CApplication::triggerCrashInfoWrite() - { - m_crashAndLogInfo.triggerWritingFile(); - } - - void CApplication::setCrashInfo(const CCrashInfo &info) - { - m_crashAndLogInfo = info; - m_dsCrashAndLogInfo.inputSignal(); - } - - void CApplication::crashAndLogInfoUserName(const QString &name) - { - m_crashAndLogInfo.setUserName(name); - m_dsCrashAndLogInfo.inputSignal(); - } - - void CApplication::crashAndLogInfoSimulator(const QString &simulator) - { - m_crashAndLogInfo.setSimulatorString(simulator); - m_dsCrashAndLogInfo.inputSignal(); - } - - void CApplication::crashAndLogInfoFlightNetwork(const QString &flightNetwork) - { - m_crashAndLogInfo.setFlightNetworkString(flightNetwork); - m_dsCrashAndLogInfo.inputSignal(); - } - - void CApplication::crashAndLogAppendInfo(const QString &info) - { - m_crashAndLogInfo.appendInfo(info); - m_dsCrashAndLogInfo.inputSignal(); } void CApplication::simulateCrash() { -#ifdef BLACK_USE_CRASHPAD - CLogMessage(this).info(u"Simulated crash dump!"); - m_crashAndLogInfo.appendInfo("Simulated crash dump!"); - m_crashAndLogInfo.writeToFile(); - CRASHPAD_SIMULATE_CRASH(); - // real crash - // raise(SIGSEGV); #include -#else - CLogMessage(this).warning(u"This compiler or platform does not support crashpad. Cannot simulate crash dump!"); -#endif + CCrashHandler::instance()->simulateCrash(); } void CApplication::enableCrashDumpUpload(bool enable) { -#ifdef BLACK_USE_CRASHPAD - if (!m_crashReportDatabase) { return; } - crashpad::Settings *settings = m_crashReportDatabase->GetSettings(); - settings->SetUploadsEnabled(enable); -#else - Q_UNUSED(enable); -#endif - } - - bool CApplication::isCrashDumpUploadEnabled() const - { -#ifdef BLACK_USE_CRASHPAD - if (!m_crashReportDatabase) { return false; } - crashpad::Settings *settings = m_crashReportDatabase->GetSettings(); - bool enabled = false; - bool ok = settings->GetUploadsEnabled(&enabled); - return ok && enabled; -#else - return false; -#endif + CCrashHandler::instance()->setUploadsEnabled(enable); } bool CApplication::isSupportingCrashpad() const diff --git a/src/blackcore/application.h b/src/blackcore/application.h index 328e342fd..50a7da618 100644 --- a/src/blackcore/application.h +++ b/src/blackcore/application.h @@ -43,10 +43,6 @@ #include #include -#if !defined(Q_CC_MINGW) -#define BLACK_USE_CRASHPAD -#endif - class QHttpMultiPart; class QNetworkReply; class QNetworkRequest; @@ -57,12 +53,6 @@ namespace BlackMisc class CLogCategoryList; } -namespace crashpad -{ - class CrashpadClient; - class CrashReportDatabase; -} - namespace BlackCore { class CCoreFacade; @@ -303,26 +293,6 @@ namespace BlackCore virtual QString cmdLineArgumentsAsString(bool withExecutable = true); //! @} - // ----------------------- Crash info --------------------------------- - - //! Extra annotation for crash to easier identify annotation - void setCrashInfo(const BlackMisc::CCrashInfo &info); - - //! User name for crash info - void crashAndLogInfoUserName(const QString &name); - - //! Simulator string - void crashAndLogInfoSimulator(const QString &simulator); - - //! Flight network - void crashAndLogInfoFlightNetwork(const QString &flightNetwork); - - //! Append crash info - void crashAndLogAppendInfo(const QString &info); - - //! Get the crash info - const BlackMisc::CCrashInfo &getCrashInfo() const { return m_crashAndLogInfo; } - //! Simulate a crash //! \private only for testing purposes void simulateCrash(); @@ -331,9 +301,6 @@ namespace BlackCore //! \remark only change for testing void enableCrashDumpUpload(bool enable); - //! Is crash dump upload enabled - bool isCrashDumpUploadEnabled() const; - //! Has crashpad support? bool isSupportingCrashpad() const; @@ -727,22 +694,10 @@ namespace BlackCore bool m_localSettingsLoaded = false; //!< local settings loaded? // -------------- crashpad ----------------- - //! Init the crash handler - BlackMisc::CStatusMessageList initCrashHandler(); + BlackMisc::CSettingReadOnly m_crashDumpSettings { this, &CApplication::onCrashDumpUploadEnabledChanged }; //! Upload settings changed void onCrashDumpUploadEnabledChanged(); - -#ifdef BLACK_USE_CRASHPAD - std::unique_ptr m_crashpadClient; - std::unique_ptr m_crashReportDatabase; - BlackMisc::CSettingReadOnly m_crashDumpSettings { this, &CApplication::onCrashDumpUploadEnabledChanged }; -#endif - // crash info - void triggerCrashInfoWrite(); - - BlackMisc::CCrashInfo m_crashAndLogInfo; //!< info representing details - BlackMisc::CDigestSignal m_dsCrashAndLogInfo { this, &CApplication::triggerCrashInfoWrite, 10000, 5 }; }; } // namespace diff --git a/src/blackcore/blackcore.pro b/src/blackcore/blackcore.pro index 3811b6416..6adcdb7a3 100644 --- a/src/blackcore/blackcore.pro +++ b/src/blackcore/blackcore.pro @@ -20,9 +20,6 @@ INCLUDEPATH += pch DEFINES += LOG_IN_FILE BUILD_BLACKCORE_LIB -INCLUDEPATH *= $$EXTERNALSROOT/common/include/crashpad -INCLUDEPATH *= $$EXTERNALSROOT/common/include/crashpad/mini_chromium - HEADERS += *.h HEADERS += $$PWD/application/*.h HEADERS += $$PWD/audio/*.h @@ -41,13 +38,6 @@ LIBS *= -lvatlib DESTDIR = $$DestRoot/lib DLLDESTDIR = $$DestRoot/bin -msvc { - CONFIG(debug, debug|release): LIBS *= -lclientd -lutild -lbased -lRpcrt4 -lAdvapi32 - CONFIG(release, debug|release): LIBS *= -lclient -lutil -lbase -lRpcrt4 -lAdvapi32 -} -macx: LIBS *= -lclient -lutil -lbase -lbsm -framework Security -unix:!macx: LIBS *= -lclient -lutil -lbase - OTHER_FILES += readme.txt *.xml win32 { diff --git a/src/blackcore/data/globalsetup.cpp b/src/blackcore/data/globalsetup.cpp index afaed7dc9..27b593760 100644 --- a/src/blackcore/data/globalsetup.cpp +++ b/src/blackcore/data/globalsetup.cpp @@ -14,6 +14,7 @@ #include "blackmisc/network/server.h" #include "blackmisc/network/user.h" #include "blackmisc/stringutils.h" +#include "blackmisc/crashhandler.h" #include #include @@ -165,7 +166,7 @@ namespace BlackCore if (CBuildConfig::isLocalDeveloperDebugBuild()) { pingUrl.appendQuery("dev", "true"); } if (sApp) { - const CCrashInfo ci = sApp->getCrashInfo(); + const CCrashInfo ci = CCrashHandler::instance()->getCrashInfo(); pingUrl.appendQuery("application", sApp->getApplicationNameAndVersion()); if (!ci.getSimulatorString().isEmpty()) { pingUrl.appendQuery("fs", ci.getSimulatorString()); } if (!ci.getFlightNetworkString().isEmpty()) { pingUrl.appendQuery("network", ci.getFlightNetworkString()); } diff --git a/src/blackcore/simulator.cpp b/src/blackcore/simulator.cpp index dd90f4557..18f511ced 100644 --- a/src/blackcore/simulator.cpp +++ b/src/blackcore/simulator.cpp @@ -12,6 +12,7 @@ #include "blackcore/application.h" #include "blackmisc/simulation/data/modelcaches.h" #include "blackmisc/math/mathutils.h" +#include "blackmisc/crashhandler.h" #include "blackmisc/directoryutils.h" #include "blackmisc/threadutils.h" #include "blackmisc/logmessage.h" @@ -153,7 +154,7 @@ namespace BlackCore void ISimulator::reloadWeatherSettings() { // log crash info about weather - if (sApp && !sApp->isShuttingDown()) { sApp->crashAndLogAppendInfo(u"Simulator weather: " % boolToYesNo(m_isWeatherActivated)); } + if (sApp && !sApp->isShuttingDown()) { CCrashHandler::instance()->crashAndLogAppendInfo(u"Simulator weather: " % boolToYesNo(m_isWeatherActivated)); } if (!m_isWeatherActivated) { return; } const CWeatherScenario selectedWeatherScenario = m_weatherScenarioSettings.get(); if (!CWeatherScenario::isRealWeatherScenario(selectedWeatherScenario)) @@ -163,7 +164,7 @@ namespace BlackCore } // log crash info about weather - if (sApp && !sApp->isShuttingDown()) { sApp->crashAndLogAppendInfo(selectedWeatherScenario.toQString(true)); } + if (sApp && !sApp->isShuttingDown()) { CCrashHandler::instance()->crashAndLogAppendInfo(selectedWeatherScenario.toQString(true)); } } void ISimulator::clearAllRemoteAircraftData() @@ -872,7 +873,7 @@ namespace BlackCore const bool r = setup.isRenderingRestricted(); const bool e = setup.isRenderingEnabled(); - if (sApp && !sApp->isShuttingDown()) { sApp->crashAndLogAppendInfo(u"Rendering setup: " % setup.toQString(true)); } + if (sApp && !sApp->isShuttingDown()) { CCrashHandler::instance()->crashAndLogAppendInfo(u"Rendering setup: " % setup.toQString(true)); } emit this->renderRestrictionsChanged(r, e, setup.getMaxRenderedAircraft(), setup.getMaxRenderedDistance()); return true; } diff --git a/src/blackgui/components/dblogincomponent.cpp b/src/blackgui/components/dblogincomponent.cpp index a54f16917..621ecd976 100644 --- a/src/blackgui/components/dblogincomponent.cpp +++ b/src/blackgui/components/dblogincomponent.cpp @@ -18,6 +18,7 @@ #include "blackmisc/logmessage.h" #include "blackmisc/statusmessage.h" #include "blackmisc/verify.h" +#include "blackmisc/crashhandler.h" #include "blackconfig/buildconfig.h" #include @@ -164,8 +165,8 @@ namespace BlackGui } // crashpad info - sGui->crashAndLogInfoUserName(user.getRealNameAndId()); - sGui->crashAndLogAppendInfo(QStringLiteral("Login as user %1 %2").arg(user.getRealNameAndId(), user.getRolesAsString())); + CCrashHandler::instance()->crashAndLogInfoUserName(user.getRealNameAndId()); + CCrashHandler::instance()->crashAndLogAppendInfo(QStringLiteral("Login as user %1 %2").arg(user.getRealNameAndId(), user.getRolesAsString())); } else { diff --git a/src/blackgui/components/internalscomponent.cpp b/src/blackgui/components/internalscomponent.cpp index d0f5e5640..2c827977d 100644 --- a/src/blackgui/components/internalscomponent.cpp +++ b/src/blackgui/components/internalscomponent.cpp @@ -25,6 +25,7 @@ #include "blackmisc/math/mathutils.h" #include "blackmisc/logmessage.h" #include "blackmisc/statusmessage.h" +#include "blackmisc/crashhandler.h" #include "blackconfig/buildconfig.h" #include "ui_internalscomponent.h" @@ -93,7 +94,7 @@ namespace BlackGui if (sGui && sGui->isSupportingCrashpad()) { - ui->cb_CrashDumpUpload->setChecked(sGui->isCrashDumpUploadEnabled()); + ui->cb_CrashDumpUpload->setChecked(CCrashHandler::instance()->isCrashDumpUploadEnabled()); connect(ui->pb_SimulateCrash, &QPushButton::released, this, &CInternalsComponent::simulateCrash); connect(ui->cb_CrashDumpUpload, &QCheckBox::toggled, this, &CInternalsComponent::onCrashDumpUploadToggled); } @@ -348,7 +349,7 @@ namespace BlackGui { if (sGui && sGui->isSupportingCrashpad()) { - const bool current = sGui->isCrashDumpUploadEnabled(); + const bool current = CCrashHandler::instance()->isCrashDumpUploadEnabled(); if (current == checked) { return; } sGui->enableCrashDumpUpload(checked); } diff --git a/src/blackgui/components/logincomponent.cpp b/src/blackgui/components/logincomponent.cpp index 8878257b2..78c421e25 100644 --- a/src/blackgui/components/logincomponent.cpp +++ b/src/blackgui/components/logincomponent.cpp @@ -35,6 +35,7 @@ #include "blackmisc/simulation/aircraftmodel.h" #include "blackmisc/simulation/simulatedaircraft.h" #include "blackmisc/statusmessage.h" +#include "blackmisc/crashhandler.h" #include "blackconfig/buildconfig.h" #include @@ -324,9 +325,9 @@ namespace BlackGui { Q_ASSERT_X(currentServer.isValidForLogin(), Q_FUNC_INFO, "invalid server"); sGui->setExtraWindowTitle(QStringLiteral("[%1]").arg(ownAircraft.getCallsignAsString())); - sGui->crashAndLogInfoUserName(currentServer.getUser().getRealNameAndId()); - sGui->crashAndLogInfoFlightNetwork(currentServer.getEcosystem().toQString(true)); - sGui->crashAndLogAppendInfo(currentServer.getServerSessionId(false)); + CCrashHandler::instance()->crashAndLogInfoUserName(currentServer.getUser().getRealNameAndId()); + CCrashHandler::instance()->crashAndLogInfoFlightNetwork(currentServer.getEcosystem().toQString(true)); + CCrashHandler::instance()->crashAndLogAppendInfo(currentServer.getServerSessionId(false)); m_networkSetup.setLastServer(currentServer); m_lastAircraftModel.set(ownAircraft.getModel()); ui->le_LoginCallsign->setText(ownAircraft.getCallsignAsString()); @@ -550,7 +551,7 @@ namespace BlackGui const CSimulatorInfo sim = sGui->getIContextSimulator()->getSimulatorPluginInfo().getSimulator(); const CSimulatorInternals simulatorInternals = sGui->getIContextSimulator()->getSimulatorInternals(); const QString simStr = sim.toQString() + QStringLiteral(" ") + simulatorInternals.getSimulatorVersion(); - sGui->crashAndLogInfoSimulator(simStr); + CCrashHandler::instance()->crashAndLogInfoSimulator(simStr); } else { diff --git a/src/blackmisc/appstarttime.cpp b/src/blackmisc/appstarttime.cpp new file mode 100644 index 000000000..71eb9c361 --- /dev/null +++ b/src/blackmisc/appstarttime.cpp @@ -0,0 +1,18 @@ +/* Copyright (C) 2019 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +#include "appstarttime.h" + +namespace BlackMisc +{ + const QDateTime &getApplicationStartTimeUtc() + { + static const QDateTime gApplicationStartTimeUtc = QDateTime::currentDateTimeUtc(); + return gApplicationStartTimeUtc; + } +} diff --git a/src/blackmisc/appstarttime.h b/src/blackmisc/appstarttime.h new file mode 100644 index 000000000..acbb421f3 --- /dev/null +++ b/src/blackmisc/appstarttime.h @@ -0,0 +1,24 @@ +/* Copyright (C) 2019 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_APPSTARTTIME_H +#define BLACKMISC_APPSTARTTIME_H + +#include "blackmisc/blackmiscexport.h" + +#include + +namespace BlackMisc +{ + //! Get the application start time in UTC. + BLACKMISC_EXPORT const QDateTime &getApplicationStartTimeUtc(); +} + +#endif diff --git a/src/blackmisc/blackmisc.pro b/src/blackmisc/blackmisc.pro index 5eafb883b..b2267df81 100644 --- a/src/blackmisc/blackmisc.pro +++ b/src/blackmisc/blackmisc.pro @@ -70,6 +70,10 @@ SOURCES += *.cpp \ $$PWD/test/*.cpp \ $$PWD/weather/*.cpp + +INCLUDEPATH *= $$EXTERNALSROOT/common/include/crashpad +INCLUDEPATH *= $$EXTERNALSROOT/common/include/crashpad/mini_chromium + win32 { LIBS *= -lShell32 -lDbghelp -lversion # Remove the one below once the Reg functions are removed again from CIdentifier @@ -79,6 +83,13 @@ win32-g++ { LIBS *= -lpsapi } +msvc { + CONFIG(debug, debug|release): LIBS *= -lclientd -lutild -lbased -lRpcrt4 -lAdvapi32 + CONFIG(release, debug|release): LIBS *= -lclient -lutil -lbase -lRpcrt4 -lAdvapi32 +} +macx: LIBS += -lclient -lutil -lbase -lbsm -framework Security -framework CoreFoundation -framework ApplicationServices -framework Foundation +unix:!macx: LIBS *= -lclient -lutil -lbase + DESTDIR = $$DestRoot/lib DLLDESTDIR = $$DestRoot/bin diff --git a/src/blackmisc/crashhandler.cpp b/src/blackmisc/crashhandler.cpp new file mode 100644 index 000000000..270c5ae94 --- /dev/null +++ b/src/blackmisc/crashhandler.cpp @@ -0,0 +1,189 @@ +/* Copyright (C) 2019 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +#include "crashhandler.h" + +#include "blackconfig/buildconfig.h" +#include "blackmisc/appstarttime.h" +#include "blackmisc/directoryutils.h" +#include "blackmisc/logmessage.h" +#include "blackmisc/filelogger.h" + +#include +#include +#include + +#ifdef BLACK_USE_CRASHPAD +#if defined(Q_OS_WIN) && !defined(NOMINMAX) +#define NOMINMAX +#endif +#include "crashpad/client/crashpad_client.h" +#include "crashpad/client/crash_report_database.h" +#include "crashpad/client/settings.h" +#include "crashpad/client/simulate_crash.h" +#endif + +using namespace BlackConfig; +using namespace BlackMisc; +using namespace crashpad; + +namespace BlackMisc +{ + CCrashHandler *CCrashHandler::instance() + { + static CCrashHandler crashHandler; + return &crashHandler; + } + + CCrashHandler::~CCrashHandler() + { } + +#ifdef BLACK_USE_CRASHPAD + //! Convert to file path + base::FilePath qstringToFilePath(const QString &str) + { +# ifdef Q_OS_WIN + return base::FilePath(str.toStdWString()); +# else + return base::FilePath(str.toStdString()); +# endif + } +#endif + + void CCrashHandler::init() + { +#ifdef BLACK_USE_CRASHPAD + static const QString crashpadHandler(CBuildConfig::isRunningOnWindowsNtPlatform() ? "swift_crashpad_handler.exe" : "swift_crashpad_handler"); + static const QString handler = CFileUtils::appendFilePaths(CDirectoryUtils::binDirectory(), crashpadHandler); + const QString database = CDirectoryUtils::crashpadDatabaseDirectory(); + const QString metrics = CDirectoryUtils::crashpadMetricsDirectory(); + + if (!QFileInfo::exists(handler)) { return; } + + const std::string serverUrl("http://swift-project.sp.backtrace.io:6097/"); + std::map annotations; + + // Backtrace annotations + annotations["token"] = "b15efd93e290be3cf5d39750cadc092b651327ff0c027b80abd75e0ee50df1da"; + annotations["format"] = "minidump"; + annotations["version"] = CBuildConfig::getVersionString().toStdString(); + annotations["platform"] = CBuildConfig::getPlatformString().toStdString(); + annotations["qtversion"] = QT_VERSION_STR; + + // add our logfile + const QString logAttachment = QStringLiteral("--attachment=attachment_%1=%2").arg(CFileLogger::getLogFileName(), CFileLogger::getLogFilePath()); + + std::vector arguments; + arguments.push_back(logAttachment.toStdString()); + + // and the simplified crash info if any + const QString crashInfoFileName("swiftcrashinfo.txt"); + const QString crashInfoFilePath(CFileUtils::appendFilePaths(CFileUtils::stripFileFromPath(CFileLogger::getLogFilePath()), crashInfoFileName)); + m_crashAndLogInfo.setLogPathAndFileName(crashInfoFilePath); + const QString crashAttachment = QStringLiteral("--attachment=attachment_%1=%2").arg(crashInfoFileName, crashInfoFilePath); + arguments.push_back(crashAttachment.toStdString()); + + // for testing purposes + if (CBuildConfig::isLocalDeveloperDebugBuild()) + { + arguments.push_back("--no-rate-limit"); + } + + QDir().mkpath(database); + + m_crashReportDatabase = CrashReportDatabase::Initialize(qstringToFilePath(database)); + m_crashpadClient = std::make_unique(); + m_crashpadClient->StartHandler(qstringToFilePath(handler), + qstringToFilePath(database), + qstringToFilePath(metrics), + serverUrl, + annotations, + arguments, + false, true); + + this->crashAndLogAppendInfo(u"Init crash info at " % QDateTime::currentDateTimeUtc().toString()); + #endif + } + + void CCrashHandler::setUploadsEnabled(bool enable) + { + #ifdef BLACK_USE_CRASHPAD + if (!m_crashReportDatabase) { return; } + crashpad::Settings *settings = m_crashReportDatabase->GetSettings(); + settings->SetUploadsEnabled(enable); + #else + Q_UNUSED(enable); + #endif + } + + bool CCrashHandler::isCrashDumpUploadEnabled() const + { + #ifdef BLACK_USE_CRASHPAD + if (!m_crashReportDatabase) { return false; } + crashpad::Settings *settings = m_crashReportDatabase->GetSettings(); + bool enabled = false; + bool ok = settings->GetUploadsEnabled(&enabled); + return ok && enabled; + #else + return false; +#endif + } + + void CCrashHandler::triggerCrashInfoWrite() + { + m_crashAndLogInfo.triggerWritingFile(); + } + + void CCrashHandler::setCrashInfo(const CCrashInfo &info) + { + m_crashAndLogInfo = info; + m_dsCrashAndLogInfo.inputSignal(); + } + + void CCrashHandler::crashAndLogInfoUserName(const QString &name) + { + m_crashAndLogInfo.setUserName(name); + m_dsCrashAndLogInfo.inputSignal(); + } + + void CCrashHandler::crashAndLogInfoSimulator(const QString &simulator) + { + m_crashAndLogInfo.setSimulatorString(simulator); + m_dsCrashAndLogInfo.inputSignal(); + } + + void CCrashHandler::crashAndLogInfoFlightNetwork(const QString &flightNetwork) + { + m_crashAndLogInfo.setFlightNetworkString(flightNetwork); + m_dsCrashAndLogInfo.inputSignal(); + } + + void CCrashHandler::crashAndLogAppendInfo(const QString &info) + { + m_crashAndLogInfo.appendInfo(info); + m_dsCrashAndLogInfo.inputSignal(); + } + + void CCrashHandler::simulateCrash() + { + #ifdef BLACK_USE_CRASHPAD + CLogMessage(this).info(u"Simulated crash dump!"); + m_crashAndLogInfo.appendInfo("Simulated crash dump!"); + m_crashAndLogInfo.writeToFile(); + CRASHPAD_SIMULATE_CRASH(); + // real crash + // raise(SIGSEGV); #include + #else + CLogMessage(this).warning(u"This compiler or platform does not support crashpad. Cannot simulate crash dump!"); +#endif + } + + CCrashHandler::CCrashHandler(QObject *parent) : + QObject(parent) + { } +} diff --git a/src/blackmisc/crashhandler.h b/src/blackmisc/crashhandler.h new file mode 100644 index 000000000..b65f97cc4 --- /dev/null +++ b/src/blackmisc/crashhandler.h @@ -0,0 +1,92 @@ +/* Copyright (C) 2019 + * swift project Community / Contributors + * + * This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level + * directory of this distribution. No part of swift project, including this file, may be copied, modified, propagated, + * or distributed except according to the terms contained in the LICENSE file. + */ + +//! \file + +#ifndef BLACKMISC_CRASHHANDLER_H +#define BLACKMISC_CRASHHANDLER_H + +#include "blackmisc/blackmiscexport.h" +#include "blackmisc/crashinfo.h" +#include "blackmisc/digestsignal.h" + +#include + +#if !defined(Q_CC_MINGW) +#define BLACK_USE_CRASHPAD +#endif + +namespace crashpad +{ + class CrashpadClient; + class CrashReportDatabase; +} + +namespace BlackMisc +{ + //! Crash handler + class BLACKMISC_EXPORT CCrashHandler : public QObject + { + Q_OBJECT + + public: + //! Get singleton instance + static CCrashHandler *instance(); + + //! Destructor + virtual ~CCrashHandler(); + + //! Initialize and start crashpad handler process + void init(); + + //! Enable/disable automatic uploading + void setUploadsEnabled(bool enable); + + //! Is automatic dump uploading enabled? + bool isCrashDumpUploadEnabled() const; + + // ----------------------- Crash info --------------------------------- + + //! Extra annotation for crash to easier identify annotation + void setCrashInfo(const BlackMisc::CCrashInfo &info); + + //! Set user name for crash info + void crashAndLogInfoUserName(const QString &name); + + //! Set simulator string in crash info + void crashAndLogInfoSimulator(const QString &simulator); + + //! Set flight network in crash info + void crashAndLogInfoFlightNetwork(const QString &flightNetwork); + + //! Append crash info + void crashAndLogAppendInfo(const QString &info); + + //! Get crash info + const BlackMisc::CCrashInfo &getCrashInfo() const { return m_crashAndLogInfo; } + + //! Simulate a crash + void simulateCrash(); + + private: + CCrashHandler(QObject *parent = nullptr); + + // crash info + void triggerCrashInfoWrite(); + + BlackMisc::CCrashInfo m_crashAndLogInfo; //!< info representing details + BlackMisc::CDigestSignal m_dsCrashAndLogInfo { this, &CCrashHandler::triggerCrashInfoWrite, 10000, 5 }; + +#ifdef BLACK_USE_CRASHPAD + std::unique_ptr m_crashpadClient; + std::unique_ptr m_crashReportDatabase; +#endif + }; +} + +#endif diff --git a/src/blackmisc/filelogger.cpp b/src/blackmisc/filelogger.cpp index 5f0ca9770..e6f88d87d 100644 --- a/src/blackmisc/filelogger.cpp +++ b/src/blackmisc/filelogger.cpp @@ -7,8 +7,10 @@ */ #include "blackconfig/buildconfig.h" +#include "blackmisc/appstarttime.h" #include "blackmisc/filelogger.h" #include "blackmisc/loghandler.h" +#include "blackmisc/directoryutils.h" #include #include @@ -26,22 +28,33 @@ using namespace BlackConfig; namespace BlackMisc { - CFileLogger::CFileLogger(QObject *parent) : - CFileLogger(QCoreApplication::applicationName(), QString(), parent) + //! Get application name + QString applicationName() { - // void + static const QString applicationName = QFileInfo(QCoreApplication::applicationFilePath()).completeBaseName(); + return applicationName; } - CFileLogger::CFileLogger(const QString &applicationName, const QString &logPath, QObject *parent) : - QObject(parent), - m_logFile(this), - m_applicationName(applicationName), - m_logPath(logPath) + //! Get log file name + QString logFileName() { - if (!m_logPath.isEmpty()) { QDir::root().mkpath(m_logPath); } + static const QString fileName = applicationName() % + QLatin1String("_") % + getApplicationStartTimeUtc().toString(QStringLiteral("yyMMddhhmmss")) % + QLatin1String("_") % + QString::number(QCoreApplication::applicationPid()) % + QLatin1String(".log"); + return fileName; + } + + CFileLogger::CFileLogger(QObject *parent) : + QObject(parent), + m_logFile(this) + { + Q_ASSERT(! applicationName().isEmpty()); + QDir::root().mkpath(CDirectoryUtils::logDirectory()); removeOldLogFiles(); - if (!m_logPath.isEmpty() && !m_logPath.endsWith('/')) { m_logPath += '/'; } - m_logFile.setFileName(getFullFileName()); + m_logFile.setFileName(getLogFilePath()); m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text); m_stream.setDevice(&m_logFile); m_stream.setCodec("UTF-8"); @@ -66,6 +79,11 @@ namespace BlackMisc } } + QString CFileLogger::getLogFileName() + { + return logFileName(); + } + void CFileLogger::ps_writeStatusMessageToFile(const BlackMisc::CStatusMessage &statusMessage) { if (statusMessage.isEmpty()) { return; } @@ -85,26 +103,17 @@ namespace BlackMisc writeContentToFile(finalContent); } - QString CFileLogger::getFullFileName() + QString CFileLogger::getLogFilePath() { - QString filePath; - Q_ASSERT(!m_applicationName.isEmpty()); - if (!m_logPath.isEmpty()) filePath += m_logPath; - - m_fileName += m_applicationName; - m_fileName += QLatin1String("_"); - m_fileName += QDateTime::currentDateTime().toString(QStringLiteral("yyMMddhhmmss")); - m_fileName += QLatin1String("_"); - m_fileName += QString::number(QCoreApplication::applicationPid()); - m_fileName += QLatin1String(".log"); - return filePath + m_fileName; + QString filePath = CDirectoryUtils::logDirectory() % '/' % logFileName(); + return filePath; } void CFileLogger::removeOldLogFiles() { - QString nameFilter(m_applicationName); + QString nameFilter(applicationName()); nameFilter += QLatin1String("*.log"); - QDir dir(m_logPath, nameFilter, QDir::Name, QDir::Files); + QDir dir(CDirectoryUtils::logDirectory(), nameFilter, QDir::Name, QDir::Files); QDateTime now = QDateTime::currentDateTime(); for (const auto &logFileInfo : dir.entryInfoList()) @@ -118,7 +127,7 @@ namespace BlackMisc void CFileLogger::writeHeaderToFile() { - m_stream << "This is " << m_applicationName; + m_stream << "This is " << applicationName(); m_stream << " version " << CBuildConfig::getVersionString(); m_stream << " running on " << QSysInfo::prettyProductName(); m_stream << " " << QSysInfo::currentCpuArchitecture() << endl; diff --git a/src/blackmisc/filelogger.h b/src/blackmisc/filelogger.h index 2ecfec289..ff295cecd 100644 --- a/src/blackmisc/filelogger.h +++ b/src/blackmisc/filelogger.h @@ -32,16 +32,6 @@ namespace BlackMisc //! Filename defaults to QCoreApplication::applicationName() and path to "." CFileLogger(QObject *parent = nullptr); - /*! - * Constructor - * \param applicationName Use the applications name without any extension. - * A timestamp and extension will be added automatically. - * \param logPath Path the log files is written to. If you leave this empty, the - * file will be written in the working directory of the binary. - * \param parent QObject parent - */ - CFileLogger(const QString &applicationName, const QString &logPath, QObject *parent = nullptr); - //! Destructor. virtual ~CFileLogger(); @@ -51,20 +41,18 @@ namespace BlackMisc //! Close file void close(); - //! Get the current log file path - QString getLogFilePath() const { return m_logFile.fileName(); } + //! Get the log file name + static QString getLogFileName(); - //! Get the current log file name - QString getLogFileName() const { return m_fileName; } + //! Get the log file path (including its name) + static QString getLogFilePath(); private slots: //! Write single status message to file void ps_writeStatusMessageToFile(const BlackMisc::CStatusMessage &statusMessage); private: - QString getFullFileName(); void removeOldLogFiles(); - void writeHeaderToFile(); void writeContentToFile(const QString &content); @@ -72,8 +60,6 @@ namespace BlackMisc QFile m_logFile; QString m_fileName; QTextStream m_stream; - QString m_applicationName; - QString m_logPath; //!< Empty by default. Hence the working directory "." is used QString m_previousCategories; }; } diff --git a/src/swiftcore/main.cpp b/src/swiftcore/main.cpp index 3ed085993..4486f8adf 100644 --- a/src/swiftcore/main.cpp +++ b/src/swiftcore/main.cpp @@ -10,6 +10,8 @@ #include "blackgui/guiapplication.h" #include "blackmisc/icons.h" #include "blackmisc/directoryutils.h" +#include "blackmisc/crashhandler.h" +#include "blackmisc/appstarttime.h" #include "swiftcore.h" #include @@ -28,6 +30,8 @@ int main(int argc, char *argv[]) CGuiApplication::highDpiScreenSupport(CGuiApplication::parseScaleFactor(argc, argv)); QApplication qa(argc, argv); Q_UNUSED(qa); // init of qa is required, but qa not used + + CCrashHandler::instance()->init(); CGuiApplication a(CApplicationInfo::swiftCore(), CApplicationInfo::PilotClientCore, CIcons::swiftCore24()); a.addWindowStateOption(); a.addDBusAddressOption(); diff --git a/src/swiftdata/main.cpp b/src/swiftdata/main.cpp index 31831b043..9174d80eb 100644 --- a/src/swiftdata/main.cpp +++ b/src/swiftdata/main.cpp @@ -10,6 +10,8 @@ #include "blackgui/guiapplication.h" #include "blackmisc/directoryutils.h" #include "blackmisc/icons.h" +#include "blackmisc/crashhandler.h" +#include "blackmisc/appstarttime.h" #include "swiftdata.h" #include @@ -26,6 +28,8 @@ int main(int argc, char *argv[]) CGuiApplication::highDpiScreenSupport(CGuiApplication::parseScaleFactor(argc, argv)); QApplication qa(argc, argv); Q_UNUSED(qa); + + CCrashHandler::instance()->init(); CGuiApplication a(CApplicationInfo::swiftMappingTool(), CApplicationInfo::MappingTool, CIcons::swiftDatabase48()); a.setSignalStartupAutomatically(false); // application will signal startup on its own a.splashScreen(CIcons::swiftDatabase256()); diff --git a/src/swiftguistandard/main.cpp b/src/swiftguistandard/main.cpp index 8d9b98ff7..fdf47f452 100644 --- a/src/swiftguistandard/main.cpp +++ b/src/swiftguistandard/main.cpp @@ -9,6 +9,8 @@ #include "blackgui/enableforframelesswindow.h" #include "blackgui/guiapplication.h" #include "blackmisc/directoryutils.h" +#include "blackmisc/crashhandler.h" +#include "blackmisc/appstarttime.h" #include "swiftguistd.h" #include "swiftguistdapplication.h" @@ -25,6 +27,8 @@ int main(int argc, char *argv[]) CGuiApplication::highDpiScreenSupport(CGuiApplication::parseScaleFactor(argc, argv)); QApplication qa(argc, argv); Q_UNUSED(qa); // application init needed + + CCrashHandler::instance()->init(); CSwiftGuiStdApplication a; // application with contexts a.setSignalStartupAutomatically(false); // application will signal startup on its own a.splashScreen(CIcons::swift256());