This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 4/4] Support ranged and masked breakpoints
- From: Thiago Jung Bauermann <bauerman at br dot ibm dot com>
- To: gdb-patches at sourceware dot org
- Cc: Luis Machado <luisgpm at linux dot vnet dot ibm dot com>, Matt Tyrlik <tyrlik at us dot ibm dot com>
- Date: Wed, 23 Dec 2009 22:31:10 -0200
- Subject: [PATCH 4/4] Support ranged and masked breakpoints
Adds support for the following types of breakpoints
* Simple Hardware Breakpoint: Monitors a single instruction address.
Available GDB commands: hbreak <address> / hbreak <line number>
Example: hbreak *0x10000658 / hbreak 20
* Range Hardware Breakpoint: Monitors an interval of instruction addresses.
Available GDB commands: hbreak-range <address1>:<address2 | +<unsigned
int length>> where <address1> <= <address2> / hbreak-range <line number
start>:<line number end>
hbreak-range <address1>, <address2>
hbreak-range <address1>, +<length>
hbreak-range <line number start>, <line number end>
Examples:
hbreak-range 0x10000658,0x10000660
hbreak-range 0x10000658,+8
hbreak-range 20,30
Since the Simple and Range variations share the same register set, the
allowed combinations of Simple Hardware Breakpoints (HW Break) and Range
Hardware Breakpoints (Range HW Break) are as follows:
- 4 HW Breaks
- 1 or 2 HW Breaks / 1 Range HW Break
- 2 Range HW Breaks
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center
2009-12-23 Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>
Thiago Jung Bauermann <bauerman@br.ibm.com>
* breakpoint.c (insert_bp_location): Propagate length of ranged
breakpoint to its target_info element.
(breakpoint_here_p): Update check for ranged breakpoints.
(print_it_typical): Handle printing of ranged breakpoints.
(bpstat_check_location): Handle the case of a hardware ranged
breakpoint.
(print_one_breakpoint_location): Ditto.
(breakpoint_1): Ditto.
(breakpoint_address_match_range): New function.
(set_raw_breakpoint): Set the breakpoint range to zero.
(hw_breakpoint_used_count): Call target-specific function that will
tell how many extra slots a hardware breakpoint needs.
(mention): Mention ranged breakpoints.
(hbreak_range_command): New function.
(_initialize_breakpoint): Register hbreak-range command.
* ppc-linux-nat.c (ppc_linux_can_use_special_hw_point_p): Handle the
HW_POINT_RANGED_BREAK case.
(ppc_linux_insert_hw_breakpoint): Handle inserting ranged breakpoints.
(ppc_linux_remove_hw_breakpoint): Handle removing ranged breakpoints.
(ppc_linux_hw_point_extra_slot_count): Handle the
HW_POINT_RANGED_BREAK case.
* breakpoint.h (hw_point_flag) <HW_POINT_RANGED_BREAK>: New value.
* ui-out.c (ui_out_field_range_core_addr): New function.
* ui-out.h (ui_out_field_range_core_addr): Declare.
Index: gdb/gdb/breakpoint.c
===================================================================
--- gdb.orig/gdb/breakpoint.c 2009-12-23 18:04:38.000000000 -0200
+++ gdb/gdb/breakpoint.c 2009-12-23 18:04:41.000000000 -0200
@@ -127,6 +127,12 @@ static int breakpoint_address_match (str
struct address_space *aspace2,
CORE_ADDR addr2);
+static int breakpoint_address_match_range (struct address_space *aspace1,
+ CORE_ADDR addr1,
+ CORE_ADDR len1,
+ struct address_space *aspace2,
+ CORE_ADDR addr2);
+
static void breakpoints_info (char *, int);
static void breakpoint_1 (int, int);
@@ -1343,6 +1349,7 @@ insert_bp_location (struct bp_location *
memset (&bpt->target_info, 0, sizeof (bpt->target_info));
bpt->target_info.placed_address = bpt->address;
bpt->target_info.placed_address_space = bpt->pspace->aspace;
+ bpt->target_info.length = bpt->ranged_hw_bp_length;
if (bpt->loc_type == bp_loc_software_breakpoint
|| bpt->loc_type == bp_loc_hardware_breakpoint)
@@ -2328,8 +2335,15 @@ breakpoint_here_p (struct address_space
if ((breakpoint_enabled (bpt->owner)
|| bpt->owner->enable_state == bp_permanent)
- && breakpoint_address_match (bpt->pspace->aspace, bpt->address,
- aspace, pc))
+ && ((breakpoint_address_match (bpt->pspace->aspace, bpt->address,
+ aspace, pc) && ((bpt->address == pc)))
+ || (bpt->owner->hw_point_flag == HW_POINT_RANGED_BREAK
+ && breakpoint_address_match_range (bpt->pspace->aspace,
+ bpt->address,
+ bpt->ranged_hw_bp_length,
+ aspace, pc)
+ && pc >= bpt->address
+ && pc < (bpt->address + bpt->ranged_hw_bp_length))))
{
if (overlay_debugging
&& section_is_overlay (bpt->section)
@@ -2900,6 +2914,11 @@ print_it_typical (bpstat bs)
annotate_breakpoint (b->number);
if (bp_temp)
ui_out_text (uiout, "\nTemporary breakpoint ");
+ else if (bl && bl->owner
+ && bl->owner->hw_point_flag == HW_POINT_RANGED_BREAK)
+ ui_out_text (uiout, "\nRange hardware assisted breakpoint ");
+ else if (b->type == bp_hardware_breakpoint)
+ ui_out_text (uiout, "\nHardware assisted breakpoint ");
else
ui_out_text (uiout, "\nBreakpoint ");
if (ui_out_is_mi_like_p (uiout))
@@ -3450,7 +3469,10 @@ bpstat_check_location (const struct bp_l
if (b->type == bp_hardware_breakpoint)
{
- if (bl->address != bp_addr)
+ if ((b->hw_point_flag == HW_POINT_NONE && bl->address != bp_addr)
+ || (b->hw_point_flag == HW_POINT_RANGED_BREAK
+ && (bp_addr < bl->address
+ || (bp_addr >= bl->address + bl->ranged_hw_bp_length))))
return 0;
if (overlay_debugging /* unmapped overlay section */
&& section_is_overlay (bl->section)
@@ -4210,7 +4232,13 @@ print_one_breakpoint_location (struct br
if (opts.addressprint)
{
if (print_address_bits <= 32)
- strcat (wrap_indent, " ");
+ {
+ if (loc && loc->owner
+ && loc->owner->hw_point_flag == HW_POINT_RANGED_BREAK)
+ strcat (wrap_indent, " ");
+ else
+ strcat (wrap_indent, " ");
+ }
else
strcat (wrap_indent, " ");
}
@@ -4268,8 +4296,14 @@ print_one_breakpoint_location (struct br
else if (b->loc == NULL || loc->shlib_disabled)
ui_out_field_string (uiout, "addr", "<PENDING>");
else
- ui_out_field_core_addr (uiout, "addr",
- loc->gdbarch, loc->address);
+ if (loc && loc->owner
+ && loc->owner->hw_point_flag == HW_POINT_RANGED_BREAK)
+ ui_out_field_range_core_addr (uiout, "addr", loc->gdbarch,
+ loc->address,
+ loc->address + loc->ranged_hw_bp_length);
+ else
+ ui_out_field_core_addr (uiout, "addr",
+ loc->gdbarch, loc->address);
}
annotate_field (5);
if (!header_of_multiple)
@@ -4605,7 +4639,10 @@ breakpoint_1 (int bnum, int allflag)
if (nr_printable_breakpoints > 0)
annotate_field (4);
if (print_address_bits <= 32)
- ui_out_table_header (uiout, 10, ui_left, "addr", "Address");/* 5 */
+ if (b && b->hw_point_flag == HW_POINT_RANGED_BREAK)
+ ui_out_table_header (uiout, 18, ui_left, "addr", "Address");
+ else
+ ui_out_table_header (uiout, 10, ui_left, "addr", "Address");/* 5 */
else
ui_out_table_header (uiout, 18, ui_left, "addr", "Address");/* 5 */
}
@@ -4794,6 +4831,23 @@ breakpoint_address_match (struct address
&& addr1 == addr2);
}
+/* Returns true if {ASPACE1,ADDR1} and {ASPACE2,ADDR2} represent the
+ same breakpoint location, but (ADDR1 <= ADDR2 <= ADDR1 + LEN1).
+ In most targets, this can only be true if ASPACE1 matches ASPACE2.
+ On targets that have global breakpoints, the address space
+ doesn't really matter. */
+
+static int
+breakpoint_address_match_range (struct address_space *aspace1, CORE_ADDR addr1,
+ CORE_ADDR len1,
+ struct address_space *aspace2, CORE_ADDR addr2)
+{
+ return ((gdbarch_has_global_breakpoints (target_gdbarch)
+ || aspace1 == aspace2)
+ && addr2 >= addr1
+ && addr2 <= addr1 + len1);
+}
+
/* Assuming LOC1 and LOC2's types' have meaningful target addresses
(breakpoint_address_is_meaningful), returns true if LOC1 and LOC2
represent the same location. */
@@ -5055,6 +5109,7 @@ set_raw_breakpoint (struct gdbarch *gdba
/* Store the program space that was used to set the breakpoint, for
breakpoint resetting. */
b->pspace = sal.pspace;
+ b->loc->ranged_hw_bp_length = 0;
if (sal.symtab == NULL)
b->source_file = NULL;
@@ -5885,7 +5940,12 @@ hw_breakpoint_used_count (void)
ALL_BREAKPOINTS (b)
{
if (b->type == bp_hardware_breakpoint && breakpoint_enabled (b))
- i++;
+ {
+ i++;
+ /* Special types of hardware breakpoints can use more than
+ one slot. */
+ i += target_hw_point_extra_slot_count (b->hw_point_flag);
+ }
}
return i;
@@ -6182,6 +6242,9 @@ mention (struct breakpoint *b)
say_where = 0;
break;
}
+
+ if (b->hw_point_flag == HW_POINT_RANGED_BREAK)
+ printf_filtered (_("Range "));
printf_filtered (_("Hardware assisted breakpoint %d"), b->number);
say_where = 1;
break;
@@ -7737,6 +7800,137 @@ rwatch_range_command (char *arg, int fro
watch_range_command_1 (arg, bp_read_watchpoint, from_tty);
}
+static void
+hbreak_range_command (char *arg, int from_tty)
+{
+ int bp_count, can_use_bp, ret, err;
+ int parse_line = 0;
+ CORE_ADDR start_addr;
+ ULONGEST length;
+ struct breakpoint *b;
+ struct symtab_and_line sal;
+ struct gdbarch *gdbarch = get_current_arch ();
+
+ /* Do we support ranged hardware breakpoints? */
+ if (!target_can_use_special_hw_point_p (HW_POINT_RANGED_BREAK))
+ error (_("This target does not support ranged hardware breakpoints."));
+
+ if (arg != NULL && *arg != '\0')
+ {
+ while (isspace (*arg))
+ arg++;
+
+ /* Is this an address (of the form `0x...'), or a line? */
+ if (arg[0] == '0' && arg[1] == 'x')
+ parse_line = 0;
+ else
+ parse_line = 1;
+ }
+
+ if (parse_line)
+ {
+ char *l1 = arg, *l2;
+ char *line1, *line2;
+ int size;
+ struct symtabs_and_lines sals1, sals2;
+ struct symtab_and_line sal1, sal2;
+
+ l2 = arg;
+ while (*l2 != ',')
+ l2++;
+
+ size = l2 - l1;
+
+ *l2 = '\0';
+ l2++;
+ while (isspace (*l2))
+ l2++;
+
+ line1 = l1;
+ line2 = l2;
+
+ /* Parsing first line. */
+ sals1 = decode_line_1 (&line1, 1, NULL, 0, NULL, NULL);
+
+ if (sals1.nelts != 1)
+ error (_("Could not get information on specified line `%s'."),
+ line1);
+ sal1 = sals1.sals[0];
+ xfree (sals1.sals);
+
+ if (*line1)
+ error (_("Junk at end of arguments."));
+
+ resolve_sal_pc (&sal1);
+
+ /* Parsing second line. */
+ sals2 = decode_line_1 (&line2, 1, NULL, 0, NULL, NULL);
+
+ if (sals2.nelts != 1)
+ error (_("Could not get information on specified line `%s'."),
+ line2);
+ sal2 = sals2.sals[0];
+ xfree (sals2.sals);
+
+ if (*line2)
+ error (_("Junk at end of arguments."));
+
+ resolve_sal_pc (&sal2);
+
+ /* Verifying if the lines make sense. We need to check if
+ the first address in the range is smaller than the second,
+ and also compute the length. */
+ if (sal1.pc > sal2.pc)
+ error (_("Invalid search space, end preceeds start."));
+
+ start_addr = sal1.pc;
+ length = sal2.pc - sal1.pc;
+
+ if (length == 0)
+ {
+ /* This range is simple enough to be treated by
+ the `hbreak' command. */
+ printf_unfiltered (_("Range is too small, using `hbreak' \
+instead.\n"));
+ hbreak_command (l1, 1);
+ return;
+ }
+ }
+ else
+ parse_addr_range (&arg, &start_addr, &length);
+
+ bp_count = hw_breakpoint_used_count ();
+ /* For ranged hardware breakpoints, we may have to use 2 slots
+ instead of 1. */
+ bp_count += target_hw_point_extra_slot_count (HW_POINT_RANGED_BREAK);
+ can_use_bp = target_can_use_hardware_watchpoint (bp_hardware_breakpoint,
+ bp_count + 1,
+ 0);
+ if (can_use_bp < 0)
+ error (_("Not enough hardware resources for specified breakpoint."));
+
+ sal = find_pc_line (start_addr, 0);
+ sal.pc = start_addr;
+ sal.section = find_pc_overlay (start_addr);
+ sal.explicit_pc = 1;
+ sal.pspace = current_program_space;
+
+ /* Now set up the breakpoint. */
+ b = set_raw_breakpoint (gdbarch, sal, bp_hardware_breakpoint);
+ set_breakpoint_count (breakpoint_count + 1);
+ b->number = breakpoint_count;
+ b->thread = -1;
+ b->disposition = disp_donttouch;
+ b->exp = NULL;
+ b->exp_string = NULL;
+ b->val = NULL;
+ b->val_valid = 0;
+ b->hw_point_flag = HW_POINT_RANGED_BREAK;
+ b->loc->ranged_hw_bp_length = length;
+
+ mention (b);
+ update_global_location_list (1);
+}
/* Helper routines for the until_command routine in infcmd.c. Here
@@ -11171,6 +11365,17 @@ The watchpoint will stop execution of yo
reads any address within that range."));
set_cmd_completer (c, expression_completer);
+ add_com ("hbreak-range", class_breakpoint, hbreak_range_command, _("\
+Set a hardware assisted breakpoint 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\
+ start-line, end-line\n\
+\n\
+The breakpoint will stop execution of your program whenever the inferior\n\
+executes any address within that range."));
+
automatic_hardware_breakpoints = 1;
observer_attach_about_to_proceed (breakpoint_about_to_proceed);
Index: gdb/gdb/ppc-linux-nat.c
===================================================================
--- gdb.orig/gdb/ppc-linux-nat.c 2009-12-23 18:04:38.000000000 -0200
+++ gdb/gdb/ppc-linux-nat.c 2009-12-23 18:14:47.000000000 -0200
@@ -1461,6 +1461,8 @@ ppc_linux_can_use_special_hw_point_p (en
switch (flag)
{
+ case HW_POINT_RANGED_BREAK:
+ return features & PPC_DEBUG_FEATURE_INSN_BP_RANGE;
case HW_POINT_RANGED_WATCH:
return features & PPC_DEBUG_FEATURE_DATA_BP_RANGE;
case HW_POINT_MASKED_WATCH:
@@ -1626,25 +1628,38 @@ ppc_linux_insert_hw_breakpoint (struct g
struct bp_target_info *bp_tgt)
{
struct lwp_info *lp;
+ struct ppc_hw_breakpoint p;
ptid_t ptid;
if (!have_ptrace_new_debug_booke)
return -1;
- ALL_LWPS (lp, ptid)
- {
- struct ppc_hw_breakpoint p;
+ if (bp_tgt->length == 0)
+ ALL_LWPS (lp, ptid)
+ {
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
+ p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) bp_tgt->placed_address;
+ p.addr2 = 0;
+ p.condition_value = 0;
- p.version = PPC_DEBUG_CURRENT_VERSION;
- p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
- p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
- p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
- p.addr = (uint64_t) bp_tgt->placed_address;
- p.addr2 = 0;
- p.condition_value = 0;
+ booke_insert_point (&p, TIDGET (ptid));
+ }
+ else
+ ALL_LWPS (lp, ptid)
+ {
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) bp_tgt->placed_address;
+ p.addr2 = (uint64_t) bp_tgt->placed_address + bp_tgt->length;
+ p.condition_value = 0;
- booke_insert_point (&p, TIDGET (ptid));
- }
+ booke_insert_point (&p, TIDGET (ptid));
+ }
return 0;
}
@@ -1654,25 +1669,38 @@ ppc_linux_remove_hw_breakpoint (struct g
struct bp_target_info *bp_tgt)
{
struct lwp_info *lp;
+ struct ppc_hw_breakpoint p;
ptid_t ptid;
if (!have_ptrace_new_debug_booke)
return -1;
- ALL_LWPS (lp, ptid)
- {
- struct ppc_hw_breakpoint p;
+ if (bp_tgt->length == 0)
+ ALL_LWPS (lp, ptid)
+ {
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
+ p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) bp_tgt->placed_address;
+ p.addr2 = 0;
+ p.condition_value = 0;
- p.version = PPC_DEBUG_CURRENT_VERSION;
- p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
- p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
- p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
- p.addr = (uint64_t) bp_tgt->placed_address;
- p.addr2 = 0;
- p.condition_value = 0;
+ booke_remove_point (&p, TIDGET (ptid));
+ }
+ else
+ ALL_LWPS (lp, ptid)
+ {
+ p.version = PPC_DEBUG_CURRENT_VERSION;
+ p.trigger_type = PPC_BREAKPOINT_TRIGGER_EXECUTE;
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+ p.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+ p.addr = (uint64_t) bp_tgt->placed_address;
+ p.addr2 = (uint64_t) bp_tgt->placed_address + bp_tgt->length;
+ p.condition_value = 0;
- booke_remove_point (&p, TIDGET (ptid));
- }
+ booke_remove_point (&p, TIDGET (ptid));
+ }
return 0;
}
@@ -2112,13 +2140,15 @@ ppc_linux_hw_point_extra_slot_count (enu
/* If this *point is a:
- Masked hardware watchpoint,
- - Ranged hardware watchpoint
+ - Ranged hardware watchpoint, or
+ - Ranged hardware breakpoint
then it uses 1 extra slot. */
switch (flag)
{
case HW_POINT_MASKED_WATCH:
case HW_POINT_RANGED_WATCH:
+ case HW_POINT_RANGED_BREAK:
return 1;
default:
return 0;
Index: gdb/gdb/breakpoint.h
===================================================================
--- gdb.orig/gdb/breakpoint.h 2009-12-23 18:04:38.000000000 -0200
+++ gdb/gdb/breakpoint.h 2009-12-23 18:04:41.000000000 -0200
@@ -401,6 +401,7 @@ DEF_VEC_P(bp_location_p);
enum hw_point_flag {
HW_POINT_NONE = 0,
HW_POINT_RANGED_WATCH, /* Hardware ranged watchpoint. */
+ HW_POINT_RANGED_BREAK, /* Hardware ranged breakpoint. */
HW_POINT_MASKED_WATCH, /* Hardware masked watchpoint. */
HW_POINT_COND_HW_ACCEL, /* Hardware watchpoint with condition
hardware-accelerated. */
Index: gdb/gdb/ui-out.c
===================================================================
--- gdb.orig/gdb/ui-out.c 2009-12-23 17:39:53.000000000 -0200
+++ gdb/gdb/ui-out.c 2009-12-23 18:04:41.000000000 -0200
@@ -486,6 +486,45 @@ 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 address_end)
+{
+ char addstr[40];
+ int addr_bit = gdbarch_addr_bit (gdbarch);
+
+ 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/gdb/ui-out.h
===================================================================
--- gdb.orig/gdb/ui-out.h 2009-12-23 17:39:53.000000000 -0200
+++ gdb/gdb/ui-out.h 2009-12-23 18:04:41.000000000 -0200
@@ -114,6 +114,11 @@ 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 address_end);
+
extern void ui_out_field_core_addr (struct ui_out *uiout, const char *fldname,
struct gdbarch *gdbarch, CORE_ADDR address);