#include "gdbcore.h"
#include "gdbcmd.h"
#include "target.h"
+#include "target-connection.h"
#include "gdbthread.h"
#include "annotate.h"
#include "symfile.h"
#include "arch-utils.h"
#include "gdbsupport/scope-exit.h"
#include "gdbsupport/forward-scope-exit.h"
-#include "gdb_select.h"
+#include "gdbsupport/gdb_select.h"
#include <unordered_map>
/* Prototypes for local functions */
static void sig_print_header (void);
-static int follow_fork (void);
-
-static int follow_fork_inferior (int follow_child, int detach_fork);
-
static void follow_inferior_reset_breakpoints (void);
static int currently_stepping (struct thread_info *tp);
the fork parent. At return inferior_ptid is the ptid of the
followed inferior. */
-static int
-follow_fork_inferior (int follow_child, int detach_fork)
+static bool
+follow_fork_inferior (bool follow_child, bool detach_fork)
{
int has_vforked;
ptid_t parent_ptid, child_ptid;
if the inferior should be resumed; false, if the target for some
reason decided it's best not to resume. */
-static int
-follow_fork (void)
+static bool
+follow_fork ()
{
- int follow_child = (follow_fork_mode_string == follow_fork_mode_child);
- int should_resume = 1;
+ bool follow_child = (follow_fork_mode_string == follow_fork_mode_child);
+ bool should_resume = true;
struct thread_info *tp;
/* Copy user stepping state to the new inferior thread. FIXME: the
happened. */
thread_info *wait_thread = find_thread_ptid (wait_target, wait_ptid);
switch_to_thread (wait_thread);
- should_resume = 0;
+ should_resume = false;
}
}
}
else
{
- struct program_space *pspace;
-
/* If this is a vfork child exiting, then the pspace and
aspaces were shared with the parent. Since we're
reporting the process exit, we'll be mourning all that is
scoped_restore restore_ptid
= make_scoped_restore (&inferior_ptid, null_ptid);
- /* This inferior is dead, so avoid giving the breakpoints
- module the option to write through to it (cloning a
- program space resets breakpoints). */
- inf->aspace = NULL;
- inf->pspace = NULL;
- pspace = new program_space (maybe_new_address_space ());
- set_current_program_space (pspace);
+ inf->pspace = new program_space (maybe_new_address_space ());
+ inf->aspace = inf->pspace->aspace;
+ set_current_program_space (inf->pspace);
inf->removable = 1;
inf->symfile_flags = SYMFILE_NO_READ;
- clone_program_space (pspace, vfork_parent->pspace);
- inf->pspace = pspace;
- inf->aspace = pspace->aspace;
+ clone_program_space (inf->pspace, vfork_parent->pspace);
resume_parent = vfork_parent->pid;
}
/* 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));
}
tp->inf->process_target ()->threads_executing = true;
- tp->resumed = 1;
+ tp->resumed = true;
/* FIXME: What should we do if we are supposed to resume this
thread with a signal? Maybe we should maintain a queue of
resume_ptid = internal_resume_ptid (user_step);
do_target_resume (resume_ptid, 0, GDB_SIGNAL_0);
- tp->resumed = 1;
+ tp->resumed = true;
return;
}
}
if (tp->control.trap_expected || bpstat_should_step ())
tp->control.may_range_step = 0;
- /* If enabled, step over breakpoints by executing a copy of the
- instruction at a different address.
+ /* If displaced stepping is enabled, step over breakpoints by executing a
+ copy of the instruction at a different address.
We can't use displaced stepping when we have a signal to deliver;
the comments for displaced_step_prepare explain why. The
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 ());
}
}
&& step_over_info_valid_p ())
{
/* If we have nested signals or a pending signal is delivered
- immediately after a handler returns, might might already have
+ immediately after a handler returns, might already have
a step-resume breakpoint set on the earlier handler. We cannot
set another step-resume breakpoint; just continue on until the
original breakpoint is hit. */
}
do_target_resume (resume_ptid, step, sig);
- tp->resumed = 1;
+ tp->resumed = true;
}
/* Resume the inferior. SIG is the signal to give the inferior
}
}
+/* Check that all the targets we're about to resume are in non-stop
+ mode. Ideally, we'd only care whether all targets support
+ target-async, but we're not there yet. E.g., stop_all_threads
+ doesn't know how to handle all-stop targets. Also, the remote
+ protocol in all-stop mode is synchronous, irrespective of
+ target-async, which means that things like a breakpoint re-set
+ triggered by one target would try to read memory from all targets
+ and fail. */
+
+static void
+check_multi_target_resumption (process_stratum_target *resume_target)
+{
+ if (!non_stop && resume_target == nullptr)
+ {
+ scoped_restore_current_thread restore_thread;
+
+ /* This is used to track whether we're resuming more than one
+ target. */
+ process_stratum_target *first_connection = nullptr;
+
+ /* The first inferior we see with a target that does not work in
+ always-non-stop mode. */
+ inferior *first_not_non_stop = nullptr;
+
+ for (inferior *inf : all_non_exited_inferiors (resume_target))
+ {
+ switch_to_inferior_no_thread (inf);
+
+ if (!target_has_execution)
+ continue;
+
+ process_stratum_target *proc_target
+ = current_inferior ()->process_target();
+
+ if (!target_is_non_stop_p ())
+ first_not_non_stop = inf;
+
+ if (first_connection == nullptr)
+ first_connection = proc_target;
+ else if (first_connection != proc_target
+ && first_not_non_stop != nullptr)
+ {
+ switch_to_inferior_no_thread (first_not_non_stop);
+
+ proc_target = current_inferior ()->process_target();
+
+ error (_("Connection %d (%s) does not support "
+ "multi-target resumption."),
+ proc_target->connection_number,
+ make_target_connection_string (proc_target).c_str ());
+ }
+ }
+ }
+}
+
/* Basic routine for continuing the program in various fashions.
ADDR is the address to resume at, or -1 for resume where stopped.
process_stratum_target *resume_target
= user_visible_resume_target (resume_ptid);
+ check_multi_target_resumption (resume_target);
+
if (addr == (CORE_ADDR) -1)
{
if (pc == cur_thr->suspend.stop_pc
inferior function, as in that case we pretend the inferior
doesn't run at all. */
if (!cur_thr->control.in_infcall)
- set_running (resume_target, resume_ptid, 1);
+ set_running (resume_target, resume_ptid, true);
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
{
switch_to_thread_no_regs (tp);
- if (!tp->inf->has_execution ())
- {
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: proceed: [%s] target has "
- "no execution\n",
- target_pid_to_str (tp->ptid).c_str ());
- continue;
- }
-
- if (tp->resumed)
- {
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: proceed: [%s] resumed\n",
- target_pid_to_str (tp->ptid).c_str ());
- gdb_assert (tp->executing || tp->suspend.waitstatus_pending_p);
- continue;
- }
+ if (!tp->inf->has_execution ())
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: proceed: [%s] target has "
+ "no execution\n",
+ target_pid_to_str (tp->ptid).c_str ());
+ continue;
+ }
- if (thread_is_in_step_over_chain (tp))
- {
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: proceed: [%s] needs step-over\n",
- target_pid_to_str (tp->ptid).c_str ());
- continue;
- }
+ if (tp->resumed)
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: proceed: [%s] resumed\n",
+ target_pid_to_str (tp->ptid).c_str ());
+ gdb_assert (tp->executing || tp->suspend.waitstatus_pending_p);
+ continue;
+ }
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: proceed: resuming %s\n",
- target_pid_to_str (tp->ptid).c_str ());
+ if (thread_is_in_step_over_chain (tp))
+ {
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: proceed: [%s] needs step-over\n",
+ target_pid_to_str (tp->ptid).c_str ());
+ continue;
+ }
- reset_ecs (ecs, tp);
- switch_to_thread (tp);
- keep_going_pass_signal (ecs);
- if (!ecs->wait_some_more)
- error (_("Command aborted."));
- }
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: proceed: resuming %s\n",
+ target_pid_to_str (tp->ptid).c_str ());
+
+ reset_ecs (ecs, tp);
+ switch_to_thread (tp);
+ keep_going_pass_signal (ecs);
+ if (!ecs->wait_some_more)
+ error (_("Command aborted."));
+ }
}
else if (!cur_thr->resumed && !thread_is_in_step_over_chain (cur_thr))
{
/* Otherwise we can process the (new) pending event now. Set
it so this pending event is considered by
do_target_wait. */
- tp->resumed = 1;
+ tp->resumed = true;
}
}
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);
int pass;
int iterations = 0;
- gdb_assert (target_is_non_stop_p ());
+ gdb_assert (exists_non_stop_target ());
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: stop_all_threads\n");
to tell the target to stop. */
for (thread_info *t : all_non_exited_threads ())
{
+ /* For a single-target setting with an all-stop target,
+ we would not even arrive here. For a multi-target
+ setting, until GDB is able to handle a mixture of
+ all-stop and non-stop targets, simply skip all-stop
+ targets' threads. This should be fine due to the
+ protection of 'check_multi_target_resumption'. */
+
+ switch_to_thread_no_regs (t);
+ if (!target_is_non_stop_p ())
+ continue;
+
if (t->executing)
{
/* If already stopping, don't request a stop again.
"infrun: %s executing, "
"need stop\n",
target_pid_to_str (t->ptid).c_str ());
- switch_to_thread_no_regs (t);
target_stop (t->ptid);
t->stop_requested = 1;
}
/* The thread may be not executing, but still be
resumed with a pending status to process. */
- t->resumed = 0;
+ t->resumed = false;
}
}
t->stop_requested = 0;
t->executing = 0;
- t->resumed = 0;
+ t->resumed = false;
t->control.may_range_step = 0;
/* This may be the first time we see the inferior report
else
mark_ptid = ecs->ptid;
- set_executing (ecs->target, mark_ptid, 0);
+ set_executing (ecs->target, mark_ptid, false);
/* Likewise the resumed flag. */
- set_resumed (ecs->target, mark_ptid, 0);
+ set_resumed (ecs->target, mark_ptid, false);
}
switch (ecs->ws.kind)
stop_waiting (ecs);
return;
- /* The following are the only cases in which we keep going;
- the above cases end in a continue or goto. */
case TARGET_WAITKIND_FORKED:
case TARGET_WAITKIND_VFORKED:
/* Check whether the inferior is displaced stepping. */
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.
watchpoints, for example, always appear in the bpstat. */
if (!bpstat_causes_stop (ecs->event_thread->control.stop_bpstat))
{
- int should_resume;
- int follow_child
+ bool follow_child
= (follow_fork_mode_string == follow_fork_mode_child);
ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
process_stratum_target *targ
= ecs->event_thread->inf->process_target ();
- should_resume = follow_fork ();
+ bool should_resume = follow_fork ();
/* Note that one of these may be an invalid pointer,
depending on detach_fork. */
"infrun: restart threads: "
"[%s] has pending status\n",
target_pid_to_str (tp->ptid).c_str ());
- tp->resumed = 1;
+ tp->resumed = true;
continue;
}
/* This was cleared early, by handle_inferior_event. Set it
so this pending event is considered by
do_target_wait. */
- tp->resumed = 1;
+ tp->resumed = true;
gdb_assert (!tp->executing);
been removed. */
if (random_signal && target_stopped_by_sw_breakpoint ())
{
- if (program_breakpoint_here_p (gdbarch,
- ecs->event_thread->suspend.stop_pc))
+ if (gdbarch_program_breakpoint_here_p (gdbarch,
+ ecs->event_thread->suspend.stop_pc))
{
struct regcache *regcache;
int decr_pc;
}
}
+ /* This always returns the sal for the inner-most frame when we are in a
+ stack of inlined frames, even if GDB actually believes that it is in a
+ more outer frame. This is checked for below by calls to
+ inline_skipped_frames. */
stop_pc_sal = find_pc_line (ecs->event_thread->suspend.stop_pc, 0);
/* NOTE: tausq/2004-05-24: This if block used to be done before all
return;
}
+ bool refresh_step_info = true;
if ((ecs->event_thread->suspend.stop_pc == stop_pc_sal.pc)
&& (ecs->event_thread->current_line != stop_pc_sal.line
|| ecs->event_thread->current_symtab != stop_pc_sal.symtab))
{
- /* We are at the start of a different line. So stop. Note that
- we don't stop if we step into the middle of a different line.
- That is said to make things like for (;;) statements work
- better. */
- if (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: stepped to a different line\n");
- end_stepping_range (ecs);
- return;
+ if (stop_pc_sal.is_stmt)
+ {
+ /* We are at the start of a different line. So stop. Note that
+ we don't stop if we step into the middle of a different line.
+ That is said to make things like for (;;) statements work
+ better. */
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepped to a different line\n");
+ end_stepping_range (ecs);
+ return;
+ }
+ else if (frame_id_eq (get_frame_id (get_current_frame ()),
+ ecs->event_thread->control.step_frame_id))
+ {
+ /* We are at the start of a different line, however, this line is
+ not marked as a statement, and we have not changed frame. We
+ ignore this line table entry, and continue stepping forward,
+ looking for a better place to stop. */
+ refresh_step_info = false;
+ if (debug_infrun)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: stepped to a different line, but "
+ "it's not the start of a statement\n");
+ }
}
/* We aren't done stepping.
Optimize by setting the stepping range to the line.
(We might not be in the original line, but if we entered a
new line in mid-statement, we continue stepping. This makes
- things like for(;;) statements work better.) */
+ things like for(;;) statements work better.)
+
+ If we entered a SAL that indicates a non-statement line table entry,
+ then we update the stepping range, but we don't update the step info,
+ which includes things like the line number we are stepping away from.
+ This means we will stop when we find a line table entry that is marked
+ as is-statement, even if it matches the non-statement one we just
+ stepped into. */
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);
+ if (refresh_step_info)
+ set_step_info (ecs->event_thread, frame, stop_pc_sal);
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: keep going\n");
get_frame_address_space (frame),
tp->suspend.stop_pc);
- tp->resumed = 1;
+ tp->resumed = true;
resume_ptid = internal_resume_ptid (tp->control.stepping_command);
do_target_resume (resume_ptid, 0, GDB_SIGNAL_0);
}
/* Let callers know we don't want to wait for the inferior anymore. */
ecs->wait_some_more = 0;
- /* If all-stop, but the target is always in non-stop mode, stop all
+ /* If all-stop, but there exists a non-stop target, stop all
threads now that we're presenting the stop to the user. */
- if (!non_stop && target_is_non_stop_p ())
+ if (!non_stop && exists_non_stop_target ())
stop_all_threads ();
}
inferior_event_handler (INF_REG_EVENT, NULL);
}
+void _initialize_infrun ();
void
-_initialize_infrun (void)
+_initialize_infrun ()
{
struct cmd_list_element *c;