[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)