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

Thiago Jung Bauermann
2011-02-23T20:47:26Z

Hi Ulrich,

On Thu, 2011-02-17 at 16:30 +0100, Ulrich Weigand wrote:
> Thiago Jung Bauermann wrote:
> The user interface for this command seems fine to me.

Great.

> A couple of detail comments:

Thanks for your review.

> > @@ -2771,11 +2779,15 @@ 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_address_match (bl->pspace->aspace, bl->address,
> > + aspace, pc)
> > + || (is_ranged_breakpoint (bl->owner)
> > + && breakpoint_address_match_range (bl->pspace->aspace,
> > + bl->address, bl->length,
> > + aspace, pc))))
>
> It might make sense to have a helper routine that does the right
> thing for both breakpoint types by matching an aspace/pc pair
> against a breakpoint location ...

Agreed. I did that.

> More problematically, while you've changed this use of breakpoint_address_match,
> there are many more of them in breakpoint.c, and it seems at least some of
> those would also need to be updated to handle range breakpoints (what about
> the one in breakpoint_thread_match for example?).

I reviewed all the callers of breakpoint_address_match. There are some
places that need to check for ranged breakpoints and others that don't,
so I didn't change all callers to use breakpoint_location_address_match
although that would be harmless (I didn't want to touch more code than
necessary, should I change all callers for consistency?).

The ones I changed to call breakpoint_location_address_match are:

breakpoint_here_p, moribund_breakpoint_here_p,
regular_breakpoint_inserted_here_p, breakpoint_thread_match and
bpstat_stop_status.

Additionally, I changed breakpoint_locations_match to compare the
bp_location.length of the two locations as well, and changed
update_breakpoint_locations to call breakpoint_locations_match instead
of calling breakpoint_address_match directly.

The callers of breakpoint_address_match that I didn't change are:

breakpoint_restore_shadows (only software breakpoints have shadow
contents), bpstat_check_location (it calls ops->breakpoint_hit if
available, which will do the right thing for the bp_location at hand),
single_step_breakpoint_inserted_here_p (it calls
breakpoint_address_match for something which isn't a bp_location, also a
comment there says it checks only software breakpoints).

> > @@ -3306,11 +3318,6 @@ print_it_typical (bpstat bs)
> > int bp_temp = 0;
> > enum print_stop_action result;
> >
> > - /* bs->breakpoint_at can be NULL if it was a momentary breakpoint
> > - which has since been deleted. */
> > - if (bs->breakpoint_at == NULL)
> > - return PRINT_UNKNOWN;
> > -
> > gdb_assert (bs->bp_location_at != NULL);
> >
> > bl = bs->bp_location_at;
> > @@ -3516,11 +3523,15 @@ print_bp_stop_message (bpstat bs)
> > {
> > struct breakpoint *b = bs->breakpoint_at;
> >
> > + /* bs->breakpoint_at can be NULL if it was a momentary breakpoint
> > + which has since been deleted. */
> > + if (b == NULL)
> > + return PRINT_UNKNOWN;
> > +
> > /* Normal case. Call the breakpoint's print_it method, or
> > print_it_typical. */
> > - /* FIXME: how breakpoint can ever be NULL here? */
>
> This seems to be an unrelated change, isn't it?

I consider it to be related. In this patch series I'm revamping the
breakpoint_ops methods to make them more suitable for implementing
breakpoints and watchpoints. print_it_typical is part of that, so an
improvement in a function which calls the print_it method and
print_it_typcal seemed to have a place in this patch.

I don't mind submitting this in a separate patch if you want.

> > - if (b != NULL && b->ops != NULL && b->ops->print_it != NULL)
> > - return b->ops->print_it (b, bs->old_val);
> > + if (b->ops != NULL && b->ops->print_it != NULL)
> > + return b->ops->print_it (bs->bp_location_at, bs->old_val);
>
> I'm not sure this API change is necessary. Yes, you need to get at
> the location to print range breakpoints, but those will always have
> exactly one location ... The print_it routine could instead just
> make the same assumption e.g. print_exception_catchpoint already
> does, and just access b->loc.

I was aiming to change breakpoint_ops in a way that makes it possible to
implement different kinds of breakpoints and watchpoints, so I didn't
want to make assumptions specific to ranged breakpoints. But I adopted
your suggestion in this version.

> > +/* Implement the "print_it" breakpoint_ops method for
> > + ranged breakpoints. */
> > +
> > +static enum print_stop_action
> > +print_it_ranged_breakpoint (const struct bp_location *bl,
> > + const struct value *old_val)
> > +{
> > + const struct breakpoint *b = bl->owner;
> > + struct ui_stream *stb;
> > + struct cleanup *old_chain;
> > +
> > + gdb_assert (b->type == bp_breakpoint || b->type == bp_hardware_breakpoint);
> > +
> > + stb = ui_out_stream_new (uiout);
> > + old_chain = make_cleanup_ui_out_stream_delete (stb);
> > +
> > + if (bl->address != bl->requested_address)
> > + breakpoint_adjustment_warning (bl->requested_address,
> > + bl->address,
> > + b->number, 1);
>
> Can this happen? Does it make sense for a range breakpoint to
> have its address adjusted? If so, does that affect the end as well?

I guess not. I carried this code from the earliest versions of this
patch series and didn't notice it was dead code. I removed the check
above, and I also simplified print_mention_ranged_breakpoint and
print_one_ranged_breakpoint.

> > +/* Implement the "print_one" breakpoint_ops method for
> > + ranged breakpoints. */
> > +
> > +static void
> > +print_one_ranged_breakpoint (struct breakpoint *b,
> > + struct bp_location **last_loc,
> > + char *wrap_indent, struct ui_stream *stb)
> > +{
> > + struct value_print_options opts;
> > +
> > + /* We're prepared to deal with only one location. */
> > + gdb_assert (b->loc && !b->loc->next);
> > +
> > + get_user_print_options (&opts);
> > +
> > + if (opts.addressprint)
> > + ui_out_field_skip (uiout, "addr");
> > + annotate_field (5);
> > + if (b->loc->enabled)
> > + print_breakpoint_location (b, b->loc, wrap_indent, stb);
> > + if (b->loc)
> > + *last_loc = b->loc;
> > +}
>
> I don't like the API change just to pass through those variables,
> which don't hold any state information and could just as well be
> recreated by the print_breakpoint_location routine itself ...

For stb it seems to be true (I'm not very familiar with GDB's output
mechanisms). Unfortunately wrap_indent can't be recreated.
print_one_breakpoint_location sets it based on its print_address_bits
argument. breakpoint_1 calculates print_address_bits taking into account
all breakpoints which pass the filter it is given, or just one
breakpoint. print_one_ranged_breakpoint and print_breakpoint_location
don't have enough information to make the same calculation that
breakpoint_1 did.

This version of the patch drops the stb argument from the print_one
method, but keeps wrap_indent (I could have exchanged wrap_indent for
print_address_bits too). What do you think?

> > +/* Implement the "print_mention" breakpoint_ops method for
> > + ranged breakpoints. */
> > +
> > +static void
> > +print_mention_ranged_breakpoint (struct breakpoint *b, int *say_where)
> > +{
> > + switch (b->type)
> > + {
> > + case bp_breakpoint:
> > + if (ui_out_is_mi_like_p (uiout))
> > + {
> > + *say_where = 0;
> > + break;
> > + }
> > + if (b->disposition == disp_del)
> > + printf_filtered (_("Temporary ranged breakpoint"));
> > + else
> > + printf_filtered (_("Ranged breakpoint"));
> > + printf_filtered (_(" %d"), b->number);
> > + *say_where = 1;
> > + break;
> > + case bp_hardware_breakpoint:
> > + if (ui_out_is_mi_like_p (uiout))
> > + {
> > + *say_where = 0;
> > + break;
> > + }
> > +
> > + printf_filtered (_("Hardware assisted ranged breakpoint %d"), b->number);
> > + *say_where = 1;
> > + break;
> > + default:
> > + internal_error (__FILE__, __LINE__, _("Invalid breakpoint type."));
> > + }
> > +}
>
> 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?

> > + /* We need to use parse_to_comma_and_eval but decode_line_1 uses
> > + parse_and_eval_address_1 (see decode_indirect), so we just call it
> > + directly if the user provided an explicit PC. */
> > + if (arg[0] == '*')
> > + {
> > + arg++;
> > + start_addr = value_as_address (parse_to_comma_and_eval (&arg));
> > +
> > + sals_start.sals = (struct symtab_and_line *)
> > + xmalloc (sizeof (struct symtab_and_line));
> > + sals_start.nelts = 1;
> > + sals_start.sals[0] = find_pc_line (start_addr, 0);
> > + sals_start.sals[0].pc = start_addr;
> > + sals_start.sals[0].section = find_pc_overlay (start_addr);
> > + sals_start.sals[0].explicit_pc = 1;
> > +
> > + cleanup_start = make_cleanup (xfree, sals_start.sals);
> > + }
> > + else
> > + {
> > +
> > + 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]);
> > + }
>
> Hmm. This should be fixed in decode_line_1 itself. The routine currently
> only says that it allows a trailing "if ...", but it should probably be
> modified to also stop parsing at the comma ...

Ok. decode_line_1 is pretty important in the breakpoint subsystem so I
was afraid to touch it. I'll submit a separate patch with the fix then.
I changed this patch to use just parse_breakpoint_sals.

> > diff --git a/gdb/breakpoint.h b/gdb/breakpoint.h
> > index 3195124..0b19c35 100644
> > --- a/gdb/breakpoint.h
> > +++ b/gdb/breakpoint.h
> > @@ -218,6 +219,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. */
> > + ULONGEST 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,8 +330,8 @@ struct bp_location
> > bp_loc_other. */
> > CORE_ADDR address;
> >
> > - /* For hardware watchpoints, the size of data ad ADDRESS being
> > - watches. */
> > + /* For hardware watchpoints, the size of the memory region being watched.
> > + For hardware ranged breakpoints, the size of the breakpoint range. */
> > int length;
>
> Why is it ULONGEST above, and int here?

I thought ULONGEST made more sense for a length field, but didn't feel
confident enough to change bp_location.length accordingly because I
couldn't audit all uses of a field with such a common name ("length") to
make sure the change in signedness wouldn't have side-effects. So I
compromised. :-)

I changed bp_target_info.length to int in this version.

Here's the new patch. No regressions on ppc-linux and ppc64-linux.

The documentation bits contain the changes from Eli's review and are
already approved.

--
[]'s
Thiago Jung Bauermann
IBM Linux Technology Center

2011-02-23 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>: Add WRAP_INDENT argument.
Update all callers.
* 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.
(print_it_typical): Move NULL check from here...
(print_bp_stop_message): ... to here.
(bpstat_check_location): Move call to
breakpoint_ops.breakpoint_hit to the top.
(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.
* ui-out.c (ui_out_field_range_core_addr): New function.
(ui_out_field_range_core_addr): Add function prototype.

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

diff --git a/gdb/NEWS b/gdb/NEWS
index 284071d..df518d8 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -153,6 +153,12 @@
by the inferior against the watchpoint address. See the "PowerPC Embedded"
section in the user manual for more details.

+* 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 7bb50a1..daae934 100644
--- a/gdb/ada-lang.c
+++ b/gdb/ada-lang.c
@@ -10898,7 +10898,8 @@ print_it_catch_exception (struct breakpoint *b, const struct value *old_val)
}

static void
-print_one_catch_exception (struct breakpoint *b, struct bp_location **last_loc)
+print_one_catch_exception (struct breakpoint *b, struct bp_location **last_loc,
+ char *wrap_indent)
{
print_one_exception (ex_catch_exception, b, last_loc);
}
@@ -10940,7 +10941,8 @@ print_it_catch_exception_unhandled (struct breakpoint *b,

static void
print_one_catch_exception_unhandled (struct breakpoint *b,
- struct bp_location **last_loc)
+ struct bp_location **last_loc,
+ char *wrap_indent)
{
print_one_exception (ex_catch_exception_unhandled, b, last_loc);
}
@@ -10980,7 +10982,8 @@ print_it_catch_assert (struct breakpoint *b, const struct value *old_val)
}

static void
-print_one_catch_assert (struct breakpoint *b, struct bp_location **last_loc)
+print_one_catch_assert (struct breakpoint *b, struct bp_location **last_loc,
+ char *wrap_indent)
{
print_one_exception (ex_catch_assert, b, last_loc);
}
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
index 4c2f4a1..f4c91b5 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);
@@ -1531,6 +1535,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)
@@ -2792,11 +2797,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)
@@ -2821,8 +2825,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;
@@ -2846,8 +2849,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)
@@ -2964,8 +2966,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)
@@ -3327,11 +3328,6 @@ print_it_typical (bpstat bs)
int bp_temp = 0;
enum print_stop_action result;

- /* bs->breakpoint_at can be NULL if it was a momentary breakpoint
- which has since been deleted. */
- if (bs->breakpoint_at == NULL)
- return PRINT_UNKNOWN;
-
gdb_assert (bs->bp_location_at != NULL);

bl = bs->bp_location_at;
@@ -3537,10 +3533,14 @@ print_bp_stop_message (bpstat bs)
{
struct breakpoint *b = bs->breakpoint_at;

+ /* bs->breakpoint_at can be NULL if it was a momentary breakpoint
+ which has since been deleted. */
+ if (b == NULL)
+ return PRINT_UNKNOWN;
+
/* Normal case. Call the breakpoint's print_it method, or
print_it_typical. */
- /* FIXME: how breakpoint can ever be NULL here? */
- if (b != NULL && b->ops != NULL && b->ops->print_it != NULL)
+ if (b->ops != NULL && b->ops->print_it != NULL)
return b->ops->print_it (b, bs->old_val);
else
return print_it_typical (bs);
@@ -3876,6 +3876,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))
@@ -3904,7 +3907,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)
@@ -3915,13 +3918,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;
}

@@ -4278,8 +4274,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. */
@@ -4796,7 +4791,7 @@ print_one_breakpoint_location (struct breakpoint *b,
calling it here is not likely to get any nice result. So,
make sure there's just one location. */
gdb_assert (b->loc == NULL || b->loc->next == NULL);
- b->ops->print_one (b, last_loc);
+ b->ops->print_one (b, last_loc, wrap_indent);
}
else
switch (b->type)
@@ -5495,6 +5490,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. */
@@ -5517,8 +5545,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
@@ -6125,9 +6155,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
@@ -6146,7 +6177,8 @@ print_it_catch_fork (struct breakpoint *b, const struct value *old_val)
catchpoints. */

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

@@ -6224,9 +6256,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
@@ -6245,7 +6278,8 @@ print_it_catch_vfork (struct breakpoint *b, const struct value *old_val)
catchpoints. */

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

@@ -6385,12 +6419,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;
@@ -6459,7 +6495,7 @@ print_it_catch_syscall (struct breakpoint *b, const struct value *old_val)

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

@@ -6677,9 +6713,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
@@ -6692,7 +6729,8 @@ print_it_catch_exec (struct breakpoint *b, const struct value *old_val)
}

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

@@ -6761,13 +6799,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;
@@ -8226,6 +8273,288 @@ 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,
+ const struct value *old_val)
+{
+ 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);
+
+ 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 ");
+ 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, ", ");
+
+ do_cleanups (old_chain);
+
+ 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,
+ 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);
+
+ /* 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);
+ *last_loc = bl;
+
+ do_cleanups (old_chain);
+}
+
+/* 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)
+{
+ 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);
+ 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,
+ NULL, /* works_in_software_mode */
+ 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, 0, 0);
+
+ 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
@@ -9348,7 +9677,8 @@ print_it_exception_catchpoint (struct breakpoint *b,

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

@@ -10722,8 +11052,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;
@@ -12845,7 +13174,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 ac9872d..a37ded7 100644
--- a/gdb/breakpoint.h
+++ b/gdb/breakpoint.h
@@ -218,6 +218,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
@@ -326,7 +330,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. */
@@ -384,7 +389,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
@@ -403,7 +409,7 @@ struct breakpoint_ops

/* Display information about this breakpoint, for "info
breakpoints". */
- void (*print_one) (struct breakpoint *, struct bp_location **);
+ void (*print_one) (struct breakpoint *, struct bp_location **, char *);

/* Display extra information about this breakpoint, below the normal
breakpoint description in "info breakpoints".
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 71371e4..77b9ebd 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -18755,9 +18755,27 @@ the @code{watch} command (@pxref{Set Watchpoints}), as in:
(@value{GDBP}) watch *0xdeadbeef mask 0xffffff00
@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 140ad97..8b5842b 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));

@@ -2468,6 +2505,7 @@ _initialize_ppc_linux_nat (void)
t->to_can_accel_watchpoint_condition
= ppc_linux_can_accel_watchpoint_condition;
t->to_masked_watch_num_registers = ppc_linux_masked_watch_num_registers;
+ 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 5afee3c..6686a73 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -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. */
@@ -3565,6 +3566,21 @@ target_masked_watch_num_registers (void)
return return_minus_one ();
}

+/* 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 return_minus_one ();
+}
+
static void
debug_to_prepare_to_store (struct regcache *regcache)
{
diff --git a/gdb/target.h b/gdb/target.h
index 44c986e..cf6f2e7 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 *);

@@ -1379,6 +1380,11 @@ extern int target_remove_mask_watchpoint (CORE_ADDR, CORE_ADDR, int);
#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. */
diff --git a/gdb/ui-out.c b/gdb/ui-out.c
index 53ad963..204a528 100644
--- a/gdb/ui-out.c
+++ b/gdb/ui-out.c
@@ -486,6 +486,50 @@ ui_out_field_fmt_int (struct ui_out *uiout,
uo_field_int (uiout, fldno, input_width, input_align, fldname, value);
}

+/* Documented in ui-out.h. */
+
+void
+ui_out_field_range_core_addr (struct ui_out *uiout,
+ const char *fldname,
+ struct gdbarch *gdbarch,
+ CORE_ADDR address_start,
+ int length)
+{
+ char addstr[80];
+ int addr_bit = gdbarch_addr_bit (gdbarch);
+ CORE_ADDR address_end = address_start + length - 1;
+
+ if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT))
+ {
+ address_start &= ((CORE_ADDR) 1 << addr_bit) - 1;
+ address_end &= ((CORE_ADDR) 1 << addr_bit) - 1;
+ }
+
+ /* FIXME: cagney/2002-05-03: Need local_address_string() function
+ that returns the language localized string formatted to a width
+ based on gdbarch_addr_bit. */
+ if (addr_bit <= 32)
+ {
+ strcpy (addstr, "[");
+ strcat (addstr, hex_string_custom (address_start, 8));
+ strcat (addstr, ", ");
+ strcat (addstr, hex_string_custom (address_end, 8));
+ strcat (addstr, "]");
+ }
+ else
+ {
+ strcpy (addstr, "[");
+ strcat (addstr, hex_string_custom (address_start, 16));
+ strcat (addstr, ", ");
+ strcat (addstr, hex_string_custom (address_end, 16));
+ strcat (addstr, "]");
+ }
+
+ ui_out_field_string (uiout, fldname, addstr);
+}
+
+/* Documented in ui-out.h. */
+
void
ui_out_field_core_addr (struct ui_out *uiout,
const char *fldname,
diff --git a/gdb/ui-out.h b/gdb/ui-out.h
index 4ad0651..b540e29 100644
--- a/gdb/ui-out.h
+++ b/gdb/ui-out.h
@@ -113,6 +113,16 @@ extern void ui_out_field_fmt_int (struct ui_out *uiout, int width,
enum ui_align align, const char *fldname,
int value);

+/* Output a field containing a range of addresses. */
+
+extern void ui_out_field_range_core_addr (struct ui_out *uiout,
+ const char *fldname,
+ struct gdbarch *gdbarch,
+ CORE_ADDR address_start,
+ int length);
+
+/* Output a field containing an addresses. */
+
extern void ui_out_field_core_addr (struct ui_out *uiout, const char *fldname,
struct gdbarch *gdbarch, CORE_ADDR address);