Using GDB in VSCode
- What is GDB?
- Setting Up GDB in VSCode
- Configuring VSCode to Run Your Program With GDB
- Debugging Your Program in VSCode
What is GDB?
The GNU Project debugger, abbreviated GDB, is a debugging tool for a variety of languages including C++. GDB allows you to watch how a program runs step by step, pause the program when it reaches specific places, and examine the values of variables in memory when the program is stopped. This allows you to identify when your program starts behaving incorrectly and gain more information about what is going wrong.
GDB is a separate tool from VSCode and can be executed on the command prompt. It does not rely on a graphical user interface, meaning that it can be used in environments where only a text-based terminal is available. It is also useful for a variety of other purposes than those described here. From the command prompt, GDB is executed somewhat like Valgrind: to debug a program that is normally launched by running ./foo arg1 arg2
, we instead run gdb --args ./foo arg1 arg2
. For more information on using GDB on the command prompt, please refer to Dr. Tia Newhall’s thorough GDB guide.
Setting Up GDB in VSCode
To use GDB in VSCode in CS35, you’ll need to begin by setting up a debugging profile. To keep things simple, you can set up a single debugging profile that you’ll use throughout the semester, changing it as you need to run different programs with different arguments. This means you should only need to follow these setup instructions once for the course. For purposes of illustration, this guide will include screenshots of a simple single-file project with a Makefile
.
To begin, we should make sure we have the Microsoft C++ support tools installed. You can install this extension through the extensions button on the left side panel. You can also do this by using your CS network account to run code --install-extension ms-vscode.cpptools
. You can do this from within VSCode or using a separate terminal.
To set up a GDB profile, we begin by clicking on the arrow-and-bug symbol on the left side panel. This opens the “Run and Debug” pane. In that pane, click the hyperlink which says “create a launch.json file”. You will be presented with a list of profile types to use when creating the file. You should choose “C++ (GDB/LLDB)”. If that option is not available and you just installed the C++ tools plugin, you may need to restart VSCode and try again before you see it.
The launch.json
file is stored in a hidden directory named .vscode
. Once you have created it, you should see open appear in your editor. This file uses a format based upon JavaScript Object Notation (JSON), which is a convenient way of using a text editor to manipulate structured data. JSON and similar formats are commonly used to configure applications like VSCode. Here is an example of how your editor might look after creating the launch.json
file.
There should be a button labeled “Add Configuration…” in the bottom right of the window. Press it and choose the “C/C++: (gdb) Launch” option. Make sure to select the right option! It will be difficult to notice if you have selected a different option here until something goes wrong down the line.
Once you’ve selected this option, a new configuration will be added to your launch.json
file. This will be a considerable quantity of text describing to VSCode precisely how you want it to run GDB. Here’s an example of how the file might appear after this configuration is added.
Press Ctrl+S (or Command+S on a Mac) to save the launch.json
file. This will change the “Run and Debug” pane to display a series of smaller panes labeled things like “variables”, “watch”, and “call stack”.
We are almost finished setting up our debug profile! To make it easier to use VSCode in the future, especially if you use it for different courses, it will be helpful to give your profile a name. The default name is (gdb) Launch
, which isn’t very informative. Find the text "name": "(gdb) Launch"
in the configuration file nad change it to "name": "CS35 Debug"
. Make sure when you do this not to delete the quotation marks the comma at the end of the line, as these are necessary for the file to match the format used by VSCode. When you save this file, the name of the selected debug profile in the upper left of the window will change as well.
Since you’ll be using this profile throughout the semester, you shouldn’t need to follow these instructions again. You’re now ready to start using GDB in VSCode!
Configuring VSCode to Run Your Program With GDB
When you want to use GDB to debug your program, you’ll need to tell VSCode how to run it. This involves editing a couple lines in your launch.json
. If you have just created your launch.json
file, you might still have it open. Otherwise, you’ll need to open the launch.json
file by clicking the arrow-and-bug symbol on the left side panel to open the “Run and Debug” pane. Click on the small gear icon at the top left of the window to open launch.json
.
There are two things you’ll probably need to edit in your launch configuration. The first is the program
setting, which should give the name of the program you want to run. Our project for this tutorial will run a program called example
. To make sure that this program runs as we expect, we will give the absolute path for that program when we configure the editor. You can find the absolute path of your program by using realpath
, a tool which will print the absolute path of your executable. In our demonstration, realpath ./example
prints /home/zpalmer/project/example
.
Edit your launch.json
file to change your program
setting to equal the absolute path of your program. In this case, we are editing our launch.json
file to include the text "program": "/home/zpalmer/project/example"
. Your program
setting will differ based upon the absolute path of your program’s executable.
Next, determine what command line arguments you want your program to receive. In the case of this tutorial, the example
program expects a single command-line argument. For instance, we might run our program by entering ./example clouds
on the command line. Because we have a single additional argument – clouds
– we will edit the args
entry of launch.json
to read "args": ["clouds"]
. The value after "args":
is expected to be a list of strings. If you have no command line arguments, use an empty list by writing []
. If you have three command line arguments, you might write "args": ["arg1", "arg2", "arg3"]
; this would be like running ./example arg1 arg2 arg3
in your terminal.
Make sure to save your launch.json
file once you have made these changes.
Debugging Your Program in VSCode
Once you have configured your program to run in VSCode’s debugger, you can tell the debugger you’d like it to pause at the top of your main
function by setting a breakpoint. Click the area just to the left of the line number at the beginning of your main
function; you’ll see a red dot appear and it will brighten when you click it.
Once you have set the breakpoint, you can run the program through the debugger by pressing the green arrow in the upper-left of the “Run and Debug” pane.
This will open a new terminal window that shows the output of the debugger, which might be either very verbose or completely blank (depending upon your situation). We can largely ignore that window for now, though. The line containing the breakpoint will be highlighted because GDB started running the program but stopped when it saw a breakpoint. Importantly, the highlighted line is the next line to run which has not run yet.
We can see the initial values of the variables in the upper left of the “Run and Debug” pane under “Variables”. Note, for instance, that argc
is 2
in this example (because we configured VSCode to run ./example clouds
).
We control the program by pressing the buttons which are, by default, docked in the top middle of the screen. (Hover over these buttons to see their keyboard shortcuts.) The second button in that row – the dot with an arrow arcing over it – will be particularly useful. It instructs GDB to run the next line of code and then pause again. In this example, pressing it causes the if
condition to run and GDB pauses on the next line, an assignment to the string
variable word
.
If we press that same button again, GDB will run this line, which assigns argv[1]
(a C-style string) to word
(a C++ string
variable). VSCode highlights the new value of word
in the Variables section to draw our attention to the fact that it changed.
Note again that the highlighted line has not yet been executed. As a result, we have not yet seen anything printed. If we step forward again, we can see this output printed to the debugging terminal.
The next line to be executed calls a function. The third button in our control bar is a downward-pointing arrow over a dot, which denotes stepping into a function. Let’s consider pressing this button to enter the beExcited
function and watch it run. Here’s what we see after we press that button:
This may be quite surprising! The code which has appeared here looks nothing like the code we were running (and isn’t code that we wrote at all). That’s because this code was running automatically as a result of passing the string
object to the beExcited
function. This is a consequence of how string
s (as objects) in C++ are copied from one variable into another: this copying function runs implicitly before the beExcited
function actually runs.
We don’t want to watch this function do whatever it does, so we can press the fourth button on the control bar (an upward arrow pointing away from the dot) to finish running the function we’re in and go back to where it was called. We find ourselves at the beExcited
call again.
Even though it looks like we’re back where we started before we pressed the “Step Into” button, we’ve made progress: the parameter of beExcited
has been filled. So we press the “Step Into” button again in order to see the beExcited
function run.
The bottom left of the “Run and Debug” pane shows us information about the current call stack. The beExcited
function is running now and it was called by the main
function. In general, this section shows us our call stack while the Variables section shows us the variables in the currently-selected stack frame. Note that the s
variable, the parameter to beExcited
, is shown here rather than the word
variable from main
.
Summary
These three buttons – “Step Over”, “Step Into”, and “Step Out” – are the primary controls you use to run your program while debugging. As you do so, you can see the variables and call stack of your program change. You can use this tool to find bugs by running your program in a way that you know isn’t working correctly and stepping through its execution until you see something wrong: a variable with an incorrect value, a condition not running the way you expected, a loop never finishing, etc. Then, you can use the Variables view to inspect the situation and come to understand why your program behaved the way it did. This perspective can be invaluable in identifying the source of problematic program behavior and determining how to correct it.