News for hackers. Stuff that matters.
Re:The unreasonable effectiveness of print debugging by zamfi on Saturday April 24, 2021 @02:58PM (#26927401)
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!

Re:The unreasonable effectiveness of print debugging by vvanders on Saturday April 24, 2021 @03:02PM (#26927434)
If 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.

Re:The unreasonable effectiveness of print debugging by zamfi on Saturday April 24, 2021 @03:22PM (#26927572)
I’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.

Re:The unreasonable effectiveness of print debugging by vvanders on Saturday April 24, 2021 @03:29PM (#26927628)
You 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).

Re:The unreasonable effectiveness of print debugging by zamfi on Saturday April 24, 2021 @03:40PM (#26927704)
> 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.

Re:The unreasonable effectiveness of print debugging by vvanders on Saturday April 24, 2021 @04:48PM (#26928166)
> 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.

Re:The unreasonable effectiveness of print debugging by zamfi on Saturday April 24, 2021 @05:07PM (#26928265)
> Like I said, different tools for different uses, and really depends on the context

We totally agree, and I'm not sure what we're arguing about--perhaps you can fill me in.

I'm arguing that print is almost always worse than any specialized tool. (After all, who would use a specialized tool worse than print?) There is not a one-size-fits-all tool, and print is not a one-size-fits-all tool.

Indeed almost every seasoned developer has a story about print failing. Whether it's the mysterious "Heisenbug" that disappears when you measure it (like the sync issues you mention) -- my personal story is when I was trying to debug a (class project) kernel scheduler. Printing to the console was so slow that by the time I'd printed anything, the scheduler had moved on to the next time slice!

It's worth nothing that "print debugging" is not literally just using the "print" function; it's a style of debugging that involves logging specific information using some logging function (usually, but not always, print) and then analyzing it after the fact (usually, but not always, by reading the printed output).

This strategy of "get data out, then analyze it" is the general form of print debugging, and in the small-memory case, or the sync Heisenbug case, this often means collecting data in the appropriate variables before outputting it to be visible. Isn't this still print debugging, even though it doesn't use a "print" function?

Re:The unreasonable effectiveness of print debugging by vvanders on Saturday April 24, 2021 @06:26PM (#26928720)
I think we're mostly arguing about how useful the various approaches are. At least for me print debugging is a measure of last resort unless I want to extract some historical data out and I know it won't influence the timing of the issue I'm trying to chase down.

With print debugging your inserting the whole build + deploy + repro setup loop into your debugging, if that's a long time(say 20 minutes in one job I had with production hardware) you're in for a world of pain. I find that just about any other tool usually is an order of magnitude more efficient.

Also even the "step debugger" tools do the same thing you'd do with a print. LLVM for instance uses the IR JIT API to generate watch/eval values: https://releases.llvm.org/9.0.0/docs/ORCv2.html#use-cases

IMO you should relentlessly optimize your iteration times, that's the inner loop of development speed and print debugging fares pretty poorly in that area for all the reasons above.

Re:The unreasonable effectiveness of print debugging by zamfi on Saturday April 24, 2021 @07:04PM (#26928961)
> I think we're mostly arguing about how useful the various approaches are.

Ah, that's fair.

> At least for me print debugging is a measure of last resort

Right, and I think this depends on the domain. For lots of mature environments, this makes sense -- there's been years for tooling to catch up to the kinds of bugs people run into, possibly corporate money being put into developing debugging tools, etc.

> IMO you should relentlessly optimize your iteration times, that's the inner loop of development speed and[...]

Agreed, though the effect on print debugging on iteration time is very environment-dependent.

> [...]print debugging fares pretty poorly in that area for all the reasons above

Adding console.log to a web app can be a trivial change (though of course reproducing app state is another issue) -- again very environment-dependent.

Re:The unreasonable effectiveness of print debugging by jayd16 on Saturday April 24, 2021 @05:19PM (#26928337)
This 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.
Re:The unreasonable effectiveness of print debugging by zamfi on Saturday April 24, 2021 @07:09PM (#26928997)
I have. What information exactly can you get from a debugger with watchable variables and immediate mode code execution, that you can't get from print?

I'm not making the argument that people shouldn't use debuggers -- obviously if there's a good one that does what you need, it'd be silly not to use it. And good debuggers are great.

But what happens when you're working on a distributed system? Or multiple processes on a single system? Or...etc. etc. -- debuggers are almost always built to support a particular set of use cases for a particular set of domains. Step outside, and they're not usually as useful.

print always works. It's almost never the best. It's more flexible because it can almost always be used.

Re:The unreasonable effectiveness of print debugging by Too on Sunday April 25, 2021 @01:10AM (#26930464)
Silly example i made up on the spot, but not too far from real life. I'm iterating a list of 10000+ objects with about 20 or more nested properties. Some times one of them behave strange. This function runs several times per second.

Option1: Print all of them, requires rebuild, would log 200000 lines every second. Unless i wrap the print inside conditions, requiring yet another rebuild.

Option2: Conditional breakpoint: user[i].department.balance <= expected_value. Bang, i can now inspect both the complete nested user-object, and the previous/next item in the list, other local and global variables, the call stack of how it reached there, the state of all other threads in this moment, and so on. With the really good debuggers like .NET or JVM I'm even able to rewind the execution pointer to the start of the function or hot-swap the code as it's running.

In short, a debugger allows you to see all state of the program at once, and you can retroactivelly choose what is relevant. As opposed to printf where you must select upfront. Maybe even more importantly it's lacking stack traces, unless you add a log and request-id at every function-enter/exit.

There is also the case of third party code which you can't edit to add printf to. Many environments usually by default give you pretty good context of the symbol names or even complete source code which you can step through (except c++ land where you'd need a debug-build of the lib, which isn't impossible either).

Re:The unreasonable effectiveness of print debugging by Igelau on Saturday April 24, 2021 @09:49PM (#26929691)
> What information exactly can you get from a debugger with watchable variables and immediate mode code execution, that you can't get from print?

If you have some completely undocumented object oriented code with tons of relationships, peeling the state apart in a debugger is a good way to get an idea of what you even have that might be worth printing.

Re:The unreasonable effectiveness of print debugging by jayd16 on Sunday April 25, 2021 @03:13PM (#26935640)
>What information exactly can you get from a debugger with watchable variables and immediate mode code execution, that you can't get from print?

Lots of things but an obvious example is private member info.

Re:The unreasonable effectiveness of print debugging by gerdesj on Saturday April 24, 2021 @06:47PM (#26928876)
Using 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.

Re:The unreasonable effectiveness of print debugging by icedchai on Saturday April 24, 2021 @08:46PM (#26929444)
With 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.
Re:The unreasonable effectiveness of print debugging by Groxx on Saturday April 24, 2021 @07:01PM (#26928944)
(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.
Re:The unreasonable effectiveness of print debugging by crancher on Monday April 26, 2021 @12:33AM (#26939260)
Discovering 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.
Re:The unreasonable effectiveness of print debugging by nyczomg on Saturday April 24, 2021 @09:32PM (#26929609)
"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.

Re:The unreasonable effectiveness of print debugging by ruslan on Saturday April 24, 2021 @10:22PM (#26929849)
You 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. :-)
Re:The unreasonable effectiveness of print debugging by Shikadi on Sunday April 25, 2021 @01:58AM (#26930605)
Collecting statistics can change race conditions too, it's just less likely
Re:The unreasonable effectiveness of print debugging by bluGill on Saturday April 24, 2021 @09:45PM (#26929666)
Debuggers don't work in the situation you described either.
Re:The unreasonable effectiveness of print debugging by Taniwha on Saturday April 24, 2021 @09:57PM (#26929728)
Yes, my reaction entirely, I do a lot of real-time work, an interactive debugger is quite useless when stopping everything is not an option
Re:The unreasonable effectiveness of print debugging by nyczomg on Saturday April 24, 2021 @10:04PM (#26929766)
Where did I say that they did work?
Re:The unreasonable effectiveness of print debugging by tomcam on Saturday April 24, 2021 @06:37PM (#26928800)
> 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.

Re:The unreasonable effectiveness of print debugging by lelanthran on Sunday April 25, 2021 @05:44AM (#26931504)
> 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 :-/

Re:The unreasonable effectiveness of print debugging by greggman3 on Saturday April 24, 2021 @08:25PM (#26929346)
That 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/

I'd be curious what it would take to add any of those concepts to existing debuggers for other languages.