mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
A lost device used to end the process from whichever submit or wait noticed it first, while the present only logged it and went on and the other threads kept calling the driver. The completion thread could take the submissions' timeline as reached and run the callbacks of submissions that never finished. vk_device_lost_check now takes the result of every submit, present, acquire and wait. The first thread to see the loss sets a flag, writes the report and ends the process as a fatal error does (the log written, a save commit under way finished), with an exit code of its own; later threads stop in that call, and a watchdog ends the process after ten seconds should the first thread hang. The replay, its submit, the uploads, the present and the lazy fetch read the flag and return without touching the device. The completion thread asks the device with a call of its own before a new value counts and runs no callback once the flag is set. The executable's --probe-devlost and tests/device_lost_check.py check this without a lost device, and device_lost_at in nx2navi.ini ends a real run the same way. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
65 lines
3.5 KiB
C
65 lines
3.5 KiB
C
// Guest address -> host function lookup for indirect branches (rt_dispatch, declared in cpu.h).
|
|
//
|
|
// One flat table covers every module's code range: slot (pc - lo) >> 2 holds the host function for a guest
|
|
// function start, as a 32-bit offset from an anchor address. Lookup is one range check and one load. The table is
|
|
// built once by rt_dispatch_init and never written again, so threads read it without locks.
|
|
#pragma once
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "cpu.h"
|
|
|
|
// A lifted module: its place in the guest layout and the generator's sorted function table.
|
|
typedef struct RtModule {
|
|
const char *name;
|
|
uint64_t base, size; // guest image (layout.h MOD_<NAME>_BASE / _SIZE)
|
|
const uint64_t *addrs; // <mod>_fn_addrs: guest function starts, strictly ascending
|
|
const GuestFn *fns; // <mod>_fn_ptrs: the lifted function for each start
|
|
uint64_t count; // <mod>_fn_count
|
|
} RtModule;
|
|
|
|
// The module glue that tools/build_game.py writes for each build (build/<version>/modules.c), from layout.json and
|
|
// layout.h: every module of the layout, and the modules linked into this executable.
|
|
typedef struct RtLayoutEntry { const char *name; uint64_t base, size; } RtLayoutEntry;
|
|
extern const RtLayoutEntry rt_layout[];
|
|
extern const int rt_layout_count;
|
|
int rt_linked_modules(RtModule *out, int max);
|
|
|
|
// Register a host function at a guest address outside every module (HLE entry points). Allowed only before
|
|
// rt_dispatch_init; the registry is fixed afterwards.
|
|
void rt_register_host(uint64_t addr, GuestFn fn);
|
|
|
|
// Build the table from the modules (kept by pointer; they must outlive the process). Call it once, before any guest
|
|
// thread starts: thread creation publishes the table to the threads. Fatal on a malformed table:
|
|
// unsorted or duplicate starts, a start outside its module, overlapping modules, host code spread over 4 GiB.
|
|
void rt_dispatch_init(const RtModule *mods, int count);
|
|
|
|
// The host function for a guest address, or NULL when nothing is there. Does not run guest code.
|
|
GuestFn rt_dispatch_lookup(uint64_t pc);
|
|
|
|
// "0x7000001234 (main+0x1234)" for a guest address inside a module, else the bare address. Returns buf.
|
|
const char *rt_guest_name(uint64_t pc, char *buf, int size);
|
|
|
|
typedef struct RtDispatchStats {
|
|
uint64_t lo, hi; // guest range the table covers
|
|
uint64_t slots; // (hi - lo) / 4
|
|
uint64_t table_bytes; // reserved for the table
|
|
uint64_t functions; // filled slots
|
|
uint64_t host_fns; // rt_register_host entries
|
|
} RtDispatchStats;
|
|
RtDispatchStats rt_dispatch_stats(void);
|
|
|
|
// Runtime fatal error: message to stderr, then exit. Used by the dispatcher and the stubs.
|
|
void rt_fatal(const char *fmt, ...) __attribute__((noreturn, format(printf, 1, 2)));
|
|
// The same end with an exit code of its own, for a failure that a script or the launcher tells from the others by it
|
|
// (vk_device.c vk_device_lost_check ends with RT_LOST_EXIT). rt_fatal ends with RT_FATAL_EXIT; 71 is the crash report's
|
|
// (main.c crash_handler).
|
|
#define RT_FATAL_EXIT 70
|
|
#define RT_LOST_EXIT 72
|
|
void rt_fatal_code(int code, const char *fmt, ...) __attribute__((noreturn, format(printf, 2, 3)));
|
|
// Lines of context for a fatal error's report, which rt_fatal and the crash report (main.c crash_handler) write after
|
|
// their own: fn logs one with rt_log. It runs on the failing thread, which may hold any lock, so it reads only what
|
|
// needs none. At most four; adding one twice keeps one. rt_fatal_notes runs them, once per process.
|
|
void rt_fatal_note_add(void (*fn)(void));
|
|
void rt_fatal_notes(void);
|