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);
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)
&& 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)
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;
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)
&& 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)
/* 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))
if (is_hardware_watchpoint (b)
&& b->watchpoint_triggered == watch_triggered_no)
return 0;
-
+
if (b->type == bp_hardware_breakpoint)
{
if (bl->address != bp_addr)
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;
}
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. */
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);
&& 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. */
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
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
NULL, /* resources_needed */
print_it_catch_fork,
print_one_catch_fork,
+ NULL, /* print_one_detail */
print_mention_catch_fork,
print_recreate_catch_fork
};
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
NULL, /* resources_needed */
print_it_catch_vfork,
print_one_catch_vfork,
+ NULL, /* print_one_detail */
print_mention_catch_vfork,
print_recreate_catch_vfork
};
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;
static void
print_one_catch_syscall (struct breakpoint *b,
- struct bp_location **last_loc)
+ struct bp_location **last_loc)
{
struct value_print_options opts;
NULL, /* resources_needed */
print_it_catch_syscall,
print_one_catch_syscall,
+ NULL, /* print_one_detail */
print_mention_catch_syscall,
print_recreate_catch_syscall
};
}
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
NULL, /* resources_needed */
print_it_catch_exec,
print_one_catch_exec,
+ NULL, /* print_one_detail */
print_mention_catch_exec,
print_recreate_catch_exec
};
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;
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
resources_needed_watchpoint,
NULL, /* print_it */
NULL, /* print_one */
+ NULL, /* print_one_detail */
NULL, /* print_mention */
NULL /* print_recreate */
};
NULL, /* resources_needed */
print_exception_catchpoint,
print_one_exception_catchpoint,
+ NULL, /* print_one_detail */
print_mention_exception_catchpoint,
print_recreate_exception_catchpoint
};
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);
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.
old symtab. */
if (b->cond_string != NULL)
{
+ char *s;
struct gdb_exception e;
s = b->cond_string;
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. */
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;
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;
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);
}
&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);