This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [patch 1/3] Implement support for PowerPC BookE masked watchpoints
- From: Thiago Jung Bauermann <bauerman at br dot ibm dot com>
- To: gdb-patches ml <gdb-patches at sourceware dot org>
- Cc: Jan Kratochvil <jan dot kratochvil at redhat dot com>, Joel Brobecker <brobecker at adacore dot com>, Eli Zaretskii <eliz at gnu dot org>
- Date: Thu, 23 Dec 2010 17:07:27 -0200
- Subject: Re: [patch 1/3] Implement support for PowerPC BookE masked watchpoints
- References: <1290548544.3164.37.camel@hactar>
Hi,
This is the latest version of this patch, refreshed to apply on top of
the ranged watchpoints patch.
I also got rid of the hw_point_flag enum, which was a source of
criticism before, at the expense of one additional target method per new
type of special breakpoint or watchpoint:
target_masked_watch_num_extra_regs and
target_ranged_break_num_extra_regs (for the upcoming new version of the
ranged breakpoints patch).
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 indentation change:
---- 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
2010-12-23 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)
(extra_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_extra_regs): New functions.
(_initialize_ppc_linux_nat): Initialize to_insert_mask_watchpoint,
to_remove_mask_watchpoint and to_masked_watch_num_extra_regs.
* target.c (update_current_target): Mention to_insert_mask_watchpoint,
to_remove_mask_watchpoint, and to_masked_watch_num_extra_regs.
(target_insert_mask_watchpoint, target_remove_mask_watchpoint)
(target_masked_watch_num_extra_regs): New functions.
* target.h (struct target_ops) <to_insert_mask_watchpoint>,
<to_remove_mask_watchpoint>, <to_masked_watch_num_extra_regs>: New
methods.
(target_insert_mask_watchpoint, target_remove_mask_watchpoint)
(target_masked_watch_num_extra_regs): 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 91f1835..c4a9c79 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -106,6 +106,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),
@@ -122,6 +130,19 @@
** 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 3a2a266..cd87a47 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -10793,7 +10793,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);
}
@@ -10822,8 +10822,10 @@ static struct breakpoint_ops catch_exception_breakpoint_ops =
NULL, /* remove */
NULL, /* breakpoint_hit */
NULL, /* extra_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
};
@@ -10831,7 +10833,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);
}
@@ -10861,8 +10864,10 @@ static struct breakpoint_ops catch_exception_unhandled_breakpoint_ops = {
NULL, /* remove */
NULL, /* breakpoint_hit */
NULL, /* extra_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
};
@@ -10870,7 +10875,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);
}
@@ -10898,8 +10903,10 @@ static struct breakpoint_ops catch_assert_breakpoint_ops = {
NULL, /* remove */
NULL, /* breakpoint_hit */
NULL, /* extra_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 9c10b30..9213289 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -219,6 +219,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) \
@@ -1402,7 +1404,14 @@ update_watchpoint (struct breakpoint *b, int reparse)
reg_cnt = can_use_hardware_watchpoint (val_chain, b->exact);
if (!reg_cnt)
- 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;
+ }
else
{
int i, other_type_used, target_resources_ok;
@@ -1425,7 +1434,17 @@ update_watchpoint (struct breakpoint *b, int reparse)
target_resources_ok = target_can_use_hardware_watchpoint
(bp_hardware_watchpoint, i + reg_cnt, 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))
+ {
+ b->type = bp_hardware_watchpoint;
+ error (_("This watchpoint cannot be used in software mode."));
+ }
+ else
+ b->type = bp_watchpoint;
+ }
else
b->type = bp_hardware_watchpoint;
}
@@ -3433,7 +3452,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);
}
@@ -3569,15 +3588,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 (¤t_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 (¤t_target,
+ newaddr, start,
+ loc->length))
+ {
+ b->watchpoint_triggered = watch_triggered_yes;
+ break;
+ }
+ }
}
return 1;
@@ -3691,6 +3725,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. */
@@ -4774,9 +4813,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);
@@ -5957,7 +5999,7 @@ breakpoint_hit_catch_fork (struct breakpoint *b)
/* Implement the "print_it" breakpoint_ops method for fork 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), "),
@@ -6016,8 +6058,10 @@ static struct breakpoint_ops catch_fork_breakpoint_ops =
remove_catch_fork,
breakpoint_hit_catch_fork,
NULL, /* extra_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
};
@@ -6050,7 +6094,7 @@ breakpoint_hit_catch_vfork (struct breakpoint *b)
/* Implement the "print_it" breakpoint_ops method for vfork 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), "),
@@ -6108,8 +6152,10 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops =
remove_catch_vfork,
breakpoint_hit_catch_vfork,
NULL, /* extra_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
};
@@ -6229,7 +6275,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
@@ -6390,8 +6436,10 @@ static struct breakpoint_ops catch_syscall_breakpoint_ops =
remove_catch_syscall,
breakpoint_hit_catch_syscall,
NULL, /* extra_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
};
@@ -6493,7 +6541,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,
@@ -6544,8 +6592,10 @@ static struct breakpoint_ops catch_exec_breakpoint_ops =
remove_catch_exec,
breakpoint_hit_catch_exec,
NULL, /* extra_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
};
@@ -8190,12 +8240,206 @@ static struct breakpoint_ops watchpoint_breakpoint_ops =
remove_watchpoint,
NULL, /* breakpoint_hit */
extra_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 "extra_resources_needed" breakpoint_ops method for
+ masked hardware watchpoints. */
+
+static int
+extra_resources_needed_masked_watchpoint (const struct breakpoint *b,
+ const struct bp_location *bl)
+{
+ return target_masked_watch_num_extra_regs ();
+}
+
+/* Implement the "works_in_software_mode_masked_watchpoint" 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, _("\nCheck the underlying instruction \
+at PC for address and value related to this watchpoint trigger.\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);
+}
+
+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 */
+ extra_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
+};
+
+/* Tells 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) */
@@ -8210,8 +8454,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;
@@ -8219,66 +8463,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;
- /* 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--;
+ /* 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."));
- /* Move the pointer forward to skip the whitespace and
- calculate the length of the token. */
- tok++;
- toklen = end_tok - tok;
+ if (use_mask)
+ error(_("You can specify only one mask."));
- 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';
- }
+ 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';
+ }
}
/* Parse the rest of the arguments. */
@@ -8356,6 +8629,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_extra_regs ();
+
i = hw_watchpoint_used_count (bp_type, &other_type_used);
target_resources_ok =
target_can_use_hardware_watchpoint (bp_type, i + reg_cnt,
@@ -8367,10 +8645,19 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
error (_("Target can only support one kind 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);
@@ -8438,7 +8725,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 only when there's one memory region to be
watched, which needs only one debug register. */
@@ -8890,7 +9184,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;
@@ -8980,8 +9275,10 @@ static struct breakpoint_ops gnu_v3_exception_catchpoint_ops = {
NULL, /* remove */
NULL, /* breakpoint_hit */
NULL, /* extra_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 8e68532..74565c0 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -27,6 +27,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 size
@@ -382,13 +383,33 @@ struct breakpoint_ops
int (*extra_resources_needed) (const struct breakpoint *,
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 *);
@@ -597,6 +618,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 cef320e..b7ab17b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3745,7 +3745,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
@@ -3756,12 +3756,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,
@@ -3772,12 +3777,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.
@@ -18748,6 +18753,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 7c853a9..7806df0 100644
--- a/gdb/ppc-linux-nat.c
+++ b/gdb/ppc-linux-nat.c
@@ -1692,6 +1692,59 @@ get_trigger_type (int rw)
return t;
}
+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;
+}
+
+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)
@@ -2158,6 +2211,14 @@ ppc_linux_watchpoint_addr_within_range (struct target_ops *target,
return start <= addr + mask && start + length - 1 >= addr;
}
+static int
+ppc_linux_masked_watch_num_extra_regs (struct target_ops *target)
+{
+ return ((have_ptrace_booke_interface ()
+ && booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_MASK)?
+ 1 : -1);
+}
+
static void
ppc_linux_store_inferior_registers (struct target_ops *ops,
struct regcache *regcache, int regno)
@@ -2371,10 +2432,13 @@ _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_extra_regs = ppc_linux_masked_watch_num_extra_regs;
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 dd976c9..0283425 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_extra_regs. */
INHERIT (to_terminal_init, t);
INHERIT (to_terminal_inferior, t);
INHERIT (to_terminal_ours_for_output, t);
@@ -3334,6 +3337,64 @@ target_verify_memory (const gdb_byte *data, CORE_ADDR memaddr, ULONGEST size)
tcomplain ();
}
+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_minus_one ();
+}
+
+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_minus_one ();
+}
+
+int
+target_masked_watch_num_extra_regs (void)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_masked_watch_num_extra_regs != NULL)
+ return t->to_masked_watch_num_extra_regs (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 2ef5c56..d956e98 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -446,6 +446,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;
@@ -459,6 +463,7 @@ struct target_ops
int (*to_can_accel_watchpoint_condition) (CORE_ADDR, int, int,
struct expression *);
+ int (*to_masked_watch_num_extra_regs) (struct target_ops *);
void (*to_terminal_init) (void);
void (*to_terminal_inferior) (void);
void (*to_terminal_ours_for_output) (void);
@@ -1336,6 +1341,11 @@ 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)
+/* Hardware watchpoints with an associated address mask. */
+
+extern int target_insert_mask_watchpoint (CORE_ADDR, CORE_ADDR, int);
+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)
@@ -1364,6 +1374,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 extra debug registers needed for a masked watchpoint,
+ or -1 if masked watchpoints are not supported. */
+
+extern int target_masked_watch_num_extra_regs (void);
+
/* Target can execute in reverse? */
#define target_can_execute_reverse \
(current_target.to_can_execute_reverse ? \