[HN Gopher] The unreasonable effectiveness of print debugging ___________________________________________________________________ The unreasonable effectiveness of print debugging Author : pcr910303 Score : 232 points Date : 2021-04-24 15:28 UTC (7 hours ago) (HTM) web link (buttondown.email) (TXT) w3m dump (buttondown.email) | SinParadise wrote: | The limitation of print incentivizes me to write smaller | functions and code that are generally free of mutations, so | traces doesn't get stale fast. | | Debugging on the otherhand, well.. I've just been told by my | senior to write bigger functions, because the line-by-line | debugging tool jumps around too much when moving between | functions to functions. | two_handfuls wrote: | I agree with the point about time travel debugging. I find it so | intriguing that I've been playing with it for a little tool to | make VR games. Anecdotally, it has helped me a lot with | debugging. | api wrote: | Print debugging is basically variable watch points, but IMHO | easier. | | The only time to really beware is embedded and real time systems | where printing can throw timing way off or cause other side | effects. | | I heard of a case once where printing via JTAG caused an issue | due to the power draw of sending all the extra data out. But that | was trying to debug a novel board design and its software at | once. | | You won't hit that kind of thing on normal computers like | desktop, mobile, or cloud unless you are writing drivers. | gdubs wrote: | GPU equivalent: putting stuff into vertex buffers so you can | inspect the buffer and see if the expected values are there. | utopcell wrote: | > I do want to point out that print debugging has one critical | feature that most step-based debuggers don't have: you can see | program state from multiple time steps all at once. | | At Google, we have time-traveling debuggers neatly integrated | into our cloud IDE: You can step forwards and backwards, you can | inspect variables for all the values they've had or will have | until program termination, and you can also see all invocations | of methods (along with their parameters) that have happened or | will happen. | | I still use logging for debugging. Cool tech aside, I think what | you really need, above everything else, is the fastest possible | iteration cycles. | LudwigNagasena wrote: | I can just write print(whatever) and get the job done, I don't | want to put breakpoints and search for a data structure I need. | Why can't I write something like this: breakpoint { debug(var) } | | ? | Groxx wrote: | Assuming you mean something like "leverage the debugger to | print, so I don't have to do it in code": you can, in most | debuggers. This is effectively a "log breakpoint", or a | "conditional breakpoint" where your condition is | "print(thing)". | | I use debuggers even for print-debugging for this kind of | reason. No need to re-compile between changing prints, just re- | run - the debugger session will hold them from previous runs, | you can temporarily disable them with a single click, etc. It's | _FAR_ faster and more flexible. | rubyist5eva wrote: | Print debugging is great but you'll pry the IntelliJ debugger | from my cold dead hands. | mistahenry wrote: | Completely agree. When implementing new functionality in my | Kotlin Spring Boot apps, I find the debugger crucial for fixing | any exception that isn't immediately clear. I'll simply rerun | my test with a breakpoint on the failing line, peruse the | values of local variables (often spelunking deep into nested | objects), and test theories with the window that lets my | evaluate arbitrary expressions. Occasionally, I'll change the | value of a local variable and let the program continue to see | if that value would fix the issue. | | It's a workflow that makes "Wait, why did that happen" such an | easy question to answer. | njharman wrote: | I think it has most to do with way user thinks. | | I need to see big picture, whole state, all the stuff and rapidly | jump back and forth. I also, supposidely, have ability to keep a | lot of state / scope/ abstraction in my head. So I find print | debugging sufficient and fast. Rarely encounter situation I feel | need for "stronger" tool. | | Where other people focus on one thing, all that simultaneous | output is just noise and distraction to them. And based on the | continued use and popularity of step-based debuggers, these | people are much more productive (and happier) using those type of | tools. | | It's very important to understand neither system is inherently | superior. Although one or the other is superior to each | individual. [btw over 35yrs of tech industry / software | development I've found this true, that tools/paradigms are not | universally superior but are superior based on individual) for | many subjects. All the ones that have internal debates in | techdom] | [deleted] | sunstone wrote: | I learned early on with an expensive microprocessor emulator, | just have the code raise the voltage on an IO pin as a print | debugger rather than spend days debugging the emulator. | praptak wrote: | You call it print debugging, I call it a powerful experimental | framework for testing hypotheses about the behaviour of the | program. | CJefferson wrote: | Personally, I think my biggest reason for using print debugging | is.. it works. | | In C++ I often find the debugger doesn't find symbols on projects | built with configure/Make. If I have a Java Gradle project I have | no idea how to get it into a debugger. Python debuggers always | seem fragile. Rust requires I install and use a "rust-gdb" script | -- except on my current machine that doesn't work and I don't | know why. | | I'm sure in each case I'm sure I could get a debugger working | given enough time, but the only error I've ever had in print | debugging is failing to flush output before a crash, and it's | never been hard to search for "how to flush output" in whatever | language I'm current using. | mschuster91 wrote: | Worst thing with Java is, especially when taking over a project | or extending some open source thing: finding where that goddamn | log4j config is. Is it in web-inf, in tomcat/glassfish config, | somewhere entirely else (e.g. specified in the run config), or | is it configured in one of the five wrapper layers. | | And _then_ you have to figure out the syntax... does it want | package names, class names, or (hello Intellij plugins!) need a | fucking # at the beginning to be recognized. | | And _then_ you have stuff like a "helpful" IDE that by default | only shows WARN and above levels without telling you somewhere | "there might be stuff you don't see" like Chrome does. | | For actual debuggers, shit is worse, across the board. Running | in Docker is always a recipe for issues, not to mention many | applications actively messing around with stuff like ports. | | A system.out.println always ends up somewhere sane. | fighterpilot wrote: | Part of the reason it works is it forces you to reason about | the code, otherwise you won't know where to put the print | statements. | zamfi wrote: | 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 wrote: | 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. | zamfi wrote: | 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. | vvanders wrote: | 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). | zamfi wrote: | > 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 wrote: | > 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. | zamfi wrote: | > 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? | vvanders wrote: | 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. | tomcam wrote: | > 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. | jayd16 wrote: | 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. | gerdesj wrote: | 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. | tgsovlerkhgsel wrote: | Especially when you need to run your code on a remote server as | part of a bigger platform like some Cloud or Serverless system. | | These systems likely already have a way to get logs, but good | luck getting a debugger to work there. | le-mark wrote: | I'm sorry but this is just ignorance, learning to use the | debugger for the platform at hand is a basic skill every | developer should master. So many times have seen developers use | the debugger to troubleshoot and fixed issues and been | perceived as a "ninja" (despise that term but that was the | effect) because they knew how to use the debugger. I mean yeah | keep printing lines, and keep being out performed by your | debugger using peers. That's the choice. | | Yes I am dieing on this hill. | CJefferson wrote: | I do use debuggers when I have a hard core problem, I've | found some horrible memory corruptions using RR and gdb for | reverse debugging. However, sometimes someone throws a | horrible gradle java project at you and asks for help, and | figuring out how to debug is a pain. | | I'd throw your comment back -- maybe all languages should be | "debug first", make it as easy to get code into a debugger as | it is to just build and run it. | d0mine wrote: | You are missing out on the important point: printing forces | you to formulate a hypothesis: what you expect and to compare | with what you actually get. Debugging encourages less | modeling and more trying random stuff until something sticks. | | It is an exaggeration. In practice, it is useful to apply | both. Novices can get some insight using debugging, more | experienced with code base people should exercise their | understanding of the code and use well picked prints. | SAI_Peregrinus wrote: | You can also do print debugging with a debugger. Just have | a breakpoint that doesn't halt, but instead simply prints | the values of interest to the debugger console. This is | particularly nice for things like debugging interrupt | handlers where the time taken to print output normally is | too much to accept. | toxik wrote: | Watched variables they were called once upon a time. | | Also, I think what you suggest to do here is way harder | to learn than printing. | saagarjha wrote: | Perhaps, but that lends credence to the theory that | people using print debugging may just not have learned | how to effectively use a debugger yet. | axaxs wrote: | OK. And I've also cringed watching ninjas step through code | slowly, reading everything, spending 20 mins catching | something 2 print statements would have achieved. | | Debuggers aren't bad. But neither is printing. Knowing when | to reach for them is probably a bit more key. | mmazing wrote: | Tools in a toolbox. | | The worst developers I've ever known always have their | "this is the best way to do everything" hill they die on. | dragonwriter wrote: | > And I've also cringed watching ninjas step through code | slowly, reading everything, spending 20 mins catching | something 2 print statements would have achieved. | | The problem is that the two print statements will only | catch the bug if they are the right two, based on a correct | hypothesis of what the bug is. Which, with a debugger, | won't require stepping, but setting two breakpoints, doing | a run-to-breakpoint, and inspecting values. | | Stepping is required when you are exploring behavior | because you _don't_ have an easily testable hypothesis | about the source of the bug. | cratermoon wrote: | But with print statements if the first place I put them | doesn't work, I can start doing bisects and quickly find | the right place to print. | | As you note, debugger breakpoints aren't magically better | than print statements when I'm investigating a hypothesis | - I'm no more likely to put them the right place than I | would have put print statements. | | And then there's a class of problems that neither | debugger nor print statements will help: many years ago a | very junior co-worker was wondering why his C code was | giving the wrong answer for some math. It took me | pointing out that one of the numeric types he was using | in his code was different from the rest (I think it was | #defined elsewhere, in some library, as an integer type). | When the compiler did the math it had to do some type | coercing. | jacques_chester wrote: | > _But with print statements if the first place I put | them doesn 't work, I can start doing bisects and quickly | find the right place to print._ | | Can't I also bisect with breakpoints? | cratermoon wrote: | You can, but the comment was talking specifically about | stepping through the code line-by-line. | dragonwriter wrote: | > And then there's a class of problems that neither | debugger nor print statements will help: many years ago a | very junior co-worker was wondering why his C code was | giving the wrong answer for some math. It took me | pointing out that one of the numeric types he was using | in his code was different from the rest | | A debugger and watches on the values of concern | _absolutely will_ help with that (so will properly placed | print statements), so its a really bad example. (Of | course, strong typing helps even more with that | particular case.) | cratermoon wrote: | No, my co-worker was so junior he didn't understand why | that was happening. It took me a moment to glance at the | types in the source and point out the problem, no | debugger needed. | pjmlp wrote: | They weren't ninjas, otherwise they would have used | breakpoint actions for doing those print statements without | modifying the source code. | majjgepolja wrote: | What's breakpoint action? Is it like inserting printf | before breakpoint? | formerly_proven wrote: | Breaking is just the default behavior when a breakpoint | is hit, you can generally attach whatever behavior / | conditions you want using the debugger's scripting | language. | edoceo wrote: | maybe this is sometimes called assert()? or in some | debuggers you set a watch on a var and the BP triggers | only on the watch-condition so the BP don't trigger each | loop, only when x=7 | pjmlp wrote: | It maps breakpoints to debugger actions that are | triggered instead of actually stopping execution, like | formatted output of whatever variables are in scope. | zeta0134 wrote: | I've used print debugging extensively doing embedded | development, when I could reasonably hook up a serial port to | capture the output, or put a crude console on a tiny screen. | These systems can't always be debugged in the traditional | sense, and if you're troubleshooting some bug that happens on | hardware but not in your simulator, then you use the tools | you have available. | xondono wrote: | I'm also willing to bet you live way up the stack. | | Try debugging something in the embedded world, and you'll see | why a lot of bare metal programmers use printfs. Turns out | timing is critical most of the time, so using a debugger | hides a LOT of bugs from your eyes. | | Debuggers are very useful, but so are prints, there just | different tools, they have different purposes. | christophilus wrote: | Agreed. Also certain classes of bugs (such as bugs in | parallelism) are easier to debug via print than using a | debugger. | pugworthy wrote: | I agree with this sentiment. Print debugging is essentially | universal. The same goes for compiler directives or simple | constant driven conditionals to toggle it (in a brute force | way) on and off. | wmichelin wrote: | Python easily has the best debugger I've ever seen in a | language. | | ``` import pdb; pdb.set_trace() ``` | | that's literally all you have to do at any point in your code. | Run your code in the foreground of a terminal, and boom you | have a debugger exactly where you want it. | anarazel wrote: | Fwiw, it's not too hard to approximate that in c/c++. Print | out the pid (I often print out "gdb -p %d") and then sleep | (and perhaps send SIGSTOP to other processes in more | complicated scenarios). | gmfawcett wrote: | In recent Python, it's even easier -- just put `breakpoint()` | in your code. | 1337shadow wrote: | What Python debuggers you are talking about? have you tried the | built-in CLI debugger? Just drop breakpoint() in your code and | you're in. Have been using it daily for over a decade and | really happy with it - it's actually one of my favorite | features of the language, amongst the many super useful | features that Python and its excellent stdlib have to offer. | zmmmmm wrote: | Only thing I hate about this .... regular point of code | review: remove the debugger breakpoint you left in your code! | | We haven't had one hit production yet, but it came close. | Print statement is a lot more harmless. | [deleted] | CJefferson wrote: | Honestly, I didn't know that and I'll try it. Last time I had | to debug I remember adding "-m pdb" (as at the start of | https://docs.python.org/3/library/pdb.html , first result in | Google), but for some reason that immediately threw an error | instead of starting the program, so I just chucked some | prints in instead. | 1337shadow wrote: | I just tried python -m pdb and it works for me | https://dpaste.org/9EQo#L26 but I really always use | breakpoint(). You can even configure it to use other | debuggers with an environment variable, ie.: | PYTHONBREAKPOINT=ipdb.set_trace | CJefferson wrote: | I just quickly went any checked the program I was trying | to debug. I was running 'python3 package --arguments', | where 'package' is the name of a directory which contains | a package I was working on. 'python -m pdb package | --arguments' just complains that 'package' is a | directory. | | Adding a 'breakpoint()' at the start of the program does | get me into the debugger. I'll remember that for future | (but, it's not easy to find by googling if you don't | already know what you are looking for!) | montecarl wrote: | I have mapped a key to insert "import pdb;pdb.set_trace()" | in my editor. Also use it daily, and not just for | debugging. It is useful when working on a new project and | you just want to interrogate some object you got back from | a library to see what valid operations you can do with it. | Or to double check some math operations. | kbd wrote: | Never felt that Python debugging was "fragile". BTW if you're | not using pudb you're missing out. | bakatubas wrote: | As a counter-point, I think there's an argument that folks | don't spend enough time in the debugger. But there's a lot of | value there and in fact one could use a debugger environment to | unit test as even native debuggers have scripting environments. | | Personally, I think folks should master the debugger _first_ | and during all steps learning a programming language. | | But similar to test-driven-development it's a different way of | thinking, and most books scarcely discuss the debuggers. | | That being said, I do use print-debugging a lot too--in C++ a | lot of functionality can be compiled-out, allowing one to, for | instance, print hex dumps of serialized data going to the | network. | | On that note, there is a distinction between trace debugging | that is part of the source code and general print statements | that are hacked in and removed. | foobarian wrote: | The problem is that it's not just a matter of "learning the | debugger for Java." In practice there are many different | projects that configure debugging many different ways, and it | doesn't matter that you know which keys to press in IntelliJ | if it will take you an hour to figure out how to attach it to | the project. This speaks to OP's point, where it's hard to | use a real debugger to casually investigate random projects. | | Having said that, it is absolutely a requirement when working | on a project for any length of time (especially | professionally) to set up and figure out a debugging | environment, because it is significantly more productive than | printing. But the startup cost is certainly there. | zmmmmm wrote: | The java case is actually pretty universal ... you run the | JVM with debugging enabled (fixed string of flags) and then | tell your IDE to attach to the JVM on the port you gave it. | You don't need compileable source, can be on a remote | server, different OS etc - if you have just the source for | the bit you want to debug you can set a breakpoint in it | and it'll stop there. | | Being able to debug third party code in remote / hostile | environments (even when its mixed with proprietary vendor | code) is one of the things I like about Java. | eitland wrote: | > The problem is that it's not just a matter of "learning | the debugger for Java." | | In the Java case, for stanalone projects (i.e. not | something deployed on a server) an if it is your own | project and you don't do anything unreasonable it is mostly | just set a breakpoint and hit "run with debugging". | | Probably the least painful debugging experience I know. | | Doing it for Tomcat/Tomee was slightly more advanced IMO | but still utterly trivial compared to wrangling css or js | ;-) | | There are reasons why we "old folks" like Java so much | despite its verboseness. | throwaway894345 wrote: | I can believe there is value in learning a debugger, but | debuggers could stand to improve significantly. Debugger UX | is almost universally awful and per the parent it's often | difficult to get one up and running. Moreover, if you do your | "print debugging" with log statements of the appropriate | level, they can be useful in production which is perhaps the | biggest value. | corty wrote: | Also, in almost all languages debuggers are an | afterthought. Take e.g. the situation with Golang, Haskell | or Python. Either there is no useful debugger or there is | one, but it came late and still cannot debug everything the | language does. | hawkice wrote: | Print debugging not (really) working is haskell is... | Non-idea, and a bad pairing for bad real debugging. But | test cases are usually easier to figure out. Presumably | there's a balance discovered by people in big projects | but it never seemed as good as normal approaches to me. | corty wrote: | Haskell debugging by testing is great for small functions | where you can use quickcheck. But larger tests for the | more complicated stuff don't work in quickcheck and there | isn't much else that one can easily do. | slaymaker1907 wrote: | I haven't actually used quickcheck in Haskell, but I've | used it for very complicated tests in other languages | including Racket, TypeScript, Rust, and Java. The nicest | thing about quickcheck is that it lets you easily create | test data without imposing too many constraints on it. | Regular fuzzing or randomized testing is almost as good, | but the narrowing done by quickcheck is sometimes nice | for understanding test failures. | Quekid5 wrote: | Not sure what you mean, there's e.g. Tasty for non-QC | testing. It can do all sorts of variations of test, e.g. | traditional unit tests, "golden" tests, etc. | kaba0 wrote: | On the other hand, it is reall great in case of the JVM | klyrs wrote: | In a world with perfect optimizing compilers that never | introduce bugs, we should never "need" print debugging. But | that's not where I live, so I'll keep using print debugging. | | On the other hand... adding print statements can also | invalidate certain optimizations (an excellent source of | heisenbugs), so I'll never stop using debuggers either | toast0 wrote: | Print debugging is essential in distributed systems. | Sitting in the debugger waiting for human input often leads | to timeouts and not covering the case you want. Of course, | sometimes adding the prints, or even just collecting values | to be printed later also changes execution flow, but like | do the best you can. | [deleted] | rufus_foreman wrote: | >> If I have a Java Gradle project I have no idea how to get it | into a debugger | | You download Intellij IDEA, run it, choose File->Open and | select the build.gradle file, right click the main class and | there's a Debug option. | [deleted] | timzaman wrote: | This is pretty much the only way I debug; | | - cross language/platform | | - forces you to come up with hypothesis up front, then test these | very systematically | | - you know what you are doing | | - debugger doesnt interfere | | - works across threads and processes | lisper wrote: | Print debugging is useful for the same reason backtraces are | useful: both allow you to see what happened in the past, which is | usually where the problem you're trying to fix actually happened. | georgewsinger wrote: | I recently discovered a Linux debugger & tool which allowed me to | solve problems 10x faster than print statements: pernos.co (which | is layered over Mozilla's rr time-tracking debugger). | | Pernosco's tool is described pretty well on their website, but | basically it allows you to view a program inside and out, | forwards /and/ backwards, with zero replay lag. Everything from | stack traces to variable displays (at any point in time in your | code execution) is extremely easy to view and understand. The | best part is the lightning fast search functionality (again: zero | lag). | | On top of this: extraordinary customer service if anything breaks | (in my experience, they fix bugs within 24 hours and are highly | communicative). | | If you value your time I _highly_ recommend you check out this | tool. | NoZZz wrote: | If you've ever used visual studio to debug c/c++ code you just | know why the linux crowd mentally works around it. | breck wrote: | I hate coding in an environment that does not easily support | step-wise debugging. And yet, I use printf 10x-100x more | frequently. Printf is actually causing you to do some thinking, | and writing a little bit of code to conduct an experiment that | hopefully will tell you in one shot what the problem is on a | simple run. Step-wise debugging instead forces you to think about | the problem, but then go through carefully and run a lot of | mental load at each "next step" push to figure it out. | | That being said, there's almost no good reason for a platform to | not support step-wise debugging, so it's a big code smell that | you're going to have a bad time in general there (even if in | practice you'd largely use printf anyway). | varispeed wrote: | There are environments where printf is not possible - e.g. MCU | development. For instance, if the code breaks before the serial | port is setup for printf to work. | mark-r wrote: | You can always just send the output to a block of memory that | is reserved for debugging, and dump out that block when | necessary. | marco_craveiro wrote: | I disagree slightly with the emphasis on "print debugging". I | think what is missing is a body of theory around logging as a | methodology. When I write code, I like to be able to look at the | log file and "see" what the code is doing, when on DEBUG or | higher. I think logging is a difficult but very important skill, | and one which we are losing over time. If anyone is aware of any | good books on logging (even if very old), do let me know. Seems | like "logging theory" is a missing subject in Software | Engineering. | | I also don't see any contradiction between liking good logs and | using the debugger when needed. | strikhedonia_ wrote: | Absolutely. | | When people say they use 'print statements,' are they talking | about log points (a debugger construct), logging or something | else? | | I should hope that, in most cases, they're not literally | modifying their source code to achieve this. While there are a | handful of scenarios in which this is necessary, on the whole | it strikes me as inefficient, time-consuming and error-prone. | In most environments, there are better ways to make this data | observable. | diegocg wrote: | I have never spent much time learning debuggers honestly. I'm not | sure if what I want exists: | | I would love to have a debugger that offers a partial text editor | experience, eg. it shows my code, I move the cursor to some | statement, then I press some key binding and the debugger starts | printing (in another window) all the state changes in that | statement. Another key binding prints all the state changes in | the entire function, etc. All of this while the program is | running. | | Are there debuggers that can do this? I have used gdb in the | past, but having to set up breakpoints by hand and remembering | names makes it too tedious. | Agingcoder wrote: | Yes, it's called pernosco, and it's quite remarkable. However, | it works from a recording of your program. | | https://www.pernos.co | chromanoid wrote: | I usually only do print debugging when I encounter a Heisenbug. I | mainly develop in Java, maybe my choice is related to its really | really great debugging tooling. | ben509 wrote: | The worst is when you put print statements in and a bug goes | away, and you realize there's some kind of instruction | reordering bullshit at work. | chromanoid wrote: | A simple toString() with side effects can be insidious when | mixed with other bugs... | dmitryminkovsky wrote: | Whenever this comes up, I think of this quote from _The Practice | of Programming_ by Brian W. Kernighan and Rob Pike [0]: | | > As personal choice, we tend not to use debuggers beyond getting | a stack trace or the value of a variable or two. One reason is | that it is easy to get lost in details of complicated data | structures and control flow; we find stepping through a program | less productive than thinking harder and adding output statements | and self-checking code at critical places. Clicking over | statements takes longer than scanning the output of judiciously- | placed displays. It takes less time to decide where to put print | statements than to single-step to the critical section of code, | even assuming we know where that is. More important, debugging | statements stay with the program; debugging sessions are | transient. | | I found this lines up with my personal experience. I used to lean | on interactive debuggers a lot, and still enjoy using them. | They're fun and make for good exploring. But the act of figuring | out where you want to print really makes you think in ways that | interactive debugging cannot. I find the two forms really | complement each other. | | [0] https://logging.apache.org/log4j/2.x/manual/index.html | varispeed wrote: | The best way of finding faults for me is writing a test that | fails the problematic condition and then use prints in all parts | that I think are being executed and may have key information to | help solving the mystery. | | I tried using debuggers, but it was always too much hassle. | adam_arthur wrote: | I've never understood print debugging, at least in a web | dev/nodejs context. | | I don't begrudge people having their own approach to things, but | almost universally when I see people use print debugging they | seem to take quite a bit longer than just break pointing at the | problem area. | | If your code is in an unexpected state, it's much easier to hit a | breakpoint, examine local values, and then backstep through the | call stack to see what went wrong. I dare to say that in a single | threaded context, it's almost objectively more effective. | | Versus the alternative of using printlines, you basically need to | map/model the state flow out in your head which is prone to error | (limited capacity of human working memory). | | Is it not easier to directly see the problem rather than doing | mental math to make assumptions about the problem? I can't see a | case for that being more effective. | | Most of the time I see people print debugging it seems to be | because they haven't used the debugger much... either they aren't | comfortable with it, or didn't bother to set it up, or see the | mental mapping approach as more "mathematical/logical"... or | something. Takes you back to the school days of solving | algorithms on paper :) | | That being said for simple problems, I've used print debugging | myself (again, usually because I'm too lazy to setup the full | debugger). Or for multithreaded contexts etc, where thinking it | through can actually be more effective than looking directly at | the problem (multiple contexts) | Jeff_Brown wrote: | For print debugging in Python I recently discovered a nice little | time-saver: the icecream package. Rather than having to type | "print( "x: ", x )", you can instead type type "ic(x)". | | [1] https://github.com/gruns/icecream | [deleted] | kstrauser wrote: | Also, "print(f'{x=}')" without an external dependency. | Jeff_Brown wrote: | Cool! Where can I read about what's going on in that? | kstrauser wrote: | Go to https://docs.python.org/3/reference/lexical_analysis. | html#f-... and search for " New in version 3.8: The equal | sign '='." | pbiggar wrote: | Both suck. With a debugger, you need to set up a debugger and | step through (and often, they don't work quite as well as you | hope). With print debugging, you need to add the print | statements. | | In both, you can't retroactively debug already executed code. | | This is one of the areas where I'm really proud of what we did in | Dark. In Dark (https://darklang.com), all execution is traced and | you can see the value of any expression on any trace by putting | your cursor in the expression. Advantages: | | - no struggle to reproduce the error | | - no need to set up a debugger | | - no need to add print statements | | When I write Dark, I can debug in seconds. When I work on the | Dark implementation (F# or ReScript), I spend at least minutes on | each bug because I need to do a bunch of setup to find enough | information to diagnose the error. | zmmmmm wrote: | Most under appreciated aspect of proper debuggers is not about | the code line of interest but the context they give you about the | whole application, ie: the stack frames and their state. When | handed a new codebase I often fire up the debugger and attach and | set various breakpoints in interesting places and then execute | the application to see where / when they get hit. It's a great | way to learn a codebase - things that are hard to discover ("when | is the database driver created and how does it know its | password") just pop out where you might have to spend ages | working it out if you were just examining the source tree. | amelius wrote: | The problem with async programming nowadays is that stack | traces become meaningless. | saagarjha wrote: | That depends on your tooling! Lots of async programming has | debuggers that tracks what you might consider to be synthetic | but more useful backtrace. | ArtWomb wrote: | Have never found writing to logs to be "effective". More a | necessary evil ;) | | What is really effective is "visual debugging". Say for example | you are testing for bias in an RNG. Rendering a large format | image of random rgb values will immediately show any cycles, even | to the untrained eye. | | Consider GPGPU workloads, for ML or ray tracing for example. | There are myriad levels of variables to track: resources, | allocations, command buffer state, synchronization, compute | kernel per vector, and so on. All primitives that very much lend | themselves to graphical representations! | | Right now editing live code in a profiler usually involves | textual editing of the graphical shaders. But it's easy to see | how this evolves to a purely visual shader editor, not unlike | those found in Unreal or Godot. | sagichmal wrote: | I've not figured out a way to effectively debug a distributed | system except via printf. Debuggers are basically a nonstarter, | because stopping one component to inspect it almost always | triggers knock-on effects in other components that change the | overall state of the system. | misiti3780 wrote: | I have been developing large-scale django apps on ec2 for a while | and the solution that has been working best for me is a lot of | logger.** statements sent to papertrail. | mikeech wrote: | Don't let debugger heavy users tell you off | billytetrud wrote: | I definitely think we should create debuggers that can step | backwards. Would be incredibly helpful | sudoit wrote: | I'm working on Swift interpreter and the codebase is fairly | difficult to debug. There's a lot of reused bits. So if you put a | debug point somewhere trying to capture one behavior, odds are | that that line will run 10 times for other work before the | relevant part uses it. | | So I tend to write a LOT of print statements that flush of debug | variables right before I where I want to debug. Then I set a | conditional breakpoint so that I can have the logs "stop" right | where I want the program to. | | Example: | | // debug print | | let someValueICareAbout = variable... | | print(someValueICareAbout) | | print("") <- conditional debug point here "if someValueICareAbout | == 3" | | I think it's technically still "print debugging", because I'm | only using the debugger to stop the program so I get a chance to | read my output. | saagarjha wrote: | Why not just add an action to the conditional breakpoint that | prints the value? | tester756 wrote: | I'd say that except some heavily multithreaded cases, then print | approach may be due to lack of mature tooling | | I can't understand why would anyone prefer to write some print, | when you can have Visual Studio's | | * break point | | * conditional break point | | * ability to place another break points when you're already on | the other | | * expression evaluation at fly!! | | * decent possibility to modify code at fly | | I still remember case where I modified function with line with | bad SQL (breakpoint after executing this SQL), added call to the | same function with the same parameters after this breakpoint, let | it execute again, caught the breakpoint once again and removed | that call to itself | | and all of that without recompiling program! it felt like magic | username90 wrote: | Print statements lets you see many things at once. Breakpoint | only breaks at one thing. They are good for different things. | tester756 wrote: | > Breakpoint only breaks at one thing. | | One breakpoint breaks at one thing, that's why you can have | many of them | username90 wrote: | But it only stops at one thing at once. With print | debugging I can print 100 things in different places and | look at all of them at once giving me a temporal overview, | I can't do that in a debugger. | arendtio wrote: | Another aspect, where printf debugging can be better than | debuggers are use-cases where timing is relevant. Some bugs don't | occur when break points stop the program at certain points in | time. For completeness is should be added, that there are also | cases where the printf can change the performance and make it | impossible to find a bug. | | I think the two methods are complementary and should be use in | combination. | | However, the big issue is that basic printf debugging is very | simple to use and debuggers have a steeper learning curve in the | beginning. Therefore, people start using printf debugging and | don't invest into learning how to use debuggers. And when | developers don't invest into learning how to use debuggers | properly, they are missing the skills to utilize them and still | use printf debugging in cases when debuggers are clearly | superior. | maccard wrote: | If you have a timing related issue fixed by a debugger it's | probably going to be fixed by printing/logging too. | majewsky wrote: | Not at all. Stepping through a function with a debugger | usually takes on the order of seconds and minutes. Printing | some stuff in the function usually takes on the order of | microseconds or milliseconds. That's a difference of at least | three, possibly eight or nine orders of magnitude. It's very | easy to imagine a timing-related issue that's affected by a | delay of X seconds, but not a delay of X microseconds (RPC | calls are typically on the order of milliseconds, for | instance). | SAI_Peregrinus wrote: | Debuggers don't _have_ to halt execution on hitting a | breakpoint. They can do other things, like print the contents | of memory, letting the host system handle the formatting. They | 're actually usually _better_ for timing-sensitive prints than | printf debugging. | | That said, most people don't know this is possible (the | learning curve issue you mentioned), even though it's an | important part of how to use debuggers! | ShroudedNight wrote: | In my experience, GDB running commands on a breakpoint is | generally much, much slower and more prone to materially | changing the timing of things than printf. | spaetzleesser wrote: | " I think the two methods are complementary and should be use | in combination" | | This should be repeated many times. I am getting very tired of | the constant need of people who want to have a strict ideology | and find the one true way of doing things. | Someone1234 wrote: | This is why products like OzCode for Visual Studio[0] are | interesting. With their ability to put in a breakpoint and see | multiple variable's values instantly and "time travel" (i.e. | limit step back through logic), it kind of gives you the print | debugging benefits in regular debugging. | | I've not seen anyone else try anything like this. There's a | YouTube demo here: | | https://youtu.be/82jq5cvl67E?t=1561 | | [0] https://oz-code.com/ozcode-production-debugger | enriquto wrote: | Why "unreasonable"? There's nothing unreasonable nor wrong about | print debugging. Moreover, it's a great first step towards | logging and testing. | xnx wrote: | "Unreasonable" here means that it works way better than it | should for something so simple. It's a somewhat common | phrasing. Example: "The Unreasonable Effectiveness of Data" | https://static.googleusercontent.com/media/research.google.c... | falcolas wrote: | It's "unreasonably effective", meaning that a cost/benefit | analysis is very clearly tilted towards benefit. | elseweather wrote: | It's a meme, like "_____ Considered Harmful". That one started | with "GOTO Considered Harmful". This one started with: | https://en.wikipedia.org/wiki/The_Unreasonable_Effectiveness... | roenxi wrote: | Probably the most interesting thing about development as a | discipline is the near radio silence on how to debug. | | There is a decided lack of academic success in engaging with | debugging as an object that can be studied. There are channels to | learn about debugging as a stand-alone topic. Programmers don't | often talk about debugging techniques in my experience. | | For something that takes up the overwhelming bulk of a | developer's time the silence is in many ways deafening. It may be | that nobody has a method superior to print debugging. | imagica wrote: | It's one of those things that is acquired with experience and | from more and more peers one has had. I think I've learned a | bit from all my peers, almost everyone had something | interesting or an interesting way to tackle a problem. Once a | lot of time is spent in the industry one starts seeing patterns | as a new cycle starts. | | As far as teaching debugging, it is one thing to show some | examples and another one is to run into a bug yourself and get | from having no idea how to debug to actually fixing it. That | whole experience is hard to replicate in unnatural ways.. When | I was in school they told me not to worry too much about | debugging and that I'd run into issues in the real world and | figure out ways to debug depending on the system and that | turned out to be quite correct. | geocar wrote: | > For something that takes up the overwhelming bulk of a | developer's time... | | It isn't the bulk of my time. | | Most of my time is spent figuring out what to do. | | Once I have decided that, telling the computer is usually | straightforward. | | > Programmers don't often talk about debugging techniques in my | experience. | | No they don't, but look at it this way: Bugs are mistakes, and | nobody wants to be told they're making too many mistakes. | Anyone who discovers some amazing debugging technique will | struggle to share it with anyone else for a lot of reasons, and | this is one. | | > It may be that nobody has a method superior to print | debugging. | | Print debugging always works. Getting "a debugger" to work | isn't always easy, and if you aren't already comfortable using | the debugger to track down the kind of bug you're facing, you | will find it very difficult to find the bug and cure it faster | than with print debugging. And since people _don 't_ tend to | make the same mistakes over and over again, debuggers tend to | have a _very_ limited utility in those few mistakes made | frequently. | | My experience is that mistakes like that are the fault of some | kind of fundamental misunderstanding, and rather than spend | time to learn all of the different fundamental | misunderstandings that the debugger was designed to work | around, time is better spent simply correcting your | misunderstandings. | thenoblesunfish wrote: | It's a very interesting point. I suspect it's hard to study | because it's a very high level capability of our brains. If you | understand how we debug, you understand a lot about how | reasoning itself works. I did read a book on debugging as a | general practice once, but it wasn't helpful, as it was mostly | anecdotes and generalities. | damagednoob wrote: | I mean there are other types of debuggers (step-through, time | travel[1]) but I agree with you that I have never seen any | research on which is better/faster. It seems an obvious topic | so it makes me suspect that it comes down to individual style. | | [1] https://docs.microsoft.com/en-us/windows- | hardware/drivers/de... | spaetzleesser wrote: | " which is better/faster" | | Because the answer is "it depends". As you mentioned it's a | lot about individual style and preference. I have seen a lot | of good coders that got things done but worked in totally | different ways. | Swizec wrote: | Debugging is impossibly difficult to teach. It's much closer to | "how to solve an escape room" than it is to "how to build X". | | Debugging requires deep understanding what _you_ are doing and | _your_ system. It 's different every time. And while there's a | general algorithm you can follow: | | 1. Guess what's wrong | | 2. Ask "How would I prove that's wrong?" | | 3. Try it | | 4. If bug found, fix, if not go back to 1 | | How would you teach that other than asking people to go solve a | bunch of real world problems in real world systems for a few | years? | anaerobicover wrote: | This doesn't seem to be a different problem in kind than | teaching people to _write_ programs. How do we do that other | than teaching them some mechanics (syntax, how to run the | compiler) and then setting them a lot of exercises to gain | experience? | | The same seems to apply to debugging. A student needs to be | introduced to the basic concepts and commands, and then | practice. Just same as with a writing exercise, the | instructor can have specific problems to practice specific | techniques. | slibhb wrote: | > 1. Guess what's wrong 2. Ask "How would I prove that's | wrong?" 3. Try it 4. If bug found, fix, if not go back to 1 | | I think this is exactly right. Teaching 1 is impossible I | guess but the general method (it's basically the scientific | method in an ideal environment) seems teachable. | | Come up with a hypothesis of what's wrong, try to prove or | disprove the hypothesis. | travisjungroth wrote: | I think this is the standard algorithm and it's absolutely | terrible. People poke at things, which ends up giving a | linear search across a possibly huge system. Even if the | "guess" is intelligent, it's not like you can trust it. If | you actually fully understood the system, you would know | what's wrong and you wouldn't be debugging. | | Do a bisect instead. The complexity is O(log n). It's | probably slower than if you guess right on the very first | time, but that's less important. Debugging time is dominated | by the _worst_ cases. | | 1. Do something you're 90% sure will work that's on the path | towards your actual goal. | | 2. If it works, move forward in complexity towards your | actual goal. Else, move halfway back to the last working | thing. | | 3. When you've trapped the bug between working and non- | working to the point that you understand it, stop. | | "The weather data isn't getting logged to the text files. Can | I ping the weather servers? Yes. Can I do a get on the report | endpoint? Yes. Can I append to a text file? Yes. Can I append | a line to the weather log file? No. Ok, that narrows it a | lot." | | The real point of this is that you should spend most of your | time with _working_ code, not non-working code. You | methodically increment the difficulty of tasks. This is a | much more pleasant experience than fucking around with code | that just won 't work and you don't know why. Most | importantly, it completely avoids all those times you wasted | hours chasing a bug because of a tiny assumption. It's sorta | like TDD but without the massive test writing overhead. | | A modification for the disciplined: give yourself one (1) | free pass at just taking a stab at the answer. This saves | time on easy fixes. "Oh, it must have been that the country | setting in the config file is off." Give it a single check. | And if it's not that, go back to the slow and steady mode. | Cause you don't understand the system as well as you thought. | majewsky wrote: | You're basically describing the scientific method. | Particularly the practical application of Occam's Razor: | Starting from simple theories, and working your way up | towards more complex ones until the theory is just complex | enough to describe the system behavior you're trying to | understand. | Swizec wrote: | Your algorithm is for creating programs or adding new | features. Not reslly for debugging already broken stuff. | | I would agree that hoing from less to more complexity is a | great heuristic for making those guesses on what to check. | Fronzie wrote: | The one beginners often miss: | | What output do you expect? | | Ask this yourself before starting the debugger. Without this, | it is very easy to glance over the point where things go | funny. | 8note wrote: | There's also: | | 1. Pick a spot in the code | | 2. Figure out what you expect the state to be there | | 3. Check and see that the actual state matches | | You can kinda binary search your way to the location of a bug | by looking in different places | lainga wrote: | I might suggest a few of the adventures of Sherlock Holmes. | colonwqbang wrote: | For a system of moderate complexity, there are myriad answers | to question (1). A skilled programmer has the intuition to | guess the most likely causes. This ranges from the mundane | (e.g. recompile everything from scratch) to the occasional | almost magical lucky guess which immediately leads to a | solution. | ianmcgowan wrote: | It feels like there are basic heuristics that too often | people either don't know or forget to apply. | | I recommend http://debuggingrules.com/ - it's a good book | that lays out some rules that have always helped me. When | people come to me for help debugging something, invariably | they've skipped some of these concepts, and applying them | usually gets to the bottom of things faster than randomly | changing things (which seems to be a common, but ineffective, | way to debug a problem). UNDERSTAND THE | SYSTEM MAKE IT FAIL QUIT THINKING AND LOOK | DIVIDE AND CONQUER CHANGE ONE THING AT A TIME | KEEP AN AUDIT TRAIL CHECK THE PLUG GET A | FRESH VIEW IF YOU DIDN'T FIX IT, IT AIN'T FIXED | jauco wrote: | set -o xtrace ftw! | | More languages should have that. | Agingcoder wrote: | (I have already replied to another comment with the same | suggestion). | | Pernosco offers the best of both worlds ( debugger, print), along | with a few magical features. | | https://www.pernos.co | | With it you can print anything present in your recording and | step, and do anything you'd do in a regular debugging. | mjw1007 wrote: | It's good to see that they hope to provide << the best printf | debugging experience you've ever had >>, but I'm disappointed | at the UI they show on the "Condition and print expressions" | page. | | The video shows the user using their mouse and typing the | expression to be printed into a tiny text input. | | Part of the attraction of print-style debugging is the | convenience of being able to use your main editor UI, along | with all the conveniences that provides, to write that | expression. | | (That might be fancy completion or vi-style editing commands or | keyboard macros; it will be different for different | programmers.) | Agingcoder wrote: | I suggest you tell them! They'll probably be happy to get | some feedback. | | If I'm honest, in spite of it not being perfect, it's much | better than regular printf. The other very nice feature is | dataflow (click on a variable value, and it tells you where | it comes from, and it handles copies seamlessly), which makes | a large number of debugging tasks trivial. | yetihehe wrote: | Besides "behaviour in time", print debugging is effective because | it's typically an extract of the most interesting for programmer | things. I have a debugger window open this very moment and I can | see about a hundred lines with various information about one | structure, but I'm interested only in two (and I have to rerun | this several times, because state got corrupted somewhere | earlier). | Dzugaru wrote: | I stopped doing step debugging at all many years ago. For me it | looks the same as visual vs. text programming. Text and text | search tools are just miles ahead of clicking buttons. | lights0123 wrote: | There's always gdb/lldb from the command line. | hobs wrote: | So (for instance) in PowerShell my code breaks right as it is | about to fail, with the state intact (ErrorActionPreference | Break) which allows me to effectively fix whatever problem is | about to occur and immediately have the state at the time of | failure. | | I dont understand how printing text could EVER approach this | given I can test my assumptions right away and generally only | need 1 error to happen to understand the totality of the | circumstances. | danbmil99 wrote: | Debuggers are next to useless when dealing with today's | distributed systems, all operating asynchronously in parallel. | For the kind of bugs (race conditions, corner cases) that aren't | easily caught by compilers, linters, unit tests or code review | (in other words, the "Heisenbugs" that can stop a release in its | tracks), aggressive logging is the only tool I've ever seen that | is useful in-the-wild. | | I would put forward that proficiency with this style of debugging | (closely related to useful performance profiling) is a major | factor separating mediocre programmers from the quasi-mythical | 10X rockstars. | zestyping wrote: | In one word, search. You can search the output over time. | SPBS wrote: | I feel like the author gets close to the point but fails to drive | it home: step-through debugging is _unbelievably_ cumbersome. | During a typical step-through debugging session, 90% of the time | is spent on lines you are completely not interested in. Oh, did | you accidentally skip the important point because of how tedious | it was to keep spamming step-over /step-in? Better start over | again. With print debugging, you set up your print statements | strategically and -zing-, you get your results back. Feedback | loop shorter. 100% of the lines are the ones you are interested | in, because you put the print statements there. | | I'm still waiting for the feature where you can conditionally | stop at some breakpoint -only- if some other | breakpoint/watchpoint was crossed over. It's not a conditional | breakpoint, because conditional breakpoints can only watch | variables, not other breakpoints. You could of course set some | variable depending on whether some section was entered and then | conditionally break based on that variable. But then you're back | to print debugging land, having to manually insert code in order | debug the program. | | Debuggers are superior when it comes to interrogating the exact | state of some variables, as well as the decision paths the | program takes. For anything simpler, print debugging simply | offers the better developer experience. | Stratoscope wrote: | > _I 'm still waiting for the feature where you can | conditionally stop at some breakpoint -only- if some other | breakpoint/watchpoint was crossed over._ | | PyCharm 2021.1 has this, so I would guess that other members of | the IntelliJ family probably have it too. | | Set a breakpoint and then right-click the red dot, and click | More to open the full Breakpoints dialog. Open the drop-down | under "Disable until hitting the following breakpoint:" and | select the other breakpoint that should enable this one. | | And thank you for mentioning this! I didn't know PyCharm had | this feature until I took a look after seeing your comment. | This will be super useful. | kmfpl wrote: | With lldb you can do that, basically you have the option of | running commands when a given breakpoint is hit, so you can | just make it place another breakpoint, and it will be placed | only if the first breakpoint is hit. I assume you can do | something like this on gdb as well. | hnnameblah365 wrote: | I think there's a conflation of processes and tools which leads | to the false comparison. Print debugging is a process, which uses | a tool called print statements. Stepping through code is a | process, which uses a tool called the debugger. | | Print debugging excels at triaging the problem. And every | language has print statements. Ubiquitous first tier support. | They help you narrow down where your assumptions about the | program behavior may be wrong. | | Once you know what area to focus on, you pull out the debugger | and step thru the code. | GuB-42 wrote: | printf debugging always have a place, but for some reason, I | found the debugging experience to be worse than 20 years ago. | Tools like Visual Studio still have great debuggers, but I didn't | notice significant improvement since the early days, and newer | toolchains are worse. | | A couple of years ago, I had to maintain a bit of Java code using | Eclipse. That is, the old IDE everyone loves to hate. And while | some of that hate is well deserved, for debugging, it was the | most pleasant experience I had in a long time. Nice object | inspector, edit-and-continue, conditional breakpoints, and step- | by-step that works. Much better than fumbling around with GDB or | one of its less-than-perfect UIs. | | Also note that printf debugging and the step-by-step and | breakpoint kind are not mutually exclusive. With an edit-and- | continue feature, you can get the best of both worlds, but that's | not something common these days, unfortunately. | vvanders wrote: | Maybe it was because I was exposed to it early in my career but | I have yet to find anything that rivals Visual Studio | debugging, either from a "just works" perspective or ability to | deep-dive into gnarly memory corruption(memory windows, robust | watch windows and data breakpoints). | da39a3ee wrote: | This should be a non-debate. | | A debugger is for when you want to inspect local state in detail. | That can indeed often be very useful, and they are sophisticated | technology. | | However, the people who think that a debugger is the only way to | debug just aren't good programmers: often you want a picture of | the overall behavior of your program. As has been said by someone | other than me, a debugger allows you to fix a bug; print | statements allow you to think about the right fix for a bug. | saagarjha wrote: | Perhaps the people using a debugger have it set so it can give | them a picture of the overall behavior of your program. | asimjalis wrote: | The reason I like print debugging is that it is repeatable. The | debugger requires too much interaction for it to be automatable. | midjji wrote: | it works, its convenient, its easier to learn, easier to setup, | it has less side effects in multithreaded programs meaning you | can debug those too. You can even log to a file and then get | these logs from your end users... The article does make a good | point, errors that only cause failure a few hundred calls after | the originating problem are easier to find this way too. Every | few years I make an effort to learn to use whatever the current | most popular debuggers are, but at the end of the day, its really | just very specific kinds of errors that the debugging tools are | better for finding and I generally go back to debug outputs soon | enough. | Rapzid wrote: | A lot of debuggers will also print/log and can even inject those | statements into a running app where hot reloading manual print | statements would otherwise not work. | | From there there are situations where a debugger will save a LOT | of time. I'm thinking of trying to figure out what's causing a | behavior in a large dependency injected application with plugins | when you have little to no familiarity with all the code | involved. And then of course all the other things a debugger can | do for you. | | > Clearly Real Debuggers offer a superior experience to print | debugging in so many ways. But print debugging is just easier to | get started with, and it reliably works anywhere, so that's why | we use print debugging so much. | | I think the tone of the first sentence and the word "superior" | unnecessarily creates a strawman. | angst_ridden wrote: | The beauty of printf debugging for a novice C programmer is that | the recompiling with printfs changes the memory layout so your | buffer overflow no longer segfaults you. | | ALternatively, your printf can use the wrong formatter string, | and cause unrelated crashes. Such joy! | | Makes me nostalgic for the good old days. | majjgepolja wrote: | > ALternatively, your printf can use the wrong formatter | string, and cause unrelated crashes. Such joy! | | What compiler are you using? Aztec C? Prehistoric C? | anarazel wrote: | For me this isn't an either or. | | I constantly use both together. For problems that a quickly and | reliably reproducible I'll often just use the debugger (if rr is | suitable, even better). | | But there's plenty problems that take a while to reproduce, | involve many threads / processes, etc. Where the initial set of | potential issues is too wide to easily target with a debugger. | There sprinkling printfs around can provide data at a lower | overhead than doable with a debugger. | | Just yesterday I was debugging something where rr didn't finish | replaying a workload that originally takes 10s within an hour | (loads of io). Switching to print debugging I pinpointed the | issue in < 10min. | oweiler wrote: | Modern debuggers allow you to execute log statements on | breakpoints. Much better than modifying your program to output | something. | p4l4g4 wrote: | I used this a lot! Combined with scripting support, you can | make the experience even more interactive. | | Used gdb scripts in the past to make debug sessions repeatable. | Stuck beyond the point your interested in? No problem, just | restart the session with your gdb script and your right back on | track! You can also add custom functions to output your state | in a more meaningful way or to mock some state. In longer | debugging sessions, a good debugger can be a life safer! | | Still, for shorter sessions, reading logs and adding occasional | prints are hard to beat. | jollybean wrote: | Qt Creator debugger fails on me constantly, it's 2021 and the | leading C++ plaf. is completely unreliable in that many more | cases. | | That's why 'I must' use print debugging, because the 'powers that | be' still provide a broken, half-baked solution 30 years in. | | Print debugging is however so powerful, I think there almost | should be a mechanism built into languages and tooling around it | so that it becomes part of the process instead of a 'kind of | workaround'. It's something we all do, constantly, and yet you'll | never hear about it when people are arguing about Rust or Go. | ddingus wrote: | This kind of thing actually got added to a Microcontroller and | its native SPIN language. | | There is "SEND" which can be used as an ad hoc comms channel, | aimed at a program method. | | And a debug system that can both stream data, text and graphics | to a client, as well as capture and report on the state of the | 8 CPU cores possibly running. | | https://www.parallax.com/propeller-2-graphical-debug-tools-i... | | The debug output is something like using an xterm with | Tektronix emulation turned on, and with all the lower level | bits packaged away. The use can do a lot, from a "print" type | operation to sophisticated graphics, static or animated. | | On the capture side, a sort of supervisor region of RAM is | reserved to for an ISR to capture processor state, or anything | in memory really. Can be time, or event driven. | winrid wrote: | Have you tried Clion? | hpoe wrote: | So I'd be curious. I usually work in scripted languages Bash, | Ruby, JS (bleh) a bit of python. | | Sometimes I do some Java work though and I usually end up going | to print debugging because trying to figure out all the Java | logging framework, or not ending up like 40 layers deep in some | magic framework dependency that is interecepting my code which is | what always happens when I use a debugger. | | That being said do those who work in compiled languages make more | heavy use of debuggers? | seneca wrote: | > do those who work in compiled languages make more heavy use | of debuggers? | | I work in both quite a bit. I actually think I end up using a | debugger more in e.g. Python because I'm more often asking | questions like "what is the type of the thing being passed | here", which is not a thing I need to seek out in something | like Go. | | That said, I think it's more a style difference than anything. | I use debuggers in both compiled and noncompiled languages when | I need a deeper look, and I'd guess people who don't use | debuggers in scripting languages wouldn't use them in compiled | languages. Probably also has to do with the ecosystem and how | easy/effective debuggers are. | UglyToad wrote: | I wonder if C# is a bit of an outlier here. I work mainly in C# | but also sometimes do Type/JavaScript. While I've got the VS | Code debugger running for JavaScript projects I'll rarely use | it and generally use print debugging. | | In C# I'd almost never use print debugging and the whole thing | seems ridiculously antiquated (it's partly why I hate JS work). | [Assuming you're working in Visual Studio...] You literally hit | 1 key, F5 and then you can step through, time travel, edit and | continue. I wonder if people just haven't experienced the ease | of debugging in .NET with VS. I'd say I write probably 50% of | my code in a debugging session, edit and continue is a game | changer. | | I did a little Java work in Intellij and it was similar but I | think partly due to a lack of familiarity with the UI didn't | feel quite as powerful. | domano wrote: | With the Jetbrains products breakpoint debugging is so easy that | i use it for development all the time. Evaluating expressions | inside a breakpoint while developing provides many answers in a | mich tighter feedback loop, even with go or something equally | fast. If i don't have the jetbrains tools i default to print | debugging because everything else is too much of a hassle. | nyanpasu64 wrote: | I wish that evaluating expressions in a C++ debugger worked | more often. It fails half the time (due to "optimized out") in | Visual Studio C++, and 80+% of the time (for various reasons) | in Qt Creator, even in debug builds. | | Maybe it works better in non-C++ languages. | thenoblesunfish wrote: | Good points. Makes me think that if print debugging is primitive, | the more sophisticated alternative isn't a step debugger, but a | logging system. | goalieca wrote: | Print debugging is the only way in a distributed system the way | we are building micro services these days. We just call it | logging. | | Edit: ..and do it in production | yaantc wrote: | Yes. The same apply to a lot of embedded systems. When you | can't stop the system you better learn how to debug from logs. | And put the right logging support in place ahead of time: it | may be impossible to replace the software in place by a new | debug version, and then it's only based on preexisting logs and | just adapting the logs configuration. | marco_craveiro wrote: | Completely agree. | kstrauser wrote: | Amen. "What do you use for debugging prod services?" | "CloudWatch." | razorfen wrote: | ITT: a non-controversial opinion shared by most programmers. | | Print debugging is fast in many cases and requires little mental | overhead to get going. | | But for some/many systems, there's a huge startup and cooldown | time for their applications - and compiling in a print, deploying | the service, and then running through the steps necessary to | recreate a bug is a non-trivial exercise. Think remote debugging | of a deployed system with a bug that requires select network and | data states that are hard or impossible to replicate in | local/dev. | | For things like this, being able to isolate the exact point of | breakage by stepping through deployed code, and doing immediate | evaluation at various points to interrogate state can't be beat. | | This post strikes me as either (a) a younger programmer who still | thinks that tool choice is a war rather than different tools for | different jobs (b) someone making a limp effort at stoking | controversy for attention. | diminish wrote: | In languages where you build a deeply nested call stack, | advanced debugging looks more promissing. But in simpler setups | like ASP/PHP/JSP etc, simply printing works fine. | jollybean wrote: | Or c) someone just making comments from observed experience, | and there's not much about that 'senior developers' have when | it comes to 'having had to compile something that takes a | while' - that's the purview of everyone, or at least, those who | have worked on those larger projects. And though remotely | debugging code definitely happens, it's in relative terms, very | rare. This is just someone making a comment on their blog, | that's it. | DangitBobby wrote: | > I should emphatically mention: I'm not saying that print | debugging is the best end state for debugging tools, far from | it. I'm just saying that we should reflect deeply on why print | debugging is so popular, beyond mere convenience, and | incorporate those lessons into new tools. | | I'm not sure what about the article makes you think either a or | b. They are trying to critically examine why some people reach | for print debugging first, and I think it's spot on. | CJefferson wrote: | On the other hand, when you are working in an example like you | are discussing (a service, or multiple services, which must all | be deployed), it can be hard to figure out how to get the | debugger attached. | | It possible depends on the kind of programming you do -- I find | myself doing little bits of work on projects in many languages, | so learning how to get the debugger going often takes longer | than finding + fixing the bug. | tyingq wrote: | Probably explains why java has such a rich set of logging and | debugging tools. Startup time, plus the idea that printing to | stderr/stdout doesn't help you figure out where that goes in | many java environments :) | pmichaud wrote: | I think the point about seeing the state over time is a great | one. | | But also I want to nitpick because the title is one of my | "favorite" pet peeves: "The Unreasonable Effectiveness of ..." | thing is now used (as in this article) by people who are trying | to say that something is remarkably or surprisingly effective, | but that's not what the original essay was about at all! | | "The unreasonable effectiveness of the mathematics in the natural | sciences" was a philosophy of science piece whose thesis was that | there is no reasonable (rational, provable) basis for the degree | to which our math abstractions and syllogisms happen to | correspond to the physical universe. | | It is self evident that they do in fact correspond super well, | but the original piece was about how weird and spooky that | actually is, if you think about it at all. Math is super | effective, and there is no reasonable basis that we yet know that | it should be so effective. It's unreasonably effective. | | It's such a perfect title for that piece, and it feels dirty or | diluting when it's just used to mean "remarkably effective." | tonymet wrote: | Print debugging is a tool in the toolkit . It's good enough for | many scenarios , and much easier to deploy most of the time . I | still recommend setting up and familiarizing yourself with a step | through debugger , but use both | [deleted] | corysama wrote: | In my experience, people who downplay debuggers don't have the | option to use effective debuggers. Debugging C++ and especially | C# in Visual Studio is wonderful. Debugging Java in Eclipse can | be great. Meanwhile GDB and most other language debuggers are | painful and every IDE integration I've seen of them has been | horribly unreliable. | | I've heard there's a culture in parts of Google where kids go | through uni using GDB because "Woo Linux!" then go straight into | Google where everyone is "Woo Linux!" (I do like Linux, btw) so | they are either still using GDB, or more likely have given up on | it and reverted to printf. So, everything takes _forever_ to | figure out and that's just "normal". This was coming from a | console gamedev who was shocked by the transition after moving to | Google. | | Meanwhile, I've spent a good part of the past couple decades | debugging large volumes of code that I will literally only see | once ever. With a good debugger, that can be done effectively | because watching and even modifying the code's behavior can be | done at a glance rather than a re-compile. | | I've also worked on a very big project that used extensive | logging because they had a very bad debugger setup and | productivity was in the toilet compared to every other job I've | had. The only way I could keep productive was to take the time to | break out systems into small independent programs in my own | environment so that I could use a debugger on that rather the run | the code where it is. | damagednoob wrote: | I dunno. I was a C# dev for 7 years and exclusively used Visual | Studio's debugger. Then went to a JRuby project which had | abysmal debugger support at the time. Learned to used printf | style and it's now been my goto for the last 8 years. This | despite coding in Nodejs for last 4 which has pretty good | support. I only reach for the step through debugger when the | problem is tricky, mainly because of having to do the setup. | thrower123 wrote: | The Visual Studio debugger is great, but there are some | limitations. Anything very serious is going to be | multithreaded, and if you block a thread poking in the | debugger, other things are going to start timing out and the | real flow of the program is interrupted and impossible to | reproduce. | | Log heavily, and log systematically - imagine you're going to | need to grep through days of logfiles to find the needle in the | haystack - you will eventually. Build in runtime switches to | dial log verbosity up and down. Err on the side of providing | more context than less. If something throws exceptions, catch | them, log exactly where it was, what it was supposed to be | doing, and any relevant parameters or state. | | If you can get them, process dump files are unreasonably | effective, too. | saagarjha wrote: | I actually find GDB to be a fairly good debugger, but you need | a bit of work in how to translate what your IDE is doing into | something you can do in GDB. | majewsky wrote: | Interesting hypothesis. | | I think a big part of the issue is that printf debugging has | always been "good enough" for me. I have used gdb in the past, | but I've never felt the incentive to become good at it, so my | knowledge of it atrophies and it has become a less interesting | option over time. On the other hand, my knowledge of how to | printf messages and extract them from the running process never | atrophy because I do exactly that every day. | | So maybe the situation changes if ever I come across a bug that's | so mindbogglingly convoluted that printf debugging is not viable. | Then I'll be forced to learn to use a step debugger well, and | that could change my choice of tools going forward. | hprotagonist wrote: | For python: i specifically recommend | https://github.com/zestyping/q a lot, which is like print | debugging on steroids: All output goes to /tmp/q | (or on Windows, to $HOME/tmp/q). You can watch the output with | this shell command while your program is running: tail | -f /tmp/q | mekoka wrote: | IDE vs Text editor. OOP vs Functional. Logger vs debugger. The | holy wars that shouldn't be. Why can't we all be friends and | accept that Vim is better than emacs. | lr4444lr wrote: | How's file exploring and method/class definition lookup these | days in Vim? | pjio wrote: | The quality of the language servers vary, but you could get a | decent IDE-like experience. | AlexCoventry wrote: | LSP is a great leveler, for that. From what I hear, vim has | great LSP support, these days. | fao_ wrote: | I used to think like you, friend! For over a decade, then I | discovered doom emacs :) | edwinyzh wrote: | Wow, by looking at this screenshot | (https://raw.githubusercontent.com/hlissner/doom- | emacs/screen...), is doom emacs a terminal/console program or | a GUI program?! | fao_ wrote: | It's a number of extremely, extremely well crafted layers | on top of Emacs. | | I switched last July and after 8+ years of using variations | of Vim, Vi, Ex-Vi, Ed(1) (yes), NeoVim, et all, it is by | _far_ the smoothest experience I 've ever had. | | Unlike my experience with Spacemacs, I haven't had _any_ | problems adapting from Vim -- there are no points where the | Vim interaction layer breaks down, and it genuinely feels | like an editor that I 'll be using for the next 20+ years. | Like something that can grow around me. | sa1 wrote: | It's configuration boilerplate for emacs. Emacs itself can | run in both terminal and GUI mode, and doom should broadly | look similar in both settings. | AlexCoventry wrote: | Spacemacs is the one true way, heretic scum! | mcbuilder wrote: | For those wondering, Doom Emacs is a better vim (from evil- | mode) than vim and so much more (easy out of the box | community configs for most languages and tools, and way more | cool stuff) inside the Emacs OS. | j4yav wrote: | I tried using it but got stuck on having to learn Lisp to | understand my config file. | revscat wrote: | Apparently all the cool kids are using neovim + Lua these | days. Lisp turned me off of emacs years ago as well. | Recently I started digging into Neovim and have found Lua | much easier to parse/internalize than Lisp, and kind of a | joy to work with. | j4yav wrote: | Great, I am at least halfway on the right track since I | am using neovim. I know Lua a bit, I actually didn't | realize it was integrated. | alpaca128 wrote: | What always prevented me to actually switch to Emacs was | how it's so huge it seems impossible to get an overview of | how to do what. Every programming language-specific mode | comes with its own unique features that surprise me when I | just want to write code, meanwhile just entering a single | tab without it getting deleted again is an odysee of | reading documentation. At the same time it's slow and | despite it having the best Vim emulation it cannot hide | that Emacs just doesn't work like that. As soon as you | leave the file's buffer you discover how Evil mode's | illusion falls apart on all sides and you always land in | situations where you have to use a mix of Vim and Emacs | keybindings. | | I love the concept behind Emacs, I just think at least 80% | of its code should actually be in plugins, and the program | itself and a lot of large expansions are really bogged down | by the sheer size and lack of simplicity. | | Oh, and Emacs-Lisp...it's much better than Vimscript, but | it's a disappointment nonetheless. Loops instead of | recursion in Lisp, really? And last time I tried it the | parser could not handle unmatched brackets in comments. | ByteJockey wrote: | > Loops instead of recursion in Lisp, really? | | That's pretty common in common lisp as well. Specifically | do loops (and lest we not forget the loop macro). | | I think you might be thinking of the scheme branch of | lisps, but not all of them work that way. | Tade0 wrote: | Let us not forget the war to end all wars: | | Tabs vs spaces | lamontcg wrote: | I've been a vim print debuggerer for like 30 years, and last | year picked up writing C# in an IDE (Rider) and its been quite | nice really. | catillac wrote: | I heard the latter sentiment earlier today, but I don't think | anyone is actually passionate about what editor others use. | Opinionated sometimes. | tyingq wrote: | Most of the time, I suppose. Watching someone try to write | java in vim (or generally, without an IDE) gives me anxiety | though, even with a language server :) | themadsens wrote: | That's perfectly doable. I routinely navigate / extend / | debug / refactor a 600 KLOC Java codebase with nvim + ctags | + ripgrep and will have the job done well before the | language server has even completed digging through those | 600 KLOC. | mdpye wrote: | Meh, it's fine. In general, I find that vim in more | productive most of the time (now I have a language server, | before, wouldn't ever consider it!) | | The fluid and consistent (java is only a portion of what I | write at work) editing experience is mostly more valuable | to me than the slightly better autocompletion. | | I keep intellij installed, but it only open it if I want to | do a fancy mechanical refactor, like extract an interface | from an existing class. Smaller niceties like creating a | local variable from an expression are only a handful of | keystrokes just feel like naturally describing what I want | (lexically, rather than semantically, I'll admit) in vim | anyway. | h2odragon wrote: | Passion about the tools others use can be called for if you | can see they're obviously struggling to meet their goals with | the tools they've chosen. | | The hard part is, unlike a screwdriver where you can | demonstrate, editors and "IT" in general are mental tools | where the mindset is an invisible, nontransferable "handle" | to the visible portion that everyone can see and use. | kstrauser wrote: | A previous manager was snarky about me using Emacs to write | Python instead of "a proper tool". Every time he'd pass my | desk, "a real IDE could to that for you". We finally had a | conversation along the lines of "can it save me more time | than you waste pestering me about meaningless stuff? Also, | STFU until I miss a deadline for the first time since I've | been here." | | I would not hire a carpenter who doesn't believe in using | hammers. Neither would I constantly bug a hired carpenter | to use the hammer I think they should be using instead of | the one they like. | [deleted] | mjw1007 wrote: | There are two separate questions: whether you want to see some | kind of trace of the program or you want to step around in its | state, and whether to use a "real" debugger or not. | | In most cases I prefer to do something trace-based, and in the | IDEs I've used the debuggers have much weaker support for that | than they do for stepping around. | | In particular, setting up tracepoints tends to involve fiddly | dialog boxes which are much less convenient than using the main | text-editor interface to say what you want. | | I think there's plenty of scope for debuggers to provide a better | interface for trace-style debugging. For example I'd like to be | able to toggle a tracepoint after capturing the run, and have the | lines it created appear or disappear, or add a filter expression | or additional information to display without having to rerun the | program. | boatsie wrote: | A few more reasons why print debugging is used. If you are | debugging multiple things at once, you'll have breakpoints set | that aren't necessarily needed at the moment, meaning you have to | continue a bunch of times to get to the right spot. Or your | breakpoint needs to be in a loop that is called multiple times | and conditional breakpoints are a pain and subject to code errors | in the condition itself. Many debuggers are not great at | examining state of objects, for instance a deeply nested object | for which you want array index 42 within a dictionary of an | object. Or you need to see a value that is calculated rather than | just present in the current state. | 1337shadow wrote: | > you'll have breakpoints set that aren't necessarily needed at | the moment, meaning you have to continue a bunch of times to | get to the right spot. | | Python: if something: breakpoint() | | Js: if (something) debugger; | | Much easier than breakpoint conditions in visual debuggers | imho. | bboreham wrote: | "With enough print statements, all bugs are shallow" | jhgb wrote: | I had a crazy idea the other day that perhaps there could be | something like "CSS for program execution traces". If you think | of function identifiers as XML/HTML tags and arguments for | individual function activations as element attributes, then | perhaps something similar to CSS selectors but acting on the tree | representation of a program's execution could trigger at certain | clearly defined points during the execution and format some | human-readable output of what the program was actually doing, or | a "cross-section" of it at least. | eddieh wrote: | Sounds a lot like syntactic sugar or a DSL for symbolic | breakpoints combined with conditionals. That's certainly | doable. | | Something like: func1(4) > func2(null) debug; | | Semantically: upon func1 called with arg 4 and some descending | path that calls func2 with arg null, enter the debugger | | Neat idea! | jhgb wrote: | I got the idea when I was thinking about the applicability of | computer algebra systems to math education. Some way of | visualizing the decisions and steps of a logically | complicated program seemed necessary for that. Getting a | readable trace of the computation in a similar way to the one | that some logical programs or expert systems can justify | their reasoning with seemed like a usable form of such a | visualization, and some time later then the analogy with | CSS/XSLT struck me. I was thinking of collecting all the | steps into an output, but setting breakpoints in a similar | fashion with individual "selectors" could be useful for | debugging, too. | jameshart wrote: | The idea that print debugging is about being able to understand | the time dimension of your code resonates, definitely. It | reminded me of how the redux dev tools browser plug-in is an | interesting pointer to a better kind of debugging. And | essentially all that is is a rich UI around printing out the | entire redux state after each operation. But because the redux | state advances in discrete steps it's very easy to express | exactly what happened, and explore precisely what state change | happened in response to each action. I do find myself wondering | whether there's a much richer debugging capability along those | lines that could be applied more generally. | wodny wrote: | Print debugging is not that different from setting logging level | to DEBUG and those logging calls should already be there in code | and give meaningful insight so I don't get printing being often | ridiculed. | | For over ten years of commercial work I used a debugger only a | couple of times and in most cases it was against someone else's | code, usually when things were completely broken and I needed to | get backtraces from multiple deadlocked threads or lacked | debugging symbols and things like radare were also required. | There were also times when I manually called a syscall using gdb. | | My opinion is that if you can't reason about the code helping | yourself with just a couple of additional messages the code is | probably broken/too complicated to begin with and requires | serious refactoring. I've never understood people stepping | through a program hoping to find some mysterious creature | somewhere along a huge stack of calls. In my career I have often | seen people always debugging an application as a whole instead of | separated modules. Dividing a problem is the key. The same key | that allows me to still program using vim without autocompletion, | keep APIs sane and coherent, and avoid dead code. | | One really useful exception is when dealing with electronics. My | friends programming hardware use debuggers all the time and in | this case it actually makes perfect sense because there is no way | to print anything and things like hardware interrupts come into | play. | lanstin wrote: | When I start to use a new server framework, I like to step thru | the main loop, just to see how it works with system | calls/listens/accepts/reads and how it dispatches up the stack. | But for debugging, I like to a) make it reproducible, b) read | the code, c) add logging to help with any deductions that b | yields. (Sometimes will just go to b if it's a simple bug). | blauditore wrote: | > I used a debugger only a couple of times and in most cases it | was against someone else's code | | The vast majority of code I investigate is "someone else's" | code. Most of the cases, it's a historical accumulation by | multiple authors. If you generally only work in your own code, | that's quite a different experience, and debugging is generally | easier (because you were there when it was written). | saagarjha wrote: | > My opinion is that if you can't reason about the code helping | yourself with just a couple of additional messages the code is | probably broken/too complicated to begin with and requires | serious refactoring. I've never understood people stepping | through a program hoping to find some mysterious creature | somewhere along a huge stack of calls. In my career I have | often seen people always debugging an application as a whole | instead of separated modules. Dividing a problem is the key. | The same key that allows me to still program using vim without | autocompletion, keep APIs sane and coherent, and avoid dead | code. | | The big thing here is that you seem to only work with your own | code, where you can arbitrary refactor it and keep the entire | thing in your head, as well as quickly find which module does | what. But when working with a large foreign project, none of | this works. You _have_ to start working at the scope of the | entire program, because you have no idea of the internal | structure yet. Of course, people who use debuggers divide the | code up as they go, but the point here is that they place a few | choice breakpoints at central points in the application logic, | inspect the stacktraces when one gets hit, and use them to | further dig in to the part of the code they need to look at. | xzel wrote: | I pretty much all of these. One thing I wanted to add is | decorators. There is code you might have easy access to edit to | add print statements. I don't love the spring boot docs and | reading the code isn't as useful as stepping through your | specific autowired code tree. There's definitely use cases but | 95% of the time prints will get you there. Imo you should learn | it because it will save you a bunch of time and headache when | you need it. | bsder wrote: | Actually, using the UART interface to send text breadcrumbs out | the port is a standard technique in embedded, too ... | | The article hits the point of print debugging, you get to see | the _backward in time_. | | By the time you hit "the problem", the pointer is NULL, the | memory is trashed, the system is deadlocked, etc. You need to | reason about how you got there. | | There is a reason why the next step up from basic debugging | embedded is "streaming trace"--effectively print on steroids. | steelframe wrote: | > I don't get printing being often ridiculed | | I just told one of my co-workers last week that I was going to | print-debug an issue. He paused for a moment before saying, | "Uh, I can just debug this for you if you like." | | So yeah, there's definitely some kind of stigma against print- | debugging. | humbleMouse wrote: | You've only used a debugger a couple of times in 10 years? | Yikes. | 0xbadcafebee wrote: | Stupid question: why don't more programming languages and/or | compilers natively support the alternative to print debugging, | which is (afaik) tracing? I guess some languages have it, but | some don't, or they are onerous add-ons? | pestatije wrote: | For production, the only way is logs (print debugging). | haolez wrote: | I don't usually resort to a debugger to hunt for bugs, but I use | them a lot to explore APIs in "real time". I find them much more | convenient than the likes of Postman. | crnkofe wrote: | I find that the greater majority of the time there are better | tools to solve a problem than using print statements even when | considering the fact, that a project needs to be refactored to be | debuggable. | | If I have a bug I can reproduce I can write a unit or integration | test, try narrowing down the issue and use a debugger on the test | itself for further help. Intellij has great support here, VS as | well and there's plenty others. | | If the bug exists in production only using a debugger I can | connect to it remotely and dump the state (thread dumps in Java | or core dumps with Delve for Go). If there's an option of using a | profiler it makes the experience even better especially for | diagnosing performance issues. | | For distributed systems monitoring libraries, log aggregators are | much more useful than raw logs. Proper metrics allow fast | pinpointing of issues and log aggregators give me an option to | either look for rare/common errors easily. | | The only case I'd resort to prints nowadays is as a last resort | if there are no better options. | Androider wrote: | Speed of iteration beats quality of iteration. | | You can step through the program, reason about what's going on, | tracking values as they change. But if you missed the moment, you | have start again from the beginning (time traveling debuggers | being rare). Or maybe you're looking at the wrong part entirely | at this stage, and just wasting time. | | With print debugging you write a bit of code to test a | hypothesis. Then you run it, and you keep running it, and | especially if it's an UI program you play with the UI and see how | the values change during that run. Ideally the loop to change the | code -> see the result should be a few seconds. | | You can then git commit or stash your prints, switch branches and | compare behavior with the same changes applied. And at the end of | the day if you walk away, your prints will still be there the | next morning. The debugger doesn't produce any comparable | tangible artifacts. | | Once you do know where the problem is, and if it's not apparent | what the problem is (most problems are pretty trivial once | located), that's IMO the time to break out the debugger and | slowly step through it. But the vast majority of problems are | faster to solve through rapid iterative exploration with prints | in my experience (C, C++ for over a decade, Python, now JS/TS). | damagednoob wrote: | > Speed of iteration beats quality of iteration. | | That's especially true if you're doing some form of TDD/unit | testing. With IntelliJ, I can easily set it to watch for | changes and cycle one unit test while I make changes. If | something weird happens I can just drop a printf in there, | understand and rectify the issue, then take it out. Much faster | than step through debugging. | forrestthewoods wrote: | > Speed of iteration beats quality of iteration. | | Right. That's why printf debugging sucks. | | If you're in a compiled language with a 2-minute iteration it | can take an hour to do a binary search to track down an issue | that would take 5 minutes with a proper step debugger. | | Print debugging is great because it works and is the ultimate | fallback. But it sucks and I hate when I am forced to use it. | matsemann wrote: | I often use prints to find the suspect, and then debugger to | weed it out. Conditional breakpoints make it easy to stop at | the correct place. | | About your debugging from the beginning: with Intellij on the | jvm one can "drop frame", which is basically to discard the | current function and start over with the stack as it was. Since | I mostly write kotlin my objects are immutable, so rerunning | most stuff actually works fine. And hot-swapping the function | while the debugger is paused I can even try multiple | implementations without having to rerun everything, just drop | frame, hot swap, step into the new and updated function. | | I'd say knowing the debugger well and using it is a faster way | to iterate than not. | Blumfid wrote: | Java debugger can easily drop frames which helps tremendously | in going over a function multiple times. | | Hot code replacement, which I have already and still use since | 2005 works very well as well. | | In php, debugger are half as good but code replacement works | immediately. | | I would rarely use print debugging and in Java never. | kapep wrote: | > Speed of iteration beats quality of iteration. | | I totally agree but for me that means using a debugger and make | full use of its features. | | > But if you missed the moment, you have start again from the | beginning | | As already mentioned in another comment, "drop frame" is a | standard Java debugger feature. You can easily go back to the | start of any method and go though everything again (side | effects of already executed code can give some trouble though). | | > Or maybe you're looking at the wrong part entirely at this | stage, and just wasting time. | | You have the same issue when printing in the wrong parts. Of | course you can plaster the code with lots of print statements | to see which gets executed. But you can do the same with | breakpoints and see where the debugger stops. | | > With print debugging you write a bit of code to test a | hypothesis. Then you run it, and you keep running it, and | especially if it's an UI program you play with the UI and see | how the values change during that run. | | I really like conditional breakpoints for this. You write a | condition for a state that interests you. Then play around in | the UI until it stops for that condition and you can easily | inspect the complete state at that moment. This is quite useful | for debugging methods that are executed very often. Trigger | breakpoint (which disable all other breakpoints until they are | triggered) are also useful in those situations without | requiring any code. | | > Once you do know where the problem is, and if it's not | apparent what the problem is (most problems are pretty trivial | once located), that's IMO the time to break out the debugger | and slowly step through it. But the vast majority of problems | are faster to solve through rapid iterative exploration with | prints in my experience [...] | | I can just say that I usually locate issued way faster with a | debugger. "rapid iterative exploration" could also kind of | describe my workflow using breakpoints. Maybe it actually less | about the tool and more about your approach for locating issues | in the code. | cheeri0 wrote: | Yeah it works great until you install 30 frameworks and they all | tell you so much useless crap that you can't see your own | messages. Why do they log these useless messages? Because, | they're bad programmers who are 1000 AU from being able to | realize it. | beiller wrote: | Here's a hack I do when I'm running a tight loop. In something | like a video game at 60 fps, print is useless cause it spams so | much in the terminal it's unreadable. So I use my hack: If | math.random() > 0.99 print(debug_msg) | slaymaker1907 wrote: | I agree about being able to see the whole program execution. This | is particularly useful for multithreaded code since it provides a | linear view into how the program actually executed. How are you | supposed to figure out that A happened before B in a | multithreaded program using only a debugger? With adequate | logging, even if you don't log the precise times for A and B, you | can often infer the ordering of these events based on other | logged data. | | For a lot of glue type code, I don't actually care about stepping | through something line by line. I really want to see how | components interact, not each step of execution. Though I do wish | languages had better support for doing something like printing | out all local variables in the current function along with the | stack trace, sort of like a very shallow, low-cost dump. | | Another big advantage is that logging is usually much easier to | turn on (or even keep on by default) for production scenarios. | Good luck getting some bank to let you run a debugger or even get | a dump for anything. | skneko wrote: | > How are you supposed to figure out that A happened before B | in a multithreaded program using only a debugger? Setting | printpoints, letting them be hit and continuing... this whole | thread seems to arise from the fact people have not learned to | use debuggers. | saagarjha wrote: | > How are you supposed to figure out that A happened before B | in a multithreaded program using only a debugger? | | Breakpoint at A, breakpoint at B, both automatically continue | when hit. ___________________________________________________________________ (page generated 2021-04-24 23:00 UTC)