gdb: move displaced stepping logic to gdbarch, allow starting concurrent displaced...
authorSimon Marchi <simon.marchi@efficios.com>
Tue, 21 Jan 2020 21:08:01 +0000 (16:08 -0500)
committerSimon Marchi <simon.marchi@efficios.com>
Thu, 4 Jun 2020 19:04:21 +0000 (15:04 -0400)
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.

15 files changed:
gdb/Makefile.in
gdb/amd64-linux-tdep.c
gdb/amd64-tdep.c
gdb/displaced-stepping.c [new file with mode: 0644]
gdb/displaced-stepping.h [new file with mode: 0644]
gdb/gdbarch.c
gdb/gdbarch.h
gdb/gdbarch.sh
gdb/gdbthread.h
gdb/i386-linux-tdep.c
gdb/infrun.c
gdb/infrun.h
gdb/testsuite/gdb.arch/amd64-disp-step-avx.exp
gdb/testsuite/gdb.threads/forking-threads-plus-breakpoint.exp
gdb/thread.c

index 32d0eee7c6376d0f82aa671560a8cc9ed55c2a3e..db54f73b778fdfa427c49db02b49a6b8685f826f 100644 (file)
@@ -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 \
index 44ecb4e0b3382a547509fd5afb6040352d0d7e74..937ec8359684c59b1a86f905f5fce35dbed70525 100644 (file)
@@ -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>
+  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);
index b28756dbb3660287d8fa0db0dee35fe95f9c45a4..2a0dfed0cd9e650e46c6e2b19bcfce26caca8a00 100644 (file)
@@ -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>
+  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 (file)
index 0000000..cbf2425
--- /dev/null
@@ -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 (file)
index 0000000..0cb2d8c
--- /dev/null
@@ -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>
+  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 */
index 87007f3cb293cb8ac7fb6edc5a54693f12c86966..e7d94a3b47a13e9a56abb352add596a4cf8524d7 100644 (file)
@@ -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
index 7f3b50d889a5d2b1a284820484c7203dc3522fd0..a87030416705f655ae1c17746f98e54288ae18a9 100644 (file)
@@ -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
index 32b9b1b1c4a87f828ee743d63ab6a0c779076d97..10332ea2824043af78a753ed2830da151f4a878b 100755 (executable)
@@ -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 <<EOF
 #include "gdb_obstack.h"
 #include "infrun.h"
 #include "osabi.h"
+#include "displaced-stepping.h"
 
 struct floatformat;
 struct ui_file;
index 4f956f19b21cd26b2610476abc0e6bdab0db1b77..95ba32fcc60122f4401596a46b21d422e3ffe255 100644 (file)
@@ -32,6 +32,7 @@ struct symtab;
 #include "gdbsupport/refcounted-object.h"
 #include "gdbsupport/common-gdbthread.h"
 #include "gdbsupport/forward-scope-exit.h"
+#include "displaced-stepping.h"
 
 struct inferior;
 struct process_stratum_target;
@@ -252,6 +253,9 @@ public:
   /* Mark this thread as running and notify observers.  */
   void set_running (bool running);
 
+  struct regcache *regcache ();
+  struct gdbarch *arch ();
+
   struct thread_info *next = NULL;
   ptid_t ptid;                 /* "Actual process id";
                                    In fact, this may be overloaded with 
@@ -406,6 +410,8 @@ public:
      fields point to self.  */
   struct thread_info *step_over_prev = NULL;
   struct thread_info *step_over_next = NULL;
+
+  displaced_step_thread_state displaced_step_state;
 };
 
 /* A gdb::ref_ptr pointer to a thread_info.  */
@@ -752,19 +758,34 @@ extern bool value_in_thread_stack_temporaries (struct value *,
 
 extern void global_thread_step_over_chain_enqueue (struct thread_info *tp);
 
+/* Remove TP from step-over chain LIST_P.  */
+
+extern void thread_step_over_chain_remove (thread_info **list_p,
+                                          thread_info *tp);
+
 /* Remove TP from the global pending step-over chain.  */
 
-extern void global_thread_step_over_chain_remove (struct thread_info *tp);
+extern void global_thread_step_over_chain_remove (thread_info *tp);
 
-/* Return the next thread in the global step-over chain.  NULL
-   if TP is the last entry in the chain.  */
+/* Return the next thread in the step-over chain whose head is CHAIN_HEAD.
+   Return NULL if TP is the last entry in the chain.  */
 
-extern struct thread_info *global_thread_step_over_chain_next (struct thread_info *tp);
+extern thread_info *thread_step_over_chain_next (thread_info *chain_head,
+                                                thread_info *tp);
+
+/* Return the next thread in the global step-over chain.  Return NULL if TP is
+   the last entry in the chain.  */
+
+extern thread_info *global_thread_step_over_chain_next (thread_info *tp);
 
 /* Return true if TP is in any step-over chain.  */
 
 extern int thread_is_in_step_over_chain (struct thread_info *tp);
 
+/* Return the length of the the step over chain TP is in.  */
+
+extern int thread_step_over_chain_length (thread_info *tp);
+
 /* Cancel any ongoing execution command.  */
 
 extern void thread_cancel_execution_command (struct thread_info *thr);
index ae1189cee5bdf4476b688c90724a50f2e5e80af3..21f403a9a2a2c88bceb6c4d8cc3a27cc4316d22e 100644 (file)
@@ -18,6 +18,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #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);
index c615522eb0a94d7cba9d539a55f64171c8fd6aff..17c342be50971433ad82a36e84be7dad2f8a97f2 100644 (file)
@@ -19,6 +19,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #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 ();
 
index 8d8264a9c3f24b8d248b8f37ee9963c40bfb0b6e..c276ca9aa6bf8b8bcf9675ccd19aff958881a52d 100644 (file)
@@ -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<displaced_step_copy_insn_closure>;
-
-/* 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 */
index ab83fe6770a52afbada61a79677a20f2d8b80c6a..4888bf408cc44f1017fa3317bb00e1977a7ca3dc 100644 (file)
@@ -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"
index c4c9596be3129ebd8a3482029f29328bf9c3acf3..9a6507f7645ddf812195a0b603234b7420ae724c 100644 (file)
@@ -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 $" {
index 289cf24feece5aeca2f5f3b9f83ea071ca7f54b3..7496da4e571b87d9d47e1f99ffcb0c1077cdbf07 100644 (file)
@@ -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
This page took 0.04797 seconds and 4 git commands to generate.