This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[patch 2/3] Implement support for PowerPC BookE ranged 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: Tue, 23 Nov 2010 19:51:40 -0200
- Subject: [patch 2/3] Implement support for PowerPC BookE ranged watchpoints
Hi,
This patch adds support for hardware ranged watchpoints, without the
watch-range command. It makes GDB use hardware ranged watchpoints
automatically when the user asks to watch an array or struct and there
are enough hardware watchpoints available.
The code is the same that was reviewed before, , except for fixing the
GNU style regarding whitespace in
ppc_linux_{insert,remove}_ranged_watchpoint.
The NEWS entry is the same that was reviewed before. The manual has the
following new wording in the second half of the paragraph (the first
half is the same):
+You can create an artificial array to watch an arbitrary memory
+region using one of the following commands (@pxref{Expressions}):
+
+@smallexample
+(@value{GDBP}) watch *((char *) @var{ADDRESS})@@@var{LENGTH}
+(@value{GDBP}) watch @{char[@var{LENGTH}]@} @var{ADDRESS}
+@end smallexample
Ok?
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
2010-11-24 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
Thiago Jung Bauermann <bauerman@br.ibm.com>
Implement support for PowerPC BookE ranged watchpoints.
gdb/
*NEWS: Mention ranged watchpoint support.
* breakpoint.c
(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.
(watch_command_1): Check whether a ranged hardware watchpoint
can be used. Set b->ops according to the type of hardware watchpoint
being created.
* breakpoint.h
(enum hw_point_flag) <HW_POINT_RANGED_WATCH>: New element.
* ppc-linux-nat.c (ppc_linux_can_use_special_hw_point): Handle
HW_POINT_RANGED_WATCH.
(ppc_linux_region_ok_for_hw_watchpoint): Always handle regions when
ranged watchpoints are available.
(ppc_linux_insert_ranged_watchpoint)
(ppc_linux_remove_ranged_watchpoint): New functions.
(ppc_linux_hw_point_extra_slot_count): Handle HW_POINT_RANGED_WATCH.
(_initialize_ppc_linux_nat): Initialize to_insert_ranged_watchpoint
and to_remove_ranged_watchpoint.
* target.c (update_current_target): Mention
to_insert_ranged_watchpoint and to_remove_ranged_watchpoint,.
(target_insert_ranged_watchpoint)
(target_remove_ranged_watchpoint): New functions.
* target.h (struct target_ops) <to_insert_ranged_watchpoint>,
<to_remove_ranged_watchpoint>: New callbacks.
(target_insert_ranged_watchpoint)
(target_remove_ranged_watchpoint): Add prototypes.
* ui-out.c (ui_out_field_range_core_addr): New function.
* ui-out.h (ui_out_field_range_core_addr): Declare.
gdb/doc/
* gdb.texinfo (PowerPC Embedded): Document ranged watchpoints.
diff --git a/gdb/NEWS b/gdb/NEWS
index 3fa763c..0ac97b5 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -93,6 +93,12 @@
by the inferior against the watchpoint address. See the "PowerPC Embedded"
section in the user manual for more details.
+* GDB now supports ranged watchpoints, which stop the inferior when it
+ accesses any address within a specified memory range. The watchpoint
+ is hardware-accelerated on some targets (currently only when locally
+ debugging programs on PowerPC BookE processors running a Linux
+ kernel version 2.6.34 or later).
+
* New features in the GDB remote stub, GDBserver
** GDBserver is now supported on PowerPC LynxOS (versions 4.x and 5.x),
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index e9d2661..5f4d472 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -8159,6 +8159,103 @@ static struct breakpoint_ops watchpoint_breakpoint_ops =
};
/* 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)
+{
+ 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);
+}
+
+/* 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,
+ NULL /* print_recreate */
+};
+
+/* Implement the "insert" breakpoint_ops method for
masked hardware watchpoints. */
static int
@@ -8376,6 +8473,9 @@ watch_command_1 (char *arg, int accessflag, int from_tty,
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')
@@ -8538,10 +8638,21 @@ This target does not support the usage of masks with hardware watchpoints."));
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 =
@@ -8563,9 +8674,12 @@ This target does not support the usage of masks with hardware watchpoints."));
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;
+ {
+ /* 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);
@@ -8640,6 +8754,8 @@ Cannot use masks without enough available hardware watchpoints (need %d)."),
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;
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index f7faf17..6d9fa07 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -433,6 +433,7 @@ 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. */
};
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b7f26c6..97ec8d5 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18702,7 +18702,7 @@ The DVC register will be automatically used whenever @value{GDBN} detects
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 masked watchpoints.
+PowerPC embedded processors support masked watchpoints and ranged 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
@@ -18719,6 +18719,17 @@ the @code{watch} command (@pxref{Set Watchpoints}), as in:
(@value{GDBP}) watch *0xdeadbeef mask 0xffffff00
@end smallexample
+A @dfn{ranged watchpoint} watches a contiguous range of addresses.
+@value{GDBN} automatically creates a ranged watchpoint when asked to watch
+an array or struct of known size and there are enough hardware registers
+available. You can create an artificial array to watch an arbitrary memory
+region using one of the following commands (@pxref{Expressions}):
+
+@smallexample
+(@value{GDBP}) watch *((char *) @var{ADDRESS})@@@var{LENGTH}
+(@value{GDBP}) watch @{char[@var{LENGTH}]@} @var{ADDRESS}
+@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 773a507..3b6a9ca 100644
--- a/gdb/ppc-linux-nat.c
+++ b/gdb/ppc-linux-nat.c
@@ -1486,11 +1486,18 @@ ppc_linux_can_use_special_hw_point (struct target_ops *ops,
if (!have_ptrace_booke_interface ())
return 0;
- if (flag != HW_POINT_MASKED_WATCH)
- internal_error (__FILE__, __LINE__,
- _("Unknown hardware breakpoint/watchpoint type."));
+ features = booke_debug_info.features;
- return booke_debug_info.features & PPC_DEBUG_FEATURE_DATA_BP_MASK;
+ 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
@@ -1505,9 +1512,16 @@ ppc_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
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 (¤t_target,
+ 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
@@ -1754,6 +1768,61 @@ ppc_linux_remove_mask_watchpoint (struct target_ops *ops, CORE_ADDR addr,
return 0;
}
+static int
+ppc_linux_insert_ranged_watchpoint (struct target_ops *ops, 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.
+
+ Note that the above sentence just documents how ptrace interprets
+ its arguments; the watchpoint is set to watch the range defined by
+ the user _inclusively_, as specified by the user interface. */
+ 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 (struct target_ops *ops, 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)
@@ -2209,7 +2278,7 @@ static int
ppc_linux_hw_point_extra_slot_count (struct target_ops *target,
enum hw_point_flag flag)
{
- return flag == HW_POINT_MASKED_WATCH;
+ return flag == HW_POINT_MASKED_WATCH || flag == HW_POINT_RANGED_WATCH;
}
static void
@@ -2426,6 +2495,8 @@ _initialize_ppc_linux_nat (void)
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;
diff --git a/gdb/target.c b/gdb/target.c
index 84b6302..47a0210 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -607,6 +607,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_ranged_watchpoint. */
+ /* Do not inherit to_remove_ranged_watchpoint. */
/* Do not inherit to_insert_mask_watchpoint. */
/* Do not inherit to_remove_mask_watchpoint. */
INHERIT (to_stopped_data_address, t);
@@ -3351,6 +3353,52 @@ target_can_use_special_hw_point (enum hw_point_flag flag)
}
int
+target_insert_ranged_watchpoint (CORE_ADDR addr, int len, int rw)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_insert_ranged_watchpoint != NULL)
+ {
+ int ret;
+
+ ret = t->to_insert_ranged_watchpoint (t, addr, len, rw);
+
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, "\
+target_insert_ranged_watchpoint (%s, %d, %d) = %d\n",
+ core_addr_to_string (addr), len, rw, ret);
+
+ return ret;
+ }
+
+ return return_minus_one ();
+}
+
+int
+target_remove_ranged_watchpoint (CORE_ADDR addr, int len, int rw)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_remove_ranged_watchpoint != NULL)
+ {
+ int ret;
+
+ ret = t->to_remove_ranged_watchpoint (t, addr, len, rw);
+
+ if (targetdebug)
+ fprintf_unfiltered (gdb_stdlog, "\
+target_remove_ranged_watchpoint (%s, %d, %d) = %d\n",
+ core_addr_to_string (addr), len, rw, ret);
+
+ return ret;
+ }
+
+ return return_minus_one ();
+}
+
+int
target_insert_mask_watchpoint (CORE_ADDR addr, CORE_ADDR mask, int rw)
{
struct target_ops *t;
diff --git a/gdb/target.h b/gdb/target.h
index 0d4bdcd..dd822a9 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -450,6 +450,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) (struct target_ops *,
+ CORE_ADDR, int, int);
+ int (*to_remove_ranged_watchpoint) (struct target_ops *,
+ CORE_ADDR, int, int);
int (*to_insert_mask_watchpoint) (struct target_ops *,
CORE_ADDR, CORE_ADDR, int);
int (*to_remove_mask_watchpoint) (struct target_ops *,
@@ -1343,6 +1347,11 @@ extern int target_can_use_special_hw_point (enum hw_point_flag);
#define target_remove_watchpoint(addr, len, type, cond) \
(*current_target.to_remove_watchpoint) (addr, len, type, cond)
+/* Hardware ranged watchpoints. */
+
+extern int target_insert_ranged_watchpoint (CORE_ADDR, int, int);
+extern int target_remove_ranged_watchpoint (CORE_ADDR, int, int);
+
/* Hardware watchpoints with an associated address mask. */
extern int target_insert_mask_watchpoint (CORE_ADDR, CORE_ADDR, int);
diff --git a/gdb/ui-out.c b/gdb/ui-out.c
index 4d3bf0c..cf4b929 100644
--- a/gdb/ui-out.c
+++ b/gdb/ui-out.c
@@ -487,6 +487,46 @@ ui_out_field_fmt_int (struct ui_out *uiout,
}
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,
diff --git a/gdb/ui-out.h b/gdb/ui-out.h
index f65f42b..49e21b6 100644
--- a/gdb/ui-out.h
+++ b/gdb/ui-out.h
@@ -113,6 +113,12 @@ extern void ui_out_field_fmt_int (struct ui_out *uiout, int width,
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);