From b1f0f1c5a1ff8d988f5cd6d57128d1374123ba2a Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Wed, 12 Mar 2025 08:29:29 +0000 Subject: [PATCH] port crash-handler from lix to nix It was first introduced in https://git.lix.systems/lix-project/lix/commit/19e0ce2c03d8e0baa16998b086665664c420c1df In Nix we only register the crash handler in main instead of initNix, because library user may want to use their own crash handler. Sample output: Mar 12 08:38:06 eve nix[2303762]: Nix crashed. This is a bug. Please report this at https://github.com/NixOS/nix/issues with the following information included: Mar 12 08:38:06 eve nix[2303762]: Exception: nix::SysError: error: writing to file: Resource temporarily unavailable Mar 12 08:38:06 eve nix[2303762]: Stack trace: Mar 12 08:38:06 eve nix[2303762]: 0# 0x000000000076876A in nix 1# 0x00007FDA40E9F20A in /nix/store/2lhklm5aizx30qbw49acnrrzkj9lbmij-gcc-14-20241116-lib/lib/libstdc++.so.6 2# std::unexpected() in /nix/store/2lhklm5aizx30qbw49acnrrzkj9lbmij-gcc-14-20241116-lib/lib/libstdc++.so.6 3# 0x00007FDA40E9F487 in /nix/store/2lhklm5aizx30qbw49acnrrzkj9lbmij-gcc-14-20241116-lib/lib/libstdc++.so.6 4# nix::writeFull(int, std::basic_string_view >, bool) in /home/joerg/git/nix/inst/lib/libnixutil.so 5# nix::writeLine(int, std::__cxx11::basic_string, std::allocator >) in /home/joerg/git/nix/inst/lib/libnixutil.so 6# nix::JSONLogger::write(nlohmann::json_abi_v3_11_3::basic_json, std::allocator >, bool, long, unsigned long, double, std::allocator, nlohmann::json_abi_v3_11_3::adl_serializer, std::vector >, void> const&) in /home/joerg/git/nix/inst/lib/libnixutil.so 7# nix::JSONLogger::logEI(nix::ErrorInfo const&) in /home/joerg/git/nix/inst/lib/libnixutil.so 8# nix::Logger::logEI(nix::Verbosity, nix::ErrorInfo) in nix 9# nix::handleExceptions(std::__cxx11::basic_string, std::allocator > const&, std::function) in /home/joerg/git/nix/inst/lib/libnixmain.so 10# 0x000000000087A563 in nix 11# 0x00007FDA40BD41FE in /nix/store/6q2mknq81cyscjmkv72fpcsvan56qhmg-glibc-2.40-66/lib/libc.so.6 12# __libc_start_main in /nix/store/6q2mknq81cyscjmkv72fpcsvan56qhmg-glibc-2.40-66/lib/libc.so.6 13# 0x00000000006F4DF5 in nix Co-authored-by: eldritch horrors (cherry picked from commit 163f94412a36c7f0ac28440db4b8e3179d07e505) --- src/nix/crash-handler.cc | 67 ++++++++++++++++++++++++++++++++++++++++ src/nix/crash-handler.hh | 11 +++++++ src/nix/main.cc | 3 ++ src/nix/meson.build | 1 + 4 files changed, 82 insertions(+) create mode 100644 src/nix/crash-handler.cc create mode 100644 src/nix/crash-handler.hh diff --git a/src/nix/crash-handler.cc b/src/nix/crash-handler.cc new file mode 100644 index 000000000..8ffd436ac --- /dev/null +++ b/src/nix/crash-handler.cc @@ -0,0 +1,67 @@ +#include "crash-handler.hh" +#include "fmt.hh" +#include "logging.hh" + +#include +#include +#include + +// Darwin and FreeBSD stdenv do not define _GNU_SOURCE but do have _Unwind_Backtrace. +#if __APPLE__ || __FreeBSD__ +# define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED +#endif + +#include + +#ifndef _WIN32 +# include +#endif + +namespace nix { + +namespace { + +void logFatal(std::string const & s) +{ + writeToStderr(s + "\n"); + // std::string for guaranteed null termination +#ifndef _WIN32 + syslog(LOG_CRIT, "%s", s.c_str()); +#endif +} + +void onTerminate() +{ + logFatal( + "Nix crashed. This is a bug. Please report this at https://github.com/NixOS/nix/issues with the following information included:\n"); + try { + std::exception_ptr eptr = std::current_exception(); + if (eptr) { + std::rethrow_exception(eptr); + } else { + logFatal("std::terminate() called without exception"); + } + } catch (const std::exception & ex) { + logFatal(fmt("Exception: %s: %s", boost::core::demangle(typeid(ex).name()), ex.what())); + } catch (...) { + logFatal("Unknown exception!"); + } + + logFatal("Stack trace:"); + std::stringstream ss; + ss << boost::stacktrace::stacktrace(); + logFatal(ss.str()); + + std::abort(); +} +} + +void registerCrashHandler() +{ + // DO NOT use this for signals. Boost stacktrace is very much not + // async-signal-safe, and in a world with ASLR, addr2line is pointless. + // + // If you want signals, set up a minidump system and do it out-of-process. + std::set_terminate(onTerminate); +} +} diff --git a/src/nix/crash-handler.hh b/src/nix/crash-handler.hh new file mode 100644 index 000000000..018e86747 --- /dev/null +++ b/src/nix/crash-handler.hh @@ -0,0 +1,11 @@ +#pragma once +/// @file Crash handler for Nix that prints back traces (hopefully in instances where it is not just going to crash the +/// process itself). + +namespace nix { + +/** Registers the Nix crash handler for std::terminate (currently; will support more crashes later). See also + * detectStackOverflow(). */ +void registerCrashHandler(); + +} diff --git a/src/nix/main.cc b/src/nix/main.cc index c5e9c0e7f..0a6b77e9e 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -20,6 +20,7 @@ #include "flake/flake.hh" #include "self-exe.hh" #include "json-utils.hh" +#include "crash-handler.hh" #include #include @@ -354,6 +355,8 @@ void mainWrapped(int argc, char * * argv) { savedArgv = argv; + registerCrashHandler(); + /* The chroot helper needs to be run before any threads have been started. */ #ifndef _WIN32 diff --git a/src/nix/meson.build b/src/nix/meson.build index 398750498..79ad840f6 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -77,6 +77,7 @@ nix_sources = [config_h] + files( 'config-check.cc', 'config.cc', 'copy.cc', + 'crash-handler.cc', 'derivation-add.cc', 'derivation-show.cc', 'derivation.cc',