Improve Windows crash report.

This commit is contained in:
Martín Lucas Golini
2025-07-26 22:14:14 -03:00
parent 84da3626f9
commit 170fca0e01
3 changed files with 254 additions and 36 deletions

1
.gitignore vendored
View File

@@ -70,3 +70,4 @@ ecode.dmg
/bin/unit_tests/eepp*
/bin/unit_tests/lib*
/projects/linux/ecode/polyfill-glibc
/bin/crashes/*

View File

@@ -4281,6 +4281,8 @@ public:
#elif defined(__mips__)
error_addr = reinterpret_cast<void *>(
reinterpret_cast<struct sigcontext *>(&uctx->uc_mcontext)->sc_pc);
#elif defined(__APPLE__) && defined(__POWERPC__)
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext->__ss.__srr0);
#elif defined(__ppc__) || defined(__powerpc) || defined(__powerpc__) || \
defined(__POWERPC__)
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.regs->nip);
@@ -4292,6 +4294,8 @@ public:
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext->__ss.__rip);
#elif defined(__APPLE__)
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext->__ss.__eip);
#elif defined(__loongarch__)
error_addr = reinterpret_cast<void *>(uctx->uc_mcontext.__pc);
#else
#warning ":/ sorry, ain't know no nothing none not of your architecture!"
#endif

View File

@@ -1,43 +1,13 @@
// Pick your poison.
//
// On GNU/Linux, you have few choices to get the most out of your stack trace.
//
// By default you get:
// - object filename
// - function name
//
// In order to add:
// - source filename
// - line and column numbers
// - source code snippet (assuming the file is accessible)
// Install one of the following libraries then uncomment one of the macro (or
// better, add the detection of the lib and the macro definition in your build
// system)
// - apt-get install libdw-dev ...
// - g++/clang++ -ldw ...
// #define BACKWARD_HAS_DW 1
// - apt-get install binutils-dev ...
// - g++/clang++ -lbfd ...
// #define BACKWARD_HAS_BFD 1
// - apt-get install libdwarf-dev ...
// - g++/clang++ -ldwarf ...
// #define BACKWARD_HAS_DWARF 1
// Regardless of the library you choose to read the debug information,
// for potentially more detailed stack traces you can use libunwind
// - apt-get install libunwind-dev
// - g++/clang++ -lunwind
// #define BACKWARD_HAS_LIBUNWIND 1
#include <eepp/config.hpp>
#include <eepp/system/filesystem.hpp>
#include <eepp/system/sys.hpp>
using namespace EE;
using namespace EE::System;
#if EE_PLATFORM != EE_PLATFORM_ANDROID && EE_PLATFORM != EE_PLATFORM_IOS
#if EE_PLATFORM == EE_PLATFORM_LINUX && defined( ECODE_HAS_DW )
#if defined( ECODE_HAS_DW )
#define BACKWARD_HAS_DW 1
#endif
@@ -45,8 +15,251 @@
namespace backward {
#if EE_PLATFORM == EE_PLATFORM_WIN
class WindowsSignalHandling {
public:
WindowsSignalHandling( const std::vector<int>& = std::vector<int>() ) :
reporter_thread_( []() {
/* We handle crashes in a utility thread:
backward structures and some Windows functions called here
need stack space, which we do not have when we encounter a
stack overflow.
To support reporting stack traces during a stack overflow,
we create a utility thread at startup, which waits until a
crash happens or the program exits normally. */
{
std::unique_lock<std::mutex> lk( mtx() );
cv().wait( lk, [] { return crashed() != crash_status::running; } );
}
if ( crashed() == crash_status::crashed ) {
handle_stacktrace( skip_recs() );
}
{
std::unique_lock<std::mutex> lk( mtx() );
crashed() = crash_status::ending;
}
cv().notify_one();
} ) {
// Disable Windows Error Reporting dialogs
// SetErrorMode( SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS );
SetUnhandledExceptionFilter( crash_handler );
signal( SIGABRT, signal_handler );
std::set_terminate( &terminator );
#ifndef BACKWARD_ATLEAST_CXX17
std::set_unexpected( &terminator );
#endif
_set_purecall_handler( &terminator );
_set_invalid_parameter_handler( &invalid_parameter_handler );
}
bool loaded() const { return true; }
~WindowsSignalHandling() {
{
std::unique_lock<std::mutex> lk( mtx() );
crashed() = crash_status::normal_exit;
}
cv().notify_one();
reporter_thread_.join();
}
private:
static CONTEXT* ctx() {
static CONTEXT data;
return &data;
}
enum class crash_status { running, crashed, normal_exit, ending };
static crash_status& crashed() {
static crash_status data;
return data;
}
static std::mutex& mtx() {
static std::mutex data;
return data;
}
static std::condition_variable& cv() {
static std::condition_variable data;
return data;
}
static HANDLE& thread_handle() {
static HANDLE handle;
return handle;
}
std::thread reporter_thread_;
static const constexpr int signal_skip_recs =
#ifdef __clang__
4
#else
3
#endif
;
static int& skip_recs() {
static int data;
return data;
}
static inline void terminator() {
crash_handler( signal_skip_recs );
abort();
}
static inline void signal_handler( int ) {
crash_handler( signal_skip_recs );
abort();
}
static inline void __cdecl invalid_parameter_handler( const wchar_t*, const wchar_t*,
const wchar_t*, unsigned int,
uintptr_t ) {
crash_handler( signal_skip_recs );
abort();
}
NOINLINE static LONG WINAPI crash_handler( EXCEPTION_POINTERS* info ) {
crash_handler( 0, info->ContextRecord );
return EXCEPTION_CONTINUE_SEARCH;
}
NOINLINE static void crash_handler( int skip, CONTEXT* ct = nullptr ) {
if ( ct == nullptr ) {
RtlCaptureContext( ctx() );
} else {
memcpy( ctx(), ct, sizeof( CONTEXT ) );
}
DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
&thread_handle(), 0, FALSE, DUPLICATE_SAME_ACCESS );
skip_recs() = skip;
{
std::unique_lock<std::mutex> lk( mtx() );
crashed() = crash_status::crashed;
}
cv().notify_one();
{
std::unique_lock<std::mutex> lk( mtx() );
cv().wait( lk, [] { return crashed() != crash_status::crashed; } );
}
}
static void display_crash_message( const std::string& crashFilePath ) {
std::string crashMsg(
String::format( "ecode has encountered an unrecoverable error and crashed! :'(\n"
"A crash log has been saved at:\n%s\n"
"Please feel free to use this file to report a bug at the ecode Github "
"page so it can be fixed as soon as possible.",
crashFilePath ) );
std::wstring wCrashMsg( crashMsg.begin(), crashMsg.end() );
std::wstring wTitle = L"ecode crashed!";
MessageBoxW( nullptr, wCrashMsg.c_str(), wTitle.c_str(),
MB_OK | MB_ICONERROR | MB_TOPMOST );
}
static void handle_stacktrace( int skip_frames = 0 ) {
std::string crashesPath( Sys::getProcessPath() );
FileSystem::dirAddSlashAtEnd( crashesPath );
crashesPath += "crashes";
FileSystem::dirAddSlashAtEnd( crashesPath );
std::string dateTimeStr( Sys::getDateTimeStr() );
String::replaceAll( dateTimeStr, " ", "_" );
String::replaceAll( dateTimeStr, ":", "-" );
std::string crashFilePath(
String::format( "%sstacktrace_%s.log", crashesPath, dateTimeStr ) );
FileSystem::makeDir( crashesPath );
std::ofstream outFile( crashFilePath );
if ( !outFile.is_open() ) {
print_to_console( "Error: Failed to open " + crashFilePath +
" for writing stack trace\n" );
print_stacktrace_to_console( skip_frames, nullptr );
return;
}
Printer printer;
printer.address = true;
printer.object = true;
StackTrace st;
st.set_machine_type( printer.resolver().machine_type() );
st.set_thread_handle( thread_handle() );
st.load_here( 64 + skip_frames, ctx() ); // Increased frame limit
st.skip_n_firsts( skip_frames );
printer.print( st, outFile );
print_stacktrace_to_console( skip_frames, &st );
outFile.close();
display_crash_message( crashFilePath );
}
static void print_stacktrace_to_console( int skip_frames, const StackTrace* st = nullptr ) {
if ( AttachConsole( ATTACH_PARENT_PROCESS ) ) {
FILE* console = nullptr;
freopen_s( &console, "CONOUT$", "w", stderr );
if ( console ) {
Printer printer;
printer.address = true;
printer.object = true;
if ( st ) {
printer.print( *st, std::cerr );
} else {
StackTrace new_st;
new_st.set_machine_type( printer.resolver().machine_type() );
new_st.set_thread_handle( thread_handle() );
new_st.load_here( 64 + skip_frames, ctx() );
new_st.skip_n_firsts( skip_frames );
printer.print( new_st, std::cerr );
}
fflush( stderr );
}
FreeConsole();
}
}
static void print_to_console( const std::string& message ) {
if ( AttachConsole( ATTACH_PARENT_PROCESS ) ) {
FILE* console = nullptr;
freopen_s( &console, "CONOUT$", "w", stderr );
if ( console ) {
std::cerr << message;
fflush( stderr );
}
FreeConsole();
}
}
};
backward::WindowsSignalHandling sh;
#else
backward::SignalHandling sh;
#endif
} // namespace backward
#endif