This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Make linux-nat.c's non-stop mode target_stop implementation really asynchronous.
- From: Pedro Alves <pedro at codesourcery dot com>
- To: gdb-patches at sourceware dot org
- Date: Fri, 9 Sep 2011 20:51:55 +0100
- Subject: Make linux-nat.c's non-stop mode target_stop implementation really asynchronous.
This makes non-stop mode's "(gdb) interrupt", or rather the linux-nat.c's
target_stop implementation truly asynchronous, like remote.c's counterpart,
closer to gdbserver's linux-low.c implementation.
Currently, we
- queue a SIGSTOP
- wait for it to be reported
- leave a event pending
- tell the event loop there's something to handle
all from within linux_nat_stop_lwp (from interrupt -> target_stop).
Back to the event loop, we'd noticing a target event,
and go do target_wait, collecting the pending event.
Now we'll just
- queue a SIGSTOP and leave.
Eventually, the lwp really stops, and we'll collect
the stop as usual. We do need to record in the lwp that
we're stopping it, and that's what the new last_resume_kind
field is for.
(I found I needed this after making the core stopping all
lwps itself instead of linux_nat_wait_1 doing it. That is,
all-stop on top of the target running on non-stop mode.
I was noticing bad things happening with the event loop
losing the pending event. I thought I might as well make
this work as always intended and get rid of the need to
leave the events pending and do async_file_mark() ...)
Tested on x86_64-linux and applied.
--
Pedro Alves
2011-09-09 Pedro Alves <pedro@codesourcery.com>
* linux-nat.h (enum resume_kind): New.
(struct lwp_info) <last_resume_kind>: New field.
* linux-nat.c (linux_child_follow_fork): Set last_resume_kind to
resume_stop on the new lwp.
(add_lwp): Set last_resume_kind as resume_continue by default.
(lin_lwp_attach_lwp): Set last_resume_kind as resume_stop.
(resume_lwp): New, factored out from resume_callback. Also check
for pending status in lp->waitstatus.
(resume_callback): Reimplement.
(resume_clear_callback): Set last_resume_kind as resume_stop.
(resume_set_callback): Set last_resume_kind as resume_continue.
(linux_nat_resume, linux_handle_extended_wait): Set
last_resume_kind.
(running_callback): Also check lp->waitstatus for pending events.
(select_singlestep_lwp_callback): Check that lp->last_resume_kind
is resume_step.
(stop_and_resume_callback): Don't re-resume if the core wanted the
lwp stopped. Use resume_lwp instead of resume_callback. Avoid
using an invalidated pointer.
(linux_nat_filter_event): Don't discard SIGSTOPs as delayed
SIGSTOPs if the core wanted the LWP to stop.
(linux_nat_wait_1) Don't consume a pending SIGSTOP if the core
wanted the lwp to stop. If the core wanted the lwp to stop, and
the lwp stopped with a SIGSTOP, report a TARGET_SIGNAL_0 instead
of TARGET_SIGNAL_STOP.
(linux_nat_stop_lwp): Don't synchronously wait for the lwp to stop
here. Instead, signal the lwp, and set the last_resume_kind to
resume_stop.
---
gdb/linux-nat.c | 189 ++++++++++++++++++++++++++++++++++----------------------
gdb/linux-nat.h | 17 +++++
2 files changed, 133 insertions(+), 73 deletions(-)
Index: src/gdb/linux-nat.h
===================================================================
--- src.orig/gdb/linux-nat.h 2011-09-09 14:45:52.329762094 +0100
+++ src/gdb/linux-nat.h 2011-09-09 15:41:34.399762678 +0100
@@ -22,6 +22,20 @@
#include <signal.h>
+/* Ways to "resume" a thread. */
+
+enum resume_kind
+{
+ /* Thread should continue. */
+ resume_continue,
+
+ /* Thread should single-step. */
+ resume_step,
+
+ /* Thread should be stopped. */
+ resume_stop
+};
+
/* Structure describing an LWP. This is public only for the purposes
of ALL_LWPS; target-specific code should generally not access it
directly. */
@@ -52,6 +66,9 @@ struct lwp_info
didn't try to let the LWP run. */
int resumed;
+ /* The last resume GDB requested on this thread. */
+ enum resume_kind last_resume_kind;
+
/* If non-zero, a pending wait status. */
int status;
Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c 2011-09-09 14:46:25.649762100 +0100
+++ src/gdb/linux-nat.c 2011-09-09 20:07:34.839765470 +0100
@@ -701,6 +701,7 @@ holding the child stopped. Try \"set de
add_thread (inferior_ptid);
child_lp = add_lwp (inferior_ptid);
child_lp->stopped = 1;
+ child_lp->last_resume_kind = resume_stop;
/* If this is a vfork child, then the address-space is
shared with the parent. */
@@ -891,6 +892,7 @@ holding the child stopped. Try \"set de
add_thread (inferior_ptid);
child_lp = add_lwp (inferior_ptid);
child_lp->stopped = 1;
+ child_lp->last_resume_kind = resume_stop;
/* If this is a vfork child, then the address-space is shared
with the parent. If we detached from the parent, then we can
@@ -1158,6 +1160,7 @@ add_lwp (ptid_t ptid)
memset (lp, 0, sizeof (struct lwp_info));
+ lp->last_resume_kind = resume_continue;
lp->waitstatus.kind = TARGET_WAITKIND_IGNORE;
lp->ptid = ptid;
@@ -1515,6 +1518,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
lp->stopped = 1;
}
+ lp->last_resume_kind = resume_stop;
restore_child_signals_mask (&prev_mask);
return 0;
}
@@ -1829,46 +1833,61 @@ linux_nat_detach (struct target_ops *ops
/* Resume LP. */
-static int
-resume_callback (struct lwp_info *lp, void *data)
+static void
+resume_lwp (struct lwp_info *lp, int step)
{
- struct inferior *inf = find_inferior_pid (GET_PID (lp->ptid));
-
- if (lp->stopped && inf->vfork_child != NULL)
+ if (lp->stopped)
{
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "RC: Not resuming %s (vfork parent)\n",
- target_pid_to_str (lp->ptid));
+ struct inferior *inf = find_inferior_pid (GET_PID (lp->ptid));
+
+ if (inf->vfork_child != NULL)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RC: Not resuming %s (vfork parent)\n",
+ target_pid_to_str (lp->ptid));
+ }
+ else if (lp->status == 0
+ && lp->waitstatus.kind == TARGET_WAITKIND_IGNORE)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RC: PTRACE_CONT %s, 0, 0 (resuming sibling)\n",
+ target_pid_to_str (lp->ptid));
+
+ linux_ops->to_resume (linux_ops,
+ pid_to_ptid (GET_LWP (lp->ptid)),
+ step, TARGET_SIGNAL_0);
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RC: PTRACE_CONT %s, 0, 0 (resume sibling)\n",
+ target_pid_to_str (lp->ptid));
+ lp->stopped = 0;
+ lp->step = step;
+ memset (&lp->siginfo, 0, sizeof (lp->siginfo));
+ lp->stopped_by_watchpoint = 0;
+ }
+ else
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "RC: Not resuming sibling %s (has pending)\n",
+ target_pid_to_str (lp->ptid));
+ }
}
- else if (lp->stopped && lp->status == 0)
+ else
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
- "RC: PTRACE_CONT %s, 0, 0 (resuming sibling)\n",
- target_pid_to_str (lp->ptid));
-
- linux_ops->to_resume (linux_ops,
- pid_to_ptid (GET_LWP (lp->ptid)),
- 0, TARGET_SIGNAL_0);
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "RC: PTRACE_CONT %s, 0, 0 (resume sibling)\n",
+ "RC: Not resuming sibling %s (not stopped)\n",
target_pid_to_str (lp->ptid));
- lp->stopped = 0;
- lp->step = 0;
- memset (&lp->siginfo, 0, sizeof (lp->siginfo));
- lp->stopped_by_watchpoint = 0;
}
- else if (lp->stopped && debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "RC: Not resuming sibling %s (has pending)\n",
- target_pid_to_str (lp->ptid));
- else if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "RC: Not resuming sibling %s (not stopped)\n",
- target_pid_to_str (lp->ptid));
+}
+static int
+resume_callback (struct lwp_info *lp, void *data)
+{
+ resume_lwp (lp, 0);
return 0;
}
@@ -1876,6 +1895,7 @@ static int
resume_clear_callback (struct lwp_info *lp, void *data)
{
lp->resumed = 0;
+ lp->last_resume_kind = resume_stop;
return 0;
}
@@ -1883,6 +1903,7 @@ static int
resume_set_callback (struct lwp_info *lp, void *data)
{
lp->resumed = 1;
+ lp->last_resume_kind = resume_continue;
return 0;
}
@@ -1922,6 +1943,7 @@ linux_nat_resume (struct target_ops *ops
/* Remember if we're stepping. */
lp->step = step;
+ lp->last_resume_kind = step ? resume_step : resume_continue;
/* If we have a pending wait status for this thread, there is no
point in resuming the process. But first make sure that
@@ -2286,6 +2308,7 @@ linux_handle_extended_wait (struct lwp_i
new_lp->stopped = 0;
new_lp->resumed = 1;
+ new_lp->last_resume_kind = resume_continue;
signo = (status
? target_signal_from_host (WSTOPSIG (status))
@@ -2918,7 +2941,10 @@ status_callback (struct lwp_info *lp, vo
static int
running_callback (struct lwp_info *lp, void *data)
{
- return (lp->stopped == 0 || (lp->status != 0 && lp->resumed));
+ return (!lp->stopped
+ || ((lp->status != 0
+ || lp->waitstatus.kind != TARGET_WAITKIND_IGNORE)
+ && lp->resumed));
}
/* Count the LWP's that have had events. */
@@ -2942,7 +2968,8 @@ count_events_callback (struct lwp_info *
static int
select_singlestep_lwp_callback (struct lwp_info *lp, void *data)
{
- if (lp->step && lp->status != 0)
+ if (lp->last_resume_kind == resume_step
+ && lp->status != 0)
return 1;
else
return 0;
@@ -3094,19 +3121,22 @@ resumed_callback (struct lwp_info *lp, v
static int
stop_and_resume_callback (struct lwp_info *lp, void *data)
{
- struct lwp_info *ptr;
-
- if (!lp->stopped && !lp->signalled)
+ if (!lp->stopped)
{
+ enum resume_kind last_resume_kind = lp->last_resume_kind;
+ ptid_t ptid = lp->ptid;
+
stop_callback (lp, NULL);
stop_wait_callback (lp, NULL);
- /* Resume if the lwp still exists. */
- for (ptr = lwp_list; ptr; ptr = ptr->next)
- if (lp == ptr)
- {
- resume_callback (lp, NULL);
- resume_set_callback (lp, NULL);
- }
+
+ /* Resume if the lwp still exists, and the core wanted it
+ running. */
+ if (last_resume_kind != resume_stop)
+ {
+ lp = find_lwp_pid (ptid);
+ if (lp)
+ resume_lwp (lp, lp->step);
+ }
}
return 0;
}
@@ -3267,25 +3297,29 @@ linux_nat_filter_event (int lwpid, int s
"LLW: Delayed SIGSTOP caught for %s.\n",
target_pid_to_str (lp->ptid));
- /* This is a delayed SIGSTOP. */
lp->signalled = 0;
- registers_changed ();
+ if (lp->last_resume_kind != resume_stop)
+ {
+ /* This is a delayed SIGSTOP. */
- linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
+ registers_changed ();
+
+ linux_ops->to_resume (linux_ops, pid_to_ptid (GET_LWP (lp->ptid)),
lp->step, TARGET_SIGNAL_0);
- if (debug_linux_nat)
- fprintf_unfiltered (gdb_stdlog,
- "LLW: %s %s, 0, 0 (discard SIGSTOP)\n",
- lp->step ?
- "PTRACE_SINGLESTEP" : "PTRACE_CONT",
- target_pid_to_str (lp->ptid));
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "LLW: %s %s, 0, 0 (discard SIGSTOP)\n",
+ lp->step ?
+ "PTRACE_SINGLESTEP" : "PTRACE_CONT",
+ target_pid_to_str (lp->ptid));
- lp->stopped = 0;
- gdb_assert (lp->resumed);
+ lp->stopped = 0;
+ gdb_assert (lp->resumed);
- /* Discard the event. */
- return NULL;
+ /* Discard the event. */
+ return NULL;
+ }
}
/* Make sure we don't report a SIGINT that we have already displayed
@@ -3435,7 +3469,7 @@ retry:
lp = NULL;
}
- if (lp && lp->signalled)
+ if (lp && lp->signalled && lp->last_resume_kind != resume_stop)
{
/* A pending SIGSTOP may interfere with the normal stream of
events. In a typical case where interference is a problem,
@@ -3747,7 +3781,10 @@ retry:
iterate_over_lwps (minus_one_ptid, resume_clear_callback, NULL);
}
else
- lp->resumed = 0;
+ {
+ lp->resumed = 0;
+ lp->last_resume_kind = resume_stop;
+ }
if (linux_nat_status_is_event (status))
{
@@ -3770,6 +3807,16 @@ retry:
restore_child_signals_mask (&prev_mask);
+ if (lp->last_resume_kind == resume_stop
+ && ourstatus->kind == TARGET_WAITKIND_STOPPED
+ && WSTOPSIG (status) == SIGSTOP)
+ {
+ /* A thread that has been requested to stop by GDB with
+ target_stop, and it stopped cleanly, so report as SIG0. The
+ use of SIGSTOP is an implementation detail. */
+ ourstatus->value.sig = TARGET_SIGNAL_0;
+ }
+
if (ourstatus->kind == TARGET_WAITKIND_EXITED
|| ourstatus->kind == TARGET_WAITKIND_SIGNALLED)
lp->core = -1;
@@ -5426,22 +5473,18 @@ linux_nat_stop_lwp (struct lwp_info *lwp
target_pid_to_str (lwp->ptid));
- stop_callback (lwp, NULL);
- stop_wait_callback (lwp, NULL);
-
- /* If the lwp exits while we try to stop it, there's nothing
- else to do. */
- lwp = find_lwp_pid (ptid);
- if (lwp == NULL)
- return 0;
+ if (lwp->last_resume_kind == resume_stop)
+ {
+ if (debug_linux_nat)
+ fprintf_unfiltered (gdb_stdlog,
+ "linux-nat: already stopping LWP %ld at "
+ "GDB's request\n",
+ ptid_get_lwp (lwp->ptid));
+ return 0;
+ }
- /* If we didn't collect any signal other than SIGSTOP while
- stopping the LWP, push a SIGNAL_0 event. In either case, the
- event-loop will end up calling target_wait which will collect
- these. */
- if (lwp->status == 0)
- lwp->status = W_STOPCODE (0);
- async_file_mark ();
+ stop_callback (lwp, NULL);
+ lwp->last_resume_kind = resume_stop;
}
else
{