CS211: (Appendix) Debugging with gdb
Table of Contents
Introduction
A lot of beginner programmers do what's called "print debugging"—using print statements to debug their programs. Although it tends to be fine for tiny programs, it is often not enough to get at the heart of catastrophic errors in complicated programs. This is where the use of programs specifically made for debugging shines.
gdb
gdb is the GNU debugger which was made to pair with gcc in order to debug C programs. Whenever using gdb it's best to make sure your C programs are compiled with the -g flag which sets special debugging symbols in the binary that gdb can pick up on.
Below is an example C program which has a simple bug in it:
1: #include <stdio.h> 2: #include <stdlib.h> 3: 4: int sum_array(int *arr, int n){ 5: int total = 0; 6: for(int i = 0; i <= n; i++){ // bug: should be i < n 7: total += arr[i]; 8: } 9: return total; 10: } 11: 12: int main(){ 13: int arr[5] = {1, 2, 3, 4, 5}; 14: int result = sum_array(arr, 5); 15: printf("Sum: %d\n", result); 16: return 0; 17: }
Compiling and running it I get:
josephraskind@stargazer:/tmp/gdb$ gcc arr_bug.c -g -o ./arr_bug; ./arr_bug Sum: 32779
Which is clearly not the 15 I would have expected. This tells me I should fire up the debugger. I can do so with a call to gdb ./arr_bug:
josephraskind@stargazer:/tmp/gdb$ gdb ./arr_bug GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.2) 9.2 Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./arr_bug... (gdb)
I've now entered gdb. There are a handful of core gdb instructions that are often always helpful:
run- Execute the program
breakfn breakbreaks at the beginning of thefnfunction
next- Advances one line at a time (bypasses functions)
step- Advances one line at a time (enters functions)
info locals- Prints out all local variables and their values
gdb let's you freeze a program and look at it step by step:
(gdb) b sum_array
Breakpoint 1 at 0x1169: file arr_bug.c, line 4.
(gdb) run
Starting program: /tmp/gdb/arr_bug
Breakpoint 1, sum_array (arr=0xf0b5ff, n=21845) at arr_bug.c:4
4 int sum_array(int *arr, int n){
(gdb)
Here I set a breakpoint at the entrance to the function sum_array and run the program. Once program execution hits sum_array it is paused. I can then step one line (after the function arguments are initialized) and look at the local variables and arguments:
Breakpoint 1, sum_array (arr=0xf0b5ff, n=21845) at arr_bug.c:4
4 int sum_array(int *arr, int n){
(gdb) step
5 int total = 0;
(gdb) info local
total = -8729
(gdb) info args
arr = 0x7fffffffdde0
n = 5
We see that gdb is now paused at the int total = 0 statement. I use info locals to check the value of all local variables (here total has yet to be initialized). I then use info args to check that the arguments look right—everything seems to check out.
I can then keep using next to go from one line to the next. Eventually, we'd be able to see that i goes beyond the boundaries of arr:
6 for(int i = 0; i <= n; i++){ /* bug: should be i < n */
(gdb) info locals
i = 5
(gdb) print arr[i]
$1 = 32767
(gdb) print arr[5]
$2 = 32767
I can use the print function to evaluate expressions using the available symbols at that point in the runtime. We can see that the access out of the array boundaries is what is returning that large value. Now it is a simple process of exiting gdb with quit and fixing the code!
Using gdb can get extremely complicated. For this class, most major issues can be solved with setting a breakpoint and stepping through a function line by line while examining local variables/global data structures. It is a great resource and should not be ignored when you are in trouble.