DEV Community

Discussion on: What are your debugging tips?

wesen profile image
Manuel Odendahl • Edited on

Planning for debuggability

Find a way to reproduce reliably, and quickly, and if possible, in an automated fashion (either through a unit test, or through an automated interaction, say scripting a browser). This means planning for mocking components, so that when a bug happens, writing a reproducible automated piece of code is just minutes away

When designing your system, design for debugging tools. For example, redux allows you to use the redux devtools to log, replay, export everything that interacted with the store. State machine frameworks often offer similar functionality.

Do preemptive logging. If you are able to log inputs and outputs of your system, do so. This will allow you to catch which inputs led to a bug in production, potentially sidestepping the entire debugging process itself.

Leverage git bisect

Now that you can quickly identify when a bug happens, use git bisect (which you can now run fully automatically) to identify the first commit that introduces the bug. Look at the commit to see if anything comes up. Usually, it gives you a sense of which files were modified.

Focus on logging

Rely on printf debugging because it gives you debug logs to pour over, instead of single stepping slowly. You can also use automatic actions on breakpoints if you don't want to litter your code.

Use stacktrace logging and structured logging to augment the richness of your printf debugging. Log into a sqlite database, for example.

Log inputs and outputs of your system.

Ideally, you want to run your failing test case once, and figure out the bug just from looking at the logs.

Know your breakpoints/watchpoints

Set breakpoints and watchpoints to get an overview of what is going on. Be aware that breakpoints and watchpoints slow both you and the program down. Depending on what you are debugging, that might actually influence the behaviour of the system significantly too.

Single step when you think you know what might be causing issues, in order to inspect memory and get a look at the stack trace. If you think you know what might be causing the issue, use breakpoint conditionals and watchpoints to confirm your suspicion.

Explain the bug to someone else

Try to explain the bug to someone else, in the dumbest terms possible (don't make assumptions). Use a whiteboard or show the code if possible. Often the rubberduck effect will kick in, and the bug will magically solve itself.

Take a walk

After fighting a bit with the bug, actually take a walk. I used to be a smoker, and I can't even remember the number of times I solved a bug when going downstairs to smoke a cigarette. Now that I don't have the nicotine withdrawal, I have to actively remind myself to get my butt off the seat and go walk a little bit. Mild physical activity has proven link to solving problems in the subconscious.

wesen profile image
Manuel Odendahl

More crazy techniques that I've used in the past, especially to figure out weird edge cases:

This is something you should do anyway, but:
put your system under immense pressure, and introduce faults into different subsystems. Every piece of software is going to break at scale in some way, might as well figure it out early. Bugs often happen in the error path / through cascading failures in the sidepath.

Embedded software

In embedded, be very careful with debuggers. They often shred the performance and behaviour of the system under test. If you can afford it and EE put in the right connectors, use a full CPU instruction trace debugger.

Use logic analyzer + scope and as many traces as possible. Know your tools inside and out (trigger settings, bus decoding, recording, etc...). Make sure the impedance of the probe doesn't mess with the signals (see next point).

Don't underestimate the power of putting your finger on communication buses / pins / power lines (provided they are low voltage). If the line starts going haywire on the scope while holding a finger somewhere, you have faulty traces or are missing proper impedance or bus termination. Many a bug is actually a badly connected bus line / missing pull ups.

Also, don't forget to check that your device is actually powered on: often, your device will get backpowered through the debugging adapter in the first place, and barely operate because of the low current (very fun).

Embedded again: use LEDs a lot, potentially even build additional LED boards that you can attach to subsystems without influencing voltage too much.

Again in embedded, use a little circuit with an amplified piezo, and put it on different communication lines or gpios. You can tick certain loops of your program, or toggle a gpio in an interrupt. Connect the piezo, which will start clicking in frequency. The human ear is very very sensitive, and you will identify any change in frequency or irregular patterns without even have to look at the device or the scope.

Use musical notes

Use MIDI events to debug your code instead of printf. For example, webmidi is very easy to use in the browser. Connect it to a synthesizer and choose notes for different events (or samples). You can now listen to your code executing, and don't have to look at the debugger / log output. You can hear if an unexpected event happens. The human brain is strongly connected to audio, chances are you'll remember the "melody" of a bug happening, which will allow you to figure out the execution path taken.

thormeier profile image
Pascal Thormeier

Woah, that midi technique sounds amazing, honestly! Are you using that on a regular basis?

Thread Thread
wesen profile image
Manuel Odendahl

No it’s been quite a while honestly but I was thinking of bringing it back out and making a nice example!

mmuller88 profile image
Martin Muller

Yeah agree the midi sound technique litterrly sounds super intereting!