Instruction tracing

NOTE: disable logging proxies and x86 binaries first

Instruction tracing allows execution of the same code on both the emulator and PS3 and comparing the values of registers at every instruction. Both PPU and SPU can be traced. There are two steps: capturing the trace and then finding differences.

Dbg-gui provides a traceto command that accepts a single argument: the address at which to stop the capture.

traceto : nip + 4

Alternatively specify zero as the adress to load a trace script from /tmp/ps3trace-ppu-script or /tmp/ps3trace-spu-script.

traceto : 0

The script has the following format

t 7FB
s 150
s 160
t 888

t stands for trace and s – for skip. The script is interpreted line by line. A skip line is used to run the program without tracing up to the specified address. This allows to skip blocks of code likely to take too much time or prevent the tracing altogether as tracing might cause the conditional store instructions to loop indefinitely. NOTE: the addresses must be in uppercase.

A script can be rebased with the –rebase_script option

./parse-trace.py --rebase_script /tmp/ps3trace-ppu-script --source_base 00051800 --target_base 0cdb00

Depending on thread type, the capture will be saved to /tmp/ps3trace or /tmp/ps3trace-spu.

A trace file contains every executed instruction address and the values of all registers at the time the instruction was executed.

To obtain a trace file on PS3 (of the same format) the tracer.c script is executed by the ProDG Debugger. After opening the script open the context menu of the Memory View and select the appopriate trace: Trace PPU or Trace SPU. Only trace scripts are implemented. They must be placed at the Desktop with names ps3_spu_trace_script.txt and ps3_ppu_trace_script.txt. The trace file will be saved to Desktop.

Now that there are two trace files of the same code, they can be compared. Comparing them directly is difficult due to most registers having their values unchagned most of the time.

A trace file can be preprocessed with the parse-trace.py that transforms a trace file into a file containing only the changed registers between adjacent instructions.

parse-trace.py --changes /tmp/ps3trace
parse-trace.py --spu --changes /tmp/ps3trace-spu

When the code is located at different base addresses on the emulator and PS3, use the --rebase option to specify the base. This option only makes sense when tracing PRX modules on PPU.

parse-trace.py --rebase 3a000 --changes /tmp/ps3trace

The result of execution of this script for both the emulator and PS3 trace files can be directly compared. Any discrepancy then becomes obvious

0x7e2c [('r3', '0x0')]
0x7e30 [('r81', '0x7b680003f4100000000000000000')]
0x7e34 []

0x7e2c [('r3', '0x0')]
0x7e30 [('r81', '0x7b6800007b680000000000000000')]
0x7e34 []

Here the instruction at address 0x7e2c produced different results in r81. The instruction at address 0x7e30 didn’t modify any registers.

Rewriter

Tracing requires recompilation of the rewritten module with TRACE defined. Example:

  1. Open the liblv2.sprx.elf.ninja and replace “trace =” with “trace = -DTRACE”
  2. Rebuild the elf by calling ninja without arguments