Re: [RFA] Implement support for PowerPC BookE ranged breakpoints

Thiago Jung Bauermann
2011-03-31T14:46:31Z

On Tue, 2011-03-29 at 14:23 +0200, Ulrich Weigand wrote:
> Thiago Jung Bauermann wrote:
> > + /* We call decode_line_n1 directly here instead of using
>
> Typo: decode_line_1

Doh, forgot to fix that one in my commit. Will do in a moment.

> Otherwise, this is OK.

Awesome! Thank you very much for helping me with this patch!
I've just committed the patch below.
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center

2011-03-31 Thiago Jung Bauermann <bauerman@br.ibm.com>
Sergio Durigan Junior <sergiodj@linux.vnet.ibm.com>

Implement support for PowerPC BookE ranged breakpoints.

gdb/
* NEWS: Mention support for ranged breakpoints on embedded PowerPC.
* breakpoint.h (struct bp_target_info) <length>: New member
variable.
(struct breakpoint_ops) <breakpoint_hit>: Take struct bp_location
instead of struct breakpoint as argument, and also add ASPACE
and BP_ADDR arguments. Update all callers.
(struct breakpoint_ops) <print_one_detail>: New method.
(struct breakpoint) <addr_string_range_end>: New member variable.
* breakpoint.c (breakpoint_location_address_match): Add function
prototype.
(insert_bp_location): Set bl->target_info.length.
(breakpoint_here_p): Call breakpoint_location_address_match.
(moribund_breakpoint_here_p): Likewise.
(regular_breakpoint_inserted_here_p): Likewise.
(breakpoint_thread_match): Likewise.
(bpstat_stop_status): Likewise.
(bpstat_check_location): Move call to
breakpoint_ops.breakpoint_hit to the top.
(print_one_breakpoint_location): Call
breakpoint_ops.print_one_detail if available.
(breakpoint_address_match_range): New function.
(breakpoint_location_address_match): Likewise.
(breakpoint_locations_match): Compare the length field of the
locations too.
(hw_breakpoint_used_count): Count resources used by all locations
in a breakpoint, and use breakpoint_ops.resources_needed if
available.
(breakpoint_hit_ranged_breakpoint): New function.
(resources_needed_ranged_breakpoint): Likewise.
(print_it_ranged_breakpoint): Likewise.
(print_one_ranged_breakpoint): Likewise.
(print_one_detail_ranged_breakpoint): Likewise.
(print_mention_ranged_breakpoint): Likewise.
(print_recreate_ranged_breakpoint): Likewise.
(ranged_breakpoint_ops): New structure.
(find_breakpoint_range_end): New function.
(break_range_command): Likewise.
(delete_breakpoint): Free addr_string_range_end.
(update_breakpoint_locations): Add SALS_END argument. Update
all callers. Calculate breakpoint length if a non-zero SALS_END
is given. Call breakpoint_locations_match instead of
breakpoint_address_match.
(reset_breakpoint): Find SaL of the end of the range if B is a
ranged breakpoint.
(_initialize_breakpoint): Register break-range command.
* defs.h (print_core_address): Add function prototype.
* ppc-linux-nat.c (ppc_linux_ranged_break_num_registers): New
function.
(ppc_linux_insert_hw_breakpoint): Support ranged breakpoints.
(ppc_linux_remove_hw_breakpoint): Likewise.
(_initialize_ppc_linux_nat): Initialize
to_ranged_break_num_registers.
* target.c (update_current_target): Add comment about
to_ranged_break_num_registers.
(target_ranged_break_num_registers): New function.
* target.h (struct target_ops) <to_ranged_break_num_registers>:
New method.
(target_ranged_break_num_registers): Add function prototype.
* ui-out.c (ui_out_field_core_addr): Move address-printing logic to ...
* utils.c (print_core_address): ... here.

gdb/doc/
* gdb.texinfo (PowerPC Embedded): Document ranged breakpoints.

Index: gdb.git/gdb/NEWS
===================================================================
--- gdb.git.orig/gdb/NEWS 2011-03-30 11:41:55.000000000 -0300
+++ gdb.git/gdb/NEWS 2011-03-30 11:54:59.000000000 -0300
@@ -156,6 +156,12 @@
libthread_db library with the "set libthread-db-search-path"
command. See the user manual for more details on this command.

+* When natively debugging programs on PowerPC BookE processors running
+ a Linux kernel version 2.6.34 or later, GDB supports ranged breakpoints,
+ which stop execution of the inferior whenever it executes an instruction
+ at any address within the specified range. See the "PowerPC Embedded"
+ section in the user manual for more details.
+
* New features in the GDB remote stub, GDBserver

** GDBserver is now supported on PowerPC LynxOS (versions 4.x and 5.x),
Index: gdb.git/gdb/ada-lang.c
===================================================================
--- gdb.git.orig/gdb/ada-lang.c 2011-03-30 11:41:55.000000000 -0300
+++ gdb.git/gdb/ada-lang.c 2011-03-30 11:54:59.000000000 -0300
@@ -10925,6 +10925,7 @@ static struct breakpoint_ops catch_excep
NULL, /* resources_needed */
print_it_catch_exception,
print_one_catch_exception,
+ NULL, /* print_one_detail */
print_mention_catch_exception,
print_recreate_catch_exception
};
@@ -10964,6 +10965,7 @@ static struct breakpoint_ops catch_excep
NULL, /* resources_needed */
print_it_catch_exception_unhandled,
print_one_catch_exception_unhandled,
+ NULL, /* print_one_detail */
print_mention_catch_exception_unhandled,
print_recreate_catch_exception_unhandled
};
@@ -11001,6 +11003,7 @@ static struct breakpoint_ops catch_asser
NULL, /* resources_needed */
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 2011-03-30 11:54:59.000000000 -0300
+++ gdb.git/gdb/breakpoint.c 2011-03-30 11:54:59.000000000 -0300
@@ -131,6 +131,10 @@ static int breakpoint_address_match (str
static int watchpoint_locations_match (struct bp_location *loc1,
struct bp_location *loc2);

+static int breakpoint_location_address_match (struct bp_location *bl,
+ struct address_space *aspace,
+ CORE_ADDR addr);
+
static void breakpoints_info (char *, int);

static void watchpoints_info (char *, int);
@@ -1537,6 +1541,7 @@ insert_bp_location (struct bp_location *
memset (&bl->target_info, 0, sizeof (bl->target_info));
bl->target_info.placed_address = bl->address;
bl->target_info.placed_address_space = bl->pspace->aspace;
+ bl->target_info.length = bl->length;

if (bl->loc_type == bp_loc_software_breakpoint
|| bl->loc_type == bp_loc_hardware_breakpoint)
@@ -2799,11 +2804,10 @@ breakpoint_here_p (struct address_space
&& bl->loc_type != bp_loc_hardware_breakpoint)
continue;

- /* ALL_BP_LOCATIONS bp_location has bl->OWNER always non-NULL. */
+ /* ALL_BP_LOCATIONS bp_location has BL->OWNER always non-NULL. */
if ((breakpoint_enabled (bl->owner)
|| bl->owner->enable_state == bp_permanent)
- && breakpoint_address_match (bl->pspace->aspace, bl->address,
- aspace, pc))
+ && breakpoint_location_address_match (bl, aspace, pc))
{
if (overlay_debugging
&& section_is_overlay (bl->section)
@@ -2828,8 +2832,7 @@ moribund_breakpoint_here_p (struct addre
int ix;

for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
- if (breakpoint_address_match (loc->pspace->aspace, loc->address,
- aspace, pc))
+ if (breakpoint_location_address_match (loc, aspace, pc))
return 1;

return 0;
@@ -2853,8 +2856,7 @@ regular_breakpoint_inserted_here_p (stru
continue;

if (bl->inserted
- && breakpoint_address_match (bl->pspace->aspace, bl->address,
- aspace, pc))
+ && breakpoint_location_address_match (bl, aspace, pc))
{
if (overlay_debugging
&& section_is_overlay (bl->section)
@@ -2971,8 +2973,7 @@ breakpoint_thread_match (struct address_
&& bl->owner->enable_state != bp_permanent)
continue;

- if (!breakpoint_address_match (bl->pspace->aspace, bl->address,
- aspace, pc))
+ if (!breakpoint_location_address_match (bl, aspace, pc))
continue;

if (bl->owner->thread != -1)
@@ -3860,6 +3861,9 @@ bpstat_check_location (const struct bp_l
/* BL is from existing struct breakpoint. */
gdb_assert (b != NULL);

+ if (b->ops && b->ops->breakpoint_hit)
+ return b->ops->breakpoint_hit (bl, aspace, bp_addr);
+
/* By definition, the inferior does not report stops at
tracepoints. */
if (is_tracepoint (b))
@@ -3888,7 +3892,7 @@ bpstat_check_location (const struct bp_l
if (is_hardware_watchpoint (b)
&& b->watchpoint_triggered == watch_triggered_no)
return 0;
-
+
if (b->type == bp_hardware_breakpoint)
{
if (bl->address != bp_addr)
@@ -3899,13 +3903,6 @@ bpstat_check_location (const struct bp_l
return 0;
}

- if (b->type == bp_catchpoint)
- {
- gdb_assert (b->ops != NULL && b->ops->breakpoint_hit != NULL);
- if (!b->ops->breakpoint_hit (b))
- return 0;
- }
-
return 1;
}

@@ -4265,8 +4262,7 @@ bpstat_stop_status (struct address_space

for (ix = 0; VEC_iterate (bp_location_p, moribund_locations, ix, loc); ++ix)
{
- if (breakpoint_address_match (loc->pspace->aspace, loc->address,
- aspace, bp_addr))
+ if (breakpoint_location_address_match (loc, aspace, bp_addr))
{
bs = bpstat_alloc (loc, &bs_link);
/* For hits of moribund locations, we should just proceed. */
@@ -4952,9 +4948,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);
@@ -5537,6 +5536,39 @@ breakpoint_address_match (struct address
&& addr1 == addr2);
}

+/* Returns true if {ASPACE2,ADDR2} falls within the range determined by
+ {ASPACE1,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,
+ int len1, struct address_space *aspace2,
+ CORE_ADDR addr2)
+{
+ return ((gdbarch_has_global_breakpoints (target_gdbarch)
+ || aspace1 == aspace2)
+ && addr2 >= addr1 && addr2 < addr1 + len1);
+}
+
+/* Returns true if {ASPACE,ADDR} matches the breakpoint BL. BL may be
+ a ranged breakpoint. In most targets, a match happens only if ASPACE
+ matches the breakpoint's address space. On targets that have global
+ breakpoints, the address space doesn't really matter. */
+
+static int
+breakpoint_location_address_match (struct bp_location *bl,
+ struct address_space *aspace,
+ CORE_ADDR addr)
+{
+ return (breakpoint_address_match (bl->pspace->aspace, bl->address,
+ aspace, addr)
+ || (bl->length
+ && breakpoint_address_match_range (bl->pspace->aspace,
+ bl->address, bl->length,
+ aspace, addr)));
+}
+
/* Assuming LOC1 and LOC2's types' have meaningful target addresses
(breakpoint_address_is_meaningful), returns true if LOC1 and LOC2
represent the same location. */
@@ -5559,8 +5591,10 @@ breakpoint_locations_match (struct bp_lo
else if (hw_point1)
return watchpoint_locations_match (loc1, loc2);
else
- return breakpoint_address_match (loc1->pspace->aspace, loc1->address,
- loc2->pspace->aspace, loc2->address);
+ /* We compare bp_location.length in order to cover ranged breakpoints. */
+ return (breakpoint_address_match (loc1->pspace->aspace, loc1->address,
+ loc2->pspace->aspace, loc2->address)
+ && loc1->length == loc2->length);
}

static void
@@ -6201,9 +6235,10 @@ remove_catch_fork (struct bp_location *b
catchpoints. */

static int
-breakpoint_hit_catch_fork (struct breakpoint *b)
+breakpoint_hit_catch_fork (const struct bp_location *bl,
+ struct address_space *aspace, CORE_ADDR bp_addr)
{
- return inferior_has_forked (inferior_ptid, &b->forked_inferior_pid);
+ return inferior_has_forked (inferior_ptid, &bl->owner->forked_inferior_pid);
}

/* Implement the "print_it" breakpoint_ops method for fork
@@ -6272,6 +6307,7 @@ static struct breakpoint_ops catch_fork_
NULL, /* resources_needed */
print_it_catch_fork,
print_one_catch_fork,
+ NULL, /* print_one_detail */
print_mention_catch_fork,
print_recreate_catch_fork
};
@@ -6298,9 +6334,10 @@ remove_catch_vfork (struct bp_location *
catchpoints. */

static int
-breakpoint_hit_catch_vfork (struct breakpoint *b)
+breakpoint_hit_catch_vfork (const struct bp_location *bl,
+ struct address_space *aspace, CORE_ADDR bp_addr)
{
- return inferior_has_vforked (inferior_ptid, &b->forked_inferior_pid);
+ return inferior_has_vforked (inferior_ptid, &bl->owner->forked_inferior_pid);
}

/* Implement the "print_it" breakpoint_ops method for vfork
@@ -6368,6 +6405,7 @@ static struct breakpoint_ops catch_vfork
NULL, /* resources_needed */
print_it_catch_vfork,
print_one_catch_vfork,
+ NULL, /* print_one_detail */
print_mention_catch_vfork,
print_recreate_catch_vfork
};
@@ -6457,12 +6495,14 @@ remove_catch_syscall (struct bp_location
catchpoints. */

static int
-breakpoint_hit_catch_syscall (struct breakpoint *b)
+breakpoint_hit_catch_syscall (const struct bp_location *bl,
+ struct address_space *aspace, CORE_ADDR bp_addr)
{
/* We must check if we are catching specific syscalls in this
breakpoint. If we are, then we must guarantee that the called
syscall is the same syscall we are catching. */
int syscall_number = 0;
+ const struct breakpoint *b = bl->owner;

if (!inferior_has_called_syscall (inferior_ptid, &syscall_number))
return 0;
@@ -6531,7 +6571,7 @@ print_it_catch_syscall (struct breakpoin

static void
print_one_catch_syscall (struct breakpoint *b,
- struct bp_location **last_loc)
+ struct bp_location **last_loc)
{
struct value_print_options opts;

@@ -6652,6 +6692,7 @@ static struct breakpoint_ops catch_sysca
NULL, /* resources_needed */
print_it_catch_syscall,
print_one_catch_syscall,
+ NULL, /* print_one_detail */
print_mention_catch_syscall,
print_recreate_catch_syscall
};
@@ -6747,9 +6788,10 @@ remove_catch_exec (struct bp_location *b
}

static int
-breakpoint_hit_catch_exec (struct breakpoint *b)
+breakpoint_hit_catch_exec (const struct bp_location *bl,
+ struct address_space *aspace, CORE_ADDR bp_addr)
{
- return inferior_has_execd (inferior_ptid, &b->exec_pathname);
+ return inferior_has_execd (inferior_ptid, &bl->owner->exec_pathname);
}

static enum print_stop_action
@@ -6806,6 +6848,7 @@ static struct breakpoint_ops catch_exec_
NULL, /* resources_needed */
print_it_catch_exec,
print_one_catch_exec,
+ NULL, /* print_one_detail */
print_mention_catch_exec,
print_recreate_catch_exec
};
@@ -6829,13 +6872,22 @@ create_syscall_event_catchpoint (int tem
static int
hw_breakpoint_used_count (void)
{
- struct breakpoint *b;
int i = 0;
+ struct breakpoint *b;
+ struct bp_location *bl;

ALL_BREAKPOINTS (b)
{
if (b->type == bp_hardware_breakpoint && breakpoint_enabled (b))
- i++;
+ for (bl = b->loc; bl; bl = bl->next)
+ {
+ /* Special types of hardware breakpoints may use more than
+ one register. */
+ if (b->ops && b->ops->resources_needed)
+ i += b->ops->resources_needed (bl);
+ else
+ i++;
+ }
}

return i;
@@ -8303,6 +8355,309 @@ stopat_command (char *arg, int from_tty)
break_command_1 (arg, 0, from_tty);
}

+/* Implement the "breakpoint_hit" breakpoint_ops method for
+ ranged breakpoints. */
+
+static int
+breakpoint_hit_ranged_breakpoint (const struct bp_location *bl,
+ struct address_space *aspace,
+ CORE_ADDR bp_addr)
+{
+ return breakpoint_address_match_range (bl->pspace->aspace, bl->address,
+ bl->length, aspace, bp_addr);
+}
+
+/* Implement the "resources_needed" breakpoint_ops method for
+ ranged breakpoints. */
+
+static int
+resources_needed_ranged_breakpoint (const struct bp_location *bl)
+{
+ return target_ranged_break_num_registers ();
+}
+
+/* Implement the "print_it" breakpoint_ops method for
+ ranged breakpoints. */
+
+static enum print_stop_action
+print_it_ranged_breakpoint (struct breakpoint *b)
+{
+ struct bp_location *bl = b->loc;
+
+ gdb_assert (b->type == bp_hardware_breakpoint);
+
+ /* Ranged breakpoints have only one location. */
+ gdb_assert (bl && bl->next == NULL);
+
+ annotate_breakpoint (b->number);
+ if (b->disposition == disp_del)
+ ui_out_text (uiout, "\nTemporary ranged breakpoint ");
+ else
+ ui_out_text (uiout, "\nRanged breakpoint ");
+ if (ui_out_is_mi_like_p (uiout))
+ {
+ ui_out_field_string (uiout, "reason",
+ async_reason_lookup (EXEC_ASYNC_BREAKPOINT_HIT));
+ ui_out_field_string (uiout, "disp", bpdisp_text (b->disposition));
+ }
+ ui_out_field_int (uiout, "bkptno", b->number);
+ ui_out_text (uiout, ", ");
+
+ return PRINT_SRC_AND_LOC;
+}
+
+/* Implement the "print_one" breakpoint_ops method for
+ ranged breakpoints. */
+
+static void
+print_one_ranged_breakpoint (struct breakpoint *b,
+ struct bp_location **last_loc)
+{
+ struct bp_location *bl = b->loc;
+ struct value_print_options opts;
+
+ /* Ranged breakpoints have only one location. */
+ gdb_assert (bl && bl->next == NULL);
+
+ get_user_print_options (&opts);
+
+ if (opts.addressprint)
+ /* We don't print the address range here, it will be printed later
+ by print_one_detail_ranged_breakpoint. */
+ ui_out_field_skip (uiout, "addr");
+ annotate_field (5);
+ print_breakpoint_location (b, bl);
+ *last_loc = bl;
+}
+
+/* Implement the "print_one_detail" breakpoint_ops method for
+ ranged breakpoints. */
+
+static void
+print_one_detail_ranged_breakpoint (const struct breakpoint *b,
+ struct ui_out *uiout)
+{
+ CORE_ADDR address_start, address_end;
+ struct bp_location *bl = b->loc;
+ struct ui_stream *stb = ui_out_stream_new (uiout);
+ struct cleanup *cleanup = make_cleanup_ui_out_stream_delete (stb);
+
+ gdb_assert (bl);
+
+ address_start = bl->address;
+ address_end = address_start + bl->length - 1;
+
+ ui_out_text (uiout, "\taddress range: ");
+ fprintf_unfiltered (stb->stream, "[%s, %s]",
+ print_core_address (bl->gdbarch, address_start),
+ print_core_address (bl->gdbarch, address_end));
+ ui_out_field_stream (uiout, "addr", stb);
+ ui_out_text (uiout, "\n");
+
+ do_cleanups (cleanup);
+}
+
+/* Implement the "print_mention" breakpoint_ops method for
+ ranged breakpoints. */
+
+static void
+print_mention_ranged_breakpoint (struct breakpoint *b)
+{
+ struct bp_location *bl = b->loc;
+
+ gdb_assert (bl);
+ gdb_assert (b->type == bp_hardware_breakpoint);
+
+ if (ui_out_is_mi_like_p (uiout))
+ return;
+
+ printf_filtered (_("Hardware assisted ranged breakpoint %d from %s to %s."),
+ b->number, paddress (bl->gdbarch, bl->address),
+ paddress (bl->gdbarch, bl->address + bl->length - 1));
+}
+
+/* Implement the "print_recreate" breakpoint_ops method for
+ ranged breakpoints. */
+
+static void
+print_recreate_ranged_breakpoint (struct breakpoint *b, struct ui_file *fp)
+{
+ fprintf_unfiltered (fp, "break-range %s, %s", b->addr_string,
+ b->addr_string_range_end);
+}
+
+/* The breakpoint_ops structure to be used in ranged breakpoints. */
+
+static struct breakpoint_ops ranged_breakpoint_ops =
+{
+ NULL, /* insert */
+ NULL, /* remove */
+ breakpoint_hit_ranged_breakpoint,
+ resources_needed_ranged_breakpoint,
+ print_it_ranged_breakpoint,
+ print_one_ranged_breakpoint,
+ print_one_detail_ranged_breakpoint,
+ print_mention_ranged_breakpoint,
+ print_recreate_ranged_breakpoint
+};
+
+/* Find the address where the end of the breakpoint range should be
+ placed, given the SAL of the end of the range. This is so that if
+ the user provides a line number, the end of the range is set to the
+ last instruction of the given line. */
+
+static CORE_ADDR
+find_breakpoint_range_end (struct symtab_and_line sal)
+{
+ CORE_ADDR end;
+
+ /* If the user provided a PC value, use it. Otherwise,
+ find the address of the end of the given location. */
+ if (sal.explicit_pc)
+ end = sal.pc;
+ else
+ {
+ int ret;
+ CORE_ADDR start;
+
+ ret = find_line_pc_range (sal, &start, &end);
+ if (!ret)
+ error (_("Could not find location of the end of the range."));
+
+ /* find_line_pc_range returns the start of the next line. */
+ end--;
+ }
+
+ return end;
+}
+
+/* Implement the "break-range" CLI command. */
+
+static void
+break_range_command (char *arg, int from_tty)
+{
+ char *arg_start, *addr_string_start, *addr_string_end;
+ struct linespec_result canonical_start, canonical_end;
+ int bp_count, can_use_bp, length;
+ CORE_ADDR end;
+ struct breakpoint *b;
+ struct symtab_and_line sal_start, sal_end;
+ struct symtabs_and_lines sals_start, sals_end;
+ struct cleanup *cleanup_bkpt;
+
+ /* We don't support software ranged breakpoints. */
+ if (target_ranged_break_num_registers () < 0)
+ error (_("This target does not support hardware ranged breakpoints."));
+
+ bp_count = hw_breakpoint_used_count ();
+ bp_count += target_ranged_break_num_registers ();
+ can_use_bp = target_can_use_hardware_watchpoint (bp_hardware_breakpoint,
+ bp_count, 0);
+ if (can_use_bp < 0)
+ error (_("Hardware breakpoints used exceeds limit."));
+
+ if (arg == NULL || arg[0] == '\0')
+ error(_("No address range specified."));
+
+ sals_start.sals = NULL;
+ sals_start.nelts = 0;
+ init_linespec_result (&canonical_start);
+
+ while (*arg == ' ' || *arg == '\t')
+ arg++;
+
+ parse_breakpoint_sals (&arg, &sals_start, &canonical_start, NULL);
+
+ sal_start = sals_start.sals[0];
+ addr_string_start = canonical_start.canonical[0];
+ cleanup_bkpt = make_cleanup (xfree, addr_string_start);
+ xfree (sals_start.sals);
+ xfree (canonical_start.canonical);
+
+ if (arg[0] != ',')
+ error (_("Too few arguments."));
+ else if (sals_start.nelts == 0)
+ error (_("Could not find location of the beginning of the range."));
+ else if (sals_start.nelts != 1)
+ error (_("Cannot create a ranged breakpoint with multiple locations."));
+
+ resolve_sal_pc (&sal_start);
+
+ arg++; /* Skip the comma. */
+ while (*arg == ' ' || *arg == '\t')
+ arg++;
+
+ /* Parse the end location. */
+
+ sals_end.sals = NULL;
+ sals_end.nelts = 0;
+ init_linespec_result (&canonical_end);
+ arg_start = arg;
+
+ /* We call decode_line_n1 directly here instead of using
+ parse_breakpoint_sals because we need to specify the start location's
+ symtab and line as the default symtab and line for the end of the
+ range. This makes it possible to have ranges like "foo.c:27, +14",
+ where +14 means 14 lines from the start location. */
+ sals_end = decode_line_1 (&arg, 1, sal_start.symtab, sal_start.line,
+ &canonical_end, NULL);
+
+ /* canonical_end can be NULL if it was of the form "*0xdeadbeef". */
+ if (canonical_end.canonical == NULL)
+ canonical_end.canonical = xcalloc (1, sizeof (char **));
+ /* Add the string if not present. */
+ if (arg_start != arg && canonical_end.canonical[0] == NULL)
+ canonical_end.canonical[0] = savestring (arg_start, arg - arg_start);
+
+ sal_end = sals_end.sals[0];
+ addr_string_end = canonical_end.canonical[0];
+ make_cleanup (xfree, addr_string_end);
+ xfree (sals_end.sals);
+ xfree (canonical_end.canonical);
+
+ if (sals_end.nelts == 0)
+ error (_("Could not find location of the end of the range."));
+ else if (sals_end.nelts != 1)
+ error (_("Cannot create a ranged breakpoint with multiple locations."));
+
+ resolve_sal_pc (&sal_end);
+
+ end = find_breakpoint_range_end (sal_end);
+ if (sal_start.pc > end)
+ error (_("Invalid address range, end preceeds start."));
+
+ length = end - sal_start.pc + 1;
+ if (length < 0)
+ /* Length overflowed. */
+ error (_("Address range too large."));
+ else if (length == 1)
+ {
+ /* This range is simple enough to be handled by
+ the `hbreak' command. */
+ hbreak_command (addr_string_start, 1);
+
+ do_cleanups (cleanup_bkpt);
+
+ return;
+ }
+
+ /* Now set up the breakpoint. */
+ b = set_raw_breakpoint (get_current_arch (), sal_start,
+ bp_hardware_breakpoint);
+ set_breakpoint_count (breakpoint_count + 1);
+ b->number = breakpoint_count;
+ b->disposition = disp_donttouch;
+ b->addr_string = addr_string_start;
+ b->addr_string_range_end = addr_string_end;
+ b->ops = &ranged_breakpoint_ops;
+ b->loc->length = length;
+
+ discard_cleanups (cleanup_bkpt);
+
+ mention (b);
+ update_global_location_list (1);
+}
+
/* Return non-zero if EXP is verified as constant. Returned zero
means EXP is variable. Also the constant detection may fail for
some constant expressions and in such case still falsely return
@@ -8455,6 +8810,7 @@ static struct breakpoint_ops watchpoint_
resources_needed_watchpoint,
NULL, /* print_it */
NULL, /* print_one */
+ NULL, /* print_one_detail */
NULL, /* print_mention */
NULL /* print_recreate */
};
@@ -9233,6 +9589,7 @@ static struct breakpoint_ops gnu_v3_exce
NULL, /* resources_needed */
print_exception_catchpoint,
print_one_exception_catchpoint,
+ NULL, /* print_one_detail */
print_mention_exception_catchpoint,
print_recreate_exception_catchpoint
};
@@ -10179,6 +10536,7 @@ delete_breakpoint (struct breakpoint *bp
xfree (bpt->cond_string);
xfree (bpt->cond_exp);
xfree (bpt->addr_string);
+ xfree (bpt->addr_string_range_end);
xfree (bpt->exp);
xfree (bpt->exp_string);
xfree (bpt->exp_string_reparse);
@@ -10473,14 +10831,21 @@ update_static_tracepoint (struct breakpo
return sal;
}

+/* Create new breakpoint locations for B (a hardware or software breakpoint)
+ based on SALS and SALS_END. If SALS_END.NELTS is not zero, then B is
+ a ranged breakpoint. */
+
void
update_breakpoint_locations (struct breakpoint *b,
- struct symtabs_and_lines sals)
+ struct symtabs_and_lines sals,
+ struct symtabs_and_lines sals_end)
{
int i;
- char *s;
struct bp_location *existing_locations = b->loc;

+ /* Ranged breakpoints have only one start location and one end location. */
+ gdb_assert (sals_end.nelts == 0 || (sals.nelts == 1 && sals_end.nelts == 1));
+
/* If there's no new locations, and all existing locations are
pending, don't do anything. This optimizes the common case where
all locations are in the same shared library, that was unloaded.
@@ -10501,6 +10866,7 @@ update_breakpoint_locations (struct brea
old symtab. */
if (b->cond_string != NULL)
{
+ char *s;
struct gdb_exception e;

s = b->cond_string;
@@ -10527,6 +10893,13 @@ update_breakpoint_locations (struct brea

if (b->line_number == 0)
b->line_number = sals.sals[i].line;
+
+ if (sals_end.nelts)
+ {
+ CORE_ADDR end = find_breakpoint_range_end (sals_end.sals[0]);
+
+ new_loc->length = end - sals.sals[0].pc + 1;
+ }
}

/* Update locations of permanent breakpoints. */
@@ -10552,8 +10925,7 @@ update_breakpoint_locations (struct brea
if (have_ambiguous_names)
{
for (; l; l = l->next)
- if (breakpoint_address_match (e->pspace->aspace, e->address,
- l->pspace->aspace, l->address))
+ if (breakpoint_locations_match (e, l))
{
l->enabled = 0;
break;
@@ -10673,8 +11045,9 @@ static void
re_set_breakpoint (struct breakpoint *b)
{
int found;
- struct symtabs_and_lines sals;
+ struct symtabs_and_lines sals, sals_end;
struct symtabs_and_lines expanded = {0};
+ struct symtabs_and_lines expanded_end = {0};
struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);

input_radix = b->input_radix;
@@ -10689,7 +11062,17 @@ re_set_breakpoint (struct breakpoint *b)
expanded = expand_line_sal_maybe (sals.sals[0]);
}

- update_breakpoint_locations (b, expanded);
+ if (b->addr_string_range_end)
+ {
+ sals_end = addr_string_to_sals (b, b->addr_string_range_end, &found);
+ if (found)
+ {
+ make_cleanup (xfree, sals_end.sals);
+ expanded_end = expand_line_sal_maybe (sals_end.sals[0]);
+ }
+ }
+
+ update_breakpoint_locations (b, expanded, expanded_end);
do_cleanups (cleanups);
}

@@ -12733,7 +13116,23 @@ inferior in all-stop mode, gdb behaves a
&show_always_inserted_mode,
&breakpoint_set_cmdlist,
&breakpoint_show_cmdlist);
-
+
+ add_com ("break-range", class_breakpoint, break_range_command, _("\
+Set a breakpoint for an address range.\n\
+break-range START-LOCATION, END-LOCATION\n\
+where START-LOCATION and END-LOCATION can be one of the following:\n\
+ LINENUM, for that line in the current file,\n\
+ FILE:LINENUM, for that line in that file,\n\
+ +OFFSET, for that number of lines after the current line\n\
+ or the start of the range\n\
+ FUNCTION, for the first line in that function,\n\
+ FILE:FUNCTION, to distinguish among like-named static functions.\n\
+ *ADDRESS, for the instruction at that address.\n\
+\n\
+The breakpoint will stop execution of the inferior whenever it executes\n\
+an instruction at any address within the [START-LOCATION, END-LOCATION]\n\
+range (including START-LOCATION and END-LOCATION)."));
+
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 2011-03-30 11:41:55.000000000 -0300
+++ gdb.git/gdb/breakpoint.h 2011-03-30 15:55:53.000000000 -0300
@@ -231,6 +231,10 @@ struct bp_target_info
is used to determine the type of breakpoint to insert. */
CORE_ADDR placed_address;

+ /* If this is a ranged breakpoint, then this field contains the
+ length of the range that will be watched for execution. */
+ int length;
+
/* If the breakpoint lives in memory and reading that memory would
give back the breakpoint, instead of the original contents, then
the original contents are cached here. Only SHADOW_LEN bytes of
@@ -339,7 +343,8 @@ struct bp_location
CORE_ADDR address;

/* For hardware watchpoints, the size of the memory region being
- watched. */
+ watched. For hardware ranged breakpoints, the size of the
+ breakpoint range. */
int length;

/* Type of hardware watchpoint. */
@@ -397,7 +402,8 @@ struct breakpoint_ops

/* Return non-zero if the debugger should tell the user that this
breakpoint was hit. */
- int (*breakpoint_hit) (struct breakpoint *);
+ int (*breakpoint_hit) (const struct bp_location *, struct address_space *,
+ CORE_ADDR);

/* Tell how many hardware resources (debug registers) are needed
for this breakpoint. If this function is not provided, then
@@ -412,6 +418,20 @@ struct breakpoint_ops
breakpoints". */
void (*print_one) (struct breakpoint *, struct bp_location **);

+ /* Display extra information about this breakpoint, below the normal
+ breakpoint description in "info breakpoints".
+
+ In the example below, the "address range" line was printed
+ by print_one_detail_ranged_breakpoint.
+
+ (gdb) info breakpoints
+ Num Type Disp Enb Address What
+ 2 hw breakpoint keep y in main at test-watch.c:70
+ address range: [0x10000458, 0x100004c7]
+
+ */
+ 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 *);
@@ -502,6 +522,11 @@ struct breakpoint

/* String we used to set the breakpoint (malloc'd). */
char *addr_string;
+
+ /* For a ranged breakpoint, the string we used to find
+ the end of the range (malloc'd). */
+ char *addr_string_range_end;
+
/* Architecture we used to set the breakpoint. */
struct gdbarch *gdbarch;
/* Language we used to set the breakpoint. */
@@ -904,7 +929,8 @@ extern int breakpoint_thread_match (stru
extern void until_break_command (char *, int, int);

extern void update_breakpoint_locations (struct breakpoint *b,
- struct symtabs_and_lines sals);
+ struct symtabs_and_lines sals,
+ struct symtabs_and_lines sals_end);

extern void breakpoint_re_set (void);

Index: gdb.git/gdb/defs.h
===================================================================
--- gdb.git.orig/gdb/defs.h 2011-03-30 11:41:55.000000000 -0300
+++ gdb.git/gdb/defs.h 2011-03-30 11:54:59.000000000 -0300
@@ -532,6 +532,12 @@ extern const char *host_address_to_strin
This is usually formatted similar to 0x%lx. */
extern const char *paddress (struct gdbarch *gdbarch, CORE_ADDR addr);

+/* Return a string representation in hexadecimal notation of ADDRESS,
+ which is suitable for printing. */
+
+extern const char *print_core_address (struct gdbarch *gdbarch,
+ CORE_ADDR address);
+
/* %d for LONGEST */
extern char *plongest (LONGEST l);
/* %u for ULONGEST */
Index: gdb.git/gdb/doc/gdb.texinfo
===================================================================
--- gdb.git.orig/gdb/doc/gdb.texinfo 2011-03-30 11:41:55.000000000 -0300
+++ gdb.git/gdb/doc/gdb.texinfo 2011-03-30 11:54:59.000000000 -0300
@@ -18742,9 +18742,27 @@ region using one of the following comman
(@value{GDBP}) watch @{char[@var{length}]@} @var{address}
@end smallexample

+@cindex ranged breakpoint
+PowerPC embedded processors support hardware accelerated
+@dfn{ranged breakpoints}. A ranged breakpoint stops execution of
+the inferior whenever it executes an instruction at any address within
+the range it specifies. To set a ranged breakpoint in @value{GDBN},
+use the @code{break-range} command.
+
@value{GDBN} provides the following PowerPC-specific commands:

@table @code
+@kindex break-range
+@item break-range @var{start-location}, @var{end-location}
+Set a breakpoint for an address range.
+@var{start-location} and @var{end-location} can specify a function name,
+a line number, an offset of lines from the current line or from the start
+location, or an address of an instruction (see @ref{Specify Location},
+for a list of all the possible ways to specify a @var{location}.)
+The breakpoint will stop execution of the inferior whenever it
+executes an instruction at any address within the specified range,
+(including @var{start-location} and @var{end-location}.)
+
@kindex set powerpc
@item set powerpc soft-float
@itemx show powerpc soft-float
Index: gdb.git/gdb/ppc-linux-nat.c
===================================================================
--- gdb.git.orig/gdb/ppc-linux-nat.c 2011-03-30 11:41:55.000000000 -0300
+++ gdb.git/gdb/ppc-linux-nat.c 2011-03-30 11:54:59.000000000 -0300
@@ -1637,6 +1637,19 @@ booke_remove_point (struct ppc_hw_breakp
hw_breaks[i].hw_break = NULL;
}

+/* Return the number of registers needed for a ranged breakpoint. */
+
+static int
+ppc_linux_ranged_break_num_registers (struct target_ops *target)
+{
+ return ((have_ptrace_booke_interface ()
+ && booke_debug_info.features & PPC_DEBUG_FEATURE_INSN_BP_RANGE)?
+ 2 : -1);
+}
+
+/* Insert the hardware breakpoint described by BP_TGT. Returns 0 for
+ success, 1 if hardware breakpoints are not supported or -1 for failure. */
+
static int
ppc_linux_insert_hw_breakpoint (struct gdbarch *gdbarch,
struct bp_target_info *bp_tgt)
@@ -1650,12 +1663,24 @@ ppc_linux_insert_hw_breakpoint (struct g

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;

+ if (bp_tgt->length)
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+
+ /* The breakpoint will trigger if the address of the instruction is
+ within the defined range, as follows: p.addr <= address < p.addr2. */
+ p.addr2 = (uint64_t) bp_tgt->placed_address + bp_tgt->length;
+ }
+ else
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+ p.addr2 = 0;
+ }
+
ALL_LWPS (lp, ptid)
booke_insert_point (&p, TIDGET (ptid));

@@ -1675,12 +1700,24 @@ ppc_linux_remove_hw_breakpoint (struct g

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;

+ if (bp_tgt->length)
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+
+ /* The breakpoint will trigger if the address of the instruction is within
+ the defined range, as follows: p.addr <= address < p.addr2. */
+ p.addr2 = (uint64_t) bp_tgt->placed_address + bp_tgt->length;
+ }
+ else
+ {
+ p.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+ p.addr2 = 0;
+ }
+
ALL_LWPS (lp, ptid)
booke_remove_point (&p, TIDGET (ptid));

@@ -2392,6 +2429,7 @@ _initialize_ppc_linux_nat (void)
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_ranged_break_num_registers = ppc_linux_ranged_break_num_registers;

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 2011-03-30 11:41:55.000000000 -0300
+++ gdb.git/gdb/target.c 2011-03-30 11:54:59.000000000 -0300
@@ -594,6 +594,7 @@ update_current_target (void)
INHERIT (to_can_use_hw_breakpoint, t);
INHERIT (to_insert_hw_breakpoint, t);
INHERIT (to_remove_hw_breakpoint, t);
+ /* Do not inherit to_ranged_break_num_registers. */
INHERIT (to_insert_watchpoint, t);
INHERIT (to_remove_watchpoint, t);
INHERIT (to_stopped_data_address, t);
@@ -3491,6 +3492,21 @@ target_verify_memory (const gdb_byte *da
tcomplain ();
}

+/* The documentation for this function is in its prototype declaration
+ in target.h. */
+
+int
+target_ranged_break_num_registers (void)
+{
+ struct target_ops *t;
+
+ for (t = current_target.beneath; t != NULL; t = t->beneath)
+ if (t->to_ranged_break_num_registers != NULL)
+ return t->to_ranged_break_num_registers (t);
+
+ return -1;
+}
+
static void
debug_to_prepare_to_store (struct regcache *regcache)
{
Index: gdb.git/gdb/target.h
===================================================================
--- gdb.git.orig/gdb/target.h 2011-03-30 11:41:55.000000000 -0300
+++ gdb.git/gdb/target.h 2011-03-30 11:54:59.000000000 -0300
@@ -450,6 +450,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_ranged_break_num_registers) (struct target_ops *);
int (*to_insert_hw_breakpoint) (struct gdbarch *, struct bp_target_info *);
int (*to_remove_hw_breakpoint) (struct gdbarch *, struct bp_target_info *);

@@ -1354,6 +1355,11 @@ extern char *target_thread_name (struct
#define target_remove_hw_breakpoint(gdbarch, bp_tgt) \
(*current_target.to_remove_hw_breakpoint) (gdbarch, bp_tgt)

+/* Return number of debug registers needed for a ranged breakpoint,
+ or -1 if ranged breakpoints are not supported. */
+
+extern int target_ranged_break_num_registers (void);
+
/* Return non-zero if target knows the data address which triggered this
target_stopped_by_watchpoint, in such case place it to *ADDR_P. Only the
INFERIOR_PTID task is being queried. */
Index: gdb.git/gdb/ui-out.c
===================================================================
--- gdb.git.orig/gdb/ui-out.c 2011-03-30 11:41:55.000000000 -0300
+++ gdb.git/gdb/ui-out.c 2011-03-30 11:54:59.000000000 -0300
@@ -492,23 +492,8 @@ ui_out_field_core_addr (struct ui_out *u
struct gdbarch *gdbarch,
CORE_ADDR address)
{
- /* Maximum size string returned by hex_string_custom is 50 chars.
- This buffer must be bigger than that, for safety. */
- char addstr[64];
- int addr_bit = gdbarch_addr_bit (gdbarch);
-
- if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT))
- address &= ((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, hex_string_custom (address, 8));
- else
- strcpy (addstr, hex_string_custom (address, 16));
-
- ui_out_field_string (uiout, fldname, addstr);
+ ui_out_field_string (uiout, fldname,
+ print_core_address (gdbarch, address));
}

void
Index: gdb.git/gdb/utils.c
===================================================================
--- gdb.git.orig/gdb/utils.c 2011-03-30 11:41:55.000000000 -0300
+++ gdb.git/gdb/utils.c 2011-03-30 11:54:59.000000000 -0300
@@ -3224,6 +3224,25 @@ paddress (struct gdbarch *gdbarch, CORE_
return hex_string (addr);
}

+/* This function is described in "defs.h". */
+
+const char *
+print_core_address (struct gdbarch *gdbarch, CORE_ADDR address)
+{
+ int addr_bit = gdbarch_addr_bit (gdbarch);
+
+ if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT))
+ address &= ((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)
+ return hex_string_custom (address, 8);
+ else
+ return hex_string_custom (address, 16);
+}
+
static char *
decimal2str (char *sign, ULONGEST addr, int width)
{
Index: gdb.git/gdb/elfread.c
===================================================================
--- gdb.git.orig/gdb/elfread.c 2011-03-30 16:07:57.000000000 -0300
+++ gdb.git/gdb/elfread.c 2011-03-30 16:08:01.000000000 -0300
@@ -1011,7 +1011,7 @@ elf_gnu_ifunc_resolver_return_stop (stru
struct value *value;
CORE_ADDR resolved_address, resolved_pc;
struct symtab_and_line sal;
- struct symtabs_and_lines sals;
+ struct symtabs_and_lines sals, sals_end;

gdb_assert (b->type == bp_gnu_ifunc_resolver_return);

@@ -1050,9 +1050,10 @@ elf_gnu_ifunc_resolver_return_stop (stru
sal = find_pc_line (resolved_pc, 0);
sals.nelts = 1;
sals.sals = &sal;
+ sals_end.nelts = 0;

b->type = bp_breakpoint;
- update_breakpoint_locations (b, sals);
+ update_breakpoint_locations (b, sals, sals_end);
}

struct build_id