aboutsummaryrefslogtreecommitdiff
path: root/gdb/infrun.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/infrun.c')
-rw-r--r--gdb/infrun.c335
1 files changed, 143 insertions, 192 deletions
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 9ac75ef..e7b69ae 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1452,21 +1452,6 @@ step_over_info_valid_p (void)
displaced step operation on it. See displaced_step_prepare and
displaced_step_finish for details. */
-/* Returns true if any inferior has a thread doing a displaced
- step. */
-
-static bool
-displaced_step_in_progress_any_inferior ()
-{
- for (inferior *i : all_inferiors ())
- {
- if (i->displaced_step_state.step_thread != nullptr)
- return true;
- }
-
- return false;
-}
-
/* Return true if THREAD is doing a displaced step. */
static bool
@@ -1474,7 +1459,7 @@ displaced_step_in_progress_thread (thread_info *thread)
{
gdb_assert (thread != NULL);
- return thread->inf->displaced_step_state.step_thread == thread;
+ return thread->displaced_step_state.in_progress ();
}
/* Return true if INF has a thread doing a displaced step. */
@@ -1482,25 +1467,21 @@ displaced_step_in_progress_thread (thread_info *thread)
static bool
displaced_step_in_progress (inferior *inf)
{
- return inf->displaced_step_state.step_thread != nullptr;
+ return inf->displaced_step_state.in_progress_count > 0;
}
-/* If inferior is in displaced stepping, and ADDR equals to starting address
- of copy area, return corresponding displaced_step_copy_insn_closure.
- Otherwise, return NULL. */
+/* Return true if any thread is doing a displaced step. */
-displaced_step_copy_insn_closure *
-get_displaced_step_copy_insn_closure_by_addr (CORE_ADDR addr)
+static bool
+displaced_step_in_progress_any_thread ()
{
- displaced_step_inferior_state &displaced
- = current_inferior ()->displaced_step_state;
-
- /* If checking the mode of displaced instruction in copy area. */
- if (displaced.step_thread != nullptr
- && displaced.step_copy == addr)
- return displaced.step_closure.get ();
+ for (inferior *inf : all_non_exited_inferiors ())
+ {
+ if (displaced_step_in_progress (inf))
+ return true;
+ }
- return NULL;
+ return false;
}
static void
@@ -1512,12 +1493,15 @@ infrun_inferior_exit (struct inferior *inf)
static void
infrun_inferior_execd (inferior *inf)
{
- /* If a thread was doing a displaced step in this inferior at the moment of
- the exec, it no longer exists. Even if the exec'ing thread was the one
+ /* If some threads where was doing a displaced step in this inferior at the
+ moment of the exec, they no longer exist. Even if the exec'ing thread
doing a displaced step, we don't want to to any fixup nor restore displaced
stepping buffer bytes. */
inf->displaced_step_state.reset ();
+ for (thread_info *thread : inf->threads ())
+ thread->displaced_step_state.reset ();
+
/* Since an in-line step is done with everything else stopped, if there was
one in progress at the time of the exec, it must have been the exec'ing
thread. */
@@ -1555,9 +1539,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. */
- return gdbarch_displaced_step_copy_insn_p (arch);
+ /* Only check for the presence of `prepare`. The gdbarch verification ensures
+ that if `prepare` is provided, so is `finish`. */
+ return gdbarch_displaced_step_prepare_p (arch);
}
/* Return non-zero if displaced stepping can/should be used to step
@@ -1595,10 +1579,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 ();
}
@@ -1649,10 +1633,8 @@ 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 &disp_step_thread_state
+ = tp->displaced_step_state;
/* We should never reach this function if the architecture does not
support displaced stepping. */
@@ -1667,15 +1649,14 @@ 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. */
-
- displaced_step_inferior_state *displaced = &tp->inf->displaced_step_state;
+ /* We are about to start a displaced step for this thread. If one is already
+ in progress, something's wrong. */
+ gdb_assert (!disp_step_thread_state.in_progress ());
- if (displaced->step_thread != nullptr)
+ if (tp->inf->displaced_step_state.unavailable)
{
- /* Already waiting for a displaced step to finish. Defer this
- request and place in queue. */
+ /* The gdbarch tells us it's not worth asking to try a prepare because
+ it is likely that it will return unavailable, so don't bother asking. */
displaced_debug_printf ("deferring step of %s",
target_pid_to_str (tp->ptid).c_str ());
@@ -1683,79 +1664,54 @@ displaced_step_prepare_throw (thread_info *tp)
global_thread_step_over_chain_enqueue (tp);
return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE;
}
- else
- displaced_debug_printf ("stepping %s now",
- target_pid_to_str (tp->ptid).c_str ());
- displaced_step_reset (displaced);
+ displaced_debug_printf ("displaced-stepping %s now",
+ target_pid_to_str (tp->ptid).c_str ());
scoped_restore_current_thread restore_thread;
switch_to_thread (tp);
- original = regcache_read_pc (regcache);
+ CORE_ADDR original_pc = regcache_read_pc (regcache);
+ CORE_ADDR displaced_pc;
- copy = gdbarch_displaced_step_location (gdbarch);
- len = gdbarch_max_insn_length (gdbarch);
+ displaced_step_prepare_status status
+ = gdbarch_displaced_step_prepare (gdbarch, tp, displaced_pc);
- if (breakpoint_in_range_p (aspace, copy, len))
+ if (status == DISPLACED_STEP_PREPARE_STATUS_CANT)
{
- /* 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. */
- displaced_debug_printf ("breakpoint set in scratch pad. "
- "Stepping over breakpoint in-line instead.");
+ displaced_debug_printf ("failed to prepare (%s)",
+ target_pid_to_str (tp->ptid).c_str ());
return DISPLACED_STEP_PREPARE_STATUS_CANT;
}
+ else if (status == DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE)
+ {
+ /* Not enough displaced stepping resources available, defer this
+ request by placing it the queue. */
- /* 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));
+ displaced_debug_printf ("not enough resources available, "
+ "deferring step of %s",
+ target_pid_to_str (tp->ptid).c_str ());
- displaced_debug_printf ("saved %s: %s",
- paddress (gdbarch, copy),
- displaced_step_dump_bytes
- (displaced->step_saved_copy.data (), len).c_str ());
+ 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 DISPLACED_STEP_PREPARE_STATUS_CANT;
+ return DISPLACED_STEP_PREPARE_STATUS_UNAVAILABLE;
}
+ gdb_assert (status == DISPLACED_STEP_PREPARE_STATUS_OK);
+
/* 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);
+ disp_step_thread_state.set (gdbarch);
- /* Resume execution at the copy. */
- regcache_write_pc (regcache, copy);
+ tp->inf->displaced_step_state.in_progress_count++;
- cleanup.release ();
- }
-
- displaced_debug_printf ("displaced pc to %s", paddress (gdbarch, copy));
+ displaced_debug_printf ("prepared successfully thread=%s, "
+ "original_pc=%s, displaced_pc=%s",
+ target_pid_to_str (tp->ptid).c_str (),
+ paddress (gdbarch, original_pc),
+ paddress (gdbarch, displaced_pc));
return DISPLACED_STEP_PREPARE_STATUS_OK;
}
@@ -1797,33 +1753,6 @@ displaced_step_prepare (thread_info *thread)
return status;
}
-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);
-
- displaced_debug_printf ("restored %s %s",
- target_pid_to_str (ptid).c_str (),
- paddress (displaced->step_gdbarch,
- displaced->step_copy));
-}
-
/* If we displaced stepped an instruction successfully, adjust registers and
memory to yield the same effect the instruction would have had if we had
executed it at its original address, and return
@@ -1836,13 +1765,15 @@ displaced_step_restore (struct displaced_step_inferior_state *displaced,
static displaced_step_finish_status
displaced_step_finish (thread_info *event_thread, enum gdb_signal signal)
{
- displaced_step_inferior_state *displaced
- = &event_thread->inf->displaced_step_state;
+ displaced_step_thread_state *displaced = &event_thread->displaced_step_state;
- /* 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 DISPLACED_STEP_FINISH_STATUS_OK;
+ gdb_assert (event_thread->inf->displaced_step_state.in_progress_count > 0);
+ event_thread->inf->displaced_step_state.in_progress_count--;
+
/* 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
@@ -1851,35 +1782,10 @@ displaced_step_finish (thread_info *event_thread, enum gdb_signal signal)
displaced_step_reset_cleanup cleanup (displaced);
- displaced_step_restore (displaced, displaced->step_thread->ptid);
-
- /* 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));
-
- return DISPLACED_STEP_FINISH_STATUS_OK;
- }
- 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);
-
- return DISPLACED_STEP_FINISH_STATUS_NOT_EXECUTED;
- }
+ /* Do the fixup, and release the resources acquired to do the displaced
+ step. */
+ return gdbarch_displaced_step_finish (displaced->get_original_gdbarch (),
+ event_thread, signal);
}
/* Data to be passed around while handling an event. This data is
@@ -1927,14 +1833,42 @@ static step_over_what thread_still_needs_step_over (struct thread_info *tp);
static bool
start_step_over (void)
{
- struct thread_info *tp, *next;
+ thread_info *next;
/* 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 false;
- for (tp = global_thread_step_over_chain_head; tp != NULL; tp = next)
+ /* Steal the global thread step over chain. As we try to initiate displaced
+ steps, threads will be enqueued in the global chain if no buffers are
+ available. If we iterated on the global chain directly, we might iterate
+ indefinitely. */
+ thread_info *threads_to_step = global_thread_step_over_chain_head;
+ global_thread_step_over_chain_head = NULL;
+
+ infrun_debug_printf ("stealing global queue of threads to step, length = %d",
+ thread_step_over_chain_length (threads_to_step));
+
+ bool started = false;
+
+ /* On scope exit (whatever the reason, return or exception), if there are
+ threads left in the THREADS_TO_STEP chain, put back these threads in the
+ global list. */
+ SCOPE_EXIT
+ {
+ if (threads_to_step == nullptr)
+ infrun_debug_printf ("step-over queue now empty");
+ else
+ {
+ infrun_debug_printf ("putting back %d threads to step in global queue",
+ thread_step_over_chain_length (threads_to_step));
+
+ global_thread_step_over_chain_enqueue_chain (threads_to_step);
+ }
+ };
+
+ for (thread_info *tp = threads_to_step; tp != NULL; tp = next)
{
struct execution_control_state ecss;
struct execution_control_state *ecs = &ecss;
@@ -1943,12 +1877,23 @@ start_step_over (void)
gdb_assert (!tp->stop_requested);
- next = global_thread_step_over_chain_next (tp);
+ next = thread_step_over_chain_next (threads_to_step, 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;
+ if (tp->inf->displaced_step_state.unavailable)
+ {
+ /* The arch told us to not even try preparing another displaced step
+ for this inferior. Just leave the thread in THREADS_TO_STEP, it
+ will get moved to the global chain on scope exit. */
+ continue;
+ }
+
+ /* Remove thread from the THREADS_TO_STEP chain. If anything goes wrong
+ while we try to prepare the displaced step, we don't add it back to
+ the global step over chain. This is to avoid a thread staying in the
+ step over chain indefinitely if something goes wrong when resuming it
+ If the error is intermittent and it still needs a step over, it will
+ get enqueued again when we try to resume it normally. */
+ thread_step_over_chain_remove (&threads_to_step, tp);
step_what = thread_still_needs_step_over (tp);
must_be_in_line = ((step_what & STEP_OVER_WATCHPOINT)
@@ -1958,13 +1903,11 @@ 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 false;
-
- global_thread_step_over_chain_remove (tp);
-
- if (global_thread_step_over_chain_head == NULL)
- infrun_debug_printf ("step-over queue now empty");
+ if (must_be_in_line && displaced_step_in_progress_any_thread ())
+ {
+ global_thread_step_over_chain_enqueue (tp);
+ continue;
+ }
if (tp->control.trap_expected
|| tp->resumed
@@ -1998,13 +1941,27 @@ 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 because no buffers
+ were available, it was re-added to the global step over chain. */
+ if (tp->resumed)
+ {
+ infrun_debug_printf ("[%s] was resumed.",
+ target_pid_to_str (tp->ptid).c_str ());
+ gdb_assert (!thread_is_in_step_over_chain (tp));
+ }
+ else
+ {
+ infrun_debug_printf ("[%s] was NOT resumed.",
+ 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 true;
+ started = true;
+ break;
}
if (!target_is_non_stop_p ())
@@ -2017,7 +1974,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 true;
+ started = true;
+ break;
}
/* Either the thread no longer needed a step-over, or a new
@@ -2026,7 +1984,7 @@ start_step_over (void)
displaced step on a thread of other process. */
}
- return false;
+ return started;
}
/* Update global variables holding ptids to hold NEW_PTID if they were
@@ -3611,18 +3569,16 @@ prepare_for_detach (void)
struct inferior *inf = current_inferior ();
ptid_t pid_ptid = ptid_t (inf->pid);
- displaced_step_inferior_state *displaced = &inf->displaced_step_state;
-
/* 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_debug_printf ("displaced-stepping in-process while detaching");
scoped_restore restore_detaching = make_scoped_restore (&inf->detaching, true);
- while (displaced->step_thread != nullptr)
+ while (displaced_step_in_progress (inf))
{
struct execution_control_state ecss;
struct execution_control_state *ecs;
@@ -5288,17 +5244,12 @@ handle_inferior_event (struct execution_control_state *ecs)
struct gdbarch *gdbarch = regcache->arch ();
inferior *parent_inf = find_inferior_ptid (ecs->target, ecs->ptid);
- /* If this is a fork (child gets its own address space copy) and the
- displaced step buffer was in use at the time of the fork, restore
- displaced step buffer bytes in the child process. */
+ /* If this is a fork (child gets its own address space copy) and some
+ displaced step buffers were in use at the time of the fork, restore
+ the displaced step buffer bytes in the child process. */
if (ecs->ws.kind == TARGET_WAITKIND_FORKED)
- {
- displaced_step_inferior_state *displaced
- = &parent_inf->displaced_step_state;
-
- if (displaced->step_thread != nullptr)
- displaced_step_restore (displaced, ecs->ws.value.related_pid);
- }
+ gdbarch_displaced_step_restore_all_in_ptid
+ (gdbarch, parent_inf, ecs->ws.value.related_pid);
/* If displaced stepping is supported, and thread ecs->ptid is
displaced stepping. */