4. • Logging: Logger.log(“This f&*%@ing thing again: #{bad_value}”)
• Asserting: assertTrue(something, “This should never happen”)
• Quitting: exit(55) // 55 - ABEND: Widget does not fit doodad. Call Bob for help
• Interactive Debuggers: I will kill this bug, line by line! Or: “Wait, I can just inspect
my program when it’s broken!”
• All of them have uses, often together!
5. • Logging: Track complex control flow, monitor background activity
• Should be able to toggle on/off as needed
• Asserting: Preventing known issues at design time, enforcing your constraints
and assumptions
• Exiting: Preventing a problem for getting worse, and terminating at the problem
6. Interactive Debugging
Tools
Breakpoints:
The first tool everyone finds in their
debugger. Suddenly, I can stop the
world, right where I want to!
But wait! There’s so much more…
That we can get to after we cover the
thinking part.
8. Information Gathering
• Get all the information you can about what happened, and what was expected to
happen
• Validate the error
• Gather any additional information – recent commits, new features, refactoring
• Figure out how to reproduce the problem
9. Make it repeatable
• Write a script, a test, whatever you need.
But make it repeatable.
• Often, this may guide you to a starting
point
10. Start Stupid
• Most bugs are silly, small errors. Think about the impact a typo or switched
variable might cause!
• Don’t start big, start small, start stupid.
• Question all of your assumptions, especially if you wrote the code.
11. Binary Search
• Look, I stole this from The Pragmatic Programmer, but it’s probably as close a
silver bullet as you can get.
• Start at opposite ends of the problem, and keep cutting the problem in to smaller
halves. Narrow down the areas of impact until you find yourself looking at one
file, one method.
• git bisect works similarly
12. Corollary: Turn things off
• When lots of things happen in the background, turning them off can work
similarly.
13. Best Practices
• Isolate changes, and measure carefully. One change at a time!
• Take copious notes of what you try, and what happened.
• Read the docs as you go, and question everything.
• Never say “Can’t happen” — it probably already did!
• Trust me: It’s not the compiler.**
** Except when it is… but really, it like never happens. It’s probably your code.
14. Other Techniques
• Rubber Duck Debugging – Talk it out
• Make yourself wrong – Tell yourself, or your
coworker all the reasons this should never
happen
• Stay Shallow – Don’t deep dive until you’re
sure you’re close to the issue
15. More Techniques and Tips
• Don’t look for the Zebra – When you hear
hoofbeats, remind yourself it’s probably a
horse.
• Know when to ask for help
16. Collaborating on a bug
• Take copious notes where you can, and be ready to deliver context
• Use the 20 minute rule
• Always bring context and notes – Don’t make anyone start from zero
18. Interactive Command Line Debuggers
• gdb, lldb, jdp, pry
• Pause the program, step through. Super cool.
• So much more you can do!
19. • Watchpoints: Watch a global variable, or a region of memory
• Registers: Read the contents of registers
• Symbolic Breakpointing: Break on a method call, not a specific line number
• Examine the current frame
• Execute code
• Show a backtrace
20. Scripting the Debugger
• LLDB: Use python to execute common
commands
For iOS:
• Print all visible views or view controllers
• Render a view into an image
• Set a watchpoint on an instance variable
• And more: https://github.com/facebook/chisel
Just one viewpoint
Lots has been written
Idea is to create discussion, cause us to think about how we solve problems
Also not a big fan of “goto”
How do we learn to debug?
Most people follow a pretty common path
You print out at various points
You realize you can assert, or quit to make it obvious when things go wrong
Eventually you realize your language probably ships with an interactive debugger, and that it might be good to learn to use it
But the things you learned previously don’t become obsolete!
Each item has uses in preventing and diagnosing errors
If you need to log to understand something when you’re writing, leave the logs in, but make them toggle-able. Future you says: “THANK YOU!”
Great example: Apple Collection View Demo - Rendering is crazy to follow.
Assert: I have an expectation, and i’m going to prevent you from doing something wrong when you write the code.
Exiting is a last resort method of ensuring that, if you have to stop execution, at least you stop when the problem occurs — You get a stack trace that ends with the program, and presumably the issue.
This is the major leagues of debugging. These tools don’t mess around, and often feel like the giant swiss army knife where you really only use the nail clippers, but I guess it’s nice to know it’s there if I need it.
The most important step in debugging is understanding the problem
You may fix a bug without understanding the problem, but this is unlikely to work well long term
The best fixes come from a strong understanding of the situation
Users experience your software “not working” — often the most important details are in the user’s story, so get as much of it as you can. Ask lots of questions, and gather as much context as possible.
Validate that the error is actually an ‘error’: Maybe it’s just poor or undefined behavior. Still worth fixing, but changes the context.
Look at the tools you have that provide context – crash reports, git history, recent new features and refactors, system updates
If you can’t verify it happens, and make it happen on command it can be very difficult to feel confident in a solution
Creating a repeatable test case may even lead you to a foothold in the code for finding the problem
Much like proofreading your own paper, debugging your own code is often very challenging. You know what you meant when you wrote it!
Small changes can have a huge impact, and can tell you a great deal about what’s really going on.
You have assumptions, but you need to be a scientist: Make a hypothesis, verify it, and throw it away if it doesn’t prove to be true
This is your debugging bread and butter. When you don’t know where to start, cut the whole program in half over and over until you’ve narrowed down there the problem is.
Use tools like Git Bisect to identify when a change introduced the bug
Background updating, syncing, data migrations, etc…
Can be tricky, but sometimes useful.
Does the bug still come up when things are turned off?
Be a scientist: Only one variable at a time
Take notes: Breadcrumbs to find your way back out, and keep from taking the same path
Question Everything/Read the docs: In very established ecosystems, the documentation will often have notes that may point you right at the answer!
Really though, it’s not the compiler. Unless you are debugging a compiler, in which case good luck.
Talking out a problem makes you explain it with more context, and can often drag out details and assumptions you weren’t fully considering
Telling someone why it can’t possibly happen is a great way to motivate yourself to figure it out
It can’t happen because this can’t happen - triggers you to realize “That actually could be what happened!”
Avoid the rabbit holes!
It’s always tempting (and fun!) to conjure up a complicated picture of what’s gone wrong, but bugs are rarely exotic or interesting. Often just typos.
Don’t bury yourself, and don’t go it alone! Know when to ask for help, and don’t forget to do it.
20 Minute Rule: Try for 20 minutes, and take notes. Then ask for help.
The swiss army knives, and often the least well understood.
More than just a way to stop and start your program!
Learn about the power these tools provide, and how to leverage it
Don’t get trapped just in your ecosystem. There are lots of ways to attack a problem!
On the opposite side: Use the right tool for the job!
Valgrind won’t help you find out why your view is misplaced.
This all leads to one final lesson for debugging
What tools do you use?
What techniques?
Where am I wrong?