/* Target-struct-independent code to start (run) and stop an inferior
process.
- Copyright (C) 1986-2019 Free Software Foundation, Inc.
+ Copyright (C) 1986-2020 Free Software Foundation, Inc.
This file is part of GDB.
#include "frame.h"
#include "inferior.h"
#include "breakpoint.h"
-#include "common/gdb_wait.h"
#include "gdbcore.h"
#include "gdbcmd.h"
-#include "cli/cli-script.h"
#include "target.h"
+#include "target-connection.h"
#include "gdbthread.h"
#include "annotate.h"
#include "symfile.h"
#include "top.h"
-#include <signal.h>
#include "inf-loop.h"
#include "regcache.h"
#include "value.h"
#include "language.h"
#include "solib.h"
#include "main.h"
-#include "dictionary.h"
#include "block.h"
#include "mi/mi-common.h"
#include "event-top.h"
#include "inline-frame.h"
#include "jit.h"
#include "tracepoint.h"
-#include "continuations.h"
-#include "interps.h"
#include "skip.h"
#include "probe.h"
#include "objfiles.h"
#include "solist.h"
#include "event-loop.h"
#include "thread-fsm.h"
-#include "common/enum-flags.h"
+#include "gdbsupport/enum-flags.h"
#include "progspace-and-thread.h"
-#include "common/gdb_optional.h"
+#include "gdbsupport/gdb_optional.h"
#include "arch-utils.h"
-#include "common/scope-exit.h"
-#include "common/forward-scope-exit.h"
+#include "gdbsupport/scope-exit.h"
+#include "gdbsupport/forward-scope-exit.h"
+#include "gdb_select.h"
+#include <unordered_map>
/* Prototypes for local functions */
static int currently_stepping (struct thread_info *tp);
-void nullify_last_target_wait_ptid (void);
-
static void insert_hp_step_resume_breakpoint_at_frame (struct frame_info *);
static void insert_step_resume_breakpoint_at_caller (struct frame_info *);
static void resume (gdb_signal sig);
+static void wait_for_inferior (inferior *inf);
+
/* Asynchronous signal handler registered as event loop source for
when we have pending events ready to be passed to the core. */
static struct async_event_handler *infrun_async_inferior_event_token;
/* When set, stop the 'step' command if we enter a function which has
no line number information. The normal behavior is that we step
over such function. */
-int step_stop_if_no_debug = 0;
+bool step_stop_if_no_debug = false;
static void
show_step_stop_if_no_debug (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
Exactly which branch is detached depends on 'set follow-fork-mode'
setting. */
-static int detach_fork = 1;
+static bool detach_fork = true;
-int debug_displaced = 0;
+bool debug_displaced = false;
static void
show_debug_displaced (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
/* Support for disabling address space randomization. */
-int disable_randomization = 1;
+bool disable_randomization = true;
static void
show_disable_randomization (struct ui_file *file, int from_tty,
/* User interface for non-stop mode. */
-int non_stop = 0;
-static int non_stop_1 = 0;
+bool non_stop = false;
+static bool non_stop_1 = false;
static void
set_non_stop (const char *args, int from_tty,
non-stop, in which all GDB operations that might affect the
target's execution have been disabled. */
-int observer_mode = 0;
-static int observer_mode_1 = 0;
+bool observer_mode = false;
+static bool observer_mode_1 = false;
static void
set_observer_mode (const char *args, int from_tty,
/* We can insert fast tracepoints in or out of observer mode,
but enable them if we're going into this mode. */
if (observer_mode)
- may_insert_fast_tracepoints = 1;
+ may_insert_fast_tracepoints = true;
may_stop = !observer_mode;
update_target_permissions ();
if (observer_mode)
{
pagination_enabled = 0;
- non_stop = non_stop_1 = 1;
+ non_stop = non_stop_1 = true;
}
if (from_tty)
void
update_observer_mode (void)
{
- int newval;
-
- newval = (!may_insert_breakpoints
- && !may_insert_tracepoints
- && may_insert_fast_tracepoints
- && !may_stop
- && non_stop);
+ bool newval = (!may_insert_breakpoints
+ && !may_insert_tracepoints
+ && may_insert_fast_tracepoints
+ && !may_stop
+ && non_stop);
/* Let the user know if things change. */
if (newval != observer_mode)
static int stop_print_frame;
-/* This is a cached copy of the pid/waitstatus of the last event
- returned by target_wait()/deprecated_target_wait_hook(). This
- information is returned by get_last_target_status(). */
+/* This is a cached copy of the target/ptid/waitstatus of the last
+ event returned by target_wait()/deprecated_target_wait_hook().
+ This information is returned by get_last_target_status(). */
+static process_stratum_target *target_last_proc_target;
static ptid_t target_last_wait_ptid;
static struct target_waitstatus target_last_waitstatus;
Can not resume the parent process over vfork in the foreground while\n\
holding the child stopped. Try \"set detach-on-fork\" or \
\"set schedule-multiple\".\n"));
- /* FIXME output string > 80 columns. */
return 1;
}
scoped_restore_current_pspace_and_thread restore_pspace_thread;
- inferior_ptid = child_ptid;
- add_thread_silent (inferior_ptid);
set_current_inferior (child_inf);
+ switch_to_no_thread ();
child_inf->symfile_flags = SYMFILE_NO_READ;
+ push_target (parent_inf->process_target ());
+ add_thread_silent (child_inf->process_target (), child_ptid);
+ inferior_ptid = child_ptid;
/* If this is a vfork child, then the address-space is
shared with the parent. */
child_inf->pspace = parent_inf->pspace;
child_inf->aspace = parent_inf->aspace;
+ exec_on_vfork ();
+
/* The parent will be frozen until the child is done
with the shared region. Keep track of the
parent. */
parent_pspace = parent_inf->pspace;
- /* If we're vforking, we want to hold on to the parent until the
- child exits or execs. At child exec or exit time we can
- remove the old breakpoints from the parent and detach or
- resume debugging it. Otherwise, detach the parent now; we'll
- want to reuse it's program/address spaces, but we can't set
- them to the child before removing breakpoints from the
- parent, otherwise, the breakpoints module could decide to
- remove breakpoints from the wrong process (since they'd be
- assigned to the same address space). */
+ process_stratum_target *target = parent_inf->process_target ();
- if (has_vforked)
- {
- gdb_assert (child_inf->vfork_parent == NULL);
- gdb_assert (parent_inf->vfork_child == NULL);
- child_inf->vfork_parent = parent_inf;
- child_inf->pending_detach = 0;
- parent_inf->vfork_child = child_inf;
- parent_inf->pending_detach = detach_fork;
- parent_inf->waiting_for_vfork_done = 0;
- }
- else if (detach_fork)
- {
- if (print_inferior_events)
- {
- /* Ensure that we have a process ptid. */
- ptid_t process_ptid = ptid_t (parent_ptid.pid ());
+ {
+ /* Hold a strong reference to the target while (maybe)
+ detaching the parent. Otherwise detaching could close the
+ target. */
+ auto target_ref = target_ops_ref::new_reference (target);
+
+ /* If we're vforking, we want to hold on to the parent until
+ the child exits or execs. At child exec or exit time we
+ can remove the old breakpoints from the parent and detach
+ or resume debugging it. Otherwise, detach the parent now;
+ we'll want to reuse it's program/address spaces, but we
+ can't set them to the child before removing breakpoints
+ from the parent, otherwise, the breakpoints module could
+ decide to remove breakpoints from the wrong process (since
+ they'd be assigned to the same address space). */
+
+ if (has_vforked)
+ {
+ gdb_assert (child_inf->vfork_parent == NULL);
+ gdb_assert (parent_inf->vfork_child == NULL);
+ child_inf->vfork_parent = parent_inf;
+ child_inf->pending_detach = 0;
+ parent_inf->vfork_child = child_inf;
+ parent_inf->pending_detach = detach_fork;
+ parent_inf->waiting_for_vfork_done = 0;
+ }
+ else if (detach_fork)
+ {
+ if (print_inferior_events)
+ {
+ /* Ensure that we have a process ptid. */
+ ptid_t process_ptid = ptid_t (parent_ptid.pid ());
+
+ target_terminal::ours_for_output ();
+ fprintf_filtered (gdb_stdlog,
+ _("[Detaching after fork from "
+ "parent %s]\n"),
+ target_pid_to_str (process_ptid).c_str ());
+ }
- target_terminal::ours_for_output ();
- fprintf_filtered (gdb_stdlog,
- _("[Detaching after fork from "
- "parent %s]\n"),
- target_pid_to_str (process_ptid).c_str ());
- }
+ target_detach (parent_inf, 0);
+ parent_inf = NULL;
+ }
- target_detach (parent_inf, 0);
- }
+ /* Note that the detach above makes PARENT_INF dangling. */
- /* Note that the detach above makes PARENT_INF dangling. */
+ /* Add the child thread to the appropriate lists, and switch
+ to this new thread, before cloning the program space, and
+ informing the solib layer about this new process. */
- /* Add the child thread to the appropriate lists, and switch to
- this new thread, before cloning the program space, and
- informing the solib layer about this new process. */
+ set_current_inferior (child_inf);
+ push_target (target);
+ }
+ add_thread_silent (target, child_ptid);
inferior_ptid = child_ptid;
- add_thread_silent (inferior_ptid);
- set_current_inferior (child_inf);
/* If this is a vfork child, then the address-space is shared
with the parent. If we detached from the parent, then we can
{
child_inf->pspace = parent_pspace;
child_inf->aspace = child_inf->pspace->aspace;
+
+ exec_on_vfork ();
}
else
{
if (!non_stop)
{
+ process_stratum_target *wait_target;
ptid_t wait_ptid;
struct target_waitstatus wait_status;
/* Get the last target status returned by target_wait(). */
- get_last_target_status (&wait_ptid, &wait_status);
+ get_last_target_status (&wait_target, &wait_ptid, &wait_status);
/* If not stopped at a fork event, then there's nothing else to
do. */
/* Check if we switched over from WAIT_PTID, since the event was
reported. */
if (wait_ptid != minus_one_ptid
- && inferior_ptid != wait_ptid)
+ && (current_inferior ()->process_target () != wait_target
+ || inferior_ptid != wait_ptid))
{
/* We did. Switch back to WAIT_PTID thread, to tell the
target to follow it (in either direction). We'll
afterwards refuse to resume, and inform the user what
happened. */
- thread_info *wait_thread
- = find_thread_ptid (wait_ptid);
+ thread_info *wait_thread = find_thread_ptid (wait_target, wait_ptid);
switch_to_thread (wait_thread);
should_resume = 0;
}
parent = inferior_ptid;
child = tp->pending_follow.value.related_pid;
+ process_stratum_target *parent_targ = tp->inf->process_target ();
/* Set up inferior(s) as specified by the caller, and tell the
target to do whatever is necessary to follow either parent
or child. */
or another. The previous selected thread may be gone
from the lists by now, but if it is still around, need
to clear the pending follow request. */
- tp = find_thread_ptid (parent);
+ tp = find_thread_ptid (parent_targ, parent);
if (tp)
tp->pending_follow.kind = TARGET_WAITKIND_SPURIOUS;
/* If we followed the child, switch to it... */
if (follow_child)
{
- thread_info *child_thr = find_thread_ptid (child);
+ thread_info *child_thr = find_thread_ptid (parent_targ, child);
switch_to_thread (child_thr);
/* ... and preserve the stepping state, in case the
int resume_parent = -1;
/* This exec or exit marks the end of the shared memory region
- between the parent and the child. If the user wanted to
- detach from the parent, now is the time. */
+ between the parent and the child. Break the bonds. */
+ inferior *vfork_parent = inf->vfork_parent;
+ inf->vfork_parent->vfork_child = NULL;
+ inf->vfork_parent = NULL;
- if (inf->vfork_parent->pending_detach)
+ /* If the user wanted to detach from the parent, now is the
+ time. */
+ if (vfork_parent->pending_detach)
{
struct thread_info *tp;
struct program_space *pspace;
/* follow-fork child, detach-on-fork on. */
- inf->vfork_parent->pending_detach = 0;
+ vfork_parent->pending_detach = 0;
gdb::optional<scoped_restore_exited_inferior>
maybe_restore_inferior;
maybe_restore_thread.emplace ();
/* We're letting loose of the parent. */
- tp = any_live_thread_of_inferior (inf->vfork_parent);
+ tp = any_live_thread_of_inferior (vfork_parent);
switch_to_thread (tp);
/* We're about to detach from the parent, which implicitly
if (print_inferior_events)
{
std::string pidstr
- = target_pid_to_str (ptid_t (inf->vfork_parent->pid));
+ = target_pid_to_str (ptid_t (vfork_parent->pid));
target_terminal::ours_for_output ();
}
}
- target_detach (inf->vfork_parent, 0);
+ target_detach (vfork_parent, 0);
/* Put it back. */
inf->pspace = pspace;
inf->removable = 1;
set_current_program_space (inf->pspace);
- resume_parent = inf->vfork_parent->pid;
-
- /* Break the bonds. */
- inf->vfork_parent->vfork_child = NULL;
+ resume_parent = vfork_parent->pid;
}
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, inf->vfork_parent->pspace);
- inf->pspace = pspace;
- inf->aspace = pspace->aspace;
+ clone_program_space (inf->pspace, vfork_parent->pspace);
- resume_parent = inf->vfork_parent->pid;
- /* Break the bonds. */
- inf->vfork_parent->vfork_child = NULL;
+ resume_parent = vfork_parent->pid;
}
- inf->vfork_parent = NULL;
-
gdb_assert (current_program_space == inf->pspace);
if (non_stop && resume_parent != -1)
/* EXEC_FILE_TARGET is assumed to be non-NULL. */
static void
-follow_exec (ptid_t ptid, char *exec_file_target)
+follow_exec (ptid_t ptid, const char *exec_file_target)
{
struct inferior *inf = current_inferior ();
int pid = ptid.pid ();
And, we DON'T want to call delete_breakpoints() here, since
that may write the bp's "shadow contents" (the instruction
- value that was overwritten witha TRAP instruction). Since
+ value that was overwritten with a TRAP instruction). Since
we now have a new a.out, those shadow contents aren't valid. */
mark_breakpoints_out ();
inf->pid = pid;
target_follow_exec (inf, exec_file_target);
- set_current_inferior (inf);
- set_current_program_space (inf->pspace);
- add_thread (ptid);
+ inferior *org_inferior = current_inferior ();
+ switch_to_inferior_no_thread (inf);
+ push_target (org_inferior->process_target ());
+ thread_info *thr = add_thread (inf->process_target (), ptid);
+ switch_to_thread (thr);
}
else
{
register contents, and memory. We use this in step n1.
- gdbarch_displaced_step_fixup adjusts registers and memory after
- we have successfuly single-stepped the instruction, to yield the
+ we have successfully single-stepped the instruction, to yield the
same effect the instruction would have had if we had executed it
at its original address. We use this in step n3.
{
prepared = displaced_step_prepare_throw (thread);
}
- catch (const gdb_exception_RETURN_MASK_ERROR &ex)
+ catch (const gdb_exception_error &ex)
{
struct displaced_step_inferior_state *displaced_state;
if (ex.error != MEMORY_ERROR
&& ex.error != NOT_SUPPORTED_ERROR)
- throw_exception (ex);
+ throw;
if (debug_infrun)
{
discarded between events. */
struct execution_control_state
{
+ process_stratum_target *target;
ptid_t ptid;
/* The thread that got the event, if this was a thread event; NULL
otherwise. */
/* True if execution commands resume all threads of all processes by
default; otherwise, resume only threads of the current inferior
process. */
-int sched_multi = 0;
+bool sched_multi = false;
/* Try to setup for software single stepping over the specified location.
Return 1 if target_resume() should use hardware single step.
return resume_ptid;
}
+/* See infrun.h. */
+
+process_stratum_target *
+user_visible_resume_target (ptid_t resume_ptid)
+{
+ return (resume_ptid == minus_one_ptid && sched_multi
+ ? NULL
+ : current_inferior ()->process_target ());
+}
+
/* Return a ptid representing the set of threads that we will resume,
in the perspective of the target, assuming run control handling
does not require leaving some threads stopped (e.g., stepping past
target_resume (resume_ptid, step, sig);
target_commit_resume ();
+
+ if (target_can_async_p ())
+ target_async (1);
}
/* Resume the inferior. SIG is the signal to give the inferior
currently_stepping (tp));
}
+ tp->inf->process_target ()->threads_executing = true;
tp->resumed = 1;
/* FIXME: What should we do if we are supposed to resume this
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
&& 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. */
{
resume_1 (sig);
}
- catch (const gdb_exception_RETURN_MASK_ALL &ex)
+ catch (const gdb_exception &ex)
{
/* If resuming is being aborted for any reason, delete any
single-step breakpoint resume_1 may have created, to avoid
we're running in non-stop mode. */
if (inferior_ptid != null_ptid)
delete_single_step_breakpoints (inferior_thread ());
- throw_exception (ex);
+ throw;
}
}
if (!non_stop && inferior_ptid != null_ptid)
{
ptid_t resume_ptid = user_visible_resume_ptid (step);
+ process_stratum_target *resume_target
+ = user_visible_resume_target (resume_ptid);
/* In all-stop mode, delete the per-thread status of all threads
we're about to resume, implicitly and explicitly. */
- for (thread_info *tp : all_non_exited_threads (resume_ptid))
+ for (thread_info *tp : all_non_exited_threads (resume_target, resume_ptid))
clear_proceed_status_thread (tp);
}
execution_direction)));
}
+/* Calls target_commit_resume on all targets. */
+
+static void
+commit_resume_all_targets ()
+{
+ scoped_restore_current_thread restore_thread;
+
+ /* Map between process_target and a representative inferior. This
+ is to avoid committing a resume in the same target more than
+ once. Resumptions must be idempotent, so this is an
+ optimization. */
+ std::unordered_map<process_stratum_target *, inferior *> conn_inf;
+
+ for (inferior *inf : all_non_exited_inferiors ())
+ if (inf->has_execution ())
+ conn_inf[inf->process_target ()] = inf;
+
+ for (const auto &ci : conn_inf)
+ {
+ inferior *inf = ci.second;
+ switch_to_inferior_no_thread (inf);
+ target_commit_resume ();
+ }
+}
+
+/* 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.
struct regcache *regcache;
struct gdbarch *gdbarch;
CORE_ADDR pc;
- ptid_t resume_ptid;
struct execution_control_state ecss;
struct execution_control_state *ecs = &ecss;
int started;
gdb_assert (!thread_is_in_step_over_chain (cur_thr));
+ ptid_t resume_ptid
+ = user_visible_resume_ptid (cur_thr->control.stepping_command);
+ 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
if (siggnal != GDB_SIGNAL_DEFAULT)
cur_thr->suspend.stop_signal = siggnal;
- resume_ptid = user_visible_resume_ptid (cur_thr->control.stepping_command);
-
/* If an exception is thrown from this point on, make sure to
propagate GDB's knowledge of the executing state to the
frontend/user running state. */
- scoped_finish_thread_state finish_state (resume_ptid);
+ scoped_finish_thread_state finish_state (resume_target, resume_ptid);
/* Even if RESUME_PTID is a wildcard, and we end up resuming fewer
threads (e.g., we might need to set threads stepping over
inferior function, as in that case we pretend the inferior
doesn't run at all. */
if (!cur_thr->control.in_infcall)
- set_running (resume_ptid, 1);
+ set_running (resume_target, resume_ptid, 1);
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog,
threads. */
if (!non_stop && !schedlock_applies (cur_thr))
{
- for (thread_info *tp : all_non_exited_threads (resume_ptid))
+ for (thread_info *tp : all_non_exited_threads (resume_target,
+ resume_ptid))
{
+ switch_to_thread_no_regs (tp);
+
/* Ignore the current thread here. It's handled
afterwards. */
if (tp == cur_thr)
thread_step_over_chain_enqueue (tp);
}
+
+ switch_to_thread (cur_thr);
}
/* Enqueue the current thread last, so that we move all other
{
/* In all-stop, but the target is always in non-stop mode.
Start all other threads that are implicitly resumed too. */
- for (thread_info *tp : all_non_exited_threads (resume_ptid))
- {
- 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;
- }
+ for (thread_info *tp : all_non_exited_threads (resume_target,
+ resume_ptid))
+ {
+ switch_to_thread_no_regs (tp);
- 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->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 (debug_infrun)
- fprintf_unfiltered (gdb_stdlog,
- "infrun: proceed: resuming %s\n",
- target_pid_to_str (tp->ptid).c_str ());
+ 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;
+ }
- reset_ecs (ecs, tp);
- switch_to_thread (tp);
- keep_going_pass_signal (ecs);
- if (!ecs->wait_some_more)
- error (_("Command aborted."));
- }
+ 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 (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))
{
}
}
- target_commit_resume ();
+ commit_resume_all_targets ();
finish_state.release ();
+ /* If we've switched threads above, switch back to the previously
+ current thread. We don't want the user to see a different
+ selected thread. */
+ switch_to_thread (cur_thr);
+
/* Tell the event loop to wait for it to stop. If the target
supports asynchronous execution, it'll do this from within
target_resume. */
void
start_remote (int from_tty)
{
- struct inferior *inferior;
-
- inferior = current_inferior ();
- inferior->control.stop_soon = STOP_QUIETLY_REMOTE;
+ inferior *inf = current_inferior ();
+ inf->control.stop_soon = STOP_QUIETLY_REMOTE;
/* Always go on waiting for the target, regardless of the mode. */
/* FIXME: cagney/1999-09-23: At present it isn't possible to
target_open() return to the caller an indication that the target
is currently running and GDB state should be set to the same as
for an async run. */
- wait_for_inferior ();
+ wait_for_inferior (inf);
/* Now that the inferior has stopped, do any bookkeeping like
loading shared libraries. We want to do this before normal_stop,
clear_proceed_status (0);
- target_last_wait_ptid = minus_one_ptid;
+ nullify_last_target_wait_ptid ();
previous_inferior_ptid = inferior_ptid;
}
static void
infrun_thread_stop_requested (ptid_t ptid)
{
+ process_stratum_target *curr_target = current_inferior ()->process_target ();
+
/* PTID was requested to stop. If the thread was already stopped,
but the user/frontend doesn't know about that yet (e.g., the
thread had been temporarily paused for some step-over), set up
for reporting the stop now. */
- for (thread_info *tp : all_threads (ptid))
+ for (thread_info *tp : all_threads (curr_target, ptid))
{
if (tp->state != THREAD_RUNNING)
continue;
/* Clear the inline-frame state, since we're re-processing the
stop. */
- clear_inline_frame_state (tp->ptid);
+ clear_inline_frame_state (tp);
/* If this thread was paused because some other thread was
doing an inline-step over, let that finish first. Once
static void
infrun_thread_thread_exit (struct thread_info *tp, int silent)
{
- if (target_last_wait_ptid == tp->ptid)
+ if (target_last_proc_target == tp->inf->process_target ()
+ && target_last_wait_ptid == tp->ptid)
nullify_last_target_wait_ptid ();
}
had events. */
static struct thread_info *
-random_pending_event_thread (ptid_t waiton_ptid)
+random_pending_event_thread (inferior *inf, ptid_t waiton_ptid)
{
int num_events = 0;
- auto has_event = [] (thread_info *tp)
+ auto has_event = [&] (thread_info *tp)
{
- return (tp->resumed
+ return (tp->ptid.matches (waiton_ptid)
+ && tp->resumed
&& tp->suspend.waitstatus_pending_p);
};
/* First see how many events we have. Count only resumed threads
that have an event pending. */
- for (thread_info *tp : all_non_exited_threads (waiton_ptid))
+ for (thread_info *tp : inf->non_exited_threads ())
if (has_event (tp))
num_events++;
num_events, random_selector);
/* Select the Nth thread that has had an event. */
- for (thread_info *tp : all_non_exited_threads (waiton_ptid))
+ for (thread_info *tp : inf->non_exited_threads ())
if (has_event (tp))
if (random_selector-- == 0)
return tp;
/* Wrapper for target_wait that first checks whether threads have
pending statuses to report before actually asking the target for
- more events. */
+ more events. INF is the inferior we're using to call target_wait
+ on. */
static ptid_t
-do_target_wait (ptid_t ptid, struct target_waitstatus *status, int options)
+do_target_wait_1 (inferior *inf, ptid_t ptid,
+ target_waitstatus *status, int options)
{
ptid_t event_ptid;
struct thread_info *tp;
pending. */
if (ptid == minus_one_ptid || ptid.is_pid ())
{
- tp = random_pending_event_thread (ptid);
+ tp = random_pending_event_thread (inf, ptid);
}
else
{
target_pid_to_str (ptid).c_str ());
/* We have a specific thread to check. */
- tp = find_thread_ptid (ptid);
+ tp = find_thread_ptid (inf, ptid);
gdb_assert (tp != NULL);
if (!tp->suspend.waitstatus_pending_p)
tp = NULL;
return event_ptid;
}
+/* Returns true if INF has any resumed thread with a status
+ pending. */
+
+static bool
+threads_are_resumed_pending_p (inferior *inf)
+{
+ for (thread_info *tp : inf->non_exited_threads ())
+ if (tp->resumed
+ && tp->suspend.waitstatus_pending_p)
+ return true;
+
+ return false;
+}
+
+/* Wrapper for target_wait that first checks whether threads have
+ pending statuses to report before actually asking the target for
+ more events. Polls for events from all inferiors/targets. */
+
+static bool
+do_target_wait (ptid_t wait_ptid, execution_control_state *ecs, int options)
+{
+ int num_inferiors = 0;
+ int random_selector;
+
+ /* For fairness, we pick the first inferior/target to poll at
+ random, and then continue polling the rest of the inferior list
+ starting from that one in a circular fashion until the whole list
+ is polled once. */
+
+ auto inferior_matches = [&wait_ptid] (inferior *inf)
+ {
+ return (inf->process_target () != NULL
+ && (threads_are_executing (inf->process_target ())
+ || threads_are_resumed_pending_p (inf))
+ && ptid_t (inf->pid).matches (wait_ptid));
+ };
+
+ /* First see how many resumed inferiors we have. */
+ for (inferior *inf : all_inferiors ())
+ if (inferior_matches (inf))
+ num_inferiors++;
+
+ if (num_inferiors == 0)
+ {
+ ecs->ws.kind = TARGET_WAITKIND_IGNORE;
+ return false;
+ }
+
+ /* Now randomly pick an inferior out of those that were resumed. */
+ random_selector = (int)
+ ((num_inferiors * (double) rand ()) / (RAND_MAX + 1.0));
+
+ if (debug_infrun && num_inferiors > 1)
+ fprintf_unfiltered (gdb_stdlog,
+ "infrun: Found %d inferiors, starting at #%d\n",
+ num_inferiors, random_selector);
+
+ /* Select the Nth inferior that was resumed. */
+
+ inferior *selected = nullptr;
+
+ for (inferior *inf : all_inferiors ())
+ if (inferior_matches (inf))
+ if (random_selector-- == 0)
+ {
+ selected = inf;
+ break;
+ }
+
+ /* Now poll for events out of each of the resumed inferior's
+ targets, starting from the selected one. */
+
+ 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);
+ };
+
+ /* Needed in all-stop+target-non-stop mode, because we end up here
+ spuriously after the target is all stopped and we've already
+ reported the stop to the user, polling for events. */
+ scoped_restore_current_thread restore_thread;
+
+ int inf_num = selected->num;
+ for (inferior *inf = selected; inf != NULL; inf = inf->next)
+ if (inferior_matches (inf))
+ if (do_wait (inf))
+ return true;
+
+ for (inferior *inf = inferior_list;
+ inf != NULL && inf->num < inf_num;
+ inf = inf->next)
+ if (inferior_matches (inf))
+ if (do_wait (inf))
+ return true;
+
+ ecs->ws.kind = TARGET_WAITKIND_IGNORE;
+ return false;
+}
+
/* Prepare and stabilize the inferior for detaching it. E.g.,
detaching while a thread is displaced stepping is a recipe for
crashing it, as nothing would readjust the PC out of the scratch
don't get any event. */
target_dcache_invalidate ();
- ecs->ptid = do_target_wait (pid_ptid, &ecs->ws, 0);
+ do_target_wait (pid_ptid, ecs, 0);
if (debug_infrun)
print_target_wait_results (pid_ptid, ecs->ptid, &ecs->ws);
/* If an error happens while handling the event, propagate GDB's
knowledge of the executing state to the frontend/user running
state. */
- scoped_finish_thread_state finish_state (minus_one_ptid);
+ scoped_finish_thread_state finish_state (inf->process_target (),
+ minus_one_ptid);
/* Now figure out what to do with the result of the result. */
handle_inferior_event (ecs);
When this function actually returns it means the inferior
should be left stopped and GDB should read more commands. */
-void
-wait_for_inferior (void)
+static void
+wait_for_inferior (inferior *inf)
{
if (debug_infrun)
fprintf_unfiltered
/* If an error happens while handling the event, propagate GDB's
knowledge of the executing state to the frontend/user running
state. */
- scoped_finish_thread_state finish_state (minus_one_ptid);
+ scoped_finish_thread_state finish_state
+ (inf->process_target (), minus_one_ptid);
while (1)
{
struct execution_control_state ecss;
struct execution_control_state *ecs = &ecss;
- ptid_t waiton_ptid = minus_one_ptid;
memset (ecs, 0, sizeof (*ecs));
don't get any event. */
target_dcache_invalidate ();
- ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws, 0);
+ ecs->ptid = do_target_wait_1 (inf, minus_one_ptid, &ecs->ws, 0);
+ ecs->target = inf->process_target ();
if (debug_infrun)
- print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
+ print_target_wait_results (minus_one_ptid, ecs->ptid, &ecs->ws);
/* Now figure out what to do with the result of the result. */
handle_inferior_event (ecs);
struct execution_control_state ecss;
struct execution_control_state *ecs = &ecss;
int cmd_done = 0;
- ptid_t waiton_ptid = minus_one_ptid;
memset (ecs, 0, sizeof (*ecs));
set_current_traceframe (-1);
}
- gdb::optional<scoped_restore_current_thread> maybe_restore_thread;
-
- if (non_stop)
- /* In non-stop mode, the user/frontend should not notice a thread
- switch due to internal events. Make sure we reverse to the
- user selected thread and frame after handling the event and
- running any breakpoint commands. */
- maybe_restore_thread.emplace ();
+ /* The user/frontend should not notice a thread switch due to
+ internal events. Make sure we revert to the user selected
+ thread and frame after handling the event and running any
+ breakpoint commands. */
+ scoped_restore_current_thread restore_thread;
overlay_cache_invalid = 1;
/* Flush target cache before starting to handle each event. Target
= make_scoped_restore (&execution_direction,
target_execution_direction ());
- ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws,
- target_can_async_p () ? TARGET_WNOHANG : 0);
+ if (!do_target_wait (minus_one_ptid, ecs, TARGET_WNOHANG))
+ return;
+
+ gdb_assert (ecs->ws.kind != TARGET_WAITKIND_IGNORE);
+
+ /* Switch to the target that generated the event, so we can do
+ target calls. Any inferior bound to the target will do, so we
+ just switch to the first we find. */
+ for (inferior *inf : all_inferiors (ecs->target))
+ {
+ switch_to_inferior_no_thread (inf);
+ break;
+ }
if (debug_infrun)
- print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
+ print_target_wait_results (minus_one_ptid, ecs->ptid, &ecs->ws);
/* If an error happens while handling the event, propagate GDB's
knowledge of the executing state to the frontend/user running
state. */
ptid_t finish_ptid = !target_is_non_stop_p () ? minus_one_ptid : ecs->ptid;
- scoped_finish_thread_state finish_state (finish_ptid);
+ scoped_finish_thread_state finish_state (ecs->target, finish_ptid);
/* Get executed before scoped_restore_current_thread above to apply
still for the thread which has thrown the exception. */
if (!ecs->wait_some_more)
{
- struct inferior *inf = find_inferior_ptid (ecs->ptid);
+ struct inferior *inf = find_inferior_ptid (ecs->target, ecs->ptid);
int should_stop = 1;
struct thread_info *thr = ecs->event_thread;
inferior_event_handler (INF_EXEC_COMPLETE, NULL);
cmd_done = 1;
}
+
+ /* If we got a TARGET_WAITKIND_NO_RESUMED event, then the
+ previously selected thread is gone. We have two
+ choices - switch to no thread selected, or restore the
+ previously selected thread (now exited). We chose the
+ later, just because that's what GDB used to do. After
+ this, "info threads" says "The current thread <Thread
+ ID 2> has terminated." instead of "No thread
+ selected.". */
+ if (!non_stop
+ && cmd_done
+ && ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED)
+ restore_thread.dont_restore ();
}
}
tss->step_after_step_resume_breakpoint = 0;
}
-/* Set the cached copy of the last ptid/waitstatus. */
+/* See infrun.h. */
void
-set_last_target_status (ptid_t ptid, struct target_waitstatus status)
+set_last_target_status (process_stratum_target *target, ptid_t ptid,
+ target_waitstatus status)
{
+ target_last_proc_target = target;
target_last_wait_ptid = ptid;
target_last_waitstatus = status;
}
-/* Return the cached copy of the last pid/waitstatus returned by
- target_wait()/deprecated_target_wait_hook(). The data is actually
- cached by handle_inferior_event(), which gets called immediately
- after target_wait()/deprecated_target_wait_hook(). */
+/* See infrun.h. */
void
-get_last_target_status (ptid_t *ptidp, struct target_waitstatus *status)
+get_last_target_status (process_stratum_target **target, ptid_t *ptid,
+ target_waitstatus *status)
{
- *ptidp = target_last_wait_ptid;
- *status = target_last_waitstatus;
+ if (target != nullptr)
+ *target = target_last_proc_target;
+ if (ptid != nullptr)
+ *ptid = target_last_wait_ptid;
+ if (status != nullptr)
+ *status = target_last_waitstatus;
}
+/* See infrun.h. */
+
void
nullify_last_target_wait_ptid (void)
{
+ target_last_proc_target = nullptr;
target_last_wait_ptid = minus_one_ptid;
+ target_last_waitstatus = {};
}
/* Switch thread contexts. */
{
if (debug_infrun
&& ecs->ptid != inferior_ptid
- && ecs->event_thread != inferior_thread ())
+ && (inferior_ptid == null_ptid
+ || ecs->event_thread != inferior_thread ()))
{
fprintf_unfiltered (gdb_stdlog, "infrun: Switching context from %s ",
target_pid_to_str (inferior_ptid).c_str ());
return 0;
}
+/* Look for an inline frame that is marked for skip.
+ If PREV_FRAME is TRUE start at the previous frame,
+ otherwise start at the current frame. Stop at the
+ first non-inline frame, or at the frame where the
+ step started. */
+
+static bool
+inline_frame_is_marked_for_skip (bool prev_frame, struct thread_info *tp)
+{
+ struct frame_info *frame = get_current_frame ();
+
+ if (prev_frame)
+ frame = get_prev_frame (frame);
+
+ for (; frame != NULL; frame = get_prev_frame (frame))
+ {
+ const char *fn = NULL;
+ symtab_and_line sal;
+ struct symbol *sym;
+
+ if (frame_id_eq (get_frame_id (frame), tp->control.step_frame_id))
+ break;
+ if (get_frame_type (frame) != INLINE_FRAME)
+ break;
+
+ sal = find_frame_sal (frame);
+ sym = get_frame_function (frame);
+
+ if (sym != NULL)
+ fn = sym->print_name ();
+
+ if (sal.line != 0
+ && function_name_is_marked_for_skip (fn, sal))
+ return true;
+ }
+
+ return false;
+}
+
/* If the event thread has the stop requested flag set, pretend it
stopped for a GDB_SIGNAL_0 (i.e., as if it stopped due to
target_stop). */
{
if (!ecs->stop_func_filled_in)
{
+ const block *block;
+
/* Don't care about return value; stop_func_start and stop_func_name
will both be 0 if it doesn't work. */
- find_function_entry_range_from_pc (ecs->event_thread->suspend.stop_pc,
- &ecs->stop_func_name,
- &ecs->stop_func_start,
- &ecs->stop_func_end);
- ecs->stop_func_start
- += gdbarch_deprecated_function_start_offset (gdbarch);
-
- if (gdbarch_skip_entrypoint_p (gdbarch))
- ecs->stop_func_start = gdbarch_skip_entrypoint (gdbarch,
- ecs->stop_func_start);
+ find_pc_partial_function (ecs->event_thread->suspend.stop_pc,
+ &ecs->stop_func_name,
+ &ecs->stop_func_start,
+ &ecs->stop_func_end,
+ &block);
+
+ /* The call to find_pc_partial_function, above, will set
+ stop_func_start and stop_func_end to the start and end
+ of the range containing the stop pc. If this range
+ contains the entry pc for the block (which is always the
+ case for contiguous blocks), advance stop_func_start past
+ the function's start offset and entrypoint. Note that
+ stop_func_start is NOT advanced when in a range of a
+ non-contiguous block that does not contain the entry pc. */
+ if (block != nullptr
+ && ecs->stop_func_start <= BLOCK_ENTRY_PC (block)
+ && BLOCK_ENTRY_PC (block) < ecs->stop_func_end)
+ {
+ ecs->stop_func_start
+ += gdbarch_deprecated_function_start_offset (gdbarch);
+
+ if (gdbarch_skip_entrypoint_p (gdbarch))
+ ecs->stop_func_start
+ = gdbarch_skip_entrypoint (gdbarch, ecs->stop_func_start);
+ }
ecs->stop_func_filled_in = 1;
}
static enum stop_kind
get_inferior_stop_soon (execution_control_state *ecs)
{
- struct inferior *inf = find_inferior_ptid (ecs->ptid);
+ struct inferior *inf = find_inferior_ptid (ecs->target, ecs->ptid);
gdb_assert (inf != NULL);
return inf->control.stop_soon;
}
-/* Wait for one event. Store the resulting waitstatus in WS, and
- return the event ptid. */
+/* Poll for one event out of the current target. Store the resulting
+ waitstatus in WS, and return the event ptid. Does not block. */
static ptid_t
-wait_one (struct target_waitstatus *ws)
+poll_one_curr_target (struct target_waitstatus *ws)
{
ptid_t event_ptid;
- ptid_t wait_ptid = minus_one_ptid;
overlay_cache_invalid = 1;
target_dcache_invalidate ();
if (deprecated_target_wait_hook)
- event_ptid = deprecated_target_wait_hook (wait_ptid, ws, 0);
+ event_ptid = deprecated_target_wait_hook (minus_one_ptid, ws, TARGET_WNOHANG);
else
- event_ptid = target_wait (wait_ptid, ws, 0);
+ event_ptid = target_wait (minus_one_ptid, ws, TARGET_WNOHANG);
if (debug_infrun)
- print_target_wait_results (wait_ptid, event_ptid, ws);
+ print_target_wait_results (minus_one_ptid, event_ptid, ws);
return event_ptid;
}
+/* An event reported by wait_one. */
+
+struct wait_one_event
+{
+ /* The target the event came out of. */
+ process_stratum_target *target;
+
+ /* The PTID the event was for. */
+ ptid_t ptid;
+
+ /* The waitstatus. */
+ target_waitstatus ws;
+};
+
+/* Wait for one event out of any target. */
+
+static wait_one_event
+wait_one ()
+{
+ while (1)
+ {
+ for (inferior *inf : all_inferiors ())
+ {
+ process_stratum_target *target = inf->process_target ();
+ if (target == NULL
+ || !target->is_async_p ()
+ || !target->threads_executing)
+ continue;
+
+ switch_to_inferior_no_thread (inf);
+
+ wait_one_event event;
+ event.target = target;
+ event.ptid = poll_one_curr_target (&event.ws);
+
+ if (event.ws.kind == TARGET_WAITKIND_NO_RESUMED)
+ {
+ /* If nothing is resumed, remove the target from the
+ event loop. */
+ target_async (0);
+ }
+ else if (event.ws.kind != TARGET_WAITKIND_IGNORE)
+ return event;
+ }
+
+ /* Block waiting for some event. */
+
+ fd_set readfds;
+ int nfds = 0;
+
+ FD_ZERO (&readfds);
+
+ for (inferior *inf : all_inferiors ())
+ {
+ process_stratum_target *target = inf->process_target ();
+ if (target == NULL
+ || !target->is_async_p ()
+ || !target->threads_executing)
+ continue;
+
+ int fd = target->async_wait_fd ();
+ FD_SET (fd, &readfds);
+ if (nfds <= fd)
+ nfds = fd + 1;
+ }
+
+ if (nfds == 0)
+ {
+ /* No waitable targets left. All must be stopped. */
+ return {NULL, minus_one_ptid, {TARGET_WAITKIND_NO_RESUMED}};
+ }
+
+ QUIT;
+
+ int numfds = interruptible_select (nfds, &readfds, 0, NULL, 0);
+ if (numfds < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ else
+ perror_with_name ("interruptible_select");
+ }
+ }
+}
+
/* Generate a wrapper for target_stopped_by_REASON that works on PTID
instead of the current thread. */
#define THREAD_STOPPED_BY(REASON) \
/* Save the thread's event and stop reason to process it later. */
static void
-save_waitstatus (struct thread_info *tp, struct target_waitstatus *ws)
+save_waitstatus (struct thread_info *tp, const target_waitstatus *ws)
{
if (debug_infrun)
{
"iterations=%d\n", pass, iterations);
while (1)
{
- ptid_t event_ptid;
- struct target_waitstatus ws;
int need_wait = 0;
update_thread_list ();
"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;
}
if (pass > 0)
pass = -1;
- event_ptid = wait_one (&ws);
+ wait_one_event event = wait_one ();
+
if (debug_infrun)
{
fprintf_unfiltered (gdb_stdlog,
"infrun: stop_all_threads %s %s\n",
- target_waitstatus_to_string (&ws).c_str (),
- target_pid_to_str (event_ptid).c_str ());
+ target_waitstatus_to_string (&event.ws).c_str (),
+ target_pid_to_str (event.ptid).c_str ());
}
- if (ws.kind == TARGET_WAITKIND_NO_RESUMED
- || ws.kind == TARGET_WAITKIND_THREAD_EXITED
- || ws.kind == TARGET_WAITKIND_EXITED
- || ws.kind == TARGET_WAITKIND_SIGNALLED)
+ if (event.ws.kind == TARGET_WAITKIND_NO_RESUMED
+ || event.ws.kind == TARGET_WAITKIND_THREAD_EXITED
+ || event.ws.kind == TARGET_WAITKIND_EXITED
+ || event.ws.kind == TARGET_WAITKIND_SIGNALLED)
{
/* All resumed threads exited
or one thread/process exited/signalled. */
}
else
{
- thread_info *t = find_thread_ptid (event_ptid);
+ thread_info *t = find_thread_ptid (event.target, event.ptid);
if (t == NULL)
- t = add_thread (event_ptid);
+ t = add_thread (event.target, event.ptid);
t->stop_requested = 0;
t->executing = 0;
/* This may be the first time we see the inferior report
a stop. */
- inferior *inf = find_inferior_ptid (event_ptid);
+ inferior *inf = find_inferior_ptid (event.target, event.ptid);
if (inf->needs_setup)
{
switch_to_thread_no_regs (t);
setup_inferior (0);
}
- if (ws.kind == TARGET_WAITKIND_STOPPED
- && ws.value.sig == GDB_SIGNAL_0)
+ if (event.ws.kind == TARGET_WAITKIND_STOPPED
+ && event.ws.value.sig == GDB_SIGNAL_0)
{
/* We caught the event that we intended to catch, so
there's no event pending. */
if (debug_infrun)
{
- std::string statstr = target_waitstatus_to_string (&ws);
+ std::string statstr = target_waitstatus_to_string (&event.ws);
fprintf_unfiltered (gdb_stdlog,
"infrun: target_wait %s, saving "
}
/* Record for later. */
- save_waitstatus (t, &ws);
+ save_waitstatus (t, &event.ws);
- sig = (ws.kind == TARGET_WAITKIND_STOPPED
- ? ws.value.sig : GDB_SIGNAL_0);
+ sig = (event.ws.kind == TARGET_WAITKIND_STOPPED
+ ? event.ws.value.sig : GDB_SIGNAL_0);
if (displaced_step_fixup (t, sig) < 0)
{
the synchronous command show "no unwaited-for " to the user. */
update_thread_list ();
- for (thread_info *thread : all_non_exited_threads ())
+ for (thread_info *thread : all_non_exited_threads (ecs->target))
{
if (thread->executing
|| thread->suspend.waitstatus_pending_p)
process exited meanwhile (thus updating the thread list results
in an empty thread list). In this case we know we'll be getting
a process exit event shortly. */
- for (inferior *inf : all_inferiors ())
+ for (inferior *inf : all_non_exited_inferiors (ecs->target))
{
- if (inf->pid == 0)
- continue;
-
thread_info *thread = any_live_thread_of_inferior (inf);
if (thread == NULL)
{
&& handle_no_resumed (ecs))
return;
- /* Cache the last pid/waitstatus. */
- set_last_target_status (ecs->ptid, ecs->ws);
+ /* Cache the last target/ptid/waitstatus. */
+ set_last_target_status (ecs->target, ecs->ptid, ecs->ws);
/* Always clear state belonging to the previous time we stopped. */
stop_stack_dummy = STOP_NONE;
if (ecs->ws.kind != TARGET_WAITKIND_EXITED
&& ecs->ws.kind != TARGET_WAITKIND_SIGNALLED)
{
- ecs->event_thread = find_thread_ptid (ecs->ptid);
+ ecs->event_thread = find_thread_ptid (ecs->target, ecs->ptid);
/* If it's a new thread, add it to the thread database. */
if (ecs->event_thread == NULL)
- ecs->event_thread = add_thread (ecs->ptid);
+ ecs->event_thread = add_thread (ecs->target, ecs->ptid);
/* Disable range stepping. If the next step request could use a
range, this will be end up re-enabled then. */
else
mark_ptid = ecs->ptid;
- set_executing (mark_ptid, 0);
+ set_executing (ecs->target, mark_ptid, 0);
/* Likewise the resumed flag. */
- set_resumed (mark_ptid, 0);
+ set_resumed (ecs->target, mark_ptid, 0);
}
switch (ecs->ws.kind)
case TARGET_WAITKIND_EXITED:
case TARGET_WAITKIND_SIGNALLED:
inferior_ptid = ecs->ptid;
- set_current_inferior (find_inferior_ptid (ecs->ptid));
+ set_current_inferior (find_inferior_ptid (ecs->target, ecs->ptid));
set_current_program_space (current_inferior ()->pspace);
handle_vfork_child_exec_or_exit (0);
target_terminal::ours (); /* Must do this before mourn anyway. */
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. */
if (displaced_step_in_progress_thread (ecs->event_thread))
{
struct inferior *parent_inf
- = find_inferior_ptid (ecs->ptid);
+ = find_inferior_ptid (ecs->target, ecs->ptid);
struct regcache *child_regcache;
CORE_ADDR parent_pc;
list yet at this point. */
child_regcache
- = get_thread_arch_aspace_regcache (ecs->ws.value.related_pid,
+ = get_thread_arch_aspace_regcache (parent_inf->process_target (),
+ ecs->ws.value.related_pid,
gdbarch,
parent_inf->aspace);
/* Read PC value of parent process. */
ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
+ process_stratum_target *targ
+ = ecs->event_thread->inf->process_target ();
+
should_resume = follow_fork ();
+ /* Note that one of these may be an invalid pointer,
+ depending on detach_fork. */
thread_info *parent = ecs->event_thread;
- thread_info *child = find_thread_ptid (ecs->ws.value.related_pid);
+ thread_info *child
+ = find_thread_ptid (targ, ecs->ws.value.related_pid);
/* At this point, the parent is marked running, and the
child is marked stopped. */
for (thread_info *tp : all_non_exited_threads ())
{
+ switch_to_thread_no_regs (tp);
+
if (tp == event_thread)
{
if (debug_infrun)
{
struct regcache *regcache = get_thread_regcache (ecs->event_thread);
struct gdbarch *reg_gdbarch = regcache->arch ();
- scoped_restore save_inferior_ptid = make_scoped_restore (&inferior_ptid);
- inferior_ptid = ecs->ptid;
+ switch_to_thread (ecs->event_thread);
fprintf_unfiltered (gdb_stdlog, "infrun: stop_pc = %s\n",
paddress (reg_gdbarch,
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;
if (random_signal)
{
/* Signal not for debugging purposes. */
- struct inferior *inf = find_inferior_ptid (ecs->ptid);
+ struct inferior *inf = find_inferior_ptid (ecs->target, ecs->ptid);
enum gdb_signal stop_signal = ecs->event_thread->suspend.stop_signal;
if (debug_infrun)
return;
}
- /* Note: step_resume_breakpoint may be non-NULL. This occures
+ /* Note: step_resume_breakpoint may be non-NULL. This occurs
when either there's a nested signal, or when there's a
pending signal enabled just as the signal handler returns
(leaving the inferior at the step-resume-breakpoint without
tmp_sal = find_pc_line (ecs->stop_func_start, 0);
if (tmp_sal.line != 0
&& !function_name_is_marked_for_skip (ecs->stop_func_name,
- tmp_sal))
+ tmp_sal)
+ && !inline_frame_is_marked_for_skip (true, ecs->event_thread))
{
if (execution_direction == EXEC_REVERSE)
handle_step_into_function_backward (gdbarch, ecs);
if (call_sal.line == ecs->event_thread->current_line
&& call_sal.symtab == ecs->event_thread->current_symtab)
- step_into_inline_frame (ecs->event_thread);
+ {
+ step_into_inline_frame (ecs->event_thread);
+ if (inline_frame_is_marked_for_skip (false, ecs->event_thread))
+ {
+ keep_going (ecs);
+ return;
+ }
+ }
end_stepping_range (ecs);
return;
fprintf_unfiltered (gdb_stdlog,
"infrun: stepping through inlined function\n");
- if (ecs->event_thread->control.step_over_calls == STEP_OVER_ALL)
+ if (ecs->event_thread->control.step_over_calls == STEP_OVER_ALL
+ || inline_frame_is_marked_for_skip (false, ecs->event_thread))
keep_going (ecs);
else
end_stepping_range (ecs);
for (thread_info *tp : all_non_exited_threads ())
{
+ switch_to_thread_no_regs (tp);
+
/* Ignore threads of processes the caller is not
resuming. */
if (!sched_multi
- && tp->ptid.pid () != ecs->ptid.pid ())
+ && (tp->inf->process_target () != ecs->target
+ || tp->inf->pid != ecs->ptid.pid ()))
continue;
/* When stepping over a breakpoint, we lock all threads
return 1;
}
}
+
+ switch_to_thread (ecs->event_thread);
}
return 0;
CORE_ADDR handler;
struct breakpoint *bp;
- vsym = lookup_symbol_search_name (SYMBOL_SEARCH_NAME (sym),
+ vsym = lookup_symbol_search_name (sym->search_name (),
b, VAR_DOMAIN);
value = read_var_value (vsym.symbol, vsym.block, frame);
/* If the value was optimized out, revert to the old behavior. */
inferior_thread ()->control.exception_resume_breakpoint = bp;
}
}
- catch (const gdb_exception_RETURN_MASK_ERROR &e)
+ catch (const gdb_exception_error &e)
{
/* We want to ignore errors here. */
}
}
}
}
- catch (const gdb_exception_RETURN_MASK_ERROR &e)
+ catch (const gdb_exception_error &e)
{
}
}
{
insert_breakpoints ();
}
- catch (const gdb_exception_RETURN_MASK_ERROR &e)
+ catch (const gdb_exception_error &e)
{
exception_print (gdb_stderr, e);
stop_waiting (ecs);
{
if (uiout->is_mi_like_p ())
uiout->field_string ("reason", async_reason_lookup (EXEC_ASYNC_EXITED));
- uiout->text ("[Inferior ");
- uiout->text (plongest (inf->num));
- uiout->text (" (");
- uiout->text (pidstr.c_str ());
- uiout->text (") exited with code ");
- uiout->field_fmt ("exit-code", "0%o", (unsigned int) exitstatus);
- uiout->text ("]\n");
+ std::string exit_code_str
+ = string_printf ("0%o", (unsigned int) exitstatus);
+ uiout->message ("[Inferior %s (%s) exited with code %pF]\n",
+ plongest (inf->num), pidstr.c_str (),
+ string_field ("exit-code", exit_code_str.c_str ()));
}
else
{
if (uiout->is_mi_like_p ())
uiout->field_string
("reason", async_reason_lookup (EXEC_ASYNC_EXITED_NORMALLY));
- uiout->text ("[Inferior ");
- uiout->text (plongest (inf->num));
- uiout->text (" (");
- uiout->text (pidstr.c_str ());
- uiout->text (") exited normally]\n");
+ uiout->message ("[Inferior %s (%s) exited normally]\n",
+ plongest (inf->num), pidstr.c_str ());
}
}
const char *name;
uiout->text ("\nThread ");
- uiout->field_fmt ("thread-id", "%s", print_thread_id (thr));
+ uiout->field_string ("thread-id", print_thread_id (thr));
name = thr->name != NULL ? thr->name : target_thread_name (thr);
if (name != NULL)
{
uiout->text (" \"");
- uiout->field_fmt ("name", "%s", name);
+ uiout->field_string ("name", name);
uiout->text ("\"");
}
}
print_stop_event (struct ui_out *uiout, bool displays)
{
struct target_waitstatus last;
- ptid_t last_ptid;
struct thread_info *tp;
- get_last_target_status (&last_ptid, &last);
+ get_last_target_status (nullptr, nullptr, &last);
{
scoped_restore save_uiout = make_scoped_restore (¤t_uiout, uiout);
normal_stop (void)
{
struct target_waitstatus last;
- ptid_t last_ptid;
- get_last_target_status (&last_ptid, &last);
+ get_last_target_status (nullptr, nullptr, &last);
new_stop_id ();
frontend/user running state. A QUIT is an easy exception to see
here, so do this before any filtered output. */
- gdb::optional<scoped_finish_thread_state> maybe_finish_thread_state;
+ ptid_t finish_ptid = null_ptid;
if (!non_stop)
- maybe_finish_thread_state.emplace (minus_one_ptid);
+ finish_ptid = minus_one_ptid;
else if (last.kind == TARGET_WAITKIND_SIGNALLED
|| last.kind == TARGET_WAITKIND_EXITED)
{
linux-fork.c automatically switches to another fork from
within target_mourn_inferior. */
if (inferior_ptid != null_ptid)
- maybe_finish_thread_state.emplace (ptid_t (inferior_ptid.pid ()));
+ finish_ptid = ptid_t (inferior_ptid.pid ());
}
else if (last.kind != TARGET_WAITKIND_NO_RESUMED)
- maybe_finish_thread_state.emplace (inferior_ptid);
+ finish_ptid = inferior_ptid;
+
+ gdb::optional<scoped_finish_thread_state> maybe_finish_thread_state;
+ if (finish_ptid != null_ptid)
+ {
+ maybe_finish_thread_state.emplace
+ (user_visible_resume_target (finish_ptid), finish_ptid);
+ }
/* As we're presenting a stop, and potentially removing breakpoints,
update the thread list so we can tell whether there are threads
{
execute_cmd_pre_hook (stop_command);
}
- catch (const gdb_exception_RETURN_MASK_ALL &ex)
+ catch (const gdb_exception &ex)
{
exception_fprintf (gdb_stderr, ex,
"Error while running hook_stop:\n");
{
restore_selected_frame (inf_status->selected_frame_id);
}
- catch (const gdb_exception_RETURN_MASK_ERROR &ex)
+ catch (const gdb_exception_error &ex)
{
exception_fprintf (gdb_stderr, ex,
"Unable to restore previously selected frame:\n");
inferior_event_handler (INF_REG_EVENT, NULL);
}
+void _initialize_infrun ();
void
-_initialize_infrun (void)
+_initialize_infrun ()
{
struct cmd_list_element *c;