From: Simon Marchi Date: Tue, 21 Jan 2020 21:08:01 +0000 (-0500) Subject: gdb: move displaced stepping logic to gdbarch, allow starting concurrent displaced... X-Git-Url: http://git.efficios.com/?a=commitdiff_plain;h=e088209c13199ead5a05f7c07be029b2e329cf8e;p=deliverable%2Fbinutils-gdb.git gdb: move displaced stepping logic to gdbarch, allow starting concurrent displaced steps Move displaced step logic to gdbarch ==================================== Currently, the task of preparing for a thread to execute a displaced step is driven by the displaced_step_prepare_throw function. It does obviously some calls to the gdbarch to do the specific operations but the high-level logic is in displaced_step_prepare_throw. The steps are roughly: - Ask the gdbarch for the displaced step buffer location - Save the existing bytes in the displaced step buffer - Ask the gdbarch to copy the instruction into the displaced step buffer - Set the pc of the thread to the beginning of the displaced step buffer Similarly, the "fixup" phase, executed after the instruction was successfully single-stepped, is driven by the infrun code (function displaced_step_fixup). The steps are roughly: - Restore the original bytes in the displaced stepping buffer - Ask the gdbarch to fixup the instruction result (adjust the target's registers or memory to do as if the instruction had been executed in its original location) In order to allow some architectures to change how this is done (in particular to allow using multiple displaced step buffers or sharing displaced step buffers between threads), this patch makes the whole task of preparing and finishing a displaced step gdbarch operations. Two new gdbarch methods are added, with the following sematics: - gdbarch_displaced_step_prepare: Prepare for the given thread to execute a displaced step. Upon return, everything should be ready for GDB to single step it. - gdbarch_displaced_step_finish: Apply any fixup required to compensate for the fact that the instruction was executed at different place than its original pc. Release any resources that was allocated for this displaced step. The displaced_step_prepare_throw function now pretty much just offloads to gdbarch_displaced_step_prepare and the displaced_step_finish function (renamed from displaced_step_fixup) offloads to gdbarch_displaced_step_finish. To keep the existing behavior, the logic that was previously implemented in infrun.c is moved to a new displaced-stepping.c file. Architectures currently using displaced stepping are modified to use that new file to implement the gdbarch methods. Allow starting concurrent displaced steps ========================================= Currently, there can only be a single displaced step in progress for each inferior. This is enforced in start_step_over: /* If this inferior already has a displaced step in process, don't start a new one. */ if (displaced_step_in_progress (tp->inf)) continue; Since we want to allow concurrent displaced steps, we need to remove this constraint. The core part of GDB handling displaced steps, in infrun.c, now tries to start as many step-overs as possible. The implementation provided by the gdbarch is responsible to decide whether a given can do a displaced step at this time, and if it is, prepare it. --- diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 32d0eee7c6..db54f73b77 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1000,6 +1000,7 @@ COMMON_SFILES = \ debuginfod-support.c \ dictionary.c \ disasm.c \ + displaced-stepping.c \ dummy-frame.c \ dwarf2/abbrev.c \ dwarf2/attribute.c \ diff --git a/gdb/amd64-linux-tdep.c b/gdb/amd64-linux-tdep.c index 44ecb4e0b3..937ec83596 100644 --- a/gdb/amd64-linux-tdep.c +++ b/gdb/amd64-linux-tdep.c @@ -21,6 +21,7 @@ #include "defs.h" #include "arch-utils.h" #include "frame.h" +#include "gdbarch.h" #include "gdbcore.h" #include "regcache.h" #include "osabi.h" @@ -34,6 +35,7 @@ #include "i386-linux-tdep.h" #include "linux-tdep.h" #include "gdbsupport/x86-xstate.h" +#include "inferior.h" #include "amd64-tdep.h" #include "solib-svr4.h" @@ -1794,6 +1796,59 @@ amd64_dtrace_parse_probe_argument (struct gdbarch *gdbarch, } } +struct amd64_linux_per_inferior +{ + amd64_linux_per_inferior (CORE_ADDR disp_step_buffer_addr) + : disp_step_buf_mgr (disp_step_buffer_addr) + {} + + single_displaced_buffer_manager disp_step_buf_mgr; +}; + +static const inferior_key + amd64_linux_per_inferior_data; + +/* Get the per-inferior AMD64/Linux data for INF. */ + +static amd64_linux_per_inferior * +get_amd64_linux_per_inferior (inferior *inf, gdbarch *arch) +{ + amd64_linux_per_inferior *per_inf = amd64_linux_per_inferior_data.get (inf); + + if (per_inf == nullptr) + { + /* Figure out where the displaced step buffer is. */ + CORE_ADDR disp_step_buffer_addr = linux_displaced_step_location (arch); + + per_inf = amd64_linux_per_inferior_data.emplace (inf, disp_step_buffer_addr); + } + + return per_inf; +} + +/* Implementation of the gdbarch_displaced_step_prepare method. */ + +static displaced_step_prepare_status +amd64_linux_displaced_step_prepare (gdbarch *arch, thread_info *thread) +{ + amd64_linux_per_inferior *per_inferior + = get_amd64_linux_per_inferior (thread->inf, arch); + + return per_inferior->disp_step_buf_mgr.prepare (thread); +} + +/* Implementation of the gdbarch_displaced_step_finish method. */ + +static displaced_step_finish_status +amd64_linux_displaced_step_finish (gdbarch *arch, thread_info *thread, + gdb_signal sig) +{ + amd64_linux_per_inferior *per_inferior + = get_amd64_linux_per_inferior (thread->inf, arch); + + return per_inferior->disp_step_buf_mgr.finish (arch, thread, sig); +} + static void amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch) { @@ -1839,8 +1894,8 @@ amd64_linux_init_abi_common(struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_displaced_step_copy_insn (gdbarch, amd64_displaced_step_copy_insn); set_gdbarch_displaced_step_fixup (gdbarch, amd64_displaced_step_fixup); - set_gdbarch_displaced_step_location (gdbarch, - linux_displaced_step_location); + set_gdbarch_displaced_step_prepare (gdbarch, amd64_linux_displaced_step_prepare); + set_gdbarch_displaced_step_finish (gdbarch, amd64_linux_displaced_step_finish); set_gdbarch_process_record (gdbarch, i386_process_record); set_gdbarch_process_record_signal (gdbarch, amd64_linux_record_signal); diff --git a/gdb/amd64-tdep.c b/gdb/amd64-tdep.c index b28756dbb3..2a0dfed0cd 100644 --- a/gdb/amd64-tdep.c +++ b/gdb/amd64-tdep.c @@ -1120,6 +1120,9 @@ struct amd64_displaced_step_copy_insn_closure : public displaced_step_copy_insn_ gdb::byte_vector insn_buf; }; +typedef std::unique_ptr + amd64_displaced_step_copy_insn_closure_up; + /* WARNING: Keep onebyte_has_modrm, twobyte_has_modrm in sync with ../opcodes/i386-dis.c (until libopcodes exports them, or an alternative, at which point delete these in favor of libopcodes' versions). */ diff --git a/gdb/displaced-stepping.c b/gdb/displaced-stepping.c new file mode 100644 index 0000000000..cbf2425e37 --- /dev/null +++ b/gdb/displaced-stepping.c @@ -0,0 +1,148 @@ +#include "defs.h" + +#include "displaced-stepping.h" + +#include "gdbarch.h" +#include "gdbthread.h" +#include "target.h" +#include "inferior.h" +#include "gdbcore.h" + +displaced_step_copy_insn_closure::~displaced_step_copy_insn_closure() = default; + +displaced_step_prepare_status +single_displaced_buffer_manager::prepare (thread_info *thread) +{ + /* Is a thread currently using the buffer? */ + if (m_current_thread != nullptr) + { + /* If so, it better not be this thread. */ + gdb_assert (thread != m_current_thread); + return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE; + } + + gdbarch *arch = thread->arch (); + struct regcache *regcache = thread->regcache (); + ULONGEST len = gdbarch_max_insn_length (arch); + m_original_pc = regcache_read_pc (regcache); + + /* Save the original contents of the displaced stepping buffer. */ + m_saved_copy.resize (len); + + int status = target_read_memory (m_buffer_addr, m_saved_copy.data (), len); + if (status != 0) + throw_error (MEMORY_ERROR, + _("Error accessing memory address %s (%s) for " + "displaced-stepping scratch space."), + paddress (arch, m_buffer_addr), safe_strerror (status)); + + if (debug_displaced) + { + fprintf_unfiltered (gdb_stdlog, "displaced: saved %s: ", + paddress (arch, m_buffer_addr)); + displaced_step_dump_bytes (gdb_stdlog, m_saved_copy.data (), len); + }; + + m_copy_insn_closure = gdbarch_displaced_step_copy_insn (arch, + m_original_pc, + m_buffer_addr, + regcache); + if (m_copy_insn_closure == nullptr) + { + /* The architecture doesn't know how or want to displaced step + this instruction or instruction sequence. Fallback to + stepping over the breakpoint in-line. */ + return DISPLACED_STEP_PREPARE_STATUS_ERROR; + } + + try + { + /* Resume execution at the copy. */ + regcache_write_pc (regcache, m_buffer_addr); + } + catch (...) + { + /* Failed to write the PC. Release the architecture's displaced + stepping resources and the thread's displaced stepping state. */ + m_copy_insn_closure.reset (); + + return DISPLACED_STEP_PREPARE_STATUS_ERROR; + } + + /* This marks the buffer as being in use. */ + m_current_thread = thread; + + return DISPLACED_STEP_PREPARE_STATUS_OK; +} + +static void +write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr, + const gdb_byte *myaddr, int len) +{ + scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid); + + inferior_ptid = ptid; + write_memory (memaddr, myaddr, len); +} + +static bool +displaced_step_instruction_executed_successfully (gdbarch *arch, gdb_signal signal) +{ + if (signal != GDB_SIGNAL_TRAP) + return false; + + if (target_stopped_by_watchpoint ()) + { + // FIXME: Not sure about this condition. + if (gdbarch_have_nonsteppable_watchpoint (arch) + || target_have_steppable_watchpoint) + return false; + } + + return true; +} + +displaced_step_finish_status +single_displaced_buffer_manager::finish (gdbarch *arch, thread_info *thread, + gdb_signal sig) +{ + displaced_step_finish_status status; + + gdb_assert (thread == m_current_thread); + + ULONGEST len = gdbarch_max_insn_length (arch); + + write_memory_ptid (thread->ptid, m_buffer_addr, + m_saved_copy.data (), len); + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: restored %s %s\n", + target_pid_to_str (thread->ptid).c_str (), + paddress (arch, m_buffer_addr)); + + regcache *rc = get_thread_regcache (thread); + + bool instruction_executed_successfully + = displaced_step_instruction_executed_successfully (arch, sig); + + + if (instruction_executed_successfully) + { + gdbarch_displaced_step_fixup (arch, m_copy_insn_closure.get (), m_original_pc, + m_buffer_addr, rc); + status = DISPLACED_STEP_FINISH_STATUS_OK; + } + else + { + /* Since the instruction didn't complete, all we can do is relocate the + PC. */ + CORE_ADDR pc = regcache_read_pc (rc); + pc = m_original_pc + (pc - m_buffer_addr); + regcache_write_pc (rc, pc); + status = DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED; + } + + m_copy_insn_closure.reset (); + m_current_thread = nullptr; + + return status; +} diff --git a/gdb/displaced-stepping.h b/gdb/displaced-stepping.h new file mode 100644 index 0000000000..0cb2d8c970 --- /dev/null +++ b/gdb/displaced-stepping.h @@ -0,0 +1,126 @@ +#ifndef DISPLACED_STEPPING_H +#define DISPLACED_STEPPING_H + +#include "gdbsupport/byte-vector.h" + +struct gdbarch; +struct thread_info; + +enum displaced_step_prepare_status +{ + /* A displaced stepping buffer was successfully allocated and prepared. */ + DISPLACED_STEP_PREPARE_STATUS_OK, + + /* Something bad happened. */ + DISPLACED_STEP_PREPARE_STATUS_ERROR, + + /* Not enough resources are available at this time, try again later. */ + DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE, +}; + +enum displaced_step_finish_status +{ + /* The instruction was stepped and fixed up. */ + DISPLACED_STEP_FINISH_STATUS_OK, + + /* The instruction was not stepped. */ + DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED, +}; + +/* Data returned by a gdbarch displaced_step_copy_insn method, to be passed to + the matching displaced_step_fixup method. */ + +struct displaced_step_copy_insn_closure +{ + virtual ~displaced_step_copy_insn_closure () = 0; +}; + +typedef std::unique_ptr + displaced_step_copy_insn_closure_up; + +/* A simple displaced step closure that contains only a byte buffer. */ + +struct buf_displaced_step_copy_insn_closure : displaced_step_copy_insn_closure +{ + buf_displaced_step_copy_insn_closure (int buf_size) + : buf (buf_size) + {} + + gdb::byte_vector buf; +}; + +/* Per-inferior displaced stepping state. */ + +struct displaced_step_inferior_state +{ + displaced_step_inferior_state () + { + reset (); + } + + /* Put this object back in its original state. */ + void reset () + { + failed_before = false; + } + + /* True if preparing a displaced step ever failed. If so, we won't + try displaced stepping for this inferior again. */ + bool failed_before; +}; + +/* Per-thread displaced stepping state. */ + +struct displaced_step_thread_state +{ + /* Return true if this thread is currently executing a displaced step. */ + bool in_progress () const + { return m_original_gdbarch != nullptr; } + + /* Return the gdbarch of the thread prior to the step. */ + gdbarch *get_original_gdbarch () const + { return m_original_gdbarch; } + + /* Mark this thread as currently executing a displaced step. + + ORIGINAL_GDBARCH is the current gdbarch of the thread (before the step + is executed). */ + void set (gdbarch *original_gdbarch) + { m_original_gdbarch = original_gdbarch; } + + /* mark this thread as no longer executing a displaced step. */ + void reset () + { m_original_gdbarch = nullptr; } + +private: + gdbarch *m_original_gdbarch = nullptr; +}; + +/* Manage access to a single displaced stepping buffer, without any + sharing. */ + +struct single_displaced_buffer_manager +{ + single_displaced_buffer_manager (CORE_ADDR buffer_addr) + : m_buffer_addr (buffer_addr) + {} + + displaced_step_prepare_status prepare (thread_info *thread); + + displaced_step_finish_status finish (gdbarch *arch, thread_info *thread, + gdb_signal sig); + +private: + + CORE_ADDR m_original_pc; + CORE_ADDR m_buffer_addr; + + /* If set, the thread currently using the buffer. */ + thread_info *m_current_thread = nullptr; + + gdb::byte_vector m_saved_copy; + displaced_step_copy_insn_closure_up m_copy_insn_closure; +}; + + +#endif /* DISPLACED_STEPPING_H */ diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index 87007f3cb2..e7d94a3b47 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -288,7 +288,8 @@ struct gdbarch gdbarch_displaced_step_copy_insn_ftype *displaced_step_copy_insn; gdbarch_displaced_step_hw_singlestep_ftype *displaced_step_hw_singlestep; gdbarch_displaced_step_fixup_ftype *displaced_step_fixup; - gdbarch_displaced_step_location_ftype *displaced_step_location; + gdbarch_displaced_step_prepare_ftype *displaced_step_prepare; + gdbarch_displaced_step_finish_ftype *displaced_step_finish; gdbarch_relocate_instruction_ftype *relocate_instruction; gdbarch_overlay_update_ftype *overlay_update; gdbarch_core_read_description_ftype *core_read_description; @@ -442,7 +443,8 @@ gdbarch_alloc (const struct gdbarch_info *info, gdbarch->skip_permanent_breakpoint = default_skip_permanent_breakpoint; gdbarch->displaced_step_hw_singlestep = default_displaced_step_hw_singlestep; gdbarch->displaced_step_fixup = NULL; - gdbarch->displaced_step_location = NULL; + gdbarch->displaced_step_prepare = NULL; + gdbarch->displaced_step_finish = NULL; gdbarch->relocate_instruction = NULL; gdbarch->has_shared_address_space = default_has_shared_address_space; gdbarch->fast_tracepoint_valid_at = default_fast_tracepoint_valid_at; @@ -653,8 +655,10 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of displaced_step_copy_insn, has predicate. */ /* Skip verify of displaced_step_hw_singlestep, invalid_p == 0 */ /* Skip verify of displaced_step_fixup, has predicate. */ - if ((! gdbarch->displaced_step_location) != (! gdbarch->displaced_step_copy_insn)) - log.puts ("\n\tdisplaced_step_location"); + if ((! gdbarch->displaced_step_prepare) != (! gdbarch->displaced_step_copy_insn)) + log.puts ("\n\tdisplaced_step_prepare"); + if ((! gdbarch->displaced_step_finish) != (! gdbarch->displaced_step_copy_insn)) + log.puts ("\n\tdisplaced_step_finish"); /* Skip verify of relocate_instruction, has predicate. */ /* Skip verify of overlay_update, has predicate. */ /* Skip verify of core_read_description, has predicate. */ @@ -911,6 +915,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) fprintf_unfiltered (file, "gdbarch_dump: displaced_step_copy_insn = <%s>\n", host_address_to_string (gdbarch->displaced_step_copy_insn)); + fprintf_unfiltered (file, + "gdbarch_dump: displaced_step_finish = <%s>\n", + host_address_to_string (gdbarch->displaced_step_finish)); fprintf_unfiltered (file, "gdbarch_dump: gdbarch_displaced_step_fixup_p() = %d\n", gdbarch_displaced_step_fixup_p (gdbarch)); @@ -921,8 +928,8 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) "gdbarch_dump: displaced_step_hw_singlestep = <%s>\n", host_address_to_string (gdbarch->displaced_step_hw_singlestep)); fprintf_unfiltered (file, - "gdbarch_dump: displaced_step_location = <%s>\n", - host_address_to_string (gdbarch->displaced_step_location)); + "gdbarch_dump: displaced_step_prepare = <%s>\n", + host_address_to_string (gdbarch->displaced_step_prepare)); fprintf_unfiltered (file, "gdbarch_dump: double_bit = %s\n", plongest (gdbarch->double_bit)); @@ -3985,21 +3992,38 @@ set_gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, gdbarch->displaced_step_fixup = displaced_step_fixup; } -CORE_ADDR -gdbarch_displaced_step_location (struct gdbarch *gdbarch) +displaced_step_prepare_status +gdbarch_displaced_step_prepare (struct gdbarch *gdbarch, thread_info *thread) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->displaced_step_prepare != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_displaced_step_prepare called\n"); + return gdbarch->displaced_step_prepare (gdbarch, thread); +} + +void +set_gdbarch_displaced_step_prepare (struct gdbarch *gdbarch, + gdbarch_displaced_step_prepare_ftype displaced_step_prepare) +{ + gdbarch->displaced_step_prepare = displaced_step_prepare; +} + +displaced_step_finish_status +gdbarch_displaced_step_finish (struct gdbarch *gdbarch, thread_info *thread, gdb_signal sig) { gdb_assert (gdbarch != NULL); - gdb_assert (gdbarch->displaced_step_location != NULL); + gdb_assert (gdbarch->displaced_step_finish != NULL); if (gdbarch_debug >= 2) - fprintf_unfiltered (gdb_stdlog, "gdbarch_displaced_step_location called\n"); - return gdbarch->displaced_step_location (gdbarch); + fprintf_unfiltered (gdb_stdlog, "gdbarch_displaced_step_finish called\n"); + return gdbarch->displaced_step_finish (gdbarch, thread, sig); } void -set_gdbarch_displaced_step_location (struct gdbarch *gdbarch, - gdbarch_displaced_step_location_ftype displaced_step_location) +set_gdbarch_displaced_step_finish (struct gdbarch *gdbarch, + gdbarch_displaced_step_finish_ftype displaced_step_finish) { - gdbarch->displaced_step_location = displaced_step_location; + gdbarch->displaced_step_finish = displaced_step_finish; } int diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 7f3b50d889..a870304167 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -31,6 +31,7 @@ #include "gdb_obstack.h" #include "infrun.h" #include "osabi.h" +#include "displaced-stepping.h" struct floatformat; struct ui_file; @@ -1074,11 +1075,17 @@ extern void set_gdbarch_displaced_step_fixup (struct gdbarch *gdbarch, gdbarch_d time. For a general explanation of displaced stepping and how GDB uses it, - see the comments in infrun.c. */ + see the comments in infrun.c. + m;CORE_ADDR;displaced_step_location;thread_info *;thread;;NULL;;(! gdbarch->displaced_step_location) != (! gdbarch->displaced_step_copy_insn) + m;CORE_ADDR;displaced_step_release_location;CORE_ADDR;addr;;NULL;;(! gdbarch->displaced_step_location) != (! gdbarch->displaced_step_copy_insn) */ + +typedef displaced_step_prepare_status (gdbarch_displaced_step_prepare_ftype) (struct gdbarch *gdbarch, thread_info *thread); +extern displaced_step_prepare_status gdbarch_displaced_step_prepare (struct gdbarch *gdbarch, thread_info *thread); +extern void set_gdbarch_displaced_step_prepare (struct gdbarch *gdbarch, gdbarch_displaced_step_prepare_ftype *displaced_step_prepare); -typedef CORE_ADDR (gdbarch_displaced_step_location_ftype) (struct gdbarch *gdbarch); -extern CORE_ADDR gdbarch_displaced_step_location (struct gdbarch *gdbarch); -extern void set_gdbarch_displaced_step_location (struct gdbarch *gdbarch, gdbarch_displaced_step_location_ftype *displaced_step_location); +typedef displaced_step_finish_status (gdbarch_displaced_step_finish_ftype) (struct gdbarch *gdbarch, thread_info *thread, gdb_signal sig); +extern displaced_step_finish_status gdbarch_displaced_step_finish (struct gdbarch *gdbarch, thread_info *thread, gdb_signal sig); +extern void set_gdbarch_displaced_step_finish (struct gdbarch *gdbarch, gdbarch_displaced_step_finish_ftype *displaced_step_finish); /* Relocate an instruction to execute at a different address. OLDLOC is the address in the inferior memory where the instruction to diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index 32b9b1b1c4..10332ea282 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -822,7 +822,10 @@ M;void;displaced_step_fixup;struct displaced_step_copy_insn_closure *closure, CO # # For a general explanation of displaced stepping and how GDB uses it, # see the comments in infrun.c. -m;CORE_ADDR;displaced_step_location;void;;;NULL;;(! gdbarch->displaced_step_location) != (! gdbarch->displaced_step_copy_insn) +#m;CORE_ADDR;displaced_step_location;thread_info *;thread;;NULL;;(! gdbarch->displaced_step_location) != (! gdbarch->displaced_step_copy_insn) +#m;CORE_ADDR;displaced_step_release_location;CORE_ADDR;addr;;NULL;;(! gdbarch->displaced_step_location) != (! gdbarch->displaced_step_copy_insn) +m;displaced_step_prepare_status;displaced_step_prepare;thread_info *thread;thread;;NULL;;(! gdbarch->displaced_step_prepare) != (! gdbarch->displaced_step_copy_insn) +m;displaced_step_finish_status;displaced_step_finish;thread_info *thread, gdb_signal sig;thread, sig;;NULL;;(! gdbarch->displaced_step_finish) != (! gdbarch->displaced_step_copy_insn) # Relocate an instruction to execute at a different address. OLDLOC # is the address in the inferior memory where the instruction to @@ -1271,6 +1274,7 @@ cat <. */ #include "defs.h" +#include "displaced-stepping.h" #include "gdbcore.h" #include "frame.h" #include "value.h" @@ -820,6 +821,20 @@ i386_linux_displaced_step_copy_insn (struct gdbarch *gdbarch, return closure_; } +static displaced_step_prepare_status +i386_displaced_step_prepare (gdbarch *arch, thread_info *thread) +{ + gdb_assert (false); + return DISPLACED_STEP_PREPARE_STATUS_OK; +} + +static displaced_step_finish_status +i386_displaced_step_finish (gdbarch *arch, thread_info *thread, gdb_signal sig) +{ + gdb_assert (false); + return DISPLACED_STEP_FINISH_STATUS_OK; +} + static void i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { @@ -1064,8 +1079,8 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_displaced_step_copy_insn (gdbarch, i386_linux_displaced_step_copy_insn); set_gdbarch_displaced_step_fixup (gdbarch, i386_displaced_step_fixup); - set_gdbarch_displaced_step_location (gdbarch, - linux_displaced_step_location); + set_gdbarch_displaced_step_prepare (gdbarch, i386_displaced_step_prepare); + set_gdbarch_displaced_step_finish (gdbarch, i386_displaced_step_finish); /* Functions for 'catch syscall'. */ set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_I386); diff --git a/gdb/infrun.c b/gdb/infrun.c index c615522eb0..17c342be50 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -19,6 +19,7 @@ along with this program. If not, see . */ #include "defs.h" +#include "displaced-stepping.h" #include "gdbsupport/common-defs.h" #include "gdbsupport/common-utils.h" #include "infrun.h" @@ -37,6 +38,7 @@ #include "top.h" #include "inf-loop.h" #include "regcache.h" +#include "utils.h" #include "value.h" #include "observable.h" #include "language.h" @@ -1495,11 +1497,7 @@ step_over_info_valid_p (void) displaced step operation on it. See displaced_step_prepare and displaced_step_fixup for details. */ -/* Default destructor for displaced_step_copy_insn_closure. */ - -displaced_step_copy_insn_closure::~displaced_step_copy_insn_closure () = default; - -/* Get the displaced stepping state of process PID. */ +/* Get the displaced stepping state of inferior INF. */ static displaced_step_inferior_state * get_displaced_stepping_state (inferior *inf) @@ -1507,38 +1505,50 @@ get_displaced_stepping_state (inferior *inf) return &inf->displaced_step_state; } -/* Returns true if any inferior has a thread doing a displaced - step. */ +/* Get the displaced stepping state of thread THREAD. */ -static bool -displaced_step_in_progress_any_inferior () +static displaced_step_thread_state * +get_displaced_stepping_state (thread_info *thread) { - for (inferior *i : all_inferiors ()) - { - if (i->displaced_step_state.step_thread != nullptr) - return true; - } - - return false; + return &thread->displaced_step_state; } -/* Return true if thread represented by PTID is doing a displaced - step. */ +/* Return true if the given thread is doing a displaced step. */ -static int -displaced_step_in_progress_thread (thread_info *thread) +static bool +displaced_step_in_progress (thread_info *thread) { gdb_assert (thread != NULL); - return get_displaced_stepping_state (thread->inf)->step_thread == thread; + return get_displaced_stepping_state (thread)->in_progress (); } -/* Return true if process PID has a thread doing a displaced step. */ +/* Return true if any thread of this inferior is doing a displaced step. */ -static int +static bool displaced_step_in_progress (inferior *inf) { - return get_displaced_stepping_state (inf)->step_thread != nullptr; + for (thread_info *thread : inf->non_exited_threads ()) + { + if (displaced_step_in_progress (thread)) + return true; + } + + return false; +} + +/* Return true if any thread is doing a displaced step. */ + +static bool +displaced_step_in_progress_any_thread () +{ + for (thread_info *thread : all_non_exited_threads ()) + { + if (displaced_step_in_progress (thread)) + return true; + } + + return false; } /* If inferior is in displaced stepping, and ADDR equals to starting address @@ -1548,14 +1558,15 @@ displaced_step_in_progress (inferior *inf) struct displaced_step_copy_insn_closure * get_displaced_step_copy_insn_closure_by_addr (CORE_ADDR addr) { - displaced_step_inferior_state *displaced - = get_displaced_stepping_state (current_inferior ()); - - /* If checking the mode of displaced instruction in copy area. */ - if (displaced->step_thread != nullptr - && displaced->step_copy == addr) - return displaced->step_closure.get (); - +// FIXME: implement me (only needed on ARM). +// displaced_step_inferior_state *displaced +// = get_displaced_stepping_state (current_inferior ()); +// +// /* If checking the mode of displaced instruction in copy area. */ +// if (displaced->step_thread != nullptr +// && displaced->step_copy == addr) +// return displaced->step_closure.get (); +// return NULL; } @@ -1596,8 +1607,9 @@ show_can_use_displaced_stepping (struct ui_file *file, int from_tty, static bool gdbarch_supports_displaced_stepping (gdbarch *arch) { - /* Only check for the presence of step_copy_insn. Other required methods - are checked by the gdbarch validation. */ + /* Only check for the presence of copy_insn. Other required methods + are checked by the gdbarch validation to be provided if copy_insn is + provided. */ return gdbarch_displaced_step_copy_insn_p (arch); } @@ -1639,10 +1651,10 @@ use_displaced_stepping (thread_info *tp) return true; } -/* Simple function wrapper around displaced_step_inferior_state::reset. */ +/* Simple function wrapper around displaced_step_thread_state::reset. */ static void -displaced_step_reset (displaced_step_inferior_state *displaced) +displaced_step_reset (displaced_step_thread_state *displaced) { displaced->reset (); } @@ -1681,15 +1693,13 @@ displaced_step_dump_bytes (struct ui_file *file, stepped now; 0 if displaced stepping this thread got queued; or -1 if this instruction can't be displaced stepped. */ -static int +static displaced_step_prepare_status displaced_step_prepare_throw (thread_info *tp) { regcache *regcache = get_thread_regcache (tp); struct gdbarch *gdbarch = regcache->arch (); - const address_space *aspace = regcache->aspace (); - CORE_ADDR original, copy; - ULONGEST len; - int status; + displaced_step_thread_state *thread_disp_step_state + = get_displaced_stepping_state (tp); /* We should never reach this function if the architecture does not support displaced stepping. */ @@ -1704,126 +1714,102 @@ displaced_step_prepare_throw (thread_info *tp) jump/branch). */ tp->control.may_range_step = 0; - /* We have to displaced step one thread at a time, as we only have - access to a single scratch space per inferior. */ + /* We are about to start a displaced step for this thread, if one is already + in progress, we goofed up somewhere. */ + gdb_assert (!thread_disp_step_state->in_progress ()); - displaced_step_inferior_state *displaced - = get_displaced_stepping_state (tp->inf); + scoped_restore_current_thread restore_thread; - if (displaced->step_thread != nullptr) - { - /* Already waiting for a displaced step to finish. Defer this - request and place in queue. */ + switch_to_thread (tp); + + CORE_ADDR original_pc = regcache_read_pc (regcache); + + displaced_step_prepare_status status = + gdbarch_displaced_step_prepare (gdbarch, tp); + if (status == DISPLACED_STEP_PREPARE_STATUS_ERROR) + { if (debug_displaced) fprintf_unfiltered (gdb_stdlog, - "displaced: deferring step of %s\n", + "displaced: failed to prepare (%s)", target_pid_to_str (tp->ptid).c_str ()); - global_thread_step_over_chain_enqueue (tp); - return 0; + return DISPLACED_STEP_PREPARE_STATUS_ERROR; } - else + else if (status == DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE) { + /* Not enough displaced stepping resources available, defer this + request by placing it the queue. */ + if (debug_displaced) fprintf_unfiltered (gdb_stdlog, - "displaced: stepping %s now\n", + "displaced: not enough resources available, " + "deferring step of %s\n", target_pid_to_str (tp->ptid).c_str ()); - } - displaced_step_reset (displaced); - - scoped_restore_current_thread restore_thread; - - switch_to_thread (tp); - - original = regcache_read_pc (regcache); - - copy = gdbarch_displaced_step_location (gdbarch); - len = gdbarch_max_insn_length (gdbarch); - - if (breakpoint_in_range_p (aspace, copy, len)) - { - /* There's a breakpoint set in the scratch pad location range - (which is usually around the entry point). We'd either - install it before resuming, which would overwrite/corrupt the - scratch pad, or if it was already inserted, this displaced - step would overwrite it. The latter is OK in the sense that - we already assume that no thread is going to execute the code - in the scratch pad range (after initial startup) anyway, but - the former is unacceptable. Simply punt and fallback to - stepping over this breakpoint in-line. */ - if (debug_displaced) - { - fprintf_unfiltered (gdb_stdlog, - "displaced: breakpoint set in scratch pad. " - "Stepping over breakpoint in-line instead.\n"); - } - - return -1; - } - - /* Save the original contents of the copy area. */ - displaced->step_saved_copy.resize (len); - status = target_read_memory (copy, displaced->step_saved_copy.data (), len); - if (status != 0) - throw_error (MEMORY_ERROR, - _("Error accessing memory address %s (%s) for " - "displaced-stepping scratch space."), - paddress (gdbarch, copy), safe_strerror (status)); - if (debug_displaced) - { - fprintf_unfiltered (gdb_stdlog, "displaced: saved %s: ", - paddress (gdbarch, copy)); - displaced_step_dump_bytes (gdb_stdlog, - displaced->step_saved_copy.data (), - len); - }; + global_thread_step_over_chain_enqueue (tp); - displaced->step_closure - = gdbarch_displaced_step_copy_insn (gdbarch, original, copy, regcache); - if (displaced->step_closure == NULL) - { - /* The architecture doesn't know how or want to displaced step - this instruction or instruction sequence. Fallback to - stepping over the breakpoint in-line. */ - return -1; - } + return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE; + } + + gdb_assert (status == DISPLACED_STEP_PREPARE_STATUS_OK); + +// FIXME: Should probably replicated in the arch implementation now. +// +// if (breakpoint_in_range_p (aspace, copy, len)) +// { +// /* There's a breakpoint set in the scratch pad location range +// (which is usually around the entry point). We'd either +// install it before resuming, which would overwrite/corrupt the +// scratch pad, or if it was already inserted, this displaced +// step would overwrite it. The latter is OK in the sense that +// we already assume that no thread is going to execute the code +// in the scratch pad range (after initial startup) anyway, but +// the former is unacceptable. Simply punt and fallback to +// stepping over this breakpoint in-line. */ +// if (debug_displaced) +// { +// fprintf_unfiltered (gdb_stdlog, +// "displaced: breakpoint set in scratch pad. " +// "Stepping over breakpoint in-line instead.\n"); +// } +// +// gdb_assert (false); +// gdbarch_displaced_step_release_location (gdbarch, copy); +// +// return -1; +// } /* Save the information we need to fix things up if the step succeeds. */ - displaced->step_thread = tp; - displaced->step_gdbarch = gdbarch; - displaced->step_original = original; - displaced->step_copy = copy; - - { - displaced_step_reset_cleanup cleanup (displaced); - - /* Resume execution at the copy. */ - regcache_write_pc (regcache, copy); + thread_disp_step_state->set (gdbarch); - cleanup.release (); - } + // FIXME: get it from _prepare? + CORE_ADDR displaced_pc = 0; if (debug_displaced) - fprintf_unfiltered (gdb_stdlog, "displaced: displaced pc to %s\n", - paddress (gdbarch, copy)); + fprintf_unfiltered (gdb_stdlog, + "displaced: prepared successfully thread=%s, " + "original_pc=%s, displaced_pc=%s\n", + target_pid_to_str (tp->ptid).c_str (), + paddress (gdbarch, original_pc), + paddress (gdbarch, displaced_pc)); - return 1; + return DISPLACED_STEP_PREPARE_STATUS_OK; } /* Wrapper for displaced_step_prepare_throw that disabled further attempts at displaced stepping if we get a memory error. */ -static int +static displaced_step_prepare_status displaced_step_prepare (thread_info *thread) { - int prepared = -1; + displaced_step_prepare_status status + = DISPLACED_STEP_PREPARE_STATUS_ERROR; try { - prepared = displaced_step_prepare_throw (thread); + status = displaced_step_prepare_throw (thread); } catch (const gdb_exception_error &ex) { @@ -1850,34 +1836,7 @@ displaced_step_prepare (thread_info *thread) displaced_state->failed_before = 1; } - return prepared; -} - -static void -write_memory_ptid (ptid_t ptid, CORE_ADDR memaddr, - const gdb_byte *myaddr, int len) -{ - scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid); - - inferior_ptid = ptid; - write_memory (memaddr, myaddr, len); -} - -/* Restore the contents of the copy area for thread PTID. */ - -static void -displaced_step_restore (struct displaced_step_inferior_state *displaced, - ptid_t ptid) -{ - ULONGEST len = gdbarch_max_insn_length (displaced->step_gdbarch); - - write_memory_ptid (ptid, displaced->step_copy, - displaced->step_saved_copy.data (), len); - if (debug_displaced) - fprintf_unfiltered (gdb_stdlog, "displaced: restored %s %s\n", - target_pid_to_str (ptid).c_str (), - paddress (displaced->step_gdbarch, - displaced->step_copy)); + return status; } /* If we displaced stepped an instruction successfully, adjust @@ -1887,53 +1846,33 @@ displaced_step_restore (struct displaced_step_inferior_state *displaced, -1. If the thread wasn't displaced stepping, return 0. */ static int -displaced_step_fixup (thread_info *event_thread, enum gdb_signal signal) +displaced_step_finish (thread_info *event_thread, enum gdb_signal signal) { - struct displaced_step_inferior_state *displaced - = get_displaced_stepping_state (event_thread->inf); - int ret; + displaced_step_thread_state *displaced + = get_displaced_stepping_state (event_thread); - /* Was this event for the thread we displaced? */ - if (displaced->step_thread != event_thread) + /* Was this thread performing a displaced step? */ + if (!displaced->in_progress ()) return 0; + displaced_step_reset_cleanup cleanup (displaced); + /* Fixup may need to read memory/registers. Switch to the thread that we're fixing up. Also, target_stopped_by_watchpoint checks the current thread, and displaced_step_restore performs ptid-dependent memory accesses using current_inferior() and current_top_target(). */ switch_to_thread (event_thread); - displaced_step_reset_cleanup cleanup (displaced); - - displaced_step_restore (displaced, displaced->step_thread->ptid); + /* Do the fixup, and release the resources acquired to do the displaced + step. */ + displaced_step_finish_status finish_status = + gdbarch_displaced_step_finish (displaced->get_original_gdbarch (), + event_thread, signal); - /* Did the instruction complete successfully? */ - if (signal == GDB_SIGNAL_TRAP - && !(target_stopped_by_watchpoint () - && (gdbarch_have_nonsteppable_watchpoint (displaced->step_gdbarch) - || target_have_steppable_watchpoint))) - { - /* Fix up the resulting state. */ - gdbarch_displaced_step_fixup (displaced->step_gdbarch, - displaced->step_closure.get (), - displaced->step_original, - displaced->step_copy, - get_thread_regcache (displaced->step_thread)); - ret = 1; - } + if (finish_status == DISPLACED_STEP_FINISH_STATUS_OK) + return 1; else - { - /* Since the instruction didn't complete, all we can do is - relocate the PC. */ - struct regcache *regcache = get_thread_regcache (event_thread); - CORE_ADDR pc = regcache_read_pc (regcache); - - pc = displaced->step_original + (pc - displaced->step_copy); - regcache_write_pc (regcache, pc); - ret = -1; - } - - return ret; + return -1; } /* Data to be passed around while handling an event. This data is @@ -1982,13 +1921,23 @@ static int start_step_over (void) { struct thread_info *tp, *next; + int started = 0; /* Don't start a new step-over if we already have an in-line step-over operation ongoing. */ if (step_over_info_valid_p ()) - return 0; + return started; + + /* Steal the global thread step over chain. */ + thread_info *threads_to_step = global_thread_step_over_chain_head; + global_thread_step_over_chain_head = NULL; + + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: stealing list of %d threads to step from global queue\n", + thread_step_over_chain_length (threads_to_step)); - for (tp = global_thread_step_over_chain_head; tp != NULL; tp = next) + for (tp = threads_to_step; tp != NULL; tp = next) { struct execution_control_state ecss; struct execution_control_state *ecs = &ecss; @@ -1997,12 +1946,7 @@ start_step_over (void) gdb_assert (!tp->stop_requested); - next = global_thread_step_over_chain_next (tp); - - /* If this inferior already has a displaced step in process, - don't start a new one. */ - if (displaced_step_in_progress (tp->inf)) - continue; + next = thread_step_over_chain_next (threads_to_step, tp); step_what = thread_still_needs_step_over (tp); must_be_in_line = ((step_what & STEP_OVER_WATCHPOINT) @@ -2012,13 +1956,10 @@ start_step_over (void) /* We currently stop all threads of all processes to step-over in-line. If we need to start a new in-line step-over, let any pending displaced steps finish first. */ - if (must_be_in_line && displaced_step_in_progress_any_inferior ()) - return 0; - - global_thread_step_over_chain_remove (tp); + if (must_be_in_line && displaced_step_in_progress_any_thread ()) + continue; - if (global_thread_step_over_chain_head == NULL) - infrun_log_debug ("step-over queue now empty"); + thread_step_over_chain_remove (&threads_to_step, tp); if (tp->control.trap_expected || tp->resumed @@ -2052,13 +1993,28 @@ start_step_over (void) if (!ecs->wait_some_more) error (_("Command aborted.")); - gdb_assert (tp->resumed); + /* If the thread's step over could not be initiated, it was re-added + to the global step over chain. */ + if (tp->resumed) + { + infrun_log_debug ("start_step_over: [%s] was resumed.\n", + target_pid_to_str (tp->ptid).c_str ()); + gdb_assert (!thread_is_in_step_over_chain (tp)); + } + else + { + infrun_log_debug ("infrun: start_step_over: [%s] was NOT resumed.\n", + target_pid_to_str (tp->ptid).c_str ()); + gdb_assert (thread_is_in_step_over_chain (tp)); + + } /* If we started a new in-line step-over, we're done. */ if (step_over_info_valid_p ()) { gdb_assert (tp->control.trap_expected); - return 1; + started = 1; + break; } if (!target_is_non_stop_p ()) @@ -2071,7 +2027,8 @@ start_step_over (void) /* With remote targets (at least), in all-stop, we can't issue any further remote commands until the program stops again. */ - return 1; + started = 1; + break; } /* Either the thread no longer needed a step-over, or a new @@ -2080,7 +2037,35 @@ start_step_over (void) displaced step on a thread of other process. */ } - return 0; + /* If there are threads left in the THREADS_TO_STEP list, but we have + detected that we can't start anything more, put back these threads + in the global list. */ + if (threads_to_step == NULL) + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: step-over queue now empty\n"); + } + else + { + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: putting back %d threads to step in global queue\n", + thread_step_over_chain_length (threads_to_step)); + while (threads_to_step != nullptr) + { + thread_info *thread = threads_to_step; + + /* Remove from that list. */ + thread_step_over_chain_remove (&threads_to_step, thread); + + /* Add to global list. */ + global_thread_step_over_chain_enqueue (thread); + + } + } + + return started; } /* Update global variables holding ptids to hold NEW_PTID if they were @@ -2455,16 +2440,17 @@ resume_1 (enum gdb_signal sig) && sig == GDB_SIGNAL_0 && !current_inferior ()->waiting_for_vfork_done) { - int prepared = displaced_step_prepare (tp); + displaced_step_prepare_status prepare_status + = displaced_step_prepare (tp); - if (prepared == 0) + if (prepare_status == DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE) { infrun_log_debug ("Got placed in step-over queue"); tp->control.trap_expected = 0; return; } - else if (prepared < 0) + else if (prepare_status == DISPLACED_STEP_PREPARE_STATUS_ERROR) { /* Fallback to stepping over the breakpoint in-line. */ @@ -2478,18 +2464,12 @@ resume_1 (enum gdb_signal sig) insert_breakpoints (); } - else if (prepared > 0) + else if (prepare_status == DISPLACED_STEP_PREPARE_STATUS_OK) { - struct displaced_step_inferior_state *displaced; - - /* Update pc to reflect the new address from which we will - execute instructions due to displaced stepping. */ - pc = regcache_read_pc (get_thread_regcache (tp)); - - displaced = get_displaced_stepping_state (tp->inf); - step = gdbarch_displaced_step_hw_singlestep - (gdbarch, displaced->step_closure.get ()); + step = gdbarch_displaced_step_hw_singlestep (gdbarch, NULL); } + else + gdb_assert_not_reached ("invalid displaced_step_prepare_status value"); } /* Do we need to do it the hard way, w/temp breakpoints? */ @@ -3672,18 +3652,19 @@ prepare_for_detach (void) struct inferior *inf = current_inferior (); ptid_t pid_ptid = ptid_t (inf->pid); - displaced_step_inferior_state *displaced = get_displaced_stepping_state (inf); + // displaced_step_inferior_state *displaced = get_displaced_stepping_state (inf); /* Is any thread of this process displaced stepping? If not, there's nothing else to do. */ - if (displaced->step_thread == nullptr) + if (displaced_step_in_progress (inf)) return; infrun_log_debug ("displaced-stepping in-process while detaching"); scoped_restore restore_detaching = make_scoped_restore (&inf->detaching, true); - while (displaced->step_thread != nullptr) + // FIXME + while (false) { struct execution_control_state ecss; struct execution_control_state *ecs; @@ -4883,7 +4864,7 @@ stop_all_threads (void) t->suspend.waitstatus.kind = TARGET_WAITKIND_IGNORE; t->suspend.waitstatus_pending_p = 0; - if (displaced_step_fixup (t, GDB_SIGNAL_0) < 0) + if (displaced_step_finish (t, GDB_SIGNAL_0) < 0) { /* Add it back to the step-over queue. */ infrun_log_debug ("displaced-step of %s " @@ -4918,7 +4899,7 @@ stop_all_threads (void) sig = (event.ws.kind == TARGET_WAITKIND_STOPPED ? event.ws.value.sig : GDB_SIGNAL_0); - if (displaced_step_fixup (t, sig) < 0) + if (displaced_step_finish (t, sig) < 0) { /* Add it back to the step-over queue. */ t->control.trap_expected = 0; @@ -5299,7 +5280,7 @@ handle_inferior_event (struct execution_control_state *ecs) /* If checking displaced stepping is supported, and thread ecs->ptid is displaced stepping. */ - if (displaced_step_in_progress_thread (ecs->event_thread)) + if (displaced_step_in_progress (ecs->event_thread)) { struct inferior *parent_inf = find_inferior_ptid (ecs->target, ecs->ptid); @@ -5308,11 +5289,12 @@ handle_inferior_event (struct execution_control_state *ecs) if (ecs->ws.kind == TARGET_WAITKIND_FORKED) { - struct displaced_step_inferior_state *displaced - = get_displaced_stepping_state (parent_inf); + // struct displaced_step_inferior_state *displaced + // = get_displaced_stepping_state (parent_inf); /* Restore scratch pad for child process. */ - displaced_step_restore (displaced, ecs->ws.value.related_pid); + //displaced_step_restore (displaced, ecs->ws.value.related_pid); + // FIXME: we should restore all the buffers that were currently in use } /* GDB has got TARGET_WAITKIND_FORKED or TARGET_WAITKIND_VFORKED, @@ -5320,7 +5302,7 @@ handle_inferior_event (struct execution_control_state *ecs) has been done. Perform cleanup for parent process here. Note that this operation also cleans up the child process for vfork, because their pages are shared. */ - displaced_step_fixup (ecs->event_thread, GDB_SIGNAL_TRAP); + displaced_step_finish (ecs->event_thread, GDB_SIGNAL_TRAP); /* Start a new step-over in another thread if there's one that needs it. */ start_step_over (); @@ -5667,8 +5649,8 @@ finish_step_over (struct execution_control_state *ecs) { int had_step_over_info; - displaced_step_fixup (ecs->event_thread, - ecs->event_thread->suspend.stop_signal); + displaced_step_finish (ecs->event_thread, + ecs->event_thread->suspend.stop_signal); had_step_over_info = step_over_info_valid_p (); diff --git a/gdb/infrun.h b/gdb/infrun.h index 8d8264a9c3..c276ca9aa6 100644 --- a/gdb/infrun.h +++ b/gdb/infrun.h @@ -261,69 +261,4 @@ extern void all_uis_check_sync_execution_done (void); started or re-started). */ extern void all_uis_on_sync_execution_starting (void); -/* Base class for displaced stepping closures (the arch-specific data). */ - -struct displaced_step_copy_insn_closure -{ - virtual ~displaced_step_copy_insn_closure () = 0; -}; - -using displaced_step_copy_insn_closure_up - = std::unique_ptr; - -/* A simple displaced step closure that contains only a byte buffer. */ - -struct buf_displaced_step_copy_insn_closure : displaced_step_copy_insn_closure -{ - buf_displaced_step_copy_insn_closure (int buf_size) - : buf (buf_size) - {} - - gdb::byte_vector buf; -}; - -/* Per-inferior displaced stepping state. */ -struct displaced_step_inferior_state -{ - displaced_step_inferior_state () - { - reset (); - } - - /* Put this object back in its original state. */ - void reset () - { - failed_before = 0; - step_thread = nullptr; - step_gdbarch = nullptr; - step_closure.reset (); - step_original = 0; - step_copy = 0; - step_saved_copy.clear (); - } - - /* True if preparing a displaced step ever failed. If so, we won't - try displaced stepping for this inferior again. */ - int failed_before; - - /* If this is not nullptr, this is the thread carrying out a - displaced single-step in process PID. This thread's state will - require fixing up once it has completed its step. */ - thread_info *step_thread; - - /* The architecture the thread had when we stepped it. */ - gdbarch *step_gdbarch; - - /* The closure provided gdbarch_displaced_step_copy_insn, to be used - for post-step cleanup. */ - displaced_step_copy_insn_closure_up step_closure; - - /* The address of the original instruction, and the copy we - made. */ - CORE_ADDR step_original, step_copy; - - /* Saved contents of copy area. */ - gdb::byte_vector step_saved_copy; -}; - #endif /* INFRUN_H */ diff --git a/gdb/testsuite/gdb.arch/amd64-disp-step-avx.exp b/gdb/testsuite/gdb.arch/amd64-disp-step-avx.exp index ab83fe6770..4888bf408c 100644 --- a/gdb/testsuite/gdb.arch/amd64-disp-step-avx.exp +++ b/gdb/testsuite/gdb.arch/amd64-disp-step-avx.exp @@ -97,7 +97,7 @@ proc disp_step_func { func } { gdb_test_no_output "set debug displaced on" gdb_test "continue" \ - "Continuing.*displaced: displaced pc to.*Breakpoint.*, ${test_end_label} ().*" \ + "Continuing.*displaced: prepared successfully.*Breakpoint.*, ${test_end_label} ().*" \ "continue to ${test_end_label}" gdb_test_no_output "set debug displaced off" diff --git a/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp b/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp index c4c9596be3..9a6507f764 100644 --- a/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp +++ b/gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp @@ -48,7 +48,7 @@ proc probe_displaced_stepping_support {} { # that breakpoint. gdb_test_no_output "set debug displaced 1" gdb_test_multiple "next" "probe" { - -re "displaced pc to.*$gdb_prompt $" { + -re "displaced: prepared successfully .*$gdb_prompt $" { pass "supported" } -re ".*$gdb_prompt $" { diff --git a/gdb/thread.c b/gdb/thread.c index 289cf24fee..7496da4e57 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -367,6 +367,20 @@ thread_info::deletable () const return refcount () == 0 && !is_current_thread (this); } +/* See gdbthread.h. */ +regcache * +thread_info::regcache () +{ + return get_thread_regcache (this); +} + +/* See gdbthread.h. */ +gdbarch * +thread_info::arch () +{ + return this->regcache ()-> arch (); +} + /* Add TP to the end of the step-over chain LIST_P. */ static void @@ -392,10 +406,10 @@ step_over_chain_enqueue (struct thread_info **list_p, struct thread_info *tp) } } -/* Remove TP from step-over chain LIST_P. */ +/* See gdbthread.h. */ -static void -step_over_chain_remove (struct thread_info **list_p, struct thread_info *tp) +void +thread_step_over_chain_remove (thread_info **list_p, thread_info *tp) { gdb_assert (tp->step_over_next != NULL); gdb_assert (tp->step_over_prev != NULL); @@ -415,12 +429,28 @@ step_over_chain_remove (struct thread_info **list_p, struct thread_info *tp) /* See gdbthread.h. */ -struct thread_info * -global_thread_step_over_chain_next (struct thread_info *tp) +void +global_thread_step_over_chain_remove (thread_info *tp) +{ + thread_step_over_chain_remove (&global_thread_step_over_chain_head, tp); +} + +/* See gdbthread.h. */ + +thread_info * +thread_step_over_chain_next (thread_info *chain_head, thread_info *tp) { - struct thread_info *next = tp->step_over_next; + thread_info *next = tp->step_over_next; - return (next == global_thread_step_over_chain_head ? NULL : next); + return next == chain_head ? NULL : next; +} + +/* See gdbthread.h. */ + +thread_info * +global_thread_step_over_chain_next (thread_info *tp) +{ + return thread_step_over_chain_next (global_thread_step_over_chain_head, tp); } /* See gdbthread.h. */ @@ -433,18 +463,33 @@ thread_is_in_step_over_chain (struct thread_info *tp) /* See gdbthread.h. */ -void -global_thread_step_over_chain_enqueue (struct thread_info *tp) +int thread_step_over_chain_length (thread_info *tp) { - step_over_chain_enqueue (&global_thread_step_over_chain_head, tp); + if (tp == nullptr) + return 0; + + int num = 1; + thread_info *iter = tp->step_over_next; + + while (iter != tp) + { + num++; + iter = iter->step_over_next; + } + + return num; + + } /* See gdbthread.h. */ void -global_thread_step_over_chain_remove (struct thread_info *tp) +global_thread_step_over_chain_enqueue (struct thread_info *tp) { - step_over_chain_remove (&global_thread_step_over_chain_head, tp); + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, "enqueueing thread %ld in global step over chain\n", tp->ptid.lwp()); + step_over_chain_enqueue (&global_thread_step_over_chain_head, tp); } /* Delete the thread referenced by THR. If SILENT, don't notify