Using GDB Effectively: A Step-by-Step Demonstration
Published:
Using GDB Effectively: A Step-by-Step Demonstration
Debugging is an essential skill for developers, and GDB (GNU Debugger) is one of the most powerful tools available. Why settle for print statements when you can step through code, inspect variables, and analyze program flow in real-time? Debugging lets you do all this and more.
This post walks you through using GDB effectively with a more complex C program. We’ll explore features like breakpoints, stepping, backtraces, watches, and conditional debugging.
A Sample C Program with Nested Functions
Let’s consider this program to demonstrate GDB’s power:
#include <stdio.h>
void helper_function(int x) {
printf("Helper received: %d\n", x);
if (x < 0) {
printf("Error: Negative number\n");
}
}
int compute_sum(int a, int b) {
helper_function(a);
helper_function(b);
return a + b;
}
int factorial(int n) {
if (n == 0) return 1;
return n * factorial(n - 1);
}
int main() {
int x = 5, y = -2;
printf("Factorial of %d is %d\n", x, factorial(x));
int sum = compute_sum(x, y);
printf("Sum of %d and %d is %d\n", x, y, sum);
return 0;
}
Save this code in a file called nested_demo.c
. Compile it with debugging information:
gcc -g nested_demo.c -o nested_demo
Launching GDB
Start GDB with the compiled program:
gdb ./nested_demo
Exploring Key GDB Features
1. Breakpoints
Set breakpoints at specific functions or lines to pause execution. For instance, set a breakpoint at compute_sum
:
break compute_sum
Run the program:
run
When the breakpoint hits, GDB pauses at the beginning of compute_sum
.
2. Stepping Through Code
To understand the flow inside a function, use:
step
This executes the current line and steps into any function calls. If you want to skip function calls:
next
3. Backtracing
To see how the program reached the current function, use:
backtrace
Here’s an example: If the program pauses in helper_function
during a call from compute_sum
, the backtrace might look like:
#0 helper_function (x=-2) at nested_demo.c:5
#1 compute_sum (a=5, b=-2) at nested_demo.c:12
#2 main () at nested_demo.c:21
This stack trace shows the chain of calls that led to the current point, making it easier to debug nested or recursive functions.
4. Watches
Monitor variables for changes dynamically. Add a watch for x
:
watch x
Continue execution:
continue
GDB halts whenever x
changes, even if no breakpoint is set.
5. Conditional Breakpoints
Set a breakpoint that activates only when certain conditions are met. For instance, break when b
is negative in compute_sum
:
break compute_sum if b < 0
Run the program, and GDB will pause only when b
is negative.
6. Examining Variables and Locals
While paused, inspect variables using:
print a
View all local variables:
info locals
7. Stepping Out of Functions
To skip the rest of the current function and return to the caller:
finish
This is especially helpful in recursive calls, like factorial
.
8. Viewing Code Layout
Enable source code view to track where execution is paused:
layout src
This splits the terminal, showing your code alongside GDB commands.
9. Continuing Until Specific Points
Use until
to continue execution until a specific line or loop iteration:
until 15
Debugging the Nested Program
- Tracking Recursive Calls:
- Set a breakpoint in
factorial
. - Use
backtrace
to view the recursive call stack. - Use
info args
to examine the arguments at each level of recursion.
- Set a breakpoint in
- Debugging Nested Function Calls:
- Break at
compute_sum
and step intohelper_function
. - Use
backtrace
to trace howhelper_function
was invoked. - Watch variable
x
to monitor its value during execution.
- Break at
- Diagnosing Errors:
- Use a conditional breakpoint in
helper_function
for negative values:break helper_function if x < 0
- Step through the error-handling logic.
- Use a conditional breakpoint in
Wrapping Up
GDB is a versatile and powerful debugger that simplifies identifying bugs and understanding program flow. By experimenting with breakpoints, backtraces, watches, and stepping commands, you can gain a deep insight into your code. Debugging programs with nested and recursive functions, as shown here, showcases the real strength of GDB.
Dive into your own projects and explore GDB’s potential. Debugging is not just about fixing errors; it’s about mastering your code.