[RFA 3/3] Implement support for PowerPC BookE masked watchpoints

Thiago Jung Bauermann
2011-04-18T21:24:04Z

Hi Uli,

Thanks for your review and suggestions!

[ This patch depends on two patches that I'm posting as replies to your
message too so that they'll be grouped together. ]

On Thu, 2011-02-17 at 15:40 +0100, Ulrich Weigand wrote:
> Thiago Jung Bauermann wrote:
> The effect is that GDB will parse the expression, create the list of
> memory regions it needs to watch to implement its high-level semantics.
> Then, it will just add the mask to each of the addresses generated
> in that manner. It seems to me that in general, the effects of what
> will actually happen now are hard to predict, and will generally not
> be what the user expects.
>
> Of course, the feature *can* be used in ways the user understands,
> like in the example you show in the documentation:
> watch *0xdeadbeef mask 0xffffff00
>
> But if used in a more complex way, like
> watch p->x mask 0xffffff00
> for some local variable p, this doesn't seem to have very useful
> (or even well-defined) semantics ...

This isn't a problem for PowerPC because there are only two hardware
watchpoint registers, and both will be needed to set up a masked
watchpoint (one for the address, the other for the mask). So the command
you mention above would fail since it would need more registers than
that. But I agree that in general it's a problem.

> Now, I'm not sure how exactly the GDB command line syntax ought to
> be modified to implement this. But most fundamentally, we should
> go away from specifying "the value of an expression", and rather
> go to specifying a memory region via address and mask.
>
> 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?

> However, even so there are still differences: even with -location,
> GDB watchpoint code will still treat a watchpoint as refering to
> one value, and track it (e.g. remember the value we saw last time;
> re-evaluate it each time execution stops etc.). This is really
> not appropriate for the masked watchpoint case, because we do not
> *have* any contiguous region that would define any single value.
> Therefore, it seems to me that all this code ought to be disabled
> for masked watchpoints.

Indeed. With this new patch, the only place that evaluates the
watchpoint expression is update_watchpoint. This is necessary because
the resulting value chain is then used to create the bp_locations and
also by can_use_hardware_watchpoint when deciding whether to set a
software or hardware watchpoint.

I could manually create a bp_location for masked watchpoints and
implement a version of can_use_hardware_watchpoint just for masked
watchpoints, but is it worth the extra special casing and complexity in
update_watchpoint?

> A couple of more specific comments on the patch:
>
> > @@ -1518,10 +1522,26 @@ update_watchpoint (struct breakpoint *b, int reparse)
> > target_resources_ok = target_can_use_hardware_watchpoint
> > (bp_hardware_watchpoint, i, other_type_used);
> > if (target_resources_ok <= 0)
> > - b->type = bp_watchpoint;
> > + {
> > + if (orig_type == bp_hardware_watchpoint
> > + && b->ops && b->ops->works_in_software_mode
> > + && !b->ops->works_in_software_mode (b))
> > + error (_("This watchpoint cannot be used "
> > + "in software mode."));
> > + else
> > + b->type = bp_watchpoint;
> > + }
> > }
> > else
> > - b->type = bp_watchpoint;
> > + {
> > + if (b->type == bp_hardware_watchpoint
> > + && b->ops && b->ops->works_in_software_mode
> > + && !b->ops->works_in_software_mode (b))
> > + error (_("This watchpoint cannot be used "
> > + "in software mode."));
> > + else
> > + b->type = bp_watchpoint;
> > + }
>
> What is the point of checking the original type? If works_in_software_mode
> returns false, the original type couldn't have been a software watchpoint
> anyway, could it?

Right, removed.

> > print_it_typical. */
> > /* FIXME: how breakpoint can ever be NULL here? */
> > if (b != NULL && b->ops != NULL && b->ops->print_it != NULL)
> > - return b->ops->print_it (b);
> > + return b->ops->print_it (b, bs->old_val);
> > else
> > return print_it_typical (bs);
> > }
>
> I don't understand this change. You add the "old_val" argument to the
> print_it routine, and the new instance of print_it for masked watchpoints
> then uses it (for what purpose?) ... But for masked watchpoints, the
> whole concept of its "value", new or old, doesn't make sense to start
> with ...

I added it for two reasons. First, because I thought it made sense to
make available to print_it implementations the same information that
print_it_typical uses itself. Also, I wasn't sure how important is this
code in print_it_masked_watchpoint which I borrowed from
print_it_typical:

+ case bp_access_watchpoint:
+ if (old_val != NULL)
+ annotate_watchpoint (b->number);

So I decided to keep it. In this version I removed the old_val argument
and the call to annotate_watchpoint.

> > @@ -3761,6 +3796,11 @@ watchpoint_check (void *p)
> > b->val_valid = 1;
> > return WP_VALUE_CHANGED;
> > }
> > + else 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;
>
> This goes back to the issue discussed above: this should not even
> attempt to "compute the old value" which doesn't make sense for a
> masked watchpoint. Instead, it should simply report a stop to the
> user whenever the hardware reports a hit.

Right. This was solved simply by doing the is_masked_watchpoint check
before fetching the watchpoint value.

> > +static void
> > +print_one_detail_masked_watchpoint (const struct breakpoint *b,
> > + struct ui_out *uiout)
> > +{
> > + ui_out_text (uiout, "\tmask ");
> > +
> > + /* I don't know whether it's possible to get here without a b->loc,
> > + but we can handle the situation. */
> > + if (b->loc)
> > + ui_out_field_core_addr (uiout, "mask", b->loc->gdbarch, b->hw_wp_mask);
> > + else
> > + ui_out_field_string (uiout, "mask", core_addr_to_string (b->hw_wp_mask));
>
> I think we should enforce that there is always exactly one location on
> masked watchpoints, just like we do e.g. for catchpoints.

Ok, with -location now we are guaranteed to have only one location.
Fixed.

> > +/* 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);
>
> Depending on the discussion on command syntax this may need to change anyway,
> but even with the current syntax, it ignores any -location flag that may have
> been given initially ...

Since mask now implies -location, no change is needed here anymore.

> > @@ -8540,7 +8832,14 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
> > 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 = hw_wp_mask;
> > + b->ops = &masked_watchpoint_breakpoint_ops;
> > + }
> > + else
> > + b->ops = &watchpoint_breakpoint_ops;
>
> We shouldn't compute an intial value or set val_valid for masked watchpoints.

Fixed.

> > +/* 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 ());
> > +
> > + if ((mask & 0xC0000000) != 0xC0000000)
> > + error (_("\
> > +The given mask covers kernel address space and cannot be used.\n\
> > +You have to delete the masked watchpoint to continue the debugging session."));
>
> Huh, this interface seems a bit odd. Shouldn't such watchpoints be rejected
> right from the start, using something like the region_ok_for_watchpoint
> callback?
>
> > +/* Return the number of registers needed for a masked hardware watchpoint. */
> > +
> > +static int
> > +ppc_linux_masked_watch_num_registers (struct target_ops *target)
> > +{
> > + return ((have_ptrace_booke_interface ()
> > + && booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_MASK)?
> > + 2 : -1);
> > +}
>
> So maybe this interface ought to get parameters to be able to check
> whether specific watchpoints are valid?

Ok, it now gets an addr and mask parameter, and returns -2 if the region
is rejected.

> > INHERIT (to_insert_watchpoint, t);
> > INHERIT (to_remove_watchpoint, t);
> > + /* Do not inherit to_insert_mask_watchpoint. */
> > + /* Do not inherit to_remove_mask_watchpoint. */
>
> Why should this differ between regular and masked watchpoints? Shouldn't
> they either both use inheritance or none?

This was discussed and settled in the ranged watchpoints patch, so I
didn't change it here.

Tested without regressions on ppc-linux, ppc64-linux and i386-linux. Ok?
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center

2011-04-18 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 a673d7a..5a68154 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,20 @@

*** 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.
+
+* 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 0a0b09f..c91df6f 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -10951,6 +10951,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 */
@@ -10991,6 +10992,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 */
@@ -11029,6 +11031,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 2bfdfb0..7d01737 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;
@@ -1437,19 +1441,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
@@ -3695,15 +3703,30 @@ 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;
- }
+ {
+ 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;
+ }
+ }
}

return 1;
@@ -3800,9 +3823,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
@@ -6306,6 +6336,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 */
@@ -6404,6 +6435,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 */
@@ -6691,6 +6723,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 */
@@ -6847,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 */
@@ -8479,6 +8513,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,
@@ -8785,6 +8820,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 =
@@ -8793,6 +8837,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 */
@@ -8800,6 +8845,200 @@ 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)
+{
+ 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);
+
+ 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");
+
+ do_cleanups (old_chain);
+
+ /* 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) */
@@ -8815,73 +9054,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. */
@@ -8912,10 +9175,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);
@@ -9013,9 +9288,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);
@@ -9550,6 +9834,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 bf827e5..ccd7c3c 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 c71d664..b1fb0ff 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3727,7 +3727,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
@@ -3738,12 +3738,17 @@ 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}.)
+
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,
@@ -3754,12 +3759,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.

@@ -18742,6 +18747,23 @@ 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 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..d20a288 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,27 @@ 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\
+You have to delete the masked watchpoint to continue the debugging session."));
+
+ return -2;
+ }
+ else
+ return 2;
+}
+
static void
ppc_linux_store_inferior_registers (struct target_ops *ops,
struct regcache *regcache, int regno)
@@ -2438,11 +2517,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 a032052..5028247 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -597,6 +597,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);
@@ -604,6 +606,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);
@@ -3492,6 +3495,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 f0b2e43..ef15022 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);
@@ -1349,6 +1355,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)

@@ -1382,6 +1402,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 ? \