/* If checking the mode of displaced instruction in copy area. */
if (displaced->step_thread != nullptr
&& displaced->step_copy == addr)
- return displaced->step_closure;
+ return displaced->step_closure.get ();
return NULL;
}
doesn't support it, GDB will instead use the traditional
hold-and-step approach. If AUTO (which is the default), GDB will
decide which technique to use to step over breakpoints depending on
- which of all-stop or non-stop mode is active --- displaced stepping
- in non-stop mode; hold-and-step in all-stop mode. */
+ whether the target works in a non-stop way (see use_displaced_stepping). */
static enum auto_boolean can_use_displaced_stepping = AUTO_BOOLEAN_AUTO;
"to step over breakpoints is %s.\n"), value);
}
+/* Return true if the gdbarch implements the required methods to use
+ displaced stepping. */
+
+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);
+}
+
/* Return non-zero if displaced stepping can/should be used to step
over breakpoints of thread TP. */
-static int
-use_displaced_stepping (struct thread_info *tp)
+static bool
+use_displaced_stepping (thread_info *tp)
{
- struct regcache *regcache = get_thread_regcache (tp);
- struct gdbarch *gdbarch = regcache->arch ();
+ /* If the user disabled it explicitly, don't use displaced stepping. */
+ if (can_use_displaced_stepping == AUTO_BOOLEAN_FALSE)
+ return false;
+
+ /* If "auto", only use displaced stepping if the target operates in a non-stop
+ way. */
+ if (can_use_displaced_stepping == AUTO_BOOLEAN_AUTO
+ && !target_is_non_stop_p ())
+ return false;
+
+ gdbarch *gdbarch = get_thread_regcache (tp)->arch ();
+
+ /* If the architecture doesn't implement displaced stepping, don't use
+ it. */
+ if (!gdbarch_supports_displaced_stepping (gdbarch))
+ return false;
+
+ /* If recording, don't use displaced stepping. */
+ if (find_record_target () != nullptr)
+ return false;
+
displaced_step_inferior_state *displaced_state
= get_displaced_stepping_state (tp->inf);
- return (((can_use_displaced_stepping == AUTO_BOOLEAN_AUTO
- && target_is_non_stop_p ())
- || can_use_displaced_stepping == AUTO_BOOLEAN_TRUE)
- && gdbarch_displaced_step_copy_insn_p (gdbarch)
- && find_record_target () == NULL
- && !displaced_state->failed_before);
+ /* If displaced stepping failed before for this inferior, don't bother trying
+ again. */
+ if (displaced_state->failed_before)
+ return false;
+
+ return true;
}
-/* Clean out any stray displaced stepping state. */
+/* Simple function wrapper around displaced_step_inferior_state::reset. */
+
static void
-displaced_step_clear (struct displaced_step_inferior_state *displaced)
+displaced_step_reset (displaced_step_inferior_state *displaced)
{
- /* Indicate that there is no cleanup pending. */
- displaced->step_thread = nullptr;
-
- delete displaced->step_closure;
- displaced->step_closure = NULL;
+ displaced->reset ();
}
-/* A cleanup that wraps displaced_step_clear. */
-using displaced_step_clear_cleanup
- = FORWARD_SCOPE_EXIT (displaced_step_clear);
+/* A cleanup that wraps displaced_step_reset. We use this instead of, say,
+ SCOPE_EXIT, because it needs to be discardable with "cleanup.release ()". */
+
+using displaced_step_reset_cleanup = FORWARD_SCOPE_EXIT (displaced_step_reset);
/* Dump LEN bytes at BUF in hex to FILE, followed by a newline. */
void
const address_space *aspace = regcache->aspace ();
CORE_ADDR original, copy;
ULONGEST len;
- struct displaced_step_closure *closure;
int status;
/* We should never reach this function if the architecture does not
support displaced stepping. */
- gdb_assert (gdbarch_displaced_step_copy_insn_p (gdbarch));
+ gdb_assert (gdbarch_supports_displaced_stepping (gdbarch));
/* Nor if the thread isn't meant to step over a breakpoint. */
gdb_assert (tp->control.trap_expected);
target_pid_to_str (tp->ptid).c_str ());
}
- displaced_step_clear (displaced);
+ displaced_step_reset (displaced);
scoped_restore_current_thread restore_thread;
len);
};
- closure = gdbarch_displaced_step_copy_insn (gdbarch,
- original, copy, regcache);
- if (closure == NULL)
+ 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
succeeds. */
displaced->step_thread = tp;
displaced->step_gdbarch = gdbarch;
- displaced->step_closure = closure;
displaced->step_original = original;
displaced->step_copy = copy;
{
- displaced_step_clear_cleanup cleanup (displaced);
+ displaced_step_reset_cleanup cleanup (displaced);
/* Resume execution at the copy. */
regcache_write_pc (regcache, copy);
if (displaced->step_thread != event_thread)
return 0;
- displaced_step_clear_cleanup cleanup (displaced);
+ displaced_step_reset_cleanup cleanup (displaced);
displaced_step_restore (displaced, displaced->step_thread->ptid);
{
/* Fix up the resulting state. */
gdbarch_displaced_step_fixup (displaced->step_gdbarch,
- displaced->step_closure,
+ displaced->step_closure.get (),
displaced->step_original,
displaced->step_copy,
get_thread_regcache (displaced->step_thread));
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);
+ step = gdbarch_displaced_step_hw_singlestep
+ (gdbarch, displaced->step_closure.get ());
}
}
ptid_t event_ptid;
struct thread_info *tp;
+ /* We know that we are looking for an event in the target of inferior
+ INF, but we don't know which thread the event might come from. As
+ such we want to make sure that INFERIOR_PTID is reset so that none of
+ the wait code relies on it - doing so is always a mistake. */
+ switch_to_inferior_no_thread (inf);
+
/* First check if there is a resumed thread with a wait status
pending. */
if (ptid == minus_one_ptid || ptid.is_pid ())
auto do_wait = [&] (inferior *inf)
{
- switch_to_inferior_no_thread (inf);
-
ecs->ptid = do_target_wait_1 (inf, wait_ptid, &ecs->ws, options);
ecs->target = inf->process_target ();
return (ecs->ws.kind != TARGET_WAITKIND_IGNORE);
printf_unfiltered (_("completed.\n"));
}
-/* Record the frame and location we're currently stepping through. */
+/* See infrun.h. */
+
void
-set_step_info (struct frame_info *frame, struct symtab_and_line sal)
+set_step_info (thread_info *tp, struct frame_info *frame,
+ struct symtab_and_line sal)
{
- struct thread_info *tp = inferior_thread ();
+ /* This can be removed once this function no longer implicitly relies on the
+ inferior_ptid value. */
+ gdb_assert (inferior_ptid == tp->ptid);
tp->control.step_frame_id = get_frame_id (frame);
tp->control.step_stack_frame_id = get_stack_frame_id (frame);
struct regcache *child_regcache;
CORE_ADDR parent_pc;
+ if (ecs->ws.kind == TARGET_WAITKIND_FORKED)
+ {
+ 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);
+ }
+
/* GDB has got TARGET_WAITKIND_FORKED or TARGET_WAITKIND_VFORKED,
indicating that the displaced stepping of syscall instruction
has been done. Perform cleanup for parent process here. Note
that needs it. */
start_step_over ();
- if (ecs->ws.kind == TARGET_WAITKIND_FORKED)
- {
- 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);
- }
-
/* Since the vfork/fork syscall instruction was executed in the scratchpad,
the child's PC is also within the scratchpad. Set the child's PC
to the parent's PC value, which has already been fixed up.
ecs->event_thread->control.step_range_start = stop_pc_sal.pc;
ecs->event_thread->control.step_range_end = stop_pc_sal.end;
ecs->event_thread->control.may_range_step = 1;
- set_step_info (frame, stop_pc_sal);
+ set_step_info (ecs->event_thread, frame, stop_pc_sal);
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");