Step 35 · First contribution
Debugging & diagnostics
Everything in this course is easier when you can see inside a running emulator. These are the switches that produce the artifacts — and the test suite that tells you whether a change broke something.
Before this page: The developer route & coverage. View the learning route.
The emulator ships with substantial instrumentation. Learning it is the difference between guessing and knowing.
Every flag, and what it is for
| Flag | Default | Effect |
|---|---|---|
--game <dir|elf|zar> | — | A directory, ELF, or .zar archive; directories and archives load /app0/eboot.bin. |
--game-patch <json> | — | An ETAHen cheat file. |
--screen-width / --screen-height | 1280 / 720 | Window size. |
--user-name / --user-id | Kyty / 1000 | Local console user identity. |
--present-mode | Mailbox | Fifo, Mailbox, or Immediate. |
--gpu <index> | -1 | Which Vulkan physical device to use, by enumeration index. -1 picks automatically; a malformed index is a usage error, an out-of-range one falls back to automatic with a log line. The launcher exposes the same choice. |
--controller-color <#RRGGBB> | unset | Override the controller light-bar colour; quote the value in shell commands. |
--controller-volume <0-100> | 50 | DualSense speaker volume. |
--controller-vibration <0-100> | 100 | DualSense vibration intensity. |
--hide-cursor | off | Hide the cursor after two seconds without pointer activity. |
--trophy-notifications <true|false> | true | Show trophy unlock notifications and play their sound. |
--skip-notice-screen <true|false> | false | Report the notice-screen skip flag to supported games; the launcher labels it “Skip boot logos”. |
--mic | unset | Capture from the named microphone. Omit it for paced silent AudioIn. |
--vr | off | Enable the virtual HMD2 headset model; this is not physical VR-headset support. |
--amd-cpu | off | Enable AMD CPU instruction patches, including reciprocal-square-root emulation. |
--tessellation | off | Enable native LS / hull / evaluation-stage draws. Tessellation patches are skipped by default. |
--fullscreen | false | Borderless desktop fullscreen. |
--vblank-frequency <n> | 60 | Virtual vertical-blank rate. Changing it changes game speed for engines that pace off vblank. |
--console-language <0-29> | 1 | Console language; 1 is English (US). |
--vulkan-validation | false | Vulkan validation layers. |
--gpu-assisted-validation | false | Very slow GPU-side shader-access bounds checking; implies Vulkan validation. |
--shader-validation | false | Runs every emitted module through spirv-val before the driver sees it. |
--shader-optimization-type | None | None, Size or Performance — passes to spirv-opt. |
--shader-log-direction | Silent | Silent, Console or File. Dumps decoded RDNA 2, the CFG and the IR per shader. |
--shader-log-folder | _Shaders | Where those dumps land. |
--command-buffer-dump | false | Decoded PM4 listing of every submitted buffer. |
--command-buffer-dump-folder | _Buffers | Where those land. |
--graphics-debug-dump | false | Additional graphics dumps, including textures into _Textures. |
--printf-direction | Silent | Silent, Console or File for the emulator logger; it also gates guest printf and several debug dumps. |
--printf-output-file | _kyty.txt | Logger/guest-printf target when the direction is File. |
--profile | off | Enable the Tracy profiler; scopes use native tracy::ScopedZone. |
--spirv-debug-printf | false | Enables printf from inside shaders. |
--readback-linear-images | false | Read back writable linear images on submit — a coherency workaround. |
--playgo-hack | false | Use the supplied PlayGo stub fallback. |
--redzone | false | Windows only: protect the guest SysV red zone. |
--keymap <Control=Input> | empty | DualSense mapping; repeat the flag for multiple mappings. |
--rd | off | Loads the RenderDoc API for frame capture. |
The configuration you will actually use
# general debugging kyty_emulator.exe --game "D:\Games\Example" ^ --printf-direction File --printf-output-file _kyty.txt ^ --vulkan-validation true --shader-validation true # graphics investigation kyty_emulator.exe --game "D:\Games\Example" ^ --command-buffer-dump true --shader-log-direction File --graphics-debug-dump true
Symptom to first move
| Symptom | Turn on | Look at |
|---|---|---|
| Exits immediately | default | The last log lines — a failed subsystem, a missing eboot.bin, or an EXIT_NOT_IMPLEMENTED in the loader naming its condition. |
| Crashes before rendering | default | Search for Guest fault context — the block names the guest thread, dumps the registers, 96 code bytes and 32 stack words, then ends with Unhandled host exception: … address=…. An address of 0x28 can come from an unpatched segment-relative access or a null pointer plus an offset. Decode the faulting instruction and inspect its operands before assigning a cause. |
| Boots, a feature does nothing | PRINT_NAME_ENABLE(true) | The per-call trace for the suspect module, plus stubbed-NID lines in the log. |
| Black screen, no errors | --command-buffer-dump true | Whether IT_DRAW_* packets arrive at all. If not, the problem is upstream of graphics. |
| Geometry wrong or missing | --rd | The captured draw in RenderDoc: vertex buffers, pipeline, viewport, scissor. |
| Wrong colours, garbage pixels | --shader-log-direction File | The pixel shader's IR. Also check for dispatcher fallback lines, which name the phase, failure kind and block. |
| Wrong GPU picked on a multi-GPU machine | --gpu <index> | The startup log names the selected device; pass the index of the one you want. |
| Textures flicker or go stale | --readback-linear-images true | A coherency bug. Start at MemoryTracker, not the texture cache. |
| Device lost / validation errors | --vulkan-validation true --shader-validation true | Validation output first; malformed SPIR-V second. |
| Very slow | --profile | Tracy. Distinguish shader compilation (cold cache) from per-draw cost. |
| Hangs with no CPU use | debugger, all threads | A WAIT_REG_MEM whose condition never becomes true, or a thread waiting on an event queue nothing signals. |
Reading the source: five conventions
| You will see | It means |
|---|---|
EXIT_NOT_IMPLEMENTED(cond) | "If this is true, I did not handle this case." A marker of the frontier, not an impossibility claim. Grepping these maps what the emulator cannot yet do. |
EXIT("…") | An unrecoverable invariant violation — a bug, not a gap. The shader recompiler now reports its own failures this way too, instead of passing error strings back up. |
KYTY_SYSV_ABI | Guest code calls this directly. Never call it from emulator code without thinking about the convention. |
PRINT_NAME() | Free per-call tracing, already wired up, off by default per thread. |
Common::Singleton<T>::Instance() | How cross-cutting services are reached. There is no dependency injection in this codebase. |
Naming that maps to hardware
HwCtx*/HwSh*/HwUc*— handlers for context, shader and user-config register writes.CpOp*— a command-processor operation: the handler for one PM4 opcode.IT_*— PM4 opcodes.R_*— Sony sub-selectors carried inIT_NOPpackets.V#/T#/S#— buffer, image and sampler descriptors; "sharp" in code.Gen5— the AGC graphics API.Prospero— the PS5 itself.
Reading any pointer: guest address or host address? Reading any function: which thread runs this — guest, GPU, or main/window? Most genuinely confusing emulator bugs are a confusion about one of those, and the code does not always spell out the answer.
- DocsRenderDoc — Quick StartCapture a frame with
--rd, then use the Event Browser to step through it. For a wrong-geometry bug this shows you the vertex buffer contents, the bound pipeline and the viewport in one place. Full documentation. - CodeTracy ProfilerWhat
--profilestreams to. TheKYTY_PROFILER_FUNCTION()macros scattered through the PM4 handlers and the recompiler feed its frame view. - Codespirv-dis and spirv-val
spirv-disturns a dumped module into readable assembly;spirv-valis what--shader-validation trueruns internally. Both are worth having on your PATH separately from the build. - CodeKytyPS5 — issues and pull requestsRead recent issues before reporting one. The Game Emulation Bug Report template asks for the complete log file, which after this chapter you know how to produce.
The aggregate suite covers shader compilation, resource binding, memory, archive I/O, audio, save data and dialogs. CMake defines 29 portable test executables plus an x86-64 macOS SSE4a executable.
The targets
cmake --build _Build/windows --target memory_tracker_tests .\_Build\windows\memory_tracker_tests.exe # Build the aggregate, then run the CTest registrations. cmake --build _Build/windows --target kyty_tests ctest --test-dir _Build/windows --output-on-failure # 29 portable targets; macOS also adds macos_sse4a_tests: ime_dialog_tests shader_cfg_tests scalar_provenance_tests image_page_table_tests memory_tracker_tests page_manager_tests archive_file_tests avplayer_file_tests bit_array_tests lru_cache_tests shader_vertex_metadata_tests resource_materialization_tests resource_tracking_tests event_queue_lifetime_tests kernel_file_system_tests sync_on_address_tests shader_recompiler_compute_tests virtual_memory_allocation_tests audio_out2_port_tests ngs2_sampler_tests pad_haptics_tests save_data_memory_tests ces_tests save_data_dialog_tests http_uri_parse_tests
The heavy executables carry several suites behind --*-only flags; ctest registers each as its own test (for example compute_meta_clear_classification is shader_recompiler_compute_tests --compute-meta-clear-only).
| File | Coverage |
|---|---|
ShaderRecompilerComputeTests.cpp | Executable shader semantics, rasterization, caches, transfers and scheduler subsets. |
shaderCfgTests.cpp | Control-flow recovery, structurization and dispatcher fallback. |
VirtualMemoryAllocationTests.cpp | Guest mappings, physical ownership, extended allocations, loader and instruction patches. |
ResourceTrackingTests.cpp | Typed resource analysis, scalar reads, indirect buffers/images and bindings. |
ResourceMaterializationTests.cpp | In-place snapshot refresh, specialization and uniform-fill classification. |
ScalarProvenanceTests.cpp | Typed value/SRT evaluation; historical filename retained. |
MemoryTrackerTests.cpp | CPU/GPU ownership, dirty regions and tracking ranges. |
PageManagerTests.cpp | Page protection, fault handling and watch lifetimes. |
ImagePageTableTests.cpp | Sparse image-page ownership, including extended guest addresses. |
EventQueueLifetimeTests.cpp | Event callbacks, deletion and queue lifetime. |
KernelFileSystemTests.cpp | Mounted paths, directory iteration, Unicode, isolation and AIO batch wait/notification contracts. |
SyncOnAddressTests.cpp | Address waits/wakes, timed deadlines and interruptions. |
AudioOut2PortTests.cpp | Audio port layouts, submission timing, capabilities and shared-buffer capture. |
Ngs2SamplerTests.cpp | PCM/ATRAC9 sampling, looping and graph playback. |
PadHapticsTests.cpp | Vibration-audio and controller-speaker routing. |
ImeDialogTests.cpp | Dialog state, focus and lifecycle. |
SaveDataMemoryTests.cpp | Persistent save memory, allocation metadata and capacity. |
SaveDataDialogTests.cpp | Save dialog state and lock behavior. |
CesTests.cpp | CP932/Shift-JIS and UTF-8 conversions. |
HttpUriParseTests.cpp | HTTP URI parsing, including empty query strings. |
ArchiveFileTests.cpp | ZArchive paths, reads, seeks and directory access. |
AvPlayerFileTests.cpp | AVPlayer file access, including archive-backed data. |
LruCacheTests.cpp | Least-recently-used cache behavior. |
BitArrayTests.cpp | Bit-range operations. |
ShaderVertexMetadataTests.cpp | Vertex metadata and semantic extraction. |
TrophySystemTests.cpp | Trophy metadata, progress, persistence and notification state. |
AudioOutTimingTests.cpp | Submission cadence across guest sample rates. |
ControllerSettingsTests.cpp | Light-bar colour, speaker volume and vibration settings. |
PngEncTests.cpp | PNG encoder parameters, output and error handling. |
macosSse4aTests.cpp | x86-64 macOS only: SSE4a signal-context restoration. |
CI now runs the recompiler suite as well as focused regressions. GPU-dependent cases can report a skip when required rasterization or subgroup capabilities are absent; inspect the test output to distinguish a skipped feature from a passing execution. CTest registrations include focused subsets of the heavy shader executable, so the number of CTest cases is larger than the number of executables. ShaderRayTracingTests.inc covers decoding and compilation; ShaderRayTracingGpuTests.inc exercises software triangle/box intersections and traversal.
Failure injection
The allocator compiles in deliberate failure hooks so its error paths are exercised rather than hoped about:
#if defined(KYTY_VIRTUAL_MEMORY_ALLOCATION_TESTS) void TestFailNextPhysicalMemoryUnmap(); void TestFailPhysicalMemoryUnmapAfter(uint32_t successful_unmaps); void TestFailGuestBackingStoreUnmapAfter(uint32_t successful_unmaps); void TestFailNextFixedReserveRangeRegistration(); bool TestPlaceholderRangeIsFree(uint64_t vaddr, uint64_t size); bool TestGuestAddressRangeIsOwned(uint64_t vaddr, uint64_t size); uint64_t TestGuestBackingSize(); #endif
The same pattern appears in the loader (TestMainEntryUsesGuestStack, TestModuleRelocationUsesWritableHostMapping) and in pthreads (TestGuestStackOwnerLifecycle).
Why the ratio is right
Notice what is not heavily tested: the HLE libraries. That is a correct allocation of effort. When an HLE function is wrong, the game misbehaves visibly and the log points at it. When the shader compiler or the memory tracker is wrong, nothing crashes and nothing logs — you get subtly wrong pixels, or corruption several frames after the cause. Tests are the only instrument that works there.
Formatting and static analysis
python -m pip install pre-commit python -m pre_commit install --install-hooks # clang-format on staged .cpp/.h/.inc in src cmake … -DKYTY_ENABLE_CLANG_TIDY=ON # clang-tidy during builds
Profiling must preserve fault handlers
--profile enables Tracy. The build sets TRACY_NO_CRASH_HANDLER=ON so connecting a profiler does not replace the emulator's exception/signal handlers: instruction emulation and CPU/GPU coherency depend on those handlers. See 3rdparty/CMakeLists.txt; the setting is independent of whether a recording client is attached.
Which switch produces which artifact — and why you want all three on at least once.
The Playground exists to read exactly what these flags produce — the log triage tool for _kyty.txt, the PM4 decoder for command-buffer dumps, the module inspector for the binaries themselves.
If you do one thing from this topic, do the first item — the later graphics topics assume those artifacts exist.
Checkpoint