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

Thiago Jung Bauermann
2011-03-12T00:41:07Z

On Mon, 2011-02-28 at 17:52 +0100, Ulrich Weigand wrote:
> Thiago Jung Bauermann wrote:
> > > This adds the SAY_WHERE parameter so that the caller will go ahead to
> > > add location information. However, for a range breakpoint, this will
> > > just be the start of the range, which may or may not be particularly
> > > useful ... Shouldn't we either:
> > > - output the full range (start and end), or
> > > - output nothing ... we'll see the actual location where we stopped anyway
> > >
> > > (Either way, the API change to add SAY_WHERE is no longer needed.)
> >
> > I think it's useful. You have a point in that it makes more sense to
> > print the end of the range as well. I'd have to extend struct breakpoint
> > to save the source file and line number of the end of the range if I
> > were to print them. I'm not sure it's worth it, so I changed
> > print_mention_ranged_breakpoint to print only the start and end address
> > of the range. And dropped the say_where argument. What do you think?
>
> Don't you have to know the end of the range anyway, in order to be
> able to re-set the breakpoint?

I was thinking of the struct breakpoint's source_file and line_number,
which are used mostly for printing (clear_command uses it for finding
breakpoints). For print_mention_ranged_breakpoint I'd need
source_file_range_end and line_number_range_end, but decided against it.

But now that you mention breakpoint re-setting, I just realised that I
need to add addr_string_range_end and make breakpoint_re_set_one use it.
As the patch currently stands, ranged breakpoints will be dropped when
loading new binaries... I'm currently working on this but decided to
send this patch as it is since it addresses all the other points you
raised.

> > +static enum print_stop_action
> > +print_it_ranged_breakpoint (struct breakpoint *b,
> > + const struct value *old_val)
>
> Hmm ... this is not against current mainline, but assumes some of
> the other patches is applied as well, right?

Yes, it applied on top of the masked watchpoints patch. Since this one
seems to be closer to being accepted than the masked watchpoints one, I
reordered the series and now this patch applies directly to mainline.
The changes were minimal (just had to add the print_one_detail
breakpoint_ops method).

> > +{
> > + struct bp_location *bl = b->loc;
> > + struct ui_stream *stb;
> > + struct cleanup *old_chain;
> > +
> > + gdb_assert (b->type == bp_hardware_breakpoint);
> > +
> > + /* Ranged breakpoints have only one location. */
> > + gdb_assert (bl && bl->next == NULL);
> > +
> > + stb = ui_out_stream_new (uiout);
> > + old_chain = make_cleanup_ui_out_stream_delete (stb);
>
> You never use this stream, what's the point of allocating it?

Ouch, leftover from some intermediate version of the code. :-/ Removed.

> > + annotate_breakpoint (b->number);
> > + if (b->disposition == disp_del)
> > + ui_out_text (uiout, "\nTemporary ranged breakpoint ");
> > + else
> > + ui_out_text (uiout, "\nHardware assisted ranged breakpoint ");
>
> Either both or none of these should be using "hardware assisted".
> I'd probably argue for "none": for regular breakpoint, this
> distinction is not made here either ...

Good point. Dropped "hardware assisted".

> > +static void
> > +print_one_ranged_breakpoint (struct breakpoint *b,
> > + struct bp_location **last_loc,
> > + char *wrap_indent)
> > +{
> > + struct bp_location *bl = b->loc;
> > + struct value_print_options opts;
> > + struct ui_stream *stb = ui_out_stream_new (uiout);
> > + struct cleanup *old_chain = make_cleanup_ui_out_stream_delete (stb);
>
> Again the unused stream ...

Ugh.

> > + /* Ranged breakpoints have only one location. */
> > + gdb_assert (bl && bl->next == NULL);
> > +
> > + get_user_print_options (&opts);
> > +
> > + if (opts.addressprint)
> > + ui_out_field_skip (uiout, "addr");
> > + annotate_field (5);
> > + if (bl->enabled)
> > + print_breakpoint_location (b, bl, wrap_indent, stb);
>
> I think you should not check bl->enabled here like that. This is
> done for regular breakpoints only because enabled breakpoints with
> a single disabled location are handled by special code in print_one_breakpoint
> which is not active for breakpoints with custom functions.

Good point. I had to stare at the code for a while to understand (again)
what was going on with print_one_breakpoint and
print_breakpoint_location, it's not very easy to follow (and I thought I
got it right the first time!). Now I just call print_breakpoint_location
directly.

> > +static void
> > +print_one_detail_ranged_breakpoint (const struct breakpoint *b,
> > + struct ui_out *uiout)
> > +{
> > + struct bp_location *bl = b->loc;
> > +
> > + gdb_assert (bl);
> > +
> > + ui_out_text (uiout, "\taddress range: ");
> > + ui_out_field_range_core_addr (uiout, "addr", bl->gdbarch,
> > + bl->address, bl->length);
>
> Do we really need to make a new ui_out_ function for this; this
> seems a bit of a special case for that. Why don't you just generate
> the output here? (Note that here you might want to use a temporary
> stream like the one you had in the above functions but never used
> there ...)

I think I was using that function somewhere else in a previous
incarnation of this patch series, so it stayed. I didn't think it was a
problem since it is analogous to other functions in ui-out.c.

> > +static void
> > +break_range_command (char *arg, int from_tty)
> > +{
> > + char *orig_arg;
> > + char **addr_string_start;
> > + int bp_count, can_use_bp, ret;
> > + CORE_ADDR start_addr, start, end;
> > + int length;
> > + struct breakpoint *b;
> > + struct symtabs_and_lines sals_start, sals_end;
> > + struct gdbarch *gdbarch = get_current_arch ();
> > + struct gdb_exception e;
> > + struct cleanup *cleanup_start, *cleanup_end, *cleanup_orig_arg;
> > +
> > + /* 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."));
> > +
> > + /* Save the original argument string for later use by
> > + print_recreate_ranged_hw_breakpoint. */
> > + orig_arg = xstrdup (arg);
> > + /* We'll only dispose of it if this function is aborted. */
> > + cleanup_orig_arg = make_cleanup (xfree, orig_arg);
> > +
> > + sals_start.sals = NULL;
> > + sals_start.nelts = 0;
> > + addr_string_start = NULL;
> > +
> > + while (*arg == ' ' || *arg == '\t')
> > + arg++;
> > +
> > + parse_breakpoint_sals (&arg, &sals_start, &addr_string_start, NULL);
> > +
> > + cleanup_start = make_cleanup (xfree, sals_start.sals);
> > + make_cleanup (xfree, addr_string_start);
> > + make_cleanup (xfree, addr_string_start[0]);
> > +
> > + 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."));
> > +
> > + breakpoint_sals_to_pc (&sals_start);
> > + start_addr = sals_start.sals[0].pc;
> > +
> > + arg++; /* Skip the comma. */
> > + while (*arg == ' ' || *arg == '\t')
> > + arg++;
> > +
> > + /* Parse the end location. */
> > +
> > + sals_end.sals = NULL;
> > + sals_end.nelts = 0;
> > +
> > + sals_end = decode_line_1 (&arg, 1, sals_start.sals[0].symtab,
> > + sals_start.sals[0].line, 0, 0);
>
> It seems oddly asymmetrical to use parse_breakpoint_sals above but
> decode_line_1 here. Shouldn't start and end of the range use the
> same symtab defaulting rules and other extra treatment done by
> parse_breakpoint_sals?

No, the end location should default to the same file and line number as
the start location. This makes it possible to have ranges like:

(gdb) break-range foo.c:27, +14

Had I used parse_breakpoint_sals, GDB would interpret "+14" as "14 lines
from the current file and line number", and not "14 lines from the start
location". As for the extra treatment, I had a look at them before and
didn't think they applied in the context of resolving the end location.

Though perhaps now that I'll need to have a b->addr_string_range_end,
the code ensuring there are addr_strings may be useful. I don't know
yet.

> > @@ -598,6 +598,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);
> > /* Do not inherit to_insert_mask_watchpoint. */
>
> Again the question why the inheritance logic for this function
> should be different than for all the other breakpoint-related
> functions ...

This was pointed out to me by Jan when reviewing another patch [1]:

> > --- a/gdb/target.c
> > +++ b/gdb/target.c
> > @@ -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);
>
> There are now two target interface styles in use. This inheriting one and the
> runtime-inheriting one (see target_pid_to_str and others). I was told the
> target_pid_to_str style is now preferred and it makes sense to me. Please
> convert the new target vector methods to the new style.

So I just followed his advice and used the new method instead of the
deprecated one which is used by the other breakpoint-related functions.

[1] http://sourceware.org/ml/gdb-patches/2010-11/msg00193.html
--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center

2011-03-11 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.
* 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): New function.
(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.
(break_range_command): New function.
(update_breakpoint_locations): Call breakpoint_locations_match
instead of breakpoint_address_match.
(_initialize_breakpoint): Register break-range command.
* 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.

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

diff --git a/gdb/NEWS b/gdb/NEWS
index fb36383..ece514e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -147,6 +147,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),
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
index ed51ab0..93f6da9 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -10924,6 +10924,7 @@ static struct breakpoint_ops catch_exception_breakpoint_ops =
NULL, /* resources_needed */
print_it_catch_exception,
print_one_catch_exception,
+ NULL, /* print_one_detail */
print_mention_catch_exception,
print_recreate_catch_exception
};
@@ -10963,6 +10964,7 @@ static struct breakpoint_ops catch_exception_unhandled_breakpoint_ops = {
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
};
@@ -11000,6 +11002,7 @@ static struct breakpoint_ops catch_assert_breakpoint_ops = {
NULL, /* resources_needed */
print_it_catch_assert,
print_one_catch_assert,
+ NULL, /* print_one_detail */
print_mention_catch_assert,
print_recreate_catch_assert
};
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 5bcab87..edcfe13 100644
--- a/gdb/breakpoint.c
+++ b/gdb/breakpoint.c
@@ -129,6 +129,10 @@ static int breakpoint_address_match (struct address_space *aspace1,
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);
@@ -1512,6 +1516,7 @@ insert_bp_location (struct bp_location *bl,
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)
@@ -2774,11 +2779,10 @@ breakpoint_here_p (struct address_space *aspace, CORE_ADDR pc)
&& 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)
@@ -2803,8 +2807,7 @@ moribund_breakpoint_here_p (struct address_space *aspace, CORE_ADDR pc)
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;
@@ -2828,8 +2831,7 @@ regular_breakpoint_inserted_here_p (struct address_space *aspace,
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)
@@ -2946,8 +2948,7 @@ breakpoint_thread_match (struct address_space *aspace, CORE_ADDR pc,
&& 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)
@@ -3837,6 +3838,9 @@ bpstat_check_location (const struct bp_location *bl,
/* 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))
@@ -3865,7 +3869,7 @@ bpstat_check_location (const struct bp_location *bl,
if (is_hardware_watchpoint (b)
&& b->watchpoint_triggered == watch_triggered_no)
return 0;
-
+
if (b->type == bp_hardware_breakpoint)
{
if (bl->address != bp_addr)
@@ -3876,13 +3880,6 @@ bpstat_check_location (const struct bp_location *bl,
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;
}

@@ -4239,8 +4236,7 @@ bpstat_stop_status (struct address_space *aspace,

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. */
@@ -4889,9 +4885,12 @@ print_one_breakpoint_location (struct breakpoint *b,
ui_out_field_int (uiout, "task", b->task);
}
}
-
+
ui_out_text (uiout, "\n");
-
+
+ if (!part_of_multiple && b->ops && b->ops->print_one_detail)
+ b->ops->print_one_detail (b, uiout);
+
if (!part_of_multiple && b->static_trace_marker_id)
{
gdb_assert (b->type == bp_static_tracepoint);
@@ -5473,6 +5472,39 @@ breakpoint_address_match (struct address_space *aspace1, CORE_ADDR addr1,
&& 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. */
@@ -5495,8 +5527,10 @@ breakpoint_locations_match (struct bp_location *loc1,
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
@@ -6105,9 +6139,10 @@ remove_catch_fork (struct bp_location *bl)
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
@@ -6176,6 +6211,7 @@ static struct breakpoint_ops catch_fork_breakpoint_ops =
NULL, /* resources_needed */
print_it_catch_fork,
print_one_catch_fork,
+ NULL, /* print_one_detail */
print_mention_catch_fork,
print_recreate_catch_fork
};
@@ -6202,9 +6238,10 @@ remove_catch_vfork (struct bp_location *bl)
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
@@ -6272,6 +6309,7 @@ static struct breakpoint_ops catch_vfork_breakpoint_ops =
NULL, /* resources_needed */
print_it_catch_vfork,
print_one_catch_vfork,
+ NULL, /* print_one_detail */
print_mention_catch_vfork,
print_recreate_catch_vfork
};
@@ -6361,12 +6399,14 @@ remove_catch_syscall (struct bp_location *bl)
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;
@@ -6435,7 +6475,7 @@ print_it_catch_syscall (struct breakpoint *b)

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

@@ -6556,6 +6596,7 @@ static struct breakpoint_ops catch_syscall_breakpoint_ops =
NULL, /* resources_needed */
print_it_catch_syscall,
print_one_catch_syscall,
+ NULL, /* print_one_detail */
print_mention_catch_syscall,
print_recreate_catch_syscall
};
@@ -6651,9 +6692,10 @@ remove_catch_exec (struct bp_location *bl)
}

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
@@ -6710,6 +6752,7 @@ static struct breakpoint_ops catch_exec_breakpoint_ops =
NULL, /* resources_needed */
print_it_catch_exec,
print_one_catch_exec,
+ NULL, /* print_one_detail */
print_mention_catch_exec,
print_recreate_catch_exec
};
@@ -6733,13 +6776,22 @@ create_syscall_event_catchpoint (int tempflag, VEC(int) *filter,
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;
@@ -8199,6 +8251,289 @@ 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)
+{
+ int addr_bit, hex_width;
+ CORE_ADDR address_start, address_end;
+ struct bp_location *bl = b->loc;
+
+ gdb_assert (bl);
+
+ addr_bit = gdbarch_addr_bit (bl->gdbarch);
+ hex_width = addr_bit / 4;
+ address_start = bl->address;
+ address_end = address_start + bl->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;
+ }
+
+ ui_out_text (uiout, "\taddress range: ");
+ ui_out_field_fmt (uiout, "addr", "[%s, %s]",
+ hex_string_custom (address_start, hex_width),
+ hex_string_custom (address_end, hex_width));
+ ui_out_text (uiout, "\n");
+}
+
+/* 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", b->exp_string);
+}
+
+/* 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
+};
+
+/* Implement the "break-range" CLI command. */
+
+static void
+break_range_command (char *arg, int from_tty)
+{
+ char *orig_arg;
+ char **addr_string_start;
+ int bp_count, can_use_bp, ret;
+ CORE_ADDR start_addr, start, end;
+ int length;
+ struct breakpoint *b;
+ struct symtabs_and_lines sals_start, sals_end;
+ struct gdbarch *gdbarch = get_current_arch ();
+ struct gdb_exception e;
+ struct cleanup *cleanup_start, *cleanup_end, *cleanup_orig_arg;
+
+ /* 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."));
+
+ /* Save the original argument string for later use by
+ print_recreate_ranged_hw_breakpoint. */
+ orig_arg = xstrdup (arg);
+ /* We'll only dispose of it if this function is aborted. */
+ cleanup_orig_arg = make_cleanup (xfree, orig_arg);
+
+ sals_start.sals = NULL;
+ sals_start.nelts = 0;
+ addr_string_start = NULL;
+
+ while (*arg == ' ' || *arg == '\t')
+ arg++;
+
+ parse_breakpoint_sals (&arg, &sals_start, &addr_string_start, NULL);
+
+ cleanup_start = make_cleanup (xfree, sals_start.sals);
+ make_cleanup (xfree, addr_string_start);
+ make_cleanup (xfree, addr_string_start[0]);
+
+ 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."));
+
+ breakpoint_sals_to_pc (&sals_start);
+ start_addr = sals_start.sals[0].pc;
+
+ arg++; /* Skip the comma. */
+ while (*arg == ' ' || *arg == '\t')
+ arg++;
+
+ /* Parse the end location. */
+
+ sals_end.sals = NULL;
+ sals_end.nelts = 0;
+
+ sals_end = decode_line_1 (&arg, 1, sals_start.sals[0].symtab,
+ sals_start.sals[0].line, NULL, NULL);
+
+ cleanup_end = make_cleanup (xfree, sals_end.sals);
+
+ 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."));
+
+ breakpoint_sals_to_pc (&sals_end);
+
+ /* If the user provided a PC value, use it. Otherwise, find the
+ address of the end of the given location. */
+ if (sals_end.sals[0].explicit_pc)
+ end = sals_end.sals[0].pc;
+ else
+ {
+ ret = find_line_pc_range (sals_end.sals[0], &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--;
+ }
+
+ if (start_addr > end)
+ error (_("Invalid address range, end preceeds start."));
+
+ length = end - start_addr + 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[0], 1);
+
+ do_cleanups (cleanup_orig_arg);
+
+ return;
+ }
+
+ do_cleanups (cleanup_end);
+
+ /* Now set up the breakpoint. */
+ b = set_raw_breakpoint (gdbarch, sals_start.sals[0], 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 = orig_arg;
+ b->val = NULL;
+ b->val_valid = 0;
+ b->ops = &ranged_breakpoint_ops;
+ b->loc->length = length;
+
+ do_cleanups (cleanup_start);
+ discard_cleanups (cleanup_orig_arg);
+
+ 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
@@ -8351,6 +8686,7 @@ static struct breakpoint_ops watchpoint_breakpoint_ops =
resources_needed_watchpoint,
NULL, /* print_it */
NULL, /* print_one */
+ NULL, /* print_one_detail */
NULL, /* print_mention */
NULL /* print_recreate */
};
@@ -9130,6 +9466,7 @@ static struct breakpoint_ops gnu_v3_exception_catchpoint_ops = {
NULL, /* resources_needed */
print_exception_catchpoint,
print_one_exception_catchpoint,
+ NULL, /* print_one_detail */
print_mention_exception_catchpoint,
print_recreate_exception_catchpoint
};
@@ -10441,8 +10778,7 @@ update_breakpoint_locations (struct breakpoint *b,
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;
@@ -12564,7 +12900,23 @@ inferior in all-stop mode, gdb behaves as if always-inserted mode is off."),
&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);
diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
index a0fea21..fd7ea62 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -217,6 +217,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
@@ -325,7 +329,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. */
@@ -383,7 +388,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
@@ -398,6 +404,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 *);
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a5eaa72..0f93850 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18727,9 +18727,27 @@ region using one of the following commands (@pxref{Expressions}):
(@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
diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c
index 049cde8..f0c7f61 100644
--- a/gdb/ppc-linux-nat.c
+++ b/gdb/ppc-linux-nat.c
@@ -1637,6 +1637,19 @@ booke_remove_point (struct ppc_hw_breakpoint *b, int tid)
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 gdbarch *gdbarch,

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 gdbarch *gdbarch,

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;
diff --git a/gdb/target.c b/gdb/target.c
index aa59920..39702eb 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -604,6 +604,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);
@@ -3505,6 +3506,21 @@ target_verify_memory (const gdb_byte *data, CORE_ADDR memaddr, ULONGEST size)
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)
{
diff --git a/gdb/target.h b/gdb/target.h
index e19f7b7..7e12db6 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -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 *);

@@ -1366,6 +1367,11 @@ extern char *target_thread_name (struct thread_info *);
#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. */