Reputation: 988
For my current embedded application I am trying to put GDB watch point at a fixed memory address.
As an example, my application updates the following address: 0x10793ad0. In order to be sure which part of the code is corrupting the value, I tried
watch 0x10793ad0
Even though GDB does not print any error after this, it is not able to break during execution even though I verified the value is getting modified at between start and end of execution.
Questions:
Upvotes: 82
Views: 67155
Reputation: 35716
The right way to set watchpoint on address is watch *0x10793ad0
. See gdb doc.
You can also specify what type GDB should interpret the memory address as. For example:
watch *(unsigned char*)0x10793ad0
or
watch *(int*)0x10793ad0
Upvotes: 124
Reputation: 382822
-l/-location
example
In this answer I'd like to further highlight the -l/-location
option previously mentioned by Nathan Kidd in a comment.
If you don't use this option, then doing:
watch mylocalvar
makes GDB break as soon as mylocalvar
goes out of scope, and then automatically delete that memory watchpoint and continue execution.
-l
can therefore be very useful when you know that there is just one instance of some value being passed around by reference, typically a this
-like struct
.
Consider:
main.c
typedef struct {
int i;
} MyThing;
void MyThing_init(MyThing *s, int i) {
i += 1;
s->i = i;
i += 2;
i += 3;
}
int MyThing_get_i(MyThing *s) {
return s->i;
}
int main(void) {
MyThing s;
MyThing_init(&s, 1);
MyThing_init(&s, 2);
MyThing_init(&s, 3);
MyThing_init(&s, 4);
return MyThing_get_i(&s);
}
Compile and debug:
gcc -ggdb3 -O0 -o main.out main.c
gdb -nh -ex 'tb MyThing_init' -ex r main.out
This leaves as at:
6 i += 1;
Now if we do:
watch s->i
it shows up under:
(gdb) i b
Num Type Disp Enb Address What
2 hw watchpoint keep y s->i
then the first c
stops just after the variable is modified as desired:
(gdb) c
Continuing.
Hardware watchpoint 2: s->i
Old value = 0
New value = 2
MyThing_init (s=0x7fffffffccb4, i=2) at main.c:8
8 i += 2;
but the second c
stops when MyThing_init
returns and explains to us that it deleted the watchpoint:
(gdb) c
Continuing.
Watchpoint 2 deleted because the program has left the block in
which its expression is valid.
main () at main.c:19
19 MyThing_init(&s, 2);
which we can confirm with:
(gdb) i b
No breakpoints or watchpoints.
and so running the program with c
again will just run until it exits.
But if we instead use -l
:
watch -l s->i
which shows up under i b
as:
(gdb) i b
Num Type Disp Enb Address What
2 hw watchpoint keep y -location s->i
then the watchpoint does not get deleted, and we break on every call:
(gdb) c
Continuing.
Hardware watchpoint 2: -location s->i
Old value = 2
New value = 3
MyThing_init (s=0x7fffffffccb4, i=3) at main.c:8
8 i += 2;
(gdb) c
Continuing.
Hardware watchpoint 2: -location s->i
Old value = 3
New value = 4
MyThing_init (s=0x7fffffffccb4, i=4) at main.c:8
8 i += 2;
(gdb) c
Continuing.
Hardware watchpoint 2: -location s->i
Old value = 4
New value = 5
MyThing_init (s=0x7fffffffccb4, i=5) at main.c:8
8 i += 2;
(gdb) c
Continuing.
Hardware watchpoint 2: -location s->i
Old value = 5
New value = 32767
0x00007ffff7c44fd6 in __run_exit_handlers (status=5, listp=0x7ffff7dfe860 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at ./stdlib/exit.c:43
43 ./stdlib/exit.c: No such file or directory.
This technique is particularly powerful in conjunction with Mozilla rr reverse debugging, because then what usually happens is you are at a point of failure like:
b MyThing_get_i
and you notice a bad value for something, say s->i
, and then you want to know what was the last thing that set that bad value. Once in rr
, you can immediately answer that with:
watch -l s->i
rc
It is a thing of great beauty.
Tested on Ubuntu 23.10, GCC 13.2.0. GDB 14.
Upvotes: 5