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.

Read

The emulator ships with substantial instrumentation. Learning it is the difference between guessing and knowing.

Every flag, and what it is for

FlagDefaultEffect
--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-height1280 / 720Window size.
--user-name / --user-idKyty / 1000Local console user identity.
--present-modeMailboxFifo, Mailbox, or Immediate.
--gpu <index>-1Which 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>unsetOverride the controller light-bar colour; quote the value in shell commands.
--controller-volume <0-100>50DualSense speaker volume.
--controller-vibration <0-100>100DualSense vibration intensity.
--hide-cursoroffHide the cursor after two seconds without pointer activity.
--trophy-notifications <true|false>trueShow trophy unlock notifications and play their sound.
--skip-notice-screen <true|false>falseReport the notice-screen skip flag to supported games; the launcher labels it “Skip boot logos”.
--micunsetCapture from the named microphone. Omit it for paced silent AudioIn.
--vroffEnable the virtual HMD2 headset model; this is not physical VR-headset support.
--amd-cpuoffEnable AMD CPU instruction patches, including reciprocal-square-root emulation.
--tessellationoffEnable native LS / hull / evaluation-stage draws. Tessellation patches are skipped by default.
--fullscreenfalseBorderless desktop fullscreen.
--vblank-frequency <n>60Virtual vertical-blank rate. Changing it changes game speed for engines that pace off vblank.
--console-language <0-29>1Console language; 1 is English (US).
--vulkan-validationfalseVulkan validation layers.
--gpu-assisted-validationfalseVery slow GPU-side shader-access bounds checking; implies Vulkan validation.
--shader-validationfalseRuns every emitted module through spirv-val before the driver sees it.
--shader-optimization-typeNoneNone, Size or Performance — passes to spirv-opt.
--shader-log-directionSilentSilent, Console or File. Dumps decoded RDNA 2, the CFG and the IR per shader.
--shader-log-folder_ShadersWhere those dumps land.
--command-buffer-dumpfalseDecoded PM4 listing of every submitted buffer.
--command-buffer-dump-folder_BuffersWhere those land.
--graphics-debug-dumpfalseAdditional graphics dumps, including textures into _Textures.
--printf-directionSilentSilent, Console or File for the emulator logger; it also gates guest printf and several debug dumps.
--printf-output-file_kyty.txtLogger/guest-printf target when the direction is File.
--profileoffEnable the Tracy profiler; scopes use native tracy::ScopedZone.
--spirv-debug-printffalseEnables printf from inside shaders.
--readback-linear-imagesfalseRead back writable linear images on submit — a coherency workaround.
--playgo-hackfalseUse the supplied PlayGo stub fallback.
--redzonefalseWindows only: protect the guest SysV red zone.
--keymap <Control=Input>emptyDualSense mapping; repeat the flag for multiple mappings.
--rdoffLoads 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

SymptomTurn onLook at
Exits immediatelydefaultThe last log lines — a failed subsystem, a missing eboot.bin, or an EXIT_NOT_IMPLEMENTED in the loader naming its condition.
Crashes before renderingdefaultSearch 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 nothingPRINT_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 trueWhether IT_DRAW_* packets arrive at all. If not, the problem is upstream of graphics.
Geometry wrong or missing--rdThe captured draw in RenderDoc: vertex buffers, pipeline, viewport, scissor.
Wrong colours, garbage pixels--shader-log-direction FileThe 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 trueA coherency bug. Start at MemoryTracker, not the texture cache.
Device lost / validation errors--vulkan-validation true --shader-validation trueValidation output first; malformed SPIR-V second.
Very slow--profileTracy. Distinguish shader compilation (cold cache) from per-draw cost.
Hangs with no CPU usedebugger, all threadsA 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 seeIt 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_ABIGuest 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 in IT_NOP packets.
  • V# / T# / S# — buffer, image and sampler descriptors; "sharp" in code.
  • Gen5 — the AGC graphics API. Prospero — the PS5 itself.
The two questions to ask constantly

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.

Learn more · the tools
  • Docs
    RenderDoc — 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.
  • Code
    Tracy ProfilerWhat --profile streams to. The KYTY_PROFILER_FUNCTION() macros scattered through the PM4 handlers and the recompiler feed its frame view.
  • Code
    spirv-dis and spirv-valspirv-dis turns a dumped module into readable assembly; spirv-val is what --shader-validation true runs internally. Both are worth having on your PATH separately from the build.
  • Code
    KytyPS5 — 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).

FileCoverage
ShaderRecompilerComputeTests.cppExecutable shader semantics, rasterization, caches, transfers and scheduler subsets.
shaderCfgTests.cppControl-flow recovery, structurization and dispatcher fallback.
VirtualMemoryAllocationTests.cppGuest mappings, physical ownership, extended allocations, loader and instruction patches.
ResourceTrackingTests.cppTyped resource analysis, scalar reads, indirect buffers/images and bindings.
ResourceMaterializationTests.cppIn-place snapshot refresh, specialization and uniform-fill classification.
ScalarProvenanceTests.cppTyped value/SRT evaluation; historical filename retained.
MemoryTrackerTests.cppCPU/GPU ownership, dirty regions and tracking ranges.
PageManagerTests.cppPage protection, fault handling and watch lifetimes.
ImagePageTableTests.cppSparse image-page ownership, including extended guest addresses.
EventQueueLifetimeTests.cppEvent callbacks, deletion and queue lifetime.
KernelFileSystemTests.cppMounted paths, directory iteration, Unicode, isolation and AIO batch wait/notification contracts.
SyncOnAddressTests.cppAddress waits/wakes, timed deadlines and interruptions.
AudioOut2PortTests.cppAudio port layouts, submission timing, capabilities and shared-buffer capture.
Ngs2SamplerTests.cppPCM/ATRAC9 sampling, looping and graph playback.
PadHapticsTests.cppVibration-audio and controller-speaker routing.
ImeDialogTests.cppDialog state, focus and lifecycle.
SaveDataMemoryTests.cppPersistent save memory, allocation metadata and capacity.
SaveDataDialogTests.cppSave dialog state and lock behavior.
CesTests.cppCP932/Shift-JIS and UTF-8 conversions.
HttpUriParseTests.cppHTTP URI parsing, including empty query strings.
ArchiveFileTests.cppZArchive paths, reads, seeks and directory access.
AvPlayerFileTests.cppAVPlayer file access, including archive-backed data.
LruCacheTests.cppLeast-recently-used cache behavior.
BitArrayTests.cppBit-range operations.
ShaderVertexMetadataTests.cppVertex metadata and semantic extraction.
TrophySystemTests.cppTrophy metadata, progress, persistence and notification state.
AudioOutTimingTests.cppSubmission cadence across guest sample rates.
ControllerSettingsTests.cppLight-bar colour, speaker volume and vibration settings.
PngEncTests.cppPNG encoder parameters, output and error handling.
macosSse4aTests.cppx86-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.

Watch

Which switch produces which artifact — and why you want all three on at least once.

Try
Feed the output straight in

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.

Do

If you do one thing from this topic, do the first item — the later graphics topics assume those artifacts exist.

Checkpoint