Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
    npro  
 Modern all-safe Rust Network Protocol library supporting h1, h2, h3, ws, wt sans-IO and with socket IO + tls
git clone https://npro.rs/repo/npro
 
root / assets / gs / sent-forgot-fail.html
Author[]Andy Green <andy@warmcat.com> 2026-09-11 05:12 UTC
Committer[]Andy Green <andy@warmcat.com> 2026-09-11 05:12 UTC
Tree904af28f25c21cfa124c7fa33eaeff0eb32190e6   Raw Patch
 
builder: windows: log a symbolised backtrace on abort() and the fatal signals
builder: windows: log a symbolised backtrace on abort() and the fatal signals

A sai-builder service on Windows was dying with exit code 3 on service
stop, which is abort(), with nothing to show for it: a service has no
stderr for the CRT's report, its message boxes are invisible in session
0, and a debugger attached after startup gets no stop because the
report-fault was already disabled.  No assert text preceded the R6010
report in the debugger output, so it is a direct abort() from somewhere
below the builder, and only a stack will say where.

The unix build already has crash_handler() printing a backtrace on the
fatal signals.  Give Windows the same: abort() raises SIGABRT before
exiting, so a handler on SIGABRT (and SIGSEGV, SIGILL, SIGFPE) walks the
stack with CaptureStackBackTrace(), symbolises it through dbghelp with
the .pdb files next to the binaries, and logs each frame with function,
file and line via lwsl_err(), which for a service lands in
%ProgramData%\sai\builder\sai-builder-service.log.  If a debugger is
attached it then breaks there, with the offending frames on the stack,
before letting the default disposition exit as before.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
diff --git a/src/builder/CMakeLists.txt b/src/builder/CMakeLists.txt index 50f43b5..998d6d9 100644 --- a/src/builder/CMakeLists.txt +++ b/src/builder/CMakeLists.txt @@ -80,7 +80,7 @@ add_dependencies(${SUB} sai_git_hash) endif() if (MSVC OR WIN32) - target_link_libraries(${SUB} ws2_32.lib userenv.lib psapi.lib iphlpapi.lib advapi32.lib) + target_link_libraries(${SUB} ws2_32.lib userenv.lib psapi.lib iphlpapi.lib advapi32.lib dbghelp.lib) endif() if (MSVC OR WIN32) diff --git a/src/builder/b-sai.c b/src/builder/b-sai.c index 78a6d68..1ad5b3e 100644 --- a/src/builder/b-sai.c +++ b/src/builder/b-sai.c @@ -56,6 +56,7 @@ #include <Shlobj.h> #include <processthreadsapi.h> #include <handleapi.h> +#include <dbghelp.h> #if !defined(PATH_MAX) @@ -539,6 +540,64 @@ crash_handler(int signum) #endif #if defined(WIN32) +/* + * The Debug CRT's abort() (an assert(), or a direct abort()) exits with + * code 3 and nothing else when there is no console: a service has no + * stderr, its message boxes are invisible in session 0, and if the + * debugger attached after startup the CRT report-fault is already off. + * abort() raises SIGABRT first, so log a symbolised backtrace from here, + * and hand a debugger the stop it was never given. + */ +static void +win_crash_handler(int signum) +{ + char symbuf[sizeof(SYMBOL_INFO) + 256]; + SYMBOL_INFO *sym = (SYMBOL_INFO *)symbuf; + HANDLE proc = GetCurrentProcess(); + IMAGEHLP_LINE64 line; + DWORD64 disp64 = 0; + void *frames[32]; + DWORD disp = 0; + USHORT n, i; + + lwsl_err("FATAL: caught signal %d, producing backtrace:\n", signum); + + SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_UNDNAME | + SYMOPT_DEFERRED_LOADS); + SymInitialize(proc, NULL, TRUE); + + n = CaptureStackBackTrace(0, (DWORD)LWS_ARRAY_SIZE(frames), frames, + NULL); + for (i = 0; i < n; i++) { + DWORD64 a = (DWORD64)(uintptr_t)frames[i]; + + memset(symbuf, 0, sizeof(symbuf)); + sym->SizeOfStruct = sizeof(SYMBOL_INFO); + sym->MaxNameLen = 255; + memset(&line, 0, sizeof(line)); + line.SizeOfStruct = sizeof(line); + + if (!SymFromAddr(proc, a, &disp64, sym)) { + lwsl_err(" #%u 0x%llx\n", i, (unsigned long long)a); + continue; + } + if (SymGetLineFromAddr64(proc, a, &disp, &line)) + lwsl_err(" #%u %s+0x%llx (%s:%lu)\n", i, sym->Name, + (unsigned long long)disp64, line.FileName, + line.LineNumber); + else + lwsl_err(" #%u %s+0x%llx\n", i, sym->Name, + (unsigned long long)disp64); + } + + if (IsDebuggerPresent()) + __debugbreak(); + + /* let the default disposition finish the job, exit code 3 for abort */ + signal(signum, SIG_DFL); + raise(signum); +} + static int log_fd = -1; static void @@ -632,6 +691,12 @@ saib_app_run(int argc, const char **argv) signal(SIGILL, crash_handler); signal(SIGFPE, crash_handler); #endif +#if defined(WIN32) + signal(SIGABRT, win_crash_handler); + signal(SIGSEGV, win_crash_handler); + signal(SIGILL, win_crash_handler); + signal(SIGFPE, win_crash_handler); +#endif lws_set_log_level(logs, NULL); }
Page fetched 0s ago, creation time: 2ms (vhost etag hits: 0%, cache hits: 0%)