mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
Two runs of 2026-11-13 (the 0.10.0 fresh install's nx2navi.1.log, 7,974,927 bytes, and the 0.9.2 test's nx2navi.3.log) ended seconds after "window lost the focus" with exit code 0, no WER event and no line of their own: an ExitProcess or exit() called by a module in the process and a kill from outside could not be told apart, and both lose the up to 250 ms of lines the log thread was gathering. A direct ExitProcess skips perf.c's atexit sync. (a) ExitProcess stops every other thread and then calls each module's detach, the executable's TLS callbacks included; TerminateProcess calls none. win32.c exit_note_tls, in the .CRT$XLB list beside _tls_used, writes on DLL_PROCESS_DETACH what the log queue holds (logq.c logq_peek: the bytes as they stand, no lock) and one line, "exit: the process is ending through ExitProcess, not one of the game's own ends", with the return addresses that led there. It runs under the loader lock with the other threads gone, so it uses no C runtime (hex written by hand) and does not ask the loader: the addresses are named from a module list noted at start and at each focus loss (plat_exit_note_modules: EnumProcessModules, two tables and a writers' lock the callback never takes), and RtlCaptureStackBackTrace and WriteFile take no lock of the process. plat_exit, plat_terminate and plat_reexec set the orderly flag (plat_exit_orderly), and main does once main_run has returned, so a tool mode's exit() leaves no line; a library's exit() during a game run does, naming ucrtbase and its caller. main.c log_open sets it up for a game run that writes a log file. Linux has empty stand-ins. (b) gfx_window_focus_changed calls the new main.c rt_log_focus_lost at a focus loss: logq_nudge makes the log thread write what it gathers at once (a flag log_main's gathering loop checks) and the module list is noted again. Not reproduced before the change (the two ends are unexplained). The new tests/exit_tls_check.py links win32.c and logq.c with a queued line still being gathered: ExitProcess from the program and exit() write the line and the note naming exit_tls_check.exe; TerminateProcess, plat_exit and a return after plat_exit_orderly write no note. tests/logq_check.c gains a nudge case, which fails with the flag left unset (252 ms) and passes with it. thread_end_check, fatal_exit_check, crash_stack_check, exit_order_check, save_share_check and the compile checks of every runtime file pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
46 lines
1.9 KiB
C
46 lines
1.9 KiB
C
// The note of an end nobody of ours asked for (runtime/platform/win32.c exit_note_tls, plat_exit_note_setup) with the
|
|
// platform layer and runtime/logq.c linked: the process logs a line through the log queue, which the log thread is
|
|
// still gathering, and ends. Built and run by tests/exit_tls_check.py, which reads the log file each mode leaves.
|
|
// exit_tls_check <mode> <log>, mode one of:
|
|
// exitprocess ExitProcess from a function of this program: the queued line, then the note naming this program
|
|
// exit exit(): the C runtime's ExitProcess, the same note
|
|
// terminate TerminateProcess: no note
|
|
// platexit plat_exit: the queued line (its hook writes the queue), no note
|
|
// return main returns after plat_exit_orderly, as main.c does after a tool mode: no note
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <windows.h>
|
|
|
|
#include "logq.h"
|
|
#include "platform/platform.h"
|
|
|
|
void rt_log(const char *fmt, ...) { (void)fmt; } // logq.c's line about slow writes
|
|
|
|
static PlatAppend *file;
|
|
static void sink(const char *p, size_t n) { plat_append_write(file, p, n); }
|
|
|
|
__attribute__((noinline)) static void end_here(void) {
|
|
ExitProcess(0);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
if (argc < 3 || !(file = plat_append_open(argv[2]))) return 2;
|
|
logq_init(sink);
|
|
if (!logq_async()) return 3;
|
|
plat_exit_hook = logq_sync;
|
|
plat_exit_note_setup(file, logq_peek);
|
|
plat_exit_note_modules();
|
|
logq_write("queued line\n", 12); // the log thread gathers it for LOGQ_GATHER_NS: still queued below
|
|
const char *m = argv[1];
|
|
if (!strcmp(m, "exitprocess")) end_here();
|
|
if (!strcmp(m, "exit")) exit(0);
|
|
if (!strcmp(m, "terminate")) TerminateProcess(GetCurrentProcess(), 0);
|
|
if (!strcmp(m, "platexit")) plat_exit(0);
|
|
if (!strcmp(m, "return")) {
|
|
plat_exit_orderly();
|
|
return 0;
|
|
}
|
|
return 2;
|
|
}
|