aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2013-08-19 13:44:41 +0000
committerPedro Alves <palves@redhat.com>2013-08-19 13:44:41 +0000
commit12696c10906026672f17a551ae1fa5d62878751c (patch)
tree3ea2685fdcebd169dae75d1c2d48203ff86042fd
parent6686753d0949e874f10cf130442cd68e445c2c88 (diff)
downloadgdb-12696c10906026672f17a551ae1fa5d62878751c.zip
gdb-12696c10906026672f17a551ae1fa5d62878751c.tar.gz
gdb-12696c10906026672f17a551ae1fa5d62878751c.tar.bz2
linux-nat.c: no need to block child signals so aggressively.
In http://sourceware.org/ml/gdb-patches/2013-08/msg00174.html , the issue of child signal handling around ptrace option support discovery being different between GDB and GDBserver came up. I recalled adding these block_child_signals calls, and the "We don't want those ptrace calls to be interrupted" comment, but not exactly why. So I looked into it. My first guess is that I got confused. The patch that added this <http://sourceware.org/ml/gdb-patches/2009-04/msg00125.html> rewrote the linux native async support completely, and the old async support code had the SIGCHLD handler itself do waitpid, so in places that we'd want a blocking waitpid, we'd have to have the signal handler blocked. That was probably the mindset I had at the time. Anyway, whatever the case, looks like I was wrong on the need for this blocking. Given GDBserver doesn't block like this, I investigated why this is currently needed on GDB but not on GDBserver. I removed the block_child_signals (and restore) calls, and hacked linux-nat.c to call linux_test_for_tracefork in a loop, like: @@ -534,7 +534,10 @@ static int linux_supports_tracefork (int pid) { if (linux_supports_tracefork_flag == -1) - linux_test_for_tracefork (pid); + { + while (1) + linux_test_for_tracefork (pid); + } return linux_supports_tracefork_flag; } Running the resulting GDB, I then saw bad things happening. Specifically, I'd end up with a bunch of zombies, and eventually, the machine would refuse to spawn new processes, claming insufficient resources. The issue is that linux_test_for_tracefork test forks, and has the child fork again. If we don't block SIGCHLD on entry to the function, the children will inherit SIGCHLD's action/disposition (meaning, SIGCHLD will be unblocked in the child). When the first child forks again a second child, and that child exits, the first child gets a SIGCHLD. Now, when we try to wrap up for the whole options test, we kill the first child, and collect the waitstatus. Here, when SIGCHLD isn't blocked, GDB will first see the child reporting a stop with SIGCHLD. gdbserver's ptrace options test does a PTRACE_KILL loop at the end, which catches the SIGCHLD, and retries the kill. The GDB version did not do that. So the GDB version would proceed, leaving the child zombie (until GDB exists), as nothing collected its final waitstatus. So this patch makes the GDB version of linux_test_for_tracefork do the exact same as the GDBserver version, removes all this unnecessary blocking throughout, and adds a couple comments at places that do need it -- namely: places where we'll use sleep with sigsuspend; and linux_async_pipe, as that destroys the pipe the signal handler touches. Tested on x86_64 Fedora 17, sync and async. gdb/ 2013-08-19 Pedro Alves <palves@redhat.com> * linux-nat.c (linux_test_for_tracefork) (linux_test_for_tracesysgood, linux_child_follow_fork) (lin_lwp_attach_lwp, linux_nat_resume): Don't block child signals. (linux_nat_wait_1): Extend comment. (linux_async_pipe): Add comment.
-rw-r--r--gdb/ChangeLog8
-rw-r--r--gdb/linux-nat.c59
2 files changed, 22 insertions, 45 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 6b34e3c..ef51a9f 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,11 @@
+2013-08-19 Pedro Alves <palves@redhat.com>
+
+ * linux-nat.c (linux_test_for_tracefork)
+ (linux_test_for_tracesysgood, linux_child_follow_fork)
+ (lin_lwp_attach_lwp, linux_nat_resume): Don't block child signals.
+ (linux_nat_wait_1): Extend comment.
+ (linux_async_pipe): Add comment.
+
2013-08-15 Kevin Buettner <kevinb@redhat.com>
* rl78-tdep.c (RL78_RAW_PC_REGNUM): New enum.
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 45a6e5f..db23433 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -395,20 +395,13 @@ linux_test_for_tracefork (int original_pid)
{
int child_pid, ret, status;
long second_pid;
- sigset_t prev_mask;
-
- /* We don't want those ptrace calls to be interrupted. */
- block_child_signals (&prev_mask);
linux_supports_tracefork_flag = 0;
linux_supports_tracevforkdone_flag = 0;
ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACEFORK);
if (ret != 0)
- {
- restore_child_signals_mask (&prev_mask);
- return;
- }
+ return;
child_pid = fork ();
if (child_pid == -1)
@@ -433,7 +426,6 @@ linux_test_for_tracefork (int original_pid)
if (ret != 0)
{
warning (_("linux_test_for_tracefork: failed to kill child"));
- restore_child_signals_mask (&prev_mask);
return;
}
@@ -445,7 +437,6 @@ linux_test_for_tracefork (int original_pid)
warning (_("linux_test_for_tracefork: unexpected "
"wait status 0x%x from killed child"), status);
- restore_child_signals_mask (&prev_mask);
return;
}
@@ -482,12 +473,14 @@ linux_test_for_tracefork (int original_pid)
warning (_("linux_test_for_tracefork: unexpected result from waitpid "
"(%d, status 0x%x)"), ret, status);
- ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
- if (ret != 0)
- warning (_("linux_test_for_tracefork: failed to kill child"));
- my_waitpid (child_pid, &status, 0);
-
- restore_child_signals_mask (&prev_mask);
+ do
+ {
+ ret = ptrace (PTRACE_KILL, child_pid, 0, 0);
+ if (ret != 0)
+ warning ("linux_test_for_tracefork: failed to kill child");
+ my_waitpid (child_pid, &status, 0);
+ }
+ while (WIFSTOPPED (status));
}
/* Determine if PTRACE_O_TRACESYSGOOD can be used to follow syscalls.
@@ -500,20 +493,14 @@ static void
linux_test_for_tracesysgood (int original_pid)
{
int ret;
- sigset_t prev_mask;
-
- /* We don't want those ptrace calls to be interrupted. */
- block_child_signals (&prev_mask);
linux_supports_tracesysgood_flag = 0;
ret = ptrace (PTRACE_SETOPTIONS, original_pid, 0, PTRACE_O_TRACESYSGOOD);
if (ret != 0)
- goto out;
+ return;
linux_supports_tracesysgood_flag = 1;
-out:
- restore_child_signals_mask (&prev_mask);
}
/* Determine wether we support PTRACE_O_TRACESYSGOOD option available.
@@ -630,12 +617,9 @@ delete_lwp_cleanup (void *lp_voidp)
static int
linux_child_follow_fork (struct target_ops *ops, int follow_child)
{
- sigset_t prev_mask;
int has_vforked;
int parent_pid, child_pid;
- block_child_signals (&prev_mask);
-
has_vforked = (inferior_thread ()->pending_follow.kind
== TARGET_WAITKIND_VFORKED);
parent_pid = ptid_get_lwp (inferior_ptid);
@@ -954,7 +938,6 @@ holding the child stopped. Try \"set detach-on-fork\" or \
check_for_thread_db ();
}
- restore_child_signals_mask (&prev_mask);
return 0;
}
@@ -1427,13 +1410,10 @@ int
lin_lwp_attach_lwp (ptid_t ptid)
{
struct lwp_info *lp;
- sigset_t prev_mask;
int lwpid;
gdb_assert (is_lwp (ptid));
- block_child_signals (&prev_mask);
-
lp = find_lwp_pid (ptid);
lwpid = GET_LWP (ptid);
@@ -1461,7 +1441,6 @@ lin_lwp_attach_lwp (ptid_t ptid)
/* We've already seen this thread stop, but we
haven't seen the PTRACE_EVENT_CLONE extended
event yet. */
- restore_child_signals_mask (&prev_mask);
return 0;
}
else
@@ -1478,8 +1457,6 @@ lin_lwp_attach_lwp (ptid_t ptid)
{
if (WIFSTOPPED (status))
add_to_pid_list (&stopped_pids, lwpid, status);
-
- restore_child_signals_mask (&prev_mask);
return 1;
}
}
@@ -1492,7 +1469,6 @@ lin_lwp_attach_lwp (ptid_t ptid)
to create them. */
warning (_("Can't attach %s: %s"), target_pid_to_str (ptid),
safe_strerror (errno));
- restore_child_signals_mask (&prev_mask);
return -1;
}
@@ -1503,10 +1479,7 @@ lin_lwp_attach_lwp (ptid_t ptid)
status = linux_nat_post_attach_wait (ptid, 0, &cloned, &signalled);
if (!WIFSTOPPED (status))
- {
- restore_child_signals_mask (&prev_mask);
- return 1;
- }
+ return 1;
lp = add_lwp (ptid);
lp->stopped = 1;
@@ -1542,7 +1515,6 @@ lin_lwp_attach_lwp (ptid_t ptid)
}
lp->last_resume_kind = resume_stop;
- restore_child_signals_mask (&prev_mask);
return 0;
}
@@ -1975,7 +1947,6 @@ static void
linux_nat_resume (struct target_ops *ops,
ptid_t ptid, int step, enum gdb_signal signo)
{
- sigset_t prev_mask;
struct lwp_info *lp;
int resume_many;
@@ -1988,8 +1959,6 @@ linux_nat_resume (struct target_ops *ops,
? strsignal (gdb_signal_to_host (signo)) : "0"),
target_pid_to_str (inferior_ptid));
- block_child_signals (&prev_mask);
-
/* A specific PTID means `step only this process id'. */
resume_many = (ptid_equal (minus_one_ptid, ptid)
|| ptid_is_pid (ptid));
@@ -2047,7 +2016,6 @@ linux_nat_resume (struct target_ops *ops,
"LLR: Short circuiting for status 0x%x\n",
lp->status);
- restore_child_signals_mask (&prev_mask);
if (target_can_async_p ())
{
target_async (inferior_event_handler, 0);
@@ -2080,7 +2048,6 @@ linux_nat_resume (struct target_ops *ops,
(signo != GDB_SIGNAL_0
? strsignal (gdb_signal_to_host (signo)) : "0"));
- restore_child_signals_mask (&prev_mask);
if (target_can_async_p ())
target_async (inferior_event_handler, 0);
}
@@ -3477,7 +3444,7 @@ linux_nat_wait_1 (struct target_ops *ops,
lp->resumed = 1;
}
- /* Make sure SIGCHLD is blocked. */
+ /* Make sure SIGCHLD is blocked until the sigsuspend below. */
block_child_signals (&prev_mask);
retry:
@@ -4907,6 +4874,8 @@ linux_async_pipe (int enable)
{
sigset_t prev_mask;
+ /* Block child signals while we create/destroy the pipe, as
+ their handler writes to it. */
block_child_signals (&prev_mask);
if (enable)