#!/usr/bin/env bash # SAFE launcher for the NATIVE Linux Xenia-Canary build on THIS box. # # Same safety contract as run-canary-safe.sh (the Wine/Windows variant): the AMD # GPU + GPU-accelerated VS Code means a hardware-Vulkan render CRASHES VS Code. # This wrapper forces software Vulkan (lavapipe/llvmpipe) IN THE SAME process and # HARD-REFUSES to launch if any hardware Vulkan device is still visible. # # Difference vs run-canary-safe.sh: runs the native ELF directly (NO wine). # # Usage: tools/run-canary-native-safe.sh [seconds] # env: CANARY_BIN override binary (default = worktree Release build) # CANARY_EXTRA_ARGS space-separated extra cvars (values w/o spaces) # Output: /tmp/canary_native.stdout (+ xenia.log in the binary dir). Prints rc + ADV.wmv hits. set -u # --- force software Vulkan, belt-and-suspenders across loader/driver variants --- 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 export LIBGL_ALWAYS_SOFTWARE=1 # --- keep background runs SILENT *without* killing the audio system --- # DO NOT use --apu=nop: with no audio system the guest's XAudio render client is # NULL and Project Sylpheed derefs null+0x3C at PC 0x824D7C40 -> a FALSE boot # crash that never happens in interactive runs. Instead keep the ALSA apu ALIVE # and route PCM "default" to the null device (see asound-null.conf): silent, but # snd_pcm_open succeeds so the guest is happy. --mute does NOT silence ALSA. export SDL_AUDIODRIVER=dummy export ALSA_CONFIG_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/asound-null.conf" [ -f "$ALSA_CONFIG_PATH" ] || { echo "ABORT: asound-null.conf missing next to script"; exit 4; } # 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)" BIN_DEFAULT="$WORKSPACE/xenia-canary-native/build/bin/Linux/Release/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:-95}" [ -x "$BIN_EXE" ] || { echo "ABORT: native binary not found/executable: $BIN_EXE"; exit 4; } # --- HARD PRE-FLIGHT GATE: only proceed if Vulkan exposes software devices ONLY --- devs="$(vulkaninfo --summary 2>/dev/null | grep -i deviceName || true)" if echo "$devs" | grep -qiE 'radv|amd|radeon|nvidia|geforce|intel\b'; then echo "ABORT(pre-flight): a HARDWARE Vulkan device is still visible -> refusing (would crash VS Code):" echo "$devs" exit 3 fi if ! echo "$devs" | grep -qiE 'llvmpipe'; then echo "ABORT(pre-flight): llvmpipe not visible; lavapipe ICD missing? devs=[$devs]" exit 3 fi echo "pre-flight OK: software-only Vulkan -> $devs" # --- clean slate --- pkill -x xenia_canary 2>/dev/null; pkill -x Xvfb 2>/dev/null; sleep 1 Xvfb :99 -screen 0 1280x720x24 -nolisten tcp >/tmp/xvfb.log 2>&1 & XVFB=$! sleep 2 export DISPLAY=:99 cd "$(dirname "$BIN_EXE")" || { echo "ABORT: bin dir missing"; kill "$XVFB" 2>/dev/null; exit 4; } rm -f xenia.log # Real ALSA apu (routed to null sink via ALSA_CONFIG_PATH) so the guest gets a # valid render client; --mute belt-and-suspenders. NEVER add --apu=nop here. args=(--log_level=3 --mute=true) if [ -n "${CANARY_EXTRA_ARGS:-}" ]; then read -ra EXTRA <<< "$CANARY_EXTRA_ARGS" args+=("${EXTRA[@]}") fi echo "launch (native, sw-vulkan, SILENT null-ALSA, ${SECS}s): $(basename "$BIN_EXE") ${args[*]}" # -k 5: xenia catches SIGTERM and can hang on shutdown; force-KILL 5s later. timeout -k 5 "$SECS" "$BIN_EXE" "$ISO" "${args[@]}" >/tmp/canary_native.stdout 2>&1 rc=$? pkill -x xenia_canary 2>/dev/null; kill "$XVFB" 2>/dev/null; pkill -x Xvfb 2>/dev/null # --- trustworthy boot-health verdict (NOT log-line-count, NOT rc) --- # A healthy boot reaches actual content: title loaded + XMA audio decoding + NO # host-exception fault loop. rc=137 is EXPECTED (timeout -k kill), not a failure. faults=$(grep -ac 'Access Violation' /tmp/canary_native.stdout 2>/dev/null) gthrow=$(grep -ac 'GUEST-THROW' xenia.log 2>/dev/null) title=$(grep -ac 'Title name: PROJECT SYLPHEED' xenia.log 2>/dev/null) xma=$(grep -acE 'XmaContext|Processing context' xenia.log 2>/dev/null) echo "rc=$rc logsize=$(wc -c < xenia.log 2>/dev/null)" echo "boot-health: title=$title xma=$xma faults=$faults guest_throw=$gthrow" if [ "$title" -ge 1 ] && [ "$xma" -ge 100 ] && [ "$faults" -eq 0 ] && [ "$gthrow" -eq 0 ]; then echo "VERDICT: HEALTHY (reached content, no crash)" elif [ "$gthrow" -ge 1 ]; then echo "VERDICT: GUEST-THROW CRASH (the real bug) -- see GUEST-THROW lines in xenia.log" elif [ "$faults" -ge 1 ]; then echo "VERDICT: FAULT (host access-violation loop) -- crash PC:" grep -aoE 'PC: 0x[0-9A-Fa-f]+' /tmp/canary_native.stdout | sort | uniq -c | head -3 else echo "VERDICT: INCOMPLETE (did not reach content in ${SECS}s -- raise timeout?)" fi