[needs doc review] Re: [RFA 3/3] Implement support for PowerPC BookE masked watchpoints

Thiago Jung Bauermann
2011-05-03T04:56:30Z

On Fri, 2011-04-29 at 19:45 +0200, Ulrich Weigand wrote:
> Thiago Jung Bauermann wrote:
>
> > > We've recently added the -location feature that already prevents
> > > GDB from fully tracking the expression, but rather just evaluate
> > > it one time for its address. Maybe one simple fix could be to
> > > restrict the "mask" feature to watch -location (or have the
> > > presence of "mask" automatically imply -location, or maybe have
> > > instead a new keyword -masked-location or so).
> >
> > -location is indeed a good fit for this patch. I don't think we need a
> > separate -masked-location keyword, so in this patch I made the mask
> > parameter imply -location. Do you think this addresses your concerns
> > regarding the semantics of masked watchpoints?
>
> OK, that's fine with me. I guess that ought to mentioned in the
> documentation somewhere ...

I updated the documentation. The following paragraphs were changing
relative to the previously approved text in the manual:

1. The last sentence was added to the paragraph below:

The @code{@r{[}mask @var{maskvalue}@r{]}} argument allows creation
of masked watchpoints, if the current architecture supports this
feature. (Currently, this is only available on PowerPC Embedded
architecture, see @ref{PowerPC Embedded}.) Specifying a mask argument
implies the @code{-location} argument.

2. The paragraph

A @dfn{masked watchpoint} specifies a mask in addition to an address
to watch. The mask specifies that some bits of an address (the bits
which are reset in the mask) should be ignored when matching the
address accessed by the inferior against the watchpoint address.
Thus, a masked watchpoint watches many addresses
simultaneously---those addresses whose unmasked bits are identical
to the unmasked bits in the watchpoint address.

became

A @dfn{masked watchpoint} specifies a mask in addition to an address
to watch. The @code{mask} argument implies the @code{-location}
argument, which means that the expression will be resolved to a memory
address at watchpoint creation time (@pxref{Set Watchpoints}.) The mask
specifies that some bits of an address (the bits which are reset in the
mask) should be ignored when matching the address accessed by the inferior
against the watchpoint address. Thus, a masked watchpoint watches many
addresses simultaneously---those addresses whose unmasked bits are
identical to the unmasked bits in the watchpoint address.

> > + {
> > + CORE_ADDR newaddr, start;
> > +
> > + if (is_masked_watchpoint (loc->owner))
> > + {
> > + newaddr = addr & loc->owner->hw_wp_mask;
> > + start = loc->address & loc->owner->hw_wp_mask;
> > + }
> > + else
> > + {
> > + newaddr = addr;
> > + start = loc->address;
> > + }
> > +
> > + /* Exact match not required. Within range is
> > + sufficient. */
> > + if (target_watchpoint_addr_within_range (&current_target,
> > + newaddr, start,
> > + loc->length))
> > + {
> > + b->watchpoint_triggered = watch_triggered_yes;
> > + break;
> > + }
> > + }
>
> Hmmm, so for masked watchpoints we implement the masking operation
> directly here, and then also call the target callback (which may use
> the length, and do some additional masking on powerpc)? That seems
> a bit odd to me ...
>
> Shouldn't we either:
> - just compare masked addresses for equality, directly (which
> implements that stated semantics)
> - or else pass the mask into target_watchpoint_addr_within_range
> and allow the target to implement whatever matching is appropriate

This version implements the first option.

> > +static enum print_stop_action
> > +print_it_masked_watchpoint (struct breakpoint *b)
> > +{
> > + struct ui_stream *stb;
> > + struct cleanup *old_chain;
> > +
> > + /* Masked watchpoints have only one location. */
> > + gdb_assert (b->loc && b->loc->next == NULL);
> > +
> > + stb = ui_out_stream_new (uiout);
> > + old_chain = make_cleanup_ui_out_stream_delete (stb);
>
> These aren't used anywhere, are they?

Right. Dropped.

> > +ppc_linux_masked_watch_num_registers (struct target_ops *target,
> > + CORE_ADDR addr, CORE_ADDR mask)
> > +{
> > + if (!have_ptrace_booke_interface ()
> > + || (booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_MASK) == 0)
> > + return -1;
> > + else if ((mask & 0xC0000000) != 0xC0000000)
> > + {
> > + warning (_("\
> > +The given mask covers kernel address space and cannot be used.\n\
> > +You have to delete the masked watchpoint to continue the debugging session."));
>
> This second sentence shouldn't be neccessary now: the watchpoint with the
> invalid mask will never be successfully created, right?

Indeed. Removed the second sentence.

> Otherwise this looks all good to me now.

Great. What about this version?
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center

2011-05-03 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
Thiago Jung Bauermann <bauerman@br.ibm.com>

Implement support for PowerPC BookE masked watchpoints.

gdb/
*NEWS: Mention masked watchpoint support. Create "Changed commands"
section.
* breakpoint.h (struct breakpoint_ops) <works_in_software_mode>: New
method. Initialize to NULL in all existing breakpoint_ops instances.
(struct breakpoint) <hw_wp_mask>: New field.
* breakpoint.c (is_masked_watchpoint): Add prototype.
(update_watchpoint): Don't set b->val for masked watchpoints. Call
breakpoint's breakpoint_ops.works_in_software_mode if available.
(watchpoints_triggered): Handle the case of a hardware masked
watchpoint trigger.
(watchpoint_check): Likewise.
(works_in_software_mode_watchpoint): New function.
(insert_masked_watchpoint, remove_masked_watchpoint)
(resources_needed_masked_watchpoint)
(works_in_software_mode_masked_watchpoint, print_it_masked_watchpoint)
(print_one_detail_masked_watchpoint, print_mention_masked_watchpoint)
(print_recreate_masked_watchpoint, is_masked_watchpoint): New
functions.
(masked_watchpoint_breakpoint_ops): New structure.
(watch_command_1): Check for the existence of the `mask' parameter.
Set b->ops according to the type of hardware watchpoint being created.
* ppc-linux-nat.c (ppc_linux_insert_mask_watchpoint)
(ppc_linux_remove_mask_watchpoint)
(ppc_linux_masked_watch_num_registers): New functions.
(_initialize_ppc_linux_nat): Initialize to_insert_mask_watchpoint,
to_remove_mask_watchpoint and to_masked_watch_num_registers.
* target.c (update_current_target): Mention to_insert_mask_watchpoint,
to_remove_mask_watchpoint, and to_masked_watch_num_registers.
(target_insert_mask_watchpoint, target_remove_mask_watchpoint)
(target_masked_watch_num_registers): New functions.
* target.h (struct target_ops) <to_insert_mask_watchpoint>,
<to_remove_mask_watchpoint>, <to_masked_watch_num_registers>: New
methods.
(target_insert_mask_watchpoint, target_remove_mask_watchpoint)
(target_masked_watch_num_registers): Add prototypes.

gdb/doc/
* gdb.texinfo (Set Watchpoints): Document mask parameter.
(PowerPC Embedded): Document masked watchpoints.

diff --git a/gdb/NEWS b/gdb/NEWS
index 1a22c66..9b59419 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,10 +3,24 @@

*** Changes since GDB 7.3

+* When natively debugging programs on PowerPC BookE processors running
+ a Linux kernel version 2.6.34 or later, GDB supports masked hardware
+ watchpoints, which specify a mask in addition to an address to watch.
+ The mask specifies that some bits of an address (the bits which are
+ reset in the mask) should be ignored when matching the address accessed
+ by the inferior against the watchpoint address. See the "PowerPC Embedded"
+ section in the user manual for more details.
+
* The new option --once causes GDBserver to stop listening for connections once
the first connection is made. The listening port used by GDBserver will
become available after that.

+* Changed commands
+
+watch EXPRESSION mask MASK_VALUE
+ The watch command now supports the mask argument which allows creation
+ of masked watchpoints, if the current architecture supports this feature.
+
*** Changes in GDB 7.3

* GDB has a new command: "thread find [REGEXP]".
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index d434521..ebd5fd2 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -10950,6 +10950,7 @@ static struct breakpoint_ops catch_exception_breakpoint_ops =
NULL, /* remove */
NULL, /* breakpoint_hit */
NULL, /* resources_needed */
+ NULL, /* works_in_software_mode */
print_it_catch_exception,
print_one_catch_exception,
NULL, /* print_one_detail */
@@ -10990,6 +10991,7 @@ static struct breakpoint_ops catch_exception_unhandled_breakpoint_ops = {
NULL, /* remove */
NULL, /* breakpoint_hit */
NULL, /* resources_needed */
+ NULL, /* works_in_software_mode */
print_it_catch_exception_unhandled,
print_one_catch_exception_unhandled,
NULL, /* print_one_detail */
@@ -11028,6 +11030,7 @@ static struct breakpoint_ops catch_assert_breakpoint_ops = {
NULL, /* remove */
NULL, /* breakpoint_hit */
NULL, /* resources_needed */
+ NULL, /* works_in_software_mode */
print_it_catch_assert,
print_one_catch_assert,
NULL, /* print_one_detail */
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 604d5ab..fc62971 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -224,6 +224,8 @@ static void disable_trace_command (char *, int);

static void trace_pass_command (char *, int);

+static int is_masked_watchpoint (const struct breakpoint *b);
+
/* Assuming we're creating a static tracepoint, does S look like a
static tracepoint marker spec ("-m MARKER_ID")? */
#define is_marker_spec(s) \
@@ -1345,8 +1347,10 @@ update_watchpoint (struct breakpoint *b, int reparse)
/* 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_valid)
+ happens, the code that reports it updates b->val directly.
+ We don't keep track of the memory value for masked
+ watchpoints. */
+ if (!b->val_valid && !is_masked_watchpoint (b))
{
b->val = v;
b->val_valid = 1;
@@ -1440,19 +1444,23 @@ update_watchpoint (struct breakpoint *b, int reparse)
(bp_hardware_watchpoint, i, other_type_used);
if (target_resources_ok <= 0)
{
- if (target_resources_ok == 0
- && b->type != bp_hardware_watchpoint)
+ /* If there's no works_in_software_mode method, we
+ assume that the watchpoint works in software mode. */
+ int sw_mode = (!b->ops || !b->ops->works_in_software_mode
+ || b->ops->works_in_software_mode (b));
+
+ if (target_resources_ok == 0 && !sw_mode)
error (_("Target does not support this type of "
"hardware watchpoint."));
- else if (target_resources_ok < 0
- && b->type != bp_hardware_watchpoint)
- error (_("Target can only support one kind "
- "of HW watchpoint at a time."));
+ else if (target_resources_ok < 0 && !sw_mode)
+ error (_("There are not enough available hardware "
+ "resources for this watchpoint."));
else
b->type = bp_watchpoint;
}
}
- else if (b->type != bp_hardware_watchpoint)
+ else if (b->ops && b->ops->works_in_software_mode
+ && !b->ops->works_in_software_mode (b))
error (_("Expression cannot be implemented with "
"read/access watchpoint."));
else
@@ -3699,15 +3707,27 @@ watchpoints_triggered (struct target_waitstatus *ws)

b->watchpoint_triggered = watch_triggered_no;
for (loc = b->loc; loc; loc = loc->next)
- /* Exact match not required. Within range is
- sufficient. */
- if (target_watchpoint_addr_within_range (&current_target,
- addr, loc->address,
- loc->length))
- {
- b->watchpoint_triggered = watch_triggered_yes;
- break;
- }
+ {
+ if (is_masked_watchpoint (loc->owner))
+ {
+ CORE_ADDR newaddr = addr & loc->owner->hw_wp_mask;
+ CORE_ADDR start = loc->address & loc->owner->hw_wp_mask;
+
+ if (newaddr == start)
+ {
+ b->watchpoint_triggered = watch_triggered_yes;
+ break;
+ }
+ }
+ /* Exact match not required. Within range is sufficient. */
+ else if (target_watchpoint_addr_within_range (&current_target,
+ addr, loc->address,
+ loc->length))
+ {
+ b->watchpoint_triggered = watch_triggered_yes;
+ break;
+ }
+ }
}

return 1;
@@ -3804,9 +3824,16 @@ watchpoint_check (void *p)
might be in the middle of evaluating a function call. */

int pc = 0;
- struct value *mark = value_mark ();
+ struct value *mark;
struct value *new_val;

+ if (is_masked_watchpoint (b))
+ /* Since we don't know the exact trigger address (from
+ stopped_data_address), just tell the user we've triggered
+ a mask watchpoint. */
+ return WP_VALUE_CHANGED;
+
+ mark = value_mark ();
fetch_subexp_value (b->exp, &pc, &new_val, NULL, NULL);

/* We use value_equal_contents instead of value_equal because
@@ -6308,6 +6335,7 @@ static struct breakpoint_ops catch_fork_breakpoint_ops =
remove_catch_fork,
breakpoint_hit_catch_fork,
NULL, /* resources_needed */
+ NULL, /* works_in_software_mode */
print_it_catch_fork,
print_one_catch_fork,
NULL, /* print_one_detail */
@@ -6406,6 +6434,7 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops =
remove_catch_vfork,
breakpoint_hit_catch_vfork,
NULL, /* resources_needed */
+ NULL, /* works_in_software_mode */
print_it_catch_vfork,
print_one_catch_vfork,
NULL, /* print_one_detail */
@@ -6693,6 +6722,7 @@ static struct breakpoint_ops catch_syscall_breakpoint_ops =
remove_catch_syscall,
breakpoint_hit_catch_syscall,
NULL, /* resources_needed */
+ NULL, /* works_in_software_mode */
print_it_catch_syscall,
print_one_catch_syscall,
NULL, /* print_one_detail */
@@ -6850,6 +6880,7 @@ static struct breakpoint_ops catch_exec_breakpoint_ops =
remove_catch_exec,
breakpoint_hit_catch_exec,
NULL, /* resources_needed */
+ NULL, /* works_in_software_mode */
print_it_catch_exec,
print_one_catch_exec,
NULL, /* print_one_detail */
@@ -8475,6 +8506,7 @@ static struct breakpoint_ops ranged_breakpoint_ops =
NULL, /* remove */
breakpoint_hit_ranged_breakpoint,
resources_needed_ranged_breakpoint,
+ NULL, /* works_in_software_mode */
print_it_ranged_breakpoint,
print_one_ranged_breakpoint,
print_one_detail_ranged_breakpoint,
@@ -8782,6 +8814,15 @@ resources_needed_watchpoint (const struct bp_location *bl)
return target_region_ok_for_hw_watchpoint (bl->address, length);
}

+/* Implement the "works_in_software_mode" breakpoint_ops method for
+ hardware watchpoints. */
+
+int
+works_in_software_mode_watchpoint (const struct breakpoint *b)
+{
+ return b->type == bp_hardware_watchpoint;
+}
+
/* The breakpoint_ops structure to be used in hardware watchpoints. */

static struct breakpoint_ops watchpoint_breakpoint_ops =
@@ -8790,6 +8831,7 @@ static struct breakpoint_ops watchpoint_breakpoint_ops =
remove_watchpoint,
NULL, /* breakpoint_hit */
resources_needed_watchpoint,
+ works_in_software_mode_watchpoint,
NULL, /* print_it */
NULL, /* print_one */
NULL, /* print_one_detail */
@@ -8797,6 +8839,192 @@ static struct breakpoint_ops watchpoint_breakpoint_ops =
NULL /* print_recreate */
};

+/* Implement the "insert" breakpoint_ops method for
+ masked hardware watchpoints. */
+
+static int
+insert_masked_watchpoint (struct bp_location *bl)
+{
+ return target_insert_mask_watchpoint (bl->address, bl->owner->hw_wp_mask,
+ bl->watchpoint_type);
+}
+
+/* Implement the "remove" breakpoint_ops method for
+ masked hardware watchpoints. */
+
+static int
+remove_masked_watchpoint (struct bp_location *bl)
+{
+ return target_remove_mask_watchpoint (bl->address, bl->owner->hw_wp_mask,
+ bl->watchpoint_type);
+}
+
+/* Implement the "resources_needed" breakpoint_ops method for
+ masked hardware watchpoints. */
+
+static int
+resources_needed_masked_watchpoint (const struct bp_location *bl)
+{
+ return target_masked_watch_num_registers (bl->address,
+ bl->owner->hw_wp_mask);
+}
+
+/* Implement the "works_in_software_mode" breakpoint_ops method for
+ masked hardware watchpoints. */
+
+static int
+works_in_software_mode_masked_watchpoint (const struct breakpoint *b)
+{
+ return 0;
+}
+
+/* Implement the "print_it" breakpoint_ops method for
+ masked hardware watchpoints. */
+
+static enum print_stop_action
+print_it_masked_watchpoint (struct breakpoint *b)
+{
+ /* Masked watchpoints have only one location. */
+ gdb_assert (b->loc && b->loc->next == NULL);
+
+ switch (b->type)
+ {
+ case bp_hardware_watchpoint:
+ annotate_watchpoint (b->number);
+ if (ui_out_is_mi_like_p (uiout))
+ ui_out_field_string
+ (uiout, "reason",
+ async_reason_lookup (EXEC_ASYNC_WATCHPOINT_TRIGGER));
+ break;
+
+ case bp_read_watchpoint:
+ if (ui_out_is_mi_like_p (uiout))
+ ui_out_field_string
+ (uiout, "reason",
+ async_reason_lookup (EXEC_ASYNC_READ_WATCHPOINT_TRIGGER));
+ break;
+
+ case bp_access_watchpoint:
+ if (ui_out_is_mi_like_p (uiout))
+ ui_out_field_string
+ (uiout, "reason",
+ async_reason_lookup (EXEC_ASYNC_ACCESS_WATCHPOINT_TRIGGER));
+ break;
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("Invalid hardware watchpoint type."));
+ }
+
+ mention (b);
+ ui_out_text (uiout, _("\n\
+Check the underlying instruction at PC for the memory\n\
+address and value which triggered this watchpoint.\n"));
+ ui_out_text (uiout, "\n");
+
+ /* More than one watchpoint may have been triggered. */
+ return PRINT_UNKNOWN;
+}
+
+/* Implement the "print_one_detail" breakpoint_ops method for
+ masked hardware watchpoints. */
+
+static void
+print_one_detail_masked_watchpoint (const struct breakpoint *b,
+ struct ui_out *uiout)
+{
+ /* Masked watchpoints have only one location. */
+ gdb_assert (b->loc && b->loc->next == NULL);
+
+ ui_out_text (uiout, "\tmask ");
+ ui_out_field_core_addr (uiout, "mask", b->loc->gdbarch, b->hw_wp_mask);
+ ui_out_text (uiout, "\n");
+}
+
+/* Implement the "print_mention" breakpoint_ops method for
+ masked hardware watchpoints. */
+
+static void
+print_mention_masked_watchpoint (struct breakpoint *b)
+{
+ struct cleanup *ui_out_chain;
+
+ switch (b->type)
+ {
+ case bp_hardware_watchpoint:
+ ui_out_text (uiout, "Masked hardware watchpoint ");
+ ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "wpt");
+ break;
+ case bp_read_watchpoint:
+ ui_out_text (uiout, "Masked hardware read watchpoint ");
+ ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "hw-rwpt");
+ break;
+ case bp_access_watchpoint:
+ ui_out_text (uiout, "Masked hardware access (read/write) watchpoint ");
+ ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "hw-awpt");
+ break;
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("Invalid hardware watchpoint type."));
+ }
+
+ ui_out_field_int (uiout, "number", b->number);
+ ui_out_text (uiout, ": ");
+ ui_out_field_string (uiout, "exp", b->exp_string);
+ do_cleanups (ui_out_chain);
+}
+
+/* Implement the "print_recreate" breakpoint_ops method for
+ masked hardware watchpoints. */
+
+static void
+print_recreate_masked_watchpoint (struct breakpoint *b, struct ui_file *fp)
+{
+ char tmp[40];
+
+ switch (b->type)
+ {
+ case bp_hardware_watchpoint:
+ fprintf_unfiltered (fp, "watch");
+ break;
+ case bp_read_watchpoint:
+ fprintf_unfiltered (fp, "rwatch");
+ break;
+ case bp_access_watchpoint:
+ fprintf_unfiltered (fp, "awatch");
+ break;
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("Invalid hardware watchpoint type."));
+ }
+
+ sprintf_vma (tmp, b->hw_wp_mask);
+ fprintf_unfiltered (fp, " %s mask 0x%s", b->exp_string, tmp);
+}
+
+/* The breakpoint_ops structure to be used in masked hardware watchpoints. */
+
+static struct breakpoint_ops masked_watchpoint_breakpoint_ops =
+{
+ insert_masked_watchpoint,
+ remove_masked_watchpoint,
+ NULL, /* breakpoint_hit */
+ resources_needed_masked_watchpoint,
+ works_in_software_mode_masked_watchpoint,
+ print_it_masked_watchpoint,
+ NULL, /* print_one */
+ print_one_detail_masked_watchpoint,
+ print_mention_masked_watchpoint,
+ print_recreate_masked_watchpoint
+};
+
+/* Tell whether the given watchpoint is a masked hardware watchpoint. */
+
+static int
+is_masked_watchpoint (const struct breakpoint *b)
+{
+ return b->ops == &masked_watchpoint_breakpoint_ops;
+}
+
/* accessflag: hw_write: watch write,
hw_read: watch read,
hw_access: watch access (read or write) */
@@ -8812,73 +9040,97 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
struct frame_info *frame;
char *exp_start = NULL;
char *exp_end = NULL;
- char *tok, *id_tok_start, *end_tok;
- int toklen;
+ char *tok, *end_tok;
+ int toklen = -1;
char *cond_start = NULL;
char *cond_end = NULL;
enum bptype bp_type;
int thread = -1;
int pc = 0;
+ /* Flag to indicate whether we are going to use masks for
+ the hardware watchpoint. */
+ int use_mask = 0;
+ CORE_ADDR mask = 0;

/* Make sure that we actually have parameters to parse. */
if (arg != NULL && arg[0] != '\0')
{
- toklen = strlen (arg); /* Size of argument list. */
+ char *value_start;

- /* Points tok to the end of the argument list. */
- tok = arg + toklen - 1;
+ /* Look for "parameter value" pairs at the end
+ of the arguments string. */
+ for (tok = arg + strlen (arg) - 1; tok > arg; tok--)
+ {
+ /* Skip whitespace at the end of the argument list. */
+ while (tok > arg && (*tok == ' ' || *tok == '\t'))
+ tok--;
+
+ /* Find the beginning of the last token.
+ This is the value of the parameter. */
+ while (tok > arg && (*tok != ' ' && *tok != '\t'))
+ tok--;
+ value_start = tok + 1;
+
+ /* Skip whitespace. */
+ while (tok > arg && (*tok == ' ' || *tok == '\t'))
+ tok--;
+
+ end_tok = tok;
+
+ /* Find the beginning of the second to last token.
+ This is the parameter itself. */
+ while (tok > arg && (*tok != ' ' && *tok != '\t'))
+ tok--;
+ tok++;
+ toklen = end_tok - tok + 1;
+
+ if (toklen == 6 && !strncmp (tok, "thread", 6))
+ {
+ /* At this point we've found a "thread" token, which means
+ the user is trying to set a watchpoint that triggers
+ only in a specific thread. */
+ char *endp;

- /* Go backwards in the parameters list. Skip the last
- parameter. If we're expecting a 'thread <thread_num>'
- parameter, this should be the thread identifier. */
- while (tok > arg && (*tok == ' ' || *tok == '\t'))
- tok--;
- while (tok > arg && (*tok != ' ' && *tok != '\t'))
- tok--;
+ if (thread != -1)
+ error(_("You can specify only one thread."));

- /* Points end_tok to the beginning of the last token. */
- id_tok_start = tok + 1;
+ /* Extract the thread ID from the next token. */
+ thread = strtol (value_start, &endp, 0);

- /* Go backwards in the parameters list. Skip one more
- parameter. If we're expecting a 'thread <thread_num>'
- parameter, we should reach a "thread" token. */
- while (tok > arg && (*tok == ' ' || *tok == '\t'))
- tok--;
+ /* Check if the user provided a valid numeric value for the
+ thread ID. */
+ if (*endp != ' ' && *endp != '\t' && *endp != '\0')
+ error (_("Invalid thread ID specification %s."), value_start);

- end_tok = tok;
+ /* Check if the thread actually exists. */
+ if (!valid_thread_id (thread))
+ error (_("Unknown thread %d."), thread);
+ }
+ else if (toklen == 4 && !strncmp (tok, "mask", 4))
+ {
+ /* We've found a "mask" token, which means the user wants to
+ create a hardware watchpoint that is going to have the mask
+ facility. */
+ struct value *mask_value, *mark;

- while (tok > arg && (*tok != ' ' && *tok != '\t'))
- tok--;
+ if (use_mask)
+ error(_("You can specify only one mask."));

- /* Move the pointer forward to skip the whitespace and
- calculate the length of the token. */
- tok++;
- toklen = end_tok - tok;
+ use_mask = just_location = 1;

- if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0)
- {
- /* At this point we've found a "thread" token, which means
- the user is trying to set a watchpoint that triggers
- only in a specific thread. */
- char *endp;
-
- /* Extract the thread ID from the next token. */
- thread = strtol (id_tok_start, &endp, 0);
-
- /* Check if the user provided a valid numeric value for the
- thread ID. */
- if (*endp != ' ' && *endp != '\t' && *endp != '\0')
- error (_("Invalid thread ID specification %s."), id_tok_start);
-
- /* Check if the thread actually exists. */
- if (!valid_thread_id (thread))
- error (_("Unknown thread %d."), thread);
-
- /* Truncate the string and get rid of the thread <thread_num>
- parameter before the parameter list is parsed by the
- evaluate_expression() function. */
- *tok = '\0';
- }
+ mark = value_mark ();
+ mask_value = parse_to_comma_and_eval (&value_start);
+ mask = value_as_address (mask_value);
+ value_free_to_mark (mark);
+ }
+ else
+ /* We didn't recognize what we found. We should stop here. */
+ break;
+
+ /* Truncate the string and get rid of the "parameter value" pair before
+ the arguments string is parsed by the parse_exp_1 function. */
+ *tok = '\0';
+ }
}

/* Parse the rest of the arguments. */
@@ -8909,10 +9161,22 @@ watch_command_1 (char *arg, int accessflag, int from_tty,

if (just_location)
{
+ int ret;
+
exp_valid_block = NULL;
val = value_addr (result);
release_value (val);
value_free_to_mark (mark);
+
+ if (use_mask)
+ {
+ ret = target_masked_watch_num_registers (value_as_address (val),
+ mask);
+ if (ret == -1)
+ error (_("This target does not support masked watchpoints."));
+ else if (ret == -2)
+ error (_("Invalid mask or memory region."));
+ }
}
else if (val != NULL)
release_value (val);
@@ -9009,9 +9273,18 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
}
else
b->exp_string = savestring (exp_start, exp_end - exp_start);
- b->val = val;
- b->val_valid = 1;
- b->ops = &watchpoint_breakpoint_ops;
+
+ if (use_mask)
+ {
+ b->hw_wp_mask = mask;
+ b->ops = &masked_watchpoint_breakpoint_ops;
+ }
+ else
+ {
+ b->val = val;
+ b->val_valid = 1;
+ b->ops = &watchpoint_breakpoint_ops;
+ }

if (cond_start)
b->cond_string = savestring (cond_start, cond_end - cond_start);
@@ -9548,6 +9821,7 @@ static struct breakpoint_ops gnu_v3_exception_catchpoint_ops = {
NULL, /* remove */
NULL, /* breakpoint_hit */
NULL, /* resources_needed */
+ NULL, /* works_in_software_mode */
print_it_exception_catchpoint,
print_one_exception_catchpoint,
NULL, /* print_one_detail */
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 7a9c2d4..7fa705f 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -410,6 +410,11 @@ struct breakpoint_ops
the breakpoint or watchpoint needs one debug register. */
int (*resources_needed) (const struct bp_location *);

+ /* Tell whether we can downgrade from a hardware watchpoint to a software
+ one. If not, the user will not be able to enable the watchpoint when
+ there are not enough hardware resources available. */
+ int (*works_in_software_mode) (const struct breakpoint *);
+
/* The normal print routine for this breakpoint, called when we
hit it. */
enum print_stop_action (*print_it) (struct breakpoint *);
@@ -651,6 +656,9 @@ struct breakpoint

/* Whether this watchpoint is exact (see target_exact_watchpoints). */
int exact;
+
+ /* The mask address for a masked hardware watchpoint. */
+ CORE_ADDR hw_wp_mask;
};

typedef struct breakpoint *breakpoint_p;
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index ff5b7cb..19104ad 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3728,7 +3728,7 @@ watchpoints, which do not slow down the running of your program.

@table @code
@kindex watch
-@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]}
+@item watch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
Set a watchpoint for an expression. @value{GDBN} will break when the
expression @var{expr} is written into by the program and its value
changes. The simplest (and the most popular) use of this command is
@@ -3739,12 +3739,18 @@ to watch the value of a single variable:
@end smallexample

If the command includes a @code{@r{[}thread @var{threadnum}@r{]}}
-clause, @value{GDBN} breaks only when the thread identified by
+argument, @value{GDBN} breaks only when the thread identified by
@var{threadnum} changes the value of @var{expr}. If any other threads
change the value of @var{expr}, @value{GDBN} will not break. Note
that watchpoints restricted to a single thread in this way only work
with Hardware Watchpoints.

+The @code{@r{[}mask @var{maskvalue}@r{]}} argument allows creation
+of masked watchpoints, if the current architecture supports this
+feature. (Currently, this is only available on PowerPC Embedded
+architecture, see @ref{PowerPC Embedded}.) Specifying a mask argument
+implies the @code{-location} argument.
+
Ordinarily a watchpoint respects the scope of variables in @var{expr}
(see below). The @code{-location} argument tells @value{GDBN} to
instead watch the memory referred to by @var{expr}. In this case,
@@ -3755,12 +3761,12 @@ result does not have an address, then @value{GDBN} will print an
error.

@kindex rwatch
-@item rwatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]}
+@item rwatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
Set a watchpoint that will break when the value of @var{expr} is read
by the program.

@kindex awatch
-@item awatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]}
+@item awatch @r{[}-l@r{|}-location@r{]} @var{expr} @r{[}thread @var{threadnum}@r{]} @r{[}mask @var{maskvalue}@r{]}
Set a watchpoint that will break when @var{expr} is either read from
or written into by the program.

@@ -18783,6 +18789,25 @@ region using one of the following commands (@pxref{Expressions}):
(@value{GDBP}) watch @{char[@var{length}]@} @var{address}
@end smallexample

+PowerPC embedded processors support masked watchpoints.
+
+A @dfn{masked watchpoint} specifies a mask in addition to an address
+to watch. The @code{mask} argument implies the @code{-location}
+argument, which means that the expression will be resolved to a memory
+address at watchpoint creation time (@pxref{Set Watchpoints}.) The mask
+specifies that some bits of an address (the bits which are reset in the
+mask) should be ignored when matching the address accessed by the inferior
+against the watchpoint address. Thus, a masked watchpoint watches many
+addresses simultaneously---those addresses whose unmasked bits are
+identical to the unmasked bits in the watchpoint address.
+
+To set a masked watchpoint in @value{GDBN}, use the @code{mask} argument in
+the @code{watch} command (@pxref{Set Watchpoints}), as in:
+
+@smallexample
+(@value{GDBP}) watch *0xdeadbeef mask 0xffffff00
+@end smallexample
+
@cindex ranged breakpoint
PowerPC embedded processors support hardware accelerated
@dfn{ranged breakpoints}. A ranged breakpoint stops execution of
diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c
index 6f11715..275de78 100644
--- a/gdb/ppc-linux-nat.c
+++ b/gdb/ppc-linux-nat.c
@@ -1739,6 +1739,64 @@ get_trigger_type (int rw)
return t;
}

+/* Insert a new masked watchpoint at ADDR using the mask MASK.
+ RW may be hw_read for a read watchpoint, hw_write for a write watchpoint
+ or hw_access for an access watchpoint. Returns 0 on success and throws
+ an error on failure. */
+
+static int
+ppc_linux_insert_mask_watchpoint (struct target_ops *ops, CORE_ADDR addr,
+ CORE_ADDR mask, int rw)
+{
+ ptid_t ptid;
+ struct lwp_info *lp;
+ struct ppc_hw_breakpoint p;
+
+ gdb_assert (have_ptrace_booke_interface ());
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = get_trigger_type (rw);
+ p.addr_mode = PPC_BREAKPOINT_MODE_MASK;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = addr;
+ p.addr2 = mask;
+ p.condition_value = 0;
+
+ ALL_LWPS (lp, ptid)
+ booke_insert_point (&p, TIDGET (ptid));
+
+ return 0;
+}
+
+/* Remove a masked watchpoint at ADDR with the mask MASK.
+ RW may be hw_read for a read watchpoint, hw_write for a write watchpoint
+ or hw_access for an access watchpoint. Returns 0 on success and throws
+ an error on failure. */
+
+static int
+ppc_linux_remove_mask_watchpoint (struct target_ops *ops, CORE_ADDR addr,
+ CORE_ADDR mask, int rw)
+{
+ ptid_t ptid;
+ struct lwp_info *lp;
+ struct ppc_hw_breakpoint p;
+
+ gdb_assert (have_ptrace_booke_interface ());
+
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = get_trigger_type (rw);
+ p.addr_mode = PPC_BREAKPOINT_MODE_MASK;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = addr;
+ p.addr2 = mask;
+ p.condition_value = 0;
+
+ ALL_LWPS (lp, ptid)
+ booke_remove_point (&p, TIDGET (ptid));
+
+ return 0;
+}
+
/* Check whether we have at least one free DVC register. */
static int
can_use_watchpoint_cond_accel (void)
@@ -2224,6 +2282,26 @@ ppc_linux_watchpoint_addr_within_range (struct target_ops *target,
return start <= addr + mask && start + length - 1 >= addr;
}

+/* Return the number of registers needed for a masked hardware watchpoint. */
+
+static int
+ppc_linux_masked_watch_num_registers (struct target_ops *target,
+ CORE_ADDR addr, CORE_ADDR mask)
+{
+ if (!have_ptrace_booke_interface ()
+ || (booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_MASK) == 0)
+ return -1;
+ else if ((mask & 0xC0000000) != 0xC0000000)
+ {
+ warning (_("The given mask covers kernel address space "
+ "and cannot be used.\n"));
+
+ return -2;
+ }
+ else
+ return 2;
+}
+
static void
ppc_linux_store_inferior_registers (struct target_ops *ops,
struct regcache *regcache, int regno)
@@ -2438,11 +2516,14 @@ _initialize_ppc_linux_nat (void)
t->to_region_ok_for_hw_watchpoint = ppc_linux_region_ok_for_hw_watchpoint;
t->to_insert_watchpoint = ppc_linux_insert_watchpoint;
t->to_remove_watchpoint = ppc_linux_remove_watchpoint;
+ t->to_insert_mask_watchpoint = ppc_linux_insert_mask_watchpoint;
+ t->to_remove_mask_watchpoint = ppc_linux_remove_mask_watchpoint;
t->to_stopped_by_watchpoint = ppc_linux_stopped_by_watchpoint;
t->to_stopped_data_address = ppc_linux_stopped_data_address;
t->to_watchpoint_addr_within_range = ppc_linux_watchpoint_addr_within_range;
t->to_can_accel_watchpoint_condition
= ppc_linux_can_accel_watchpoint_condition;
+ t->to_masked_watch_num_registers = ppc_linux_masked_watch_num_registers;
t->to_ranged_break_num_registers = ppc_linux_ranged_break_num_registers;

t->to_read_description = ppc_linux_read_description;
diff --git a/gdb/target.c b/gdb/target.c
index 1e1c38e..9c522cb 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -595,6 +595,8 @@ update_current_target (void)
/* Do not inherit to_ranged_break_num_registers. */
INHERIT (to_insert_watchpoint, t);
INHERIT (to_remove_watchpoint, t);
+ /* Do not inherit to_insert_mask_watchpoint. */
+ /* Do not inherit to_remove_mask_watchpoint. */
INHERIT (to_stopped_data_address, t);
INHERIT (to_have_steppable_watchpoint, t);
INHERIT (to_have_continuable_watchpoint, t);
@@ -602,6 +604,7 @@ update_current_target (void)
INHERIT (to_watchpoint_addr_within_range, t);
INHERIT (to_region_ok_for_hw_watchpoint, t);
INHERIT (to_can_accel_watchpoint_condition, t);
+ /* Do not inherit to_masked_watch_num_registers. */
INHERIT (to_terminal_init, t);
INHERIT (to_terminal_inferior, t);
INHERIT (to_terminal_ours_for_output, t);
@@ -3518,6 +3521,75 @@ target_verify_memory (const gdb_byte *data, CORE_ADDR memaddr, ULONGEST size)
tcomplain ();
}

+/* The documentation for this function is in its prototype declaration in
+ target.h. */
+
+int
+target_insert_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, int rw)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_insert_mask_watchpoint != NULL)
+ {
+ int ret;
+
+ ret = t->to_insert_mask_watchpoint (t, addr, mask, rw);
+
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, "\
+target_insert_mask_watchpoint (%s, %s, %d) = %d\n",
+ core_addr_to_string (addr),
+ core_addr_to_string (mask), rw, ret);
+
+ return ret;
+ }
+
+ return 1;
+}
+
+/* The documentation for this function is in its prototype declaration in
+ target.h. */
+
+int
+target_remove_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, int rw)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_remove_mask_watchpoint != NULL)
+ {
+ int ret;
+
+ ret = t->to_remove_mask_watchpoint (t, addr, mask, rw);
+
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, "\
+target_remove_mask_watchpoint (%s, %s, %d) = %d\n",
+ core_addr_to_string (addr),
+ core_addr_to_string (mask), rw, ret);
+
+ return ret;
+ }
+
+ return 1;
+}
+
+/* The documentation for this function is in its prototype declaration
+ in target.h. */
+
+int
+target_masked_watch_num_registers (CORE_ADDR addr, CORE_ADDR mask)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_masked_watch_num_registers != NULL)
+ return t->to_masked_watch_num_registers (t, addr, mask);
+
+ return -1;
+}
+
/* The documentation for this function is in its prototype declaration
in target.h. */

diff --git a/gdb/target.h b/gdb/target.h
index 11380ed..52e0276 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -459,6 +459,10 @@ struct target_ops
int (*to_remove_watchpoint) (CORE_ADDR, int, int, struct expression *);
int (*to_insert_watchpoint) (CORE_ADDR, int, int, struct expression *);

+ int (*to_insert_mask_watchpoint) (struct target_ops *,
+ CORE_ADDR, CORE_ADDR, int);
+ int (*to_remove_mask_watchpoint) (struct target_ops *,
+ CORE_ADDR, CORE_ADDR, int);
int (*to_stopped_by_watchpoint) (void);
int to_have_steppable_watchpoint;
int to_have_continuable_watchpoint;
@@ -472,6 +476,8 @@ struct target_ops

int (*to_can_accel_watchpoint_condition) (CORE_ADDR, int, int,
struct expression *);
+ int (*to_masked_watch_num_registers) (struct target_ops *,
+ CORE_ADDR, CORE_ADDR);
void (*to_terminal_init) (void);
void (*to_terminal_inferior) (void);
void (*to_terminal_ours_for_output) (void);
@@ -1362,6 +1368,20 @@ extern char *target_thread_name (struct thread_info *);
#define target_remove_watchpoint(addr, len, type, cond) \
(*current_target.to_remove_watchpoint) (addr, len, type, cond)

+/* Insert a new masked watchpoint at ADDR using the mask MASK.
+ RW may be hw_read for a read watchpoint, hw_write for a write watchpoint
+ or hw_access for an access watchpoint. Returns 0 for success, 1 if
+ masked watchpoints are not supported, -1 for failure. */
+
+extern int target_insert_mask_watchpoint (CORE_ADDR, CORE_ADDR, int);
+
+/* Remove a masked watchpoint at ADDR with the mask MASK.
+ RW may be hw_read for a read watchpoint, hw_write for a write watchpoint
+ or hw_access for an access watchpoint. Returns 0 for success, non-zero
+ for failure. */
+
+extern int target_remove_mask_watchpoint (CORE_ADDR, CORE_ADDR, int);
+
#define target_insert_hw_breakpoint(gdbarch, bp_tgt) \
(*current_target.to_insert_hw_breakpoint) (gdbarch, bp_tgt)

@@ -1395,6 +1415,12 @@ extern int target_ranged_break_num_registers (void);
#define target_can_accel_watchpoint_condition(addr, len, type, cond) \
(*current_target.to_can_accel_watchpoint_condition) (addr, len, type, cond)

+/* Return number of debug registers needed for a masked watchpoint,
+ -1 if masked watchpoints are not supported or -2 if the given address
+ and mask combination cannot be used. */
+
+extern int target_masked_watch_num_registers (CORE_ADDR addr, CORE_ADDR mask);
+
/* Target can execute in reverse? */
#define target_can_execute_reverse \
(current_target.to_can_execute_reverse ? \