mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-23 03:29:47 +08:00
Use multiple locations for hardware watchpoints.
This eliminates the need to traverse value chain, doing various checks, in three different places. * breakpoint.h (struct bp_location): New fields lengths and watchpoint_type. (struct breakpoint): Remove the val_chain field. * breakpoint.c (is_hardware_watchpoint): New. (free_valchain): Remove. (update_watchpoint): New. (insert_bp_location): For hardware watchpoint, just directly insert it. (insert_breakpoints): Call update_watchpoint_locations on all watchpoints. If we have failed to insert any location of a hardware watchpoint, remove all inserted locations. (remove_breakpoint): For hardware watchpoints, directly remove location. (watchpoints_triggered): Iterate over locations. (bpstat_stop_status): Use only first location of a resource watchpoint. (delete_breakpoint): Don't call free_valchain. (print_one_breakpoint): Don't print all locations for watchpoints. (breakpoint_re_set_one): Use update_watchpoint for watchpoints.
This commit is contained in:
@ -1,3 +1,32 @@
|
|||||||
|
2008-01-29 Vladimir Prus <vladimir@codesourcery.com>
|
||||||
|
|
||||||
|
Use multiple locations for hardware watchpoints.
|
||||||
|
This eliminates the need to traverse value chain, doing
|
||||||
|
various checks, in three different places.
|
||||||
|
|
||||||
|
* breakpoint.h (struct bp_location): New fields
|
||||||
|
lengths and watchpoint_type.
|
||||||
|
(struct breakpoint): Remove the val_chain field.
|
||||||
|
* breakpoint.c (is_hardware_watchpoint): New.
|
||||||
|
(free_valchain): Remove.
|
||||||
|
(update_watchpoint): New.
|
||||||
|
(insert_bp_location): For hardware watchpoint, just
|
||||||
|
directly insert it.
|
||||||
|
(insert_breakpoints): Call update_watchpoint_locations
|
||||||
|
on all watchpoints. If we have failed to insert
|
||||||
|
any location of a hardware watchpoint, remove all inserted
|
||||||
|
locations.
|
||||||
|
(remove_breakpoint): For hardware watchpoints, directly
|
||||||
|
remove location.
|
||||||
|
(watchpoints_triggered): Iterate over locations.
|
||||||
|
(bpstat_stop_status): Use only first location of
|
||||||
|
a resource watchpoint.
|
||||||
|
(delete_breakpoint): Don't call free_valchain.
|
||||||
|
(print_one_breakpoint): Don't print all
|
||||||
|
locations for watchpoints.
|
||||||
|
(breakpoint_re_set_one): Use update_watchpoint for
|
||||||
|
watchpoints.
|
||||||
|
|
||||||
2008-01-29 Vladimir Prus <vladimir@codesourcery.com>
|
2008-01-29 Vladimir Prus <vladimir@codesourcery.com>
|
||||||
|
|
||||||
Don't reset watchpoint block on solib load.
|
Don't reset watchpoint block on solib load.
|
||||||
|
512
gdb/breakpoint.c
512
gdb/breakpoint.c
@ -200,6 +200,15 @@ static void free_bp_location (struct bp_location *loc);
|
|||||||
|
|
||||||
static void mark_breakpoints_out (void);
|
static void mark_breakpoints_out (void);
|
||||||
|
|
||||||
|
static struct bp_location *
|
||||||
|
allocate_bp_location (struct breakpoint *bpt, enum bptype bp_type);
|
||||||
|
|
||||||
|
static void
|
||||||
|
unlink_locations_from_global_list (struct breakpoint *bpt);
|
||||||
|
|
||||||
|
static int
|
||||||
|
is_hardware_watchpoint (struct breakpoint *bpt);
|
||||||
|
|
||||||
/* Prototypes for exported functions. */
|
/* Prototypes for exported functions. */
|
||||||
|
|
||||||
/* If FALSE, gdb will not use hardware support for watchpoints, even
|
/* If FALSE, gdb will not use hardware support for watchpoints, even
|
||||||
@ -808,24 +817,182 @@ insert_catchpoint (struct ui_out *uo, void *args)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper routine: free the value chain for a breakpoint (watchpoint). */
|
static int
|
||||||
|
is_hardware_watchpoint (struct breakpoint *bpt)
|
||||||
static void
|
|
||||||
free_valchain (struct bp_location *b)
|
|
||||||
{
|
{
|
||||||
struct value *v;
|
return (bpt->type == bp_hardware_watchpoint
|
||||||
struct value *n;
|
|| bpt->type == bp_read_watchpoint
|
||||||
|
|| bpt->type == bp_access_watchpoint);
|
||||||
/* Free the saved value chain. We will construct a new one
|
|
||||||
the next time the watchpoint is inserted. */
|
|
||||||
for (v = b->owner->val_chain; v; v = n)
|
|
||||||
{
|
|
||||||
n = value_next (v);
|
|
||||||
value_free (v);
|
|
||||||
}
|
|
||||||
b->owner->val_chain = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Assuming that B is a hardware breakpoint:
|
||||||
|
- Reparse watchpoint expression, is REPARSE is non-zero
|
||||||
|
- Evaluate expression and store the result in B->val
|
||||||
|
- Update the list of values that must be watched in B->loc.
|
||||||
|
|
||||||
|
If the watchpoint is disabled, do nothing. If this is
|
||||||
|
local watchpoint that is out of scope, delete it. */
|
||||||
|
static void
|
||||||
|
update_watchpoint (struct breakpoint *b, int reparse)
|
||||||
|
{
|
||||||
|
int within_current_scope;
|
||||||
|
struct value *mark = value_mark ();
|
||||||
|
struct frame_id saved_frame_id;
|
||||||
|
struct bp_location *loc;
|
||||||
|
bpstat bs;
|
||||||
|
|
||||||
|
unlink_locations_from_global_list (b);
|
||||||
|
for (loc = b->loc; loc;)
|
||||||
|
{
|
||||||
|
struct bp_location *loc_next = loc->next;
|
||||||
|
remove_breakpoint (loc, mark_uninserted);
|
||||||
|
xfree (loc);
|
||||||
|
loc = loc_next;
|
||||||
|
}
|
||||||
|
b->loc = NULL;
|
||||||
|
|
||||||
|
if (b->disposition == disp_del_at_next_stop)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Save the current frame's ID so we can restore it after
|
||||||
|
evaluating the watchpoint expression on its own frame. */
|
||||||
|
/* FIXME drow/2003-09-09: It would be nice if evaluate_expression
|
||||||
|
took a frame parameter, so that we didn't have to change the
|
||||||
|
selected frame. */
|
||||||
|
saved_frame_id = get_frame_id (get_selected_frame (NULL));
|
||||||
|
|
||||||
|
/* Determine if the watchpoint is within scope. */
|
||||||
|
if (b->exp_valid_block == NULL)
|
||||||
|
within_current_scope = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
struct frame_info *fi;
|
||||||
|
fi = frame_find_by_id (b->watchpoint_frame);
|
||||||
|
within_current_scope = (fi != NULL);
|
||||||
|
if (within_current_scope)
|
||||||
|
select_frame (fi);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (within_current_scope && reparse)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
if (b->exp)
|
||||||
|
{
|
||||||
|
xfree (b->exp);
|
||||||
|
b->exp = NULL;
|
||||||
|
}
|
||||||
|
s = b->exp_string;
|
||||||
|
b->exp = parse_exp_1 (&s, b->exp_valid_block, 0);
|
||||||
|
/* If the meaning of expression itself changed, the old value is
|
||||||
|
no longer relevant. We don't want to report a watchpoint hit
|
||||||
|
to the user when the old value and the new value may actually
|
||||||
|
be completely different objects. */
|
||||||
|
value_free (b->val);
|
||||||
|
b->val = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* If we failed to parse the expression, for example because
|
||||||
|
it refers to a global variable in a not-yet-loaded shared library,
|
||||||
|
don't try to insert watchpoint. We don't automatically delete
|
||||||
|
such watchpoint, though, since failure to parse expression
|
||||||
|
is different from out-of-scope watchpoint. */
|
||||||
|
if (within_current_scope && b->exp)
|
||||||
|
{
|
||||||
|
struct value *v, *next;
|
||||||
|
|
||||||
|
/* Evaluate the expression and make sure it's not lazy, so that
|
||||||
|
after target stops again, we have a non-lazy previous value
|
||||||
|
to compare with. Also, making the value non-lazy will fetch
|
||||||
|
intermediate values as needed, which we use to decide which
|
||||||
|
addresses to watch.
|
||||||
|
|
||||||
|
The value returned by evaluate_expression is stored in b->val.
|
||||||
|
In addition, we look at all values which were created
|
||||||
|
during evaluation, and set watchoints at addresses as needed.
|
||||||
|
Those values are explicitly deleted here. */
|
||||||
|
v = evaluate_expression (b->exp);
|
||||||
|
/* Avoid setting b->val if it's already set. The meaning of
|
||||||
|
b->val is 'the last value' user saw, and we should update
|
||||||
|
it only if we reported that last value to user. As it
|
||||||
|
happens, the code that reports it updates b->val directly. */
|
||||||
|
if (b->val == NULL)
|
||||||
|
b->val = v;
|
||||||
|
value_contents (v);
|
||||||
|
value_release_to_mark (mark);
|
||||||
|
|
||||||
|
/* Look at each value on the value chain. */
|
||||||
|
for (; v; v = next)
|
||||||
|
{
|
||||||
|
/* If it's a memory location, and GDB actually needed
|
||||||
|
its contents to evaluate the expression, then we
|
||||||
|
must watch it. */
|
||||||
|
if (VALUE_LVAL (v) == lval_memory
|
||||||
|
&& ! value_lazy (v))
|
||||||
|
{
|
||||||
|
struct type *vtype = check_typedef (value_type (v));
|
||||||
|
|
||||||
|
/* We only watch structs and arrays if user asked
|
||||||
|
for it explicitly, never if they just happen to
|
||||||
|
appear in the middle of some value chain. */
|
||||||
|
if (v == b->val
|
||||||
|
|| (TYPE_CODE (vtype) != TYPE_CODE_STRUCT
|
||||||
|
&& TYPE_CODE (vtype) != TYPE_CODE_ARRAY))
|
||||||
|
{
|
||||||
|
CORE_ADDR addr;
|
||||||
|
int len, type;
|
||||||
|
struct bp_location *loc, **tmp;
|
||||||
|
|
||||||
|
addr = VALUE_ADDRESS (v) + value_offset (v);
|
||||||
|
len = TYPE_LENGTH (value_type (v));
|
||||||
|
type = hw_write;
|
||||||
|
if (b->type == bp_read_watchpoint)
|
||||||
|
type = hw_read;
|
||||||
|
else if (b->type == bp_access_watchpoint)
|
||||||
|
type = hw_access;
|
||||||
|
|
||||||
|
loc = allocate_bp_location (b, bp_hardware_watchpoint);
|
||||||
|
for (tmp = &(b->loc); *tmp != NULL; tmp = &((*tmp)->next))
|
||||||
|
;
|
||||||
|
*tmp = loc;
|
||||||
|
loc->address = addr;
|
||||||
|
loc->length = len;
|
||||||
|
loc->watchpoint_type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next = value_next (v);
|
||||||
|
if (v != b->val)
|
||||||
|
value_free (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reparse && b->cond_string != NULL)
|
||||||
|
{
|
||||||
|
char *s = b->cond_string;
|
||||||
|
if (b->loc->cond)
|
||||||
|
{
|
||||||
|
xfree (b->loc->cond);
|
||||||
|
b->loc->cond = NULL;
|
||||||
|
}
|
||||||
|
b->loc->cond = parse_exp_1 (&s, b->exp_valid_block, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!within_current_scope)
|
||||||
|
{
|
||||||
|
printf_filtered (_("\
|
||||||
|
Hardware watchpoint %d deleted because the program has left the block \n\
|
||||||
|
in which its expression is valid.\n"),
|
||||||
|
b->number);
|
||||||
|
if (b->related_breakpoint)
|
||||||
|
b->related_breakpoint->disposition = disp_del_at_next_stop;
|
||||||
|
b->disposition = disp_del_at_next_stop;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore the selected frame. */
|
||||||
|
select_frame (frame_find_by_id (saved_frame_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Insert a low-level "breakpoint" of some type. BPT is the breakpoint.
|
/* Insert a low-level "breakpoint" of some type. BPT is the breakpoint.
|
||||||
Any error messages are printed to TMP_ERROR_STREAM; and DISABLED_BREAKS,
|
Any error messages are printed to TMP_ERROR_STREAM; and DISABLED_BREAKS,
|
||||||
PROCESS_WARNING, and HW_BREAKPOINT_ERROR are used to report problems.
|
PROCESS_WARNING, and HW_BREAKPOINT_ERROR are used to report problems.
|
||||||
@ -1016,136 +1183,10 @@ Note: automatically using hardware breakpoints for read-only addresses.\n"));
|
|||||||
watchpoints. It's not clear that it's necessary... */
|
watchpoints. It's not clear that it's necessary... */
|
||||||
&& bpt->owner->disposition != disp_del_at_next_stop)
|
&& bpt->owner->disposition != disp_del_at_next_stop)
|
||||||
{
|
{
|
||||||
/* FIXME drow/2003-09-08: This code sets multiple hardware watchpoints
|
val = target_insert_watchpoint (bpt->address,
|
||||||
based on the expression. Ideally this should happen at a higher level,
|
bpt->length,
|
||||||
and there should be one bp_location for each computed address we
|
bpt->watchpoint_type);
|
||||||
must watch. As soon as a many-to-one mapping is available I'll
|
bpt->inserted = (val != -1);
|
||||||
convert this. */
|
|
||||||
|
|
||||||
int within_current_scope;
|
|
||||||
struct value *mark = value_mark ();
|
|
||||||
struct value *v;
|
|
||||||
struct frame_id saved_frame_id;
|
|
||||||
|
|
||||||
/* Save the current frame's ID so we can restore it after
|
|
||||||
evaluating the watchpoint expression on its own frame. */
|
|
||||||
/* FIXME drow/2003-09-09: It would be nice if evaluate_expression
|
|
||||||
took a frame parameter, so that we didn't have to change the
|
|
||||||
selected frame. */
|
|
||||||
saved_frame_id = get_frame_id (get_selected_frame (NULL));
|
|
||||||
|
|
||||||
/* Determine if the watchpoint is within scope. */
|
|
||||||
if (bpt->owner->exp_valid_block == NULL)
|
|
||||||
within_current_scope = 1;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct frame_info *fi;
|
|
||||||
fi = frame_find_by_id (bpt->owner->watchpoint_frame);
|
|
||||||
within_current_scope = (fi != NULL);
|
|
||||||
if (within_current_scope)
|
|
||||||
select_frame (fi);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (within_current_scope)
|
|
||||||
{
|
|
||||||
free_valchain (bpt);
|
|
||||||
|
|
||||||
/* Evaluate the expression and cut the chain of values
|
|
||||||
produced off from the value chain.
|
|
||||||
|
|
||||||
Make sure the value returned isn't lazy; we use
|
|
||||||
laziness to determine what memory GDB actually needed
|
|
||||||
in order to compute the value of the expression. */
|
|
||||||
v = evaluate_expression (bpt->owner->exp);
|
|
||||||
value_contents (v);
|
|
||||||
value_release_to_mark (mark);
|
|
||||||
|
|
||||||
bpt->owner->val_chain = v;
|
|
||||||
bpt->inserted = 1;
|
|
||||||
|
|
||||||
/* Look at each value on the value chain. */
|
|
||||||
for (; v; v = value_next (v))
|
|
||||||
{
|
|
||||||
/* If it's a memory location, and GDB actually needed
|
|
||||||
its contents to evaluate the expression, then we
|
|
||||||
must watch it. */
|
|
||||||
if (VALUE_LVAL (v) == lval_memory
|
|
||||||
&& ! value_lazy (v))
|
|
||||||
{
|
|
||||||
struct type *vtype = check_typedef (value_type (v));
|
|
||||||
|
|
||||||
/* We only watch structs and arrays if user asked
|
|
||||||
for it explicitly, never if they just happen to
|
|
||||||
appear in the middle of some value chain. */
|
|
||||||
if (v == bpt->owner->val_chain
|
|
||||||
|| (TYPE_CODE (vtype) != TYPE_CODE_STRUCT
|
|
||||||
&& TYPE_CODE (vtype) != TYPE_CODE_ARRAY))
|
|
||||||
{
|
|
||||||
CORE_ADDR addr;
|
|
||||||
int len, type;
|
|
||||||
|
|
||||||
addr = VALUE_ADDRESS (v) + value_offset (v);
|
|
||||||
len = TYPE_LENGTH (value_type (v));
|
|
||||||
type = hw_write;
|
|
||||||
if (bpt->owner->type == bp_read_watchpoint)
|
|
||||||
type = hw_read;
|
|
||||||
else if (bpt->owner->type == bp_access_watchpoint)
|
|
||||||
type = hw_access;
|
|
||||||
|
|
||||||
val = target_insert_watchpoint (addr, len, type);
|
|
||||||
if (val == -1)
|
|
||||||
{
|
|
||||||
/* Don't exit the loop, try to insert
|
|
||||||
every value on the value chain. That's
|
|
||||||
because we will be removing all the
|
|
||||||
watches below, and removing a
|
|
||||||
watchpoint we didn't insert could have
|
|
||||||
adverse effects. */
|
|
||||||
bpt->inserted = 0;
|
|
||||||
}
|
|
||||||
val = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bpt->owner->cond_string != NULL)
|
|
||||||
{
|
|
||||||
char *s = bpt->owner->cond_string;
|
|
||||||
if (bpt->cond)
|
|
||||||
{
|
|
||||||
xfree (bpt->cond);
|
|
||||||
bpt->cond = NULL;
|
|
||||||
}
|
|
||||||
bpt->cond = parse_exp_1 (&s, bpt->owner->exp_valid_block, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Failure to insert a watchpoint on any memory value in the
|
|
||||||
value chain brings us here. */
|
|
||||||
if (!bpt->inserted)
|
|
||||||
{
|
|
||||||
remove_breakpoint (bpt, mark_uninserted);
|
|
||||||
*hw_breakpoint_error = 1;
|
|
||||||
fprintf_unfiltered (tmp_error_stream,
|
|
||||||
"Could not insert hardware watchpoint %d.\n",
|
|
||||||
bpt->owner->number);
|
|
||||||
val = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf_filtered (_("\
|
|
||||||
Hardware watchpoint %d deleted because the program has left the block \n\
|
|
||||||
in which its expression is valid.\n"),
|
|
||||||
bpt->owner->number);
|
|
||||||
if (bpt->owner->related_breakpoint)
|
|
||||||
bpt->owner->related_breakpoint->disposition = disp_del_at_next_stop;
|
|
||||||
bpt->owner->disposition = disp_del_at_next_stop;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Restore the selected frame. */
|
|
||||||
select_frame (frame_find_by_id (saved_frame_id));
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (bpt->owner->type == bp_catch_fork
|
else if (bpt->owner->type == bp_catch_fork
|
||||||
@ -1178,6 +1219,7 @@ in which its expression is valid.\n"),
|
|||||||
void
|
void
|
||||||
insert_breakpoints (void)
|
insert_breakpoints (void)
|
||||||
{
|
{
|
||||||
|
struct breakpoint *bpt;
|
||||||
struct bp_location *b, *temp;
|
struct bp_location *b, *temp;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
int val = 0;
|
int val = 0;
|
||||||
@ -1192,6 +1234,10 @@ insert_breakpoints (void)
|
|||||||
there was an error. */
|
there was an error. */
|
||||||
fprintf_unfiltered (tmp_error_stream, "Warning:\n");
|
fprintf_unfiltered (tmp_error_stream, "Warning:\n");
|
||||||
|
|
||||||
|
ALL_BREAKPOINTS (bpt)
|
||||||
|
if (is_hardware_watchpoint (bpt))
|
||||||
|
update_watchpoint (bpt, 0 /* don't reparse */);
|
||||||
|
|
||||||
ALL_BP_LOCATIONS_SAFE (b, temp)
|
ALL_BP_LOCATIONS_SAFE (b, temp)
|
||||||
{
|
{
|
||||||
if (!breakpoint_enabled (b->owner))
|
if (!breakpoint_enabled (b->owner))
|
||||||
@ -1203,19 +1249,6 @@ insert_breakpoints (void)
|
|||||||
&& !valid_thread_id (b->owner->thread))
|
&& !valid_thread_id (b->owner->thread))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* FIXME drow/2003-10-07: This code should be pushed elsewhere when
|
|
||||||
hardware watchpoints are split into multiple loc breakpoints. */
|
|
||||||
if ((b->loc_type == bp_loc_hardware_watchpoint
|
|
||||||
|| b->owner->type == bp_watchpoint) && !b->owner->val)
|
|
||||||
{
|
|
||||||
struct value *val;
|
|
||||||
val = evaluate_expression (b->owner->exp);
|
|
||||||
release_value (val);
|
|
||||||
if (value_lazy (val))
|
|
||||||
value_fetch_lazy (val);
|
|
||||||
b->owner->val = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
val = insert_bp_location (b, tmp_error_stream,
|
val = insert_bp_location (b, tmp_error_stream,
|
||||||
&disabled_breaks, &process_warning,
|
&disabled_breaks, &process_warning,
|
||||||
&hw_breakpoint_error);
|
&hw_breakpoint_error);
|
||||||
@ -1223,6 +1256,39 @@ insert_breakpoints (void)
|
|||||||
error = val;
|
error = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we failed to insert all locations of a watchpoint,
|
||||||
|
remove them, as half-inserted watchpoint is of limited use. */
|
||||||
|
ALL_BREAKPOINTS (bpt)
|
||||||
|
{
|
||||||
|
int some_failed = 0;
|
||||||
|
struct bp_location *loc;
|
||||||
|
|
||||||
|
if (!is_hardware_watchpoint (bpt))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bpt->enable_state != bp_enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (loc = bpt->loc; loc; loc = loc->next)
|
||||||
|
if (!loc->inserted)
|
||||||
|
{
|
||||||
|
some_failed = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (some_failed)
|
||||||
|
{
|
||||||
|
for (loc = bpt->loc; loc; loc = loc->next)
|
||||||
|
if (loc->inserted)
|
||||||
|
remove_breakpoint (loc, mark_uninserted);
|
||||||
|
|
||||||
|
hw_breakpoint_error = 1;
|
||||||
|
fprintf_unfiltered (tmp_error_stream,
|
||||||
|
"Could not insert hardware watchpoint %d.\n",
|
||||||
|
bpt->number);
|
||||||
|
error = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
/* If a hardware breakpoint or watchpoint was inserted, add a
|
/* If a hardware breakpoint or watchpoint was inserted, add a
|
||||||
@ -1508,46 +1574,15 @@ remove_breakpoint (struct bp_location *b, insertion_state_t is)
|
|||||||
return val;
|
return val;
|
||||||
b->inserted = (is == mark_inserted);
|
b->inserted = (is == mark_inserted);
|
||||||
}
|
}
|
||||||
else if (b->loc_type == bp_loc_hardware_watchpoint
|
else if (b->loc_type == bp_loc_hardware_watchpoint)
|
||||||
&& breakpoint_enabled (b->owner)
|
|
||||||
&& !b->duplicate)
|
|
||||||
{
|
{
|
||||||
struct value *v;
|
struct value *v;
|
||||||
struct value *n;
|
struct value *n;
|
||||||
|
|
||||||
b->inserted = (is == mark_inserted);
|
b->inserted = (is == mark_inserted);
|
||||||
/* Walk down the saved value chain. */
|
val = target_remove_watchpoint (b->address, b->length,
|
||||||
for (v = b->owner->val_chain; v; v = value_next (v))
|
b->watchpoint_type);
|
||||||
{
|
|
||||||
/* For each memory reference remove the watchpoint
|
|
||||||
at that address. */
|
|
||||||
if (VALUE_LVAL (v) == lval_memory
|
|
||||||
&& ! value_lazy (v))
|
|
||||||
{
|
|
||||||
struct type *vtype = check_typedef (value_type (v));
|
|
||||||
|
|
||||||
if (v == b->owner->val_chain
|
|
||||||
|| (TYPE_CODE (vtype) != TYPE_CODE_STRUCT
|
|
||||||
&& TYPE_CODE (vtype) != TYPE_CODE_ARRAY))
|
|
||||||
{
|
|
||||||
CORE_ADDR addr;
|
|
||||||
int len, type;
|
|
||||||
|
|
||||||
addr = VALUE_ADDRESS (v) + value_offset (v);
|
|
||||||
len = TYPE_LENGTH (value_type (v));
|
|
||||||
type = hw_write;
|
|
||||||
if (b->owner->type == bp_read_watchpoint)
|
|
||||||
type = hw_read;
|
|
||||||
else if (b->owner->type == bp_access_watchpoint)
|
|
||||||
type = hw_access;
|
|
||||||
|
|
||||||
val = target_remove_watchpoint (addr, len, type);
|
|
||||||
if (val == -1)
|
|
||||||
b->inserted = 1;
|
|
||||||
val = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Failure to remove any of the hardware watchpoints comes here. */
|
/* Failure to remove any of the hardware watchpoints comes here. */
|
||||||
if ((is == mark_uninserted) && (b->inserted))
|
if ((is == mark_uninserted) && (b->inserted))
|
||||||
warning (_("Could not remove hardware watchpoint %d."),
|
warning (_("Could not remove hardware watchpoint %d."),
|
||||||
@ -2451,33 +2486,19 @@ watchpoints_triggered (struct target_waitstatus *ws)
|
|||||||
|| b->type == bp_read_watchpoint
|
|| b->type == bp_read_watchpoint
|
||||||
|| b->type == bp_access_watchpoint)
|
|| b->type == bp_access_watchpoint)
|
||||||
{
|
{
|
||||||
|
struct bp_location *loc;
|
||||||
struct value *v;
|
struct value *v;
|
||||||
|
|
||||||
b->watchpoint_triggered = watch_triggered_no;
|
b->watchpoint_triggered = watch_triggered_no;
|
||||||
for (v = b->val_chain; v; v = value_next (v))
|
for (loc = b->loc; loc; loc = loc->next)
|
||||||
{
|
/* Exact match not required. Within range is
|
||||||
if (VALUE_LVAL (v) == lval_memory && ! value_lazy (v))
|
sufficient. */
|
||||||
{
|
if (addr >= loc->address
|
||||||
struct type *vtype = check_typedef (value_type (v));
|
&& addr < loc->address + loc->length)
|
||||||
|
{
|
||||||
if (v == b->val_chain
|
b->watchpoint_triggered = watch_triggered_yes;
|
||||||
|| (TYPE_CODE (vtype) != TYPE_CODE_STRUCT
|
break;
|
||||||
&& TYPE_CODE (vtype) != TYPE_CODE_ARRAY))
|
}
|
||||||
{
|
|
||||||
CORE_ADDR vaddr;
|
|
||||||
|
|
||||||
vaddr = VALUE_ADDRESS (v) + value_offset (v);
|
|
||||||
/* Exact match not required. Within range is
|
|
||||||
sufficient. */
|
|
||||||
if (addr >= vaddr
|
|
||||||
&& addr < vaddr + TYPE_LENGTH (value_type (v)))
|
|
||||||
{
|
|
||||||
b->watchpoint_triggered = watch_triggered_yes;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -2716,6 +2737,15 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid)
|
|||||||
&& !inferior_has_execd (PIDGET (inferior_ptid), &b->exec_pathname))
|
&& !inferior_has_execd (PIDGET (inferior_ptid), &b->exec_pathname))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* For hardware watchpoints, we look only at the first location.
|
||||||
|
The watchpoint_check function will work on entire expression,
|
||||||
|
not the individual locations. For read watchopints, the
|
||||||
|
watchpoints_triggered function have checked all locations
|
||||||
|
alrea
|
||||||
|
*/
|
||||||
|
if (b->type == bp_hardware_watchpoint && bl != b->loc)
|
||||||
|
continue;
|
||||||
|
|
||||||
/* Come here if it's a watchpoint, or if the break address matches */
|
/* Come here if it's a watchpoint, or if the break address matches */
|
||||||
|
|
||||||
bs = bpstat_alloc (bl, bs); /* Alloc a bpstat to explain stop */
|
bs = bpstat_alloc (bl, bs); /* Alloc a bpstat to explain stop */
|
||||||
@ -2909,6 +2939,10 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid)
|
|||||||
|| bs->breakpoint_at->owner->type == bp_read_watchpoint
|
|| bs->breakpoint_at->owner->type == bp_read_watchpoint
|
||||||
|| bs->breakpoint_at->owner->type == bp_access_watchpoint))
|
|| bs->breakpoint_at->owner->type == bp_access_watchpoint))
|
||||||
{
|
{
|
||||||
|
/* remove/insert can invalidate bs->breakpoint_at, if this
|
||||||
|
location is no longer used by the watchpoint. Prevent
|
||||||
|
further code from trying to use it. */
|
||||||
|
bs->breakpoint_at = NULL;
|
||||||
remove_breakpoints ();
|
remove_breakpoints ();
|
||||||
insert_breakpoints ();
|
insert_breakpoints ();
|
||||||
break;
|
break;
|
||||||
@ -3629,10 +3663,14 @@ print_one_breakpoint (struct breakpoint *b,
|
|||||||
disabled, we print it as if it had
|
disabled, we print it as if it had
|
||||||
several locations, since otherwise it's hard to
|
several locations, since otherwise it's hard to
|
||||||
represent "breakpoint enabled, location disabled"
|
represent "breakpoint enabled, location disabled"
|
||||||
situation. */
|
situation.
|
||||||
|
Note that while hardware watchpoints have
|
||||||
|
several locations internally, that's no a property
|
||||||
|
exposed to user. */
|
||||||
if (b->loc
|
if (b->loc
|
||||||
|
&& !is_hardware_watchpoint (b)
|
||||||
&& (b->loc->next || !b->loc->enabled)
|
&& (b->loc->next || !b->loc->enabled)
|
||||||
&& !ui_out_is_mi_like_p (uiout))
|
&& !ui_out_is_mi_like_p (uiout))
|
||||||
{
|
{
|
||||||
struct bp_location *loc;
|
struct bp_location *loc;
|
||||||
int n = 1;
|
int n = 1;
|
||||||
@ -6829,9 +6867,7 @@ delete_breakpoint (struct breakpoint *bpt)
|
|||||||
{
|
{
|
||||||
if (loc->inserted)
|
if (loc->inserted)
|
||||||
remove_breakpoint (loc, mark_inserted);
|
remove_breakpoint (loc, mark_inserted);
|
||||||
|
|
||||||
free_valchain (loc);
|
|
||||||
|
|
||||||
if (loc->cond)
|
if (loc->cond)
|
||||||
xfree (loc->cond);
|
xfree (loc->cond);
|
||||||
|
|
||||||
@ -7265,39 +7301,7 @@ breakpoint_re_set_one (void *bint)
|
|||||||
|
|
||||||
Don't do anything about disabled watchpoints, since they will
|
Don't do anything about disabled watchpoints, since they will
|
||||||
be reevaluated again when enabled. */
|
be reevaluated again when enabled. */
|
||||||
|
update_watchpoint (b, 1 /* reparse */);
|
||||||
if (!breakpoint_enabled (b))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (b->exp_valid_block == NULL
|
|
||||||
|| frame_find_by_id (b->watchpoint_frame) != NULL)
|
|
||||||
{
|
|
||||||
if (b->exp)
|
|
||||||
{
|
|
||||||
xfree (b->exp);
|
|
||||||
b->exp = NULL;
|
|
||||||
}
|
|
||||||
s = b->exp_string;
|
|
||||||
b->exp = parse_exp_1 (&s, b->exp_valid_block, 0);
|
|
||||||
|
|
||||||
/* Since we reparsed expression, we need to update the
|
|
||||||
value. I'm not aware of any way a single solib load or unload
|
|
||||||
can change a valid value into different valid value, but:
|
|
||||||
- even if the value is no longer valid, we have to record
|
|
||||||
this fact, so that when it becomes valid we reports this
|
|
||||||
as value change
|
|
||||||
- unloaded followed by load can change the value for sure.
|
|
||||||
|
|
||||||
We set value to NULL, and insert_breakpoints will
|
|
||||||
update the value. */
|
|
||||||
if (b->val)
|
|
||||||
value_free (b->val);
|
|
||||||
b->val = NULL;
|
|
||||||
|
|
||||||
/* Loading of new shared library change the meaning of
|
|
||||||
watchpoint condition. However, insert_bp_location will
|
|
||||||
recompute watchpoint condition anyway, nothing to do here. */
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
/* We needn't really do anything to reset these, since the mask
|
/* We needn't really do anything to reset these, since the mask
|
||||||
that requests them is unaffected by e.g., new libraries being
|
that requests them is unaffected by e.g., new libraries being
|
||||||
|
@ -273,6 +273,12 @@ struct bp_location
|
|||||||
bp_loc_other. */
|
bp_loc_other. */
|
||||||
CORE_ADDR address;
|
CORE_ADDR address;
|
||||||
|
|
||||||
|
/* For hardware watchpoints, the size of data ad ADDRESS being watches. */
|
||||||
|
int length;
|
||||||
|
|
||||||
|
/* Type of hardware watchpoint. */
|
||||||
|
enum target_hw_bp_type watchpoint_type;
|
||||||
|
|
||||||
/* For any breakpoint type with an address, this is the BFD section
|
/* For any breakpoint type with an address, this is the BFD section
|
||||||
associated with the address. Used primarily for overlay debugging. */
|
associated with the address. Used primarily for overlay debugging. */
|
||||||
asection *section;
|
asection *section;
|
||||||
@ -388,9 +394,6 @@ struct breakpoint
|
|||||||
/* Value of the watchpoint the last time we checked it. */
|
/* Value of the watchpoint the last time we checked it. */
|
||||||
struct value *val;
|
struct value *val;
|
||||||
|
|
||||||
/* Holds the value chain for a hardware watchpoint expression. */
|
|
||||||
struct value *val_chain;
|
|
||||||
|
|
||||||
/* Holds the address of the related watchpoint_scope breakpoint
|
/* Holds the address of the related watchpoint_scope breakpoint
|
||||||
when using watchpoints on local variables (might the concept
|
when using watchpoints on local variables (might the concept
|
||||||
of a related breakpoint be useful elsewhere, if not just call
|
of a related breakpoint be useful elsewhere, if not just call
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
2008-01-29 Vladimir Prus <vladimir@codesourcery.com>
|
||||||
|
|
||||||
|
* gdb.base/watchpoint-solib.exp: New.
|
||||||
|
* gdb.base/watchpoint-solib.c: New.
|
||||||
|
* gdb.base/watchpoint-solib-shr.c: New.
|
||||||
|
|
||||||
2008-01-29 Pierre Muller <muller@ics.u-strasbg.fr>
|
2008-01-29 Pierre Muller <muller@ics.u-strasbg.fr>
|
||||||
|
|
||||||
* gdb.base/gdb1056.exp: Add unsigned integer test.
|
* gdb.base/gdb1056.exp: Add unsigned integer test.
|
||||||
|
25
gdb/testsuite/gdb.base/watchpoint-solib-shr.c
Normal file
25
gdb/testsuite/gdb.base/watchpoint-solib-shr.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/* This testcase is part of GDB, the GNU debugger.
|
||||||
|
|
||||||
|
Copyright 2004, 2007 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int g = 0;
|
||||||
|
|
||||||
|
void foo (int i)
|
||||||
|
{
|
||||||
|
g = i;
|
||||||
|
}
|
68
gdb/testsuite/gdb.base/watchpoint-solib.c
Normal file
68
gdb/testsuite/gdb.base/watchpoint-solib.c
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/* This testcase is part of GDB, the GNU debugger.
|
||||||
|
|
||||||
|
Copyright 2004, 2007 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#ifdef __WIN32__
|
||||||
|
#include <windows.h>
|
||||||
|
#define dlopen(name, mode) LoadLibrary (TEXT (name))
|
||||||
|
#ifdef _WIN32_WCE
|
||||||
|
# define dlsym(handle, func) GetProcAddress (handle, TEXT (func))
|
||||||
|
#else
|
||||||
|
# define dlsym(handle, func) GetProcAddress (handle, func)
|
||||||
|
#endif
|
||||||
|
#define dlclose(handle) FreeLibrary (handle)
|
||||||
|
#define dlerror() "error %d occurred", GetLastError ()
|
||||||
|
#else
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void open_shlib ()
|
||||||
|
{
|
||||||
|
void *handle;
|
||||||
|
void (*foo) (int);
|
||||||
|
|
||||||
|
handle = dlopen (SHLIB_NAME, RTLD_LAZY);
|
||||||
|
|
||||||
|
if (!handle)
|
||||||
|
{
|
||||||
|
fprintf (stderr, dlerror ());
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
foo = (void (*)(int))dlsym (handle, "foo");
|
||||||
|
|
||||||
|
if (!foo)
|
||||||
|
{
|
||||||
|
fprintf (stderr, dlerror ());
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
foo (1); // call to foo
|
||||||
|
foo (2);
|
||||||
|
|
||||||
|
dlclose (handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
open_shlib ();
|
||||||
|
return 0;
|
||||||
|
}
|
91
gdb/testsuite/gdb.base/watchpoint-solib.exp
Normal file
91
gdb/testsuite/gdb.base/watchpoint-solib.exp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
# Copyright 2007 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
if $tracelevel then {
|
||||||
|
strace $tracelevel
|
||||||
|
}
|
||||||
|
|
||||||
|
#
|
||||||
|
# test running programs
|
||||||
|
#
|
||||||
|
set prms_id 0
|
||||||
|
set bug_id 0
|
||||||
|
|
||||||
|
if {[skip_shlib_tests]} {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: Use LoadLibrary on this target instead of dlopen.
|
||||||
|
if {[istarget arm*-*-symbianelf*]} {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
set testfile "watchpoint-solib"
|
||||||
|
set libfile "watchpoint-solib-shr"
|
||||||
|
set libname "${libfile}.sl"
|
||||||
|
set libsrcfile ${libfile}.c
|
||||||
|
set srcfile $srcdir/$subdir/$testfile.c
|
||||||
|
set binfile $objdir/$subdir/$testfile
|
||||||
|
set shlibdir ${objdir}/${subdir}
|
||||||
|
set libsrc $srcdir/$subdir/$libfile.c
|
||||||
|
set lib_sl $objdir/$subdir/$libname
|
||||||
|
|
||||||
|
if [get_compiler_info ${binfile}] {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
set lib_opts debug
|
||||||
|
set exec_opts [list debug shlib_load additional_flags=-DSHLIB_NAME\=\"${libname}\"]
|
||||||
|
|
||||||
|
if { [gdb_compile_shlib $libsrc $lib_sl $lib_opts] != ""
|
||||||
|
|| [gdb_compile $srcfile $binfile executable $exec_opts] != ""} {
|
||||||
|
untested "Couldn't compile $libsrc or $srcfile."
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start with a fresh gdb.
|
||||||
|
|
||||||
|
gdb_exit
|
||||||
|
gdb_start
|
||||||
|
gdb_reinitialize_dir $srcdir/$subdir
|
||||||
|
gdb_load ${binfile}
|
||||||
|
gdb_load_shlibs $lib_sl
|
||||||
|
|
||||||
|
if [target_info exists gdb_stub] {
|
||||||
|
gdb_step_for_stub;
|
||||||
|
}
|
||||||
|
|
||||||
|
runto_main
|
||||||
|
|
||||||
|
# Test that if we set a watchpoint on a global variable
|
||||||
|
# in a explicitly loaded shared library, and then
|
||||||
|
# re-run the application, gdb does not crash.
|
||||||
|
gdb_test_multiple "break foo" "set pending breakpoint" {
|
||||||
|
-re ".*Make breakpoint pending.*y or \\\[n\\\]. $" {
|
||||||
|
gdb_test "y" "Breakpoint.*foo.*pending." "set pending breakpoint"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gdb_test "continue" ".*Breakpoint 2.*foo.*" "continue to foo"
|
||||||
|
gdb_test "watch g" "Hardware watchpoint 3: g" "set watchpoint on g"
|
||||||
|
gdb_test "continue" ".*New value = 1.*" "continue to watchpoint hit"
|
||||||
|
rerun_to_main
|
||||||
|
gdb_test "continue" ".*Breakpoint 2.*foo.*" "continue to foo again"
|
||||||
|
gdb_test "continue" ".*New value = 1.*" "continue to watchpoint hit again"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Reference in New Issue
Block a user