pspemu (annex I) : dynarec debugging

Apr 21 2010

Since I started with dynamic recompilation, a couple of persons have asked me "how can I debug that". To debug it I'm using a disassembler, as expected. I recommend you [IDA](http://www.hex-rays.com/idapro/idadowndemo.htm)'s demo, even if it is limited, it is enough to do this. The idea is simple: stop the execution once you reach a dynamically generated block of code, and go step by step checking out if everything works as expected. To stop at that point, yo can do it manually, or do it as I'm doing right now for this case: I'm detecting if there is an attached debugger to the debugger, and if it is, I'm emitting interruption 3 just before starting the code block. Interruption 3 is a software breakpoint. The debugger stops there automatically, so we don't need to know the address beforehand. [IsDebuggerPresent](http://msdn.microsoft.com/en-us/library/ms680345(VS.85).aspx) ```d if (addBreakPointWhenStart && (PC == StartPC)) { if (IsDebuggerPresent()) emiter.INT3(); // Debugger emiter.MOV(Register32.EDX, PC); // Just for debugging. } ``` This allows me to use the same executable (without the need of recompiling) to be able to debug. The bad part is that the interruption happens on all the native blocks. At this point is what I want, but eventually I will want only to place breakpoints with the debugger I want to do. Once the debugger is ready, in order to stop the execution in a specific place, it would be enough to replace the instruction where we want to stop the code. But before we should store the bytes we are replacing, so we can restore them once the execution is resumed. ![](/img/debug_options.png) ![](/img/debug_start_process.png) ![](/img/debug_software_breakpoint.png) ```asm .text ;loop_test: ;j loop_test li t0, 128 loop_color: li a0, 0x04000000 li a1, 0x88000 loop_write: addiu a0, a0, 1 addiu a1, a1, -1 sb t0, 0(a0) bne a1, zr, loop_write nop addi t0, t0, 1 syscall 0x2147 ; sceDisplay.sceDisplayWaitVblankStart j loop_color nop ``` ![](/img/debug_disasm.png)
Desde que empecé con las pruebas de recompilación dinámica, un par de personas me han preguntado cómo me las apaño "para depurar eso". Para depurarlo uso un desensamblador, como era de esperar. Yo recomiendo la demo del [IDA](http://www.hex-rays.com/idapro/idadowndemo.htm), que aunque está limitada para esto puede ser suficiente. La idea es sencilla: para la ejecución cuando se llegue al bloque generador dinámciamente e ir paso a paso viendo si hace lo que se espera. Para llegar a este punto, se puede hacer manualmente. O como hago yo en este caso: detecto si hay un depurador vinculado al ejecutable, y si lo hay emito la interrupción 3 justo antes de empezar el bloque. La interrupción 3 es un software breakpoint. El debugger se para ahí automáticamente, con lo que no es necesario conocer la dirección de antemano. [IsDebuggerPresent](http://msdn.microsoft.com/en-us/library/ms680345(VS.85).aspx) ```d if (addBreakPointWhenStart && (PC == StartPC)) { if (IsDebuggerPresent()) emiter.INT3(); // Debugger emiter.MOV(Register32.EDX, PC); // Just for debugging. } ``` Esto me permite con el mismo ejecutable (sin necesidad de recompilar) poder depurar. La pega es que la interrupción se produce en cada bloque nativo. Por el momento es lo que me interesa, pero más adelante lo que interesará será poner breakpoints con el debugger que haré. Cuando esté el debugger, para parar la ejecución en un sitio concreto con el dynarec, bastará con machacar la instrucción donde queremos que se pare con un código que parar la ejecución. Habiendo guardado previamente los bytes que machacamos y restaurándolos cuando se retome la ejecución. ![](/img/debug_options.png) ![](/img/debug_start_process.png) ![](/img/debug_software_breakpoint.png) ```asm .text ;loop_test: ;j loop_test li t0, 128 loop_color: li a0, 0x04000000 li a1, 0x88000 loop_write: addiu a0, a0, 1 addiu a1, a1, -1 sb t0, 0(a0) bne a1, zr, loop_write nop addi t0, t0, 1 syscall 0x2147 ; sceDisplay.sceDisplayWaitVblankStart j loop_color nop ``` ![](/img/debug_disasm.png)