mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 17:09:57 -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>
15 lines
1.2 KiB
C
15 lines
1.2 KiB
C
// Log writes on a thread of their own (logq.c). main.c owns the log's destinations and hands every line here.
|
|
#pragma once
|
|
#include <stddef.h>
|
|
|
|
typedef void (*LogqSink)(const char *p, size_t n); // writes the bytes to the log's destinations; may block
|
|
|
|
void logq_init(LogqSink sink); // before the first logq_write; lines are written by the thread that logs them
|
|
int logq_async(void); // from now on the log thread writes them; 0 when it could not be started
|
|
void logq_write(const char *p, size_t n); // queues the bytes, or writes them when the queue is off or has no room
|
|
void logq_sync(void); // writes what is queued, on the calling thread, and turns the queue off
|
|
void logq_flush(void); // writes what is queued, on the calling thread, and leaves the queue on
|
|
void logq_nudge(void); // the log thread writes what it gathers now (a focus loss)
|
|
void logq_peek(const char **p, size_t *n); // what is queued as it stands, no lock (the end nobody asked for)
|
|
size_t logq_bytes(void); // the queue's buffers, for the memory report (vk_memory.c system_log)
|