[RFA] Implement support for PowerPC BookE masked watchpoints

Thiago Jung Bauermann
2011-01-13T20:06:49Z

Hi,

I'm almost done with the series adding suport for PowerPC BookE
features... There's only this patch adding masked watchpoints, and
another one adding ranged hardware breakpoints (to be posted later). So
please bear with me a little longer. :-)

The documentation part of the patch was already reviewed and approved.
No regressions on ppc-linux, ppc64-linux or i386-linux. Ok to commit?

To ease review, here are the changes on parsing the command line in
watch_command_1, with a diff ignoring whitespace changes:

---- 8< ---- 8< ----

@@ -8219,64 +8463,93 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
int reg_cnt = 0;
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 hw_wp_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;
-
- /* 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. */
+ /* 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;

- /* Points end_tok to the beginning of the last token. */
- id_tok_start = tok + 1;
-
- /* 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. */
+ /* 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--;
-
- /* Move the pointer forward to skip the whitespace and
- calculate the length of the token. */
tok++;
- toklen = end_tok - tok;
+ toklen = end_tok - tok + 1;

- if (toklen >= 1 && strncmp (tok, "thread", toklen) == 0)
+ 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;

+ if (thread != -1)
+ error(_("You can specify only one thread."));
+
/* Extract the thread ID from the next token. */
- thread = strtol (id_tok_start, &endp, 0);
+ thread = strtol (value_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);
+ error (_("Invalid thread ID specification %s."), value_start);

/* 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;
+
+ /* Does the target support masked watchpoints? */
+ if (!target_masked_watch_num_extra_regs () < 0)
+ error (_("\
+This target does not support the usage of masks with hardware watchpoints."));

- /* Truncate the string and get rid of the thread <thread_num>
- parameter before the parameter list is parsed by the
- evaluate_expression() function. */
+ if (use_mask)
+ error(_("You can specify only one mask."));
+
+ use_mask = 1;
+
+ mark = value_mark ();
+ mask_value = parse_to_comma_and_eval (&value_start);
+ hw_wp_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';
}
}

---- >8 ---- >8 ----

--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center

2011-01-13 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"
sections and populate it based on other NEWS items.
* breakpoint.h (struct ui_out): New opaque declaration.
(struct breakpoint_ops) <works_in_software_mode>,
<print_one_detail>: New methods. Initialize them to NULL in all
existing breakpoint_ops instances.
(struct breakpoint_ops) <print_it>: Add OLD_VAL parameter. Update all
implementations of the method.
(struct breakpoint) <hw_wp_mask>: New field.
* breakpoint.c (is_masked_watchpoint): Add prototype.
(update_watchpoint): 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.
(print_one_breakpoint_location): Call breakpoint's
breakpoint_ops.print_one_detail if available.
(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 9283e15..bf89a6d 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -112,6 +112,14 @@
libthread_db library with the "set libthread-db-search-path"
command. See the user manual for more details on this command.

+* When locally debugging programs on PowerPC BookE processors running
+ a Linux kernel version 2.6.34 or later, GDB supports masked hardware
+ watchpoints, which specifiy 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.
+
* New features in the GDB remote stub, GDBserver

** GDBserver is now supported on PowerPC LynxOS (versions 4.x and 5.x),
@@ -134,6 +142,19 @@ Analog Devices, Inc. Blackfin Processor bfin-*

** The --map-info flag lists all known core mappings.

+* 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.
+
+watch -location EXPRESSION
+watch -l EXPRESSION
+ The "watch" command now accepts an optional "-location" argument.
+ When used, this causes GDB to watch the memory referred to by the
+ expression. Such a watchpoint is never deleted due to it going out
+ of scope.
+
*** Changes in GDB 7.2

* Shared library support for remote targets by default
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index 890e091..2fda77f 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -10875,7 +10875,7 @@ print_recreate_exception (enum exception_catchpoint_kind ex,
/* Virtual table for "catch exception" breakpoints. */

static enum print_stop_action
-print_it_catch_exception (struct breakpoint *b)
+print_it_catch_exception (struct breakpoint *b, const struct value *old_val)
{
return print_it_exception (ex_catch_exception, b);
}
@@ -10904,8 +10904,10 @@ 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 */
print_mention_catch_exception,
print_recreate_catch_exception
};
@@ -10913,7 +10915,8 @@ static struct breakpoint_ops catch_exception_breakpoint_ops =
/* Virtual table for "catch exception unhandled" breakpoints. */

static enum print_stop_action
-print_it_catch_exception_unhandled (struct breakpoint *b)
+print_it_catch_exception_unhandled (struct breakpoint *b,
+ const struct value *old_val)
{
return print_it_exception (ex_catch_exception_unhandled, b);
}
@@ -10943,8 +10946,10 @@ 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 */
print_mention_catch_exception_unhandled,
print_recreate_catch_exception_unhandled
};
@@ -10952,7 +10957,7 @@ static struct breakpoint_ops catch_exception_unhandled_breakpoint_ops = {
/* Virtual table for "catch assert" breakpoints. */

static enum print_stop_action
-print_it_catch_assert (struct breakpoint *b)
+print_it_catch_assert (struct breakpoint *b, const struct value *old_val)
{
return print_it_exception (ex_catch_assert, b);
}
@@ -10980,8 +10985,10 @@ 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 */
print_mention_catch_assert,
print_recreate_catch_assert
};
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 8d0692b..7eef1b4 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -220,6 +220,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) \
@@ -1494,6 +1496,7 @@ update_watchpoint (struct breakpoint *b, int reparse)
{
int i, target_resources_ok, other_type_used;
enum enable_state orig_enable_state;
+ enum bptype orig_type;

/* We need to determine how many resources are already
used for all other hardware watchpoints plus this one
@@ -1502,6 +1505,7 @@ update_watchpoint (struct breakpoint *b, int reparse)
hw_watchpoint_used_count call below counts this
watchpoint, make sure that it is marked as a hardware
watchpoint. */
+ orig_type = b->type;
b->type = bp_hardware_watchpoint;

/* hw_watchpoint_used_count ignores disabled watchpoints,
@@ -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;
+ }

loc_type = (b->type == bp_watchpoint? bp_loc_other
: bp_loc_hardware_watchpoint);
@@ -3500,7 +3520,7 @@ print_bp_stop_message (bpstat bs)
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);
}
@@ -3636,15 +3656,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;
@@ -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;
else
{
/* Nothing changed. */
@@ -4846,9 +4886,12 @@ print_one_breakpoint_location (struct breakpoint *b,
ui_out_field_int (uiout, "task", b->task);
}
}
-
+
ui_out_text (uiout, "\n");
-
+
+ if (!part_of_multiple && b->ops && b->ops->print_one_detail)
+ b->ops->print_one_detail (b, uiout);
+
if (!part_of_multiple && b->static_trace_marker_id)
{
gdb_assert (b->type == bp_static_tracepoint);
@@ -6050,7 +6093,7 @@ breakpoint_hit_catch_fork (struct breakpoint *b)
catchpoints. */

static enum print_stop_action
-print_it_catch_fork (struct breakpoint *b)
+print_it_catch_fork (struct breakpoint *b, const struct value *old_val)
{
annotate_catchpoint (b->number);
printf_filtered (_("\nCatchpoint %d (forked process %d), "),
@@ -6110,8 +6153,10 @@ 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 */
print_mention_catch_fork,
print_recreate_catch_fork
};
@@ -6147,7 +6192,7 @@ breakpoint_hit_catch_vfork (struct breakpoint *b)
catchpoints. */

static enum print_stop_action
-print_it_catch_vfork (struct breakpoint *b)
+print_it_catch_vfork (struct breakpoint *b, const struct value *old_val)
{
annotate_catchpoint (b->number);
printf_filtered (_("\nCatchpoint %d (vforked process %d), "),
@@ -6206,8 +6251,10 @@ 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 */
print_mention_catch_vfork,
print_recreate_catch_vfork
};
@@ -6329,7 +6376,7 @@ breakpoint_hit_catch_syscall (struct breakpoint *b)
catchpoints. */

static enum print_stop_action
-print_it_catch_syscall (struct breakpoint *b)
+print_it_catch_syscall (struct breakpoint *b, const struct value *old_val)
{
/* These are needed because we want to know in which state a
syscall is. It can be in the TARGET_WAITKIND_SYSCALL_ENTRY
@@ -6490,8 +6537,10 @@ 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 */
print_mention_catch_syscall,
print_recreate_catch_syscall
};
@@ -6593,7 +6642,7 @@ breakpoint_hit_catch_exec (struct breakpoint *b)
}

static enum print_stop_action
-print_it_catch_exec (struct breakpoint *b)
+print_it_catch_exec (struct breakpoint *b, const struct value *old_val)
{
annotate_catchpoint (b->number);
printf_filtered (_("\nCatchpoint %d (exec'd %s), "), b->number,
@@ -6644,8 +6693,10 @@ 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 */
print_mention_catch_exec,
print_recreate_catch_exec
};
@@ -8291,12 +8342,211 @@ static struct breakpoint_ops watchpoint_breakpoint_ops =
remove_watchpoint,
NULL, /* breakpoint_hit */
resources_needed_watchpoint,
+ NULL, /* works_in_software_mode */
NULL, /* print_it */
NULL, /* print_one */
+ NULL, /* print_one_detail */
NULL, /* print_mention */
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 ();
+}
+
+/* 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,
+ const struct value *old_val)
+{
+ struct ui_stream *stb;
+ struct cleanup *old_chain;
+
+ 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 (old_val != NULL)
+ annotate_watchpoint (b->number);
+ 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)
+{
+ 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));
+
+ 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) */
@@ -8311,8 +8561,8 @@ 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;
int i, other_type_used, target_resources_ok = 0;
@@ -8320,66 +8570,95 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
int reg_cnt = 0;
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 hw_wp_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;
+
+ /* 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;

- /* Points tok to the end of the argument list. */
- tok = arg + toklen - 1;
+ if (thread != -1)
+ error(_("You can specify only one thread."));

- /* 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--;
+ /* Extract the thread ID from the next token. */
+ thread = strtol (value_start, &endp, 0);

- /* Points end_tok to the beginning of the last token. */
- id_tok_start = tok + 1;
+ /* 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);

- /* 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 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;

- end_tok = tok;
+ /* Does the target support masked watchpoints? */
+ if (!target_masked_watch_num_registers () < 0)
+ error (_("This target does not support the usage of "
+ "masks with hardware watchpoints."));

- 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 = 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);
+ hw_wp_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. */
@@ -8457,6 +8736,11 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
error (_("Expression cannot be implemented with read/access watchpoint."));
if (reg_cnt != 0)
{
+ /* If we are going to use masks, then we may need more
+ slots in order to use the hardware watchpoint. */
+ if (use_mask)
+ reg_cnt = target_masked_watch_num_registers ();
+
i = hw_watchpoint_used_count (bp_type, &other_type_used);
target_resources_ok =
target_can_use_hardware_watchpoint (bp_type, i + reg_cnt,
@@ -8469,10 +8753,18 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
"of HW watchpoint at a time."));
}

- /* Change the type of breakpoint to an ordinary watchpoint if a
- hardware watchpoint could not be set. */
if (!reg_cnt || target_resources_ok <= 0)
- bp_type = bp_watchpoint;
+ {
+ if (use_mask && !reg_cnt)
+ error (_("Cannot use masks with the given expression."));
+ else if (use_mask && target_resources_ok <= 0)
+ error (_("Cannot use masks without enough available "
+ "hardware watchpoints (need %d)."), reg_cnt);
+ else
+ /* Change the type of breakpoint to an ordinary watchpoint if a
+ hardware watchpoint could not be set. */
+ bp_type = bp_watchpoint;
+ }

frame = block_innermost_frame (exp_valid_block);

@@ -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;

/* Use an exact watchpoint when there's only one memory region to be
watched, and only one debug register is needed to watch it. */
@@ -8993,7 +9292,8 @@ catch_exec_command_1 (char *arg, int from_tty,
}

static enum print_stop_action
-print_exception_catchpoint (struct breakpoint *b)
+print_it_exception_catchpoint (struct breakpoint *b,
+ const struct value *old_val)
{
int bp_temp, bp_throw;

@@ -9084,8 +9384,10 @@ static struct breakpoint_ops gnu_v3_exception_catchpoint_ops = {
NULL, /* remove */
NULL, /* breakpoint_hit */
NULL, /* resources_needed */
- print_exception_catchpoint,
+ NULL, /* works_in_software_mode */
+ print_it_exception_catchpoint,
print_one_exception_catchpoint,
+ NULL, /* print_one_detail */
print_mention_exception_catchpoint,
print_recreate_exception_catchpoint
};
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index 69598a7..3195124 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -28,6 +28,7 @@
struct value;
struct block;
struct breakpoint_object;
+struct ui_out;

/* This is the maximum number of bytes a breakpoint instruction can
take. Feel free to increase it. It's just used in a few places to
@@ -390,14 +391,34 @@ 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 *);
+ enum print_stop_action (*print_it) (struct breakpoint *,
+ const struct value *old_val);

/* Display information about this breakpoint, for "info
breakpoints". */
void (*print_one) (struct breakpoint *, struct bp_location **);

+ /* Display extra information about this breakpoint, below the normal
+ breakpoint description in "info breakpoints".
+
+ In the example below, the line with "mask 0xffffff00" was printed
+ by print_one_detail_ranged_watchpoint.
+
+ (gdb) info breakpoints
+ Num Type Disp Enb Address What
+ 2 hw watchpoint keep y *0x10094420
+ mask 0xffffff00
+
+ */
+ void (*print_one_detail) (const struct breakpoint *, struct ui_out *);
+
/* Display information about this breakpoint after setting it
(roughly speaking; this is called from "mention"). */
void (*print_mention) (struct breakpoint *);
@@ -610,6 +631,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 d48d95c..355afcb 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3688,7 +3688,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
@@ -3699,12 +3699,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,
@@ -3715,12 +3720,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.

@@ -18684,6 +18689,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
+
@value{GDBN} provides the following PowerPC-specific commands:

@table @code
diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c
index 0c5563e..140ad97 100644
--- a/gdb/ppc-linux-nat.c
+++ b/gdb/ppc-linux-nat.c
@@ -1702,6 +1702,69 @@ 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 ());
+
+ 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."));
+
+ 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)
@@ -2173,6 +2236,16 @@ 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)
+{
+ return ((have_ptrace_booke_interface ()
+ && booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_MASK)?
+ 2 : -1);
+}
+
static void
ppc_linux_store_inferior_registers (struct target_ops *ops,
struct regcache *regcache, int regno)
@@ -2387,11 +2460,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_read_description = ppc_linux_read_description;
t->to_auxv_parse = ppc_linux_auxv_parse;
diff --git a/gdb/target.c b/gdb/target.c
index ff87931..d14ec6a 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -606,6 +606,8 @@ update_current_target (void)
INHERIT (to_remove_hw_breakpoint, t);
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);
@@ -613,6 +615,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);
@@ -3345,6 +3348,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 return_one ();
+}
+
+/* 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 return_one ();
+}
+
+/* The documentation for this function is in its prototype declaration
+ in target.h. */
+
+int
+target_masked_watch_num_registers (void)
+{
+ 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);
+
+ return return_minus_one ();
+}
+
static void
debug_to_prepare_to_store (struct regcache *regcache)
{
diff --git a/gdb/target.h b/gdb/target.h
index a1288d0..3f895cf 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -447,6 +447,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;
@@ -460,6 +464,7 @@ struct target_ops

int (*to_can_accel_watchpoint_condition) (CORE_ADDR, int, int,
struct expression *);
+ int (*to_masked_watch_num_registers) (struct target_ops *);
void (*to_terminal_init) (void);
void (*to_terminal_inferior) (void);
void (*to_terminal_ours_for_output) (void);
@@ -1331,6 +1336,20 @@ extern char *normal_pid_to_str (ptid_t ptid);
#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)

@@ -1359,6 +1378,11 @@ extern char *normal_pid_to_str (ptid_t ptid);
#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,
+ or -1 if masked watchpoints are not supported. */
+
+extern int target_masked_watch_num_registers (void);
+
/* Target can execute in reverse? */
#define target_can_execute_reverse \
(current_target.to_can_execute_reverse ? \