mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
main.c foreign_hook matches "hook", so a crash in RTSSHooks64.dll got the line that the library is not part of the game and that a recording, overlay or capture program put it there, also when the game's own thread that sets the frame limit (rtss_limit.c's "rtss" worker) had loaded and called it. The one RTSS crash on record (issue #231) faulted in ntdll on that thread, where no note applied. And the crash handler then called rtss_limit_restore from that thread, whose own lock it holds (SRW locks are not recursive), so the restore's trylocks spent a second. The platform layer keeps the name a thread was made with in the thread itself (plat_thread_name, copied at its start: a detached thread's struct may go before it ends). main.c crash_module_note picks the note: on the "rtss" thread, whatever the module, the line says the fault is in the game's calls of RTSS's library or around them and that rtss = 0 keeps the game from calling it; on other threads RTSSHooks64.dll stays foreign, as it is RTSS's overlay there. The restore is skipped on the rtss thread. rtss_limit.c is unchanged: the thread is known by the name it is spawned with. Crash text only, besides the skipped restore. Not reproduced before the change. tests/foreign_hook_check.py checks the note's choice (RTSSHooks64.dll and ntdll on the RTSS thread: the RTSS note; RTSSHooks64.dll elsewhere: foreign; a driver: none), and tests/crash_stack_check.py that the crash report reads the faulting thread's name. They, thread_end_check, fatal_exit_check and the compile checks of every runtime file pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
72 lines
3.1 KiB
Python
72 lines
3.1 KiB
Python
"""Build and run tests/crash_stack_check.c: a stack overflow on a thread and on a fiber made by the platform layer
|
|
(runtime/platform/win32.c thread_main, fiber_start, plat_stack_guarantee) still gets its whole crash report, a file
|
|
write and three 2 KiB log lines, and ends with the report's code 71. Without the stack kept back the report faulted
|
|
again and the process ended with an access violation and no line. No game files.
|
|
|
|
Usage: crash_stack_check.py
|
|
"""
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
MSYS_BIN = Path(r'C:\msys64\ucrt64\bin')
|
|
|
|
LAYOUT = '''// A stand-in for the generated layout.h (tests/crash_stack_check.py).
|
|
#pragma once
|
|
#define MOD_MAIN_BASE 0x7000000000ull
|
|
#define MOD_MAIN_SIZE 0x4ea0000ull
|
|
'''
|
|
|
|
|
|
def compiler():
|
|
if os.environ.get('CC'):
|
|
return os.environ['CC']
|
|
if os.name == 'nt' and (MSYS_BIN / 'clang.exe').exists():
|
|
return str(MSYS_BIN / 'clang.exe')
|
|
return shutil.which('clang') or 'clang'
|
|
|
|
|
|
def main():
|
|
if os.name != 'nt':
|
|
print('crash_stack_check: Windows only')
|
|
return 0
|
|
work = ROOT / 'build' / 'test' / 'crash_stack'
|
|
work.mkdir(parents=True, exist_ok=True)
|
|
(work / 'layout.h').write_text(LAYOUT)
|
|
exe = work / 'crash_stack_check.exe'
|
|
env = dict(os.environ)
|
|
if MSYS_BIN.exists():
|
|
env['PATH'] = str(MSYS_BIN) + os.pathsep + env.get('PATH', '')
|
|
rt = ROOT / 'runtime'
|
|
plat = [rt / 'platform' / 'win32.c', rt / 'platform' / 'common.c']
|
|
libs = ['-lbcrypt', '-lws2_32', '-lpsapi', '-lwinmm', '-ldbghelp', '-lshell32', '-lole32', '-luser32', '-ladvapi32']
|
|
subprocess.check_call([compiler(), '-O1', '-g', '-Wall', '-Wno-unused-function', '-fno-strict-aliasing',
|
|
'-march=x86-64-v3', '-I', str(rt), '-I', str(work), str(ROOT / 'tests' / 'crash_stack_check.c'),
|
|
*map(str, plat), '-o', str(exe), '-lm', *libs], env=env)
|
|
bad = 0
|
|
for mode in ('thread', 'fiber'):
|
|
note = work / f'note_{mode}.txt'
|
|
note.unlink(missing_ok=True)
|
|
p = subprocess.run([str(exe), mode, str(note)], env=env, capture_output=True, text=True, errors='replace',
|
|
timeout=60)
|
|
lines = [f'report line {i}' for i in (1, 2, 3)]
|
|
missing = [x for x in lines if x not in p.stdout]
|
|
# platform.h plat_thread_name, which the crash report reads: the spawned thread's name; the fiber runs on the
|
|
# main thread, which the platform layer did not make
|
|
named = ('on thread deep' if mode == 'thread' else 'on thread (unnamed)') in p.stdout
|
|
ok = p.returncode == 71 and not missing and 'stack overflow' in p.stdout and 'the file written: 1' in p.stdout and named
|
|
print(f'a stack overflow on a {mode}: exit code {p.returncode & 0xffffffff:#x} (want 0x47), '
|
|
f'{3 - len(missing)} of 3 report lines: {"ok" if ok else "FAIL"}', flush=True)
|
|
if not ok:
|
|
print(p.stdout[-2000:], p.stderr[-2000:])
|
|
bad += not ok
|
|
print(f'crash_stack_check: {"FAILED" if bad else "ok"}')
|
|
return 1 if bad else 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|