6 ms·
Print debugging always works, but also: it lets the programmer customize their view of the program’s (very, very large) hidden state in any way imaginable. Step
by zamfi 6y ago
Print debugging always works, but also: it lets the programmer customize their view of the program’s (very, very large) hidden state in any way imaginable. Step debuggers are the “no-code” equivalent: extremely useful for the purposes for which they were designed—and often the better choice there—but inherently limited.
Geoff’s not wrong in invoking Bret Victor’s Learnable Programming argument that being able to track state over time is critical to debugging, and Geoff’s right that print debugging makes this easier than almost any existing step-debugger.
Bret’s deeper point, though, is that a major challenge in debugging is hidden state in general, and that variable state changing over time is just one example.
Not only is there a ton of hidden state in the execution of a program—the full execution trace PLUS state of every variable at each point in that trace—but there is also a ton of interpretation of that state that the programmer needs to do while debugging: “what does this sequence of events imply?” - “why is this pointer pointing here?” - etc.
Doing that interpretation is much easier when the programmer gets to selectively view that (again, HUGE amount of) hidden state. Print debugging gives the programmer complete control over what state is shown. No other debugger does that: they all show a ton of data and context (often useful!) and make certain operations easy (inspecting single variables! viewing call stack snapshot!), and these are often just the right things.
But sometimes they’re not. And often, when you start debugging, you don’t know if the fancy debuggers will be too much or not enough.
Print debugging gives you the power to write code to selectively view the (again, HUGE!) hidden state of your program, and this scales from the smallest code-tracing bug to the largest distributed systems.
Step debuggers, on the other hand, are essentially “no-code” debuggers — extremely useful for the purpose for which they are designed, still useful for adjacent purposes, and a great place to start if you know the tool well, but ultimately not as powerful if your needs exceed their capacities.
A good programmer will know how to use all these tools for what they’re best at!
- vvanders 6y agoIf you've chased heap corruption printf doesn't really help you much but a data breakpoint is a godsend. Same thing with watch windows, memory views and the like. There are classes of problems that do well with printf but calling them "no-code" is vastly underselling them.
- zamfi 6y agoI’m not sure when no-code became a pejorative, but that wasn’t my intent! Only that most of these tools, unlike print, are special-purpose, exceptional for that purpose, and often even useful in other circumstances. A data breakpoint is a great example of something useful that print doesn’t do well.
- vvanders 6y agoYou seemed to be drawing a parallel between no-code -> "not as powerful", in my experience they're different tools for different use cases. I also don't think they're nearly as no-code as you call out. VS' watch window has very few limitations compared to printf back when I was working on win32 things. Also important to consider iteration time. I once worked on a system where adding a printf was a 20 minute process due to the need to heavily optimize for a memory constrained platform(scripting fit in 400kb block with the asset bake step).
- zamfi 6y ago> in my experience they're different tools for different use cases Exactly! Debuggers are very useful tools, and typically not as general-purpose as print. I don’t view “not as powerful” as a meaningful distinction, because it requires that you ask “powerful at what?” --- VS’ watch window is great but (I assume) doesn’t work across distributed systems, etc. — as a general technique, print is universal in the sense that there are very few problems that can’t be diagnosed by modifying your code and printing some (possibly a manipulation!) of the hidden state. This is going to be harder than using a special-purpose tool designed for exactly your problem. In the same way, “no-code” tools are typically better and/or easier than writing code to solve the same problem, but special-purpose.
- vvanders 6y ago> typically not as general-purpose as print In my domain which doesn't usually cover distributed systems printf can be worse because it introduces synchronization primitives that have caused race conditions to disappear(and that race condition causes second order heap corruption or the like). On one platform system memory was so small(8mb total) that each output to stdout went over the serial link slowing performance down to 1/20th of a realtime process under any real logging. Like I said, different tools for different uses, and really depends on the context. If there was one size fits all then we'd just use that but the diversity of debugging tools I think shows that you need a variety of techniques to approach the problems we encounter.
- jayd16 6y agoThis is a lot of words but I wonder if you've ever worked with a debugger with watchable variables or immediate mode code execution. I find it odd you say print debugging is more flexible.
- gerdesj 6y agoUsing a debugger is largely passive - it shows you what is actually happening. Debugging via print allows you to step outside and peer in ie it is active. Print debugging can be prone to bugs within itself which may cause additional ignorance about the potential bugs being diagnosed. How meta can you get? 8) There's also the effect of the effort of actually looking - that may or may not have an effect. Anyway, the discussion here is largely ignorant of language and function. At the moment I spend time fiddling up Python and OpenSCAD scripts if I dig out a programming language. For me, print is really handy. For a Linux low level latency sensitive driver in highly optimised ASM n C I suspect this matter is moot.
- Groxx 6y ago(most) debuggers can print - I often use conditional breakpoints with the "condition" "print(thing)". It works great, doesn't require re-compiling, can be enabled/disabled with a single click, etc. It's handy when you want to see a lengthy sequence all at once.
- crancher 6y agoDiscovering this capability changed how I debug. Conditional breakpoints that log-only creating an always useful, easily enabled/disabled log of critical method results without littering the code itself with logging statements.
- icedchai 6y agoWith interactive debuggers like python's pdb you can actively manipulate data, print it out, basically do whatever you need to do in a REPL. It is way more effective than adding print statements, rerunning your script, etc. That being said I typically use a combination of both: I use a print to get me to where I think the problem begins, then an interactive debugger (pdb.set_trace / breakpoint / etc) to drill down into the details.
- tomcam 6y ago> it lets the programmer customize their view of the program’s (very, very large) hidden state in any way imaginable. I’ve been trying to articulate this to myself for a long time. Thank you.
- greggman3 6y agoThat suggests that debuggers should let you write visualizers and they should be so easy to write that you don't hesitate to write them. That seems to be the idea behind Glamorous Toolkit https://gtoolkit.com/ https://gtoolkit.com/ I'd be curious what it would take to add any of those concepts to existing debuggers for other languages.
- nyczomg 6y ago"Print debugging always works..." Nope, print debugging does not always work. Please stop saying that. Sure, it's often useful and often works. But go try to debug a race condition, or bug in some locking mechanism with print statements. I'll wait as you add a bunch of print statements, and then learn the harsh lesson that you are now debugging a completely different program with very different performance characteristics that now doesn't deadlock where it used to deadlock because you slowed down one of the threads massively as it spends time it used to not spend logging stuff. "Step debuggers, on the other hand, are essentially “no-code” debuggers — extremely useful for the purpose for which they are designed, still useful for adjacent purposes, and a great place to start if you know the tool well, but ultimately not as powerful if your needs exceed their capacities." I guess you'd think this if you thought print debugging "always works". But I promise you it is equally (if not easier) to exceed the capabilities of print statements in some scenarios.
- bluGill 6y agoDebuggers don't work in the situation you described either.
- ruslan 6y agoYou don't put printf() in critical sections, instead you collect some statistics. Once critical section is over you can freely print collected data for further analyzis. Or you can print it once a second. This simple technique works well for debugging racing conditions, performance issues (profiling), memory leaks, kernel drivers and bare-metal stuff where timing is a concern. So, yes "Print debugging always works...", but must be used wisely. :-)
- lelanthran 6y ago> A good programmer will know how to use all these tools for what they’re best at! Yeah, I run through valgrind; 9 times out of 10 I don't even get to the point of starting the debugger or recompiling with prints. The remaining 1 time where valgrind output is clean, I go with printing. About half the time the prints aren't enough and then I fire up the debugger. Also, it depends. I'm working on a legacy C# project where the previous dev read somewhere that passwords shouldn't be stored in plain-text, so when changing the stored DB credentials you have to set a breakpoint where the hash is calculated, change the variable holding the cleartext password to the new password you want to use, step the line that calculates the new hash, copy the text out of the watch window and finally paste that text into the file holding the credentials. I do not know how this helps. I also don't care enough to write a c/line utility that generates the hash in a base-64 string, becasue we've changed the DB credentials for the webapp only once since he left. We may have changed it more often if we had an easy way to do so :-/