3 ms·
99.9% of the time (long-term personal C++ project) I use printf-based logging to root-cause unexpected behavior. I use gdb only to root-cause segfaults (which
by cagey 4y ago
99.9% of the time (long-term personal C++ project) I use printf-based logging to root-cause unexpected behavior. I use gdb only to root-cause segfaults (which are thankfully quite rare).
I use GCC exclusively, and its type-checking of param types vs format string largely prevents me from spending time in gdb; pre-GCC adoption, the biggest source of segfaults in my world was printf type mismatches.
Addendum:
I'm sure it violates every coding convention ever created, but, in almost all cases, once I add a debug printf to my source code, for example,
ED && DBG( "%s", foostr ); // ED is usually a function-scoped enum normally ==0
I leave it there forever. This allows all DBG's to be eternally subject to GCC printf param type build time checking going forward (preventing the debug print code from rotting (as it absolutely would if commented out), at minor maintenance cost (typically only when I switch to a new GCC release)) while not imposing any runtime cost (unless I flip ED to 1 and rebuild to enable function-specific detailed logging).
As far as this practice injecting large amounts of comprehension-thwarting noise into my source code: all such `ED && DBG(...);` code sequences are positioned in their own column far to the right of the non-DBG code (needless to say, I don't subject myself to 80-column coding-convention limits; more like 160-column). That this results in multiple statements per line (another typical taboo) is a don't care, since I'm never using a debugger to single-step thru code, and in any case, by convention my DBG() calls never modify program state.
It works great for me, but probably not for anyone else.
- Gibbon1 4y agoThing I do in my embedded code is trap seg faults and then use some magic to pull the offending instruction address off the stack and store it in no init memory, then reboot. On startup I check and spit out and log the address. addr2line 99% of the time can tell you what line of code was a bad boy. Like you I also leave debug printf's behind. I find that they also function as comments about what's important and likely break. Unlike everyone else I've seen I have a command line interface I can use to poke at the program while it's running.
- cagey 4y agoI too did embedded code (firmware) work, in the 199x's with only an ICE (basically, hardware debugger). I only dreamed of having printf-capable logging facilities on the HW target; almost all of the firmware "game" comes down to finagling the egress of debug (and application characterization) information off the (in my case, eternally extremely cost-/resource-constrained) target. These days things are much better in this regard (32-bit CPU's and associated memory and other-infra (data-path) resources are MUCH more capable than those of the 8- and 16-bitters of a few decades ago...). Briefly returning to the thread topic: this experience doing FW dev and usually only having a debugger (and how terribly inefficient that was) strongly guided me to a logging-based debug approach: the ability to collect a log and filter it using ad-hoc (e.g. Perl-based) tools, provides IMO an order of magnitude more powerful way to truly understand program (and HW target) behavior compared to a debugger. I didn't mention earlier that a key component of my "leave DBG printfs in the source code and let the compiler both (a) type-check the trailing param types vs the format string, and (b) elide codegen (executable file footprint) for those same DBG printfs due to the compiler optimizing those calls out" approach is that I build with -O3 which (at least historically; I haven't checked recently) provides a poor gdb debugging experience because the optimizer frequently de-correlates source code lines from generated machine code; if I encounter a segfault, I must suss out a repeatable test case, clean rebuild with -O0 (no optimization), keep fingers crossed that the test case still fails (it usually does), and repro under gdb. The hassle associated with this gyration inhibits my debugger use in non-segfault cases (though these days a clean rebuild is a lot faster than it used to be).
- jeffrallen 4y agoDid you learn programming in Fortran? No shade. Just wondering.
- cagey 4y agoNope. Asm, then later Pascal quickly followed by C. Later I had to take a "Numerical Methods" course that required FORTRAN, but my only gleaning from that torture was "never again".