aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog55
-rw-r--r--gdb/infcmd.c16
-rw-r--r--gdb/infrun.c226
-rw-r--r--gdb/infrun.h98
-rw-r--r--gdb/mi/mi-main.c4
-rw-r--r--gdb/process-stratum-target.h32
-rw-r--r--gdb/record-full.c10
-rw-r--r--gdb/remote.c149
-rw-r--r--gdb/target-delegates.c18
-rw-r--r--gdb/target.c27
-rw-r--r--gdb/target.h31
-rw-r--r--gdb/top.c7
12 files changed, 575 insertions, 98 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 7f60d3c..e3ae1c4 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,58 @@
+2021-03-26 Simon Marchi <simon.marchi@efficios.com>
+ Pedro Alves <pedro@palves.net>
+
+ * infcmd.c (run_command_1, attach_command, detach_command)
+ (interrupt_target_1): Use scoped_disable_commit_resumed.
+ * infrun.c (do_target_resume): Remove
+ target_commit_resume call.
+ (commit_resume_all_targets): Remove.
+ (maybe_set_commit_resumed_all_targets): New.
+ (maybe_call_commit_resumed_all_targets): New.
+ (enable_commit_resumed): New.
+ (scoped_disable_commit_resumed::scoped_disable_commit_resumed)
+ (scoped_disable_commit_resumed::~scoped_disable_commit_resumed)
+ (scoped_disable_commit_resumed::reset)
+ (scoped_disable_commit_resumed::reset_and_commit)
+ (scoped_enable_commit_resumed::scoped_enable_commit_resumed)
+ (scoped_enable_commit_resumed::~scoped_enable_commit_resumed):
+ New.
+ (proceed): Use scoped_disable_commit_resumed and
+ maybe_call_commit_resumed_all_targets.
+ (fetch_inferior_event): Use scoped_disable_commit_resumed.
+ * infrun.h (struct scoped_disable_commit_resumed): New.
+ (maybe_call_commit_resumed_all_process_targets): New.
+ (struct scoped_enable_commit_resumed): New.
+ * mi/mi-main.c (exec_continue): Use scoped_disable_commit_resumed.
+ * process-stratum-target.h (class process_stratum_target):
+ <commit_resumed_state>: New.
+ * record-full.c (record_full_wait_1): Change commit_resumed_state
+ around calling commit_resumed.
+ * remote.c (class remote_target) <commit_resume>: Rename to...
+ <commit_resumed>: ... this.
+ (struct stop_reply): Move up.
+ (remote_target::commit_resume): Rename to...
+ (remote_target::commit_resumed): ... this. Check if there is any
+ thread pending vCont resume.
+ (remote_target::remote_stop_ns): Generate stop replies for resumed
+ but pending vCont threads.
+ (remote_target::wait_ns): Add gdb_assert.
+ * target-delegates.c: Regenerate.
+ * target.c (target_wait, target_resume): Assert that the current
+ process_stratum target isn't in commit-resumed state.
+ (defer_target_commit_resume): Remove.
+ (target_commit_resume): Remove.
+ (target_commit_resumed): New.
+ (make_scoped_defer_target_commit_resume): Remove.
+ (target_stop): Assert that the current process_stratum target
+ isn't in commit-resumed state.
+ * target.h (struct target_ops) <commit_resume>: Rename to ...
+ <commit_resumed>: ... this.
+ (target_commit_resume): Remove.
+ (target_commit_resumed): New.
+ (make_scoped_defer_target_commit_resume): Remove.
+ * top.c (wait_sync_command_done): Use
+ scoped_enable_commit_resumed.
+
2021-03-26 Pedro Alves <pedro@palves.net>
* target.c (target_always_non_stop_p): Also check whether the
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index a6e9572..9b0186d 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -419,6 +419,8 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how)
dont_repeat ();
+ scoped_disable_commit_resumed disable_commit_resumed ("running");
+
kill_if_already_running (from_tty);
init_wait_for_inferior ();
@@ -538,6 +540,8 @@ run_command_1 (const char *args, int from_tty, enum run_how run_how)
/* Since there was no error, there's no need to finish the thread
states here. */
finish_state.release ();
+
+ disable_commit_resumed.reset_and_commit ();
}
static void
@@ -2565,6 +2569,8 @@ attach_command (const char *args, int from_tty)
dont_repeat (); /* Not for the faint of heart */
+ scoped_disable_commit_resumed disable_commit_resumed ("attaching");
+
if (gdbarch_has_global_solist (target_gdbarch ()))
/* Don't complain if all processes share the same symbol
space. */
@@ -2673,6 +2679,8 @@ attach_command (const char *args, int from_tty)
}
else
attach_post_wait (args, from_tty, mode);
+
+ disable_commit_resumed.reset_and_commit ();
}
/* We had just found out that the target was already attached to an
@@ -2746,6 +2754,8 @@ detach_command (const char *args, int from_tty)
if (inferior_ptid == null_ptid)
error (_("The program is not being run."));
+ scoped_disable_commit_resumed disable_commit_resumed ("detaching");
+
query_if_trace_running (from_tty);
disconnect_tracing ();
@@ -2779,6 +2789,8 @@ detach_command (const char *args, int from_tty)
if (!was_non_stop_p)
restart_after_all_stop_detach (as_process_stratum_target (target_ref.get ()));
+
+ disable_commit_resumed.reset_and_commit ();
}
/* Disconnect from the current target without resuming it (leaving it
@@ -2827,6 +2839,8 @@ stop_current_target_threads_ns (ptid_t ptid)
void
interrupt_target_1 (bool all_threads)
{
+ scoped_disable_commit_resumed disable_commit_resumed ("interrupting");
+
if (non_stop)
{
if (all_threads)
@@ -2844,6 +2858,8 @@ interrupt_target_1 (bool all_threads)
}
else
target_interrupt ();
+
+ disable_commit_resumed.reset_and_commit ();
}
/* interrupt [-a]
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 20035a0..347eefb 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -2173,8 +2173,6 @@ do_target_resume (ptid_t resume_ptid, bool step, enum gdb_signal sig)
target_resume (resume_ptid, step, sig);
- target_commit_resume ();
-
if (target_can_async_p ())
target_async (1);
}
@@ -2761,28 +2759,208 @@ schedlock_applies (struct thread_info *tp)
execution_direction)));
}
-/* Calls target_commit_resume on all targets. */
+/* Set process_stratum_target::COMMIT_RESUMED_STATE in all target
+ stacks that have threads executing and don't have threads with
+ pending events. */
static void
-commit_resume_all_targets ()
+maybe_set_commit_resumed_all_targets ()
+{
+ for (inferior *inf : all_non_exited_inferiors ())
+ {
+ process_stratum_target *proc_target = inf->process_target ();
+
+ if (proc_target->commit_resumed_state)
+ {
+ /* We already set this in a previous iteration, via another
+ inferior sharing the process_stratum target. */
+ continue;
+ }
+
+ /* If the target has no resumed threads, it would be useless to
+ ask it to commit the resumed threads. */
+ if (!proc_target->threads_executing)
+ {
+ infrun_debug_printf ("not requesting commit-resumed for target "
+ "%s, no resumed threads",
+ proc_target->shortname ());
+ continue;
+ }
+
+ /* As an optimization, if a thread from this target has some
+ status to report, handle it before requiring the target to
+ commit its resumed threads: handling the status might lead to
+ resuming more threads. */
+ bool has_thread_with_pending_status = false;
+ for (thread_info *thread : all_non_exited_threads (proc_target))
+ if (thread->resumed && thread->suspend.waitstatus_pending_p)
+ {
+ has_thread_with_pending_status = true;
+ break;
+ }
+
+ if (has_thread_with_pending_status)
+ {
+ infrun_debug_printf ("not requesting commit-resumed for target %s, a"
+ " thread has a pending waitstatus",
+ proc_target->shortname ());
+ continue;
+ }
+
+ infrun_debug_printf ("enabling commit-resumed for target %s",
+ proc_target->shortname ());
+
+ proc_target->commit_resumed_state = true;
+ }
+}
+
+/* See infrun.h. */
+
+void
+maybe_call_commit_resumed_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 ())
+ {
+ process_stratum_target *proc_target = inf->process_target ();
+
+ if (!proc_target->commit_resumed_state)
+ continue;
+
+ switch_to_inferior_no_thread (inf);
+
+ infrun_debug_printf ("calling commit_resumed for target %s",
+ proc_target->shortname());
+
+ target_commit_resumed ();
+ }
+}
+
+/* To track nesting of scoped_disable_commit_resumed objects, ensuring
+ that only the outermost one attempts to re-enable
+ commit-resumed. */
+static bool enable_commit_resumed = true;
+
+/* See infrun.h. */
+
+scoped_disable_commit_resumed::scoped_disable_commit_resumed
+ (const char *reason)
+ : m_reason (reason),
+ m_prev_enable_commit_resumed (enable_commit_resumed)
+{
+ infrun_debug_printf ("reason=%s", m_reason);
+
+ enable_commit_resumed = false;
for (inferior *inf : all_non_exited_inferiors ())
- if (inf->has_execution ())
- conn_inf[inf->process_target ()] = inf;
+ {
+ process_stratum_target *proc_target = inf->process_target ();
+
+ if (m_prev_enable_commit_resumed)
+ {
+ /* This is the outermost instance: force all
+ COMMIT_RESUMED_STATE to false. */
+ proc_target->commit_resumed_state = false;
+ }
+ else
+ {
+ /* This is not the outermost instance, we expect
+ COMMIT_RESUMED_STATE to have been cleared by the
+ outermost instance. */
+ gdb_assert (!proc_target->commit_resumed_state);
+ }
+ }
+}
- for (const auto &ci : conn_inf)
+/* See infrun.h. */
+
+void
+scoped_disable_commit_resumed::reset ()
+{
+ if (m_reset)
+ return;
+ m_reset = true;
+
+ infrun_debug_printf ("reason=%s", m_reason);
+
+ gdb_assert (!enable_commit_resumed);
+
+ enable_commit_resumed = m_prev_enable_commit_resumed;
+
+ if (m_prev_enable_commit_resumed)
{
- inferior *inf = ci.second;
- switch_to_inferior_no_thread (inf);
- target_commit_resume ();
+ /* This is the outermost instance, re-enable
+ COMMIT_RESUMED_STATE on the targets where it's possible. */
+ maybe_set_commit_resumed_all_targets ();
+ }
+ else
+ {
+ /* This is not the outermost instance, we expect
+ COMMIT_RESUMED_STATE to still be false. */
+ for (inferior *inf : all_non_exited_inferiors ())
+ {
+ process_stratum_target *proc_target = inf->process_target ();
+ gdb_assert (!proc_target->commit_resumed_state);
+ }
+ }
+}
+
+/* See infrun.h. */
+
+scoped_disable_commit_resumed::~scoped_disable_commit_resumed ()
+{
+ reset ();
+}
+
+/* See infrun.h. */
+
+void
+scoped_disable_commit_resumed::reset_and_commit ()
+{
+ reset ();
+ maybe_call_commit_resumed_all_targets ();
+}
+
+/* See infrun.h. */
+
+scoped_enable_commit_resumed::scoped_enable_commit_resumed
+ (const char *reason)
+ : m_reason (reason),
+ m_prev_enable_commit_resumed (enable_commit_resumed)
+{
+ infrun_debug_printf ("reason=%s", m_reason);
+
+ if (!enable_commit_resumed)
+ {
+ enable_commit_resumed = true;
+
+ /* Re-enable COMMIT_RESUMED_STATE on the targets where it's
+ possible. */
+ maybe_set_commit_resumed_all_targets ();
+
+ maybe_call_commit_resumed_all_targets ();
+ }
+}
+
+/* See infrun.h. */
+
+scoped_enable_commit_resumed::~scoped_enable_commit_resumed ()
+{
+ infrun_debug_printf ("reason=%s", m_reason);
+
+ gdb_assert (enable_commit_resumed);
+
+ enable_commit_resumed = m_prev_enable_commit_resumed;
+
+ if (!enable_commit_resumed)
+ {
+ /* Force all COMMIT_RESUMED_STATE back to false. */
+ for (inferior *inf : all_non_exited_inferiors ())
+ {
+ process_stratum_target *proc_target = inf->process_target ();
+ proc_target->commit_resumed_state = false;
+ }
}
}
@@ -3006,7 +3184,7 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
cur_thr->prev_pc = regcache_read_pc_protected (regcache);
{
- scoped_restore save_defer_tc = make_scoped_defer_target_commit_resume ();
+ scoped_disable_commit_resumed disable_commit_resumed ("proceeding");
started = start_step_over ();
@@ -3074,9 +3252,9 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
if (!ecs->wait_some_more)
error (_("Command aborted."));
}
- }
- commit_resume_all_targets ();
+ disable_commit_resumed.reset_and_commit ();
+ }
finish_state.release ();
@@ -3878,8 +4056,16 @@ fetch_inferior_event ()
= make_scoped_restore (&execution_direction,
target_execution_direction ());
+ /* Allow targets to pause their resumed threads while we handle
+ the event. */
+ scoped_disable_commit_resumed disable_commit_resumed ("handling event");
+
if (!do_target_wait (minus_one_ptid, ecs, TARGET_WNOHANG))
- return;
+ {
+ infrun_debug_printf ("do_target_wait returned no event");
+ disable_commit_resumed.reset_and_commit ();
+ return;
+ }
gdb_assert (ecs->ws.kind != TARGET_WAITKIND_IGNORE);
@@ -3970,6 +4156,8 @@ fetch_inferior_event ()
/* No error, don't finish the thread states yet. */
finish_state.release ();
+ disable_commit_resumed.reset_and_commit ();
+
/* This scope is used to ensure that readline callbacks are
reinstalled here. */
}
diff --git a/gdb/infrun.h b/gdb/infrun.h
index e643c84..220ccc7 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -273,4 +273,102 @@ extern void all_uis_on_sync_execution_starting (void);
detach. */
extern void restart_after_all_stop_detach (process_stratum_target *proc_target);
+/* RAII object to temporarily disable the requirement for target
+ stacks to commit their resumed threads.
+
+ On construction, set process_stratum_target::commit_resumed_state
+ to false for all process_stratum targets in all target
+ stacks.
+
+ On destruction (or if reset_and_commit() is called), set
+ process_stratum_target::commit_resumed_state to true for all
+ process_stratum targets in all target stacks, except those that:
+
+ - have no resumed threads
+ - have a resumed thread with a pending status
+
+ target_commit_resumed is not called in the destructor, because its
+ implementations could throw, and we don't to swallow that error in
+ a destructor. Instead, the caller should call the
+ reset_and_commit_resumed() method so that an eventual exception can
+ propagate. "reset" in the method name refers to the fact that this
+ method has the same effect as the destructor, in addition to
+ committing resumes.
+
+ The creation of nested scoped_disable_commit_resumed objects is
+ tracked, such that only the outermost instance actually does
+ something, for cases like this:
+
+ void
+ inner_func ()
+ {
+ scoped_disable_commit_resumed disable;
+
+ // do stuff
+
+ disable.reset_and_commit ();
+ }
+
+ void
+ outer_func ()
+ {
+ scoped_disable_commit_resumed disable;
+
+ for (... each thread ...)
+ inner_func ();
+
+ disable.reset_and_commit ();
+ }
+
+ In this case, we don't want the `disable` destructor in
+ `inner_func` to require targets to commit resumed threads, so that
+ the `reset_and_commit()` call in `inner_func` doesn't actually
+ resume threads. */
+
+struct scoped_disable_commit_resumed
+{
+ explicit scoped_disable_commit_resumed (const char *reason);
+ ~scoped_disable_commit_resumed ();
+
+ DISABLE_COPY_AND_ASSIGN (scoped_disable_commit_resumed);
+
+ /* Undoes the disabling done by the ctor, and calls
+ maybe_call_commit_resumed_all_targets(). */
+ void reset_and_commit ();
+
+private:
+ /* Undoes the disabling done by the ctor. */
+ void reset ();
+
+ /* Whether this object has been reset. */
+ bool m_reset = false;
+
+ const char *m_reason;
+ bool m_prev_enable_commit_resumed;
+};
+
+/* Call target_commit_resumed method on all target stacks whose
+ process_stratum target layer has COMMIT_RESUME_STATE set. */
+
+extern void maybe_call_commit_resumed_all_targets ();
+
+/* RAII object to temporarily enable the requirement for target stacks
+ to commit their resumed threads. This is the inverse of
+ scoped_disable_commit_resumed. The constructor calls the
+ maybe_call_commit_resumed_all_targets function itself, since it's
+ OK to throw from a constructor. */
+
+struct scoped_enable_commit_resumed
+{
+ explicit scoped_enable_commit_resumed (const char *reason);
+ ~scoped_enable_commit_resumed ();
+
+ DISABLE_COPY_AND_ASSIGN (scoped_enable_commit_resumed);
+
+private:
+ const char *m_reason;
+ bool m_prev_enable_commit_resumed;
+};
+
+
#endif /* INFRUN_H */
diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
index c6f1ab4..988db55 100644
--- a/gdb/mi/mi-main.c
+++ b/gdb/mi/mi-main.c
@@ -266,6 +266,8 @@ exec_continue (char **argv, int argc)
{
prepare_execution_command (current_inferior ()->top_target (), mi_async_p ());
+ scoped_disable_commit_resumed disable_commit_resumed ("mi continue");
+
if (non_stop)
{
/* In non-stop mode, 'resume' always resumes a single thread.
@@ -311,6 +313,8 @@ exec_continue (char **argv, int argc)
continue_1 (1);
}
}
+
+ disable_commit_resumed.reset_and_commit ();
}
static void
diff --git a/gdb/process-stratum-target.h b/gdb/process-stratum-target.h
index b513c26..6fddbba 100644
--- a/gdb/process-stratum-target.h
+++ b/gdb/process-stratum-target.h
@@ -72,6 +72,38 @@ public:
/* The connection number. Visible in "info connections". */
int connection_number = 0;
+
+ /* Whether resumed threads must be committed to the target.
+
+ When true, resumed threads must be committed to the execution
+ target.
+
+ When false, the target may leave resumed threads stopped when
+ it's convenient or efficient to do so. When the core requires
+ resumed threads to be committed again, this is set back to true
+ and calls the `commit_resumed` method to allow the target to do
+ so.
+
+ To simplify the implementation of targets, the following methods
+ are guaranteed to be called with COMMIT_RESUMED_STATE set to
+ false:
+
+ - resume
+ - stop
+ - wait
+
+ Knowing this, the target doesn't need to implement different
+ behaviors depending on the COMMIT_RESUMED_STATE, and can simply
+ assume that it is false.
+
+ Targets can take advantage of this to batch resumption requests,
+ for example. In that case, the target doesn't actually resume in
+ its `resume` implementation. Instead, it takes note of the
+ resumption intent in `resume` and defers the actual resumption to
+ `commit_resumed`. For example, the remote target uses this to
+ coalesce multiple resumption requests in a single vCont
+ packet. */
+ bool commit_resumed_state = false;
};
/* Downcast TARGET to process_stratum_target. */
diff --git a/gdb/record-full.c b/gdb/record-full.c
index 8310b7b..23cbdcb 100644
--- a/gdb/record-full.c
+++ b/gdb/record-full.c
@@ -1242,11 +1242,11 @@ record_full_wait_1 (struct target_ops *ops,
break;
}
+ process_stratum_target *proc_target
+ = current_inferior ()->process_target ();
+
if (gdbarch_software_single_step_p (gdbarch))
{
- process_stratum_target *proc_target
- = current_inferior ()->process_target ();
-
/* Try to insert the software single step breakpoint.
If insert success, set step to 0. */
set_executing (proc_target, inferior_ptid, false);
@@ -1263,7 +1263,9 @@ record_full_wait_1 (struct target_ops *ops,
"issuing one more step in the "
"target beneath\n");
ops->beneath ()->resume (ptid, step, GDB_SIGNAL_0);
- ops->beneath ()->commit_resume ();
+ proc_target->commit_resumed_state = true;
+ proc_target->commit_resumed ();
+ proc_target->commit_resumed_state = false;
continue;
}
}
diff --git a/gdb/remote.c b/gdb/remote.c
index 188f507..1036ffd 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -426,7 +426,7 @@ public:
void detach (inferior *, int) override;
void disconnect (const char *, int) override;
- void commit_resume () override;
+ void commit_resumed () override;
void resume (ptid_t, int, enum gdb_signal) override;
ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
@@ -6499,6 +6499,36 @@ get_remote_inferior (inferior *inf)
return static_cast<remote_inferior *> (inf->priv.get ());
}
+struct stop_reply : public notif_event
+{
+ ~stop_reply ();
+
+ /* The identifier of the thread about this event */
+ ptid_t ptid;
+
+ /* The remote state this event is associated with. When the remote
+ connection, represented by a remote_state object, is closed,
+ all the associated stop_reply events should be released. */
+ struct remote_state *rs;
+
+ struct target_waitstatus ws;
+
+ /* The architecture associated with the expedited registers. */
+ gdbarch *arch;
+
+ /* Expedited registers. This makes remote debugging a bit more
+ efficient for those targets that provide critical registers as
+ part of their normal status mechanism (as another roundtrip to
+ fetch them is avoided). */
+ std::vector<cached_reg_t> regcache;
+
+ enum target_stop_reason stop_reason;
+
+ CORE_ADDR watch_data_address;
+
+ int core;
+};
+
/* Class used to track the construction of a vCont packet in the
outgoing packet buffer. This is used to send multiple vCont
packets if we have more actions than would fit a single packet. */
@@ -6603,7 +6633,7 @@ vcont_builder::push_action (ptid_t ptid, bool step, gdb_signal siggnal)
/* to_commit_resume implementation. */
void
-remote_target::commit_resume ()
+remote_target::commit_resumed ()
{
int any_process_wildcard;
int may_global_wildcard_vcont;
@@ -6678,6 +6708,8 @@ remote_target::commit_resume ()
disable process and global wildcard resumes appropriately. */
check_pending_events_prevent_wildcard_vcont (&may_global_wildcard_vcont);
+ bool any_pending_vcont_resume = false;
+
for (thread_info *tp : all_non_exited_threads (this))
{
remote_thread_info *priv = get_remote_thread_info (tp);
@@ -6694,6 +6726,9 @@ remote_target::commit_resume ()
continue;
}
+ if (priv->get_resume_state () == resume_state::RESUMED_PENDING_VCONT)
+ any_pending_vcont_resume = true;
+
/* If a thread is the parent of an unfollowed fork, then we
can't do a global wildcard, as that would resume the fork
child. */
@@ -6701,6 +6736,11 @@ remote_target::commit_resume ()
may_global_wildcard_vcont = 0;
}
+ /* We didn't have any resumed thread pending a vCont resume, so nothing to
+ do. */
+ if (!any_pending_vcont_resume)
+ return;
+
/* Now let's build the vCont packet(s). Actions must be appended
from narrower to wider scopes (thread -> process -> global). If
we end up with too many actions for a single packet vcont_builder
@@ -6721,6 +6761,13 @@ remote_target::commit_resume ()
gdb_assert (!thread_is_in_step_over_chain (tp));
+ /* We should never be commit-resuming a thread that has a stop reply.
+ Otherwise, we would end up reporting a stop event for a thread while
+ it is running on the remote target. */
+ remote_state *rs = get_remote_state ();
+ for (const auto &stop_reply : rs->stop_reply_queue)
+ gdb_assert (stop_reply->ptid != tp->ptid);
+
const resumed_pending_vcont_info &info
= remote_thr->resumed_pending_vcont_info ();
@@ -6786,6 +6833,74 @@ remote_target::remote_stop_ns (ptid_t ptid)
char *p = rs->buf.data ();
char *endp = p + get_remote_packet_size ();
+ /* If any thread that needs to stop was resumed but pending a vCont
+ resume, generate a phony stop_reply. However, first check
+ whether the thread wasn't resumed with a signal. Generating a
+ phony stop in that case would result in losing the signal. */
+ bool needs_commit = false;
+ for (thread_info *tp : all_non_exited_threads (this, ptid))
+ {
+ remote_thread_info *remote_thr = get_remote_thread_info (tp);
+
+ if (remote_thr->get_resume_state ()
+ == resume_state::RESUMED_PENDING_VCONT)
+ {
+ const resumed_pending_vcont_info &info
+ = remote_thr->resumed_pending_vcont_info ();
+ if (info.sig != GDB_SIGNAL_0)
+ {
+ /* This signal must be forwarded to the inferior. We
+ could commit-resume just this thread, but its simpler
+ to just commit-resume everything. */
+ needs_commit = true;
+ break;
+ }
+ }
+ }
+
+ if (needs_commit)
+ commit_resumed ();
+ else
+ for (thread_info *tp : all_non_exited_threads (this, ptid))
+ {
+ remote_thread_info *remote_thr = get_remote_thread_info (tp);
+
+ if (remote_thr->get_resume_state ()
+ == resume_state::RESUMED_PENDING_VCONT)
+ {
+ remote_debug_printf ("Enqueueing phony stop reply for thread pending "
+ "vCont-resume (%d, %ld, %ld)", tp->ptid.pid(),
+ tp->ptid.lwp (), tp->ptid.tid ());
+
+ /* Check that the thread wasn't resumed with a signal.
+ Generating a phony stop would result in losing the
+ signal. */
+ const resumed_pending_vcont_info &info
+ = remote_thr->resumed_pending_vcont_info ();
+ gdb_assert (info.sig == GDB_SIGNAL_0);
+
+ stop_reply *sr = new stop_reply ();
+ sr->ptid = tp->ptid;
+ sr->rs = rs;
+ sr->ws.kind = TARGET_WAITKIND_STOPPED;
+ sr->ws.value.sig = GDB_SIGNAL_0;
+ sr->arch = tp->inf->gdbarch;
+ sr->stop_reason = TARGET_STOPPED_BY_NO_REASON;
+ sr->watch_data_address = 0;
+ sr->core = 0;
+ this->push_stop_reply (sr);
+
+ /* Pretend that this thread was actually resumed on the
+ remote target, then stopped. If we leave it in the
+ RESUMED_PENDING_VCONT state and the commit_resumed
+ method is called while the stop reply is still in the
+ queue, we'll end up reporting a stop event to the core
+ for that thread while it is running on the remote
+ target... that would be bad. */
+ remote_thr->set_resumed ();
+ }
+ }
+
/* FIXME: This supports_vCont_probed check is a workaround until
packet_support is per-connection. */
if (packet_support (PACKET_vCont) == PACKET_SUPPORT_UNKNOWN
@@ -6990,36 +7105,6 @@ remote_console_output (const char *msg)
gdb_stdtarg->flush ();
}
-struct stop_reply : public notif_event
-{
- ~stop_reply ();
-
- /* The identifier of the thread about this event */
- ptid_t ptid;
-
- /* The remote state this event is associated with. When the remote
- connection, represented by a remote_state object, is closed,
- all the associated stop_reply events should be released. */
- struct remote_state *rs;
-
- struct target_waitstatus ws;
-
- /* The architecture associated with the expedited registers. */
- gdbarch *arch;
-
- /* Expedited registers. This makes remote debugging a bit more
- efficient for those targets that provide critical registers as
- part of their normal status mechanism (as another roundtrip to
- fetch them is avoided). */
- std::vector<cached_reg_t> regcache;
-
- enum target_stop_reason stop_reason;
-
- CORE_ADDR watch_data_address;
-
- int core;
-};
-
/* Return the length of the stop reply queue. */
int
diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
index 74103bf..efbb31b 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.c
@@ -14,7 +14,7 @@ struct dummy_target : public target_ops
void detach (inferior *arg0, int arg1) override;
void disconnect (const char *arg0, int arg1) override;
void resume (ptid_t arg0, int arg1, enum gdb_signal arg2) override;
- void commit_resume () override;
+ void commit_resumed () override;
ptid_t wait (ptid_t arg0, struct target_waitstatus *arg1, target_wait_flags arg2) override;
void fetch_registers (struct regcache *arg0, int arg1) override;
void store_registers (struct regcache *arg0, int arg1) override;
@@ -188,7 +188,7 @@ struct debug_target : public target_ops
void detach (inferior *arg0, int arg1) override;
void disconnect (const char *arg0, int arg1) override;
void resume (ptid_t arg0, int arg1, enum gdb_signal arg2) override;
- void commit_resume () override;
+ void commit_resumed () override;
ptid_t wait (ptid_t arg0, struct target_waitstatus *arg1, target_wait_flags arg2) override;
void fetch_registers (struct regcache *arg0, int arg1) override;
void store_registers (struct regcache *arg0, int arg1) override;
@@ -447,22 +447,22 @@ debug_target::resume (ptid_t arg0, int arg1, enum gdb_signal arg2)
}
void
-target_ops::commit_resume ()
+target_ops::commit_resumed ()
{
- this->beneath ()->commit_resume ();
+ this->beneath ()->commit_resumed ();
}
void
-dummy_target::commit_resume ()
+dummy_target::commit_resumed ()
{
}
void
-debug_target::commit_resume ()
+debug_target::commit_resumed ()
{
- fprintf_unfiltered (gdb_stdlog, "-> %s->commit_resume (...)\n", this->beneath ()->shortname ());
- this->beneath ()->commit_resume ();
- fprintf_unfiltered (gdb_stdlog, "<- %s->commit_resume (", this->beneath ()->shortname ());
+ fprintf_unfiltered (gdb_stdlog, "-> %s->commit_resumed (...)\n", this->beneath ()->shortname ());
+ this->beneath ()->commit_resumed ();
+ fprintf_unfiltered (gdb_stdlog, "<- %s->commit_resumed (", this->beneath ()->shortname ());
fputs_unfiltered (")\n", gdb_stdlog);
}
diff --git a/gdb/target.c b/gdb/target.c
index 51832c5..0da035e 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -2597,6 +2597,9 @@ target_wait (ptid_t ptid, struct target_waitstatus *status,
target_wait_flags options)
{
target_ops *target = current_inferior ()->top_target ();
+ process_stratum_target *proc_target = current_inferior ()->process_target ();
+
+ gdb_assert (!proc_target->commit_resumed_state);
if (!target->can_async_p ())
gdb_assert ((options & TARGET_WNOHANG) == 0);
@@ -2653,6 +2656,7 @@ void
target_resume (ptid_t ptid, int step, enum gdb_signal signal)
{
process_stratum_target *curr_target = current_inferior ()->process_target ();
+ gdb_assert (!curr_target->commit_resumed_state);
target_dcache_invalidate ();
@@ -2666,26 +2670,13 @@ target_resume (ptid_t ptid, int step, enum gdb_signal signal)
clear_inline_frame_state (curr_target, ptid);
}
-/* If true, target_commit_resume is a nop. */
-static int defer_target_commit_resume;
-
/* See target.h. */
void
-target_commit_resume (void)
-{
- if (defer_target_commit_resume)
- return;
-
- current_inferior ()->top_target ()->commit_resume ();
-}
-
-/* See target.h. */
-
-scoped_restore_tmpl<int>
-make_scoped_defer_target_commit_resume ()
+target_commit_resumed ()
{
- return make_scoped_restore (&defer_target_commit_resume, 1);
+ gdb_assert (current_inferior ()->process_target ()->commit_resumed_state);
+ current_inferior ()->top_target ()->commit_resumed ();
}
void
@@ -3761,6 +3752,10 @@ target_update_thread_list (void)
void
target_stop (ptid_t ptid)
{
+ process_stratum_target *proc_target = current_inferior ()->process_target ();
+
+ gdb_assert (!proc_target->commit_resumed_state);
+
if (!may_stop)
{
warning (_("May not interrupt or stop the target, ignoring attempt"));
diff --git a/gdb/target.h b/gdb/target.h
index 5d14edb..0aef372 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -485,8 +485,15 @@ struct target_ops
int TARGET_DEBUG_PRINTER (target_debug_print_step),
enum gdb_signal)
TARGET_DEFAULT_NORETURN (noprocess ());
- virtual void commit_resume ()
+
+ /* Ensure that all resumed threads are committed to the target.
+
+ See the description of
+ process_stratum_target::commit_resumed_state for more
+ details. */
+ virtual void commit_resumed ()
TARGET_DEFAULT_IGNORE ();
+
/* See target_wait's description. Note that implementations of
this method must not assume that inferior_ptid on entry is
pointing at the thread or inferior that ends up reporting an
@@ -1463,23 +1470,11 @@ extern void target_disconnect (const char *, int);
target_commit_resume below. */
extern void target_resume (ptid_t ptid, int step, enum gdb_signal signal);
-/* Commit a series of resumption requests previously prepared with
- target_resume calls.
-
- GDB always calls target_commit_resume after calling target_resume
- one or more times. A target may thus use this method in
- coordination with the target_resume method to batch target-side
- resumption requests. In that case, the target doesn't actually
- resume in its target_resume implementation. Instead, it prepares
- the resumption in target_resume, and defers the actual resumption
- to target_commit_resume. E.g., the remote target uses this to
- coalesce multiple resumption requests in a single vCont packet. */
-extern void target_commit_resume ();
-
-/* Setup to defer target_commit_resume calls, and reactivate
- target_commit_resume on destruction, if it was previously
- active. */
-extern scoped_restore_tmpl<int> make_scoped_defer_target_commit_resume ();
+/* Ensure that all resumed threads are committed to the target.
+
+ See the description of process_stratum_target::commit_resumed_state
+ for more details. */
+extern void target_commit_resumed ();
/* For target_read_memory see target/target.h. */
diff --git a/gdb/top.c b/gdb/top.c
index 3be9507..31b751f 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -517,6 +517,13 @@ wait_sync_command_done (void)
scoped_restore save_ui = make_scoped_restore (&current_ui);
struct ui *ui = current_ui;
+ /* We're about to wait until the target stops after having resumed
+ it so must force-commit resumptions, in case we're being called
+ in some context where a scoped_disable_commit_resumed object is
+ active. I.e., this function is a commit-resumed sync/flush
+ point. */
+ scoped_enable_commit_resumed enable ("sync wait");
+
while (gdb_do_one_event () >= 0)
if (ui->prompt_state != PROMPT_BLOCKED)
break;