Luxury. We used to _dream_ of printf debugging when we're doing embedded microcontroller systems! (at least the ones I work on).
There the debugging is toggling a port pin and examining with an oscilloscope. I typically toggle it different numbers of times at different places in the code to see where I've got to.
I grew fond of printing a char, after I spent some time snooping a uart. The o-scope in capture mode (I realize this wasn't always an option) grabs the char. Handy because I find char waveforms very easy to recognize at just a glance, and you don't have to measure periodicity or count toggles.
Even if you don't have a uart, you can write a little proc to bit-bang a pin.
When developing for microcontrollers and for example my leds aren't working, I like to define a place in memory where I write values to depending on the place in code. Then just read out that memory address with OpenOCD (jtag of swd).
I love ARM semihosting for this reason. It's obviously insanely slow, but unlike a UART it doesn't need any extra hardware and the target is halted while the slow bit is taking place.
There the debugging is toggling a port pin and examining with an oscilloscope. I typically toggle it different numbers of times at different places in the code to see where I've got to.