Some distributed dumps were repacked with ampr_emu's pack tool: the packed files are removed from app0 and stored in ampr_assets-*.pak (AMPRPAK4, raw LZ4 blocks) that only ampr_emu reads. A Unity title converted from such a dump stops at start-up because Media/Resources/unity default resources is missing. Describe how to restore the files with ampr_pack.py unpack. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
11 KiB
Game testing
Running a game is a big effort for the community: a legally dumped title, a suitable machine, hours of setup, often a PR build, and time spent watching it run. Many runs end early with a crash or a stall. Every run is still useful, so this page asks for as much information as possible from each one. A failed run with a complete log helps as much as a successful one.
For user options see USAGE; for internals see ARCHITECTURE.
Before a run
- Build
mainor the PR head in a clean worktree, including thelibstarget (it is not in the default build). If a new test breaks the build,cmake --build build --target libs relinkeris enough to convert and run a title. - Convert the title from plain ELF files (
tools/unwrap_self.pyunwraps a dump'seboot.binand.prx):- Linux:
relinker --registry src/eboot.elf app.elf - Windows:
relinker --windows --registry src/eboot.elf app.exe. Without--windowsthe output is an ELF file whatever its name, and Windows refuses to start it (exit code 216, "not compatible with the version of Windows").
- Linux:
- A dump whose
app0holdsampr_assets.indexandampr_assets-*.pakwas repacked for ampr_emu: the packed files were removed fromapp0, and only ampr_emu reads the packs. Restore them first with ampr_emu'stools/ampr_pack.py unpack --index app0/ampr_assets.index --output <dir>and copy the files missing fromapp0. Without them a Unity title stops at start-up on the missingMedia/Resources/unity default resources. - Convert every module the title loads from its own folders (
sce_module/,Media/Modules/,Media/Plugins/). Unity titles need--module-dir Media/Modules --module-dir Media/Plugins. Put each printedGuest module:file next to its original.prxinapp0/, and keep the original there. libs/holds only AnyPS5's own libraries, frombuild/core/libs/libs(notunpatched/). A game module placed there fails to load (error 193 on Windows). A self-builtlibs/on Windows also needslibgcc_s_seh-1.dll,libstdc++-6.dllandlibwinpthread-1.dllfrom the toolchain'smingw64/bin, or every library fails with error 126 (USAGE).- Run
tools/import_audit.pyon the registries. Exit code 1 lists imports that will fail or throw. - Run one title at a time. Titles share the GPU and the controller file. Some titles start a second process (Peace Walker's
mgspw.mgspw.elf) that can outlive a time cap on the first: check that every process of the previous run is gone.
Launch
Linux:
APS5_PROFILE_DRAW=1 APS5_PROFILE_GPU=1 APS5_LOG_TIMESTAMPS=1 \
APS5_PAD_INPUT_FILE=/tmp/anyps5-pad \
setarch x86_64 -R timeout 600 ./app.elf > run.log 2>&1; echo "exit=$?"
Windows PowerShell:
$env:APS5_PROFILE_DRAW = '1'
$env:APS5_PAD_INPUT_FILE = "$PWD\pad.txt"
cmd /c '.\app.exe > run.log 2>&1'
"exit=$LASTEXITCODE"
- On Windows PowerShell 5.1,
*>writes the log as UTF-16 and wraps the first stderr line in an error record, sogrepcan't read it; letcmddo the redirection, as above.$LASTEXITCODEstill carries the exit code. APS5_PROFILE_DRAW=1prints[present]lines every 10 s. Without it a working title looks frozen.APS5_NO_PAD_INPUT=1ignores keyboard and mouse, so a stray press doesn't move a measurement run.setarch x86_64 -R(Linux only) turns off address randomisation so addresses match between runs.- On Windows, a
--windows-guibuild started without a console writes its output toanyps5.logbeside the executable, replacing the previous run's file. - On Windows the title needs a signed-in desktop. Started from an SSH session (session 0) it has no usable window. Start it inside the user's session instead, for example with a scheduled task that runs interactively. A newly created task does not start on battery power by default and fails silently; allow that in its settings.
Check the GPU and the caches
-
GPU: copy the GPU from the log's
Selected GPU name=line. Without presentation support AnyPS5 can select a software renderer: under Xvfb (no DRI3) that is llvmpipe, without an error, and the title still runs. SetANYPS5_GPUto part of the expected device name; the start then fails if that device can't be used. For headless Linux runs on a real GPU, Weston headless with the GL renderer plus Xwayland provides DRI3. -
Caches: first and later runs can differ a lot. On a slow GPU a first frame took minutes with empty caches and seconds on the next run. Two caches matter:
- AnyPS5's
shader_cache/beside the executable, orANYPS5_SHADER_CACHE_DIR.ANYPS5_NO_SHADER_CACHE=1disables it. - The driver's own: Mesa (
MESA_SHADER_CACHE_DIR, orMESA_SHADER_CACHE_DISABLE=true), AMD on Windows (%LOCALAPPDATA%\AMD\VkCache). Back a driver cache up instead of deleting it; it isn't AnyPS5's.
Say in the report whether the caches were empty or warm. A first run that ends in device lost (
VK_ERROR_DEVICE_LOST) or a GPU wait timeout while large pipelines are still being built, seen on AMD, often is the cache and not a regression: run again with the caches warm and report both runs. - AnyPS5's
-
Power and display: on a laptop, run on AC power; a run on battery can make a neutral change look like a regression. Note the display's refresh rate: a title that asks for 120 Hz output can stop at start on a 60 Hz display.
What to read
| Check | Where | Meaning |
|---|---|---|
| Exit code | shell | Linux: 124 time cap, 134 uncaught exception, 139 segfault, 137 killed. Windows: the exception code, 0xc0000005 access violation, 0xc0000409 uncaught exception or abort() (the log ends with FATAL: abort() (SIGABRT) on thread N) (PowerShell shows it signed, -1073740791) |
| Last lines | run.log or anyps5.log |
terminate called, what(), presentation failed, ASSERT, FATAL: crash report |
| Frames | [present] N presents over 10 s |
Steady N above 0 means frames are moving |
| Memory | [texture] N storage images (X MiB) |
Growth before a vkAllocateMemory ... -2 error |
| Queue waits | [packets] ... WAIT_REG_MEM_64 ... |
A queue spending the window waiting on the GPU |
| Milestones | Level has started: <name> |
How far the title got |
| Kernel (Linux) | dmesg | grep app.elf |
Fault address and instruction pointer |
| System log (Windows) | Event Viewer, System | A hard hang with nothing logged shows up afterwards as Kernel-Power 41 |
Stall, slowdown, or crash
- Stall: no
[present]lines for a long time while other periodic lines keep coming. Some titles present only when the view changes, so first send one input (tools/pad_input.py) and check whether presents resume. - Slowdown: presents per 10 s far below earlier runs of the same scene. Record the scene (save file or level), since it changes the numbers, and check power and caches first.
- Crash on Linux: take the
ipfromdmesgand its offset in the image. Repeat the run undergdb(launched by gdb, not attached) for registers, the faulting instructions and all thread stacks. - Crash on Windows: the
FATAL: unhandled exception ...report in the log lists the faultingrip(for exampleapp.exe+0x...), the registers and a stack scan.APS5_CRASH_INDIRECT_MEMORY=1adds up to 64 bytes from each pointer in the register dump.APS5_HANG_DUMP_SECS=<n>writes a dump after a hang of n seconds.APS5_TRACE_EXIT=1reports the process exit point. - Traces (heavy):
APS5_TRACE_FRAMES=<n>:<m>orAPS5_TRACE_SECONDS=<s>:<len>writes a capture fortools/trace_digest.py. Traces slow the title and can move the crash; say so when reporting.
Measuring performance
- Measure in gameplay, not in cinematics, menus or loading screens.
- Use the title's default settings, and say so. A changed graphics mode makes a number incomparable with everyone else's.
- Measure the same spot every time: load a save, wait for the scene to settle, then measure a fixed window with no input.
- Compare builds on the same machine, alternating them (A, B, A, B), at least two runs each. Throw away a run that was disturbed (another program, a sleeping display) and say so.
APS5_TRACE_SUMMARY_EVERY=10writes one line per 10 s toaps5-summary.jsonlwithout slowing the title;tools/trace_digest.pyturns it into a table, andtools/performance_compare.pycompares two captures (BUILD). Pull requests labelledperfneed a capture ofmainand one with the change.APS5_DUMP_FRAMES=<n>withAPS5_DUMP_FRAMES_EVERY=<k>saves frames asframe_<index>.bmpto check what was on screen. A second process of the same title writes to the same names and overwrites the first one's frames.
Title notes
- PlayGo: dumps without PlayGo metadata stop at start unless
APS5_PLAYGO_UNLISTED_CHUNKSis set; report the value used. - UE4 titles: keep the startup movies in place. Renaming them to skip the intro has made a level render without its 3D view, which looked like an emulator bug and wasn't.
Input
tools/pad_input.py writes presses to APS5_PAD_INPUT_FILE; they reach the title without focusing its window. Keys sent to the window instead (for example with SendInput on Windows) only arrive while it has focus. Physical controllers are opened at start and when plugged in later; the log names them with Pad: connected game controller.
Saves
Save data is kept under the runtime's _sd/ folder, and save-data memory under _sd_mem/. Copy them into a new runtime to continue from the same point.
Reporting a result
Include, in this order:
- The build: the
maincommit, each PR applied on top with its head, and any local change. A result from an integration branch should stay distinguishable after one of its PRs merges. - Title ID and content version, not only the game name.
- Host: CPU, OS (and kernel on Linux), the GPU as the
Selected GPU name=line names it, the driver version, AC or battery, and the display refresh rate. - The exact command and every
APS5_*/ANYPS5_*variable, the in-game settings if they differ from the defaults, and whether the caches were empty or warm. - How far the title got (menu, a save, a scene) and what stopped it: the exit code and the last 200 log lines, plus the kernel line (Linux) or crash report (Windows) for a crash.
- Presents per 10 s, and storage-image lines if memory is involved.
- How many runs and how long each lasted. One run is an observation, not a reproduction.
- The date of the test. Leave unknown fields empty rather than filling them from another report.
Keep AI-generated analysis separate from measured data, and say which is which.