From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 17837 invoked by alias); 28 Jan 2011 01:27:05 -0000 Received: (qmail 17799 invoked by uid 22791); 28 Jan 2011 01:26:58 -0000 X-SWARE-Spam-Status: No, hits=-0.5 required=5.0 tests=AWL,BAYES_20,TW_CP,TW_EG,TW_XF,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from e24smtp03.br.ibm.com (HELO e24smtp03.br.ibm.com) (32.104.18.24) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 28 Jan 2011 01:26:36 +0000 Received: from /spool/local by e24smtp03.br.ibm.com with XMail ESMTP for from ; Thu, 27 Jan 2011 23:26:31 -0200 Received: from mailhub1.br.ibm.com ([9.18.232.109]) by e24smtp03.br.ibm.com ([10.172.0.139]) with XMail ESMTP; Thu, 27 Jan 2011 23:26:29 -0200 Received: from d24av03.br.ibm.com (d24av03.br.ibm.com [9.8.31.95]) by mailhub1.br.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p0S1YLtM1384540 for ; Thu, 27 Jan 2011 23:34:21 -0200 Received: from d24av03.br.ibm.com (loopback [127.0.0.1]) by d24av03.br.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p0S1QS9O032559 for ; Thu, 27 Jan 2011 23:26:28 -0200 Received: from [9.78.137.161] ([9.78.137.161]) by d24av03.br.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p0S1QRTf032532 for ; Thu, 27 Jan 2011 23:26:27 -0200 Subject: [RFA] Implement support for PowerPC BookE ranged breakpoints From: Thiago Jung Bauermann To: gdb-patches ml Content-Type: text/plain; charset="UTF-8" Date: Fri, 28 Jan 2011 01:54:00 -0000 Message-ID: <1296177985.2843.82.camel@hactar> Mime-Version: 1.0 Content-Transfer-Encoding: 7bit x-cbid: 11012801-9254-0000-0000-000000184665 X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2011-01/txt/msg00524.txt.bz2 Hi all, This is (finally!) the last patch in my series to support BookE hardware debug features. It adds the following command: (gdb) help break-range Set a breakpoint for an address range. break-range START-LOCATION, END-LOCATION where START-LOCATION and END-LOCATION can be one of the following: LINENUM, for that line in the current file, FILE:LINENUM, for that line in that file, +OFFSET, for that number of lines after the current line or the start of the range FUNCTION, for the first line in that function, FILE:FUNCTION, to distinguish among like-named static functions. *ADDRESS, for the instruction at that address. The breakpoint will stop execution of the inferior whenever it executes any address within the [start-address, end-address] range (including START-LOCATION and END-LOCATION). This patch needs a doc review. There are no regressions on ppc-linux, ppc64-linux and i386-linux. Ok? -- []'s Thiago Jung Bauermann IBM Linux Technology Center 2011-01-27 Thiago Jung Bauermann Sergio Durigan Junior Implement support for PowerPC BookE ranged breakpoints. gdb/ * NEWS: Mention support for ranged breakpoints on embedded PowerPC. * breakpoint.h (struct ui_stream): Add opaque declaration. (struct bp_target_info) : New member variable. (struct breakpoint_ops) : Take struct bp_location instead of struct breakpoint as argument, and also add ASPACE and BP_ADDR arguments. Update all callers. (struct breakpoint_ops) : Take struct bp_location instead of struct breakpoint as argument. Update all callers. (struct breakpoint_ops) : Add WRAP_INDENT and STB arguments. Update all callers. (struct breakpoint_ops) : Add SAY_WHERE argument. Update all callers. * breakpoint.c (breakpoint_address_match_range): Add function prototype. (is_ranged_breakpoint): Likewise. (insert_bp_location): Set bl->target_info.length. (breakpoint_here_p): Check for address match for in ranged breakpoints. (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. (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. (is_ranged_breakpoint): New function. (break_range_command): Likewise. (_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) : 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 d5d15ec..0a5d82d 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -131,6 +131,12 @@ by the inferior against the watchpoint address. See the "PowerPC Embedded" section in the user manual for more details. +* When locally 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 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 2fda77f..7eedc0a 100644 --- a/gdb/ada-lang.c +++ b/gdb/ada-lang.c @@ -10875,20 +10875,23 @@ print_recreate_exception (enum exception_catchpoint_kind ex, /* Virtual table for "catch exception" breakpoints. */ static enum print_stop_action -print_it_catch_exception (struct breakpoint *b, const struct value *old_val) +print_it_catch_exception (const struct bp_location *bl, + const struct value *old_val) { - return print_it_exception (ex_catch_exception, b); + return print_it_exception (ex_catch_exception, bl->owner); } 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, struct ui_stream *stb) { print_one_exception (ex_catch_exception, b, last_loc); } static void -print_mention_catch_exception (struct breakpoint *b) +print_mention_catch_exception (struct breakpoint *b, int *say_where) { + *say_where = 0; print_mention_exception (ex_catch_exception, b); } @@ -10915,22 +10918,24 @@ static struct breakpoint_ops catch_exception_breakpoint_ops = /* Virtual table for "catch exception unhandled" breakpoints. */ static enum print_stop_action -print_it_catch_exception_unhandled (struct breakpoint *b, +print_it_catch_exception_unhandled (const struct bp_location *bl, const struct value *old_val) { - return print_it_exception (ex_catch_exception_unhandled, b); + return print_it_exception (ex_catch_exception_unhandled, bl->owner); } static void print_one_catch_exception_unhandled (struct breakpoint *b, - struct bp_location **last_loc) + struct bp_location **last_loc, + char *wrap_indent, struct ui_stream *stb) { print_one_exception (ex_catch_exception_unhandled, b, last_loc); } static void -print_mention_catch_exception_unhandled (struct breakpoint *b) +print_mention_catch_exception_unhandled (struct breakpoint *b, int *say_where) { + *say_where = 0; print_mention_exception (ex_catch_exception_unhandled, b); } @@ -10957,20 +10962,23 @@ static struct breakpoint_ops catch_exception_unhandled_breakpoint_ops = { /* Virtual table for "catch assert" breakpoints. */ static enum print_stop_action -print_it_catch_assert (struct breakpoint *b, const struct value *old_val) +print_it_catch_assert (const struct bp_location *bl, + const struct value *old_val) { - return print_it_exception (ex_catch_assert, b); + return print_it_exception (ex_catch_assert, bl->owner); } 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, struct ui_stream *stb) { print_one_exception (ex_catch_assert, b, last_loc); } static void -print_mention_catch_assert (struct breakpoint *b) +print_mention_catch_assert (struct breakpoint *b, int *say_where) { + *say_where = 0; print_mention_exception (ex_catch_assert, b); } diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 7eef1b4..b939b6f 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -128,6 +128,11 @@ 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_address_match_range (struct address_space *aspace1, + CORE_ADDR addr1, CORE_ADDR len1, + struct address_space *aspace2, + CORE_ADDR addr2); + static void breakpoints_info (char *, int); static void watchpoints_info (char *, int); @@ -222,6 +227,8 @@ static void trace_pass_command (char *, int); static int is_masked_watchpoint (const struct breakpoint *b); +static int is_ranged_breakpoint (const struct breakpoint *b); + /* Assuming we're creating a static tracepoint, does S look like a static tracepoint marker spec ("-m MARKER_ID")? */ #define is_marker_spec(s) \ @@ -1644,6 +1651,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) @@ -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)))) { if (overlay_debugging && section_is_overlay (bl->section) @@ -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? */ - 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); else return print_it_typical (bs); } @@ -3855,6 +3866,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)) @@ -3883,7 +3897,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) @@ -3894,13 +3908,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; } @@ -4775,7 +4782,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, stb); } else switch (b->type) @@ -5467,6 +5474,21 @@ 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, + CORE_ADDR len1, struct address_space *aspace2, + CORE_ADDR addr2) +{ + return ((gdbarch_has_global_breakpoints (target_gdbarch) + || aspace1 == aspace2) + && addr2 >= addr1 && addr2 < addr1 + len1); +} + /* Assuming LOC1 and LOC2's types' have meaningful target addresses (breakpoint_address_is_meaningful), returns true if LOC1 and LOC2 represent the same location. */ @@ -6084,17 +6106,20 @@ 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 catchpoints. */ static enum print_stop_action -print_it_catch_fork (struct breakpoint *b, const struct value *old_val) +print_it_catch_fork (const struct bp_location *bl, const struct value *old_val) { + const struct breakpoint *b = bl->owner; + annotate_catchpoint (b->number); printf_filtered (_("\nCatchpoint %d (forked process %d), "), b->number, ptid_get_pid (b->forked_inferior_pid)); @@ -6105,7 +6130,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 ui_stream *stb) { struct value_print_options opts; @@ -6131,8 +6157,9 @@ print_one_catch_fork (struct breakpoint *b, struct bp_location **last_loc) catchpoints. */ static void -print_mention_catch_fork (struct breakpoint *b) +print_mention_catch_fork (struct breakpoint *b, int *say_where) { + *say_where = 0; printf_filtered (_("Catchpoint %d (fork)"), b->number); } @@ -6183,17 +6210,20 @@ 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 catchpoints. */ static enum print_stop_action -print_it_catch_vfork (struct breakpoint *b, const struct value *old_val) +print_it_catch_vfork (const struct bp_location *bl, const struct value *old_val) { + const struct breakpoint *b = bl->owner; + annotate_catchpoint (b->number); printf_filtered (_("\nCatchpoint %d (vforked process %d), "), b->number, ptid_get_pid (b->forked_inferior_pid)); @@ -6204,7 +6234,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 ui_stream *stb) { struct value_print_options opts; @@ -6229,8 +6260,9 @@ print_one_catch_vfork (struct breakpoint *b, struct bp_location **last_loc) catchpoints. */ static void -print_mention_catch_vfork (struct breakpoint *b) +print_mention_catch_vfork (struct breakpoint *b, int *say_where) { + *say_where = 0; printf_filtered (_("Catchpoint %d (vfork)"), b->number); } @@ -6344,12 +6376,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; @@ -6376,7 +6410,8 @@ breakpoint_hit_catch_syscall (struct breakpoint *b) catchpoints. */ static enum print_stop_action -print_it_catch_syscall (struct breakpoint *b, const struct value *old_val) +print_it_catch_syscall (const struct bp_location *bl, + const struct value *old_val) { /* These are needed because we want to know in which state a syscall is. It can be in the TARGET_WAITKIND_SYSCALL_ENTRY @@ -6387,6 +6422,7 @@ print_it_catch_syscall (struct breakpoint *b, const struct value *old_val) struct syscall s; struct cleanup *old_chain; char *syscall_id; + const struct breakpoint *b = bl->owner; get_last_target_status (&ptid, &last); @@ -6417,8 +6453,8 @@ print_it_catch_syscall (struct breakpoint *b, const struct value *old_val) catchpoints. */ static void -print_one_catch_syscall (struct breakpoint *b, - struct bp_location **last_loc) +print_one_catch_syscall (struct breakpoint *b, struct bp_location **last_loc, + char *wrap_indent, struct ui_stream *stb) { struct value_print_options opts; @@ -6472,8 +6508,10 @@ print_one_catch_syscall (struct breakpoint *b, catchpoints. */ static void -print_mention_catch_syscall (struct breakpoint *b) +print_mention_catch_syscall (struct breakpoint *b, int *say_where) { + *say_where = 0; + if (b->syscalls_to_be_caught) { int i, iter; @@ -6636,14 +6674,17 @@ 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 -print_it_catch_exec (struct breakpoint *b, const struct value *old_val) +print_it_catch_exec (const struct bp_location *bl, const struct value *old_val) { + const struct breakpoint *b = bl->owner; + annotate_catchpoint (b->number); printf_filtered (_("\nCatchpoint %d (exec'd %s), "), b->number, b->exec_pathname); @@ -6651,7 +6692,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 ui_stream *stb) { struct value_print_options opts; @@ -6673,8 +6715,9 @@ print_one_catch_exec (struct breakpoint *b, struct bp_location **last_loc) } static void -print_mention_catch_exec (struct breakpoint *b) +print_mention_catch_exec (struct breakpoint *b, int *say_where) { + *say_where = 0; printf_filtered (_("Catchpoint %d (exec)"), b->number); } @@ -6720,13 +6763,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; @@ -6948,7 +7000,7 @@ mention (struct breakpoint *b) observer_notify_breakpoint_created (b->number); if (b->ops != NULL && b->ops->print_mention != NULL) - b->ops->print_mention (b); + b->ops->print_mention (b, &say_where); else switch (b->type) { @@ -8192,6 +8244,329 @@ 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 (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); + annotate_breakpoint (b->number); + if (b->disposition == disp_del) + ui_out_text (uiout, "\nTemporary ranged breakpoint "); + else if (b->type == bp_hardware_breakpoint) + ui_out_text (uiout, "\nHardware assisted 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, ", "); + + 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 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; +} + +/* 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) +{ + gdb_assert (b->loc); + + ui_out_text (uiout, "\taddress range: "); + ui_out_field_range_core_addr (uiout, "addr", b->loc->gdbarch, + b->loc->address, b->loc->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, 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.")); + } +} + +/* 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 +}; + +/* Tell whether the given breakpoint is a ranged breakpoint. */ + +static int +is_ranged_breakpoint (const struct breakpoint *b) +{ + return b->ops == &ranged_breakpoint_ops; +} + +/* 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; + LONGEST 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++; + + /* 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]); + } + + 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 == 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 @@ -8392,9 +8767,10 @@ works_in_software_mode_masked_watchpoint (const struct breakpoint *b) masked hardware watchpoints. */ static enum print_stop_action -print_it_masked_watchpoint (struct breakpoint *b, +print_it_masked_watchpoint (const struct bp_location *bl, const struct value *old_val) { + struct breakpoint *b = bl->owner; struct ui_stream *stb; struct cleanup *old_chain; @@ -8466,10 +8842,12 @@ print_one_detail_masked_watchpoint (const struct breakpoint *b, masked hardware watchpoints. */ static void -print_mention_masked_watchpoint (struct breakpoint *b) +print_mention_masked_watchpoint (struct breakpoint *b, int *say_where) { struct cleanup *ui_out_chain; + *say_where = 0; + switch (b->type) { case bp_hardware_watchpoint: @@ -9292,10 +9670,11 @@ catch_exec_command_1 (char *arg, int from_tty, } static enum print_stop_action -print_it_exception_catchpoint (struct breakpoint *b, +print_it_exception_catchpoint (const struct bp_location *bl, const struct value *old_val) { int bp_temp, bp_throw; + const struct breakpoint *b = bl->owner; annotate_catchpoint (b->number); @@ -9325,7 +9704,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 ui_stream *stb) { struct value_print_options opts; @@ -9349,11 +9729,13 @@ print_one_exception_catchpoint (struct breakpoint *b, } static void -print_mention_exception_catchpoint (struct breakpoint *b) +print_mention_exception_catchpoint (struct breakpoint *b, int *say_where) { int bp_temp; int bp_throw; + *say_where = 0; + bp_temp = b->disposition == disp_del; bp_throw = strstr (b->addr_string, "throw") != NULL; ui_out_text (uiout, bp_temp ? _("Temporary catchpoint ") @@ -12830,7 +13212,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\n\ +executes any address within the [start-address, end-address] range\n\ +(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 3195124..0b19c35 100644 --- a/gdb/breakpoint.h +++ b/gdb/breakpoint.h @@ -29,6 +29,7 @@ struct value; struct block; struct breakpoint_object; struct ui_out; +struct ui_stream; /* This is the maximum number of bytes a breakpoint instruction can take. Feel free to increase it. It's just used in a few places to @@ -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; /* 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 @@ -398,12 +404,13 @@ struct breakpoint_ops /* The normal print routine for this breakpoint, called when we hit it. */ - enum print_stop_action (*print_it) (struct breakpoint *, + enum print_stop_action (*print_it) (const struct bp_location *, const struct value *old_val); /* 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 *wrap_indent, struct ui_stream *); /* Display extra information about this breakpoint, below the normal breakpoint description in "info breakpoints". @@ -420,8 +427,10 @@ struct breakpoint_ops 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 *); + (roughly speaking; this is called from "mention"). On return, + SAY_WHERE will be one if the caller is supposed to print + location information about the breakpoint, or zero otherwise. */ + void (*print_mention) (struct breakpoint *, int *say_where); /* Print to FP the CLI command that recreates this breakpoint. */ void (*print_recreate) (struct breakpoint *, struct ui_file *fp); diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 24d0441..0d0b14c 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -18724,9 +18724,25 @@ the @code{watch} command (@pxref{Set Watchpoints}), as in: (@value{GDBP}) watch *0xdeadbeef mask 0xffffff00 @end smallexample +PowerPC embedded processors support hardware accelerated ranged breakpoints. +A @dfn{ranged breakpoint} stops execution of the inferior whenever it +executes 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 (@xref{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 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 7a9b3bd..7e54c5e 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. */ @@ -3429,6 +3430,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 e996272..dec0250 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -448,6 +448,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 *); @@ -1371,6 +1372,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..97b9df0 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, + CORE_ADDR 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..098f1bb 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, + CORE_ADDR 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);