aboutsummaryrefslogtreecommitdiff
path: root/gdb/gdbserver/linux-low.c
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2009-04-01 22:48:05 +0000
committerPedro Alves <palves@redhat.com>2009-04-01 22:48:05 +0000
commitbd99dc858385792aad304d42f4a47791fd8d3272 (patch)
treeee30ea92e8d9e1ddbfb5a2f5f2a26b0ed12364b6 /gdb/gdbserver/linux-low.c
parent5b1c542ea1c4ff247db390bd24a9e0665d0c2e48 (diff)
downloadgdb-bd99dc858385792aad304d42f4a47791fd8d3272.zip
gdb-bd99dc858385792aad304d42f4a47791fd8d3272.tar.gz
gdb-bd99dc858385792aad304d42f4a47791fd8d3272.tar.bz2
Non-stop mode support.
* server.h (non_stop): Declare. (gdb_client_data, handler_func): Declare. (delete_file_handler, add_file_handler, start_event_loop): Declare. (handle_serial_event, handle_target_event, push_event) (putpkt_notif): Declare. * target.h (enum resume_kind): New. (struct thread_resume): Replace `step' field by `kind' field. (TARGET_WNOHANG): Define. (struct target_ops) <wait>: Add `options' argument. <supports_non_stop, async, start_non_stop>: New fields. (target_supports_non_stop, target_async): New. (start_non_stop): Declare. (mywait): Add `options' argument. * target.c (mywait): Add `options' argument. Print child exit notifications here. (start_non_stop): New. * server.c (non_stop, own_buf, mem_buf): New globals. (struct vstop_notif): New. (notif_queue): New global. (queue_stop_reply, push_event, discard_queued_stop_replies) (send_next_stop_reply): New. (start_inferior): Adjust to use resume_kind. Adjust to mywait interface changes. (attach_inferior): In non-stop mode, don't wait for the target here. (handle_general_set): Handle QNonStop. (handle_query): When handling qC, return the current general thread, instead of the first thread of the list. (handle_query): If the backend supports non-stop mode, include QNonStop+ in the qSupported query response. (handle_v_cont): Adjust to use resume_kind. Handle resume_stop and non-stop mode. (handle_v_attach, handle_v_run): Handle non-stop mode. (handle_v_stopped): New. (handle_v_requests): Report support for vCont;t. Handle vStopped. (myresume): Adjust to use resume_kind. Handle non-stop. (queue_stop_reply_callback): New. (handle_status): Handle non-stop mode. (main): Clear non_stop flag on reconnection. Use the event-loop. Refactor serial protocol handling from here ... (process_serial_event): ... to this new function. When GDB selects any thread, select one here. In non-stop mode, wait until GDB acks all pending events before exiting. (handle_serial_event, handle_target_event): New. * remote-utils.c (remote_open): Install remote_desc in the event loop. (remote_close): Remove remote_desc from the event loop. (putpkt_binary): Rename to... (putpkt_binary_1): ... this. Add `is_notic' argument. Handle it. (putpkt_binary): New as wrapper around putpkt_binary_1. (putpkt_notif): New. (prepare_resume_reply): In non-stop mode, don't change the general_thread. * event-loop.c: New. * Makefile.in (OBJ): Add event-loop.o. (event-loop.o): New rule. * linux-low.h (pid_of): Moved here. (lwpid_of): New. (get_lwp_thread): Use lwpid_of. (struct lwp_info): Delete `lwpid' field. Add `suspended' field. * linux-low.c (pid_of): Delete. (inferior_pid): Use lwpid_of. (linux_event_pipe): New. (target_is_async_p): New. (delete_lwp): New. (handle_extended_wait): Use lwpid_of. (add_lwp): Don't set lwpid field. (linux_attach_lwp): Adjust debug output. Use lwpid_of. (linux_kill_one_lwp): If killing a running lwp, stop it first. Use lwpid_of. Adjust to linux_wait_for_event interface changes. (linux_detach_one_lwp): If detaching from a running lwp, stop it first. Adjust to linux_wait_for_event interface changes. Use lwpid_of. (linux_detach): Don't delete the main lwp here. (linux_join): Use my_waitpid. Avoid signal_pid. Use lwpid_of. (status_pending_p): Don't consider explicitly suspended lwps. (linux_wait_for_lwp): Take an integer pid instead of a lwp_info pointer. Add OPTIONS argument. Change return type to int. Use my_waitpid instead of sleeping. Handle WNOHANG. Use lwpid_of. (linux_wait_for_event): Take an integer pid instead of a lwp_info pointer. Add status pointer argument. Return a pid instead of a status. Use lwpid_of. Adjust to linux_wait_for_lwp interface changes. In non-stop mode, don't switch to a random thread. (linux_wait): Rename to... (linux_wait_1): ... this. Add target_options argument, and handle it. Adjust to use resume_kind. Use lwpid_of. In non-stop mode, don't handle the continue thread. Handle TARGET_WNOHANG. Merge clean exit and signal exit code. Don't stop all threads in non-stop mode. In all-stop mode, only stop all threads when reporting a stop to GDB. Handle explicit thread stop requests. (async_file_flush, async_file_mark): New. (linux_wait): New. (send_sigstop): Use lwpid_of. (wait_for_sigstop): Use lwpid_of. Adjust to linux_wait_for_event interface changes. In non-stop mode, don't switch to a random thread. (linux_resume_one_lwp): Use lwpid_of. (linux_continue_one_thread, linux_queue_one_thread): Merge into ... (linux_resume_one_thread): ... this. Handle resume_stop. In non-stop mode, don't look for pending flag in all threads. (resume_status_pending_p): Don't consider explicitly suspended threads. (my_waitpid): Reimplement. Emulate __WALL. (linux_request_interrupt, linux_read_offsets, linux_xfer_siginfo): Use lwpid_of. (sigchld_handler, linux_supports_non_stop, linux_async) (linux_start_non_stop): New. (linux_target_ops): Register linux_supports_non_stop, linux_async and linux_start_non_stop. (initialize_low): Install SIGCHLD handler. * thread-db.c (thread_db_create_event, find_one_thread) (thread_db_get_tls_address): Use lwpid_of. * win32-low.c (win32_detach): Adjust to use resume_kind. (win32_wait): Add `options' argument. * spu-low.c (spu_resume): Adjust to use resume_kind. (spu_wait): Add `options' argument.
Diffstat (limited to 'gdb/gdbserver/linux-low.c')
-rw-r--r--gdb/gdbserver/linux-low.c893
1 files changed, 638 insertions, 255 deletions
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index c0b1e53..ba1d7b4 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -118,7 +118,7 @@ static void linux_resume_one_lwp (struct inferior_list_entry *entry,
int step, int signal, siginfo_t *info);
static void linux_resume (struct thread_resume *resume_info, size_t n);
static void stop_all_lwps (void);
-static int linux_wait_for_event (struct thread_info *child);
+static int linux_wait_for_event (int pid, int *wstat, int options);
static int check_removed_breakpoint (struct lwp_info *event_child);
static void *add_lwp (unsigned long pid);
static int my_waitpid (int pid, int *status, int flags);
@@ -139,10 +139,30 @@ static char *disabled_regsets;
static int num_regsets;
#endif
-#define pid_of(proc) ((proc)->head.id)
-
/* FIXME: Delete eventually. */
-#define inferior_pid (pid_of (get_thread_lwp (current_inferior)))
+#define inferior_pid (lwpid_of (get_thread_lwp (current_inferior)))
+
+/* The read/write ends of the pipe registered as waitable file in the
+ event loop. */
+static int linux_event_pipe[2] = { -1, -1 };
+
+/* True if we're currently in async mode. */
+#define target_is_async_p() (linux_event_pipe[0] != -1)
+
+static void send_sigstop (struct inferior_list_entry *entry);
+static void wait_for_sigstop (struct inferior_list_entry *entry);
+
+static void
+delete_lwp (struct lwp_info *lwp)
+{
+ remove_thread (get_lwp_thread (lwp));
+ remove_inferior (&all_lwps, &lwp->head);
+ free (lwp);
+}
+
+/* Handle a GNU/Linux extended wait response. If we see a clone
+ event, we need to add the new LWP to our list (and not report the
+ trap to higher layers). */
static void
handle_extended_wait (struct lwp_info *event_child, int wstat)
@@ -155,7 +175,7 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
unsigned long new_pid;
int ret, status = W_STOPCODE (SIGSTOP);
- ptrace (PTRACE_GETEVENTMSG, inferior_pid, 0, &new_pid);
+ ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_child), 0, &new_pid);
/* If we haven't already seen the new PID stop, wait for it now. */
if (! pull_pid_from_list (&stopped_pids, new_pid))
@@ -177,7 +197,7 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
new_lwp = (struct lwp_info *) add_lwp (new_pid);
add_thread (new_pid, new_lwp, new_pid);
- new_thread_notify (thread_id_to_gdb_id (new_lwp->lwpid));
+ new_thread_notify (thread_id_to_gdb_id (lwpid_of (new_lwp)));
/* Normally we will get the pending SIGSTOP. But in some cases
we might get another signal delivered to the group first.
@@ -254,7 +274,6 @@ add_lwp (unsigned long pid)
memset (lwp, 0, sizeof (*lwp));
lwp->head.id = pid;
- lwp->lwpid = pid;
add_inferior_to_list (&all_lwps, &lwp->head);
@@ -323,7 +342,7 @@ linux_attach_lwp (unsigned long pid)
}
else
/* If we fail to attach to a process, report an error. */
- error ("Cannot attach to process %ld: %s (%d)\n", pid,
+ error ("Cannot attach to lwp %ld: %s (%d)\n", pid,
strerror (errno), errno);
}
@@ -333,7 +352,7 @@ linux_attach_lwp (unsigned long pid)
new_lwp = (struct lwp_info *) add_lwp (pid);
add_thread (pid, new_lwp, pid);
- new_thread_notify (thread_id_to_gdb_id (new_lwp->lwpid));
+ new_thread_notify (thread_id_to_gdb_id (lwpid_of (new_lwp)));
/* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH
brings it to a halt.
@@ -377,10 +396,13 @@ linux_attach (unsigned long pid)
linux_attach_lwp (pid);
- /* Don't ignore the initial SIGSTOP if we just attached to this process.
- It will be collected by wait shortly. */
- lwp = (struct lwp_info *) find_inferior_id (&all_lwps, pid);
- lwp->stop_expected = 0;
+ if (!non_stop)
+ {
+ /* Don't ignore the initial SIGSTOP if we just attached to this
+ process. It will be collected by wait shortly. */
+ lwp = (struct lwp_info *) find_inferior_id (&all_lwps, pid);
+ lwp->stop_expected = 0;
+ }
new_inferior = 1;
@@ -394,6 +416,7 @@ linux_kill_one_lwp (struct inferior_list_entry *entry)
{
struct thread_info *thread = (struct thread_info *) entry;
struct lwp_info *lwp = get_thread_lwp (thread);
+ int pid;
int wstat;
/* We avoid killing the first thread here, because of a Linux kernel (at
@@ -403,13 +426,18 @@ linux_kill_one_lwp (struct inferior_list_entry *entry)
if (entry == all_threads.head)
return;
+ /* If we're killing a running inferior, make sure it is stopped
+ first, as PTRACE_KILL will not work otherwise. */
+ if (!lwp->stopped)
+ send_sigstop (&lwp->head);
+
do
{
- ptrace (PTRACE_KILL, pid_of (lwp), 0, 0);
+ ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0);
/* Make sure it died. The loop is most likely unnecessary. */
- wstat = linux_wait_for_event (thread);
- } while (WIFSTOPPED (wstat));
+ pid = linux_wait_for_event (lwpid_of (lwp), &wstat, __WALL);
+ } while (pid > 0 && WIFSTOPPED (wstat));
}
static void
@@ -418,6 +446,7 @@ linux_kill (void)
struct thread_info *thread = (struct thread_info *) all_threads.head;
struct lwp_info *lwp;
int wstat;
+ int pid;
if (thread == NULL)
return;
@@ -427,17 +456,25 @@ linux_kill (void)
/* See the comment in linux_kill_one_lwp. We did not kill the first
thread in the list, so do so now. */
lwp = get_thread_lwp (thread);
+
+ if (debug_threads)
+ fprintf (stderr, "lk_1: killing lwp %ld\n", lwpid_of (lwp));
+
+ /* If we're killing a running inferior, make sure it is stopped
+ first, as PTRACE_KILL will not work otherwise. */
+ if (!lwp->stopped)
+ send_sigstop (&lwp->head);
+
do
{
- ptrace (PTRACE_KILL, pid_of (lwp), 0, 0);
+ ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0);
/* Make sure it died. The loop is most likely unnecessary. */
- wstat = linux_wait_for_event (thread);
- } while (WIFSTOPPED (wstat));
+ pid = linux_wait_for_event (lwpid_of (lwp), &wstat, __WALL);
+ } while (pid > 0 && WIFSTOPPED (wstat));
+ delete_lwp (lwp);
clear_inferiors ();
- free (all_lwps.head);
- all_lwps.head = all_lwps.tail = NULL;
}
static void
@@ -446,6 +483,28 @@ linux_detach_one_lwp (struct inferior_list_entry *entry)
struct thread_info *thread = (struct thread_info *) entry;
struct lwp_info *lwp = get_thread_lwp (thread);
+ /* If we're detaching from a running inferior, make sure it is
+ stopped first, as PTRACE_DETACH will not work otherwise. */
+ if (!lwp->stopped)
+ {
+ int pid = lwpid_of (lwp);
+
+ stopping_threads = 1;
+ send_sigstop (&lwp->head);
+
+ /* If this detects a new thread through a clone event, the new
+ thread is appended to the end of the lwp list, so we'll
+ eventually detach from it. */
+ wait_for_sigstop (&lwp->head);
+ stopping_threads = 0;
+
+ /* If LWP exits while we're trying to stop it, there's nothing
+ left to do. */
+ lwp = (struct lwp_info *) find_inferior_id (&all_lwps, pid);
+ if (lwp == NULL)
+ return;
+ }
+
/* Make sure the process isn't stopped at a breakpoint that's
no longer there. */
check_removed_breakpoint (lwp);
@@ -455,11 +514,12 @@ linux_detach_one_lwp (struct inferior_list_entry *entry)
to collect the SIGSTOP, but is fairly likely to. */
if (lwp->stop_expected)
{
+ int wstat;
/* Clear stop_expected, so that the SIGSTOP will be reported. */
lwp->stop_expected = 0;
if (lwp->stopped)
linux_resume_one_lwp (&lwp->head, 0, 0, NULL);
- linux_wait_for_event (thread);
+ linux_wait_for_event (lwpid_of (lwp), &wstat, __WALL);
}
/* Flush any pending changes to the process's registers. */
@@ -467,7 +527,9 @@ linux_detach_one_lwp (struct inferior_list_entry *entry)
get_lwp_thread (lwp));
/* Finally, let it resume. */
- ptrace (PTRACE_DETACH, pid_of (lwp), 0, 0);
+ ptrace (PTRACE_DETACH, lwpid_of (lwp), 0, 0);
+
+ delete_lwp (lwp);
}
static int
@@ -476,19 +538,21 @@ linux_detach (void)
delete_all_breakpoints ();
for_each_inferior (&all_threads, linux_detach_one_lwp);
clear_inferiors ();
- free (all_lwps.head);
- all_lwps.head = all_lwps.tail = NULL;
return 0;
}
static void
linux_join (void)
{
- extern unsigned long signal_pid;
int status, ret;
+ struct thread_info *thread;
+ struct lwp_info *lwp;
+
+ thread = (struct thread_info *) all_threads.head;
+ lwp = get_thread_lwp (thread);
do {
- ret = waitpid (signal_pid, &status, 0);
+ ret = my_waitpid (lwpid_of (lwp), &status, 0);
if (WIFEXITED (status) || WIFSIGNALED (status))
break;
} while (ret != -1 || errno != ECHILD);
@@ -518,7 +582,7 @@ check_removed_breakpoint (struct lwp_info *event_child)
if (debug_threads)
fprintf (stderr, "Checking for breakpoint in lwp %ld.\n",
- event_child->lwpid);
+ lwpid_of (event_child));
saved_inferior = current_inferior;
current_inferior = get_lwp_thread (event_child);
@@ -573,7 +637,7 @@ status_pending_p (struct inferior_list_entry *entry, void *dummy)
{
struct lwp_info *lwp = (struct lwp_info *) entry;
- if (lwp->status_pending_p)
+ if (lwp->status_pending_p && !lwp->suspended)
if (check_removed_breakpoint (lwp))
{
/* This thread was stopped at a breakpoint, and the breakpoint
@@ -586,43 +650,28 @@ status_pending_p (struct inferior_list_entry *entry, void *dummy)
return 0;
}
- return lwp->status_pending_p;
+ return (lwp->status_pending_p && !lwp->suspended);
}
-static void
-linux_wait_for_lwp (struct lwp_info **childp, int *wstatp)
+static struct lwp_info *
+linux_wait_for_lwp (int pid, int *wstatp, int options)
{
int ret;
- int to_wait_for = -1;
-
- if (*childp != NULL)
- to_wait_for = (*childp)->lwpid;
-
-retry:
- while (1)
- {
- ret = waitpid (to_wait_for, wstatp, WNOHANG);
+ int to_wait_for = pid;
+ struct lwp_info *child = NULL;
- if (ret == -1)
- {
- if (errno != ECHILD)
- perror_with_name ("waitpid");
- }
- else if (ret > 0)
- break;
+ if (debug_threads)
+ fprintf (stderr, "linux_wait_for_lwp: %d\n", pid);
- ret = waitpid (to_wait_for, wstatp, WNOHANG | __WCLONE);
+ options |= __WALL;
- if (ret == -1)
- {
- if (errno != ECHILD)
- perror_with_name ("waitpid (WCLONE)");
- }
- else if (ret > 0)
- break;
+retry:
- usleep (1000);
- }
+ ret = my_waitpid (to_wait_for, wstatp, options);
+ if (ret == 0 || (ret == -1 && errno == ECHILD && (options & WNOHANG)))
+ return NULL;
+ else if (ret == -1)
+ perror_with_name ("waitpid");
if (debug_threads
&& (!WIFSTOPPED (*wstatp)
@@ -630,25 +679,24 @@ retry:
&& WSTOPSIG (*wstatp) != 33)))
fprintf (stderr, "Got an event from %d (%x)\n", ret, *wstatp);
- if (to_wait_for == -1)
- *childp = (struct lwp_info *) find_inferior_id (&all_lwps, ret);
+ child = (struct lwp_info *) find_inferior_id (&all_lwps, ret);
/* If we didn't find a process, one of two things presumably happened:
- A process we started and then detached from has exited. Ignore it.
- A process we are controlling has forked and the new child's stop
was reported to us by the kernel. Save its PID. */
- if (*childp == NULL && WIFSTOPPED (*wstatp))
+ if (child == NULL && WIFSTOPPED (*wstatp))
{
add_pid_to_list (&stopped_pids, ret);
goto retry;
}
- else if (*childp == NULL)
+ else if (child == NULL)
goto retry;
- (*childp)->stopped = 1;
- (*childp)->pending_is_breakpoint = 0;
+ child->stopped = 1;
+ child->pending_is_breakpoint = 0;
- (*childp)->last_status = *wstatp;
+ child->last_status = *wstatp;
/* Architecture-specific setup after inferior is running.
This needs to happen after we have attached to the inferior
@@ -668,54 +716,61 @@ retry:
{
struct thread_info *saved_inferior = current_inferior;
current_inferior = (struct thread_info *)
- find_inferior_id (&all_threads, (*childp)->lwpid);
+ find_inferior_id (&all_threads, lwpid_of (child));
/* For testing only; i386_stop_pc prints out a diagnostic. */
if (the_low_target.get_pc != NULL)
get_stop_pc ();
current_inferior = saved_inferior;
}
+
+ return child;
}
+/* Wait for an event from child PID. If PID is -1, wait for any
+ child. Store the stop status through the status pointer WSTAT.
+ OPTIONS is passed to the waitpid call. Return 0 if no child stop
+ event was found and OPTIONS contains WNOHANG. Return the PID of
+ the stopped child otherwise. */
+
static int
-linux_wait_for_event (struct thread_info *child)
+linux_wait_for_event (int pid, int *wstat, int options)
{
CORE_ADDR stop_pc;
- struct lwp_info *event_child;
- int wstat;
+ struct lwp_info *event_child = NULL;
int bp_status;
+ struct lwp_info *requested_child = NULL;
/* Check for a process with a pending status. */
/* It is possible that the user changed the pending task's registers since
it stopped. We correctly handle the change of PC if we hit a breakpoint
(in check_removed_breakpoint); signals should be reported anyway. */
- if (child == NULL)
+
+ if (pid == -1)
{
event_child = (struct lwp_info *)
find_inferior (&all_lwps, status_pending_p, NULL);
if (debug_threads && event_child)
- fprintf (stderr, "Got a pending child %ld\n", event_child->lwpid);
+ fprintf (stderr, "Got a pending child %ld\n", lwpid_of (event_child));
}
else
{
- event_child = get_thread_lwp (child);
- if (event_child->status_pending_p
- && check_removed_breakpoint (event_child))
- event_child = NULL;
+ requested_child = (struct lwp_info *)
+ find_inferior_id (&all_lwps, pid);
+ if (requested_child->status_pending_p
+ && !check_removed_breakpoint (requested_child))
+ event_child = requested_child;
}
if (event_child != NULL)
{
- if (event_child->status_pending_p)
- {
- if (debug_threads)
- fprintf (stderr, "Got an event from pending child %ld (%04x)\n",
- event_child->lwpid, event_child->status_pending);
- wstat = event_child->status_pending;
- event_child->status_pending_p = 0;
- event_child->status_pending = 0;
- current_inferior = get_lwp_thread (event_child);
- return wstat;
- }
+ if (debug_threads)
+ fprintf (stderr, "Got an event from pending child %ld (%04x)\n",
+ lwpid_of (event_child), event_child->status_pending);
+ *wstat = event_child->status_pending;
+ event_child->status_pending_p = 0;
+ event_child->status_pending = 0;
+ current_inferior = get_lwp_thread (event_child);
+ return lwpid_of (event_child);
}
/* We only enter this loop if no process has a pending wait status. Thus
@@ -724,47 +779,59 @@ linux_wait_for_event (struct thread_info *child)
events. */
while (1)
{
- if (child == NULL)
- event_child = NULL;
- else
- event_child = get_thread_lwp (child);
+ event_child = linux_wait_for_lwp (pid, wstat, options);
- linux_wait_for_lwp (&event_child, &wstat);
+ if ((options & WNOHANG) && event_child == NULL)
+ return 0;
if (event_child == NULL)
error ("event from unknown child");
- current_inferior = (struct thread_info *)
- find_inferior_id (&all_threads, event_child->lwpid);
+ current_inferior = get_lwp_thread (event_child);
/* Check for thread exit. */
- if (! WIFSTOPPED (wstat))
+ if (! WIFSTOPPED (*wstat))
{
+ int lwpid = lwpid_of (event_child);
if (debug_threads)
- fprintf (stderr, "LWP %ld exiting\n", event_child->head.id);
+ fprintf (stderr, "LWP %d exiting\n", lwpid);
/* If the last thread is exiting, just return. */
if (all_threads.head == all_threads.tail)
- return wstat;
+ {
+ if (debug_threads)
+ fprintf (stderr, "LWP %d is last lwp of process\n", lwpid);
+ return lwpid_of (event_child);
+ }
- dead_thread_notify (thread_id_to_gdb_id (event_child->lwpid));
+ dead_thread_notify (thread_id_to_gdb_id (lwpid_of (event_child)));
+ delete_lwp (event_child);
- remove_inferior (&all_lwps, &event_child->head);
- free (event_child);
- remove_thread (current_inferior);
- current_inferior = (struct thread_info *) all_threads.head;
+ if (!non_stop)
+ {
+ current_inferior = (struct thread_info *) all_threads.head;
+ if (debug_threads)
+ fprintf (stderr, "Current inferior is now %ld\n",
+ lwpid_of (get_thread_lwp (current_inferior)));
+ }
+ else
+ {
+ current_inferior = NULL;
+ if (debug_threads)
+ fprintf (stderr, "Current inferior is now <NULL>\n");
+ }
/* If we were waiting for this particular child to do something...
well, it did something. */
- if (child != NULL)
- return wstat;
+ if (requested_child != NULL)
+ return lwpid;
/* Wait for a more interesting event. */
continue;
}
- if (WIFSTOPPED (wstat)
- && WSTOPSIG (wstat) == SIGSTOP
+ if (WIFSTOPPED (*wstat)
+ && WSTOPSIG (*wstat) == SIGSTOP
&& event_child->stop_expected)
{
if (debug_threads)
@@ -775,10 +842,10 @@ linux_wait_for_event (struct thread_info *child)
continue;
}
- if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
- && wstat >> 16 != 0)
+ if (WIFSTOPPED (*wstat) && WSTOPSIG (*wstat) == SIGTRAP
+ && *wstat >> 16 != 0)
{
- handle_extended_wait (event_child, wstat);
+ handle_extended_wait (event_child, *wstat);
continue;
}
@@ -791,42 +858,43 @@ linux_wait_for_event (struct thread_info *child)
special handling to skip the signal handler. */
/* FIXME drow/2002-06-09: Get signal numbers from the inferior's
thread library? */
- if (WIFSTOPPED (wstat)
+ if (WIFSTOPPED (*wstat)
&& !event_child->stepping
&& (
#ifdef USE_THREAD_DB
- (thread_db_active && (WSTOPSIG (wstat) == __SIGRTMIN
- || WSTOPSIG (wstat) == __SIGRTMIN + 1))
+ (thread_db_active
+ && (WSTOPSIG (*wstat) == __SIGRTMIN
+ || WSTOPSIG (*wstat) == __SIGRTMIN + 1))
||
#endif
- (pass_signals[target_signal_from_host (WSTOPSIG (wstat))]
- && (WSTOPSIG (wstat) != SIGSTOP || !stopping_threads))))
+ (pass_signals[target_signal_from_host (WSTOPSIG (*wstat))]
+ && (WSTOPSIG (*wstat) != SIGSTOP || !stopping_threads))))
{
siginfo_t info, *info_p;
if (debug_threads)
fprintf (stderr, "Ignored signal %d for LWP %ld.\n",
- WSTOPSIG (wstat), event_child->head.id);
+ WSTOPSIG (*wstat), lwpid_of (event_child));
- if (ptrace (PTRACE_GETSIGINFO, event_child->lwpid, 0, &info) == 0)
+ if (ptrace (PTRACE_GETSIGINFO, lwpid_of (event_child), 0, &info) == 0)
info_p = &info;
else
info_p = NULL;
linux_resume_one_lwp (&event_child->head,
event_child->stepping,
- WSTOPSIG (wstat), info_p);
+ WSTOPSIG (*wstat), info_p);
continue;
}
/* If this event was not handled above, and is not a SIGTRAP, report
it. */
- if (!WIFSTOPPED (wstat) || WSTOPSIG (wstat) != SIGTRAP)
- return wstat;
+ if (!WIFSTOPPED (*wstat) || WSTOPSIG (*wstat) != SIGTRAP)
+ return lwpid_of (event_child);
/* If this target does not support breakpoints, we simply report the
SIGTRAP; it's of no concern to us. */
if (the_low_target.get_pc == NULL)
- return wstat;
+ return lwpid_of (event_child);
stop_pc = get_stop_pc ();
@@ -877,6 +945,11 @@ linux_wait_for_event (struct thread_info *child)
Otherwise, call the target function to figure out where we need
our temporary breakpoint, create it, and continue executing this
process. */
+
+ /* NOTE: we're lifting breakpoints in non-stop mode. This
+ is currently only used for thread event breakpoints, so
+ it isn't that bad as long as we have PTRACE_EVENT_CLONE
+ events. */
if (bp_status == 2)
/* No need to reinsert. */
linux_resume_one_lwp (&event_child->head, 0, 0, NULL);
@@ -904,7 +977,7 @@ linux_wait_for_event (struct thread_info *child)
do not clear clear the stepping flag yet; we need to check it
in wait_for_sigstop. */
if (event_child->stepping)
- return wstat;
+ return lwpid_of (event_child);
/* A SIGTRAP that we can't explain. It may have been a breakpoint.
Check if it is a breakpoint, and if so mark the process information
@@ -920,7 +993,7 @@ linux_wait_for_event (struct thread_info *child)
event_child->pending_stop_pc = stop_pc;
}
- return wstat;
+ return lwpid_of (event_child);
}
/* NOTREACHED */
@@ -930,45 +1003,58 @@ linux_wait_for_event (struct thread_info *child)
/* Wait for process, returns status. */
static unsigned long
-linux_wait (struct target_waitstatus *ourstatus)
+linux_wait_1 (struct target_waitstatus *ourstatus, int target_options)
{
int w;
- struct thread_info *child = NULL;
- struct lwp_info *lwp;
+ struct thread_info *thread = NULL;
+ struct lwp_info *lwp = NULL;
+ int options;
+ int wait_pid = -1;
+ int pid;
+
+ /* Translate generic target options into linux options. */
+ options = __WALL;
+ if (target_options & TARGET_WNOHANG)
+ options |= WNOHANG;
retry:
+ ourstatus->kind = TARGET_WAITKIND_IGNORE;
+
/* If we were only supposed to resume one thread, only wait for
that thread - if it's still alive. If it died, however - which
can happen if we're coming from the thread death case below -
then we need to make sure we restart the other threads. We could
pick a thread at random or restart all; restarting all is less
arbitrary. */
- if (cont_thread != 0 && cont_thread != -1)
+ if (!non_stop && cont_thread != 0 && cont_thread != -1)
{
- child = (struct thread_info *) find_inferior_id (&all_threads,
- cont_thread);
+ thread = (struct thread_info *) find_inferior_id (&all_threads,
+ cont_thread);
/* No stepping, no signal - unless one is pending already, of course. */
- if (child == NULL)
+ if (thread == NULL)
{
struct thread_resume resume_info;
resume_info.thread = -1;
- resume_info.step = resume_info.sig = 0;
+ resume_info.kind = resume_continue;
+ resume_info.sig = 0;
linux_resume (&resume_info, 1);
}
+ else
+ wait_pid = cont_thread;
}
- w = linux_wait_for_event (child);
- stop_all_lwps ();
+ pid = linux_wait_for_event (wait_pid, &w, options);
+ if (pid == 0) /* only if TARGET_WNOHANG */
+ return pid;
+
+ lwp = get_thread_lwp (current_inferior);
if (must_set_ptrace_flags)
{
- ptrace (PTRACE_SETOPTIONS, inferior_pid, 0, PTRACE_O_TRACECLONE);
+ ptrace (PTRACE_SETOPTIONS, lwpid_of (lwp), 0, PTRACE_O_TRACECLONE);
must_set_ptrace_flags = 0;
}
-
- lwp = get_thread_lwp (current_inferior);
-
/* If we are waiting for a particular child, and it exited,
linux_wait_for_event will return its exit status. Similarly if
the last child exited. If this is not the last child, however,
@@ -983,32 +1069,34 @@ retry:
if (all_threads.head == all_threads.tail)
{
- int pid = pid_of (lwp);
- if (WIFEXITED (w))
+ if (WIFEXITED (w) || WIFSIGNALED (w))
{
- if (debug_threads)
- fprintf (stderr, "\nChild exited with retcode = %x \n",
- WEXITSTATUS (w));
+ int pid;
+
+ pid = pid_of (lwp);
- ourstatus->kind = TARGET_WAITKIND_EXITED;
- ourstatus->value.integer = WEXITSTATUS (w);
+ delete_lwp (lwp);
clear_inferiors ();
- free (all_lwps.head);
- all_lwps.head = all_lwps.tail = NULL;
- return pid;
- }
- else if (!WIFSTOPPED (w))
- {
- if (debug_threads)
- fprintf (stderr, "\nChild terminated with signal = %x \n",
- WTERMSIG (w));
+ current_inferior = NULL;
- ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
- ourstatus->value.sig = target_signal_from_host (WTERMSIG (w));
- clear_inferiors ();
- free (all_lwps.head);
- all_lwps.head = all_lwps.tail = NULL;
+ if (WIFEXITED (w))
+ {
+ ourstatus->kind = TARGET_WAITKIND_EXITED;
+ ourstatus->value.integer = WEXITSTATUS (w);
+
+ if (debug_threads)
+ fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w));
+ }
+ else
+ {
+ ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ ourstatus->value.sig = target_signal_from_host (WTERMSIG (w));
+
+ if (debug_threads)
+ fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w));
+
+ }
return pid;
}
@@ -1019,10 +1107,92 @@ retry:
goto retry;
}
+ /* In all-stop, stop all threads. Be careful to only do this if
+ we're about to report an event to GDB. */
+ if (!non_stop)
+ stop_all_lwps ();
+
ourstatus->kind = TARGET_WAITKIND_STOPPED;
- ourstatus->value.sig = target_signal_from_host (WSTOPSIG (w));
- return lwp->lwpid;
+ if (lwp->suspended && WSTOPSIG (w) == SIGSTOP)
+ {
+ /* A thread that has been requested to stop by GDB with vCont;t,
+ and it stopped cleanly, so report as SIG0. The use of
+ SIGSTOP is an implementation detail. */
+ ourstatus->value.sig = TARGET_SIGNAL_0;
+ }
+ else if (lwp->suspended && WSTOPSIG (w) != SIGSTOP)
+ {
+ /* A thread that has been requested to stop by GDB with vCont;t,
+ but, it stopped for other reasons. Set stop_expected so the
+ pending SIGSTOP is ignored and the LWP is resumed. */
+ lwp->stop_expected = 1;
+ ourstatus->value.sig = target_signal_from_host (WSTOPSIG (w));
+ }
+ else
+ {
+ ourstatus->value.sig = target_signal_from_host (WSTOPSIG (w));
+ }
+
+ if (debug_threads)
+ fprintf (stderr, "linux_wait ret = %ld, %d, %d\n",
+ lwpid_of (lwp),
+ ourstatus->kind,
+ ourstatus->value.sig);
+
+ return lwpid_of (lwp);
+}
+
+/* Get rid of any pending event in the pipe. */
+static void
+async_file_flush (void)
+{
+ int ret;
+ char buf;
+
+ do
+ ret = read (linux_event_pipe[0], &buf, 1);
+ while (ret >= 0 || (ret == -1 && errno == EINTR));
+}
+
+/* Put something in the pipe, so the event loop wakes up. */
+static void
+async_file_mark (void)
+{
+ int ret;
+
+ async_file_flush ();
+
+ do
+ ret = write (linux_event_pipe[1], "+", 1);
+ while (ret == 0 || (ret == -1 && errno == EINTR));
+
+ /* Ignore EAGAIN. If the pipe is full, the event loop will already
+ be awakened anyway. */
+}
+
+static unsigned long
+linux_wait (struct target_waitstatus *ourstatus, int target_options)
+{
+ unsigned long event_ptid;
+
+ if (debug_threads)
+ fprintf (stderr, "linux_wait\n");
+
+ /* Flush the async file first. */
+ if (target_is_async_p ())
+ async_file_flush ();
+
+ event_ptid = linux_wait_1 (ourstatus, target_options);
+
+ /* If at least one stop was reported, there may be more. A single
+ SIGCHLD can signal more than one child stop. */
+ if (target_is_async_p ()
+ && (target_options & TARGET_WNOHANG) != 0
+ && event_ptid != 0)
+ async_file_mark ();
+
+ return event_ptid;
}
/* Send a signal to an LWP. For LinuxThreads, kill is enough; however, if
@@ -1053,17 +1223,19 @@ static void
send_sigstop (struct inferior_list_entry *entry)
{
struct lwp_info *lwp = (struct lwp_info *) entry;
+ int pid;
if (lwp->stopped)
return;
+ pid = lwpid_of (lwp);
+
/* If we already have a pending stop signal for this process, don't
send another. */
if (lwp->stop_expected)
{
if (debug_threads)
- fprintf (stderr, "Have pending sigstop for lwp %ld\n",
- lwp->lwpid);
+ fprintf (stderr, "Have pending sigstop for lwp %d\n", pid);
/* We clear the stop_expected flag so that wait_for_sigstop
will receive the SIGSTOP event (instead of silently resuming and
@@ -1073,27 +1245,32 @@ send_sigstop (struct inferior_list_entry *entry)
}
if (debug_threads)
- fprintf (stderr, "Sending sigstop to lwp %ld\n", lwp->head.id);
+ fprintf (stderr, "Sending sigstop to lwp %d\n", pid);
- kill_lwp (lwp->head.id, SIGSTOP);
+ kill_lwp (pid, SIGSTOP);
}
static void
wait_for_sigstop (struct inferior_list_entry *entry)
{
struct lwp_info *lwp = (struct lwp_info *) entry;
- struct thread_info *saved_inferior, *thread;
+ struct thread_info *saved_inferior;
int wstat;
unsigned long saved_tid;
+ unsigned long ptid;
if (lwp->stopped)
return;
saved_inferior = current_inferior;
- saved_tid = ((struct inferior_list_entry *) saved_inferior)->id;
- thread = (struct thread_info *) find_inferior_id (&all_threads,
- lwp->lwpid);
- wstat = linux_wait_for_event (thread);
+ if (saved_inferior != NULL)
+ saved_tid = ((struct inferior_list_entry *) saved_inferior)->id;
+ else
+ saved_tid = 0; /* avoid bogus unused warning */
+
+ ptid = lwpid_of (lwp);
+
+ linux_wait_for_event (ptid, &wstat, __WALL);
/* If we stopped with a non-SIGSTOP signal, save it for later
and record the pending SIGSTOP. If the process exited, just
@@ -1103,7 +1280,7 @@ wait_for_sigstop (struct inferior_list_entry *entry)
{
if (debug_threads)
fprintf (stderr, "LWP %ld stopped with non-sigstop status %06x\n",
- lwp->lwpid, wstat);
+ lwpid_of (lwp), wstat);
/* Do not leave a pending single-step finish to be reported to
the client. The client will give us a new action for this
@@ -1125,15 +1302,25 @@ wait_for_sigstop (struct inferior_list_entry *entry)
lwp->stop_expected = 1;
}
- if (linux_thread_alive (saved_tid))
+ if (saved_inferior == NULL || linux_thread_alive (saved_tid))
current_inferior = saved_inferior;
else
{
if (debug_threads)
fprintf (stderr, "Previously current thread died.\n");
- /* Set a valid thread as current. */
- set_desired_inferior (0);
+ if (non_stop)
+ {
+ /* We can't change the current inferior behind GDB's back,
+ otherwise, a subsequent command may apply to the wrong
+ process. */
+ current_inferior = NULL;
+ }
+ else
+ {
+ /* Set a valid thread as current. */
+ set_desired_inferior (0);
+ }
}
}
@@ -1186,7 +1373,7 @@ linux_resume_one_lwp (struct inferior_list_entry *entry,
if (debug_threads)
fprintf (stderr, "Resuming lwp %ld (%s, signal %d, stop %s)\n",
- inferior_pid, step ? "step" : "continue", signal,
+ lwpid_of (lwp), step ? "step" : "continue", signal,
lwp->stop_expected ? "expected" : "not expected");
/* This bit needs some thinking about. If we get a signal that
@@ -1231,7 +1418,7 @@ linux_resume_one_lwp (struct inferior_list_entry *entry,
signal = (*p_sig)->signal;
if ((*p_sig)->info.si_signo != 0)
- ptrace (PTRACE_SETSIGINFO, lwp->lwpid, 0, &(*p_sig)->info);
+ ptrace (PTRACE_SETSIGINFO, lwpid_of (lwp), 0, &(*p_sig)->info);
free (*p_sig);
*p_sig = NULL;
@@ -1242,7 +1429,7 @@ linux_resume_one_lwp (struct inferior_list_entry *entry,
errno = 0;
lwp->stopped = 0;
lwp->stepping = step;
- ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, lwp->lwpid, 0, signal);
+ ptrace (step ? PTRACE_SINGLESTEP : PTRACE_CONT, lwpid_of (lwp), 0, signal);
current_inferior = saved_inferior;
if (errno)
@@ -1299,104 +1486,152 @@ linux_set_resume_request (struct inferior_list_entry *entry, void *arg)
return 0;
}
-/* This function is called once per thread. We check the thread's resume
- request, which will tell us whether to resume, step, or leave the thread
- stopped; and what signal, if any, it should be sent. For threads which
- we aren't explicitly told otherwise, we preserve the stepping flag; this
- is used for stepping over gdbserver-placed breakpoints. */
-static void
-linux_continue_one_thread (struct inferior_list_entry *entry)
+/* Set *FLAG_P if this lwp has an interesting status pending. */
+static int
+resume_status_pending_p (struct inferior_list_entry *entry, void *flag_p)
{
- struct lwp_info *lwp;
- struct thread_info *thread;
- int step;
-
- thread = (struct thread_info *) entry;
- lwp = get_thread_lwp (thread);
+ struct lwp_info *lwp = (struct lwp_info *) entry;
+ /* LWPs which will not be resumed are not interesting, because
+ we might not wait for them next time through linux_wait. */
if (lwp->resume == NULL)
- return;
+ return 0;
- if (lwp->resume->thread == -1
- && lwp->stepping
- && lwp->pending_is_breakpoint)
- step = 1;
- else
- step = lwp->resume->step;
+ /* If this thread has a removed breakpoint, we won't have any
+ events to report later, so check now. check_removed_breakpoint
+ may clear status_pending_p. We avoid calling check_removed_breakpoint
+ for any thread that we are not otherwise going to resume - this
+ lets us preserve stopped status when two threads hit a breakpoint.
+ GDB removes the breakpoint to single-step a particular thread
+ past it, then re-inserts it and resumes all threads. We want
+ to report the second thread without resuming it in the interim. */
+ if (lwp->status_pending_p)
+ check_removed_breakpoint (lwp);
- linux_resume_one_lwp (&lwp->head, step, lwp->resume->sig, NULL);
+ if (lwp->status_pending_p)
+ * (int *) flag_p = 1;
- lwp->resume = NULL;
+ return 0;
}
/* This function is called once per thread. We check the thread's resume
request, which will tell us whether to resume, step, or leave the thread
- stopped; and what signal, if any, it should be sent. We queue any needed
- signals, since we won't actually resume. We already have a pending event
- to report, so we don't need to preserve any step requests; they should
- be re-issued if necessary. */
+ stopped; and what signal, if any, it should be sent.
-static void
-linux_queue_one_thread (struct inferior_list_entry *entry)
+ For threads which we aren't explicitly told otherwise, we preserve
+ the stepping flag; this is used for stepping over gdbserver-placed
+ breakpoints.
+
+ If pending_flags was set in any thread, we queue any needed
+ signals, since we won't actually resume. We already have a pending
+ event to report, so we don't need to preserve any step requests;
+ they should be re-issued if necessary. */
+
+static int
+linux_resume_one_thread (struct inferior_list_entry *entry, void *arg)
{
struct lwp_info *lwp;
struct thread_info *thread;
+ int step;
+ int pending_flag = * (int *) arg;
thread = (struct thread_info *) entry;
lwp = get_thread_lwp (thread);
if (lwp->resume == NULL)
- return;
+ return 0;
- /* If we have a new signal, enqueue the signal. */
- if (lwp->resume->sig != 0)
+ if (lwp->resume->kind == resume_stop)
{
- struct pending_signals *p_sig;
- p_sig = xmalloc (sizeof (*p_sig));
- p_sig->prev = lwp->pending_signals;
- p_sig->signal = lwp->resume->sig;
- memset (&p_sig->info, 0, sizeof (siginfo_t));
+ if (debug_threads)
+ fprintf (stderr, "suspending LWP %ld\n", lwpid_of (lwp));
+
+ if (!lwp->stopped)
+ {
+ if (debug_threads)
+ fprintf (stderr, "running -> suspending %ld\n", lwpid_of (lwp));
+
+ lwp->suspended = 1;
+ send_sigstop (&lwp->head);
+ }
+ else
+ {
+ if (debug_threads)
+ {
+ if (lwp->suspended)
+ fprintf (stderr, "already stopped/suspended LWP %ld\n",
+ lwpid_of (lwp));
+ else
+ fprintf (stderr, "already stopped/not suspended LWP %ld\n",
+ lwpid_of (lwp));
+ }
- /* If this is the same signal we were previously stopped by,
- make sure to queue its siginfo. We can ignore the return
- value of ptrace; if it fails, we'll skip
- PTRACE_SETSIGINFO. */
- if (WIFSTOPPED (lwp->last_status)
- && WSTOPSIG (lwp->last_status) == lwp->resume->sig)
- ptrace (PTRACE_GETSIGINFO, lwp->lwpid, 0, &p_sig->info);
+ /* Make sure we leave the LWP suspended, so we don't try to
+ resume it without GDB telling us to. FIXME: The LWP may
+ have been stopped in an internal event that was not meant
+ to be notified back to GDB (e.g., gdbserver breakpoint),
+ so we should be reporting a stop event in that case
+ too. */
+ lwp->suspended = 1;
+ }
- lwp->pending_signals = p_sig;
+ /* For stop requests, we're done. */
+ lwp->resume = NULL;
+ return 0;
}
+ else
+ lwp->suspended = 0;
- lwp->resume = NULL;
-}
+ /* If this thread which is about to be resumed has a pending status,
+ then don't resume any threads - we can just report the pending
+ status. Make sure to queue any signals that would otherwise be
+ sent. In all-stop mode, we do this decision based on if *any*
+ thread has a pending status. */
+ if (non_stop)
+ resume_status_pending_p (&lwp->head, &pending_flag);
-/* Set DUMMY if this process has an interesting status pending. */
-static int
-resume_status_pending_p (struct inferior_list_entry *entry, void *flag_p)
-{
- struct lwp_info *lwp = (struct lwp_info *) entry;
+ if (!pending_flag)
+ {
+ if (debug_threads)
+ fprintf (stderr, "resuming LWP %ld\n", lwpid_of (lwp));
- /* Processes which will not be resumed are not interesting, because
- we might not wait for them next time through linux_wait. */
- if (lwp->resume == NULL)
- return 0;
+ if (lwp->resume->thread == -1
+ && lwp->stepping
+ && lwp->pending_is_breakpoint)
+ step = 1;
+ else
+ step = (lwp->resume->kind == resume_step);
- /* If this thread has a removed breakpoint, we won't have any
- events to report later, so check now. check_removed_breakpoint
- may clear status_pending_p. We avoid calling check_removed_breakpoint
- for any thread that we are not otherwise going to resume - this
- lets us preserve stopped status when two threads hit a breakpoint.
- GDB removes the breakpoint to single-step a particular thread
- past it, then re-inserts it and resumes all threads. We want
- to report the second thread without resuming it in the interim. */
- if (lwp->status_pending_p)
- check_removed_breakpoint (lwp);
+ linux_resume_one_lwp (&lwp->head, step, lwp->resume->sig, NULL);
+ }
+ else
+ {
+ if (debug_threads)
+ fprintf (stderr, "leaving LWP %ld stopped\n", lwpid_of (lwp));
- if (lwp->status_pending_p)
- * (int *) flag_p = 1;
+ /* If we have a new signal, enqueue the signal. */
+ if (lwp->resume->sig != 0)
+ {
+ struct pending_signals *p_sig;
+ p_sig = xmalloc (sizeof (*p_sig));
+ p_sig->prev = lwp->pending_signals;
+ p_sig->signal = lwp->resume->sig;
+ memset (&p_sig->info, 0, sizeof (siginfo_t));
+
+ /* If this is the same signal we were previously stopped by,
+ make sure to queue its siginfo. We can ignore the return
+ value of ptrace; if it fails, we'll skip
+ PTRACE_SETSIGINFO. */
+ if (WIFSTOPPED (lwp->last_status)
+ && WSTOPSIG (lwp->last_status) == lwp->resume->sig)
+ ptrace (PTRACE_GETSIGINFO, lwpid_of (lwp), 0, &p_sig->info);
+
+ lwp->pending_signals = p_sig;
+ }
+ }
+ lwp->resume = NULL;
return 0;
}
@@ -1411,9 +1646,11 @@ linux_resume (struct thread_resume *resume_info, size_t n)
/* If there is a thread which would otherwise be resumed, which
has a pending status, then don't resume any threads - we can just
report the pending status. Make sure to queue any signals
- that would otherwise be sent. */
+ that would otherwise be sent. In non-stop mode, we'll apply this
+ logic to each thread individually. */
pending_flag = 0;
- find_inferior (&all_lwps, resume_status_pending_p, &pending_flag);
+ if (!non_stop)
+ find_inferior (&all_lwps, resume_status_pending_p, &pending_flag);
if (debug_threads)
{
@@ -1423,10 +1660,7 @@ linux_resume (struct thread_resume *resume_info, size_t n)
fprintf (stderr, "Resuming, no pending status\n");
}
- if (pending_flag)
- for_each_inferior (&all_threads, linux_queue_one_thread);
- else
- for_each_inferior (&all_threads, linux_continue_one_thread);
+ find_inferior (&all_threads, linux_resume_one_thread, &pending_flag);
}
#ifdef HAVE_LINUX_USRREGS
@@ -1873,18 +2107,78 @@ linux_tracefork_child (void *arg)
_exit (0);
}
-/* Wrapper function for waitpid which handles EINTR. */
+/* Wrapper function for waitpid which handles EINTR, and emulates
+ __WALL for systems where that is not available. */
static int
my_waitpid (int pid, int *status, int flags)
{
- int ret;
- do
+ int ret, out_errno;
+
+ if (debug_threads)
+ fprintf (stderr, "my_waitpid (%d, 0x%x)\n", pid, flags);
+
+ if (flags & __WALL)
{
- ret = waitpid (pid, status, flags);
+ sigset_t block_mask, org_mask, wake_mask;
+ int wnohang;
+
+ wnohang = (flags & WNOHANG) != 0;
+ flags &= ~(__WALL | __WCLONE);
+ flags |= WNOHANG;
+
+ /* Block all signals while here. This avoids knowing about
+ LinuxThread's signals. */
+ sigfillset (&block_mask);
+ sigprocmask (SIG_BLOCK, &block_mask, &org_mask);
+
+ /* ... except during the sigsuspend below. */
+ sigemptyset (&wake_mask);
+
+ while (1)
+ {
+ /* Since all signals are blocked, there's no need to check
+ for EINTR here. */
+ ret = waitpid (pid, status, flags);
+ out_errno = errno;
+
+ if (ret == -1 && out_errno != ECHILD)
+ break;
+ else if (ret > 0)
+ break;
+
+ if (flags & __WCLONE)
+ {
+ /* We've tried both flavors now. If WNOHANG is set,
+ there's nothing else to do, just bail out. */
+ if (wnohang)
+ break;
+
+ if (debug_threads)
+ fprintf (stderr, "blocking\n");
+
+ /* Block waiting for signals. */
+ sigsuspend (&wake_mask);
+ }
+
+ flags ^= __WCLONE;
+ }
+
+ sigprocmask (SIG_SETMASK, &org_mask, NULL);
}
- while (ret == -1 && errno == EINTR);
+ else
+ {
+ do
+ ret = waitpid (pid, status, flags);
+ while (ret == -1 && errno == EINTR);
+ out_errno = errno;
+ }
+
+ if (debug_threads)
+ fprintf (stderr, "my_waitpid (%d, 0x%x): status(%x), %d\n",
+ pid, flags, status ? *status : -1, ret);
+ errno = out_errno;
return ret;
}
@@ -1999,9 +2293,11 @@ linux_request_interrupt (void)
if (cont_thread != 0 && cont_thread != -1)
{
struct lwp_info *lwp;
+ int lwpid;
lwp = get_thread_lwp (current_inferior);
- kill_lwp (lwp->lwpid, SIGINT);
+ lwpid = lwpid_of (lwp);
+ kill_lwp (lwpid, SIGINT);
}
else
kill_lwp (signal_pid, SIGINT);
@@ -2090,7 +2386,7 @@ linux_read_offsets (CORE_ADDR *text_p, CORE_ADDR *data_p)
{
#if defined(PT_TEXT_ADDR) && defined(PT_DATA_ADDR) && defined(PT_TEXT_END_ADDR)
unsigned long text, text_end, data;
- int pid = get_thread_lwp (current_inferior)->head.id;
+ int pid = lwpid_of (get_thread_lwp (current_inferior));
errno = 0;
@@ -2232,7 +2528,7 @@ linux_xfer_siginfo (const char *annex, unsigned char *readbuf,
if (current_inferior == NULL)
return -1;
- pid = pid_of (get_thread_lwp (current_inferior));
+ pid = lwpid_of (get_thread_lwp (current_inferior));
if (debug_threads)
fprintf (stderr, "%s siginfo for lwp %ld.\n",
@@ -2260,6 +2556,83 @@ linux_xfer_siginfo (const char *annex, unsigned char *readbuf,
return len;
}
+/* SIGCHLD handler that serves two purposes: In non-stop/async mode,
+ so we notice when children change state; as the handler for the
+ sigsuspend in my_waitpid. */
+
+static void
+sigchld_handler (int signo)
+{
+ int old_errno = errno;
+
+ if (debug_threads)
+ /* fprintf is not async-signal-safe, so call write directly. */
+ write (2, "sigchld_handler\n", sizeof ("sigchld_handler\n") - 1);
+
+ if (target_is_async_p ())
+ async_file_mark (); /* trigger a linux_wait */
+
+ errno = old_errno;
+}
+
+static int
+linux_supports_non_stop (void)
+{
+ return 1;
+}
+
+static int
+linux_async (int enable)
+{
+ int previous = (linux_event_pipe[0] != -1);
+
+ if (previous != enable)
+ {
+ sigset_t mask;
+ sigemptyset (&mask);
+ sigaddset (&mask, SIGCHLD);
+
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+
+ if (enable)
+ {
+ if (pipe (linux_event_pipe) == -1)
+ fatal ("creating event pipe failed.");
+
+ fcntl (linux_event_pipe[0], F_SETFL, O_NONBLOCK);
+ fcntl (linux_event_pipe[1], F_SETFL, O_NONBLOCK);
+
+ /* Register the event loop handler. */
+ add_file_handler (linux_event_pipe[0],
+ handle_target_event, NULL);
+
+ /* Always trigger a linux_wait. */
+ async_file_mark ();
+ }
+ else
+ {
+ delete_file_handler (linux_event_pipe[0]);
+
+ close (linux_event_pipe[0]);
+ close (linux_event_pipe[1]);
+ linux_event_pipe[0] = -1;
+ linux_event_pipe[1] = -1;
+ }
+
+ sigprocmask (SIG_UNBLOCK, &mask, NULL);
+ }
+
+ return previous;
+}
+
+static int
+linux_start_non_stop (int nonstop)
+{
+ /* Register or unregister from event-loop accordingly. */
+ linux_async (nonstop);
+ return 0;
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
@@ -2294,6 +2667,9 @@ static struct target_ops linux_target_ops = {
hostio_last_error_from_errno,
linux_qxfer_osdata,
linux_xfer_siginfo,
+ linux_supports_non_stop,
+ linux_async,
+ linux_start_non_stop,
};
static void
@@ -2307,6 +2683,8 @@ linux_init_signals ()
void
initialize_low (void)
{
+ struct sigaction sigchld_action;
+ memset (&sigchld_action, 0, sizeof (sigchld_action));
thread_db_active = 0;
set_target_ops (&linux_target_ops);
set_breakpoint_data (the_low_target.breakpoint,
@@ -2318,4 +2696,9 @@ initialize_low (void)
;
disabled_regsets = xmalloc (num_regsets);
#endif
+
+ sigchld_action.sa_handler = sigchld_handler;
+ sigemptyset (&sigchld_action.sa_mask);
+ sigchld_action.sa_flags = SA_RESTART;
+ sigaction (SIGCHLD, &sigchld_action, NULL);
}