🐛 GNU Debugger (GDB)

🐛 GNU Debugger (GDB)

🎯 Introduction to GDB

The GNU Debugger (GDB) is the most powerful and widely-used debugger for programs written in C, C++, and many other programming languages. It allows you to see what's happening inside a program while it executes or what it was doing at the moment it crashed.

💡 What GDB Can Do:

  • Start and stop your program at specific points
  • Examine variable values and memory contents
  • Step through code line by line
  • Analyze core dumps from crashed programs
  • Modify variables and memory during execution

🔄 GDB Debugging Workflow

Article content

🛠️ Setting Up for Debugging

Compilation with Debug Information

To debug effectively, compile your program with the -g flag:

$ gcc -g -o myprogram myprogram.c 
$ gcc -g -Wall -Wextra -o advanced_program advanced_program.c        

⚠️ Important: The -g flag includes debugging symbols in the executable. Without it, GDB won't be able to show source code or variable names.

📚 Essential GDB Commands

Article content

💻 Concrete Example: Debugging a Buggy Program

Sample C Program with Bugs

// File: buggy_program.c
#include <stdio.h>
#include <stdlib.h>

int factorial(int n) {
    if (n <= 1) {
        return 1;
    }
    return n * factorial(n - 1);  // Bug: no bounds checking
}

int divide_numbers(int a, int b) {
    return a / b;  // Bug: division by zero not handled
}

int main() {
    int numbers[] = {5, 0, -3, 10};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    
    printf("Testing factorial and division:\n");
    
    for (int i = 0; i < size; i++) {
        int num = numbers[i];
        printf("Number: %d\n", num);
        
        // Calculate factorial
        int fact = factorial(num);
        printf("Factorial of %d: %d\n", num, fact);
        
        // Divide by the number
        int result = divide_numbers(100, num);
        printf("100 / %d = %d\n", num, result);
        
        printf("---\n");
    }
    
    return 0;
}        

Step-by-Step Debugging Session

🚀 Complete Debugging Session:

$ gcc -g -o buggy_program buggy_program.c
$ gdb ./buggy_program

(gdb) list
1	#include <stdio.h>
2	#include <stdlib.h>
3	
4	int factorial(int n) {
5	    if (n <= 1) {
6	        return 1;
7	    }
8	    return n * factorial(n - 1);
9	}
10

(gdb) break main
Breakpoint 1 at 0x1149: file buggy_program.c, line 14.

(gdb) break divide_numbers
Breakpoint 2 at 0x1129: file buggy_program.c, line 11.

(gdb) run
Starting program: /path/to/buggy_program 

Breakpoint 1, main () at buggy_program.c:14
14	    int numbers[] = {5, 0, -3, 10};

(gdb) print numbers
$1 = {5, 0, -3, 10}

(gdb) continue
Continuing.
Testing factorial and division:
Number: 5
Factorial of 5: 120

Breakpoint 2, divide_numbers (a=100, b=5) at buggy_program.c:11
11	    return a / b;

(gdb) print a
$2 = 100

(gdb) print b
$3 = 5

(gdb) continue
Continuing.
100 / 5 = 20
---
Number: 0
Factorial of 0: 1

Breakpoint 2, divide_numbers (a=100, b=0) at buggy_program.c:11
11	    return a / b;

(gdb) print b
$4 = 0

(gdb) print a/b
Floating point exception        

🐛 Bug Found! Division by zero detected! The program will crash when trying to divide 100 by 0.

🧠 Memory and Stack Visualization

Program Memory Layout During Debugging

Article content

🔍 Advanced GDB Features

Watchpoints

Monitor when a variable changes:

(gdb) watch variable_name
(gdb) rwatch variable_name  // Read watchpoint
(gdb) awatch variable_name  // Access watchpoint        

Conditional Breakpoints

Break only when certain conditions are met:

(gdb) break 20 if x > 10
(gdb) break function_name if parameter == NULL
(gdb) condition 1 x != 0  // Add condition to existing breakpoint        

Examining Memory

(gdb) x/10x $sp        // Examine 10 hex words at stack pointer
(gdb) x/s 0x12345678   // Examine as string
(gdb) x/i $pc          // Examine as instruction
(gdb) x/20b &array[0]  // Examine 20 bytes        

Call Stack Navigation

(gdb) backtrace        // Show full call stack
(gdb) frame 2          // Switch to frame 2
(gdb) up               // Move up one frame
(gdb) down             // Move down one frame
(gdb) info frame       // Show current frame info        

🛡️ Debugging Core Dumps

When a program crashes, it may create a core dump. Here's how to analyze it:

$ ulimit -c unlimited    # Enable core dumps
$ ./buggy_program
Floating point exception (core dumped)

$ gdb ./buggy_program core
(gdb) backtrace
#0  0x0000000000001129 in divide_numbers (a=100, b=0) at buggy_program.c:11
#1  0x00000000000011a3 in main () at buggy_program.c:25

(gdb) frame 0
(gdb) print a
$1 = 100
(gdb) print b
$2 = 0        

💡 Core Dump Analysis Tips:

  • Use bt to see where the crash occurred
  • Navigate frames with frame N
  • Examine variables at crash time with print
  • Look at memory around the crash with x commands

🎭 GDB Debugging Strategies

Problem-Solving Approach

Article content

Common Debugging Scenarios

Article content

⚡ Pro Tips and Best Practices

Article content

🎯 Efficient Debugging Tips:

  • Use .gdbinit: Create a .gdbinit file in your project directory with common settings
  • Save breakpoints: Use save breakpoints filename to save your breakpoint configuration
  • Use TUI mode: Press Ctrl+X, A for a split-screen interface
  • History: Use up/down arrows to recall previous commands
  • Tab completion: Use Tab to auto-complete commands and variable names

Sample .gdbinit Configuration

# .gdbinit file
set print pretty on
set print array on
set print array-indexes on
set history save on
set history size 10000
set history filename ~/.gdb_history

# Custom commands
define hook-quit
  set confirm off
end

# Show more context when listing
set listsize 20        

⚠️ Security Note: GDB will only read .gdbinit files in your home directory by default. To use project-specific .gdbinit files, you need to add set auto-load safe-path / to your home directory's .gdbinit.

🎓 Conclusion

GDB is an incredibly powerful tool that can save you hours of debugging time once you master its features. Remember that debugging is both an art and a science – use GDB's systematic approach combined with your understanding of the code to efficiently track down bugs.

🚀 Next Steps:

  • Practice with simple programs first
  • Learn to read assembly code for deeper debugging
  • Explore GDB scripting for automated testing
  • Try GUI frontends like DDD or IDE integrations
  • Study advanced topics like remote debugging and multi-threaded programs

Hi Friends, Any other topics or concepts you are curious about, please write them down here, I will pick some to explain. 👏

Like
Reply

gdb is really a powerful tool. I found a memory corruption using gdb on one of the programs I wrote for sockets. Tweaked the pointer and the program worked like magic.

To view or add a comment, sign in

More articles by David Zhu

Others also viewed

Explore content categories