Same as Jeroen3, I found out that stepping through code in debugger is helpful so rarely that I stopped doing it entirely.
I mostly need that kind of "debugging" when accessing with outside world (or with complex time-dependent accesses with peripherals, like DMA), which can't be halted. A typical approach I use is something like this:
original_code()
uint32_t tmp1 = INTERESTING_REGISTER;
more_original_code()
uint32_t tmp2 = INTERESTING_REGISTER;
more_original_code()
....
when you have time:
printf("%x, %x\n", tmp1, tmp2);
Or, for example, just a few days ago I debugged strange issues with DMA-driven I2C and DCMI (camera interface) interfacing with a CMOS sensor, and did it like this:
uint32_t snapshots[1000];
start_i2c_transfer_here();
for(int i=0; i<1000; i++)
{
snapshots[i] = interesting register, pin state, whatever;
blink_io_pin_to_sync_the_datatrace_with_oscilloscope
}
print_snapshots_here();
This gives minimal interference and allows you to fine tune what you gather and when. Of course slower to write than just looking at the debugger screen, but with this kind of creative approach, you can basically solve all the issues that come to you, whereas the debugger always has serious limits in regards to application timing.