From patchwork Thu Feb 26 00:17:27 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 5294 Received: (qmail 45586 invoked by alias); 26 Feb 2015 00:17:45 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 45485 invoked by uid 89); 26 Feb 2015 00:17:44 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.0 required=5.0 tests=AWL, BAYES_00, SPF_HELO_PASS, SPF_PASS, T_RP_MATCHES_RCVD autolearn=ham version=3.3.2 X-HELO: mx1.redhat.com Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES256-GCM-SHA384 encrypted) ESMTPS; Thu, 26 Feb 2015 00:17:38 +0000 Received: from int-mx13.intmail.prod.int.phx2.redhat.com (int-mx13.intmail.prod.int.phx2.redhat.com [10.5.11.26]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id t1Q0HbF4001979 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL) for ; Wed, 25 Feb 2015 19:17:37 -0500 Received: from brno.lan (ovpn01.gateway.prod.ext.ams2.redhat.com [10.39.146.11]) by int-mx13.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id t1Q0HXwF000814 for ; Wed, 25 Feb 2015 19:17:37 -0500 From: Pedro Alves To: gdb-patches@sourceware.org Subject: [PATCH 3/8] record-full/record-btrace: software/hardware breakpoint trap Date: Thu, 26 Feb 2015 00:17:27 +0000 Message-Id: <1424909852-25917-4-git-send-email-palves@redhat.com> In-Reply-To: <1424909852-25917-1-git-send-email-palves@redhat.com> References: <1424909852-25917-1-git-send-email-palves@redhat.com> This adjusts the record targets to tell the core whether a trap was caused by a breakpoint. Targets that can do this should report breakpoint traps with the PC already adjusted, so this removes the re-incrementing record-full was doing. These targets need to be adjusted before process_stratum targets beneath are, otherwise target_supports_stopped_by_sw_breakpoint, etc. would fall through to the target beneath while recording/replaying, and the core would get confused. Tested on x86-64 Fedora 20, native and gdbserver. gdb/ChangeLog: 2015-02-25 Pedro Alves * btrace.h: Include target/waitstatus.h. (struct btrace_thread_info) : New field. * record-btrace.c (record_btrace_step_thread): Use record_check_stopped_by_breakpoint instead of breakpoint_here_p. (record_btrace_decr_pc_after_break): Delete. (record_btrace_stopped_by_sw_breakpoint) (record_btrace_supports_stopped_by_sw_breakpoint) (record_btrace_stopped_by_hw_breakpoint) (record_btrace_supports_stopped_by_hw_breakpoint): New functions. (init_record_btrace_ops): Install them. * record-full.c (record_full_hw_watchpoint): Delete and replace with ... (record_full_stop_reason): ... this throughout. (record_full_exec_insn): Adjust. (record_full_wait_1): Adjust. No longer re-increment the PC. (record_full_wait_1): Adjust. Use record_check_stopped_by_breakpoint instead of breakpoint_here_p. (record_full_stopped_by_watchpoint): Adjust. (record_full_stopped_by_sw_breakpoint) (record_full_supports_stopped_by_sw_breakpoint) (record_full_supports_stopped_by_sw_breakpoint) (record_full_stopped_by_hw_breakpoint) (record_full_supports_stopped_by_hw_breakpoint): New functions. (init_record_full_ops, init_record_full_core_ops): Install them. * record.c (record_check_stopped_by_breakpoint): New function. * record.h: Include target/waitstatus.h. (record_check_stopped_by_breakpoint): New declaration. --- gdb/btrace.h | 4 ++ gdb/record-btrace.c | 69 +++++++++++++++++++++++++++++------ gdb/record-full.c | 103 ++++++++++++++++++++++++++++++++++------------------ gdb/record.c | 19 ++++++++++ gdb/record.h | 13 +++++++ 5 files changed, 161 insertions(+), 47 deletions(-) diff --git a/gdb/btrace.h b/gdb/btrace.h index 0ddd4c1..a17b3cb 100644 --- a/gdb/btrace.h +++ b/gdb/btrace.h @@ -27,6 +27,7 @@ list of sequential control-flow blocks, one such list per thread. */ #include "btrace-common.h" +#include "target/waitstatus.h" /* For enum target_stop_reason. */ struct thread_info; struct btrace_function; @@ -261,6 +262,9 @@ struct btrace_thread_info Gaps are skipped during replay, so REPLAY always points to a valid instruction. */ struct btrace_insn_iterator *replay; + + /* Why the thread stopped, if we need to track it. */ + enum target_stop_reason stop_reason; }; /* Enable branch tracing for a thread. */ diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c index 102e0eb..ef5f367 100644 --- a/gdb/record-btrace.c +++ b/gdb/record-btrace.c @@ -1919,7 +1919,8 @@ record_btrace_step_thread (struct thread_info *tp) target_pid_to_str (tp->ptid), core_addr_to_string_nz (insn->pc)); - if (breakpoint_here_p (aspace, insn->pc)) + if (record_check_stopped_by_breakpoint (aspace, insn->pc, + &btinfo->stop_reason)) return btrace_step_stopped (); } @@ -1951,7 +1952,8 @@ record_btrace_step_thread (struct thread_info *tp) target_pid_to_str (tp->ptid), core_addr_to_string_nz (insn->pc)); - if (breakpoint_here_p (aspace, insn->pc)) + if (record_check_stopped_by_breakpoint (aspace, insn->pc, + &btinfo->stop_reason)) return btrace_step_stopped (); } } @@ -2009,18 +2011,58 @@ record_btrace_can_execute_reverse (struct target_ops *self) return 1; } -/* The to_decr_pc_after_break method of target record-btrace. */ +/* The to_stopped_by_sw_breakpoint method of target record-btrace. */ -static CORE_ADDR -record_btrace_decr_pc_after_break (struct target_ops *ops, - struct gdbarch *gdbarch) +static int +record_btrace_stopped_by_sw_breakpoint (struct target_ops *ops) { - /* When replaying, we do not actually execute the breakpoint instruction - so there is no need to adjust the PC after hitting a breakpoint. */ if (record_btrace_is_replaying (ops)) - return 0; + { + struct thread_info *tp = inferior_thread (); + + return tp->btrace.stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT; + } + + return ops->beneath->to_stopped_by_sw_breakpoint (ops->beneath); +} + +/* The to_supports_stopped_by_sw_breakpoint method of target + record-btrace. */ + +static int +record_btrace_supports_stopped_by_sw_breakpoint (struct target_ops *ops) +{ + if (record_btrace_is_replaying (ops)) + return 1; + + return ops->beneath->to_supports_stopped_by_sw_breakpoint (ops->beneath); +} + +/* The to_stopped_by_sw_breakpoint method of target record-btrace. */ + +static int +record_btrace_stopped_by_hw_breakpoint (struct target_ops *ops) +{ + if (record_btrace_is_replaying (ops)) + { + struct thread_info *tp = inferior_thread (); + + return tp->btrace.stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT; + } + + return ops->beneath->to_stopped_by_hw_breakpoint (ops->beneath); +} + +/* The to_supports_stopped_by_hw_breakpoint method of target + record-btrace. */ + +static int +record_btrace_supports_stopped_by_hw_breakpoint (struct target_ops *ops) +{ + if (record_btrace_is_replaying (ops)) + return 1; - return ops->beneath->to_decr_pc_after_break (ops->beneath, gdbarch); + return ops->beneath->to_supports_stopped_by_hw_breakpoint (ops->beneath); } /* The to_update_thread_list method of target record-btrace. */ @@ -2203,7 +2245,12 @@ init_record_btrace_ops (void) ops->to_goto_record_end = record_btrace_goto_end; ops->to_goto_record = record_btrace_goto; ops->to_can_execute_reverse = record_btrace_can_execute_reverse; - ops->to_decr_pc_after_break = record_btrace_decr_pc_after_break; + ops->to_stopped_by_sw_breakpoint = record_btrace_stopped_by_sw_breakpoint; + ops->to_supports_stopped_by_sw_breakpoint + = record_btrace_supports_stopped_by_sw_breakpoint; + ops->to_stopped_by_hw_breakpoint = record_btrace_stopped_by_hw_breakpoint; + ops->to_supports_stopped_by_hw_breakpoint + = record_btrace_supports_stopped_by_hw_breakpoint; ops->to_execution_direction = record_btrace_execution_direction; ops->to_prepare_to_generate_core = record_btrace_prepare_to_generate_core; ops->to_done_generating_core = record_btrace_done_generating_core; diff --git a/gdb/record-full.c b/gdb/record-full.c index c660743..0fbb264 100644 --- a/gdb/record-full.c +++ b/gdb/record-full.c @@ -688,7 +688,8 @@ record_full_gdb_operation_disable_set (void) } /* Flag set to TRUE for target_stopped_by_watchpoint. */ -static int record_full_hw_watchpoint = 0; +static enum target_stop_reason record_full_stop_reason + = TARGET_STOPPED_BY_NO_REASON; /* Execute one instruction from the record log. Each instruction in the log will be represented by an arbitrary sequence of register @@ -766,7 +767,7 @@ record_full_exec_insn (struct regcache *regcache, if (hardware_watchpoint_inserted_in_range (get_regcache_aspace (regcache), entry->u.mem.addr, entry->u.mem.len)) - record_full_hw_watchpoint = 1; + record_full_stop_reason = TARGET_STOPPED_BY_WATCHPOINT; } } } @@ -1079,6 +1080,8 @@ record_full_wait_1 (struct target_ops *ops, record_full_get_sig = 0; signal (SIGINT, record_full_sig_handler); + record_full_stop_reason = TARGET_STOPPED_BY_NO_REASON; + if (!RECORD_FULL_IS_REPLAY && ops != &record_full_core_ops) { if (record_full_resume_step) @@ -1119,6 +1122,8 @@ record_full_wait_1 (struct target_ops *ops, { struct regcache *regcache; struct address_space *aspace; + enum target_stop_reason *stop_reason_p + = &record_full_stop_reason; /* Yes -- this is likely our single-step finishing, but check if there's any reason the core would be @@ -1133,20 +1138,11 @@ record_full_wait_1 (struct target_ops *ops, { /* Always interested in watchpoints. */ } - else if (breakpoint_inserted_here_p (aspace, tmp_pc)) + else if (record_check_stopped_by_breakpoint (aspace, tmp_pc, + stop_reason_p)) { /* There is a breakpoint here. Let the core handle it. */ - if (software_breakpoint_inserted_here_p (aspace, tmp_pc)) - { - struct gdbarch *gdbarch - = get_regcache_arch (regcache); - CORE_ADDR decr_pc_after_break - = target_decr_pc_after_break (gdbarch); - if (decr_pc_after_break) - regcache_write_pc (regcache, - tmp_pc + decr_pc_after_break); - } } else { @@ -1205,27 +1201,20 @@ record_full_wait_1 (struct target_ops *ops, = make_cleanup (record_full_wait_cleanups, 0); CORE_ADDR tmp_pc; - record_full_hw_watchpoint = 0; + record_full_stop_reason = TARGET_STOPPED_BY_NO_REASON; status->kind = TARGET_WAITKIND_STOPPED; /* Check breakpoint when forward execute. */ if (execution_direction == EXEC_FORWARD) { tmp_pc = regcache_read_pc (regcache); - if (breakpoint_inserted_here_p (aspace, tmp_pc)) + if (record_check_stopped_by_breakpoint (aspace, tmp_pc, + &record_full_stop_reason)) { - int decr_pc_after_break = target_decr_pc_after_break (gdbarch); - if (record_debug) fprintf_unfiltered (gdb_stdlog, "Process record: break at %s.\n", paddress (gdbarch, tmp_pc)); - - if (decr_pc_after_break - && !record_full_resume_step - && software_breakpoint_inserted_here_p (aspace, tmp_pc)) - regcache_write_pc (regcache, - tmp_pc + decr_pc_after_break); goto replay_out; } } @@ -1293,27 +1282,19 @@ record_full_wait_1 (struct target_ops *ops, /* check breakpoint */ tmp_pc = regcache_read_pc (regcache); - if (breakpoint_inserted_here_p (aspace, tmp_pc)) + if (record_check_stopped_by_breakpoint (aspace, tmp_pc, + &record_full_stop_reason)) { - int decr_pc_after_break - = target_decr_pc_after_break (gdbarch); - if (record_debug) fprintf_unfiltered (gdb_stdlog, "Process record: break " "at %s.\n", paddress (gdbarch, tmp_pc)); - if (decr_pc_after_break - && execution_direction == EXEC_FORWARD - && !record_full_resume_step - && software_breakpoint_inserted_here_p (aspace, - tmp_pc)) - regcache_write_pc (regcache, - tmp_pc + decr_pc_after_break); + continue_flag = 0; } - if (record_full_hw_watchpoint) + if (record_full_stop_reason == TARGET_STOPPED_BY_WATCHPOINT) { if (record_debug) fprintf_unfiltered (gdb_stdlog, @@ -1384,7 +1365,7 @@ static int record_full_stopped_by_watchpoint (struct target_ops *ops) { if (RECORD_FULL_IS_REPLAY) - return record_full_hw_watchpoint; + return record_full_stop_reason == TARGET_STOPPED_BY_WATCHPOINT; else return ops->beneath->to_stopped_by_watchpoint (ops->beneath); } @@ -1398,6 +1379,40 @@ record_full_stopped_data_address (struct target_ops *ops, CORE_ADDR *addr_p) return ops->beneath->to_stopped_data_address (ops->beneath, addr_p); } +/* The to_stopped_by_sw_breakpoint method of target record-full. */ + +static int +record_full_stopped_by_sw_breakpoint (struct target_ops *ops) +{ + return record_full_stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT; +} + +/* The to_supports_stopped_by_sw_breakpoint method of target + record-full. */ + +static int +record_full_supports_stopped_by_sw_breakpoint (struct target_ops *ops) +{ + return 1; +} + +/* The to_stopped_by_hw_breakpoint method of target record-full. */ + +static int +record_full_stopped_by_hw_breakpoint (struct target_ops *ops) +{ + return record_full_stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT; +} + +/* The to_supports_stopped_by_sw_breakpoint method of target + record-full. */ + +static int +record_full_supports_stopped_by_hw_breakpoint (struct target_ops *ops) +{ + return 1; +} + /* Record registers change (by user or by GDB) to list as an instruction. */ static void @@ -1926,6 +1941,14 @@ init_record_full_ops (void) record_full_ops.to_remove_breakpoint = record_full_remove_breakpoint; record_full_ops.to_stopped_by_watchpoint = record_full_stopped_by_watchpoint; record_full_ops.to_stopped_data_address = record_full_stopped_data_address; + record_full_ops.to_stopped_by_sw_breakpoint + = record_full_stopped_by_sw_breakpoint; + record_full_ops.to_supports_stopped_by_sw_breakpoint + = record_full_supports_stopped_by_sw_breakpoint; + record_full_ops.to_stopped_by_hw_breakpoint + = record_full_stopped_by_hw_breakpoint; + record_full_ops.to_supports_stopped_by_hw_breakpoint + = record_full_supports_stopped_by_hw_breakpoint; record_full_ops.to_can_execute_reverse = record_full_can_execute_reverse; record_full_ops.to_stratum = record_stratum; /* Add bookmark target methods. */ @@ -2164,6 +2187,14 @@ init_record_full_core_ops (void) = record_full_stopped_by_watchpoint; record_full_core_ops.to_stopped_data_address = record_full_stopped_data_address; + record_full_core_ops.to_stopped_by_sw_breakpoint + = record_full_stopped_by_sw_breakpoint; + record_full_core_ops.to_supports_stopped_by_sw_breakpoint + = record_full_supports_stopped_by_sw_breakpoint; + record_full_core_ops.to_stopped_by_hw_breakpoint + = record_full_stopped_by_hw_breakpoint; + record_full_core_ops.to_supports_stopped_by_hw_breakpoint + = record_full_supports_stopped_by_hw_breakpoint; record_full_core_ops.to_can_execute_reverse = record_full_can_execute_reverse; record_full_core_ops.to_has_execution = record_full_core_has_execution; diff --git a/gdb/record.c b/gdb/record.c index 57851ec..a64543a 100644 --- a/gdb/record.c +++ b/gdb/record.c @@ -189,6 +189,25 @@ record_kill (struct target_ops *t) target_kill (); } +/* See record.h. */ + +int +record_check_stopped_by_breakpoint (struct address_space *aspace, CORE_ADDR pc, + enum target_stop_reason *reason) +{ + if (breakpoint_inserted_here_p (aspace, pc)) + { + if (hardware_breakpoint_inserted_here_p (aspace, pc)) + *reason = TARGET_STOPPED_BY_HW_BREAKPOINT; + else + *reason = TARGET_STOPPED_BY_SW_BREAKPOINT; + return 1; + } + + *reason = TARGET_STOPPED_BY_NO_REASON; + return 0; +} + /* Implement "show record debug" command. */ static void diff --git a/gdb/record.h b/gdb/record.h index 771b14d..101daae 100644 --- a/gdb/record.h +++ b/gdb/record.h @@ -20,6 +20,8 @@ #ifndef _RECORD_H_ #define _RECORD_H_ +#include "target/waitstatus.h" /* For enum target_stop_reason. */ + struct cmd_list_element; extern unsigned int record_debug; @@ -47,6 +49,17 @@ enum record_print_flag RECORD_PRINT_INDENT_CALLS = (1 << 2) }; +/* Determined whether the target is stopped at a software or hardware + breakpoint, based on PC and the breakpoint tables. The breakpoint + type is translated to the appropriate target_stop_reason and + written to REASON. Returns true if stopped at a breakpoint, false + otherwise. */ + +extern int + record_check_stopped_by_breakpoint (struct address_space *aspace, + CORE_ADDR pc, + enum target_stop_reason *reason); + /* Wrapper for target_read_memory that prints a debug message if reading memory fails. */ extern int record_read_memory (struct gdbarch *gdbarch,