This is the mail archive of the
gdb-prs@sourceware.org
mailing list for the GDB project.
[Bug breakpoints/16115] watch -location needless software watchpoint
- From: "simon.marchi at ericsson dot com" <sourceware-bugzilla at sourceware dot org>
- To: gdb-prs at sourceware dot org
- Date: Thu, 02 Jun 2016 13:53:01 +0000
- Subject: [Bug breakpoints/16115] watch -location needless software watchpoint
- Auto-submitted: auto-generated
- References: <bug-16115-4717 at http dot sourceware dot org/bugzilla/>
https://sourceware.org/bugzilla/show_bug.cgi?id=16115
--- Comment #1 from Simon Marchi <simon.marchi at ericsson dot com> ---
Here's a copy paste (slightly modified) of an email I sent to a colleague who
hit this bug. I don't know whether it's the same cause as your example, but
it's the same symptoms. The example code we used is the following:
struct A
{
int a;
int b;
int c;
} A
static A var1 = {10, 20, 30};
static A *var2;
void main(void)
{
int i;
var2 = malloc(sizeof(A));
for (i= 0; i < 5; i++) {
var2->b = 1 + i;
}
var1.a = 0;
}
And the question was why #3 doesn't use a hardware watchpoint:
1.
(gdb) watch var2
Hardware watchpoint 2: var2
2.
(gdb) watch var1
Hardware watchpoint 2: var1
3.
(gdb) watch -location var1
Watchpoint 3: -location var1
4.
(gdb) watch -location var1.a
Hardware watchpoint 4: -location var1.a
The function can_use_hardware_watchpoint is responsible for telling whether the
expression is watchable using hardware watchpoints. It says that "watch -l
var1" can't be watched by a hardware watchpoint. I don't think that's correct,
since there's no difference in amount of bytes to watch than just "watch var1".
I started by looking at the criteria with which it decides whether a hardware
watchpoint can be used:
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=gdb/breakpoint.c;h=d2dafef0b03b2be5219417a65e28fd973036e6de;hb=885a10879eea3cf7ccbb324109a56f0bc391dcfa#l11461
Apparently, when you create a watchpoint, it starts by evaluating the
expression that you pass to it. In can_use_hardware_watchpoint, it looks at
all intermediate "struct value" created in the process of evaluating the
expression, and counts how many memory locations it needs to watch in order to
watch the expression (and will later compare that to the number of available
hardware watchpoint registers on the machine). For example, if you watch
a->b->c, and all a, b and c are stored in memory, then it will say that it's
possible to watch it with hardware. But if "a" is stored in a register, it will
say you can't, since it's not possible to watch for a register change using an
hardware watchpoint.
However, there is this special case about struct/array:
/* We only watch structs and arrays if user asked for it
explicitly, never if they just happen to appear in a
middle of some value chain. */
if (v == head
|| (TYPE_CODE (vtype) != TYPE_CODE_STRUCT
&& TYPE_CODE (vtype) != TYPE_CODE_ARRAY))
{
I think the comment makes sense because if you do
(gdb) watch var1.a
then GDB will create a struct value for var1, and then derive another for the
field a. However, you don't want to watch the whole memory of var1. Same if
you do:
(gdb) watch var2->a
In the process of evaluating the expression, some of the struct values created
will be:
- var2 (the pointer)
- the whole pointed structure
- the field "a" in the pointed structure
Here, we want to watch the pointer var2, which could change, as well as the
field "a". But we don't need nor want to watch the whole memory of the
structure. It's the same with an array, you don't want to watch the whole
array when you do "watch array[3]".
The exception is when the user actually requests to watch the structure (when
the expression evaluates to a structure), and is not simply a by-product of
evaluating the expression. GDB does that by checking "v == head". In other
words, if that struct value is the last struct value we created
(chronologically), then it must mean that it's the result of the expression
evaluation, and that it's what the user wants us to watch.
It all works fine with "watch var1", so why does it fail with "watch -l var1"?
It turns out that when you use -l/-location, GDB internally transforms the
watchpoint. It finds the address of the resulting expression (let's assume
it's 0xabcd) and creates the watchpoint with:
*(<type> *) <address>
in our case:
*(struct A*) 0xabcd
See:
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=gdb/breakpoint.c;h=d2dafef0b03b2be5219417a65e28fd973036e6de;hb=885a10879eea3cf7ccbb324109a56f0bc391dcfa#l11370
Normally, this should work fine, as the resulting expression (and the last
struct value created) should be of type "struct A". When tracking struct value
creation however, we find that there is a rogue struct value created after the
final struct value for expression:
#0 allocate_value_lazy (type=0x41b5d70) at
../../../src/binutils-gdb/gdb/value.c:931
#1 0x00000000006df33f in allocate_value (type=0x41b5d70) at
../../../src/binutils-gdb/gdb/value.c:1047
#2 0x00000000006e3853 in value_from_pointer (type=0x41b5d70, addr=6295616) at
../../../src/binutils-gdb/gdb/value.c:3628
#3 0x00000000006f13fa in value_addr (arg1=0x41afef0) at
../../../src/binutils-gdb/gdb/valops.c:1504
#4 0x0000000000898502 in gnuv3_rtti_type (value=0x41afef0,
full_p=0x7ffccfa1a2b4, top_p=0x7ffccfa1a2b8, using_enc_p=0x7ffccfa1a2bc) at
../../../src/binutils-gdb/gdb/gnu-v3-abi.c:318
#5 0x000000000089b2d9 in value_rtti_type (v=0x41afef0, full=0x7ffccfa1a2b4,
top=0x7ffccfa1a2b8, using_enc=0x7ffccfa1a2bc) at
../../../src/binutils-gdb/gdb/cp-abi.c:117
#6 0x00000000006f64cc in value_full_object (argp=0x41afef0, rtype=0x0,
xfull=0, xtop=0, xusing_enc=0) at ../../../src/binutils-gdb/gdb/valops.c:3676
#7 0x00000000006e3d8a in readjust_indirect_value_type (value=0x41afef0,
enc_type=0x4163080, original_type=0x41b5d70, original_value=0x41b8900) at
../../../src/binutils-gdb/gdb/value.c:3820
#8 0x00000000006f1647 in value_ind (arg1=0x41b8900) at
../../../src/binutils-gdb/gdb/valops.c:1581
#9 0x00000000006ec049 in evaluate_subexp_standard (expect_type=0x0,
exp=0x41afff0, pos=0x7ffccfa1a9a4, noside=EVAL_NORMAL) at
../../../src/binutils-gdb/gdb/eval.c:2535
#10 0x000000000083e857 in evaluate_subexp_c (expect_type=0x0, exp=0x41afff0,
pos=0x7ffccfa1a9a4, noside=EVAL_NORMAL) at
../../../src/binutils-gdb/gdb/c-lang.c:716
#11 0x00000000006e4c56 in evaluate_subexp (expect_type=0x0, exp=0x41afff0,
pos=0x7ffccfa1a9a4, noside=EVAL_NORMAL) at
../../../src/binutils-gdb/gdb/eval.c:80
#12 0x00000000006e4f37 in fetch_subexp_value (exp=0x41afff0, pc=0x7ffccfa1a9a4,
valp=0x7ffccfa1a9e0, resultp=0x7ffccfa1a9e8, val_chain=0x7ffccfa1a9d8,
preserve_errors=0) at ../../../src/binutils-gdb/gdb/eval.c:228
#13 0x00000000006ad2f6 in update_watchpoint (b=0x41fe060, reparse=1) at
../../../src/binutils-gdb/gdb/breakpoint.c:1971
#14 0x00000000006bf2c6 in watch_command_1 (arg=0x4231954 "", accessflag=0,
from_tty=1, just_location=1, internal=0) at
../../../src/binutils-gdb/gdb/breakpoint.c:11435
#15 0x00000000006bf623 in watch_maybe_just_location (arg=0x38d9099 "var1",
accessflag=0, from_tty=1) at ../../../src/binutils-gdb/gdb/breakpoint.c:11554
#16 0x00000000006bf648 in watch_command (arg=0x38d9096 "-l var1", from_tty=1)
at ../../../src/binutils-gdb/gdb/breakpoint.c:11560
When evaluating the dereference operator, GDB tries to be be smart and
determine whether the pointer is pointing to a derived type (I don't know why
it does this even though it's not a C++ program, maybe that's a bug). To do
so, it tries to access the vtable of the object, creating a struct value in the
process. It eventually realize that it's not a C++ object and returns the
original struct value. Because of that, the struct value that represents the
result of our expression is no longer the last one created, and will not pass
the condition in can_use_hardware_watchpoint.
A quick hack to see that it could work is if you put an early return NULL in
value_rtti_type, so that this last struct value never created:
diff --git a/gdb/cp-abi.c b/gdb/cp-abi.c index 96533b1..0460306 100644
--- a/gdb/cp-abi.c
+++ b/gdb/cp-abi.c
@@ -110,6 +110,8 @@ value_rtti_type (struct value *v, int *full, {
struct type *ret = NULL;
+ return NULL;
+
if ((current_cp_abi.rtti_type) == NULL)
return NULL;
TRY
Then, I get a hardware watchpoint when doing "watch -l var1".
It's just a quick first look, so I don't know what would be the best fix.
Maybe can_use_hardware_watchpoint should not trust that the last created struct
value (first in the linked list) is really the result of the expression
evaluation, it looks a bit fragile. Perhaps the result struct value could be
passed as a parameters from update_watchpoint.
--
You are receiving this mail because:
You are on the CC list for the bug.