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.
This commit is contained in:
Pedro Alves 2008-06-28 11:15:34 +00:00
parent 0f8d4a2f6e
commit 84e46146f7
5 changed files with 209 additions and 57 deletions

View file

@ -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> 2008-06-28 Vladimir Prus <vladimir@codesourcery.com>
* breakpoint.c (moribund_locations): New. * breakpoint.c (moribund_locations): New.

View file

@ -256,11 +256,24 @@ static int linux_nat_event_pipe[2] = { -1, -1 };
/* Number of queued events in the pipe. */ /* Number of queued events in the pipe. */
static volatile int linux_nat_num_queued_events; static volatile int linux_nat_num_queued_events;
/* If async mode is on, true if we're listening for events; false if /* The possible SIGCHLD handling states. */
target events are blocked. */
static int linux_nat_async_events_enabled;
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 pipe_to_local_event_queue (void);
static void local_event_queue_to_pipe (void); static void local_event_queue_to_pipe (void);
static void linux_nat_event_pipe_push (int pid, int status, int options); 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) if (debug_linux_nat_async)
fprintf_unfiltered (gdb_stdlog, fprintf_unfiltered (gdb_stdlog,
"\ "\
QWPID: linux_nat_async_events_enabled(%d), linux_nat_num_queued_events(%d)\n", QWPID: linux_nat_async_events_state(%d), linux_nat_num_queued_events(%d)\n",
linux_nat_async_events_enabled, linux_nat_async_events_state,
linux_nat_num_queued_events); linux_nat_num_queued_events);
if (flags & __WALL) if (flags & __WALL)
@ -441,7 +454,7 @@ my_waitpid (int pid, int *status, int flags)
int ret; int ret;
/* There should be no concurrent calls to waitpid. */ /* 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); ret = queued_waitpid (pid, status, flags);
if (ret != -1) if (ret != -1)
@ -862,6 +875,9 @@ struct sigaction sync_sigchld_action;
/* SIGCHLD action for asynchronous mode. */ /* SIGCHLD action for asynchronous mode. */
static struct sigaction async_sigchld_action; static struct sigaction async_sigchld_action;
/* SIGCHLD default action, to pass to new inferiors. */
static struct sigaction sigchld_default_action;
/* Prototypes for local functions. */ /* Prototypes for local functions. */
@ -1185,12 +1201,11 @@ int
lin_lwp_attach_lwp (ptid_t ptid) lin_lwp_attach_lwp (ptid_t ptid)
{ {
struct lwp_info *lp; struct lwp_info *lp;
int async_events_were_enabled = 0; enum sigchld_state async_events_original_state;
gdb_assert (is_lwp (ptid)); gdb_assert (is_lwp (ptid));
if (target_can_async_p ()) async_events_original_state = linux_nat_async_events (sigchld_sync);
async_events_were_enabled = linux_nat_async_events (0);
lp = find_lwp_pid (ptid); lp = find_lwp_pid (ptid);
@ -1255,9 +1270,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
lp->stopped = 1; lp->stopped = 1;
} }
if (async_events_were_enabled) linux_nat_async_events (async_events_original_state);
linux_nat_async_events (1);
return 0; return 0;
} }
@ -1271,6 +1284,8 @@ linux_nat_create_inferior (char *exec_file, char *allargs, char **env,
we have to mask the async mode. */ we have to mask the async mode. */
if (target_can_async_p ()) 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); saved_async = linux_nat_async_mask (0);
else else
{ {
@ -1281,6 +1296,12 @@ linux_nat_create_inferior (char *exec_file, char *allargs, char **env,
sigdelset (&suspend_mask, SIGCHLD); 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); linux_ops->to_create_inferior (exec_file, allargs, env, from_tty);
if (saved_async) if (saved_async)
@ -1512,7 +1533,7 @@ linux_nat_resume (ptid_t ptid, int step, enum target_signal signo)
if (target_can_async_p ()) if (target_can_async_p ())
/* Block events while we're here. */ /* 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'. */ /* A specific PTID means `step only this process id'. */
resume_all = (PIDGET (ptid) == -1); resume_all = (PIDGET (ptid) == -1);
@ -2574,9 +2595,8 @@ linux_nat_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
sigemptyset (&flush_mask); sigemptyset (&flush_mask);
if (target_can_async_p ()) /* Block events while we're here. */
/* Block events while we're here. */ linux_nat_async_events (sigchld_sync);
target_async (NULL, 0);
retry: retry:
@ -3035,7 +3055,7 @@ static void
sigchld_handler (int signo) sigchld_handler (int signo)
{ {
if (linux_nat_async_enabled if (linux_nat_async_enabled
&& linux_nat_async_events_enabled && linux_nat_async_events_state != sigchld_sync
&& signo == SIGCHLD) && signo == SIGCHLD)
/* It is *always* a bug to hit this. */ /* It is *always* a bug to hit this. */
internal_error (__FILE__, __LINE__, internal_error (__FILE__, __LINE__,
@ -3894,15 +3914,9 @@ linux_nat_async_mask (int mask)
{ {
linux_nat_async (NULL, 0); linux_nat_async (NULL, 0);
linux_nat_async_mask_value = mask; 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 else
{ {
/* Restore the async handler. */
sigaction (SIGCHLD, &async_sigchld_action, NULL);
linux_nat_async_mask_value = mask; linux_nat_async_mask_value = mask;
linux_nat_async (inferior_event_handler, 0); linux_nat_async (inferior_event_handler, 0);
} }
@ -3960,7 +3974,8 @@ get_pending_events (void)
{ {
int status, options, pid; 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__, internal_error (__FILE__, __LINE__,
"get_pending_events called with async masked"); "get_pending_events called with async masked");
@ -4014,44 +4029,75 @@ async_sigchld_handler (int signo)
get_pending_events (); get_pending_events ();
} }
/* Enable or disable async SIGCHLD handling. */ /* Set SIGCHLD handling state to STATE. Returns previous state. */
static int static enum sigchld_state
linux_nat_async_events (int enable) 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) if (debug_linux_nat_async)
fprintf_unfiltered (gdb_stdlog, 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", "linux_nat_num_queued_events(%d)\n",
enable, linux_nat_async_events_enabled, state, linux_nat_async_events_state,
linux_nat_num_queued_events); linux_nat_num_queued_events);
if (current_state != enable) if (current_state != state)
{ {
sigset_t mask; sigset_t mask;
sigemptyset (&mask); sigemptyset (&mask);
sigaddset (&mask, SIGCHLD); sigaddset (&mask, SIGCHLD);
if (enable)
{
/* Unblock target events. */
linux_nat_async_events_enabled = 1;
local_event_queue_to_pipe (); /* Always block before changing state. */
/* While in masked async, we may have not collected all the sigprocmask (SIG_BLOCK, &mask, NULL);
pending events. Get them out now. */
get_pending_events (); /* Set new state. */
sigprocmask (SIG_UNBLOCK, &mask, NULL); linux_nat_async_events_state = state;
}
else switch (state)
{ {
/* Block target events. */ case sigchld_sync:
sigprocmask (SIG_BLOCK, &mask, NULL); {
linux_nat_async_events_enabled = 0; /* Block target events. */
/* Get events out of queue, and make them available to sigprocmask (SIG_BLOCK, &mask, NULL);
queued_waitpid / my_waitpid. */ sigaction (SIGCHLD, &sync_sigchld_action, NULL);
pipe_to_local_event_queue (); /* 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], add_file_handler (linux_nat_event_pipe[0],
linux_nat_async_file_handler, NULL); linux_nat_async_file_handler, NULL);
linux_nat_async_events (1); linux_nat_async_events (sigchld_async);
} }
else else
{ {
async_client_callback = callback; async_client_callback = callback;
async_client_context = context; async_client_context = context;
linux_nat_async_events (0); linux_nat_async_events (sigchld_sync);
delete_file_handler (linux_nat_event_pipe[0]); delete_file_handler (linux_nat_event_pipe[0]);
} }
return; return;
@ -4166,21 +4212,15 @@ linux_nat_set_async_mode (int on)
if (on) if (on)
{ {
gdb_assert (waitpid_queue == NULL); gdb_assert (waitpid_queue == NULL);
sigaction (SIGCHLD, &async_sigchld_action, NULL);
if (pipe (linux_nat_event_pipe) == -1) if (pipe (linux_nat_event_pipe) == -1)
internal_error (__FILE__, __LINE__, internal_error (__FILE__, __LINE__,
"creating event pipe failed."); "creating event pipe failed.");
fcntl (linux_nat_event_pipe[0], F_SETFL, O_NONBLOCK); fcntl (linux_nat_event_pipe[0], F_SETFL, O_NONBLOCK);
fcntl (linux_nat_event_pipe[1], F_SETFL, O_NONBLOCK); fcntl (linux_nat_event_pipe[1], F_SETFL, O_NONBLOCK);
} }
else else
{ {
sigaction (SIGCHLD, &sync_sigchld_action, NULL);
drain_queued_events (-1); drain_queued_events (-1);
linux_nat_num_queued_events = 0; linux_nat_num_queued_events = 0;
close (linux_nat_event_pipe[0]); close (linux_nat_event_pipe[0]);
close (linux_nat_event_pipe[1]); 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_set_cmdlist,
&maintenance_show_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 /* Block SIGCHLD by default. Doing this early prevents it getting
unblocked if an exception is thrown due to an error while the unblocked if an exception is thrown due to an error while the
inferior is starting (sigsetjmp/siglongjmp). */ inferior is starting (sigsetjmp/siglongjmp). */

View file

@ -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> 2008-06-28 Vladimir Prus <vladimir@codesourcery.com>
* lib/mi-support.exp (mi_send_resuming_command_raw): Report pass. * lib/mi-support.exp (mi_send_resuming_command_raw): Report pass.

View file

@ -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 */
}

View file

@ -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"