Fundamentals 55 min read

Mastering GDB: Essential Techniques for Memory Debugging and Analysis

This comprehensive guide explores GDB as a powerful debugging tool for memory analysis, covering installation, startup methods, core commands, breakpoint strategies, memory inspection, stack tracing, advanced features, remote debugging, and best practices for production environments, complete with practical code examples and step‑by‑step instructions.

Deepin Linux
Deepin Linux
Deepin Linux
Mastering GDB: Essential Techniques for Memory Debugging and Analysis

In the world of programming, memory is like a mysterious building where data and instructions are stored and executed, sometimes leading to issues such as memory leaks, out‑of‑bounds access, or abnormal usage.

GDB acts as a skilled "building manager" with powerful tools to locate and even repair these memory problems across languages like C, C++, and Python.

1. What Is GDB?

1.1 What Is GDB?

GDB (GNU Debugger) is a powerful debugging tool essential for locating and solving bugs in software.

GDB performs four main tasks:

Specify variables or conditions before program start.

Pause the program at specified locations or conditions.

Inspect the program state when paused.

Dynamically modify the execution environment.

GDB can be started directly with gdb or by attaching to a running process using gdb <pid>.

1.2 Installing and Starting GDB

Install GDB and verify with gdb -v. Compile programs with -g to include debug symbols, e.g., gcc -g -o my_program my_program.c. Start GDB with gdb my_program or attach to a process.

1.3 Using GDB

Common commands include: run (or r) to start the program. list ( l) to view source lines. break ( b) to set breakpoints. continue ( c) to resume execution. next ( n) to step over functions. step ( s) to step into functions. print ( p) to display variable values. info break ( i b) to list breakpoints.

1.4 Memory Inspection: From Variables to Raw Bytes

The print command shows variable values, automatically handling complex types. The x/<format> command examines raw memory, e.g., x/10xw 0x7fffffffde40 displays ten 4‑byte words in hexadecimal.

The disassemble command translates machine code into assembly for deeper analysis.

1.5 Breakpoints and Watchpoints

Conditional breakpoints ( break <line> if <condition>) pause execution only when a condition is met. Watchpoints ( watch <expr>) monitor variable changes and halt when they occur.

1.6 Stack Tracing

The backtrace ( bt) command prints the call stack, showing function calls and line numbers. frame <num> switches to a specific stack frame to inspect local variables.

2. Basic GDB Memory Analysis Skills

2.1 GDB Debugging Basics

GDB helps you start programs, pause at breakpoints, inspect state, and modify execution dynamically.

2.2 Memory Viewing

Use print, x, and disassemble to view memory contents, raw bytes, and assembly code.

2.3 Breakpoints and Watchpoints

Conditional breakpoints and watchpoints act as monitoring cameras to capture memory changes precisely.

2.4 Stack Tracing

Backtrace and frame commands provide a roadmap of function calls and allow inspection of each frame’s variables.

3. Advanced Topics

3.1 Advanced GDB Features

Backtrace : Use bt to view the call stack and locate where errors occur.

Dynamic Memory Detection : Load heap plugins (e.g., gdbheap.py) and use monitor heap to inspect heap usage.

Conditional Breakpoints & Watchpoints : Set breakpoints with conditions and watchpoints to monitor variable changes.

Remote Debugging : Run gdbserver :<port> /path/to/program on the remote host and connect with target remote <host>:<port> from the local GDB.

3.2 Dynamic Variable Modification (Hot‑Fix)

Use set variable to change values at runtime, e.g., set variable position = 90, to test hypotheses without recompiling.

3.3 Multithreaded Memory Contention

Use info threads to list threads and thread <id> to switch context. Watchpoints ( watch -l <expr>) monitor shared variables across threads.

3.4 Core File Analysis

Generate core dumps with ulimit -c unlimited and analyze them using gdb ./program core and bt to locate crashes.

4. Practical Cases

4.1 Case 1: C Buffer Overflow

A program copies user input into a 20‑byte buffer using strcpy, causing a crash when the input exceeds the buffer size. Using GDB, the crash is traced to strcpy. The fix adds a length check before copying.

#include <stdio.h>
#include <string.h>
int main() {
    char buffer[20];
    char input[100];
    printf("Enter string: ");
    fgets(input, sizeof(input), stdin);
    input[strcspn(input, "
")] = '\0';
    if (strlen(input) < sizeof(buffer)) {
        strcpy(buffer, input);
        printf("Copied: %s
", buffer);
    } else {
        printf("Input too long, cannot copy
");
    }
    return 0;
}

4.2 Case 2: Java Off‑Heap Memory Leak (with pmap)

Using pmap -x <PID> reveals large anonymous memory regions. Export suspicious regions with GDB's dump memory, then analyze with strings to find Netty ByteBuf allocations that were not released. The solution is to ensure release() is called on ByteBuf objects.

4.3 Case 3: Multithreaded Data Race

A C program with two threads increments a shared variable and prints it without synchronization, leading to data races. Valgrind's Helgrind reports the race, and GDB with conditional breakpoints confirms the issue. Adding a pthread_mutex resolves the race.

#include <stdio.h>
#include <pthread.h>
int shared = 0;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
void* inc(void* _) {
    for(int i=0;i<1000;i++){
        pthread_mutex_lock(&m);
        shared++;
        pthread_mutex_unlock(&m);
    }
    return NULL;
}
void* print(void* _) {
    for(int i=0;i<1000;i++){
        pthread_mutex_lock(&m);
        printf("shared=%d
", shared);
        pthread_mutex_unlock(&m);
    }
    return NULL;
}
int main(){
    pthread_t t1,t2;
    pthread_create(&t1,NULL,inc,NULL);
    pthread_create(&t2,NULL,print,NULL);
    pthread_join(t1,NULL);
    pthread_join(t2,NULL);
    pthread_mutex_destroy(&m);
    return 0;
}

5. Tool Integration

5.1 Valgrind + GDB

Use Valgrind (Memcheck for leaks, Helgrind for data races) to generate logs, then load the program in GDB and set breakpoints at addresses reported by Valgrind for deeper inspection.

5.2 pmap + GDB

pmap provides a memory map overview; identify suspicious regions and dump them with GDB ( dump memory) for offline analysis using strings and hexdump.

6. Production Debugging Best Practices

6.1 Permission Control

Ensure you have sufficient privileges (root or appropriate user) before attaching GDB to a live process to avoid service disruption.

6.2 Non‑Intrusive Debugging

Prefer gcore <PID> to capture a core dump without stopping the process, then analyze the dump offline.

6.3 Symbol Tables

Compile with -g to retain debug symbols, or load symbols later with add-symbol-file using the process’s memory map.

6.4 Scripted Debugging

Use a .gdbinit script to automate repetitive tasks such as setting breakpoints and dumping memory regions. Example:

define dump_mem
    dump memory mem.bin 0x7ffff7a00000 0x7ffff7c00000
end
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DebuggingCLinuxMemory analysisgdb
Deepin Linux
Written by

Deepin Linux

Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.