From ea9687abb2dee446f628457f47a56873b7afe948 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Fri, 17 Jan 2025 01:50:05 +0100 Subject: [PATCH] lightrec: Add the big-ass debugger This debugger has this name because it doesn't even try to be suttle - it will run the dynarec and interpreter versions of Lightrec in parallel, comparing their behaviour at every exit point, and returning any issue as soon as they appear. By default, the emulator will print a checksum of the registers after each exit point. When a mismatch is found, it is advised to re-start the debugging setting the LIGHTREC_VERY_DEBUG=1 environment variable, and to set the LIGHTREC_BEGIN_CYCLES environment variable to the cycle value of the last known state. When the "very debug" mode is used, the interpreter and dynarec will exit after each single block, and the emulator will compute a checksum of the whole RAM and scratchpad and print all registers. This two-level debugging allows to find a mismatch point very fast, and then fine-tune until the exact breaking point is found. Signed-off-by: Paul Cercueil --- libpcsxcore/lightrec/big_ass_debugger.py | 112 +++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100755 libpcsxcore/lightrec/big_ass_debugger.py diff --git a/libpcsxcore/lightrec/big_ass_debugger.py b/libpcsxcore/lightrec/big_ass_debugger.py new file mode 100755 index 00000000..ed07e7d3 --- /dev/null +++ b/libpcsxcore/lightrec/big_ass_debugger.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +from time import sleep +from sys import argv +from os import environ +import subprocess + +def get_next_line(p): + line = "" + + while line[0:5] != "CYCLE": + line = p.readline().decode() + + if (len(line) == 0): + sleep(0.001) + elif line[0:5] != "CYCLE": + print(line[:-1]) + + return line + +def print_differences(inter, dynarec): + inter_array = inter.split(" ") + inter_dict = dict(zip(inter_array[::2], inter_array[1::2])) + dynarec_array = dynarec.split(" ") + dynarec_dict = dict(zip(dynarec_array[::2], dynarec_array[1::2])) + + diff = dict([(k, (inter_dict[k], dynarec_dict[k])) for k in inter_dict.keys() if inter_dict[k] != dynarec_dict[k]]) + + print("\nDifferences:") + print("{:15}{:15}{:15}".format("", "Interpreter", "Dynarec")) + for k in diff: + print("{:15}{:15}{:15}".format(k, diff[k][0], diff[k][1])) + +def print_mismatch(inter, dynarec, oldline): + print("\nMismatch!") + print(inter + " - Interpreter") + print(dynarec + " - Dynarec") + print("State before the mismatch:") + print(oldline) + print_differences(inter, dynarec) + +def read_loop(p1, p2): + oldline = "" + + while True: + line1 = get_next_line(p1) + line2 = get_next_line(p2) + + if line1 != line2: + # TODO: Proper matching + + # Lightrec might be lagging behind + #if line1[0:16] != line2[0:16]: + if line1[6:16] != line2[6:16]: + cycle1 = int(line1[6:16], 16) + cycle2 = int(line2[6:16], 16) + + if cycle1 < cycle2: + print(line2[:-1] + " - Dynarec") + + while cycle1 < cycle2: + print(line1[:-1] + " - Interpreter lagging behind") + print_differences(line1[:-1], line2[:-1]) + line1 = get_next_line(p1) + cycle1 = int(line1[6:16], 16) + + while cycle1 > cycle2: + print(line2[:-1] + " - Dynarec lagging behind") + print_differences(line1[:-1], line2[:-1]) + line2 = get_next_line(p2) + cycle2 = int(line2[6:16], 16) + + if line1 != line2: + print_mismatch(line1[:-1], line2[:-1], oldline) + break + + if cycle2 < cycle1: + print(line1[:-1] + " - Interpreter") + + while cycle1 > cycle2: + print(line2[:-1] + " - Dynarec lagging behind") + print_differences(line1[:-1], line2[:-1]) + line2 = get_next_line(p2) + cycle2 = int(line2[6:16], 16) + + while cycle1 < cycle2: + print(line1[:-1] + " - Interpreter lagging behind") + print_differences(line1[:-1], line2[:-1]) + line1 = get_next_line(p1) + cycle1 = int(line1[6:16], 16) + + if line1 != line2: + print_mismatch(line1[:-1], line2[:-1], oldline) + break + + if line1 == line2: + oldline = line1[:-1] + print(oldline[:16] + " - Match") + continue + + print_mismatch(line1[:-1], line2[:-1], oldline) + break + else: + oldline = line1[:-1] + +def main(): + with subprocess.Popen(['./pcsx'] + argv[1:], env={ **environ, 'LIGHTREC_DEBUG': '1', 'LIGHTREC_INTERPRETER': '1' }, stdout=subprocess.PIPE, bufsize=1) as fifo_int: + with subprocess.Popen(['./pcsx'] + argv[1:], env={ **environ, 'LIGHTREC_DEBUG': '1' }, stdout=subprocess.PIPE, bufsize=1) as fifo_jit: + read_loop(fifo_int.stdout, fifo_jit.stdout) + +if __name__ == '__main__': + main() -- 2.39.5