This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [patch 2/2] Implement support for PowerPC BookE masked and ranged watchpoints
- From: Thiago Jung Bauermann <bauerman at br dot ibm dot com>
- To: gdb-patches ml <gdb-patches at sourceware dot org>
- Date: Sat, 23 Oct 2010 02:22:41 -0200
- Subject: Re: [patch 2/2] Implement support for PowerPC BookE masked and ranged watchpoints
- References: <1282074110.2606.703.camel@hactar>
Hi,
This is a new version of the patch. It doesn't rely on an old_ops field
in struct breakpoint. Instead, it makes the breakpoint_ops methods for
ranged watchpoint accept ranged software watchpoints (this was trivial
and involved only new cases in some print methods), and adds a
works_in_software_mode method which is called by update_watchpoint if it
determines that it needs to downgrade a hardware watchpoint to a
software watchpoint. In case it can't be done, it'll throw an exception.
There are no regressions on ppc32-linux, ppc64-linux and i386-linux. Ok?
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
2010-10-23 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
Thiago Jung Bauermann <bauerman@br.ibm.com>
Implement support for PowerPC BookE masked and ranged watchpoints.
gdb/
* breakpoint.c (update_watchpoint): Call breakpoint's
breakpoint_ops.extra_resources_needed and
breakpoint_ops.works_in_software_mode if available.
(watchpoints_triggered): Handle the case of a hardware masked
watchpoint trigger.
(watchpoint_check): Handle the case of a hardware masked watchpoint
trigger.
(print_one_breakpoint_location): Call breakpoint's
breakpoint_ops.print_one_detail if available.
(hw_watchpoint_used_count): Call breakpoint's
breakpoint_ops.extra_resources_needed if available.
(insert_ranged_watchpoint, remove_ranged_watchpoint)
(extra_resources_needed_ranged_watchpoint)
(print_one_detail_ranged_watchpoint, print_mention_ranged_watchpoint)
(print_recreate_ranged_watchpoint): New functions.
(ranged_watchpoint_breakpoint_ops): New structure.
(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.
Check whether a ranged hardware watchpoint can be used. Set b->ops
according to the type of hardware watchpoint being created.
(watch_range_command_1, watch_range_command)
(awatch_range_command, rwatch_range_command): New functions.
(_initialize_breakpoint): Register watch-range, awatch-range and
rwatch-range commands.
* breakpoint.h (struct ui_out): New opaque declaration.
(struct breakpoint_ops) <extra_resources_needed>,
<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.
(enum hw_point_flag): New enumeration.
(struct breakpoint) <hw_wp_mask>: New field.
* findcmd.c (parse_addr_range): New function factored out of
parse_find_args.
(parse_find_args): Call `parse_addr_range'.
* ppc-linux-nat.c (ppc_linux_can_use_special_hw_point): New function.
(ppc_linux_region_ok_for_hw_watchpoint): Always handle regions when
ranged watchpoints are available.
(ppc_linux_insert_mask_watchpoint, ppc_linux_remove_mask_watchpoint)
(ppc_linux_insert_ranged_watchpoint, ppc_linux_remove_ranged_watchpoint)
(ppc_linux_hw_point_extra_slot_count): New functions.
(_initialize_ppc_linux_nat): Initialize to_insert_mask_watchpoint,
to_remove_mask_watchpoint, to_insert_ranged_watchpoint,
to_remove_ranged_watchpoint, hw_point_extra_slot_count and
to_can_use_special_hw_point.
* target.c (update_current_target): Insert to_insert_mask_watchpoint,
to_remove_mask_watchpoint, to_insert_ranged_watchpoint,
to_remove_ranged_watchpoint, to_hw_point_extra_slot_count
and to_can_use_special_hw_point.
* target.h (enum hw_point_flag): New opaque declaration.
(struct target_ops) <to_insert_mask_watchpoint>,
<to_remove_mask_watchpoint>, <to_insert_ranged_watchpoint>,
<to_remove_ranged_watchpoint>, <to_hw_point_extra_slot_count>,
<to_can_use_special_hw_point>: New callbacks.
(target_insert_mask_watchpoint, target_remove_mask_watchpoint)
(target_insert_ranged_watchpoint, target_remove_ranged_watchpoint)
(target_hw_point_extra_slot_count, target_can_use_special_hw_point): New
defines.
* ui-out.c (ui_out_field_range_core_addr): New function.
* ui-out.h (ui_out_field_range_core_addr): Declare.
* value.h (parse_addr_range): Declare.
gdb/doc/
* gdb.texinfo (Set Watchpoints): Document mask parameter.
(PowerPC Embedded): Document masked watchpoints and ranged watchpoints.
Index: gdb.git/gdb/ada-lang.c
===================================================================
--- gdb.git.orig/gdb/ada-lang.c 2010-10-13 15:59:11.000000000 -0300
+++ gdb.git/gdb/ada-lang.c 2010-10-22 18:41:37.000000000 -0200
@@ -10756,7 +10756,7 @@ print_recreate_exception (enum exception
/* 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);
}
@@ -10784,8 +10784,11 @@ static struct breakpoint_ops catch_excep
NULL, /* insert */
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
};
@@ -10793,7 +10796,8 @@ static struct breakpoint_ops catch_excep
/* 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);
}
@@ -10822,8 +10826,11 @@ static struct breakpoint_ops catch_excep
NULL, /* insert */
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
};
@@ -10831,7 +10838,7 @@ static struct breakpoint_ops catch_excep
/* 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);
}
@@ -10858,8 +10865,11 @@ static struct breakpoint_ops catch_asser
NULL, /* insert */
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
};
Index: gdb.git/gdb/breakpoint.c
===================================================================
--- gdb.git.orig/gdb/breakpoint.c 2010-10-22 18:41:34.000000000 -0200
+++ gdb.git/gdb/breakpoint.c 2010-10-22 18:41:37.000000000 -0200
@@ -219,6 +219,8 @@ static void disable_trace_command (char
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) \
@@ -1391,27 +1393,52 @@ update_watchpoint (struct breakpoint *b,
if ((b->type == bp_watchpoint || b->type == bp_hardware_watchpoint)
&& reparse)
{
- int i, mem_cnt, other_type_used;
+ int mem_cnt;
- /* We need to determine how many resources are already used
- for all other hardware watchpoints to see if we still have
- enough resources to also fit this watchpoint in as well.
- To avoid the hw_watchpoint_used_count call below from counting
- this watchpoint, make sure that it is marked as a software
- watchpoint. */
- b->type = bp_watchpoint;
- i = hw_watchpoint_used_count (bp_hardware_watchpoint,
- &other_type_used);
mem_cnt = can_use_hardware_watchpoint (val_chain);
if (!mem_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 target_resources_ok = target_can_use_hardware_watchpoint
- (bp_hardware_watchpoint, i + mem_cnt, other_type_used);
+ int i, other_type_used, target_resources_ok;
+ enum bptype orig_type;
+
+ if (b->ops && b->ops->extra_resources_needed)
+ mem_cnt += b->ops->extra_resources_needed (b);
+
+ /* We need to determine how many resources are already used
+ for all other hardware watchpoints to see if we still have
+ enough resources to also fit this watchpoint in as well.
+ To avoid the hw_watchpoint_used_count call below from
+ counting this watchpoint, make sure that it is marked as a
+ software watchpoint. */
+ orig_type = b->type;
+ b->type = bp_watchpoint;
+ i = hw_watchpoint_used_count (bp_hardware_watchpoint,
+ &other_type_used);
+
+ target_resources_ok = target_can_use_hardware_watchpoint
+ (bp_hardware_watchpoint, i + mem_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;
}
@@ -3356,7 +3383,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);
}
@@ -3492,15 +3519,30 @@ watchpoints_triggered (struct target_wai
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;
@@ -3614,6 +3656,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. */
@@ -4684,9 +4731,12 @@ print_one_breakpoint_location (struct br
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);
@@ -5857,7 +5907,7 @@ breakpoint_hit_catch_fork (struct breakp
/* 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), "),
@@ -5915,8 +5965,11 @@ static struct breakpoint_ops catch_fork_
insert_catch_fork,
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
};
@@ -5949,7 +6002,7 @@ breakpoint_hit_catch_vfork (struct break
/* 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), "),
@@ -6006,8 +6059,11 @@ static struct breakpoint_ops catch_vfork
insert_catch_vfork,
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
};
@@ -6127,7 +6183,7 @@ breakpoint_hit_catch_syscall (struct bre
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
@@ -6287,8 +6343,11 @@ static struct breakpoint_ops catch_sysca
insert_catch_syscall,
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
};
@@ -6390,7 +6449,7 @@ breakpoint_hit_catch_exec (struct breakp
}
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,
@@ -6440,8 +6499,11 @@ static struct breakpoint_ops catch_exec_
insert_catch_exec,
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
};
@@ -6489,7 +6551,14 @@ hw_watchpoint_used_count (enum bptype ty
if (breakpoint_enabled (b))
{
if (b->type == type)
- i++;
+ {
+ i++;
+
+ /* Special types of hardware watchpoints can use more than
+ one register. */
+ if (b->ops && b->ops->extra_resources_needed)
+ i += b->ops->extra_resources_needed (b);
+ }
else if (is_hardware_watchpoint (b))
*other_type_used = 1;
}
@@ -8031,12 +8100,353 @@ static struct breakpoint_ops watchpoint_
insert_watchpoint,
remove_watchpoint,
NULL, /* breakpoint_hit */
+ NULL, /* extra_resources_needed */
+ 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
+ ranged hardware watchpoints. */
+
+static int
+insert_ranged_watchpoint (struct bp_location *bpt)
+{
+ return target_insert_ranged_watchpoint (bpt->address,
+ bpt->length,
+ bpt->watchpoint_type);
+}
+
+/* Implement the "remove" breakpoint_ops method for
+ ranged hardware watchpoints. */
+
+static int
+remove_ranged_watchpoint (struct bp_location *bpt)
+{
+ return target_remove_ranged_watchpoint (bpt->address, bpt->length,
+ bpt->watchpoint_type);
+}
+
+/* Implement the "extra_resources_needed" breakpoint_ops method for
+ ranged hardware watchpoints. */
+
+static int
+extra_resources_needed_ranged_watchpoint (const struct breakpoint *b)
+{
+ return target_hw_point_extra_slot_count (HW_POINT_RANGED_WATCH);
+}
+
+/* Implement the "print_one_detail" breakpoint_ops method for
+ ranged hardware watchpoints. */
+
+static void
+print_one_detail_ranged_watchpoint (const struct breakpoint *b, struct ui_out *uiout)
+{
+ /* If there's a separate expression for reparsing, then exp_string is already
+ a nice text set by watch_range_command_1 and was printed earlier. */
+ if (b->exp_string_reparse)
+ return;
+
+ gdb_assert (b->loc);
+
+ ui_out_text (uiout, "\tmemory range: ");
+ ui_out_field_range_core_addr (uiout, "addr", b->loc->gdbarch,
+ b->loc->address, b->loc->length);
+ ui_out_text (uiout, "\n");
+}
+
+/* Implement the "print_mention" breakpoint_ops method for
+ ranged hardware watchpoints. */
+
+static void
+print_mention_ranged_watchpoint (struct breakpoint *b)
+{
+ struct cleanup *ui_out_chain;
+
+ switch (b->type)
+ {
+ case bp_watchpoint:
+ ui_out_text (uiout, "Ranged watchpoint ");
+ ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "wpt");
+ break;
+ case bp_hardware_watchpoint:
+ ui_out_text (uiout, "Ranged hardware watchpoint ");
+ ui_out_chain = make_cleanup_ui_out_tuple_begin_end (uiout, "wpt");
+ break;
+ case bp_read_watchpoint:
+ ui_out_text (uiout, "Ranged 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, "Ranged 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_ranged_watchpoint (struct breakpoint *b, struct ui_file *fp)
+{
+ switch (b->type)
+ {
+ case bp_watchpoint:
+ case bp_hardware_watchpoint:
+ if (b->exp_string_reparse)
+ fprintf_unfiltered (fp, "watch-range");
+ else
+ fprintf_unfiltered (fp, "watch");
+ break;
+ case bp_read_watchpoint:
+ if (b->exp_string_reparse)
+ fprintf_unfiltered (fp, "rwatch-range");
+ else
+ fprintf_unfiltered (fp, "rwatch");
+ break;
+ case bp_access_watchpoint:
+ if (b->exp_string_reparse)
+ fprintf_unfiltered (fp, "awatch-range");
+ else
+ fprintf_unfiltered (fp, "awatch");
+ break;
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("Invalid hardware watchpoint type."));
+ }
+
+ if (b->exp_string_reparse)
+ {
+ char start_addr[40], length[40];
+
+ /* watch_range_command_1 creates the following expression to represent
+ internally a ranged watchpoint. */
+ sscanf (b->exp_string_reparse, "{char[ %39s ]} %39s", length, start_addr);
+
+ fprintf_unfiltered (fp, " %s, +%s", start_addr, length);
+ }
+ else
+ fprintf_unfiltered (fp, " %s", b->exp_string);
+}
+
+/* The breakpoint_ops structure to be used in ranged hardware watchpoints. */
+
+static struct breakpoint_ops ranged_watchpoint_breakpoint_ops =
+{
+ insert_ranged_watchpoint,
+ remove_ranged_watchpoint,
+ NULL, /* breakpoint_hit */
+ extra_resources_needed_ranged_watchpoint,
+ NULL, /* works_in_software_mode */
+ NULL, /* print_it */
+ NULL, /* print_one */
+ print_one_detail_ranged_watchpoint,
+ print_mention_ranged_watchpoint,
+ print_recreate_ranged_watchpoint
+};
+
+/* Implement the "insert" breakpoint_ops method for
+ masked hardware watchpoints. */
+
+static int
+insert_masked_watchpoint (struct bp_location *bpt)
+{
+ return target_insert_mask_watchpoint (bpt->address, bpt->owner->hw_wp_mask,
+ bpt->watchpoint_type);
+}
+
+/* Implement the "remove" breakpoint_ops method for
+ masked hardware watchpoints. */
+
+static int
+remove_masked_watchpoint (struct bp_location *bpt)
+{
+ return target_remove_mask_watchpoint (bpt->address, bpt->owner->hw_wp_mask,
+ bpt->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)
+{
+ return target_hw_point_extra_slot_count (HW_POINT_MASKED_WATCH);
+}
+
+/* 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) */
@@ -8050,8 +8460,8 @@ watch_command_1 (char *arg, int accessfl
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;
@@ -8059,66 +8469,98 @@ watch_command_1 (char *arg, int accessfl
int mem_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;
+ /* Whether we are watching an array or struct and hence we will
+ try to use ranged hardware watchpoints, if available. */
+ int use_ranged = 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;
+
+ if (thread != -1)
+ error(_("You can specify only one thread."));
+
+ /* Extract the thread ID from the next token. */
+ 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."), 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;
- /* 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--;
-
- /* 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. */
- while (tok > arg && (*tok == ' ' || *tok == '\t'))
- tok--;
+ /* Does the target support masked watchpoints? */
+ if (!target_can_use_special_hw_point (HW_POINT_MASKED_WATCH))
+ error (_("\
+This target does not support the usage of masks with hardware watchpoints."));
- end_tok = tok;
+ if (use_mask)
+ error(_("You can specify only one mask."));
- while (tok > arg && (*tok != ' ' && *tok != '\t'))
- tok--;
+ use_mask = 1;
- /* Move the pointer forward to skip the whitespace and
- calculate the length of the token. */
- tok++;
- toklen = end_tok - tok;
+ 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;
- 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';
- }
+ /* 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. */
@@ -8196,6 +8638,22 @@ watch_command_1 (char *arg, int accessfl
error (_("Expression cannot be implemented with read/access watchpoint."));
if (mem_cnt != 0)
{
+ struct type *vtype = check_typedef (value_type (val));
+
+ /* If we are going to use masks, then we may need more
+ slots in order to use the hardware watchpoint. */
+ if (use_mask)
+ mem_cnt += target_hw_point_extra_slot_count (HW_POINT_MASKED_WATCH);
+ /* If we are watching an array or struct, we may be able to do it using
+ a ranged watchpoint. */
+ else if ((TYPE_CODE (vtype) == TYPE_CODE_STRUCT
+ || TYPE_CODE (vtype) == TYPE_CODE_ARRAY)
+ && target_can_use_special_hw_point (HW_POINT_RANGED_WATCH))
+ {
+ use_ranged = 1;
+ mem_cnt += target_hw_point_extra_slot_count (HW_POINT_RANGED_WATCH);
+ }
+
i = hw_watchpoint_used_count (bp_type, &other_type_used);
target_resources_ok =
target_can_use_hardware_watchpoint (bp_type, i + mem_cnt,
@@ -8207,10 +8665,22 @@ watch_command_1 (char *arg, int accessfl
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 (!mem_cnt || target_resources_ok <= 0)
- bp_type = bp_watchpoint;
+ {
+ if (use_mask && !mem_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)."),
+ mem_cnt);
+ else
+ {
+ /* Change the type of breakpoint to an ordinary watchpoint if a
+ hardware watchpoint could not be set. */
+ bp_type = bp_watchpoint;
+ use_ranged = 0;
+ }
+ }
frame = block_innermost_frame (exp_valid_block);
@@ -8279,7 +8749,16 @@ watch_command_1 (char *arg, int accessfl
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 if (use_ranged)
+ b->ops = &ranged_watchpoint_breakpoint_ops;
+ else
+ b->ops = &watchpoint_breakpoint_ops;
if (cond_start)
b->cond_string = savestring (cond_start, cond_end - cond_start);
@@ -8466,6 +8945,92 @@ awatch_command (char *arg, int from_tty)
{
watch_maybe_just_location (arg, hw_access, from_tty);
}
+
+static void
+watch_range_command_1 (char *arg, enum bptype type, int from_tty)
+{
+ char *exp_string, *string_p;
+ struct gdbarch *gdbarch = get_current_arch ();
+ int wp_count, other_type_used, can_use_wp, mem_cnt, pc = 0;
+ CORE_ADDR start_addr;
+ ULONGEST length;
+ struct breakpoint *b;
+ struct expression *exp;
+ struct symtab_and_line sal;
+ struct value *val;
+ struct cleanup *cleanups;
+
+ /* Do we support ranged hardware watchpoints? */
+ if (!target_can_use_special_hw_point (HW_POINT_RANGED_WATCH))
+ error (_("This target does not support ranged hardware watchpoints."));
+ /* Did the user specifically forbid us to use hardware watchpoints? */
+ else if (!can_use_hw_watchpoints)
+ error (_("\
+Hardware watchpoints are disabled (see \"set can-use-hw-watchpoints\")."));
+
+ parse_addr_range (&arg, &start_addr, &length);
+
+ /* We need spaces around the brackets in the expression below so that
+ print_it_recreate_ranged_watchpoint can use scanf on it. */
+ exp_string = string_p = xstrprintf ("{char[ %s ]} %s", pulongest (length),
+ paddress (gdbarch, start_addr));
+ exp = parse_exp_1 (&string_p, 0, 0);
+ fetch_subexp_value (exp, &pc, &val, NULL, NULL);
+ if (val != NULL)
+ release_value (val);
+ cleanups = make_cleanup (xfree, exp_string);
+
+ mem_cnt = target_hw_point_extra_slot_count (HW_POINT_RANGED_WATCH) + 1;
+
+ wp_count = hw_watchpoint_used_count (type, &other_type_used);
+ can_use_wp = target_can_use_hardware_watchpoint (type, wp_count + mem_cnt,
+ other_type_used);
+ if (can_use_wp < 0)
+ error (_("Not enough available hardware watchpoints (need %d)."), mem_cnt);
+
+ init_sal (&sal); /* initialize to zeroes */
+ sal.pspace = current_program_space;
+
+ /* Now set up the breakpoint. */
+ b = set_raw_breakpoint (gdbarch, sal, type);
+ set_breakpoint_count (breakpoint_count + 1);
+ b->number = breakpoint_count;
+ b->thread = -1;
+ b->disposition = disp_donttouch;
+ b->exp = exp;
+ b->exp_string_reparse = exp_string;
+ b->exp_string = xstrprintf (_("range [%s, %s]"),
+ paddress (gdbarch, start_addr),
+ paddress (gdbarch, start_addr + length - 1));
+ b->ops = &ranged_watchpoint_breakpoint_ops;
+ b->val = val;
+ b->val_valid = 1;
+ b->watchpoint_frame = null_frame_id;
+
+ mention (b);
+ update_global_location_list (1);
+
+ discard_cleanups (cleanups);
+}
+
+static void
+watch_range_command (char *arg, int from_tty)
+{
+ watch_range_command_1 (arg, bp_hardware_watchpoint, from_tty);
+}
+
+static void
+awatch_range_command (char *arg, int from_tty)
+{
+ watch_range_command_1 (arg, bp_access_watchpoint, from_tty);
+}
+
+static void
+rwatch_range_command (char *arg, int from_tty)
+{
+ watch_range_command_1 (arg, bp_read_watchpoint, from_tty);
+}
+
/* Helper routines for the until_command routine in infcmd.c. Here
@@ -8703,7 +9268,8 @@ catch_exec_command_1 (char *arg, int fro
}
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;
@@ -8792,8 +9358,11 @@ static struct breakpoint_ops gnu_v3_exce
NULL, /* insert */
NULL, /* remove */
NULL, /* breakpoint_hit */
- print_exception_catchpoint,
+ NULL, /* extra_resources_needed */
+ 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
};
@@ -12183,7 +12752,41 @@ inferior in all-stop mode, gdb behaves a
&show_always_inserted_mode,
&breakpoint_set_cmdlist,
&breakpoint_show_cmdlist);
-
+
+ c = add_com ("watch-range", class_breakpoint, watch_range_command, _("\
+Set a hardware watchpoint for an address range.\n\
+The address range should be specified in one of the following formats:\n\
+\n\
+ start-address, end-address\n\
+ start-address, +length\n\
+\n\
+The watchpoint will stop execution of your program whenever the inferior\n\
+writes to any address within the [start-address, end-address] interval."));
+ set_cmd_completer (c, expression_completer);
+
+ c = add_com ("awatch-range", class_breakpoint, awatch_range_command, _("\
+Set an access hardware watchpoint for an address range.\n\
+The address range should be specified in one of the following formats:\n\
+\n\
+ start-address, end-address\n\
+ start-address, +length\n\
+\n\
+The watchpoint will stop execution of your program whenever the inferior\n\
+accesses (reads from or writes to) any address within the\n\
+[start-address, end-address] interval."));
+ set_cmd_completer (c, expression_completer);
+
+ c = add_com ("rwatch-range", class_breakpoint, rwatch_range_command, _("\
+Set a read hardware watchpoint for an address range.\n\
+The address range should be specified in one of the following formats:\n\
+\n\
+ start-address, end-address\n\
+ start-address, +length\n\
+\n\
+The watchpoint will stop execution of your program whenever the inferior\n\
+reads from any address within the [start-address, end-address] interval."));
+ set_cmd_completer (c, expression_completer);
+
automatic_hardware_breakpoints = 1;
observer_attach_about_to_proceed (breakpoint_about_to_proceed);
Index: gdb.git/gdb/breakpoint.h
===================================================================
--- gdb.git.orig/gdb/breakpoint.h 2010-10-22 18:41:34.000000000 -0200
+++ gdb.git/gdb/breakpoint.h 2010-10-22 18:41:37.000000000 -0200
@@ -26,6 +26,7 @@
struct value;
struct block;
+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
@@ -361,13 +362,28 @@ struct breakpoint_ops
breakpoint was hit. */
int (*breakpoint_hit) (struct breakpoint *);
+ /* Tell how many additional hardware resources (debug registers) are needed
+ for this breakpoint. We always count at least one resource. If this
+ element is NULL, then no additional resource is accounted for. */
+ int (*extra_resources_needed) (const struct breakpoint *);
+
+ /* 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 one line of extra information about this breakpoint,
+ for "info breakpoints". */
+ 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 *);
@@ -400,6 +416,12 @@ DEF_VEC_P(bp_location_p);
detail to the breakpoints module. */
struct counted_command_line;
+/* Special types of hardware breakpoints/watchpoints. */
+enum hw_point_flag {
+ HW_POINT_RANGED_WATCH, /* Hardware ranged watchpoint. */
+ HW_POINT_MASKED_WATCH /* Hardware masked watchpoint. */
+};
+
/* Note that the ->silent field is not currently used by any commands
(though the code is in there if it was to be, and set_raw_breakpoint
does set it to 0). I implemented it because I thought it would be
@@ -559,6 +581,9 @@ struct breakpoint
breakpoints, we will use this index to try to find the same
marker again. */
int static_trace_marker_id_idx;
+
+ /* The mask address for a hardware watchpoint. */
+ CORE_ADDR hw_wp_mask;
};
typedef struct breakpoint *breakpoint_p;
Index: gdb.git/gdb/doc/gdb.texinfo
===================================================================
--- gdb.git.orig/gdb/doc/gdb.texinfo 2010-10-19 21:04:59.000000000 -0200
+++ gdb.git/gdb/doc/gdb.texinfo 2010-10-22 18:41:37.000000000 -0200
@@ -3711,7 +3711,7 @@ watchpoints, which do not slow down the
@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
@@ -3728,6 +3728,11 @@ change the value of @var{expr}, @value{G
that watchpoints restricted to a single thread in this way only work
with Hardware Watchpoints.
+The @code{@r{[}mask @var{maskvalue}@r{]}} clause is used to create a masked
+watchpoint if the current architecture supports the feature (currently,
+only available in the 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,
@@ -3738,12 +3743,12 @@ result does not have an address, then @v
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.
@@ -18517,9 +18522,41 @@ The DVC register will be automatically u
such pattern in a condition expression. This feature is available in native
@value{GDBN} running on a Linux kernel version 2.6.34 or newer.
+PowerPC embedded processors support additional types of hardware watchpoints,
+namely masked watchpoints and ranged watchpoints.
+
+A @dfn{masked watchpoint} is defined by an address and a mask. It triggers
+when the address of a memory access matches the watchpoint address when both
+are masked by the watchpoint mask. That is, the bits set in the mask determine
+which bits are relevant in the address comparison. To set a masked watchpoint
+in @value{GDBN}, use the @code{mask} parameter in the @code{watch} command
+(see @ref{Set Watchpoints}), as in:
+
+@smallexample
+(@value{GDBP}) watch *0xdeadbeef mask 0xffffff00
+@end smallexample
+
+A @dfn{ranged watchpoint} is defined by a start address and an end address
+specifying a region of memory inside which any access will trigger the
+watchpoint. Both the start and end addresses are within the memory region.
+To set a ranged watchpoint in @value{GDBN}, use the @code{watch-range} command.
+
@value{GDBN} provides the following PowerPC-specific commands:
@table @code
+@kindex watch-range
+@item watch-range @var{start-address, +length}
+@itemx watch-range @var{start-address, end-address}
+@item rwatch-range @var{start-address, +length}
+@itemx rwatch-range @var{start-address, end-address}
+@item awatch-range @var{start-address, +length}
+@itemx awatch-range @var{start-address, end-address}
+Set a hardware watchpoint for an address range.
+The watchpoint will stop execution of your program whenever the inferior
+writes, reads, or accesses (respectively for watch-range, awatch-range
+and rwatch-range) any address within the @code{[start-address, end-address]}
+interval.
+
@kindex set powerpc
@item set powerpc soft-float
@itemx show powerpc soft-float
Index: gdb.git/gdb/findcmd.c
===================================================================
--- gdb.git.orig/gdb/findcmd.c 2010-08-20 16:00:16.000000000 -0300
+++ gdb.git/gdb/findcmd.c 2010-10-22 18:41:37.000000000 -0200
@@ -45,6 +45,79 @@ put_bits (bfd_uint64_t data, char *buf,
}
}
+/* Reads an address range, in one of the following formats:
+
+ start-address, end-address
+ start-address, +length
+
+ ARGS will be set to the first character after the end-address or length,
+ or if that character is a comma, the character following it. If a parser
+ error occurs, an exception is thrown and none of the arguments is
+ touched. Returns 1 on success, or 0 if an empty address range was given. */
+
+int
+parse_addr_range (char **args, CORE_ADDR *start_addrp,
+ ULONGEST *search_space_lenp)
+{
+ char *s = *args;
+ CORE_ADDR start_addr;
+ ULONGEST search_space_len;
+ struct value *v;
+
+ v = parse_to_comma_and_eval (&s);
+ start_addr = value_as_address (v);
+
+ if (*s == ',')
+ ++s;
+ while (isspace (*s))
+ ++s;
+
+ if (*s == '+')
+ {
+ LONGEST len;
+
+ ++s;
+ v = parse_to_comma_and_eval (&s);
+ len = value_as_long (v);
+ if (len == 0)
+ {
+ printf_filtered (_("Empty search range.\n"));
+ return 0;
+ }
+ if (len < 0)
+ error (_("Invalid length."));
+ /* Watch for overflows. */
+ if (len > CORE_ADDR_MAX
+ || (start_addr + len - 1) < start_addr)
+ error (_("Search space too large."));
+ search_space_len = len;
+ }
+ else
+ {
+ CORE_ADDR end_addr;
+
+ v = parse_to_comma_and_eval (&s);
+ end_addr = value_as_address (v);
+ if (start_addr > end_addr)
+ error (_("Invalid search space, end preceeds start."));
+ search_space_len = end_addr - start_addr + 1;
+ /* We don't support searching all of memory
+ (i.e. start=0, end = 0xff..ff).
+ Bail to avoid overflows later on. */
+ if (search_space_len == 0)
+ error (_("Overflow in address range computation, choose smaller range."));
+ }
+
+ if (*s == ',')
+ ++s;
+
+ *args = s;
+ *start_addrp = start_addr;
+ *search_space_lenp = search_space_len;
+
+ return 1;
+}
+
/* Subroutine of find_command to simplify it.
Parse the arguments of the "find" command. */
@@ -114,53 +187,8 @@ parse_find_args (char *args, ULONGEST *m
}
/* Get the search range. */
-
- v = parse_to_comma_and_eval (&s);
- start_addr = value_as_address (v);
-
- if (*s == ',')
- ++s;
- while (isspace (*s))
- ++s;
-
- if (*s == '+')
- {
- LONGEST len;
-
- ++s;
- v = parse_to_comma_and_eval (&s);
- len = value_as_long (v);
- if (len == 0)
- {
- printf_filtered (_("Empty search range.\n"));
- return;
- }
- if (len < 0)
- error (_("Invalid length."));
- /* Watch for overflows. */
- if (len > CORE_ADDR_MAX
- || (start_addr + len - 1) < start_addr)
- error (_("Search space too large."));
- search_space_len = len;
- }
- else
- {
- CORE_ADDR end_addr;
-
- v = parse_to_comma_and_eval (&s);
- end_addr = value_as_address (v);
- if (start_addr > end_addr)
- error (_("Invalid search space, end preceeds start."));
- search_space_len = end_addr - start_addr + 1;
- /* We don't support searching all of memory
- (i.e. start=0, end = 0xff..ff).
- Bail to avoid overflows later on. */
- if (search_space_len == 0)
- error (_("Overflow in address range computation, choose smaller range."));
- }
-
- if (*s == ',')
- ++s;
+ if (!parse_addr_range (&s, &start_addr, &search_space_len))
+ return;
/* Fetch the search string. */
Index: gdb.git/gdb/ppc-linux-nat.c
===================================================================
--- gdb.git.orig/gdb/ppc-linux-nat.c 2010-08-20 16:00:16.000000000 -0300
+++ gdb.git/gdb/ppc-linux-nat.c 2010-10-22 18:41:37.000000000 -0200
@@ -1478,6 +1478,28 @@ ppc_linux_can_use_hw_breakpoint (int typ
}
static int
+ppc_linux_can_use_special_hw_point (enum hw_point_flag flag)
+{
+ uint64_t features;
+
+ if (!have_ptrace_booke_interface ())
+ return 0;
+
+ features = booke_debug_info.features;
+
+ switch (flag)
+ {
+ case HW_POINT_RANGED_WATCH:
+ return features & PPC_DEBUG_FEATURE_DATA_BP_RANGE;
+ case HW_POINT_MASKED_WATCH:
+ return features & PPC_DEBUG_FEATURE_DATA_BP_MASK;
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("Unknown hardware breakpoint/watchpoint type."));
+ }
+}
+
+static int
ppc_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
{
/* Handle sub-8-byte quantities. */
@@ -1489,9 +1511,15 @@ ppc_linux_region_ok_for_hw_watchpoint (C
to determine the hardcoded watchable region for watchpoints. */
if (have_ptrace_booke_interface ())
{
- if (booke_debug_info.data_bp_alignment
- && (addr + len > (addr & ~(booke_debug_info.data_bp_alignment - 1))
- + booke_debug_info.data_bp_alignment))
+ /* DAC-based processors (i.e., embedded processors), like the PowerPC 440
+ have ranged watchpoints and can watch any access within an arbitrary
+ memory region. This is useful to watch arrays and structs, for
+ instance. It takes two hardware watchpoints though. */
+ if (ppc_linux_can_use_special_hw_point (HW_POINT_RANGED_WATCH))
+ return 1;
+ else if (booke_debug_info.data_bp_alignment
+ && (addr + len > (addr & ~(booke_debug_info.data_bp_alignment - 1))
+ + booke_debug_info.data_bp_alignment))
return 0;
}
/* addr+len must fall in the 8 byte watchable region for DABR-based
@@ -1685,6 +1713,106 @@ get_trigger_type (int rw)
return t;
}
+static int
+ppc_linux_insert_mask_watchpoint (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 = (uint64_t) addr;
+ p.addr2 = (uint64_t) mask;
+ p.condition_value = 0;
+
+ ALL_LWPS (lp, ptid)
+ booke_insert_point (&p, TIDGET (ptid));
+
+ return 0;
+}
+
+static int
+ppc_linux_remove_mask_watchpoint (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 = (uint64_t) addr;
+ p.addr2 = (uint64_t) mask;
+ p.condition_value = 0;
+
+ ALL_LWPS (lp, ptid)
+ booke_remove_point (&p, TIDGET (ptid));
+
+ return 0;
+}
+
+static int
+ppc_linux_insert_ranged_watchpoint (CORE_ADDR addr, int len, 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_RANGE_INCLUSIVE;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.condition_value = 0;
+
+ /* The watchpoint will trigger if the address of the memory access is
+ within the defined range, as follows: p.addr <= address < p.addr2. */
+ p.addr = (uint64_t) addr;
+ p.addr2 = (uint64_t) addr + len;
+
+ ALL_LWPS (lp, ptid)
+ booke_insert_point (&p, TIDGET (ptid));
+
+ return 0;
+}
+
+static int
+ppc_linux_remove_ranged_watchpoint (CORE_ADDR addr, int len, 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_RANGE_INCLUSIVE;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) addr;
+ p.addr2 = (uint64_t) addr + len;
+ 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)
@@ -2136,6 +2264,14 @@ ppc_linux_watchpoint_addr_within_range (
return start <= addr + mask && start + length - 1 >= addr;
}
+static int
+ppc_linux_hw_point_extra_slot_count (enum hw_point_flag flag)
+{
+ gdb_assert (flag == HW_POINT_MASKED_WATCH || flag == HW_POINT_RANGED_WATCH);
+
+ return 1;
+}
+
static void
ppc_linux_store_inferior_registers (struct target_ops *ops,
struct regcache *regcache, int regno)
@@ -2347,12 +2483,18 @@ _initialize_ppc_linux_nat (void)
t->to_insert_hw_breakpoint = ppc_linux_insert_hw_breakpoint;
t->to_remove_hw_breakpoint = ppc_linux_remove_hw_breakpoint;
t->to_region_ok_for_hw_watchpoint = ppc_linux_region_ok_for_hw_watchpoint;
+ t->to_can_use_special_hw_point = ppc_linux_can_use_special_hw_point;
t->to_insert_watchpoint = ppc_linux_insert_watchpoint;
t->to_remove_watchpoint = ppc_linux_remove_watchpoint;
+ t->to_insert_ranged_watchpoint = ppc_linux_insert_ranged_watchpoint;
+ t->to_remove_ranged_watchpoint = ppc_linux_remove_ranged_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_hw_point_extra_slot_count = ppc_linux_hw_point_extra_slot_count;
t->to_read_description = ppc_linux_read_description;
t->to_auxv_parse = ppc_linux_auxv_parse;
Index: gdb.git/gdb/target.c
===================================================================
--- gdb.git.orig/gdb/target.c 2010-10-22 18:41:34.000000000 -0200
+++ gdb.git/gdb/target.c 2010-10-22 18:41:37.000000000 -0200
@@ -601,11 +601,16 @@ update_current_target (void)
INHERIT (to_files_info, t);
INHERIT (to_insert_breakpoint, t);
INHERIT (to_remove_breakpoint, t);
+ INHERIT (to_can_use_special_hw_point, t);
INHERIT (to_can_use_hw_breakpoint, t);
INHERIT (to_insert_hw_breakpoint, t);
INHERIT (to_remove_hw_breakpoint, t);
INHERIT (to_insert_watchpoint, t);
INHERIT (to_remove_watchpoint, t);
+ INHERIT (to_insert_ranged_watchpoint, t);
+ INHERIT (to_remove_ranged_watchpoint, t);
+ INHERIT (to_insert_mask_watchpoint, t);
+ INHERIT (to_remove_mask_watchpoint, t);
INHERIT (to_stopped_data_address, t);
INHERIT (to_have_steppable_watchpoint, t);
INHERIT (to_have_continuable_watchpoint, t);
@@ -613,6 +618,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);
+ INHERIT (to_hw_point_extra_slot_count, t);
INHERIT (to_terminal_init, t);
INHERIT (to_terminal_inferior, t);
INHERIT (to_terminal_ours_for_output, t);
@@ -727,6 +733,9 @@ update_current_target (void)
de_fault (to_can_use_hw_breakpoint,
(int (*) (int, int, int))
return_zero);
+ de_fault (to_can_use_special_hw_point,
+ (int (*) (enum hw_point_flag))
+ return_zero);
de_fault (to_insert_hw_breakpoint,
(int (*) (struct gdbarch *, struct bp_target_info *))
return_minus_one);
@@ -739,6 +748,18 @@ update_current_target (void)
de_fault (to_remove_watchpoint,
(int (*) (CORE_ADDR, int, int, struct expression *))
return_minus_one);
+ de_fault (to_insert_ranged_watchpoint,
+ (int (*) (CORE_ADDR, int, int))
+ return_minus_one);
+ de_fault (to_remove_ranged_watchpoint,
+ (int (*) (CORE_ADDR, int, int))
+ return_minus_one);
+ de_fault (to_insert_mask_watchpoint,
+ (int (*) (CORE_ADDR, CORE_ADDR, int))
+ return_minus_one);
+ de_fault (to_remove_mask_watchpoint,
+ (int (*) (CORE_ADDR, CORE_ADDR, int))
+ return_minus_one);
de_fault (to_stopped_by_watchpoint,
(int (*) (void))
return_zero);
@@ -752,6 +773,9 @@ update_current_target (void)
de_fault (to_can_accel_watchpoint_condition,
(int (*) (CORE_ADDR, int, int, struct expression *))
return_zero);
+ de_fault (to_hw_point_extra_slot_count,
+ (int (*) (enum hw_point_flag))
+ return_zero);
de_fault (to_terminal_init,
(void (*) (void))
target_ignore);
Index: gdb.git/gdb/target.h
===================================================================
--- gdb.git.orig/gdb/target.h 2010-10-22 18:41:34.000000000 -0200
+++ gdb.git/gdb/target.h 2010-10-22 18:41:37.000000000 -0200
@@ -39,6 +39,8 @@ struct static_tracepoint_marker;
struct expression;
+enum hw_point_flag;
+
/* This include file defines the interface between the main part
of the debugger, and the part which is target-specific, or
specific to the communications interface between us and the
@@ -438,6 +440,7 @@ struct target_ops
int (*to_insert_breakpoint) (struct gdbarch *, struct bp_target_info *);
int (*to_remove_breakpoint) (struct gdbarch *, struct bp_target_info *);
int (*to_can_use_hw_breakpoint) (int, int, int);
+ int (*to_can_use_special_hw_point) (enum hw_point_flag);
int (*to_insert_hw_breakpoint) (struct gdbarch *, struct bp_target_info *);
int (*to_remove_hw_breakpoint) (struct gdbarch *, struct bp_target_info *);
@@ -446,6 +449,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_ranged_watchpoint) (CORE_ADDR, int, int);
+ int (*to_remove_ranged_watchpoint) (CORE_ADDR, int, int);
+ int (*to_insert_mask_watchpoint) (CORE_ADDR, CORE_ADDR, int);
+ int (*to_remove_mask_watchpoint) (CORE_ADDR, CORE_ADDR, int);
int (*to_stopped_by_watchpoint) (void);
int to_have_steppable_watchpoint;
int to_have_continuable_watchpoint;
@@ -455,6 +462,7 @@ struct target_ops
int (*to_region_ok_for_hw_watchpoint) (CORE_ADDR, int);
int (*to_can_accel_watchpoint_condition) (CORE_ADDR, int, int,
struct expression *);
+ int (*to_hw_point_extra_slot_count) (enum hw_point_flag);
void (*to_terminal_init) (void);
void (*to_terminal_inferior) (void);
void (*to_terminal_ours_for_output) (void);
@@ -1314,6 +1322,11 @@ extern char *normal_pid_to_str (ptid_t p
#define target_region_ok_for_hw_watchpoint(addr, len) \
(*current_target.to_region_ok_for_hw_watchpoint) (addr, len)
+/* Returns non-zero if the target supports the special type of hardware
+ breakpoint/watchpoint represented by FLAG. */
+#define target_can_use_special_hw_point(flag) \
+ (*current_target.to_can_use_special_hw_point) (flag)
+
/* Set/clear a hardware watchpoint starting at ADDR, for LEN bytes.
TYPE is 0 for write, 1 for read, and 2 for read/write accesses.
@@ -1327,6 +1340,20 @@ extern char *normal_pid_to_str (ptid_t p
#define target_remove_watchpoint(addr, len, type, cond) \
(*current_target.to_remove_watchpoint) (addr, len, type, cond)
+/* Hardware ranged watchpoints. */
+#define target_insert_ranged_watchpoint(addr, len, type) \
+ (*current_target.to_insert_ranged_watchpoint) (addr, len, type)
+
+#define target_remove_ranged_watchpoint(addr, len, type) \
+ (*current_target.to_remove_ranged_watchpoint) (addr, len, type)
+
+/* Hardware watchpoints with an associated address mask. */
+#define target_insert_mask_watchpoint(addr, mask, type) \
+ (*current_target.to_insert_mask_watchpoint) (addr, mask, type)
+
+#define target_remove_mask_watchpoint(addr, mask, type) \
+ (*current_target.to_remove_mask_watchpoint) (addr, mask, type)
+
#define target_insert_hw_breakpoint(gdbarch, bp_tgt) \
(*current_target.to_insert_hw_breakpoint) (gdbarch, bp_tgt)
@@ -1355,6 +1382,9 @@ extern char *normal_pid_to_str (ptid_t p
#define target_can_accel_watchpoint_condition(addr, len, type, cond) \
(*current_target.to_can_accel_watchpoint_condition) (addr, len, type, cond)
+#define target_hw_point_extra_slot_count(flag) \
+ (*current_target.to_hw_point_extra_slot_count) (flag)
+
/* Target can execute in reverse? */
#define target_can_execute_reverse \
(current_target.to_can_execute_reverse ? \
Index: gdb.git/gdb/ui-out.c
===================================================================
--- gdb.git.orig/gdb/ui-out.c 2010-08-20 16:00:16.000000000 -0300
+++ gdb.git/gdb/ui-out.c 2010-10-22 18:41:37.000000000 -0200
@@ -487,6 +487,46 @@ ui_out_field_fmt_int (struct ui_out *uio
}
void
+ui_out_field_range_core_addr (struct ui_out *uiout,
+ const char *fldname,
+ struct gdbarch *gdbarch,
+ CORE_ADDR address_start,
+ CORE_ADDR length)
+{
+ char addstr[80];
+ int addr_bit = gdbarch_addr_bit (gdbarch);
+ CORE_ADDR address_end = address_start + length - 1;
+
+ if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT))
+ {
+ address_start &= ((CORE_ADDR) 1 << addr_bit) - 1;
+ address_end &= ((CORE_ADDR) 1 << addr_bit) - 1;
+ }
+
+ /* FIXME: cagney/2002-05-03: Need local_address_string() function
+ that returns the language localized string formatted to a width
+ based on gdbarch_addr_bit. */
+ if (addr_bit <= 32)
+ {
+ strcpy (addstr, "[");
+ strcat (addstr, hex_string_custom (address_start, 8));
+ strcat (addstr, ", ");
+ strcat (addstr, hex_string_custom (address_end, 8));
+ strcat (addstr, "]");
+ }
+ else
+ {
+ strcpy (addstr, "[");
+ strcat (addstr, hex_string_custom (address_start, 16));
+ strcat (addstr, ", ");
+ strcat (addstr, hex_string_custom (address_end, 16));
+ strcat (addstr, "]");
+ }
+
+ ui_out_field_string (uiout, fldname, addstr);
+}
+
+void
ui_out_field_core_addr (struct ui_out *uiout,
const char *fldname,
struct gdbarch *gdbarch,
Index: gdb.git/gdb/ui-out.h
===================================================================
--- gdb.git.orig/gdb/ui-out.h 2010-08-20 16:00:16.000000000 -0300
+++ gdb.git/gdb/ui-out.h 2010-10-22 18:41:37.000000000 -0200
@@ -113,6 +113,12 @@ extern void ui_out_field_fmt_int (struct
enum ui_align align, const char *fldname,
int value);
+extern void ui_out_field_range_core_addr (struct ui_out *uiout,
+ const char *fldname,
+ struct gdbarch *gdbarch,
+ CORE_ADDR address_start,
+ CORE_ADDR length);
+
extern void ui_out_field_core_addr (struct ui_out *uiout, const char *fldname,
struct gdbarch *gdbarch, CORE_ADDR address);
Index: gdb.git/gdb/value.h
===================================================================
--- gdb.git.orig/gdb/value.h 2010-10-19 21:05:00.000000000 -0200
+++ gdb.git/gdb/value.h 2010-10-22 18:41:37.000000000 -0200
@@ -559,6 +559,9 @@ extern CORE_ADDR parse_and_eval_address_
extern LONGEST parse_and_eval_long (char *exp);
+int parse_addr_range (char **args, CORE_ADDR *start_addrp,
+ ULONGEST *search_space_lenp);
+
extern void unop_promote (const struct language_defn *language,
struct gdbarch *gdbarch,
struct value **arg1);