aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2008-06-28 11:15:34 +0000
committerPedro Alves <palves@redhat.com>2008-06-28 11:15:34 +0000
commit84e46146f7e8f26eafd2e9a6f0bc229423e0950c (patch)
tree0e93332ad81dc099f27fca437dae42091bde8e86
parent0f8d4a2f6e8a4b83340e6e80a00062c12d31ecfb (diff)
downloadgdb-84e46146f7e8f26eafd2e9a6f0bc229423e0950c.zip
gdb-84e46146f7e8f26eafd2e9a6f0bc229423e0950c.tar.gz
gdb-84e46146f7e8f26eafd2e9a6f0bc229423e0950c.tar.bz2
gdb/
2008-06-28 Pedro Alves <pedro@codesourcery.com> * linux-nat.c (enum sigchld_state): New. (linux_nat_async_events_state): Renamed from linux_nat_async_events_enabled. (linux_nat_event_pipe_push, my_waitpid): Adjust. (sigchld_default_action): New. (lin_lwp_attach_lwp): Adjust. Call linux_nat_async_events unconditionally. (linux_nat_create_inferior): Set events state to sigchld_default state. (linux_nat_resume): Adjust. (linux_nat_wait): Call linux_nat_async_events unconditionally. (sigchld_handler): Adjust. (linux_nat_async_mask): Don't set SIGCHLD actions here. (get_pending_events): Adjust. (linux_nat_async_events): Rewrite to handle enum sigchld_state instead of a boolean. (linux_nat_async): Adjust. (_initialize_linux_nat): Capture default SIGCHLD action into sigchld_default_action. gdb/testsuite/ 2008-06-28 Pedro Alves <pedro@codesourcery.com> * gdb.base/sigchld.c, gdb.base/sigchld.exp: New test.
-rw-r--r--gdb/ChangeLog22
-rw-r--r--gdb/linux-nat.c160
-rw-r--r--gdb/testsuite/ChangeLog4
-rw-r--r--gdb/testsuite/gdb.base/sigchld.c37
-rw-r--r--gdb/testsuite/gdb.base/sigchld.exp45
5 files changed, 210 insertions, 58 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index d54679d..0589c7f 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,25 @@
+2008-06-28 Pedro Alves <pedro@codesourcery.com>
+
+ * linux-nat.c (enum sigchld_state): New.
+ (linux_nat_async_events_state): Renamed from
+ linux_nat_async_events_enabled.
+ (linux_nat_event_pipe_push, my_waitpid): Adjust.
+ (sigchld_default_action): New.
+ (lin_lwp_attach_lwp): Adjust. Call linux_nat_async_events
+ unconditionally.
+ (linux_nat_create_inferior): Set events state to sigchld_default
+ state.
+ (linux_nat_resume): Adjust.
+ (linux_nat_wait): Call linux_nat_async_events unconditionally.
+ (sigchld_handler): Adjust.
+ (linux_nat_async_mask): Don't set SIGCHLD actions here.
+ (get_pending_events): Adjust.
+ (linux_nat_async_events): Rewrite to handle enum sigchld_state
+ instead of a boolean.
+ (linux_nat_async): Adjust.
+ (_initialize_linux_nat): Capture default SIGCHLD action into
+ sigchld_default_action.
+
2008-06-28 Vladimir Prus <vladimir@codesourcery.com>
* breakpoint.c (moribund_locations): New.
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index f981e65..9643378 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -256,11 +256,24 @@ static int linux_nat_event_pipe[2] = { -1, -1 };
/* Number of queued events in the pipe. */
static volatile int linux_nat_num_queued_events;
-/* If async mode is on, true if we're listening for events; false if
- target events are blocked. */
-static int linux_nat_async_events_enabled;
+/* The possible SIGCHLD handling states. */
-static int linux_nat_async_events (int enable);
+enum sigchld_state
+{
+ /* SIGCHLD disabled, with action set to sigchld_handler, for the
+ sigsuspend in linux_nat_wait. */
+ sigchld_sync,
+ /* SIGCHLD enabled, with action set to async_sigchld_handler. */
+ sigchld_async,
+ /* Set SIGCHLD to default action. Used while creating an
+ inferior. */
+ sigchld_default
+};
+
+/* The current SIGCHLD handling state. */
+static enum sigchld_state linux_nat_async_events_state;
+
+static enum sigchld_state linux_nat_async_events (enum sigchld_state enable);
static void pipe_to_local_event_queue (void);
static void local_event_queue_to_pipe (void);
static void linux_nat_event_pipe_push (int pid, int status, int options);
@@ -294,8 +307,8 @@ queued_waitpid (int pid, int *status, int flags)
if (debug_linux_nat_async)
fprintf_unfiltered (gdb_stdlog,
"\
-QWPID: linux_nat_async_events_enabled(%d), linux_nat_num_queued_events(%d)\n",
- linux_nat_async_events_enabled,
+QWPID: linux_nat_async_events_state(%d), linux_nat_num_queued_events(%d)\n",
+ linux_nat_async_events_state,
linux_nat_num_queued_events);
if (flags & __WALL)
@@ -441,7 +454,7 @@ my_waitpid (int pid, int *status, int flags)
int ret;
/* There should be no concurrent calls to waitpid. */
- gdb_assert (!linux_nat_async_events_enabled);
+ gdb_assert (linux_nat_async_events_state == sigchld_sync);
ret = queued_waitpid (pid, status, flags);
if (ret != -1)
@@ -862,6 +875,9 @@ struct sigaction sync_sigchld_action;
/* SIGCHLD action for asynchronous mode. */
static struct sigaction async_sigchld_action;
+
+/* SIGCHLD default action, to pass to new inferiors. */
+static struct sigaction sigchld_default_action;
/* Prototypes for local functions. */
@@ -1185,12 +1201,11 @@ int
lin_lwp_attach_lwp (ptid_t ptid)
{
struct lwp_info *lp;
- int async_events_were_enabled = 0;
+ enum sigchld_state async_events_original_state;
gdb_assert (is_lwp (ptid));
- if (target_can_async_p ())
- async_events_were_enabled = linux_nat_async_events (0);
+ async_events_original_state = linux_nat_async_events (sigchld_sync);
lp = find_lwp_pid (ptid);
@@ -1255,9 +1270,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
lp->stopped = 1;
}
- if (async_events_were_enabled)
- linux_nat_async_events (1);
-
+ linux_nat_async_events (async_events_original_state);
return 0;
}
@@ -1271,6 +1284,8 @@ linux_nat_create_inferior (char *exec_file, char *allargs, char **env,
we have to mask the async mode. */
if (target_can_async_p ())
+ /* Mask async mode. Creating a child requires a loop calling
+ wait_for_inferior currently. */
saved_async = linux_nat_async_mask (0);
else
{
@@ -1281,6 +1296,12 @@ linux_nat_create_inferior (char *exec_file, char *allargs, char **env,
sigdelset (&suspend_mask, SIGCHLD);
}
+ /* Set SIGCHLD to the default action, until after execing the child,
+ since the inferior inherits the superior's signal mask. It will
+ be blocked again in linux_nat_wait, which is only reached after
+ the inferior execing. */
+ linux_nat_async_events (sigchld_default);
+
linux_ops->to_create_inferior (exec_file, allargs, env, from_tty);
if (saved_async)
@@ -1512,7 +1533,7 @@ linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
if (target_can_async_p ())
/* Block events while we're here. */
- linux_nat_async_events (0);
+ linux_nat_async_events (sigchld_sync);
/* A specific PTID means `step only this process id'. */
resume_all = (PIDGET (ptid) == -1);
@@ -2574,9 +2595,8 @@ linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
sigemptyset (&flush_mask);
- if (target_can_async_p ())
- /* Block events while we're here. */
- target_async (NULL, 0);
+ /* Block events while we're here. */
+ linux_nat_async_events (sigchld_sync);
retry:
@@ -3035,7 +3055,7 @@ static void
sigchld_handler (int signo)
{
if (linux_nat_async_enabled
- && linux_nat_async_events_enabled
+ && linux_nat_async_events_state != sigchld_sync
&& signo == SIGCHLD)
/* It is *always* a bug to hit this. */
internal_error (__FILE__, __LINE__,
@@ -3894,15 +3914,9 @@ linux_nat_async_mask (int mask)
{
linux_nat_async (NULL, 0);
linux_nat_async_mask_value = mask;
- /* We're in sync mode. Make sure SIGCHLD isn't handled by
- async_sigchld_handler when we come out of sigsuspend in
- linux_nat_wait. */
- sigaction (SIGCHLD, &sync_sigchld_action, NULL);
}
else
{
- /* Restore the async handler. */
- sigaction (SIGCHLD, &async_sigchld_action, NULL);
linux_nat_async_mask_value = mask;
linux_nat_async (inferior_event_handler, 0);
}
@@ -3960,7 +3974,8 @@ get_pending_events (void)
{
int status, options, pid;
- if (!linux_nat_async_enabled || !linux_nat_async_events_enabled)
+ if (!linux_nat_async_enabled
+ || linux_nat_async_events_state != sigchld_async)
internal_error (__FILE__, __LINE__,
"get_pending_events called with async masked");
@@ -4014,44 +4029,75 @@ async_sigchld_handler (int signo)
get_pending_events ();
}
-/* Enable or disable async SIGCHLD handling. */
+/* Set SIGCHLD handling state to STATE. Returns previous state. */
-static int
-linux_nat_async_events (int enable)
+static enum sigchld_state
+linux_nat_async_events (enum sigchld_state state)
{
- int current_state = linux_nat_async_events_enabled;
+ enum sigchld_state current_state = linux_nat_async_events_state;
if (debug_linux_nat_async)
fprintf_unfiltered (gdb_stdlog,
- "LNAE: enable(%d): linux_nat_async_events_enabled(%d), "
+ "LNAE: state(%d): linux_nat_async_events_state(%d), "
"linux_nat_num_queued_events(%d)\n",
- enable, linux_nat_async_events_enabled,
+ state, linux_nat_async_events_state,
linux_nat_num_queued_events);
- if (current_state != enable)
+ if (current_state != state)
{
sigset_t mask;
sigemptyset (&mask);
sigaddset (&mask, SIGCHLD);
- if (enable)
- {
- /* Unblock target events. */
- linux_nat_async_events_enabled = 1;
-
- local_event_queue_to_pipe ();
- /* While in masked async, we may have not collected all the
- pending events. Get them out now. */
- get_pending_events ();
- sigprocmask (SIG_UNBLOCK, &mask, NULL);
- }
- else
+
+ /* Always block before changing state. */
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+
+ /* Set new state. */
+ linux_nat_async_events_state = state;
+
+ switch (state)
{
- /* Block target events. */
- sigprocmask (SIG_BLOCK, &mask, NULL);
- linux_nat_async_events_enabled = 0;
- /* Get events out of queue, and make them available to
- queued_waitpid / my_waitpid. */
- pipe_to_local_event_queue ();
+ case sigchld_sync:
+ {
+ /* Block target events. */
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+ sigaction (SIGCHLD, &sync_sigchld_action, NULL);
+ /* Get events out of queue, and make them available to
+ queued_waitpid / my_waitpid. */
+ pipe_to_local_event_queue ();
+ }
+ break;
+ case sigchld_async:
+ {
+ /* Unblock target events for async mode. */
+
+ sigprocmask (SIG_BLOCK, &mask, NULL);
+
+ /* Put events we already waited on, in the pipe first, so
+ events are FIFO. */
+ local_event_queue_to_pipe ();
+ /* While in masked async, we may have not collected all
+ the pending events. Get them out now. */
+ get_pending_events ();
+
+ /* Let'em come. */
+ sigaction (SIGCHLD, &async_sigchld_action, NULL);
+ sigprocmask (SIG_UNBLOCK, &mask, NULL);
+ }
+ break;
+ case sigchld_default:
+ {
+ /* SIGCHLD default mode. */
+ sigaction (SIGCHLD, &sigchld_default_action, NULL);
+
+ /* Get events out of queue, and make them available to
+ queued_waitpid / my_waitpid. */
+ pipe_to_local_event_queue ();
+
+ /* Unblock SIGCHLD. */
+ sigprocmask (SIG_UNBLOCK, &mask, NULL);
+ }
+ break;
}
}
@@ -4143,14 +4189,14 @@ linux_nat_async (void (*callback) (enum inferior_event_type event_type,
add_file_handler (linux_nat_event_pipe[0],
linux_nat_async_file_handler, NULL);
- linux_nat_async_events (1);
+ linux_nat_async_events (sigchld_async);
}
else
{
async_client_callback = callback;
async_client_context = context;
- linux_nat_async_events (0);
+ linux_nat_async_events (sigchld_sync);
delete_file_handler (linux_nat_event_pipe[0]);
}
return;
@@ -4166,21 +4212,15 @@ linux_nat_set_async_mode (int on)
if (on)
{
gdb_assert (waitpid_queue == NULL);
- sigaction (SIGCHLD, &async_sigchld_action, NULL);
-
if (pipe (linux_nat_event_pipe) == -1)
internal_error (__FILE__, __LINE__,
"creating event pipe failed.");
-
fcntl (linux_nat_event_pipe[0], F_SETFL, O_NONBLOCK);
fcntl (linux_nat_event_pipe[1], F_SETFL, O_NONBLOCK);
}
else
{
- sigaction (SIGCHLD, &sync_sigchld_action, NULL);
-
drain_queued_events (-1);
-
linux_nat_num_queued_events = 0;
close (linux_nat_event_pipe[0]);
close (linux_nat_event_pipe[1]);
@@ -4297,6 +4337,10 @@ Tells gdb whether to control the GNU/Linux inferior in asynchronous mode."),
&maintenance_set_cmdlist,
&maintenance_show_cmdlist);
+ /* Get the default SIGCHLD action. Used while forking an inferior
+ (see linux_nat_create_inferior/linux_nat_async_events). */
+ sigaction (SIGCHLD, NULL, &sigchld_default_action);
+
/* Block SIGCHLD by default. Doing this early prevents it getting
unblocked if an exception is thrown due to an error while the
inferior is starting (sigsetjmp/siglongjmp). */
diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog
index 9b7fdc5..32256e7 100644
--- a/gdb/testsuite/ChangeLog
+++ b/gdb/testsuite/ChangeLog
@@ -1,3 +1,7 @@
+2008-06-28 Pedro Alves <pedro@codesourcery.com>
+
+ * gdb.base/sigchld.c, gdb.base/sigchld.exp: New test.
+
2008-06-28 Vladimir Prus <vladimir@codesourcery.com>
* lib/mi-support.exp (mi_send_resuming_command_raw): Report pass.
diff --git a/gdb/testsuite/gdb.base/sigchld.c b/gdb/testsuite/gdb.base/sigchld.c
new file mode 100644
index 0000000..7cee041
--- /dev/null
+++ b/gdb/testsuite/gdb.base/sigchld.c
@@ -0,0 +1,37 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2008 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* Check that GDB isn't messing the SIGCHLD mask while creating an
+ inferior. */
+
+#include <signal.h>
+#include <stdlib.h>
+
+int
+main ()
+{
+ sigset_t mask;
+
+ sigemptyset (&mask);
+ sigprocmask (SIG_BLOCK, NULL, &mask);
+
+ if (!sigismember (&mask, SIGCHLD))
+ return 0; /* good, not blocked */
+ else
+ return 1; /* bad, blocked */
+}
diff --git a/gdb/testsuite/gdb.base/sigchld.exp b/gdb/testsuite/gdb.base/sigchld.exp
new file mode 100644
index 0000000..3151b29
--- /dev/null
+++ b/gdb/testsuite/gdb.base/sigchld.exp
@@ -0,0 +1,45 @@
+# Copyright (C) 2008 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Check that GDB isn't messing the SIGCHLD mask while creating an
+# inferior.
+
+if [target_info exists gdb,nosignals] {
+ verbose "Skipping sigchld.exp because of nosignals."
+ continue
+}
+
+set testfile "sigchld"
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if {[gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
+ return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+gdb_load ${binfile}
+
+runto_main
+
+gdb_test "b [gdb_get_line_number "good, not blocked"]" \
+ ".*Breakpoint .*sigchld.*" "set breakpoint at success exit"
+
+gdb_test "b [gdb_get_line_number "bad, blocked"]" \
+ ".*Breakpoint .*sigchld.*" "set breakpoint at failure exit"
+
+gdb_test "continue" ".*good, not blocked.*" "SIGCHLD blocked in inferior"