Files
AnyPS5/docs/user/run-report.md
T
Dean Galvin fd4f6c74d9 feat(run-report): count milestones, frames, GPU and shader diagnostics
The run report now records how far a run got (loaded, started, first draw,
first present), the frames presented and the average frame rate, the selected
GPU, the number of shaders compiled, failed and served from the cache, the
instructions the shader recompiler could not translate, the guest image
formats looked up or rejected, and the descriptor kinds that shaders bind.
The counters are relaxed atomics and are only touched while a report is being
collected. The window system's request to quit is recorded as the reason a run
ended.
2026-11-01 17:15:09 +13:00

12 KiB

Run report

anyps5-report.json is a structured summary of one run. run-report.schema.json is the single source of truth for its shape; the sample reports in run-report-fixtures/ all validate against it. This is version 1. Adding a field keeps version 1; renaming or removing a field, or changing what one means, is version 2.

The file stays on your machine. Nothing is uploaded unless you share it.

Switching it off

The report is written by default. Turn it off with the launch flag --aps5-report=off or the environment variable APS5_REPORT=0. The flag wins over the environment variable, and the environment variable wins over the default (on). There is no ini setting. The flag needs a game converted with a new enough relinker; the environment variable also works with older conversions. The runtime reads the flag from the command line and does not remove it, so the game can see --aps5-report=... in its arguments. When the game relaunches itself, the choice is passed on through APS5_REPORT.

Where it goes

anyps5-report.json is written next to the game's executable. If that folder can't be written, it goes to the AnyPS5 data folder under reports/<run id>/ and the runtime prints where. run.id is that same id: 32 lowercase hex characters, random per run.

The report is written once, when the run ends through the guest's exit or _exit, and on Linux and macOS also when the entry returns or the host calls std::exit. On Windows only exit and _exit write a report: a return from the entry writes none, because exit handlers of a DLL run inside the loader lock. The Windows and macOS code and the junction test have not been run. A run that is killed, crashes or aborts writes no report.

What anyps5.modified means

false means the build's git tree had no change to tracked files, no untracked files that git does not ignore, and no change inside a submodule. true means any of those. null means the build did not come from a git checkout. A mixed set of libraries from different builds is not detected.

Unknown is null

A value the runtime cannot know is null, never a guess. This applies to anyps5.*, every game.* and system.* field, the whole system.gpu object run.exitCode, stop.source, and instruction details. Read null as "unknown", not as false or zero.

Every text value in the report goes through the same filter for user names, host names, addresses and account numbers. A version made of four numbers separated by dots (1.2.3.4) looks like an IPv4 address, so it is left out (null) rather than risk writing an address; versions of three numbers (01.002.000) and release tags (v0.1.3) are kept. The commit and the run id are fixed-length hex and are checked by their pattern only, so the build identity is always there.

game.titleId is copied only when it matches PPSA or CUSA style (four capital letters and five digits); otherwise it is null.

Run

Field Meaning
stage The furthest milestone the runtime observed. unknown is the starting value. The runtime never claims converts: conversion happens in the converter.
loads The runtime reached the game's entry point. This does not prove every import resolved, because imports can load later.
starts Game code was entered on its thread.
first-draw A real game draw or dispatch was submitted to the GPU.
presents The GPU accepted a game frame for display. This does not prove the frame was visible or not black.
milestones Seconds since start. Milestones not reached are left out.
exit clean, crash, abort or killed. A non-zero exit code from the game is not automatically a crash.
exitCode The process exit code when known, otherwise null.
averageFps, fpsWindowSeconds Frames per second measured from the first presented frame to the last, and the length of that window. Both are null with fewer than two presented frames.
recovered, incomplete incomplete is true when an observation had to be dropped.

Stop

stop.kind is none and stop.reason is guest-exit for a run that ended through the exit path. The schema lists the other kinds and reasons; this build writes only these.

What system holds

system.osVersion is the operating system name and version (on Linux the distribution's NAME and VERSION_ID from os-release and the first three numbers of the kernel release, for example Ubuntu 24.04 (kernel 5.15.153); whatever the vendor or the user appended to the kernel release is left out), system.cpu is the CPU model name (without a trailing clock speed such as @ 3.50GHz), system.ramGiB is the installed memory in whole GiB, and system.gpu is the graphics device the runtime selected: vendor (from the PCI vendor id: AMD, NVIDIA, Intel, Apple or Other), name, driver (the driver's own name and version text) and vulkan (the API version of the device). gpu is set when the device is selected, so it is null in a report of a run that stopped before that. These are meant to be model and version names, and they are filtered like every other text: a string that contains your login, your home folder's name, an address, an account number, a UUID, a path, an e-mail address or a serial-number-like token is left out of the report (that field becomes null). Your host name is compared too, with one exception: in system.osVersion only, a host name is allowed when it equals the identifier of a public distribution from a fixed list in the source (ubuntu, debian, fedora, arch, cachyos, manjaro, nixos and so on) and the ID of the machine's os-release is that same entry (a machine called cachyos running CachyOS keeps CachyOS). The ID is text its owner can edit, so it counts only when it parses (matching quotes or none) and is on the list; NAME and ID_LIKE never count. A host named after a distribution that is not on the list, or whose os-release says something else, loses the OS name from system.osVersion. An os-release value with an unmatched quote is not used, and the OS version then falls back to what is left (for example Linux (kernel 6.8.0)). In cpu, gpu.name and gpu.driver every name is compared. A login is never given that allowance, so a login that equals a word of the distribution's name (arch on Arch Linux) makes system.osVersion null. Any machine string that contains your login is cleared as a whole, so a login arc also clears Intel Arc. The serial-like rule (twelve or more letters and digits with both) also drops a driver string that carries a long git hash, as development builds of Mesa do.

Which milestones a system records

System loaded, started firstDraw, firstPresent stage, framesPresented, frame rate
Linux never recorded recorded recorded
Windows never recorded recorded recorded
macOS recorded recorded recorded

coverage.milestones is true on every system; it means the stage, the milestones the system records and the frame counts were collected. Read a missing loaded or started on Linux or Windows as "this system does not record it", never as "not reached": a report with stage: first-draw and no loaded is a run that drew. The report says only what was observed: any other milestone that is missing may mean the run did not reach it, and it may mean the system does not record it, so read the table before you read a gap. firstDraw in particular is not set by compute work that is submitted and waited for on its own.

What the runtime counts

  • milestones.firstDraw is set when a batch that carries a recorded guest draw or dispatch has been accepted by the GPU queue. A guest draw counts in either kind of batch the runtime submits; a guest compute dispatch counts only when it is recorded into the runtime's main batch (a dispatch that is submitted and waited for on its own does not set the milestone). firstPresent and framesPresented count guest display buffers that the swapchain queue accepted (a suboptimal one too); internal clears, test pixels and rejected presents are not counted. loaded and started come from the entry bridge of the converted executable; only the macOS bridge reports them so far, so on other systems those two are left out.
  • missing.functions counts calls to a function the runtime does not implement. The library is the folder the stub is compiled in; a call from older code that does not say where it is made has a null library. The name is the exported name without the runtime's suffix; a name that does not fit the schema is not listed and is counted in truncated.functions.
  • shaders.compiled and failed count compilations where they happen (once per variant); reuse of a variant, in memory or from the disk cache, counts as a cache hit.
  • shaders.unsupported lists instructions the recompiler could not translate, by encoding family and numeric opcode (the name is null). It also lists instructions met in shaders that were prepared and never used, so an entry does not by itself mean the run stopped on it; the stop says that.
  • shaders.imageFormats counts guest format lookups, internal ones included, with the use shown as other, plus the formats the runtime rejected. It measures how often a format was looked up, not how many images used it. shaders.descriptors counts the bindings of shaders as they were prepared for a draw; the count measures use, not distinct shaders.
  • truncated counts entries (a function, an instruction, a format, a binding kind) that were dropped, once each however many calls they made. When a hook had to give up an observation because the collector was busy, the matching count is at least 1 and the report is marked incomplete.
  • Nothing in this list is collected, counted or scanned when the report is off.

Lists, coverage and truncation

  • missing.functions[]: library can be null. calls counts calls that reached the missing function. The first one usually ends the run, so 1 is common; this is not a list of everything unimplemented.
  • shaders.compiled and failed count real compilations; cache hits are not compilations (cacheHits is optional). unsupported[] has encoding, opcode (name) and modifiers, each null when the failure can't be pinned to one guest instruction. An unknown numeric opcode keeps its number in opcodeValue with a null opcode.
  • shaders.imageFormats[] and descriptors[] have status (accepted, rejected or unknown), usage and count. count counts checks, not pixels. Accepted does not prove every use works.
    • Image format usage: sampled, storage, color-target, depth-target, transfer, other.
    • Descriptor usage is the access: read, write, atomic, other.
  • coverage says which sections the runtime collected for this run. A list can be empty because nothing happened or because this build doesn't collect it. With a section's coverage off, don't read its empty list as "nothing missing".
  • truncated counts the entries dropped from each capped list, so a capped list never looks complete.

Lists are sorted by count, highest first. They hold at most 200 entries (missing.functions, shaders.unsupported) or 100 (imageFormats, descriptors); strings are at most 300 characters. The whole file is at most 65,536 bytes of UTF-8. When it would be larger, the runtime drops the lowest-ranked list entries first, then optional descriptions, and never the build, game or stop details.

Privacy

Only known, structured values are copied. Free-form text the runtime can't vouch for is left out (null). The file holds no usernames, home paths, hostnames, IP or MAC addresses, serial numbers, account IDs, save data or screenshots. Paths are relative to the AnyPS5 source tree.