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 <palves@redhat.com> * btrace.h: Include target/waitstatus.h. (struct btrace_thread_info) <stop_reason>: 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.
This commit is contained in:
parent
15c66dd626
commit
9e8915c6ce
6 changed files with 191 additions and 47 deletions
|
@ -1,3 +1,33 @@
|
|||
2015-03-04 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* btrace.h: Include target/waitstatus.h.
|
||||
(struct btrace_thread_info) <stop_reason>: 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 <palves@redhat.com>
|
||||
|
||||
enum lwp_stop_reason -> enum target_stop_reason
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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 ops->beneath->to_decr_pc_after_break (ops->beneath, gdbarch);
|
||||
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_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;
|
||||
|
|
|
@ -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;
|
||||
|
|
19
gdb/record.c
19
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
|
||||
|
|
13
gdb/record.h
13
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,
|
||||
|
|
Loading…
Reference in a new issue