Enable stacktrace support in gitian builds (#3006)

* Remove use of -rdynamic

This causes check-symbols to fail horribly and also turned out to be not
required when using libbacktrace. It was only required when using
"backtrace()" from "<execinfo.h>"

* Remove spurious ], from configure.ac

* Add -DENABLE_STACKTRACES=1 to CMakeLists.txt

* Remove unused method my_backtrace_simple_callback

* Use fs::path().filename() instead of basename()

* Add static g_exeFileName and g_exeFileBaseName

* Use .exe.dbg file when available

* Use uint64_t instead of uintptr_t

* Implement GetBaseAddress() for unix and win32

* Implement unified crash_info and use it everywhere before printing crash info

* Print a serialized version of crash_info when there is no debug info

* Implement "-printcrashinfo" command line option

* Compile stacktrace support unconditionally and only make crash hooks conditional

This also renames the --enable-stacktraces option to --enable-crash-hooks

* Enable crash hooks in win/linux Gitian builds

* Try to load .debug file on MacOS and enable crash hooks for osx Gitian builds

* Check for dsymutil and if it needs --flat

* Create .debug files in osx Gitian build

* Handle review comments

* Also print crash description when no stacktrace is available

* Unconditionally add -g1 debug information

Instead of making it dependent on "--enable-crash-hooks". We will need the
debug info every time now, even in release builds.

* Put MacOS debug info into dSYM symbols instead of plain .debug files

* Implement MacOS specific GetBaseAddress
This commit is contained in:
Alexander Block 2019-07-02 06:16:11 +02:00 committed by UdjinM6
parent 5809c5c3d0
commit 780bffeb78
13 changed files with 371 additions and 145 deletions

View File

@ -33,6 +33,7 @@ if(DEFINED DEPENDS_PREFIX)
endif() endif()
add_definitions( add_definitions(
-DENABLE_CRASH_HOOKS=1
-DENABLE_WALLET=1 -DENABLE_WALLET=1
) )

View File

@ -87,6 +87,7 @@ AC_PATH_PROG(HEXDUMP,hexdump)
AC_PATH_TOOL(READELF, readelf) AC_PATH_TOOL(READELF, readelf)
AC_PATH_TOOL(CPPFILT, c++filt) AC_PATH_TOOL(CPPFILT, c++filt)
AC_PATH_TOOL(OBJCOPY, objcopy) AC_PATH_TOOL(OBJCOPY, objcopy)
AC_PATH_TOOL(DSYMUTIL, dsymutil)
AC_ARG_VAR(PYTHONPATH, Augments the default search path for python module files) AC_ARG_VAR(PYTHONPATH, Augments the default search path for python module files)
@ -192,12 +193,12 @@ AC_ARG_ENABLE([debug],
[enable_debug=$enableval], [enable_debug=$enableval],
[enable_debug=no]) [enable_debug=no])
# Enable exception stacktraces # Enable crash hooks
AC_ARG_ENABLE([stacktraces], AC_ARG_ENABLE([crash-hooks],
[AS_HELP_STRING([--enable-stacktraces], [AS_HELP_STRING([--enable-crash-hooks],
[gather and print exception stack traces (default is no)])], [hook into exception/signal/assert handling to gather stack traces (default is no)])],
[enable_stacktraces=$enableval], [enable_crashhooks=$enableval],
[enable_stacktraces=no]) [enable_crashhooks=no])
# Enable in-wallet miner # Enable in-wallet miner
AC_ARG_ENABLE([miner], AC_ARG_ENABLE([miner],
@ -229,9 +230,10 @@ if test "x$enable_debug" = xyes; then
if test "x$GXX" = xyes; then if test "x$GXX" = xyes; then
CXXFLAGS="$CXXFLAGS -g3 -O0" CXXFLAGS="$CXXFLAGS -g3 -O0"
fi fi
elif test "x$enable_stacktraces" = xyes; then else
# Enable debug information but don't turn off optimization # We always enable at at least -g1 debug info to support proper stacktraces in crash infos
# (stacktraces will be suboptimal, but better than nothing) # Stacktraces will be suboptimal due to optimization, but better than nothing. Also, -fno-omit-frame-pointer
# mitigates this a little bit
if test "x$GCC" = xyes; then if test "x$GCC" = xyes; then
CFLAGS="$CFLAGS -g1 -fno-omit-frame-pointer" CFLAGS="$CFLAGS -g1 -fno-omit-frame-pointer"
fi fi
@ -241,17 +243,15 @@ elif test "x$enable_stacktraces" = xyes; then
fi fi
fi fi
AM_CONDITIONAL([ENABLE_STACKTRACES], [test x$enable_stacktraces = xyes]) AM_CONDITIONAL([ENABLE_CRASH_HOOKS], [test x$enable_crashhooks = xyes])
if test "x$enable_stacktraces" = xyes; then if test "x$enable_crashhooks" = xyes; then
AC_DEFINE(ENABLE_STACKTRACES, 1, [Define this symbol if stacktraces should be enabled]) AC_DEFINE(ENABLE_CRASH_HOOKS, 1, [Define this symbol if crash hooks should be enabled])
fi fi
AX_CHECK_LINK_FLAG([-Wl,-wrap=__cxa_allocate_exception], [LINK_WRAP_SUPPORTED=yes],,,) AX_CHECK_LINK_FLAG([-Wl,-wrap=__cxa_allocate_exception], [LINK_WRAP_SUPPORTED=yes],,,)
AX_CHECK_COMPILE_FLAG([-rdynamic], [RDYNAMIC_SUPPORTED=yes],,,) AM_CONDITIONAL([CRASH_HOOKS_WRAPPED_CXX_ABI],[test x$LINK_WRAP_SUPPORTED = xyes])
AM_CONDITIONAL([STACKTRACE_WRAPPED_CXX_ABI],[test x$LINK_WRAP_SUPPORTED = xyes])
AM_CONDITIONAL([RDYNAMIC_SUPPORTED],[test x$RDYNAMIC_SUPPORTED = xyes])
if test x$LINK_WRAP_SUPPORTED = "xyes"; then if test x$LINK_WRAP_SUPPORTED = "xyes"; then
AC_DEFINE(STACKTRACE_WRAPPED_CXX_ABI, 1, [Define this symbol to use wrapped CXX ABIs for exception stacktraces])], AC_DEFINE(CRASH_HOOKS_WRAPPED_CXX_ABI, 1, [Define this symbol to use wrapped CXX ABIs for exception stacktraces])
fi fi
# Needed for MinGW targets when debug symbols are enabled as compiled objects get very large # Needed for MinGW targets when debug symbols are enabled as compiled objects get very large
@ -1142,6 +1142,18 @@ else
AC_MSG_RESULT([no]) AC_MSG_RESULT([no])
fi fi
# When compiled natively on MacOS, we need to specify -flat to avoid producing a dSYM bundle
# When cross-compiled on linux, we're using a different version of the tool that only supports flat symbol files
AC_MSG_CHECKING([whether dsymutil needs -flat])
if test x$DSYMUTIL != x && ($DSYMUTIL --help | grep -q \\-flat); then
AC_MSG_RESULT([yes])
DSYMUTIL_FLAT="$DSYMUTIL -flat"
else
AC_MSG_RESULT([no])
DSYMUTIL_FLAT="$DSYMUTIL"
fi
AC_MSG_RESULT($dsymutil_needs_flat)
if test x$build_bitcoin_utils$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests = xnononononono; then if test x$build_bitcoin_utils$build_bitcoin_libs$build_bitcoind$bitcoin_enable_qt$use_bench$use_tests = xnononononono; then
AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --enable-bench or --enable-tests]) AC_MSG_ERROR([No targets! Please specify at least one of: --with-utils --with-libs --with-daemon --with-gui --enable-bench or --enable-tests])
fi fi
@ -1205,6 +1217,7 @@ AC_SUBST(EVENT_PTHREADS_LIBS)
AC_SUBST(ZMQ_LIBS) AC_SUBST(ZMQ_LIBS)
AC_SUBST(PROTOBUF_LIBS) AC_SUBST(PROTOBUF_LIBS)
AC_SUBST(QR_LIBS) AC_SUBST(QR_LIBS)
AC_SUBST(DSYMUTIL_FLAT)
AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/functional/config.ini]) AC_CONFIG_FILES([Makefile src/Makefile doc/man/Makefile share/setup.nsi share/qt/Info.plist test/functional/config.ini])
AC_CONFIG_FILES([test/util/buildenv.py],[chmod +x test/util/buildenv.py]) AC_CONFIG_FILES([test/util/buildenv.py],[chmod +x test/util/buildenv.py])
AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh]) AC_CONFIG_FILES([contrib/devtools/split-debug.sh],[chmod +x contrib/devtools/split-debug.sh])
@ -1277,7 +1290,7 @@ echo " with test = $use_tests"
echo " with bench = $use_bench" echo " with bench = $use_bench"
echo " with upnp = $use_upnp" echo " with upnp = $use_upnp"
echo " debug enabled = $enable_debug" echo " debug enabled = $enable_debug"
echo " stacktraces enabled = $enable_stacktraces" echo " crash hooks enabled = $enable_crashhooks"
echo " miner enabled = $enable_miner" echo " miner enabled = $enable_miner"
echo " werror = $enable_werror" echo " werror = $enable_werror"
echo echo

View File

@ -38,7 +38,7 @@ script: |
WRAP_DIR=$HOME/wrapped WRAP_DIR=$HOME/wrapped
HOSTS="i686-pc-linux-gnu x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu" HOSTS="i686-pc-linux-gnu x86_64-linux-gnu arm-linux-gnueabihf aarch64-linux-gnu"
CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --disable-gui-tests" CONFIGFLAGS="--enable-glibc-back-compat --enable-reduce-exports --disable-bench --disable-gui-tests --enable-crash-hooks"
FAKETIME_HOST_PROGS="" FAKETIME_HOST_PROGS=""
FAKETIME_PROGS="date ar ranlib nm" FAKETIME_PROGS="date ar ranlib nm"
HOST_CFLAGS="-O2 -g" HOST_CFLAGS="-O2 -g"

View File

@ -37,7 +37,7 @@ files:
script: | script: |
WRAP_DIR=$HOME/wrapped WRAP_DIR=$HOME/wrapped
HOSTS="x86_64-apple-darwin11" HOSTS="x86_64-apple-darwin11"
CONFIGFLAGS="--enable-reduce-exports --disable-miner --disable-bench --disable-gui-tests GENISOIMAGE=$WRAP_DIR/genisoimage" CONFIGFLAGS="--enable-reduce-exports --disable-miner --disable-bench --disable-gui-tests GENISOIMAGE=$WRAP_DIR/genisoimage --enable-crash-hooks"
FAKETIME_HOST_PROGS="" FAKETIME_HOST_PROGS=""
FAKETIME_PROGS="ar ranlib date dmg genisoimage" FAKETIME_PROGS="ar ranlib date dmg genisoimage"
@ -146,6 +146,7 @@ script: |
CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS} CONFIG_SITE=${BASEPREFIX}/${i}/share/config.site ./configure --prefix=/ --disable-maintainer-mode --disable-dependency-tracking ${CONFIGFLAGS}
make ${MAKEOPTS} make ${MAKEOPTS}
make -C src osx_debug
make install-strip DESTDIR=${INSTALLPATH} make install-strip DESTDIR=${INSTALLPATH}
make osx_volname make osx_volname
@ -170,12 +171,15 @@ script: |
find . -name "lib*.la" -delete find . -name "lib*.la" -delete
find . -name "lib*.a" -delete find . -name "lib*.a" -delete
rm -rf ${DISTNAME}/lib/pkgconfig rm -rf ${DISTNAME}/lib/pkgconfig
find ${DISTNAME} | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz find .. -name *.dSYM -exec cp -ra {} ${DISTNAME}/bin \;
find ${DISTNAME} -not -path '*.dSYM*' | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}.tar.gz
find ${DISTNAME} -path '*.dSYM*' | sort | tar --no-recursion --mode='u+rw,go+r-w,a+X' --owner=0 --group=0 -c -T - | gzip -9n > ${OUTDIR}/${DISTNAME}-${i}-debug.tar.gz
cd ../../ cd ../../
done done
mkdir -p $OUTDIR/src mkdir -p $OUTDIR/src
mv $SOURCEDIST $OUTDIR/src mv $SOURCEDIST $OUTDIR/src
mv ${OUTDIR}/${DISTNAME}-x86_64-*.tar.gz ${OUTDIR}/${DISTNAME}-osx64.tar.gz mv ${OUTDIR}/${DISTNAME}-x86_64-apple-darwin11.tar.gz ${OUTDIR}/${DISTNAME}-osx64.tar.gz
mv ${OUTDIR}/${DISTNAME}-x86_64-apple-darwin11-debug.tar.gz ${OUTDIR}/${DISTNAME}-osx64-debug.tar.gz
# Compress ccache (otherwise the assert file will get too huge) # Compress ccache (otherwise the assert file will get too huge)
if [ "$CCACHE_DIR" != "" ]; then if [ "$CCACHE_DIR" != "" ]; then

View File

@ -31,7 +31,7 @@ files: []
script: | script: |
WRAP_DIR=$HOME/wrapped WRAP_DIR=$HOME/wrapped
HOSTS="i686-w64-mingw32 x86_64-w64-mingw32" HOSTS="i686-w64-mingw32 x86_64-w64-mingw32"
CONFIGFLAGS="--enable-reduce-exports --disable-miner --disable-bench --disable-gui-tests" CONFIGFLAGS="--enable-reduce-exports --disable-miner --disable-bench --disable-gui-tests --enable-crash-hooks"
FAKETIME_HOST_PROGS="ar ranlib nm windres strip objcopy" FAKETIME_HOST_PROGS="ar ranlib nm windres strip objcopy"
FAKETIME_PROGS="date makensis zip" FAKETIME_PROGS="date makensis zip"
HOST_CFLAGS="-O2 -g" HOST_CFLAGS="-O2 -g"

View File

@ -10,8 +10,8 @@ AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS)
AM_CPPFLAGS = $(HARDENED_CPPFLAGS) AM_CPPFLAGS = $(HARDENED_CPPFLAGS)
EXTRA_LIBRARIES = EXTRA_LIBRARIES =
if ENABLE_STACKTRACES if ENABLE_CRASH_HOOKS
if STACKTRACE_WRAPPED_CXX_ABI if CRASH_HOOKS_WRAPPED_CXX_ABI
# Wrap internal C++ ABI's so that we can attach stacktraces to exceptions # Wrap internal C++ ABI's so that we can attach stacktraces to exceptions
LDFLAGS_WRAP_EXCEPTIONS = -Wl,-wrap,__cxa_allocate_exception -Wl,-wrap,__cxa_free_exception LDFLAGS_WRAP_EXCEPTIONS = -Wl,-wrap,__cxa_allocate_exception -Wl,-wrap,__cxa_free_exception
if TARGET_WINDOWS if TARGET_WINDOWS
@ -20,10 +20,6 @@ else
LDFLAGS_WRAP_EXCEPTIONS += -Wl,-wrap,__assert_fail LDFLAGS_WRAP_EXCEPTIONS += -Wl,-wrap,__assert_fail
endif endif
endif endif
if RDYNAMIC_SUPPORTED
# This gives better stacktraces
AM_CXXFLAGS += -rdynamic
endif endif
if TARGET_WINDOWS if TARGET_WINDOWS
@ -31,7 +27,6 @@ BACKTRACE_LIB = -ldbghelp -lbacktrace
else else
BACKTRACE_LIB = -lbacktrace BACKTRACE_LIB = -lbacktrace
endif endif
endif
if EMBEDDED_UNIVALUE if EMBEDDED_UNIVALUE
LIBUNIVALUE = univalue/libunivalue.la LIBUNIVALUE = univalue/libunivalue.la
@ -646,6 +641,7 @@ clean-local:
-rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno -rm -f leveldb/*/*.gcda leveldb/*/*.gcno leveldb/helpers/memenv/*.gcda leveldb/helpers/memenv/*.gcno
-rm -f config.h -rm -f config.h
-rm -rf test/__pycache__ -rm -rf test/__pycache__
-rm -rf *.dSYM test/*.dSYM bench/*.dSYM qt/*.dSYM qt/test/*.dSYM
.rc.o: .rc.o:
@test -f $(WINDRES) @test -f $(WINDRES)
@ -668,6 +664,10 @@ if HARDEN
$(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS) $(AM_V_at) READELF=$(READELF) OBJDUMP=$(OBJDUMP) $(top_srcdir)/contrib/devtools/security-check.py < $(bin_PROGRAMS)
endif endif
osx_debug: $(bin_PROGRAMS)
for i in $(bin_PROGRAMS); do mkdir -p $$i.dSYM/Contents/Resources/DWARF && $(DSYMUTIL_FLAT) -o $$i.dSYM/Contents/Resources/DWARF/$$(basename $$i) $$i &> /dev/null ; done
%.pb.cc %.pb.h: %.proto %.pb.cc %.pb.h: %.proto
@test -f $(PROTOC) @test -f $(PROTOC)
$(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(<D) $< $(AM_V_GEN) $(PROTOC) --cpp_out=$(@D) --proto_path=$(<D) $<

View File

@ -79,6 +79,12 @@ static int AppInitRPC(int argc, char* argv[])
// Parameters // Parameters
// //
gArgs.ParseParameters(argc, argv); gArgs.ParseParameters(argc, argv);
if (gArgs.IsArgSet("-printcrashinfo")) {
std::cout << GetCrashInfoStrFromSerializedStr(gArgs.GetArg("-printcrashinfo", "")) << std::endl;
return true;
}
if (argc<2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) { if (argc<2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) {
std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n"; std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n";
if (!gArgs.IsArgSet("-version")) { if (!gArgs.IsArgSet("-version")) {

View File

@ -43,6 +43,11 @@ static int AppInitRawTx(int argc, char* argv[])
// //
gArgs.ParseParameters(argc, argv); gArgs.ParseParameters(argc, argv);
if (gArgs.IsArgSet("-printcrashinfo")) {
std::cout << GetCrashInfoStrFromSerializedStr(gArgs.GetArg("-printcrashinfo", "")) << std::endl;
return true;
}
// Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
try { try {
SelectParams(ChainNameFromCommandLine()); SelectParams(ChainNameFromCommandLine());

View File

@ -76,6 +76,11 @@ bool AppInit(int argc, char* argv[])
// If Qt is used, parameters/dash.conf are parsed in qt/dash.cpp's main() // If Qt is used, parameters/dash.conf are parsed in qt/dash.cpp's main()
gArgs.ParseParameters(argc, argv); gArgs.ParseParameters(argc, argv);
if (gArgs.IsArgSet("-printcrashinfo")) {
std::cout << GetCrashInfoStrFromSerializedStr(gArgs.GetArg("-printcrashinfo", "")) << std::endl;
return true;
}
// Process help and version before taking care about datadir // Process help and version before taking care about datadir
if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version"))
{ {

View File

@ -622,6 +622,13 @@ int main(int argc, char *argv[])
initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator);
translationInterface.Translate.connect(Translate); translationInterface.Translate.connect(Translate);
if (gArgs.IsArgSet("-printcrashinfo")) {
auto crashInfo = GetCrashInfoStrFromSerializedStr(gArgs.GetArg("-printcrashinfo", ""));
std::cout << crashInfo << std::endl;
QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QString::fromStdString(crashInfo));
return EXIT_SUCCESS;
}
// Show help message immediately after parsing command-line options (for "-lang") and setting locale, // Show help message immediately after parsing command-line options (for "-lang") and setting locale,
// but before showing splash screen. // but before showing splash screen.
if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version"))

View File

@ -3,9 +3,12 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "stacktraces.h" #include "stacktraces.h"
#include "fs.h"
#include "tinyformat.h" #include "tinyformat.h"
#include "random.h" #include "random.h"
#include "streams.h"
#include "util.h" #include "util.h"
#include "utilstrencodings.h"
#include "dash-config.h" #include "dash-config.h"
@ -30,25 +33,21 @@
#if !WIN32 #if !WIN32
#include <dlfcn.h> #include <dlfcn.h>
#if !__APPLE__
#include <link.h>
#endif
#endif #endif
#if __APPLE__ #if __APPLE__
#include <mach-o/dyld.h> #include <mach-o/dyld.h>
#include <mach/mach_init.h>
#include <sys/sysctl.h>
#include <mach/mach_vm.h>
#endif #endif
#ifdef ENABLE_STACKTRACES
#include <backtrace.h> #include <backtrace.h>
#endif
#include <libgen.h> // for basename()
#include <string.h> #include <string.h>
static void PrintCrashInfo(const std::string& s)
{
LogPrintf("%s", s);
fprintf(stderr, "%s", s.c_str());
fflush(stderr);
}
std::string DemangleSymbol(const std::string& name) std::string DemangleSymbol(const std::string& name)
{ {
#if __GNUC__ || __clang__ #if __GNUC__ || __clang__
@ -71,8 +70,7 @@ std::string DemangleSymbol(const std::string& name)
// this is the case when the terminate handler or an assert already handled the exception // this is the case when the terminate handler or an assert already handled the exception
static std::atomic<bool> skipAbortSignal(false); static std::atomic<bool> skipAbortSignal(false);
#ifdef ENABLE_STACKTRACES static ssize_t GetExeFileNameImpl(char* buf, size_t bufSize)
ssize_t GetExeFileNameImpl(char* buf, size_t bufSize)
{ {
#if WIN32 #if WIN32
std::vector<TCHAR> tmp(bufSize); std::vector<TCHAR> tmp(bufSize);
@ -101,7 +99,7 @@ ssize_t GetExeFileNameImpl(char* buf, size_t bufSize)
#endif #endif
} }
std::string GetExeFileName() static std::string GetExeFileName()
{ {
std::vector<char> buf(1024); std::vector<char> buf(1024);
while (true) { while (true) {
@ -116,38 +114,51 @@ std::string GetExeFileName()
} }
} }
static std::string g_exeFileName = GetExeFileName();
static std::string g_exeFileBaseName = fs::path(g_exeFileName).filename().string();
static void my_backtrace_error_callback (void *data, const char *msg, static void my_backtrace_error_callback (void *data, const char *msg,
int errnum) int errnum)
{ {
PrintCrashInfo(strprintf("libbacktrace error: %d - %s\n", errnum, msg));
} }
static backtrace_state* GetLibBacktraceState() static backtrace_state* GetLibBacktraceState()
{ {
static std::string exeFileName = GetExeFileName(); #if WIN32
static const char* exeFileNamePtr = exeFileName.empty() ? nullptr : exeFileName.c_str(); // libbacktrace is not able to handle the DWARF debuglink in the .exe
// but luckily we can just specify the .dbg file here as it's a valid PE/XCOFF file
static std::string debugFileName = g_exeFileName + ".dbg";
static const char* exeFileNamePtr = fs::exists(debugFileName) ? debugFileName.c_str() : g_exeFileName.c_str();
#else
static const char* exeFileNamePtr = g_exeFileName.empty() ? nullptr : g_exeFileName.c_str();
#endif
static backtrace_state* st = backtrace_create_state(exeFileNamePtr, 1, my_backtrace_error_callback, NULL); static backtrace_state* st = backtrace_create_state(exeFileNamePtr, 1, my_backtrace_error_callback, NULL);
return st; return st;
} }
#if WIN32 #if WIN32
static uint64_t GetBaseAddress()
{
return 0;
}
// PC addresses returned by StackWalk64 are in the real mapped space, while libbacktrace expects them to be in the // PC addresses returned by StackWalk64 are in the real mapped space, while libbacktrace expects them to be in the
// default mapped space starting at 0x400000. This method converts the address. // default mapped space starting at 0x400000. This method converts the address.
// TODO this is probably the same reason libbacktrace is not able to gather the stacktrace on Windows (returns pointers like 0x1 or 0xfffffff) // TODO this is probably the same reason libbacktrace is not able to gather the stacktrace on Windows (returns pointers like 0x1 or 0xfffffff)
// If they ever fix this problem, we might end up converting to invalid addresses here // If they ever fix this problem, we might end up converting to invalid addresses here
static uintptr_t ConvertAddress(uintptr_t addr) static uint64_t ConvertAddress(uint64_t addr)
{ {
MEMORY_BASIC_INFORMATION mbi; MEMORY_BASIC_INFORMATION mbi;
if (!VirtualQuery((PVOID)addr, &mbi, sizeof(mbi))) if (!VirtualQuery((PVOID)addr, &mbi, sizeof(mbi)))
return 0; return 0;
uintptr_t hMod = (uintptr_t)mbi.AllocationBase; uint64_t hMod = (uint64_t)mbi.AllocationBase;
uintptr_t offset = addr - hMod; uint64_t offset = addr - hMod;
return 0x400000 + offset; return 0x400000 + offset;
} }
static __attribute__((noinline)) std::vector<uintptr_t> GetStackFrames(size_t skip, size_t max_frames, const CONTEXT* pContext = nullptr) static __attribute__((noinline)) std::vector<uint64_t> GetStackFrames(size_t skip, size_t max_frames, const CONTEXT* pContext = nullptr)
{ {
// We can't use libbacktrace for stack unwinding on Windows as it returns invalid addresses (like 0x1 or 0xffffffff) // We can't use libbacktrace for stack unwinding on Windows as it returns invalid addresses (like 0x1 or 0xffffffff)
static BOOL symInitialized = SymInitialize(GetCurrentProcess(), NULL, TRUE); static BOOL symInitialized = SymInitialize(GetCurrentProcess(), NULL, TRUE);
@ -195,7 +206,7 @@ static __attribute__((noinline)) std::vector<uintptr_t> GetStackFrames(size_t sk
#error unsupported architecture #error unsupported architecture
#endif #endif
std::vector<uintptr_t> ret; std::vector<uint64_t> ret;
size_t i = 0; size_t i = 0;
while (ret.size() < max_frames) { while (ret.size() < max_frames) {
@ -208,7 +219,7 @@ static __attribute__((noinline)) std::vector<uintptr_t> GetStackFrames(size_t sk
break; break;
} }
if (i >= skip) { if (i >= skip) {
uintptr_t pc = ConvertAddress(stackframe.AddrPC.Offset); uint64_t pc = ConvertAddress(stackframe.AddrPC.Offset);
if (pc == 0) { if (pc == 0) {
pc = stackframe.AddrPC.Offset; pc = stackframe.AddrPC.Offset;
} }
@ -220,18 +231,48 @@ static __attribute__((noinline)) std::vector<uintptr_t> GetStackFrames(size_t sk
return ret; return ret;
} }
#else #else
static int my_backtrace_simple_callback(void *data, uintptr_t pc)
#if __APPLE__
static uint64_t GetBaseAddress()
{ {
auto v = (std::vector<uintptr_t>*)data; mach_port_name_t target_task;
v->emplace_back(pc); vm_map_offset_t vmoffset;
if (v->size() >= 128) { vm_map_size_t vmsize;
// abort uint32_t nesting_depth = 0;
return 1; struct vm_region_submap_info_64 vbr;
mach_msg_type_number_t vbrcount = 16;
kern_return_t kr;
kr = task_for_pid(mach_task_self(), getpid(), &target_task);
if (kr != KERN_SUCCESS) {
return 0;
}
kr = mach_vm_region_recurse(target_task, &vmoffset, &vmsize, &nesting_depth, (vm_region_recurse_info_t)&vbr, &vbrcount);
if (kr != KERN_SUCCESS) {
return 0;
}
return vmoffset;
}
#else
static int dl_iterate_callback(struct dl_phdr_info* info, size_t s, void* data)
{
uint64_t* p = (uint64_t*)data;
if (info->dlpi_name == NULL || info->dlpi_name[0] == '\0') {
*p = info->dlpi_addr;
} }
return 0;
} }
static __attribute__((noinline)) std::vector<uintptr_t> GetStackFrames(size_t skip, size_t max_frames) static uint64_t GetBaseAddress()
{
uint64_t basePtr = 0;
dl_iterate_phdr(dl_iterate_callback, &basePtr);
return basePtr;
}
#endif
static __attribute__((noinline)) std::vector<uint64_t> GetStackFrames(size_t skip, size_t max_frames)
{ {
// FYI, this is not using libbacktrace, but "backtrace()" from <execinfo.h> // FYI, this is not using libbacktrace, but "backtrace()" from <execinfo.h>
std::vector<void*> buf(max_frames); std::vector<void*> buf(max_frames);
@ -241,20 +282,31 @@ static __attribute__((noinline)) std::vector<uintptr_t> GetStackFrames(size_t sk
} }
buf.resize((size_t)count); buf.resize((size_t)count);
std::vector<uintptr_t> ret; std::vector<uint64_t> ret;
ret.reserve(count); ret.reserve(count);
for (size_t i = skip + 1; i < buf.size(); i++) { for (size_t i = skip + 1; i < buf.size(); i++) {
ret.emplace_back((uintptr_t) buf[i]); ret.emplace_back((uint64_t) buf[i]);
} }
return ret; return ret;
} }
#endif #endif
struct stackframe_info { struct stackframe_info {
uintptr_t pc; uint64_t pc{0};
std::string filename; std::string filename;
int lineno; int lineno{-1};
std::string function; std::string function;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(pc);
READWRITE(filename);
READWRITE(lineno);
READWRITE(function);
}
}; };
static int my_backtrace_full_callback (void *data, uintptr_t pc, const char *filename, int lineno, const char *function) static int my_backtrace_full_callback (void *data, uintptr_t pc, const char *filename, int lineno, const char *function)
@ -284,7 +336,7 @@ static int my_backtrace_full_callback (void *data, uintptr_t pc, const char *fil
return 0; return 0;
} }
static std::vector<stackframe_info> GetStackFrameInfos(const std::vector<uintptr_t>& stackframes) static std::vector<stackframe_info> GetStackFrameInfos(const std::vector<uint64_t>& stackframes)
{ {
std::vector<stackframe_info> infos; std::vector<stackframe_info> infos;
infos.reserve(stackframes.size()); infos.reserve(stackframes.size());
@ -298,10 +350,124 @@ static std::vector<stackframe_info> GetStackFrameInfos(const std::vector<uintptr
return infos; return infos;
} }
static std::string GetStackFrameInfosStr(const std::vector<stackframe_info>& sis, size_t spaces = 2) struct crash_info_header
{ {
if (sis.empty()) { std::string magic;
return "\n"; uint16_t version;
std::string exeFileName;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(magic);
READWRITE(version);
READWRITE(exeFileName);
}
};
struct crash_info
{
std::string crashDescription;
std::vector<uint64_t> stackframes;
std::vector<stackframe_info> stackframeInfos;
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream& s, Operation ser_action)
{
READWRITE(crashDescription);
READWRITE(stackframes);
READWRITE(stackframeInfos);
}
void ConvertAddresses(int64_t offset)
{
for (auto& sf : stackframes) {
sf += offset;
}
for (auto& sfi : stackframeInfos) {
sfi.pc += offset;
}
}
};
static std::string GetCrashInfoStrNoDebugInfo(crash_info ci)
{
static uint64_t basePtr = GetBaseAddress();
CDataStream ds(SER_DISK, 0);
crash_info_header hdr;
hdr.magic = "DashCrashInfo";
hdr.version = 1;
hdr.exeFileName = g_exeFileBaseName;
ds << hdr;
ci.ConvertAddresses(-(int64_t)basePtr);
ds << ci;
auto ciStr = EncodeBase32((const unsigned char*)ds.data(), ds.size());
std::string s = ci.crashDescription + "\n";
s += strprintf("No debug information available for stacktrace. You should add debug information and then run:\n"
"%s -printcrashinfo=%s\n", g_exeFileBaseName, ciStr);
return s;
}
static std::string GetCrashInfoStr(const crash_info& ci, size_t spaces = 2);
std::string GetCrashInfoStrFromSerializedStr(const std::string& ciStr)
{
static uint64_t basePtr = GetBaseAddress();
bool dataInvalid = false;
auto buf = DecodeBase32(ciStr.c_str(), &dataInvalid);
if (buf.empty() || dataInvalid) {
return "Error while deserializing crash info";
}
CDataStream ds(buf, SER_DISK, 0);
crash_info_header hdr;
try {
ds >> hdr;
} catch (...) {
return "Error while deserializing crash info header";
}
if (hdr.magic != "DashCrashInfo") {
return "Invalid magic string";
}
if (hdr.version != 1) {
return "Unsupported version";
}
if (hdr.exeFileName != g_exeFileBaseName) {
return "Crash info is not for this executable";
}
crash_info ci;
try {
ds >> ci;
} catch (...) {
return "Error while deserializing crash info";
}
ci.ConvertAddresses(basePtr);
if (ci.stackframeInfos.empty()) {
std::vector<uint64_t> stackframes(ci.stackframes.begin(), ci.stackframes.end());
ci.stackframeInfos = GetStackFrameInfos(stackframes);
}
return GetCrashInfoStr(ci);
}
static std::string GetCrashInfoStr(const crash_info& ci, size_t spaces)
{
if (ci.stackframeInfos.empty()) {
return GetCrashInfoStrNoDebugInfo(ci);
} }
std::string sp; std::string sp;
@ -310,16 +476,14 @@ static std::string GetStackFrameInfosStr(const std::vector<stackframe_info>& sis
} }
std::vector<std::string> lstrs; std::vector<std::string> lstrs;
lstrs.reserve(sis.size()); lstrs.reserve(ci.stackframeInfos.size());
for (size_t i = 0; i < sis.size(); i++) { for (size_t i = 0; i < ci.stackframeInfos.size(); i++) {
auto& si = sis[i]; auto& si = ci.stackframeInfos[i];
std::string lstr; std::string lstr;
if (!si.filename.empty()) { if (!si.filename.empty()) {
std::vector<char> vecFilename(si.filename.size() + 1, '\0'); lstr += fs::path(si.filename).filename().string();
strcpy(vecFilename.data(), si.filename.c_str());
lstr += basename(vecFilename.data());
} else { } else {
lstr += "<unknown-file>"; lstr += "<unknown-file>";
} }
@ -335,9 +499,9 @@ static std::string GetStackFrameInfosStr(const std::vector<stackframe_info>& sis
std::string fmtStr = strprintf("%%2d#: (0x%%08X) %%-%ds - %%s\n", lstrlen); std::string fmtStr = strprintf("%%2d#: (0x%%08X) %%-%ds - %%s\n", lstrlen);
std::string s; std::string s = ci.crashDescription + "\n";
for (size_t i = 0; i < sis.size(); i++) { for (size_t i = 0; i < ci.stackframeInfos.size(); i++) {
auto& si = sis[i]; auto& si = ci.stackframeInfos[i];
auto& lstr = lstrs[i]; auto& lstr = lstrs[i];
@ -356,10 +520,19 @@ static std::string GetStackFrameInfosStr(const std::vector<stackframe_info>& sis
return s; return s;
} }
static std::mutex g_stacktraces_mutex; static void PrintCrashInfo(const crash_info& ci)
static std::map<void*, std::shared_ptr<std::vector<uintptr_t>>> g_stacktraces; {
auto str = GetCrashInfoStr(ci);
LogPrintf("%s", str);
fprintf(stderr, "%s", str.c_str());
fflush(stderr);
}
#if STACKTRACE_WRAPPED_CXX_ABI #ifdef ENABLE_CRASH_HOOKS
static std::mutex g_stacktraces_mutex;
static std::map<void*, std::shared_ptr<std::vector<uint64_t>>> g_stacktraces;
#if CRASH_HOOKS_WRAPPED_CXX_ABI
// These come in through -Wl,-wrap // These come in through -Wl,-wrap
// It only works on GCC // It only works on GCC
extern "C" void* __real___cxa_allocate_exception(size_t thrown_size); extern "C" void* __real___cxa_allocate_exception(size_t thrown_size);
@ -402,7 +575,7 @@ extern "C" void __real___assert_fail(const char *assertion, const char *file, un
#endif #endif
#endif #endif
#if STACKTRACE_WRAPPED_CXX_ABI #if CRASH_HOOKS_WRAPPED_CXX_ABI
#define WRAPPED_NAME(x) __wrap_##x #define WRAPPED_NAME(x) __wrap_##x
#else #else
#define WRAPPED_NAME(x) x #define WRAPPED_NAME(x) x
@ -415,7 +588,7 @@ extern "C" void* __attribute__((noinline)) WRAPPED_NAME(__cxa_allocate_exception
// WARNING keep this as it is and don't try to optimize it (no std::move, no std::make_shared, ...) // WARNING keep this as it is and don't try to optimize it (no std::move, no std::make_shared, ...)
// trying to optimize this will cause the optimizer to move the GetStackFrames() call deep into the stl libs // trying to optimize this will cause the optimizer to move the GetStackFrames() call deep into the stl libs
std::shared_ptr<std::vector<uintptr_t>> st(new std::vector<uintptr_t>(localSt)); std::shared_ptr<std::vector<uint64_t>> st(new std::vector<uint64_t>(localSt));
void* p = __real___cxa_allocate_exception(thrown_size); void* p = __real___cxa_allocate_exception(thrown_size);
@ -432,41 +605,64 @@ extern "C" void __attribute__((noinline)) WRAPPED_NAME(__cxa_free_exception)(voi
g_stacktraces.erase(thrown_exception); g_stacktraces.erase(thrown_exception);
} }
static __attribute__((noinline)) crash_info GetCrashInfoFromAssertion(const char* assertion, const char* file, int line, const char* function)
{
crash_info ci;
ci.stackframes = GetStackFrames(1, 16);
ci.crashDescription = "Assertion failure:";
if (assertion) {
ci.crashDescription += strprintf("\n assertion: %s", assertion);
}
if (file) {
ci.crashDescription += strprintf("\n file: %s, line: %d", file, line);
}
if (function) {
ci.crashDescription += strprintf("\n function: %s", function);
}
ci.stackframeInfos = GetStackFrameInfos(ci.stackframes);
return ci;
}
#if __clang__ #if __clang__
extern "C" void __attribute__((noinline)) WRAPPED_NAME(__assert_rtn)(const char *function, const char *file, int line, const char *assertion) extern "C" void __attribute__((noinline)) WRAPPED_NAME(__assert_rtn)(const char *function, const char *file, int line, const char *assertion)
{ {
auto st = GetCurrentStacktraceStr(1); auto ci = GetCrashInfoFromAssertion(assertion, file, line, function);
PrintCrashInfo(strprintf("#### assertion failed: %s ####\n%s", assertion, st)); PrintCrashInfo(ci);
skipAbortSignal = true; skipAbortSignal = true;
__real___assert_rtn(function, file, line, assertion); __real___assert_rtn(function, file, line, assertion);
} }
#elif WIN32 #elif WIN32
extern "C" void __attribute__((noinline)) WRAPPED_NAME(_assert)(const char *assertion, const char *file, unsigned int line) extern "C" void __attribute__((noinline)) WRAPPED_NAME(_assert)(const char *assertion, const char *file, unsigned int line)
{ {
auto st = GetCurrentStacktraceStr(1); auto ci = GetCrashInfoFromAssertion(assertion, file, line, nullptr);
PrintCrashInfo(strprintf("#### assertion failed: %s ####\n%s", assertion, st)); PrintCrashInfo(ci);
skipAbortSignal = true; skipAbortSignal = true;
__real__assert(assertion, file, line); __real__assert(assertion, file, line);
} }
extern "C" void __attribute__((noinline)) WRAPPED_NAME(_wassert)(const wchar_t *assertion, const wchar_t *file, unsigned int line) extern "C" void __attribute__((noinline)) WRAPPED_NAME(_wassert)(const wchar_t *assertion, const wchar_t *file, unsigned int line)
{ {
auto st = GetCurrentStacktraceStr(1); auto ci = GetCrashInfoFromAssertion(
PrintCrashInfo(strprintf("#### assertion failed: %s ####\n%s", std::string(assertion, assertion + wcslen(assertion)), st)); assertion ? std::string(assertion, assertion + wcslen(assertion)).c_str() : nullptr,
file ? std::string(file, file + wcslen(file)).c_str() : nullptr,
line, nullptr);
PrintCrashInfo(ci);
skipAbortSignal = true; skipAbortSignal = true;
__real__wassert(assertion, file, line); __real__wassert(assertion, file, line);
} }
#else #else
extern "C" void __attribute__((noinline)) WRAPPED_NAME(__assert_fail)(const char *assertion, const char *file, unsigned int line, const char *function) extern "C" void __attribute__((noinline)) WRAPPED_NAME(__assert_fail)(const char *assertion, const char *file, unsigned int line, const char *function)
{ {
auto st = GetCurrentStacktraceStr(1); auto ci = GetCrashInfoFromAssertion(assertion, file, line, function);
PrintCrashInfo(strprintf("#### assertion failed: %s ####\n%s", assertion, st)); PrintCrashInfo(ci);
skipAbortSignal = true; skipAbortSignal = true;
__real___assert_fail(assertion, file, line, function); __real___assert_fail(assertion, file, line, function);
} }
#endif #endif
#endif //ENABLE_CRASH_HOOKS
static std::shared_ptr<std::vector<uintptr_t>> GetExceptionStacktrace(const std::exception_ptr& e) static std::shared_ptr<std::vector<uint64_t>> GetExceptionStacktrace(const std::exception_ptr& e)
{ {
#ifdef ENABLE_CRASH_HOOKS
void* p = *(void**)&e; void* p = *(void**)&e;
std::lock_guard<std::mutex> l(g_stacktraces_mutex); std::lock_guard<std::mutex> l(g_stacktraces_mutex);
@ -475,36 +671,19 @@ static std::shared_ptr<std::vector<uintptr_t>> GetExceptionStacktrace(const std:
return nullptr; return nullptr;
} }
return it->second; return it->second;
}
#endif //ENABLE_STACKTRACES
std::string GetExceptionStacktraceStr(const std::exception_ptr& e)
{
#ifdef ENABLE_STACKTRACES
auto stackframes = GetExceptionStacktrace(e);
if (stackframes && !stackframes->empty()) {
auto infos = GetStackFrameInfos(*stackframes);
return GetStackFrameInfosStr(infos);
}
#endif
return "<no stacktrace>\n";
}
std::string __attribute__((noinline)) GetCurrentStacktraceStr(size_t skip, size_t max_depth)
{
#ifdef ENABLE_STACKTRACES
auto stackframes = GetStackFrames(skip + 1, max_depth); // +1 to skip current method
auto infos = GetStackFrameInfos(stackframes);
return GetStackFrameInfosStr(infos);
#else #else
return "<no stacktrace>\n"; return {};
#endif #endif
} }
std::string GetPrettyExceptionStr(const std::exception_ptr& e) crash_info GetCrashInfoFromException(const std::exception_ptr& e)
{ {
crash_info ci;
ci.crashDescription = "Exception: ";
if (!e) { if (!e) {
return "<no exception>\n"; ci.crashDescription += "<null>";
return ci;
} }
std::string type; std::string type;
@ -536,35 +715,42 @@ std::string GetPrettyExceptionStr(const std::exception_ptr& e)
type = DemangleSymbol(type); type = DemangleSymbol(type);
} }
std::string s = strprintf("Exception: type=%s, what=\"%s\"\n", type, what); ci.crashDescription += strprintf("type=%s, what=\"%s\"", type, what);
#if ENABLE_STACKTRACES auto stackframes = GetExceptionStacktrace(e);
s += GetExceptionStacktraceStr(e); if (stackframes) {
#endif ci.stackframes = *stackframes;
ci.stackframeInfos = GetStackFrameInfos(ci.stackframes);
}
return s; return ci;
}
std::string GetPrettyExceptionStr(const std::exception_ptr& e)
{
return GetCrashInfoStr(GetCrashInfoFromException(e));
} }
static void terminate_handler() static void terminate_handler()
{ {
auto exc = std::current_exception(); auto exc = std::current_exception();
std::string s, s2; crash_info ci;
s += "#### std::terminate() called, aborting ####\n"; ci.crashDescription = "std::terminate() called, aborting";
if (exc) { if (exc) {
s += "#### UNCAUGHT EXCEPTION ####\n"; auto ci2 = GetCrashInfoFromException(exc);
s2 = GetPrettyExceptionStr(exc); ci.crashDescription = strprintf("std::terminate() called due to unhandled exception\n%s", ci2.crashDescription);
ci.stackframes = std::move(ci2.stackframes);
ci.stackframeInfos = std::move(ci2.stackframeInfos);
} else { } else {
s += "#### UNKNOWN REASON ####\n"; ci.crashDescription = "std::terminate() called due unknown reason";
#ifdef ENABLE_STACKTRACES ci.stackframes = GetStackFrames(0, 16);
s2 = GetCurrentStacktraceStr(0);
#else
s2 = "\n";
#endif
} }
PrintCrashInfo(strprintf("%s%s", s, s2)); ci.stackframeInfos = GetStackFrameInfos(ci.stackframes);
PrintCrashInfo(ci);
skipAbortSignal = true; skipAbortSignal = true;
std::abort(); std::abort();
@ -575,25 +761,28 @@ void RegisterPrettyTerminateHander()
std::set_terminate(terminate_handler); std::set_terminate(terminate_handler);
} }
#ifdef ENABLE_STACKTRACES
#if !WIN32 #if !WIN32
static void HandlePosixSignal(int s) static void HandlePosixSignal(int s)
{ {
if (s == SIGABRT && skipAbortSignal) { if (s == SIGABRT && skipAbortSignal) {
return; return;
} }
std::string st = GetCurrentStacktraceStr(0);
const char* name = strsignal(s); const char* name = strsignal(s);
if (!name) { if (!name) {
name = "UNKNOWN"; name = "UNKNOWN";
} }
PrintCrashInfo(strprintf("#### signal %s ####\n%s", name, st));
crash_info ci;
ci.crashDescription = strprintf("Posix Signal: %s", name);
ci.stackframes = GetStackFrames(0, 16);
ci.stackframeInfos = GetStackFrameInfos(ci.stackframes);
PrintCrashInfo(ci);
// avoid a signal loop // avoid a signal loop
skipAbortSignal = true; skipAbortSignal = true;
std::abort(); std::abort();
} }
#else #else
static void DoHandleWindowsException(EXCEPTION_POINTERS * ExceptionInfo) static void DoHandleWindowsException(EXCEPTION_POINTERS * ExceptionInfo)
{ {
@ -623,11 +812,12 @@ static void DoHandleWindowsException(EXCEPTION_POINTERS * ExceptionInfo)
default: excType = "UNKNOWN"; break; default: excType = "UNKNOWN"; break;
} }
auto stackframes = GetStackFrames(0, 16, ExceptionInfo->ContextRecord); crash_info ci;
auto infos = GetStackFrameInfos(stackframes); ci.crashDescription = strprintf("Windows Exception: %s", excType);
auto infosStr = GetStackFrameInfosStr(infos); ci.stackframes = GetStackFrames(0, 16, ExceptionInfo->ContextRecord);
ci.stackframeInfos = GetStackFrameInfos(ci.stackframes);
PrintCrashInfo(strprintf("#### Windows Exception %s ####\n%s", excType, infosStr)); PrintCrashInfo(ci);
} }
LONG WINAPI HandleWindowsException(EXCEPTION_POINTERS * ExceptionInfo) LONG WINAPI HandleWindowsException(EXCEPTION_POINTERS * ExceptionInfo)
@ -645,11 +835,9 @@ LONG WINAPI HandleWindowsException(EXCEPTION_POINTERS * ExceptionInfo)
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
#endif #endif
#endif // ENABLE_STACKTRACES
void RegisterPrettySignalHandlers() void RegisterPrettySignalHandlers()
{ {
#if ENABLE_STACKTRACES
#if WIN32 #if WIN32
SetUnhandledExceptionFilter(HandleWindowsException); SetUnhandledExceptionFilter(HandleWindowsException);
#else #else
@ -679,5 +867,4 @@ void RegisterPrettySignalHandlers()
sigaction(s, &sa_segv, NULL); sigaction(s, &sa_segv, NULL);
} }
#endif #endif
#endif
} }

View File

@ -15,10 +15,8 @@
std::string DemangleSymbol(const std::string& name); std::string DemangleSymbol(const std::string& name);
std::string GetCurrentStacktraceStr(size_t skip = 0, size_t max_depth = 16);
std::string GetExceptionStacktraceStr(const std::exception_ptr& e);
std::string GetPrettyExceptionStr(const std::exception_ptr& e); std::string GetPrettyExceptionStr(const std::exception_ptr& e);
std::string GetCrashInfoStrFromSerializedStr(const std::string& ciStr);
template<typename T> template<typename T>
std::string GetExceptionWhat(const T& e); std::string GetExceptionWhat(const T& e);

View File

@ -602,7 +602,7 @@ std::string HelpMessageOpt(const std::string &option, const std::string &message
static std::string FormatException(const std::exception_ptr pex, const char* pszThread) static std::string FormatException(const std::exception_ptr pex, const char* pszThread)
{ {
return strprintf("EXCEPTION: %s", GetPrettyExceptionStr(pex)); return GetPrettyExceptionStr(pex);
} }
void PrintExceptionContinue(const std::exception_ptr pex, const char* pszThread) void PrintExceptionContinue(const std::exception_ptr pex, const char* pszThread)