Sorry, this post is coming a day late! Had some family stuff come up and didn't get a chance to finish it yesterday.
Hello again, friends! Let's talk about debuggers.
What is a debugger? Well, usually when we talk about bugs in a computer system we are discussing a few different things. First, there is code that is written incorrectly. We all do this - programming is a constant battle between time and quality. Secondly, there are unintended consequences - maybe an API changes or a vendor removes some code you are relying on. Finally, there are what my company calls immaculate defections - bugs that just appear, seemingly out of nowhere.
A debugger is a program that helps you research and resolve bugs. They can differ, depending on use case - some work to debug compiled programs, some allow access to currently running code, and others debug web systems. Many programming languages include debuggers, which make it easy to start and stop your code at certain points. Logging is an often used method of debugging as well.
Depending on the language there are a couple of things we might need to debug.
This is where many developers spend time with debuggers. The stack is the layers of functions that make up a program. For instance, we might have a function called
ReadFile, which is being called by another function called
OpenLog, which was called by a
main function. In this case, the stack would look like this:
----------------- | ReadFile | <- This is the top "frame" of the stack, the function we are currently in ----------------- | OpenLog | <- This is the next frame, which is what called the first frame ----------------- | main | <- This is the bottom-most frame, which is started all the other calls -----------------
binding.pry will allow you to do the same thing in Ruby.
Pausing stack execution is the bread and butter of debugging. It allows you to see what is happening inside a function at any given time. This is incredibly useful when trying to track down what could be causing a bug and to check any assumptions you might be making about how a function works.
A stack trace is a textual representation of a stack, after an error has happened.
In some languages like C, a developer might need to manage memory. Memory debuggers help you debug things like a memory overflow or improper garbage collection. Most modern lanugages handle this, making memory debugging less useful today.
A debugger like
gdb allows you to take a peek inside some compiled code. Many modern debuggers inherit ideas from
gdb, allowing you to set breakpoints in code (even if you don't own it) and walk through the code using
Debugging output is a great way to see what assumptions you might be making about a function or a program. This is most often done by dropping in console log statements and printing out variable values as you run a function. I would say this is 98% of the debugging I do on a daily basis. I really only step into the bigger debugging tools when I am working on a really difficult problem.
localstorage or service workers. Luckily the browser DevTools help a lot with this. Here is the example of dev.to's localstorage, as viewed through the Firefox Dev Tools:
Fun fact - the first instance of an actual computer bug was by Grace Hopper in 1947. A moth had gotten caught between the relays.
I don't have a lot more to add to this post, but I want to walk through some common scenarios that I find myself debugging.
Debugging programs is all about validating or invalidating the assumptions you are making about said program. Sometimes, those assumptions are about what happens inside a function. Dropping a debugger statement or adding a breakpoint is a great way to stop the execution of a function and walk through it step by step. Pair this with some rubber duck debugging and you'll be well on your way to finding the problem.
When you are using a library or a framework, it can help to see how they are using/manipulating the data that you are sending to them. As an example, in React it can be really helpful to debug how often a component is re-rendered.
Another really common scenario is using a debugger to find where a problem is not. Often times you may only have 20-30 minutes to spend researching a bug, before you have to do other work. In these scenarios, you can often narrow down the problem set a lot using a debugger. Step through all the code that touches the buggy code and you'll start to get a fuller picture of exactly what is happening. Just make sure you write it down! Never research twice what you could have written down once!
What is your experience with debuggers? What kind of questions do you have about deubgging code?
Want to read more like this? I tweet all my posts!