# Simple debugging with gdb

### Debugging

Finding and repairing software errors is known as debugging. When a program has bugs, it can crash, behave unpredictably, or generate inaccurate results. In most cases, the steps involved in debugging are:

1. **Identifying the Bug**: This can be done through testing or user reports. The developer needs to understand the circumstances under which the bug occurs.
    
2. **Isolating the Problem**: The developer narrows down the part of the code where the bug is located. This might involve examining error messages, using debug logs, or employing debugging tools.
    
3. **Understanding the Bug**: It’s important to understand why the bug is occurring. This might involve studying the relevant section of the code and understanding how it interacts with the rest of the system.
    
4. **Fixing the Bug**: Once the bug is understood, the developer modifies the code to fix it. This also involves ensuring that the fix doesn’t introduce new bugs.
    
5. **Testing the Fix**: After the bug is fixed, the software must be tested to ensure that the bug is truly gone and that no new issues have been introduced.
    
6. **Documenting the Change**: It's good practice to document the changes made during debugging, especially in a team environment or for future reference.
    

Debugging can be challenging, especially in complex software systems, as bugs can be subtle and hard to reproduce.

### GDB (GNU Debugger)

**GDB** is the GNU Project's debugger, a powerful tool used in software development for debugging applications written in C, C++, and other programming languages. Key features and uses of GDB include:

1. **Breakpoints**: Setting breakpoints allows the program to be stopped at specific points to inspect the current state.
    
2. **Stepping Through Code**: GDB allows developers to execute code line by line (or instruction by instruction) to observe the behavior of the program at each step.
    
3. **Inspecting Variables**: It can be used to print out the current values of variables at any point during the program execution.
    
4. **Watching Variables**: GDB can watch variables for changes, which helps in understanding how and why their values are altered.
    
5. **Evaluating Expressions**: Evaluating expressions at runtime can help in understanding the program flow and logic.
    
6. **Call Stack Inspection**: It allows inspection of the call stack, helping to trace the sequence of function calls leading to a particular point in execution.
    
7. **Handling Signals and Exceptions**: GDB can catch signals and exceptions, providing a way to debug issues related to these events.
    
8. **Post-Mortem Debugging**: It can analyze core dumps, which are snapshots of a program's state at the time of a crash, allowing post-mortem debugging.
    
9. **Remote Debugging**: GDB can be used to debug programs running on different machines.
    

Though it may be intimidating at first, the command-line tool GDB provides profound insights into a program's operation. Its versatility, power, and the wealth of information it gives developers make it a popular choice.

To demonstrate how GDB can be used for debugging, let's go through a simple example. We'll create a basic C program that has a bug, and then use GDB to find and fix the bug.Certainly! Let's revisit the debugging process of the sample C program using GDB, incorporating additional commands such as `continue`, `list`, `step`, and `run`.

### Step 1: Write a Sample C Program

Here's the C program with a bug:

```c
#include <stdio.h>

int factorial(int n) {
    if (n == 1) return 1;
    return n * factorial(n - 1); // Bug: No base case for n=0
}

int main() {
    int num = 5;
    printf("Factorial of %d is %d\n", num, factorial(num));
    return 0;
}
```

Save this as `example.c`.

### Step 2: Compile with Debug Information

Compile the program to include debug information:

```bash
gcc -g example.c -o example
```

### Step 3: Start GDB

Launch GDB:

```bash
gdb ./example
```

### Step 4: Set a Breakpoint

Set a breakpoint at the start of `main`:

```bash
(gdb) break main
```

### Step 5: Run the Program

Start the program execution:

```bash
(gdb) run
```

Execution will stop at the beginning of `main`.

### Step 6: List Source Code

List the source code around the current line:

```bash
(gdb) list
```

This will display the source code near the current breakpoint.

### Step 7: Step Into Functions

Step into the `factorial` function:

```bash
(gdb) step
```

This will take you to the first line of the `factorial` function.

### Step 8: Inspect and Step Through

Inspect variables and step through the code:

```bash
(gdb) print n
(gdb) step
```

Repeat the `print` and `step` commands to observe how `n` changes with each recursive call.

### Step 9: Continue Execution

Once you've identified the bug (no base case for `n == 0`), you can let the program continue running:

```bash
(gdb) continue
```

The program will run to completion or until it hits another breakpoint.

### Step 10: Fix the Bug

Exit GDB and fix the bug in `example.c`:

```c
int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}
```

### Step 11: Rebuild and Rerun

Recompile the program and start GDB again:

```bash
gcc -g example.c -o example
gdb ./example
```

### Step 12: Debug the Fixed Program

Set the breakpoint, run the program, and use `list`, `step`, `print`, and `continue` as needed to ensure the bug is fixed.

### Step 13: Exit GDB

Once satisfied with the fix:

```bash
(gdb) quit
```

This demonstrates a more detailed debugging session using GDB, utilizing a variety of commands to navigate and inspect the program execution.
