Hacker News new | ask | show | jobs
by colesantiago 1616 days ago
I thought using debuggers was standard practice in non trivial programs?
6 comments

It really depends on a lot of things. Debuggers can completely throw off timing, concealing the bug you're after. Debuggers may tell you that your program state is messed up (doh, sure), but not how you got there. Figuring out how you got there isn't necessarily any easier with a debugger than with generous logging. If anything, it can be much harder. Figuring out where exactly to break or which particular instance of a million dynamically allocated objects to watch, etc. is no easier with the debugger than it is reading the source code.. Single stepping sucks when you don't know where your things go haywire. You spend too long in the wrong part or speed right through the problematic part. So you gotta restart and do it all over again, rinse and repeat if there's a pile of abstractions between where you are and where the problem is. ($deity/printf() help you if you're looking at a task composed of tiny asynchronous events and any attempt at stepping forward in the code just throws you back into the guts of the async executor, ready to pop off a completely unrelated event from the queue.)

If anything, I think debuggers are nice for simple, borderline trivial programs where everything fits on a screen and there's a handful of variables to keep track of. (These tend to be the programs I rarely need a debugger for though, and the debugger isn't necessarily any faster than a bunch of print statements). They're nice for getting stack traces or figuring out the contents of RAM and sometimes that's just what you need, but often that's just working your way backwards towards the real issue, without having a way to rewind time.

rr might change a lot of that, if it's available for your platform..

> Figuring out how you got there isn't necessarily any easier with a debugger than with generous logging. If anything, it can be much harder.

Put a breakpoint early on startup, once hit put a data breakpoint on the part of the state which messed up, reproduce the bug, and there's very high chance debugger will take you to the code which broke the state.

This will even happen if the code which breaks the state is a memory corruption bug in different thread, in the code written in another language, from a third-party DLL.

> debuggers are nice for simple, borderline trivial programs where everything fits on a screen and there's a handful of variables to keep track of

It's funny I think it's the opposite. When there's only a few variables, one can print/log the complete state pretty often. When the state takes a gigabyte of memory and changes often, similar amount of logging going to produce too many terabytes of logs to be useful. Debugging is interactive, you can inspect the complete state and find the most relevant pieces to watch.

> Put a breakpoint early on startup, once hit put a data breakpoint on the part of the state which messed up, reproduce the bug, and there's very high chance debugger will take you to the code which broke the state.

Except when you find the data you're after doesn't exist early at startup, it's allocated on the fly as connections come and go, and you need thousands of connections to come and go before the bug manifests, and the data that eventually gets messed up may first be used thousands of times before it's wrong all of a sudden. IME it's precisely these multi-threaded memory corruption bugs that really resist debuggers. And as is often the case, debugger changes timing so much that the bug doesn't even reproduce.

Even if you find the code that corrupts the memory, it might be totally okay, it just somehow got passed the wrong memory long ago through no fault of its own (or you're looking at a use-after-free).

Indeed, it can happen too. All software is different, bugs are different, and optimal debugging tactics is very different as well.

But when the one in my comment does work, the saved debugging time can be measured in days.

Speaking about debugging tactics, there’re way more than just two. There’re OS-level tools like process monitor on Windows / strace on Linux. There’re specialized tools like RenderDoc or Wireshark. For different types of bugs, these tools can sometimes also save days of work compared to other methods.

Aye. It's also not always obvious what's the optimal tactic, and I believe in many cases there are multiple equally valid approaches and you pick the one you feel most comfortable with. Sometimes the one you start with is a dead end.

That's why I don't really like an absolutist stance (if you rarely use debugger you're dumb / if you always poke around with a debugger you're dumb). I just happen to fall in the "rarely uses debugger" camp myself.

> I’ve done hundreds of interviews like this, and it’s fascinating watching what people do. Do they read the code first? Fire up a debugger? Add print statements and binary search by hand? [..] After hundreds of interviews I still couldn’t tell you which approach is best.

(From https://news.ycombinator.com/item?id=29813036)

> Put a breakpoint early on startup, once hit put a data breakpoint on the part of the state which messed up, reproduce the bug, and there's very high chance debugger will take you to the code which broke the state.

Knowing which part of the state messed up is like 90% of the debugging. Most of the time the part of the state you see messed up is like that because another part of the state is also messed up. Given that neither debuggers nor logging can go back in time to backtrack the state (well, time travel debuggers exist but aren't common or cover all cases), it's usually faster to inspect the code manually to try to find the original bug instead of re-running continuously to backtrack the state.

> it's usually faster to inspect the code manually

I’m not sure how usual that is. Sometimes “the code” is too many thousands of lines to inspect. Other times, “the code” is only the machine code but not the source code, inspecting megabytes of disassembly is not fun.

If you're tracking changes to a given state and seeing which other parts of the state affected those changes, you end up inspecting the same spots, whether it's with a debugger or not.

> inspecting megabytes of disassembly is not fun.

Well, of course there you need the debugger and memory watchers. But I think that fits into the "unusual" slot, most people aren't doing that.

You'd think. But I still see discussions in JavaScript and PHP related threads where people swear by adding `console.log`/`var_dump` statements to their code over using the debugger.

I've seen quite a few threads on r/php where individuals introduce a newer, supposedly better, library that can be used instead of `var_dump`, and there's always the inevitable "why not use the debugger?" to which many reply "because it's hard to set up" or "var_dump is better" or something silly to that effect.

Logging has the benefit of not stopping your programs. On what I work on stopping the program is not feasible. It will block all of the clients trying to communicate with it and if you pause for too long the clients will disconnect and this may make the problem hard to reproduce. Additionally it can be difficult to attach a debugger on a remote machine compared to deploying a simple update which mechanisms already exist for doing.
Logging is not debugging/print statements.

You also wouldn't generally be debugging a request from random clients. You'd generally be debugging in your local dev environment.

Which is strange, because debugging in JS and PHP is almost trivial to setup. Perhaps less so in PHP, but for JS it's right there in the browser for client side code.
JS is super straightforward until you're transpiling from a newer version of JS or from TypeScript and debugging is supported via source maps (which tends to be most JavaScript codebases). At that point, the debugging experience is pretty hit and miss. When it works it's fantastic, but it doesn't always work.
For sure not trivial with php
It is pretty easy to locally or remotely debug with xDebug, the docs are pretty straight forward. What makes it difficult I find is when you're trying to use it in weird VMs.
I have mentored a number of software developers from just starting their career to a decade+ long career. Nearly every time I open the debugger to help them work through an issue I get 'what is this? Why has no one ever told me about this?'.

This is not a judgement, just an observation. I always assumed debuggers were just part of a tool set but it appears (in my experience) many individuals were never taught that it exists.

I also do not claim its a magical tool that can solve all problems. There are plenty of times i do a "print 'i'm here' + var".

I have the same experience, but with even far more basic things than a debugger, such as the Home/End keys on your keyboard, or the search/replace function. Nobody ever bothers to try and see what they do.
You probably were not born last time I fired a debugger.

Those who can't code, debug. :-)

Joke apart it's a style thing, tracing and thinking gets you a long way, though virtuoso debugger users can be productive too no doubt. It's a bit like GUI vs CLI.

In principle, yes, thinking is strictly better than simulating, and over-relying on a debugger can get you into the habit of not reasoning about your code.

In practice, most software is a stack of abstractions and bugs can be caused by anything at any level of that pile, including libraries. The ability to quickly inspect the state at a given execution step is not a tool that can realistically be dispensed by just thinking hard about the code.

I wrote without a debugger for most of the start of my career, but problem solving is about having the right information on hand, and for me a debugger lets me gather that information quicker, and it also lets me iterate faster.

There's another great benefit to a debugger, and that is troubleshooting long-running code or hard to prime iterations. Like for example, debugging a checkout flow can be very cumbersome as you have to prime the state for every test. There are plenty of workarounds if you have to log your way through it, but wouldn't it just be easier to use a tool designed for the job?

You are thinking while you debug. It just gives you more to think about.
I hardly ever use them. I think it's mainly that I don't know how. I also generally dislike having to learn "context specific" tools, since you generally need different debuggers for different languages/environments/runtimes.

I also do a lot of low level code (kernel/bootloader) where debuggers can be available but are often a lot harder to setup. Keep in mind that I also don't like IDEs and my coding setup is mostly vim + ctags + terminal.

I really agree with the quote in TFA, when I write rust code a few judiciously placed dbg!() calls are generally all I need to identify the issue.

Depends on the language. Debugger support is extremely poor in Rust for example, so most people (in my anecdotal experience) just use print statements.