From: Pedro Alves Date: Wed, 4 Mar 2015 20:41:15 +0000 (+0000) Subject: record-full/record-btrace: software/hardware breakpoint trap X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=9e8915c6cee5c37637521b424d723e990e06d597;p=binutils-gdb.git record-full/record-btrace: software/hardware breakpoint trap 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-03-04 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. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index a1eab854147..11f6caa0b64 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,33 @@ +2015-03-04 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. + 2015-03-04 Pedro Alves enum lwp_stop_reason -> enum target_stop_reason diff --git a/gdb/btrace.h b/gdb/btrace.h index 36bf5001f2c..b7437f173df 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; @@ -258,6 +259,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 af7b65feda2..f2d35a3642e 100644 --- a/gdb/record-btrace.c +++ b/gdb/record-btrace.c @@ -1954,7 +1954,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 (); } @@ -1986,7 +1987,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 (); } } @@ -2044,18 +2046,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. */ @@ -2238,7 +2280,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 c660743a407..0fbb2646e95 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 57851ecdfea..a64543aaae3 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 771b14d99c0..101daae73ae 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,