How do you "print" these values out without a debug interface? Means you need a UART etc which may not be available, or some crappy bit bashed interface?
Thanks for asking,
Depends on the project. Except for the simplest LED blinkers, usually most projects have either some communication link for the application, or at least some diagnostic port, typically UART, SPI, CAN...
It's instrumentation, the point is, except for trivially simple projects, you need instrumentation (or call it telemetry) of the application
in any case, you can't replace it with the debug interface. You log what is happening, for robust development you think about this beforehand.
This is how you debug a vehicle or ECU, you connect the manufacturer's tool to the diagnostics port, you don't connect a debugger in the CPUs debug pins and single step the code.
For an almost IO-less project, they tend to be so simple you don't need to spend hours debugging a problem anyway.
Naturally there are instances where this instrumentation/diagnostics link itself needs debugging, or the CPU hardfaults instantly before reaching the initialization of CAN (for example), for this a debugger may be needed but in my experience I can nowadays usually guess the problem and in any case this happens only during the first few hours of the project. I have used a debugger once during last 5 years I think.
Working with mobile robots (physically somewhere I may not be able to go to access them) really emphasized the point that I just can't connect a probe and hope to catch a problem on the lab table, I need to have instrumentation in the application to store state and report it to catch rarely occurring problems "on the field". I simply don't understand who needs going through the code line-by-line, that's obvious from looking at the code. Instead you need to understand the graph of higher level events, what lead to what, and this is something quite easily done in code, and when it's in code, it stays in your program and you can look at the log
after the issue arose on the field instead of connecting the probe trying to reproduce. To give a practical example, this can be as simple as doing this in each non-trivial, interesting function:
call_graph[idx++] = 42; // function ID. wrap this as an atomic block.
Then do error checks / sanity checks everywhere and in the error handler, dump everything you have collected into all available interfaces so that if one is down, others can be used.
Another thing I like doing, is adding
trace[idx++] = value;
in feedback loop / etc. handlers which handle ADC data for example. Then I have a simple gnuplot script which takes the dump through whatever application bus and plots it like a scope trace; I have attached an example image of such automated output to give you an idea. Yes, you can do the same with some more advanced debug tools saving a few minutes of work, and utilizing the ARM trace macrocell it will have more bandwidth than through UART for example. So this becomes like a penis length competition where you win by buying a 1000HP Ferrari while I get the job done with Toyota Corolla.
I understand the argument of just connecting the probe, examining memory and single-stepping through code being easy, fast and appealing, especially to beginners, but I just think I don't need it, and I'm probably
not alone. But anyone is free to go on calling this wrong or crappy, it tells more about them than the methodology itself.
So I'm not against debuggers but I just think people who talk about them like they are silver bullets that answer to all debugging (using the word in the general sense of finding and correcting bugs) needs simply don't know what they are talking about, and many such people being so arrogant*, I like to reply a tad arrogantly as well, but I hope I have provided some actual points you can think about.
*) Making it
appear that they have some 5-10 years of experience, are past the beginner phase, remember how much they sucked doing printf on arduino, and are so glad they found debuggers.