2007-09-16 Daniel Jacobowitz <dan@codesourcery.com>

Jeff Johnston  <jjohnstn@redhat.com>

	* breakpoint.c (watchpoints_triggered): New.
	(bpstat_stop_status): Remove STOPPED_BY_WATCHPOINT argument.
	Check watchpoint_triggered instead.  Combine handling for software
	and hardware watchpoints.  Do not use target_stopped_data_address
	here.  Always check a watchpoint if its scope breakpoint triggers.
	Do not stop for thread or overlay events.  Improve check for
	triggered watchpoints without a value change.
	(watch_command_1): Insert the scope breakpoint first.  Link the
	scope breakpoint to the watchpoint.
	* breakpoint.h (enum watchpoint_triggered): New.
	(struct breakpoint): Add watchpoint_triggered.
	(bpstat_stop_status): Update prototype.
	(watchpoints_triggered): Declare.
	* infrun.c (enum infwait_status): Add infwait_step_watch_state.
	(stepped_after_stopped_by_watchpoint): Delete.
	(handle_inferior_event): Make stepped_after_stopped_by_watchpoint
	local.  Handle infwait_step_watch_state.  Update calls to
	bpstat_stop_status.  Use watchpoints_triggered to check
	watchpoints.
	* remote.c (stepped_after_stopped_by_watchpoint): Remove extern.
	(remote_stopped_data_address): Do not check it.

	* gdb.texinfo (Setting Watchpoints): Adjust warning text about
	multi-threaded watchpoints.
	* gdbint.texinfo (Watchpoints): Describe how watchpoints are
	checked.  Describe sticky notification.  Expand description
	of steppable and continuable watchpoints.
	(Watchpoints and Threads): New subsection.

	* gdb.threads/watchthreads.c (thread_function): Sleep between
	iterations.
	* gdb.threads/watchthreads.exp: Allow two watchpoints to trigger
	at once for S/390.  Generate matching fails and passes.
This commit is contained in:
Daniel Jacobowitz 2007-10-01 00:17:58 +00:00
parent d830e0e0c9
commit d983da9c3d
11 changed files with 430 additions and 223 deletions

View file

@ -1,3 +1,28 @@
2007-09-30 Daniel Jacobowitz <dan@codesourcery.com>
Jeff Johnston <jjohnstn@redhat.com>
* breakpoint.c (watchpoints_triggered): New.
(bpstat_stop_status): Remove STOPPED_BY_WATCHPOINT argument.
Check watchpoint_triggered instead. Combine handling for software
and hardware watchpoints. Do not use target_stopped_data_address
here. Always check a watchpoint if its scope breakpoint triggers.
Do not stop for thread or overlay events. Improve check for
triggered watchpoints without a value change.
(watch_command_1): Insert the scope breakpoint first. Link the
scope breakpoint to the watchpoint.
* breakpoint.h (enum watchpoint_triggered): New.
(struct breakpoint): Add watchpoint_triggered.
(bpstat_stop_status): Update prototype.
(watchpoints_triggered): Declare.
* infrun.c (enum infwait_status): Add infwait_step_watch_state.
(stepped_after_stopped_by_watchpoint): Delete.
(handle_inferior_event): Make stepped_after_stopped_by_watchpoint
local. Handle infwait_step_watch_state. Update calls to
bpstat_stop_status. Use watchpoints_triggered to check
watchpoints.
* remote.c (stepped_after_stopped_by_watchpoint): Remove extern.
(remote_stopped_data_address): Do not check it.
2007-09-29 Daniel Jacobowitz <dan@codesourcery.com> 2007-09-29 Daniel Jacobowitz <dan@codesourcery.com>
* configure.ac: Add $LIBINTL when testing libbfd. * configure.ac: Add $LIBINTL when testing libbfd.

View file

@ -2537,6 +2537,83 @@ bpstat_alloc (struct bp_location *bl, bpstat cbs /* Current "bs" value */ )
return bs; return bs;
} }
/* The target has stopped with waitstatus WS. Check if any hardware
watchpoints have triggered, according to the target. */
int
watchpoints_triggered (struct target_waitstatus *ws)
{
int stopped_by_watchpoint = STOPPED_BY_WATCHPOINT (*ws);
CORE_ADDR addr;
struct breakpoint *b;
if (!stopped_by_watchpoint)
{
/* We were not stopped by a watchpoint. Mark all watchpoints
as not triggered. */
ALL_BREAKPOINTS (b)
if (b->type == bp_hardware_watchpoint
|| b->type == bp_read_watchpoint
|| b->type == bp_access_watchpoint)
b->watchpoint_triggered = watch_triggered_no;
return 0;
}
if (!target_stopped_data_address (&current_target, &addr))
{
/* We were stopped by a watchpoint, but we don't know where.
Mark all watchpoints as unknown. */
ALL_BREAKPOINTS (b)
if (b->type == bp_hardware_watchpoint
|| b->type == bp_read_watchpoint
|| b->type == bp_access_watchpoint)
b->watchpoint_triggered = watch_triggered_unknown;
return stopped_by_watchpoint;
}
/* The target could report the data address. Mark watchpoints
affected by this data address as triggered, and all others as not
triggered. */
ALL_BREAKPOINTS (b)
if (b->type == bp_hardware_watchpoint
|| b->type == bp_read_watchpoint
|| b->type == bp_access_watchpoint)
{
struct value *v;
b->watchpoint_triggered = watch_triggered_no;
for (v = b->val_chain; v; v = value_next (v))
{
if (VALUE_LVAL (v) == lval_memory && ! value_lazy (v))
{
struct type *vtype = check_typedef (value_type (v));
if (v == b->val_chain
|| (TYPE_CODE (vtype) != TYPE_CODE_STRUCT
&& 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;
}
/* Possible return values for watchpoint_check (this can't be an enum /* Possible return values for watchpoint_check (this can't be an enum
because of check_errors). */ because of check_errors). */
/* The watchpoint has been deleted. */ /* The watchpoint has been deleted. */
@ -2655,11 +2732,9 @@ which its expression is valid.\n");
} }
/* Get a bpstat associated with having just stopped at address /* Get a bpstat associated with having just stopped at address
BP_ADDR in thread PTID. STOPPED_BY_WATCHPOINT is 1 if the BP_ADDR in thread PTID.
target thinks we stopped due to a hardware watchpoint, 0 if we
know we did not trigger a hardware watchpoint, and -1 if we do not know. */
/* Determine whether we stopped at a breakpoint, etc, or whether we Determine whether we stopped at a breakpoint, etc, or whether we
don't understand this stop. Result is a chain of bpstat's such that: don't understand this stop. Result is a chain of bpstat's such that:
if we don't understand the stop, the result is a null pointer. if we don't understand the stop, the result is a null pointer.
@ -2674,7 +2749,7 @@ which its expression is valid.\n");
commands, FIXME??? fields. */ commands, FIXME??? fields. */
bpstat bpstat
bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid, int stopped_by_watchpoint) bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid)
{ {
struct breakpoint *b = NULL; struct breakpoint *b = NULL;
struct bp_location *bl; struct bp_location *bl;
@ -2712,16 +2787,17 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid, int stopped_by_watchpoint)
continue; continue;
} }
/* Continuable hardware watchpoints are treated as non-existent if the /* Continuable hardware watchpoints are treated as non-existent if the
reason we stopped wasn't a hardware watchpoint (we didn't stop on reason we stopped wasn't a hardware watchpoint (we didn't stop on
some data address). Otherwise gdb won't stop on a break instruction some data address). Otherwise gdb won't stop on a break instruction
in the code (not from a breakpoint) when a hardware watchpoint has in the code (not from a breakpoint) when a hardware watchpoint has
been defined. */ been defined. Also skip watchpoints which we know did not trigger
(did not match the data address). */
if ((b->type == bp_hardware_watchpoint if ((b->type == bp_hardware_watchpoint
|| b->type == bp_read_watchpoint || b->type == bp_read_watchpoint
|| b->type == bp_access_watchpoint) || b->type == bp_access_watchpoint)
&& !stopped_by_watchpoint) && b->watchpoint_triggered == watch_triggered_no)
continue; continue;
if (b->type == bp_hardware_breakpoint) if (b->type == bp_hardware_breakpoint)
@ -2787,82 +2863,33 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid, int stopped_by_watchpoint)
bs->stop = 1; bs->stop = 1;
bs->print = 1; bs->print = 1;
if (b->type == bp_watchpoint || if (b->type == bp_watchpoint
b->type == bp_hardware_watchpoint) || b->type == bp_read_watchpoint
{ || b->type == bp_access_watchpoint
char *message = xstrprintf ("Error evaluating expression for watchpoint %d\n", || b->type == bp_hardware_watchpoint)
b->number);
struct cleanup *cleanups = make_cleanup (xfree, message);
int e = catch_errors (watchpoint_check, bs, message,
RETURN_MASK_ALL);
do_cleanups (cleanups);
switch (e)
{
case WP_DELETED:
/* We've already printed what needs to be printed. */
/* Actually this is superfluous, because by the time we
call print_it_typical() the wp will be already deleted,
and the function will return immediately. */
bs->print_it = print_it_done;
/* Stop. */
break;
case WP_VALUE_CHANGED:
/* Stop. */
++(b->hit_count);
break;
case WP_VALUE_NOT_CHANGED:
/* Don't stop. */
bs->print_it = print_it_noop;
bs->stop = 0;
continue;
default:
/* Can't happen. */
/* FALLTHROUGH */
case 0:
/* Error from catch_errors. */
printf_filtered (_("Watchpoint %d deleted.\n"), b->number);
if (b->related_breakpoint)
b->related_breakpoint->disposition = disp_del_at_next_stop;
b->disposition = disp_del_at_next_stop;
/* We've already printed what needs to be printed. */
bs->print_it = print_it_done;
/* Stop. */
break;
}
}
else if (b->type == bp_read_watchpoint ||
b->type == bp_access_watchpoint)
{ {
CORE_ADDR addr; CORE_ADDR addr;
struct value *v; struct value *v;
int found = 0; int must_check_value = 0;
if (!target_stopped_data_address (&current_target, &addr)) if (b->type == bp_watchpoint)
continue; /* For a software watchpoint, we must always check the
for (v = b->val_chain; v; v = value_next (v)) watched value. */
{ must_check_value = 1;
if (VALUE_LVAL (v) == lval_memory else if (b->watchpoint_triggered == watch_triggered_yes)
&& ! value_lazy (v)) /* We have a hardware watchpoint (read, write, or access)
{ and the target earlier reported an address watched by
struct type *vtype = check_typedef (value_type (v)); this watchpoint. */
must_check_value = 1;
else if (b->watchpoint_triggered == watch_triggered_unknown
&& b->type == bp_hardware_watchpoint)
/* We were stopped by a hardware watchpoint, but the target could
not report the data address. We must check the watchpoint's
value. Access and read watchpoints are out of luck; without
a data address, we can't figure it out. */
must_check_value = 1;
if (v == b->val_chain if (must_check_value)
|| (TYPE_CODE (vtype) != TYPE_CODE_STRUCT
&& 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)))
found = 1;
}
}
}
if (found)
{ {
char *message = xstrprintf ("Error evaluating expression for watchpoint %d\n", char *message = xstrprintf ("Error evaluating expression for watchpoint %d\n",
b->number); b->number);
@ -2890,6 +2917,15 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid, int stopped_by_watchpoint)
++(b->hit_count); ++(b->hit_count);
break; break;
case WP_VALUE_NOT_CHANGED: case WP_VALUE_NOT_CHANGED:
if (b->type == bp_hardware_watchpoint
|| b->type == bp_watchpoint)
{
/* Don't stop: write watchpoints shouldn't fire if
the value hasn't changed. */
bs->print_it = print_it_noop;
bs->stop = 0;
continue;
}
/* Stop. */ /* Stop. */
++(b->hit_count); ++(b->hit_count);
break; break;
@ -2906,12 +2942,12 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid, int stopped_by_watchpoint)
break; break;
} }
} }
else /* found == 0 */ else /* must_check_value == 0 */
{ {
/* This is a case where some watchpoint(s) triggered, /* This is a case where some watchpoint(s) triggered, but
but not at the address of this watchpoint (FOUND not at the address of this watchpoint, or else no
was left zero). So don't print anything for this watchpoint triggered after all. So don't print
watchpoint. */ anything for this watchpoint. */
bs->print_it = print_it_noop; bs->print_it = print_it_noop;
bs->stop = 0; bs->stop = 0;
continue; continue;
@ -2933,6 +2969,13 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid, int stopped_by_watchpoint)
{ {
int value_is_zero = 0; int value_is_zero = 0;
/* If this is a scope breakpoint, mark the associated
watchpoint as triggered so that we will handle the
out-of-scope event. We'll get to the watchpoint next
iteration. */
if (b->type == bp_watchpoint_scope)
b->related_breakpoint->watchpoint_triggered = watch_triggered_yes;
if (bl->cond) if (bl->cond)
{ {
/* Need to select the frame, with all that implies /* Need to select the frame, with all that implies
@ -2963,6 +3006,9 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid, int stopped_by_watchpoint)
annotate_ignore_count_change (); annotate_ignore_count_change ();
bs->stop = 0; bs->stop = 0;
} }
else if (b->type == bp_thread_event || b->type == bp_overlay_event)
/* We do not stop for these. */
bs->stop = 0;
else else
{ {
/* We will stop here */ /* We will stop here */
@ -2989,17 +3035,27 @@ bpstat_stop_status (CORE_ADDR bp_addr, ptid_t ptid, int stopped_by_watchpoint)
bs->next = NULL; /* Terminate the chain */ bs->next = NULL; /* Terminate the chain */
bs = root_bs->next; /* Re-grab the head of the chain */ bs = root_bs->next; /* Re-grab the head of the chain */
/* The value of a hardware watchpoint hasn't changed, but the /* If we aren't stopping, the value of some hardware watchpoint may
intermediate memory locations we are watching may have. */ not have changed, but the intermediate memory locations we are
if (bs && !bs->stop && watching may have. Don't bother if we're stopping; this will get
(b->type == bp_hardware_watchpoint || done later. */
b->type == bp_read_watchpoint || for (bs = root_bs->next; bs != NULL; bs = bs->next)
b->type == bp_access_watchpoint)) if (bs->stop)
{ break;
remove_breakpoints ();
insert_breakpoints (); if (bs == NULL)
} for (bs = root_bs->next; bs != NULL; bs = bs->next)
return bs; if (!bs->stop
&& (bs->breakpoint_at->owner->type == bp_hardware_watchpoint
|| bs->breakpoint_at->owner->type == bp_read_watchpoint
|| bs->breakpoint_at->owner->type == bp_access_watchpoint))
{
remove_breakpoints ();
insert_breakpoints ();
break;
}
return root_bs->next;
} }
/* Tell what to do about this bpstat. */ /* Tell what to do about this bpstat. */
@ -5965,7 +6021,7 @@ stopat_command (char *arg, int from_tty)
static void static void
watch_command_1 (char *arg, int accessflag, int from_tty) watch_command_1 (char *arg, int accessflag, int from_tty)
{ {
struct breakpoint *b; struct breakpoint *b, *scope_breakpoint = NULL;
struct symtab_and_line sal; struct symtab_and_line sal;
struct expression *exp; struct expression *exp;
struct block *exp_valid_block; struct block *exp_valid_block;
@ -6043,6 +6099,37 @@ watch_command_1 (char *arg, int accessflag, int from_tty)
if (!mem_cnt || target_resources_ok <= 0) if (!mem_cnt || target_resources_ok <= 0)
bp_type = bp_watchpoint; bp_type = bp_watchpoint;
frame = block_innermost_frame (exp_valid_block);
if (frame)
prev_frame = get_prev_frame (frame);
else
prev_frame = NULL;
/* If the expression is "local", then set up a "watchpoint scope"
breakpoint at the point where we've left the scope of the watchpoint
expression. Create the scope breakpoint before the watchpoint, so
that we will encounter it first in bpstat_stop_status. */
if (innermost_block && prev_frame)
{
scope_breakpoint = create_internal_breakpoint (get_frame_pc (prev_frame),
bp_watchpoint_scope);
scope_breakpoint->enable_state = bp_enabled;
/* Automatically delete the breakpoint when it hits. */
scope_breakpoint->disposition = disp_del;
/* Only break in the proper frame (help with recursion). */
scope_breakpoint->frame_id = get_frame_id (prev_frame);
/* Set the address at which we will stop. */
scope_breakpoint->loc->requested_address
= get_frame_pc (prev_frame);
scope_breakpoint->loc->address
= adjust_breakpoint_address (scope_breakpoint->loc->requested_address,
scope_breakpoint->type);
}
/* Now set up the breakpoint. */ /* Now set up the breakpoint. */
b = set_raw_breakpoint (sal, bp_type); b = set_raw_breakpoint (sal, bp_type);
set_breakpoint_count (breakpoint_count + 1); set_breakpoint_count (breakpoint_count + 1);
@ -6058,48 +6145,19 @@ watch_command_1 (char *arg, int accessflag, int from_tty)
else else
b->cond_string = 0; b->cond_string = 0;
frame = block_innermost_frame (exp_valid_block);
if (frame) if (frame)
{ b->watchpoint_frame = get_frame_id (frame);
prev_frame = get_prev_frame (frame);
b->watchpoint_frame = get_frame_id (frame);
}
else else
memset (&b->watchpoint_frame, 0, sizeof (b->watchpoint_frame));
if (scope_breakpoint != NULL)
{ {
memset (&b->watchpoint_frame, 0, sizeof (b->watchpoint_frame)); /* The scope breakpoint is related to the watchpoint. We will
need to act on them together. */
b->related_breakpoint = scope_breakpoint;
scope_breakpoint->related_breakpoint = b;
} }
/* If the expression is "local", then set up a "watchpoint scope"
breakpoint at the point where we've left the scope of the watchpoint
expression. */
if (innermost_block)
{
if (prev_frame)
{
struct breakpoint *scope_breakpoint;
scope_breakpoint = create_internal_breakpoint (get_frame_pc (prev_frame),
bp_watchpoint_scope);
scope_breakpoint->enable_state = bp_enabled;
/* Automatically delete the breakpoint when it hits. */
scope_breakpoint->disposition = disp_del;
/* Only break in the proper frame (help with recursion). */
scope_breakpoint->frame_id = get_frame_id (prev_frame);
/* Set the address at which we will stop. */
scope_breakpoint->loc->requested_address
= get_frame_pc (prev_frame);
scope_breakpoint->loc->address
= adjust_breakpoint_address (scope_breakpoint->loc->requested_address,
scope_breakpoint->type);
/* The scope breakpoint is related to the watchpoint. We
will need to act on them together. */
b->related_breakpoint = scope_breakpoint;
}
}
value_free_to_mark (mark); value_free_to_mark (mark);
mention (b); mention (b);
} }

View file

@ -318,6 +318,19 @@ struct breakpoint_ops
void (*print_mention) (struct breakpoint *); void (*print_mention) (struct breakpoint *);
}; };
enum watchpoint_triggered
{
/* This watchpoint definitely did not trigger. */
watch_triggered_no = 0,
/* Some hardware watchpoint triggered, and it might have been this
one, but we do not know which it was. */
watch_triggered_unknown,
/* This hardware watchpoint definitely did trigger. */
watch_triggered_yes
};
/* Note that the ->silent field is not currently used by any commands /* Note that the ->silent field is not currently used by any commands
(though the code is in there if it was to be, and set_raw_breakpoint (though the code is in there if it was to be, and set_raw_breakpoint
does set it to 0). I implemented it because I thought it would be does set it to 0). I implemented it because I thought it would be
@ -395,6 +408,10 @@ struct breakpoint
should be evaluated on the outermost frame. */ should be evaluated on the outermost frame. */
struct frame_id watchpoint_frame; struct frame_id watchpoint_frame;
/* For hardware watchpoints, the triggered status according to the
hardware. */
enum watchpoint_triggered watchpoint_triggered;
/* Thread number for thread-specific breakpoint, or -1 if don't care */ /* Thread number for thread-specific breakpoint, or -1 if don't care */
int thread; int thread;
@ -459,8 +476,7 @@ extern void bpstat_clear (bpstat *);
is part of the bpstat is copied as well. */ is part of the bpstat is copied as well. */
extern bpstat bpstat_copy (bpstat); extern bpstat bpstat_copy (bpstat);
extern bpstat bpstat_stop_status (CORE_ADDR pc, ptid_t ptid, extern bpstat bpstat_stop_status (CORE_ADDR pc, ptid_t ptid);
int stopped_by_watchpoint);
/* This bpstat_what stuff tells wait_for_inferior what to do with a /* This bpstat_what stuff tells wait_for_inferior what to do with a
breakpoint (a challenging task). */ breakpoint (a challenging task). */
@ -853,4 +869,8 @@ extern void remove_single_step_breakpoints (void);
extern void *deprecated_insert_raw_breakpoint (CORE_ADDR); extern void *deprecated_insert_raw_breakpoint (CORE_ADDR);
extern int deprecated_remove_raw_breakpoint (void *); extern int deprecated_remove_raw_breakpoint (void *);
/* Check if any hardware watchpoints have triggered, according to the
target. */
int watchpoints_triggered (struct target_waitstatus *);
#endif /* !defined (BREAKPOINT_H) */ #endif /* !defined (BREAKPOINT_H) */

View file

@ -1,3 +1,12 @@
2007-09-30 Daniel Jacobowitz <dan@codesourcery.com>
* gdb.texinfo (Setting Watchpoints): Adjust warning text about
multi-threaded watchpoints.
* gdbint.texinfo (Watchpoints): Describe how watchpoints are
checked. Describe sticky notification. Expand description
of steppable and continuable watchpoints.
(Watchpoints and Threads): New subsection.
2007-09-28 Vladimir Prus <vladimir@codesourcery.com> 2007-09-28 Vladimir Prus <vladimir@codesourcery.com>
* gdb.texinfo (Setting Breakpoints): Revise * gdb.texinfo (Setting Breakpoints): Revise

View file

@ -3346,20 +3346,13 @@ rerun the program, you will need to set all such watchpoints again. One
way of doing that would be to set a code breakpoint at the entry to the way of doing that would be to set a code breakpoint at the entry to the
@code{main} function and when it breaks, set all the watchpoints. @code{main} function and when it breaks, set all the watchpoints.
@quotation
@cindex watchpoints and threads @cindex watchpoints and threads
@cindex threads and watchpoints @cindex threads and watchpoints
@emph{Warning:} In multi-thread programs, watchpoints have only limited In multi-threaded programs, watchpoints will detect changes to the
usefulness. With the current watchpoint implementation, @value{GDBN} watched expression from every thread.
can only watch the value of an expression @emph{in a single thread}. If
you are confident that the expression can only change due to the current
thread's activity (and if you are also confident that no other thread
can become current), then you can use watchpoints as usual. However,
@value{GDBN} may not notice when a non-current thread's activity changes
the expression.
@c FIXME: this is almost identical to the previous paragraph. @quotation
@emph{HP-UX Warning:} In multi-thread programs, software watchpoints @emph{Warning:} In multi-threaded programs, software watchpoints
have only limited usefulness. If @value{GDBN} creates a software have only limited usefulness. If @value{GDBN} creates a software
watchpoint, it can only watch the value of an expression @emph{in a watchpoint, it can only watch the value of an expression @emph{in a
single thread}. If you are confident that the expression can only single thread}. If you are confident that the expression can only

View file

@ -660,15 +660,26 @@ section is mostly irrelevant for software watchpoints.
When the inferior stops, @value{GDBN} tries to establish, among other When the inferior stops, @value{GDBN} tries to establish, among other
possible reasons, whether it stopped due to a watchpoint being hit. possible reasons, whether it stopped due to a watchpoint being hit.
For a data-write watchpoint, it does so by evaluating, for each It first uses @code{STOPPED_BY_WATCHPOINT} to see if any watchpoint
watchpoint, the expression whose value is being watched, and testing was hit. If not, all watchpoint checking is skipped.
whether the watched value has changed. For data-read and data-access
watchpoints, @value{GDBN} needs the target to supply a primitive that Then @value{GDBN} calls @code{target_stopped_data_address} exactly
returns the address of the data that was accessed or read (see the once. This method returns the address of the watchpoint which
description of @code{target_stopped_data_address} below): if this triggered, if the target can determine it. If the triggered address
primitive returns a valid address, @value{GDBN} infers that a is available, @value{GDBN} compares the address returned by this
watchpoint triggered if it watches an expression whose evaluation uses method with each watched memory address in each active watchpoint.
that address. For data-read and data-access watchpoints, @value{GDBN} announces
every watchpoint that watches the triggered address as being hit.
For this reason, data-read and data-access watchpoints
@emph{require} that the triggered address be available; if not, read
and access watchpoints will never be considered hit. For data-write
watchpoints, if the triggered address is available, @value{GDBN}
considers only those watchpoints which match that address;
otherwise, @value{GDBN} considers all data-write watchpoints. For
each data-write watchpoint that @value{GDBN} considers, it evaluates
the expression whose value is being watched, and tests whether the
watched value has changed. Watchpoints whose watched values have
changed are announced as hit.
@value{GDBN} uses several macros and primitives to support hardware @value{GDBN} uses several macros and primitives to support hardware
watchpoints: watchpoints:
@ -721,26 +732,40 @@ These two macros should return 0 for success, non-zero for failure.
@item target_stopped_data_address (@var{addr_p}) @item target_stopped_data_address (@var{addr_p})
If the inferior has some watchpoint that triggered, place the address If the inferior has some watchpoint that triggered, place the address
associated with the watchpoint at the location pointed to by associated with the watchpoint at the location pointed to by
@var{addr_p} and return non-zero. Otherwise, return zero. Note that @var{addr_p} and return non-zero. Otherwise, return zero. This
this primitive is used by @value{GDBN} only on targets that support is required for data-read and data-access watchpoints. It is
data-read or data-access type watchpoints, so targets that have not required for data-write watchpoints, but @value{GDBN} uses
support only for data-write watchpoints need not implement these it to improve handling of those also.
primitives.
@value{GDBN} will only call this method once per watchpoint stop,
immediately after calling @code{STOPPED_BY_WATCHPOINT}. If the
target's watchpoint indication is sticky, i.e., stays set after
resuming, this method should clear it. For instance, the x86 debug
control register has sticky triggered flags.
@findex HAVE_STEPPABLE_WATCHPOINT @findex HAVE_STEPPABLE_WATCHPOINT
@item HAVE_STEPPABLE_WATCHPOINT @item HAVE_STEPPABLE_WATCHPOINT
If defined to a non-zero value, it is not necessary to disable a If defined to a non-zero value, it is not necessary to disable a
watchpoint to step over it. watchpoint to step over it. Like @code{gdbarch_have_nonsteppable_watchpoint},
this is usually set when watchpoints trigger at the instruction
which will perform an interesting read or write. It should be
set if there is a temporary disable bit which allows the processor
to step over the interesting instruction without raising the
watchpoint exception again.
@findex gdbarch_have_nonsteppable_watchpoint @findex gdbarch_have_nonsteppable_watchpoint
@item int gdbarch_have_nonsteppable_watchpoint (@var{gdbarch}) @item int gdbarch_have_nonsteppable_watchpoint (@var{gdbarch})
If it returns a non-zero value, @value{GDBN} should disable a If it returns a non-zero value, @value{GDBN} should disable a
watchpoint to step the inferior over it. watchpoint to step the inferior over it. This is usually set when
watchpoints trigger at the instruction which will perform an
interesting read or write.
@findex HAVE_CONTINUABLE_WATCHPOINT @findex HAVE_CONTINUABLE_WATCHPOINT
@item HAVE_CONTINUABLE_WATCHPOINT @item HAVE_CONTINUABLE_WATCHPOINT
If defined to a non-zero value, it is possible to continue the If defined to a non-zero value, it is possible to continue the
inferior after a watchpoint has been hit. inferior after a watchpoint has been hit. This is usually set
when watchpoints trigger at the instruction following an interesting
read or write.
@findex CANNOT_STEP_HW_WATCHPOINTS @findex CANNOT_STEP_HW_WATCHPOINTS
@item CANNOT_STEP_HW_WATCHPOINTS @item CANNOT_STEP_HW_WATCHPOINTS
@ -763,6 +788,32 @@ determine for sure whether the inferior stopped due to a watchpoint,
it could return non-zero ``just in case''. it could return non-zero ``just in case''.
@end table @end table
@subsection Watchpoints and Threads
@cindex watchpoints, with threads
@value{GDBN} only supports process-wide watchpoints, which trigger
in all threads. @value{GDBN} uses the thread ID to make watchpoints
act as if they were thread-specific, but it cannot set hardware
watchpoints that only trigger in a specific thread. Therefore, even
if the target supports threads, per-thread debug registers, and
watchpoints which only affect a single thread, it should set the
per-thread debug registers for all threads to the same value. On
@sc{gnu}/Linux native targets, this is accomplished by using
@code{ALL_LWPS} in @code{target_insert_watchpoint} and
@code{target_remove_watchpoint} and by using
@code{linux_set_new_thread} to register a handler for newly created
threads.
@value{GDBN}'s @sc{gnu}/Linux support only reports a single event
at a time, although multiple events can trigger simultaneously for
multi-threaded programs. When multiple events occur, @file{linux-nat.c}
queues subsequent events and returns them the next time the program
is resumed. This means that @code{STOPPED_BY_WATCHPOINT} and
@code{target_stopped_data_address} only need to consult the current
thread's state---the thread indicated by @code{inferior_ptid}. If
two threads have hit watchpoints simultaneously, those routines
will be called a second time for the second thread.
@subsection x86 Watchpoints @subsection x86 Watchpoints
@cindex x86 debug registers @cindex x86 debug registers
@cindex watchpoints, on x86 @cindex watchpoints, on x86

View file

@ -881,6 +881,7 @@ enum infwait_states
{ {
infwait_normal_state, infwait_normal_state,
infwait_thread_hop_state, infwait_thread_hop_state,
infwait_step_watch_state,
infwait_nonstep_watch_state infwait_nonstep_watch_state
}; };
@ -1220,17 +1221,12 @@ adjust_pc_after_break (struct execution_control_state *ecs)
by an event from the inferior, figure out what it means and take by an event from the inferior, figure out what it means and take
appropriate action. */ appropriate action. */
int stepped_after_stopped_by_watchpoint;
void void
handle_inferior_event (struct execution_control_state *ecs) handle_inferior_event (struct execution_control_state *ecs)
{ {
/* NOTE: bje/2005-05-02: If you're looking at this code and thinking
that the variable stepped_after_stopped_by_watchpoint isn't used,
then you're wrong! See remote.c:remote_stopped_data_address. */
int sw_single_step_trap_p = 0; int sw_single_step_trap_p = 0;
int stopped_by_watchpoint = -1; /* Mark as unknown. */ int stopped_by_watchpoint;
int stepped_after_stopped_by_watchpoint = 0;
/* Cache the last pid/waitstatus. */ /* Cache the last pid/waitstatus. */
target_last_wait_ptid = ecs->ptid; target_last_wait_ptid = ecs->ptid;
@ -1250,7 +1246,14 @@ handle_inferior_event (struct execution_control_state *ecs)
case infwait_normal_state: case infwait_normal_state:
if (debug_infrun) if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: infwait_normal_state\n"); fprintf_unfiltered (gdb_stdlog, "infrun: infwait_normal_state\n");
stepped_after_stopped_by_watchpoint = 0; break;
case infwait_step_watch_state:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
"infrun: infwait_step_watch_state\n");
stepped_after_stopped_by_watchpoint = 1;
break; break;
case infwait_nonstep_watch_state: case infwait_nonstep_watch_state:
@ -1435,7 +1438,7 @@ handle_inferior_event (struct execution_control_state *ecs)
stop_pc = read_pc (); stop_pc = read_pc ();
stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid, 0); stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
ecs->random_signal = !bpstat_explains_signal (stop_bpstat); ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
@ -1483,7 +1486,7 @@ handle_inferior_event (struct execution_control_state *ecs)
ecs->saved_inferior_ptid = inferior_ptid; ecs->saved_inferior_ptid = inferior_ptid;
inferior_ptid = ecs->ptid; inferior_ptid = ecs->ptid;
stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid, 0); stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
ecs->random_signal = !bpstat_explains_signal (stop_bpstat); ecs->random_signal = !bpstat_explains_signal (stop_bpstat);
inferior_ptid = ecs->saved_inferior_ptid; inferior_ptid = ecs->saved_inferior_ptid;
@ -1796,24 +1799,20 @@ handle_inferior_event (struct execution_control_state *ecs)
singlestep_breakpoints_inserted_p = 0; singlestep_breakpoints_inserted_p = 0;
} }
/* It may not be necessary to disable the watchpoint to stop over if (stepped_after_stopped_by_watchpoint)
it. For example, the PA can (with some kernel cooperation) stopped_by_watchpoint = 0;
single step over a watchpoint without disabling the watchpoint. */ else
if (HAVE_STEPPABLE_WATCHPOINT && STOPPED_BY_WATCHPOINT (ecs->ws)) stopped_by_watchpoint = watchpoints_triggered (&ecs->ws);
/* If necessary, step over this watchpoint. We'll be back to display
it in a moment. */
if (stopped_by_watchpoint
&& (HAVE_STEPPABLE_WATCHPOINT
|| gdbarch_have_nonsteppable_watchpoint (current_gdbarch)))
{ {
if (debug_infrun) if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: STOPPED_BY_WATCHPOINT\n"); fprintf_unfiltered (gdb_stdlog, "infrun: STOPPED_BY_WATCHPOINT\n");
resume (1, 0);
prepare_to_wait (ecs);
return;
}
/* It is far more common to need to disable a watchpoint to step
the inferior over it. FIXME. What else might a debug
register or page protection watchpoint scheme need here? */
if (gdbarch_have_nonsteppable_watchpoint (current_gdbarch)
&& STOPPED_BY_WATCHPOINT (ecs->ws))
{
/* At this point, we are stopped at an instruction which has /* At this point, we are stopped at an instruction which has
attempted to write to a piece of memory under control of attempted to write to a piece of memory under control of
a watchpoint. The instruction hasn't actually executed a watchpoint. The instruction hasn't actually executed
@ -1823,31 +1822,31 @@ handle_inferior_event (struct execution_control_state *ecs)
In order to make watchpoints work `right', we really need In order to make watchpoints work `right', we really need
to complete the memory write, and then evaluate the to complete the memory write, and then evaluate the
watchpoint expression. The following code does that by watchpoint expression. We do this by single-stepping the
removing the watchpoint (actually, all watchpoints and target.
breakpoints), single-stepping the target, re-inserting
watchpoints, and then falling through to let normal
single-step processing handle proceed. Since this
includes evaluating watchpoints, things will come to a
stop in the correct manner. */
if (debug_infrun) It may not be necessary to disable the watchpoint to stop over
fprintf_unfiltered (gdb_stdlog, "infrun: STOPPED_BY_WATCHPOINT\n"); it. For example, the PA can (with some kernel cooperation)
remove_breakpoints (); single step over a watchpoint without disabling the watchpoint.
It is far more common to need to disable a watchpoint to step
the inferior over it. If we have non-steppable watchpoints,
we must disable the current watchpoint; it's simplest to
disable all watchpoints and breakpoints. */
if (!HAVE_STEPPABLE_WATCHPOINT)
remove_breakpoints ();
registers_changed (); registers_changed ();
target_resume (ecs->ptid, 1, TARGET_SIGNAL_0); /* Single step */ target_resume (ecs->ptid, 1, TARGET_SIGNAL_0); /* Single step */
ecs->waiton_ptid = ecs->ptid; ecs->waiton_ptid = ecs->ptid;
ecs->wp = &(ecs->ws); if (HAVE_STEPPABLE_WATCHPOINT)
ecs->infwait_state = infwait_nonstep_watch_state; ecs->infwait_state = infwait_step_watch_state;
else
ecs->infwait_state = infwait_nonstep_watch_state;
prepare_to_wait (ecs); prepare_to_wait (ecs);
return; return;
} }
/* It may be possible to simply continue after a watchpoint. */
if (HAVE_CONTINUABLE_WATCHPOINT)
stopped_by_watchpoint = STOPPED_BY_WATCHPOINT (ecs->ws);
ecs->stop_func_start = 0; ecs->stop_func_start = 0;
ecs->stop_func_end = 0; ecs->stop_func_end = 0;
ecs->stop_func_name = 0; ecs->stop_func_name = 0;
@ -1969,8 +1968,7 @@ handle_inferior_event (struct execution_control_state *ecs)
else else
{ {
/* See if there is a breakpoint at the current PC. */ /* See if there is a breakpoint at the current PC. */
stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid, stop_bpstat = bpstat_stop_status (stop_pc, ecs->ptid);
stopped_by_watchpoint);
/* Following in case break condition called a /* Following in case break condition called a
function. */ function. */

View file

@ -5406,14 +5406,11 @@ remote_stopped_by_watchpoint (void)
return remote_stopped_by_watchpoint_p; return remote_stopped_by_watchpoint_p;
} }
extern int stepped_after_stopped_by_watchpoint;
static int static int
remote_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p) remote_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p)
{ {
int rc = 0; int rc = 0;
if (remote_stopped_by_watchpoint () if (remote_stopped_by_watchpoint ())
|| stepped_after_stopped_by_watchpoint)
{ {
*addr_p = remote_watch_data_address; *addr_p = remote_watch_data_address;
rc = 1; rc = 1;

View file

@ -1,3 +1,10 @@
2007-09-30 Daniel Jacobowitz <dan@codesourcery.com>
* gdb.threads/watchthreads.c (thread_function): Sleep between
iterations.
* gdb.threads/watchthreads.exp: Allow two watchpoints to trigger
at once for S/390. Generate matching fails and passes.
2007-09-27 Vladimir Prus <vladimir@codesourcery.com> 2007-09-27 Vladimir Prus <vladimir@codesourcery.com>
* gdb.mi/var-cmd.c (do_children_tests): Initialize * gdb.mi/var-cmd.c (do_children_tests): Initialize

View file

@ -56,7 +56,7 @@ void *thread_function(void *arg) {
/* Don't run forever. Run just short of it :) */ /* Don't run forever. Run just short of it :) */
while (*myp > 0) while (*myp > 0)
{ {
(*myp) ++; /* Loop increment. */ (*myp) ++; usleep (1); /* Loop increment. */
} }
pthread_exit(NULL); pthread_exit(NULL);

View file

@ -30,6 +30,10 @@ if [target_info exists gdb,no_hardware_watchpoints] {
return 0; return 0;
} }
proc target_no_stopped_data { } {
return [istarget s390*-*-*]
}
set testfile "watchthreads" set testfile "watchthreads"
set srcfile ${testfile}.c set srcfile ${testfile}.c
set binfile ${objdir}/${subdir}/${testfile} set binfile ${objdir}/${subdir}/${testfile}
@ -61,20 +65,58 @@ gdb_test "watch args\[1\]" "Hardware watchpoint 3: args\\\[1\\\]"
set init_line [expr [gdb_get_line_number "Init value"]+1] set init_line [expr [gdb_get_line_number "Init value"]+1]
set inc_line [gdb_get_line_number "Loop increment"] set inc_line [gdb_get_line_number "Loop increment"]
set main_loc "main \\\(\\\) at .*watchthreads.c:$init_line"
set thread0_loc "thread_function \\\(arg=0x0\\\) at .*watchthreads.c:$inc_line"
set thread1_loc "thread_function \\\(arg=0x1\\\) at .*watchthreads.c:$inc_line"
# Loop and continue to allow both watchpoints to be triggered. # Loop and continue to allow both watchpoints to be triggered.
for {set i 0} {$i < 30} {incr i} { for {set i 0} {$i < 30} {incr i} {
set test_flag_0 0
set test_flag_1 0
set test_flag 0 set test_flag 0
gdb_test_multiple "continue" "threaded watch loop" { gdb_test_multiple "continue" "threaded watch loop" {
-re "Hardware watchpoint 2: args\\\[0\\\].*Old value = 0.*New value = 1.*main \\\(\\\) at .*watchthreads.c:$init_line.*$gdb_prompt $" -re "(.*Hardware watchpoint.*)$gdb_prompt $" {
{ set args_0 1; set test_flag 1 } # At least one hardware watchpoint was hit. Check if both were.
-re "Hardware watchpoint 3: args\\\[1\\\].*Old value = 0.*New value = 1.*main \\\(\\\) at .*watchthreads.c:$init_line.*$gdb_prompt $" set string $expect_out(1,string)
{ set args_1 1; set test_flag 1 }
-re "Hardware watchpoint 2: args\\\[0\\\].*Old value = $args_0.*New value = [expr $args_0+1].*in thread_function \\\(arg=0x0\\\) at .*watchthreads.c:$inc_line.*$gdb_prompt $" if [regexp "Hardware watchpoint 2: args\\\[0\\\]\[^\r\]*\r\[^\r\]*\r\[^\r\]*Old value = $args_0\[^\r\]*\r\[^\r\]*New value = [expr $args_0+1]\r" $string] {
{ set args_0 [expr $args_0+1]; set test_flag 1 } incr args_0
-re "Hardware watchpoint 3: args\\\[1\\\].*Old value = $args_1.*New value = [expr $args_1+1].*in thread_function \\\(arg=0x1\\\) at .*watchthreads.c:$inc_line.*$gdb_prompt $" incr test_flag_0
{ set args_1 [expr $args_1+1]; set test_flag 1 } }
if [regexp "Hardware watchpoint 3: args\\\[1\\\]\[^\r\]*\r\[^\r\]*\r\[^\r\]*Old value = $args_1\[^\r\]*\r\[^\r\]*New value = [expr $args_1+1]\r" $string] {
incr args_1
incr test_flag_1
}
set expected_loc "bogus location"
if { $test_flag_0 == 1 && $test_flag_1 == 0 && $args_0 == 1 } {
set expected_loc $main_loc
} elseif { $test_flag_0 == 0 && $test_flag_1 == 1 && $args_1 == 1 } {
set expected_loc $main_loc
} elseif { $test_flag_0 == 1 && $test_flag_1 == 0 } {
set expected_loc $thread0_loc
} elseif { $test_flag_0 == 0 && $test_flag_1 == 1 } {
set expected_loc $thread1_loc
} elseif { $test_flag_0 + $test_flag_1 == 2 } {
# On S/390, or any other system which can not report the
# stopped data address, it is OK to report two watchpoints
# at once in this test. Make sure the reported location
# corresponds to at least one of the watchpoints (and not,
# e.g., __nptl_create_event). On other systems, we should
# report the two watchpoints serially.
if { [target_no_stopped_data] } {
set expected_loc "($main_loc|$thread0_loc|$thread1_loc)"
}
}
if [ regexp "$expected_loc" $string ] {
set test_flag 1
} else {
fail "threaded watch loop"
}
}
} }
# If we fail above, don't bother continuing loop # If we fail above, don't bother continuing loop
if { $test_flag == 0 } { if { $test_flag == 0 } {
set i 30; set i 30;
@ -120,7 +162,14 @@ if { $args_1 > 1 } {
# Verify that all watchpoint hits are accounted for. # Verify that all watchpoint hits are accounted for.
set message "combination of threaded watchpoints = 30" set message "combination of threaded watchpoints = 30"
if { [expr $args_0+$args_1] == 30 } { if { [target_no_stopped_data] } {
# See above. If we allow two watchpoints to be hit at once, we
# may have more than 30 hits total.
set result [expr $args_0 + $args_1 >= 30]
} else {
set result [expr $args_0 + $args_1 == 30]
}
if { $result } {
pass $message pass $message
} else { } else {
fail $message fail $message