Skip to Content
Course content

193: Setting Breakpoints and Inspecting Variables in gdb

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I can't tell you how many times I've seen a developer stare at a screen for three hours, adding printf statements every two lines, trying to figure out why their program is crashing. While "printf debugging" is a rite of passage, it's like trying to perform surgery with a flashlight and a butter knife. When your program hits a segmentation fault, you don't want to guess where it happened—you want to be standing right there when it happens.

Let's look at a piece of code that looks perfectly reasonable at first glance but will crash your program in a way that's frustratingly hard to spot without a debugger.

#include <stdio.h>
#include <string.h>

void reverse_string(char *str) {
    if (!str || !*str) return;

    char *end = str;
    while (*end != '\0') {
        end++;
    }
    end--; // Move back one to point at the last character

    while (str < end) {
        char temp = *str;
        *str = *end;
        *end = temp;
        str++;
        end--;
    }
}

int main() {
    char my_text[] = "Hello GDB";
    reverse_string(my_text);
    printf("Reversed: %s\n", my_text);
    return 0;
}

The Mystery of the Occasional Segfault

In the example above, the logic for reversing the string seems sound. We find the end, then swap characters moving inward. However, imagine this was part of a larger project where reverse_string was being called on buffers that weren't always null-terminated correctly, or where the pointer arithmetic was slightly off. If end somehow started before str, or if the loop condition failed, we'd be in trouble.

Let's assume we've modified the code slightly, and now it's crashing. Instead of adding printf("Here 1\n"); printf("Here 2\n");, we're going to use gdb (the GNU Debugger). First, you must compile with the -g flag. If you don't, gdb won't have the symbol table, and you'll see memory addresses instead of your actual variable names. I've made this mistake more times than I'd like to admit.

gcc -g -o reverse_test main.c
gdb ./reverse_test

Pinpointing the Crash with Breakpoints

Once you're inside gdb, the first thing I usually do is set a breakpoint at the start of the suspect function. This tells the program: "Run normally until you hit this line, then freeze everything so I can look around."

(gdb) break reverse_string
Breakpoint 1 at 0x1149: file main.c, line 4.
(gdb) run

The program will run and then stop right at the entry of reverse_string. Now, we don't want to just run the whole thing; we want to step through it line by line. Use next (or just n) to execute the current line and move to the next one.

While stepping, you can inspect your variables. This is where the magic happens. Instead of guessing what end is, just ask gdb:

(gdb) print end
$1 = 0x7fffffffe110 "Hello GDB"
(gdb) print *end
$2 = 10  // This is the newline or null terminator

If the program crashes (a Segmentation Fault), gdb will stop exactly on the line that caused the crash. You can then use the backtrace (or bt) command to see the call stack—which is essentially a map of how you got to that specific line of code.

Watching Variables in Real Time

Sometimes a variable changes value when you don't expect it to. Rather than printing the variable after every single line, you can set a watchpoint. A watchpoint stops program execution the moment a specific variable's value changes.

(gdb) watch end
Hardware watchpoint 2: end
(gdb) continue

Now, gdb will let the program run, but it will interrupt you the second end is modified. This is an absolute lifesaver when you're dealing with complex loops or pointer arithmetic where a value is being overwritten by a rogue pointer elsewhere in your code.

To wrap up a session, you can use quit to exit. The core workflow I want you to remember is: Compile with -g → Break at the function → Step through with 'n' → Inspect with 'p' → Watch changes with 'watch'.




📋 Practical Task

Debugging the Binary Search Boundary Error

You are given a program that implements a binary search. However, the program crashes with a segmentation fault or returns the wrong index when searching for an element that doesn't exist in the array.

Your task:

  1. Copy the provided broken binary search code into a file named search.c.
  2. Compile it using gcc -g search.c -o search.
  3. Load it into gdb.
  4. Set a breakpoint at the binary_search function.
  5. Use print to inspect the low and high variables during the loop iterations.
  6. Identify the logic error in the while loop condition or the index calculation that leads to the bug.
  7. Fix the code and verify the fix by running it through gdb one last time to ensure the variables behave as expected.
# Broken Binary Search Code
#include <stdio.h>

int binary_search(int arr[], int size, int target) {
    int low = 0;
    int high = size; // Potential bug here

    while (low <= high) {
        int mid = (low + high) / 2;
        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] < target) {
            low = mid + 1;
        } else {
            high = mid - 1;
        }
    }
    return -1;
}

int main() {
    int data[] = {10, 20, 30, 40, 50};
    int target = 60; // Searching for something not there
    int result = binary_search(data, 5, target);
    printf("Result: %d\n", result);
    return 0;
}
Rating
0 0

There are no comments for now.

to be the first to leave a comment.