Ask HN: Do You Use a Debugger?

47 points by alfiedotwtf ↗ HN
It's been years since I've used a debugger (Turbo Debug), never bothered to learn how to use a debugger for non-assembly code, and tend to just sprinkle `print` everywhere until I can narrow down the problem.

I'm a Vim person, and am envious of IDE users with integrated breakpoints, stepping, and variable inspection with just a mouse, but only today found out about Vim's `termdebug` :headdesk:

So I'm wondering... what does the HN crowd use to investigate code problems:

    - sprinkling print
    - IDE with integrated debugger
    - termdebug (i.e editor with gdb in another pane)
    - debugger by hand on the command line
    - other (please comment)

106 comments

[ 2.9 ms ] story [ 153 ms ] thread
Yeah, I use debuggers, primarily the integrated Visual Stufio debuggers. Also gdb and WinDbg, but I need to learn those better. And ofc the integrated debuggers in browsers.

I’ll also use print debugging if I feel the situation isn’t worth bringing in a debugger, primarily one I don’t know well yet (see above).

Print statements for small one-off programs.

IDE with integrated debugger for larger programs (mostly C# and some Java experience here).

gdb (or similar) for programs without an IDE and larger than a small one-off program.

Some languages also lend themselves (or their implementations do) to better debugging experiences. Common Lisp debugging, for instance, is almost a joy. For grins, I implemented the VM for the Synacor Challenge [0] in tandem with SBCL's debugger. After parsing the opcode, the program entered an ecase. If there was no entry for the opcode, it brought up the debugger, I extended the program, recompiled whatever needed to be recompiled, and resumed it. Wash, rinse, repeat. Once every opcode was implemented it then went on to run the image and I could finish the challenge.

[0] https://challenge.synacor.com/

Do you use plain terminal gdb or some sort of front end over it? Gdb is extremely powerful but UX is abysmal, I wish there was some interactive GUI that would expose most of its functionality in an interactive and intuitive way.
Plain gdb, or its TUI on occasion.

EDIT: I originally wrote this on my phone and while in a bit of a rush. More relaxed today.

I learned plain (CLI) gdb in college, it was taught as part of an exercise in the 3rd CS course and I found it useful during the course. Though I also still largely used printf debugging back then. At some point I had to debug embedded systems that I could connect to via a serial terminal that had gdb running on them. A GUI was not feasible, so I really learned gdb at that point and found I remembered it well enough to not need another interface. But yes, a GUI would be nice especially if it's a tool you use infrequently (like it would be for me now, I don't do much in the way of professional C or C++ programming that would benefit from gdb knowledge at this point, or not on systems where it can be used).

lldb with a graphical interface. I grew up with graphical debugging interfaces, could probably use console based ones if i had to but never bothered to learn
Depends. I use debuggers like pdb when I can’t quickly figure something out as it lets me interact with the running state. I’m less likely though in languages like Golang where I haven’t had as easy of a time using one, and have ended up avoiding them often in UI work as I more often end up in frameworks than code I care about
As a system/DevOps engineer I rarely write code complex enough to need a full blown debugger.

Most times either a print statement or the equivalent of an assertion will be enough to spot the problem.

Some stuff need to be coded in python though, and then sometimes I use pdb.

The first tasks with debugging are reproducing the issue and then isolating the problem area through process of elimination.

I read the code and think through it to form a hypothesis, then use print/error logging to test the hypothesis and come up with a fix.

I haven't used a real debugger in a long time.

Pdb and/or its integration vscode for python. Back when I worked with Groovy professionally I made heavy use of the debugger in IDEA.
Reading Coders at Work, I discovered that even legends like Donald Knuth, Peter Norvig, and Ken Thompson rely on print statements for debugging.
Haha wow, OK thank you... This brings me comfort :)
They are great for detecting whether you hit some code or not without having to use breakpoints and such.
I sprinkle printf's OR use my 'xlog' logging function which can be configured to use more restricted parameters.

Mainly I use the 'xlog' function. I can then wander about the logging file (using a text-editor for navigation) looking at various 'befores' and 'afters'.

I use a bit-mapped variable 'debug' which allows or disallows the logging statements accordingly.

For example, in an emulator, with debug only configured for floppy-disk-controller logging, and looking at the floppy-disk controller code, we might see:

      xlog (FDC,
            "sdma_getdatareg: disk_data: fd_byte_ptr:%04X  bytes_to_xfer= %4X data:%02X\n",
            fdcd->fd_byte_ptr, fdcd->bytes_to_xfer, data);
which would be logged, and

     xlog (INFO, "RESETTING %s\n", MACHINE_NAME);
which would not.

As for using 'gdb', I tend to use that only when I have no clue, or a wrong clue, where the code is breaking down.

I tend to leave the logging code around if it's not particularly obstructive or long-winded as I can switch it out at will by setting 'debug' to zero.

I really like when software uses this technique and leaves the logging present in release code. As a sysadmin being able to crank up debugging logs is nearly as useful as source code access if the developer has been thorough with their logging calls. Seeing internal state goes a long way toward figuring out whys seemingly-correct configuration isn't actually doing what you want.
I can do the same in the debugger without changing one line of production code thanks to tracepoints, and breakpoint actions.
Yes you can, but you need to be pro-active about what you want to know when using gdb (etc.) that way.

Logging gives a solid background of stuff you may not have thought about specifically. It is often the first clue that 'something is rotten in the state of Denmark' when the values that show up are not what they should be.

In truth, debugging is a detective-story, and anything that helps, be it debuggers or logging or printf, is grist for the mill.

Most of the logs I see in production are useless garbage because the coder did not thought of what is relevant.

With a debugger I can place the probes and do my own printfs instead.

<grin>

It's not one or the other. You are free to use both, logging and a debugger.

Yes all the time, debugger for JavaScript and binding.pry for ruby. I don’t understand how people can rely purely on print debugging. I find it much more useful to stop and inspect the state and input, and being able to print a backtrace from that point is also valuable.
I rely only on print debugging purely due to lack of investment in my tooling, both by myself and my work environment.

The thing is, it always just works. I don't need to think about it. I write code in 5+ languages fairly regularly, with some frameworks that have really weird... "support" for dev tools.

But maybe I am just a bad developer with bad excuses. If anyone has been in my shoes and seen the light, would love to hear how you manage jumping in to multiple different languages and keeping a consistent debugging experience (or, at least, more consistent than print).

use the best tool for the job. vscode has some ok debugging features for some scripting languages. some of the eclipse backed things like pycharm or jetbrains also has some decent debugging baked in. Anything c/c++/c#/java/swift/kotlin use the appropriate IDE for that(visual studio has really good debugging, xcode is ok, android studio is ok).

for 80% of the work I'm doing lately it's vscode + LSP + debugger + neovim as a backend.

It really isn't possible to have a consistent cross platform experience outside "closed ecosystems" like the JVM. The problem is that good debugging usually has to integrate very deeply with the language/ecosystem/hardware. Unfortunately programming languages are a mishmash of distinct philosophies implemented differently by people who largely didn't agree with one another, so the debugging experience in most of them is inconsistent and because experience transfers less than you might think, generally abysmal. Moreover, all that deep integration and complexity mean that debuggers are sometimes wholly inappropriate for particular situations and as the use you'll just have to know/learn when that is.

But if you can get your hands on a "good" debugger with (e.g.) time travel or reverse debugging, there are entire classes of issues you can instantly solve. It's like having superpowers.

Sometimes I use a debugger. Sometimes I use print statements. It depends on the task. If it's a loop that runs multiple times, I lean toward using a debugger. If stepping through the code would be too much hassle, then I lean toward using print statements. If I need to explore the environment (often due to poor documentation), then I lean toward using a debugger. In short, I use whatever seems to be the most effective approach.
If I'm confused why something is happening I'll set a break point and then use the debugger to see what the world looks like at that point. That's an advantage when something happens very rarely. One trick is to write a test to detect a problem and then put a break point on that. Let it run until the bug gets hit. Then look at the call stack to see who was naughty.

I use debug print statements to monitor running programs while doing end to end tests.

I have functions to log errors and restart programs in production. Any busfault gets trapped and logged as well. Often just knowing which line of code barfed tells you what happened. Often I also log the calling address off the stack.

I also have a table driven command line interface that I use heavily to poke at and inspect running programs. Having that probably reduces my need for a debugger. Unlike a debugger it's effect on a running program is minimal.

For timing related stuff and debugging bus interfaces I often use a four channel scope.

Yes. I use PyCharm specifically for its debugger.
I mostly code in TypeScript / Node / React.

My debugging tooling of choice is `console.log` when dealing with simple flows: Do an HTTP request and print the value of a variable

I find it lighter (and faster) than attaching a debugger and slowing everything to a halt.

For advanced asynchronous behavior I use Chrome Dev Tools + React Developer Tools + Redux DevTools with these I can inspect any value, profile, time-travel etc..

I'd say the ratio is 80% console.log / 20% dev-tools

I think. I think about what the code is doing, what bug I am seeing, and what could have gone wrong to make it happen. I then watch whatever I need to to confirm/deny my hypothesis. Sometimes it is a debugger. More often it is a print statement. Sometimes it is the UI. Sometimes it is the database. Once in a while the network.

If I have no freaking clue why something is going on, I run through a debugger line by line and watch it all. But in all honesty, that is a rare case. It is far more likely that just slowing down and thinking what could possibly cause a result will guide you in the right direction.

In Python, I use the PDB/PDB++ debugger extensively. When you get a confusing or nonsensical error from deep inside an under-documented 3rd party library, sometimes stepping through that code with a debugger is the only way to figure out what mistake you've made, or what under-documented assumptions the library makes. I also use it a lot for diagnosing test case failures, especially in property-based testing when I might have accidentally generated something I shouldn't have, or otherwise can't figure out why a test is failing.

The experience is even nicer in the Pycharm IDE, where I can set breakpoints with arbitrary conditional logic just by pointing and clicking. It's so easy it almost feels like cheating.

That said, Pycharm is a heavy beast. Psychologically it's not worth the overhead of opening it or even switching projects within it unless I'm working on something involved for an extended period of time. So figuring out how to do something like this in Neovim has been on my to-do list for a while.

I also use the built-in Python logging framework to help me identify where problems might be happening in the first place.

FWIW, debugging python in VS Code is nice, also allows setting conditional breakpoints and watches. Nice middle of the road option between Pycharm and Neovim. Pretty much no setup, works out of the box after installing the python plugin.
Fully agree with debugging Python in VS Code. It's everything one can ask for.
I'm just not that good a programmer, but I still feel the need to program so I tend to take print debugging to absurdist levels, if I start a project that i anticipate is a stretch for me (not being that smart), I make sure that debugging strategies are there from the start.

One particular knotty routine for me was a small synth, I knew I had neither the math ability or c wizardry to develop complex things easily so I made sure there was plenty of scaffolding to assist me. For each 1k of audio output, there is easily 1m of debugging output that I would then "query" with perl, pagenate each step that I could page through, etc, debugging was put in from the start.

Allowing flexible debugging strategies that I could adjust for the difficulties at hand just seems right for me and I find often that different programming constructs need different debugging strategies, and I just don't think debuggers are agile enough to let me adjust.

Its really that I'm just too dumb to get benefits from debuggers, although I'm guessing they may be just perfect for folks smarter than me.

Stop downplaying your own abilities. My day job involves unfucking code written by people who think they’re smarter than they actually are. Write humble code, but stop thinking you’re dumb because of it.
Of course. I use a debugger but I also use print statements. Using a debugger can be great because you can change values on the fly and explore in different ways.

If it's available I would question not using one. If I was pairing with someone who wouldn't use one or didn't know how I would be frustrated.

I have used a debugger, most recently on a VB.NET web application that someone else wrote and that I had to maintain. I could not have done without the debugger there. But I haven't touched the application in several years now.

Before that--almost twenty-five years ago--I figured out how to use the COBOL "animator" to find out why Peoplesoft payroll processing was crashing on us. There was no way in the world to have figured that out without the animator.

These days, though, I mostly use print or logging.debug

Nope. I've tried a number of times; most debuggers I've used don't seem worth the complexity over printing etc.
(comment deleted)
Almost never. Logging is the only real solution in real-time distributed systems.
I'm trying to get myself to use debuggers more often, but I find that just modifying the code to print stuff, then inspecting the result, allows me to better figure out what went wrong. Say I have a small sample data set where I expect a loop to run three times, but it runs four times. It's really easy to see in the printout, but counting the steps in the debugger is tedious.
> counting the steps in the debugger is tedious.

that's a debugger UX problem - the debugger should have a way of setting conditional breakpoints, and allow you to evaluate expressions (and optionally print out expressions).

The only time i've found the debugger lacking is when you have to debug concurrent or async code, and the act of debugging changes the execution to either hide or present a different bug.

I have yet to see a debugger that counts the times a breakpoint is hit. But maybe rr and it’s ilk do.

I find it really valuable that I can just read a text file to spot some patterns even if I don’t know beforehand which patterns I’m looking for.

Visual studio debugger can track hit counts. I would be _very_ surprised if gdb can't do it as well.

With gdb you can even script what it needs to do when it hits a breakpoint. So even if it's not built-in, it's scriptable.

edit: You can even trigger a break or commands if a breakpoint hits a certain number of times: https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Breakpo...

So the hit count is definitely stored.

A vim user might be interested in using vimspector [1], which implements a debugger adapter protocol (DAP) client for (neo)?vim. It's actively developed.

Leveraging DAP lets you have a uniform interface for debugging in multiple languages. (DAP is a sister protocol to the Language Server Protocol which abstracts IDEs from debugger services)

[1] https://github.com/puremourning/vimspector