From 7f07f98f92f90838ab65a3ae8d578c59672a7cee Mon Sep 17 00:00:00 2001 From: tobtoht Date: Thu, 26 Jan 2023 13:12:33 +0100 Subject: [PATCH] Linux: stack trace on crash --- CMakeLists.txt | 15 +++++++-- contrib/depends/packages/boost.mk | 2 +- contrib/guix/libexec/build.sh | 1 + src/CMakeLists.txt | 15 +++++++++ src/WindowManager.cpp | 55 +++++++++++++++++++++++++++++++ src/WindowManager.h | 1 + src/main.cpp | 35 ++++++++++++++++++++ 7 files changed, 120 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f217b7d..d014361 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,7 @@ option(PLATFORM_INSTALLER "Built-in updater fetches installer (windows-only)" OF option(USE_DEVICE_TREZOR "Trezor support compilation" ON) option(DONATE_BEG "Prompt donation window every once in a while" OFF) option(WITH_SCANNER "Enable webcam QR scanner" ON) +option(STACK_TRACE "Dump stack trace on crash (Linux only)" OFF) list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/cmake") include(CheckCCompilerFlag) @@ -105,8 +106,8 @@ if(MINGW) set(Boost_THREADAPI win32) endif() -set(Boost_USE_MULTITHREADED ON) -find_package(Boost 1.58 REQUIRED COMPONENTS + +set(BOOST_COMPONENTS system filesystem thread @@ -116,7 +117,15 @@ find_package(Boost 1.58 REQUIRED COMPONENTS serialization program_options locale - ) +) + +if(STACK_TRACE AND UNIX AND NOT APPLE) + list(APPEND BOOST_COMPONENTS + stacktrace_basic) +endif() + +set(Boost_USE_MULTITHREADED ON) +find_package(Boost 1.58 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) if(UNIX AND NOT APPLE) if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/contrib/depends/packages/boost.mk b/contrib/depends/packages/boost.mk index e77e278..ae5bacd 100644 --- a/contrib/depends/packages/boost.mk +++ b/contrib/depends/packages/boost.mk @@ -22,7 +22,7 @@ $(package)_toolset_$(host_os)=gcc $(package)_archiver_$(host_os)=$($(package)_ar) $(package)_toolset_darwin=darwin $(package)_archiver_darwin=$($(package)_libtool) -$(package)_config_libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale +$(package)_config_libraries=chrono,filesystem,program_options,system,thread,test,date_time,regex,serialization,locale,stacktrace $(package)_cxxflags=-std=c++17 $(package)_cxxflags_linux=-fPIC $(package)_cxxflags_freebsd=-fPIC diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh index edcb336..5393853 100755 --- a/contrib/guix/libexec/build.sh +++ b/contrib/guix/libexec/build.sh @@ -284,6 +284,7 @@ mkdir -p "$DISTSRC" esac ;; *linux*) + CMAKEVARS+=" -DSTACK_TRACE=ON" case "$OPTIONS" in tails) CMAKEVARS+=" -DTOR_DIR=Off -DTOR_VERSION=Off" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 59014fc..03e4fbc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -117,6 +117,13 @@ set_target_properties(feather PROPERTIES LINK_FLAGS_RELEASE -s ) +if(STACK_TRACE) + message(STATUS "Stack Trace Enabled") + if (STATIC) + set_property(TARGET feather APPEND PROPERTY LINK_FLAGS "-Wl,--wrap=__cxa_throw") + endif() +endif() + target_include_directories(feather PUBLIC ${CMAKE_BINARY_DIR}/src/feather_autogen/include ${CMAKE_SOURCE_DIR}/monero/include @@ -186,6 +193,10 @@ if(PLATFORM_INSTALLER) target_compile_definitions(feather PRIVATE PLATFORM_INSTALLER=1) endif() +if(STACK_TRACE) + target_compile_definitions(feather PRIVATE STACK_TRACE=1) +endif() + if(HAVE_SYS_PRCTL_H) target_compile_definitions(feather PRIVATE HAVE_SYS_PRCTL_H=1) endif() @@ -290,6 +301,10 @@ if(DEPENDS AND APPLE) ${CMAKE_OSX_SYSROOT}/lib/darwin/libclang_rt.osx.a) endif() +if(STACK_TRACE AND CMAKE_C_COMPILER_ID STREQUAL "GNU") + target_link_libraries(feather -rdynamic) +endif() + install(TARGETS feather DESTINATION ${CMAKE_INSTALL_PREFIX} ) diff --git a/src/WindowManager.cpp b/src/WindowManager.cpp index ec0fda7..e6758ad 100644 --- a/src/WindowManager.cpp +++ b/src/WindowManager.cpp @@ -3,6 +3,7 @@ #include "WindowManager.h" +#include #include #include @@ -38,6 +39,8 @@ WindowManager::WindowManager(EventFilter *eventFilter) this->initSkins(); + this->showCrashLogs(); + if (!config()->get(Config::firstRun).toBool() || TailsOS::detect() || WhonixOS::detect()) { this->onInitialNetworkConfigured(); } @@ -410,6 +413,58 @@ void WindowManager::displayWalletErrorMessage(const QString &message) { } } +void WindowManager::showCrashLogs() { + QString crashLogPath{Config::defaultConfigDir().path() + "/crash_report.txt"}; + QFile crashLogFile{crashLogPath}; + + if (!crashLogFile.exists()) { + return; + } + + bool r = crashLogFile.open(QIODevice::ReadOnly); + if (!r) { + qWarning() << "Unable to open crash log file: " << crashLogPath; + return; + } + + QTextStream log(&crashLogFile); + QString logString = log.readAll(); + crashLogFile.close(); + + bool renamed = false; + for (int i = 1; i < 999; i++) { + QString name{QString("/crash_report_%1.txt").arg(QString::number(i))}; + if (crashLogFile.rename(Config::defaultConfigDir().path() + name)) { + renamed = true; + break; + } + } + + if (!renamed) { + crashLogFile.remove(); + } + + QDialog dialog(nullptr); + dialog.setWindowTitle("Crash report"); + + QVBoxLayout layout; + QLabel msg{"Feather encountered an unrecoverable error.\n\nPlease send a copy of these logs to the developers. Logs are not automatically reported.\n"}; + QTextEdit logs; + logs.setText(logString); + + layout.addWidget(&msg); + layout.addWidget(&logs); + QDialogButtonBox buttons(QDialogButtonBox::Ok); + layout.addWidget(&buttons); + dialog.setLayout(&layout); + QObject::connect(&buttons, &QDialogButtonBox::accepted, [&dialog]{ + dialog.close(); + }); + dialog.exec(); + + exit(0); +} + // ######################## DEVICE ######################## void WindowManager::onDeviceButtonRequest(quint64 code) { diff --git a/src/WindowManager.h b/src/WindowManager.h index 6df35db..4d3c7c0 100644 --- a/src/WindowManager.h +++ b/src/WindowManager.h @@ -69,6 +69,7 @@ private: void buildTrayMenu(); void startupWarning(); void showWarningMessageBox(const QString &title, const QString &message); + void showCrashLogs(); void quitAfterLastWindow(); diff --git a/src/main.cpp b/src/main.cpp index d3e2bcf..ab89fc7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,6 +12,16 @@ #include "MainWindow.h" #include "utils/EventFilter.h" #include "WindowManager.h" +#include "config.h" + +#if defined(Q_OS_LINUX) && defined(STACK_TRACE) +#define BOOST_STACKTRACE_LINK +#include +#include +#include +#endif + +#include #if defined(Q_OS_WIN) #include @@ -22,10 +32,35 @@ Q_IMPORT_PLUGIN(QXcbIntegrationPlugin) #endif +#if defined(Q_OS_LINUX) && defined(STACK_TRACE) +void signal_handler(int signum) { + ::signal(signum, SIG_DFL); + std::stringstream keyStream; + keyStream << boost::stacktrace::stacktrace(); + std::cout << keyStream.str(); + + // Write stack trace to disk + QString crashLogPath{Config::defaultConfigDir().path() + "/crash_report.txt"}; + std::ofstream out(crashLogPath.toStdString()); + out << keyStream.str(); + out.close(); + + // Make a last ditch attempt to restart the application + QProcess::startDetached(qApp->arguments()[0], qApp->arguments()); + + ::raise(SIGABRT); +} +#endif + int main(int argc, char *argv[]) { Q_INIT_RESOURCE(assets); +#if defined(Q_OS_LINUX) && defined(STACK_TRACE) + ::signal(SIGSEGV, &signal_handler); + ::signal(SIGABRT, &signal_handler); +#endif + #if defined(HAS_TOR_BIN) Q_INIT_RESOURCE(assets_tor); #endif