diff --git a/src/libutil/error.cc b/src/libutil/error.cc index 8d6a1d9d9f5b..758e734a3bb8 100644 --- a/src/libutil/error.cc +++ b/src/libutil/error.cc @@ -6,6 +6,7 @@ #include "nix/util/signals.hh" #include "nix/util/terminal.hh" #include "nix/util/position.hh" +#include "nix/util/sentry.hh" #include #include @@ -452,6 +453,7 @@ void panic(std::string_view msg) writeErr("\n\n" ANSI_RED "terminating due to unexpected unrecoverable internal error: " ANSI_NORMAL); writeErr(msg); writeErr("\n"); + setSentryTag("panic_msg", std::string(msg).c_str()); std::terminate(); } diff --git a/src/nix/crash.cc b/src/nix/crash.cc index 3ed491ce75e4..6778d70894ab 100644 --- a/src/nix/crash.cc +++ b/src/nix/crash.cc @@ -4,6 +4,11 @@ using namespace nix; +static void doThrow() +{ + throw Error("exception in destructor"); +} + struct CmdCrash : Command { std::string type; @@ -41,6 +46,25 @@ struct CmdCrash : Command std::bitset<4>{"012"}; } + else if (type == "panic") { + printError("Triggering a panic..."); + panic("test panic"); + } + + else if (type == "terminate") { + printError("Triggering an std::terminate..."); + + struct Foo + { + ~Foo() + { + doThrow(); + } + }; + + Foo foo; + } + else { throw Error("unknown crash type '%s'", type); } diff --git a/src/nix/main.cc b/src/nix/main.cc index 4758ca1082fc..7d7dbb44ac5b 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -384,6 +384,33 @@ struct CmdHelpStores : Command static auto rCmdHelpStores = registerCommand("help-stores"); +static void terminateHandler() +{ + // Add the exception type and message to the Sentry crash report. + auto ex = std::current_exception(); + if (ex) { + try { + std::rethrow_exception(ex); + } catch (BaseError & e) { + try { + nix::setSentryTag("terminate_exception_type", typeid(e).name()); + nix::setSentryTag("terminate_exception_msg", e.message().c_str()); + } catch (...) { + } + } catch (const std::exception & e) { + try { + nix::setSentryTag("terminate_exception_type", typeid(e).name()); + nix::setSentryTag("terminate_exception_msg", e.what()); + } catch (...) { + } + } + } + + // Call the original terminate handler. + std::set_terminate(nullptr); + std::terminate(); +} + void mainWrapped(int argc, char ** argv) { savedArgv = argv; @@ -423,6 +450,7 @@ void mainWrapped(int argc, char ** argv) sentry_init(options); setSentryTag = [](const char * key, const char * value) { sentry_set_tag(key, value); }; setSentryTag("nix_command", argc > 0 ? std::string(baseNameOf(argv[0])).c_str() : ""); + std::set_terminate(terminateHandler); sentryEnabled = true; } diff --git a/tests/functional/sentry.sh b/tests/functional/sentry.sh index 0be7a3d305a4..b739f1ff3ad6 100644 --- a/tests/functional/sentry.sh +++ b/tests/functional/sentry.sh @@ -27,7 +27,7 @@ waitForCrashDump() { return 1 } -for type in segfault assert logic-error; do +for type in segfault assert logic-error panic terminate; do if [[ $type = logic-error && $(uname) = Darwin ]]; then continue; fi rm -rf "$sentryDir"