`/sylph-canary` was dead at both ends, measured rather than assumed:
* `run-canary-safe.sh` runs the Wine build and defaults to
`xenia_canary_i2d.exe`, which DOES NOT EXIST. The `xenia_canary.exe` that
does (2026-06-19) has ZERO strings for `audit_61_branch_probe_pcs` and zero
for `RE-INPUT`/`RE-DRAW` -- it predates all our instrumentation, and
refreshing it needs the clang-cl + xwin toolchain on `cross-build-wine`.
* both native launchers defaulted to
`xenia-canary-native/build/bin/Linux/Release/xenia_canary`. That directory
does not exist on this workspace -- the checkout is `xenia-canary/` -- and
Release is not even a target in that build tree (`ninja -n` lists 0 steps),
so its binary is frozen at 2026-08-28, before the probe.
Native launchers now default to
`xenia-canary/build/bin/Linux/Checked/xenia_canary`: the config this tree can
actually build, rebuilt today with the audit_61 probe (Canary PR #2), verified
by `strings` -- audit_61 8 hits, RE-INPUT/RE-DRAW 4. `Checked` is optimised WITH
assertions, which is what a behavioural reference should be: a bad state stops
loudly rather than being sampled.
The Wine launcher now ABORTS with the reason and points at the native one,
instead of failing somewhere downstream on a missing file.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
141 lines
6.7 KiB
Bash
Executable File
141 lines
6.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# INTERACTIVE launcher for the NATIVE Linux Xenia-Canary build.
|
|
#
|
|
# Opens a REAL window on your desktop (display :0) and — unlike
|
|
# run-canary-native-safe.sh — uses HARDWARE Vulkan (the AMD GPU) and leaves
|
|
# audio ON. This is for hands-on play/testing, not headless tracer runs.
|
|
#
|
|
# ⚠ WARNING: this renders on the same AMD GPU that drives your VS Code / desktop.
|
|
# The historical "crashes VS Code" issue was vkd3d-proton (D3D12->Vulkan under
|
|
# Wine); the native build talks to Vulkan directly, so it MAY be fine — but if
|
|
# the desktop glitches or VS Code dies, fall back to run-canary-native-safe.sh
|
|
# (software Vulkan) or set CANARY_SOFTWARE=1 below.
|
|
#
|
|
# Usage: tools/run-canary-native.sh [seconds] (no arg => runs until you close it)
|
|
# env: CANARY_BIN override binary (default = worktree Release build)
|
|
# CANARY_SOFTWARE=1 force software Vulkan (lavapipe) even here
|
|
# CANARY_MUTE=1 start muted / no audio device (--apu=nop)
|
|
# CANARY_GPU=<substr> pick a specific Vulkan device by name (e.g. radv, 6800)
|
|
# CANARY_EXTRA_ARGS space-separated extra cvars
|
|
# DISPLAY target X display (default :0 = your screen)
|
|
# Output: /tmp/canary_native_interactive.stdout (+ xenia.log in the binary dir).
|
|
set -u
|
|
|
|
# Paths derive from where this script sits — see the note in run-canary-safe.sh.
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
WORKSPACE="$(cd "$HERE/../.." && pwd)"
|
|
# 🔴 THE INSTRUMENTED BINARY IS THE `Checked` ONE, and it is the only config this
|
|
# build tree can produce: `ninja -n bin/Linux/Release/xenia_canary` lists ZERO
|
|
# steps, so Release is not a target here and its binary still dates from
|
|
# 2026-08-28, before the audit_61 probe existed. Pointing at Release therefore
|
|
# runs an oracle with no probe and no way to refresh it.
|
|
#
|
|
# The old default named `xenia-canary-native/`, a directory that does not exist
|
|
# on this workspace at all -- the checkout is `xenia-canary/`.
|
|
#
|
|
# `Checked` is optimised WITH assertions, which is what you want from a
|
|
# behavioural reference: a bad state stops loudly instead of being sampled.
|
|
BIN_DEFAULT="$WORKSPACE/xenia-canary/build/bin/Linux/Checked/xenia_canary"
|
|
BIN_EXE="${CANARY_BIN:-$BIN_DEFAULT}"
|
|
ISO="${SYLPHEED_ISO:-$WORKSPACE/Project Sylpheed - Arc of Deception (USA, Europe) (En,Ja).iso}"
|
|
[ -f "$ISO" ] || { echo "ABORT: no ISO at '$ISO' — set \$SYLPHEED_ISO"; exit 4; }
|
|
SECS="${1:-0}" # 0 => no timeout (interactive)
|
|
export DISPLAY="${DISPLAY:-:0}"
|
|
|
|
[ -x "$BIN_EXE" ] || { echo "ABORT: native binary not found/executable: $BIN_EXE"; exit 4; }
|
|
|
|
# --- Vulkan device selection ---
|
|
if [ "${CANARY_SOFTWARE:-0}" = "1" ]; then
|
|
export VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/lvp_icd.json
|
|
export VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json
|
|
export MESA_VK_DEVICE_SELECT=llvmpipe
|
|
echo "GPU: forced software Vulkan (lavapipe)."
|
|
else
|
|
# Hardware Vulkan. Optionally pin a device by substring (RADV AMD is default [0]).
|
|
[ -n "${CANARY_GPU:-}" ] && export MESA_VK_DEVICE_SELECT="$CANARY_GPU"
|
|
echo "GPU: HARDWARE Vulkan${CANARY_GPU:+ (pinned: $CANARY_GPU)}."
|
|
echo " Visible devices:"; vulkaninfo --summary 2>/dev/null | grep -i deviceName | sed 's/^/ /'
|
|
fi
|
|
|
|
# --- logging: keep it QUIET so kernel/APU debug spam doesn't starve the audio
|
|
# worker thread. At --log_level=3 (Debug) the d>/A> firehose (piped through
|
|
# tee) stalls the ALSA pipeline and mission audio dies permanently — the
|
|
# exact "mission-audio silence" that commit f10484834 fixes in code; the
|
|
# log flood defeats the keepalive. log_mask=13 = suppress Kernel|Cpu|Gpu.
|
|
# (guest_audio_flags defaults to 0 = Digital Stereo, the Linux-safe path.)
|
|
LOGLEVEL="${CANARY_LOGLEVEL:-1}"
|
|
LOGMASK="${CANARY_LOGMASK:-13}"
|
|
# --- audio ---
|
|
args=(--log_level="$LOGLEVEL" --log_mask="$LOGMASK")
|
|
if [ "${CANARY_MUTE:-0}" = "1" ]; then
|
|
export SDL_AUDIODRIVER=dummy
|
|
args+=(--mute=true --apu=nop)
|
|
echo "AUDIO: muted (apu=nop)."
|
|
else
|
|
echo "AUDIO: on (ALSA)."
|
|
fi
|
|
|
|
# --- audio RE: capture true XMA per-stream params (channels/rate/head bytes) to
|
|
# xenia.log as you play. Deduped (one line per unique sound); logs at Warning
|
|
# so it shows at the audio-safe log level without spam. Off unless requested.
|
|
if [ "${CANARY_XMA_PROBE:-0}" = "1" ]; then
|
|
args+=(--xma_param_probe=true)
|
|
echo "XMA-PARAM probe: ON (params captured to xenia.log)."
|
|
fi
|
|
|
|
# --- audio watchdog: when audio dies mid-mission and never returns, report
|
|
# WHICH pipeline stage stopped (guest callback blocked / no XMA decode /
|
|
# no frames submitted / host driver not writing). Near-silent while healthy.
|
|
if [ "${CANARY_AUDIO_WD:-0}" = "1" ]; then
|
|
args+=(--audio_watchdog=true)
|
|
echo "AUDIO watchdog: ON (logs 'AUDIO-WD ...' when audio dies)."
|
|
fi
|
|
|
|
# --- hang watchdog: if the guest stops presenting frames for N seconds, the
|
|
# emulator dumps every guest thread's registers + guest call stack to
|
|
# xenia.log by itself. No debugger, no ptrace, no need to keep the window
|
|
# open -- the freeze autopsy is already in the log.
|
|
if [ -n "${CANARY_HANG_WD:-}" ]; then
|
|
args+=(--hang_watchdog_secs="$CANARY_HANG_WD")
|
|
echo "HANG watchdog: ON (${CANARY_HANG_WD}s without a frame => guest dump to xenia.log)."
|
|
fi
|
|
|
|
if [ -n "${CANARY_EXTRA_ARGS:-}" ]; then
|
|
read -ra EXTRA <<< "$CANARY_EXTRA_ARGS"
|
|
args+=("${EXTRA[@]}")
|
|
fi
|
|
|
|
# --- CPU stress: the mid-mission audio death only shows up under host CPU load
|
|
# (the guest's audio callback misses its 5.33ms deadlines and the game tears
|
|
# its own audio client down). CANARY_STRESS=<n> spins n busy loops for the
|
|
# duration of the run so the bug reproduces on demand instead of by luck.
|
|
# They are killed when the run ends. Silent (no audio involvement).
|
|
STRESS_PIDS=()
|
|
stress_cleanup() {
|
|
if [ ${#STRESS_PIDS[@]} -gt 0 ]; then
|
|
kill "${STRESS_PIDS[@]}" 2>/dev/null
|
|
echo "stress: stopped ${#STRESS_PIDS[@]} load generators."
|
|
fi
|
|
}
|
|
trap stress_cleanup EXIT INT TERM
|
|
if [ -n "${CANARY_STRESS:-}" ] && [ "${CANARY_STRESS}" -gt 0 ] 2>/dev/null; then
|
|
for _i in $(seq 1 "$CANARY_STRESS"); do
|
|
# Pure userspace spin; no I/O, no audio, no privileges.
|
|
( while :; do :; done ) &
|
|
STRESS_PIDS+=($!)
|
|
done
|
|
echo "STRESS: ON -- ${CANARY_STRESS} busy loops competing for CPU (of $(nproc) cores)."
|
|
fi
|
|
|
|
pkill -x xenia_canary 2>/dev/null
|
|
cd "$(dirname "$BIN_EXE")" || { echo "ABORT: bin dir missing"; exit 4; }
|
|
rm -f xenia.log
|
|
|
|
echo "launch (interactive, DISPLAY=$DISPLAY): $(basename "$BIN_EXE") ${args[*]}"
|
|
if [ "$SECS" -gt 0 ] 2>/dev/null; then
|
|
timeout -k 5 "$SECS" "$BIN_EXE" "$ISO" "${args[@]}" 2>&1 | tee /tmp/canary_native_interactive.stdout
|
|
else
|
|
"$BIN_EXE" "$ISO" "${args[@]}" 2>&1 | tee /tmp/canary_native_interactive.stdout
|
|
fi
|
|
rc=${PIPESTATUS[0]}
|
|
echo "rc=$rc ADV.wmv=$(grep -ac 'ADV.wmv' xenia.log 2>/dev/null)" |