How to Catch Memory Bugs with GDB Watchpoints in C/C++
This guide explains how to use GDB watchpoints to monitor specific memory addresses, detect unexpected modifications caused by bugs such as wild pointers or multithreaded writes, and pinpoint the exact code responsible, illustrated with a C++ example that spawns a thread modifying a variable.
Why memory bugs are hard to locate
In C/C++ the programmer controls memory allocation and deallocation, which yields high performance but also introduces bugs such as dangling pointers, out‑of‑bounds accesses, mismatched new/delete or malloc/free, and data races that corrupt memory far from the original fault.
Using GDB watchpoints to observe memory changes
GDB can set a watchpoint on a specific address. When the watched memory is read, written, or both (depending on the watchpoint type), the program stops and the debugger shows the instruction that caused the change.
Example program
#include <iostream>
#include <thread>
using namespace std;
// Thread that modifies a variable
void memory_write(int* value) {
*value = 1;
}
int main() {
int a = 10;
int* c = &a;
for (int i = 0; i < 100; ++i) {
a += i; // a becomes 4960
}
cout << a << endl; // prints 4960
thread t(memory_write, c); // separate thread changes a to 1
t.join();
return 0;
}The program prints 4960, then a second thread writes the value 1 to the same variable via the pointer c.
Step‑by‑step debugging with GDB
Compile with debugging symbols and thread support, e.g. g++ -g -pthread a.cc -o a.out.
Start GDB: gdb a.out.
Set a breakpoint at the line that prints a (line 20 in the source): b a.cc:20.
Run the program ( r). When the breakpoint hits, query the address of a with p &a. Example output: $1 = (int *) 0x7fffffffe508.
Set a hardware watchpoint on that address: watch *(int*)0x7fffffffe508. GDB reports a hardware watchpoint if the CPU supports it.
Continue execution ( c). When the second thread writes to the address, GDB stops and prints something similar to:
Hardware watchpoint 2: *(int*)0x7fffffffe508
Old value = 4960
New value = 1
memory_write (value=0x7fffffffe508) at a.cc:8The output identifies the exact source line ( a.cc:8) that performed the write.
How watchpoints work
Modern CPUs provide debug registers (DR0‑DR7 on x86). A hardware watchpoint programs one of these registers with the target address and the access type (read, write, or access). When the CPU executes an instruction that accesses the watched address, it generates a trap that transfers control to the debugger.
GDB also offers rwatch (read‑only watchpoint) and awatch (read/write watchpoint). If the hardware cannot accommodate the requested size or number of watchpoints, GDB falls back to a software watchpoint, which checks the condition after each executed instruction and is therefore much slower.
Software watchpoints are unreliable in multithreaded programs because another thread may modify the watched memory between the instruction‑level checks, causing the change to be missed.
Conclusion
Hardware watchpoints in GDB allow developers to pinpoint elusive memory‑corruption bugs by stopping exactly when a specific address changes. This reveals the offending line of code and greatly simplifies debugging of complex C/C++ applications, especially those that involve multiple threads.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
