Fix PR 10970, PR 12702.
	* linux-nat.c (linux_lwp_is_zombie): New function.
	(wait_lwp): Initialize status.  New variable prev_mask.  Block signals.
	Check for linux_lwp_is_zombie.  Use WNOHANG and sigsuspend.

gdb/testsuite/
	* gdb.threads/leader-exit.c: New file.
	* gdb.threads/leader-exit.exp: New file.
This commit is contained in:
Jan Kratochvil 2011-05-27 16:55:39 +00:00
parent b0f260d61e
commit 432b4d03ad
5 changed files with 185 additions and 10 deletions

View File

@ -1,3 +1,11 @@
2011-05-27 Jan Kratochvil <jan.kratochvil@redhat.com>
Doug Evans <dje@google.com>
Fix PR 10970, PR 12702.
* linux-nat.c (linux_lwp_is_zombie): New function.
(wait_lwp): Initialize status. New variable prev_mask. Block signals.
Check for linux_lwp_is_zombie. Use WNOHANG and sigsuspend.
2011-05-27 Pedro Alves <pedro@codesourcery.com>
* defs.h (continuation_ftype, continuation_free_arg_ftype): New

View File

@ -2356,6 +2356,33 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
_("unknown ptrace event %d"), event);
}
/* Return non-zero if LWP is a zombie. */
static int
linux_lwp_is_zombie (long lwp)
{
char buffer[MAXPATHLEN];
FILE *procfile;
int retval = 0;
sprintf (buffer, "/proc/%ld/status", lwp);
procfile = fopen (buffer, "r");
if (procfile == NULL)
{
warning (_("unable to open /proc file '%s'"), buffer);
return 0;
}
while (fgets (buffer, sizeof (buffer), procfile) != NULL)
if (strcmp (buffer, "State:\tZ (zombie)\n") == 0)
{
retval = 1;
break;
}
fclose (procfile);
return retval;
}
/* Wait for LP to stop. Returns the wait status, or 0 if the LWP has
exited. */
@ -2363,28 +2390,76 @@ static int
wait_lwp (struct lwp_info *lp)
{
pid_t pid;
int status;
int status = 0;
int thread_dead = 0;
sigset_t prev_mask;
gdb_assert (!lp->stopped);
gdb_assert (lp->status == 0);
pid = my_waitpid (GET_LWP (lp->ptid), &status, 0);
if (pid == -1 && errno == ECHILD)
/* Make sure SIGCHLD is blocked for sigsuspend avoiding a race below. */
block_child_signals (&prev_mask);
for (;;)
{
pid = my_waitpid (GET_LWP (lp->ptid), &status, __WCLONE);
/* If my_waitpid returns 0 it means the __WCLONE vs. non-__WCLONE kind
was right and we should just call sigsuspend. */
pid = my_waitpid (GET_LWP (lp->ptid), &status, WNOHANG);
if (pid == -1 && errno == ECHILD)
pid = my_waitpid (GET_LWP (lp->ptid), &status, __WCLONE | WNOHANG);
if (pid != 0)
break;
/* Bugs 10970, 12702.
Thread group leader may have exited in which case we'll lock up in
waitpid if there are other threads, even if they are all zombies too.
Basically, we're not supposed to use waitpid this way.
__WCLONE is not applicable for the leader so we can't use that.
LINUX_NAT_THREAD_ALIVE cannot be used here as it requires a STOPPED
process; it gets ESRCH both for the zombie and for running processes.
As a workaround, check if we're waiting for the thread group leader and
if it's a zombie, and avoid calling waitpid if it is.
This is racy, what if the tgl becomes a zombie right after we check?
Therefore always use WNOHANG with sigsuspend - it is equivalent to
waiting waitpid but the linux_lwp_is_zombie is safe this way. */
if (GET_PID (lp->ptid) == GET_LWP (lp->ptid)
&& linux_lwp_is_zombie (GET_LWP (lp->ptid)))
{
/* The thread has previously exited. We need to delete it
now because, for some vendor 2.4 kernels with NPTL
support backported, there won't be an exit event unless
it is the main thread. 2.6 kernels will report an exit
event for each thread that exits, as expected. */
thread_dead = 1;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, "WL: %s vanished.\n",
fprintf_unfiltered (gdb_stdlog,
"WL: Thread group leader %s vanished.\n",
target_pid_to_str (lp->ptid));
break;
}
/* Wait for next SIGCHLD and try again. This may let SIGCHLD handlers
get invoked despite our caller had them intentionally blocked by
block_child_signals. This is sensitive only to the loop of
linux_nat_wait_1 and there if we get called my_waitpid gets called
again before it gets to sigsuspend so we can safely let the handlers
get executed here. */
sigsuspend (&suspend_mask);
}
restore_child_signals_mask (&prev_mask);
if (pid == -1 && errno == ECHILD)
{
/* The thread has previously exited. We need to delete it
now because, for some vendor 2.4 kernels with NPTL
support backported, there won't be an exit event unless
it is the main thread. 2.6 kernels will report an exit
event for each thread that exits, as expected. */
thread_dead = 1;
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog, "WL: %s vanished.\n",
target_pid_to_str (lp->ptid));
}
if (!thread_dead)

View File

@ -1,3 +1,8 @@
2011-05-27 Jan Kratochvil <jan.kratochvil@redhat.com>
* gdb.threads/leader-exit.c: New file.
* gdb.threads/leader-exit.exp: New file.
2011-05-27 Marek Polacek <mpolacek@redhat.com>
* gdb.mi/mi-nonstop.exp: Replace gdb_test_multiple with mi_gdb_test.

View File

@ -0,0 +1,49 @@
/* Clean exit of the thread group leader should not break GDB.
Copyright 2007, 2011 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/>. */
#include <pthread.h>
#include <assert.h>
#include <unistd.h>
static volatile pthread_t main_thread;
static void *
start (void *arg)
{
int i;
i = pthread_join (main_thread, NULL);
assert (i == 0);
return arg; /* break-here */
}
int
main (void)
{
pthread_t thread;
int i;
main_thread = pthread_self ();
i = pthread_create (&thread, NULL, start, NULL);
assert (i == 0);
pthread_exit (NULL);
/* NOTREACHED */
return 0;
}

View File

@ -0,0 +1,38 @@
# Copyright (C) 2007, 2011 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/>.
# Exit of the thread group leader should not break GDB.
set testfile "leader-exit"
set srcfile ${testfile}.c
set executable ${testfile}
set binfile ${objdir}/${subdir}/${executable}
if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
return -1
}
clean_restart ${executable}
if ![runto_main] {
return -1
}
gdb_breakpoint [gdb_get_line_number "break-here"]
gdb_continue_to_breakpoint "break-here" ".* break-here .*"
gdb_test "info threads" \
"\r\n\[ \t\]*Id\[ \t\]+Target\[ \t\]+Id\[ \t\]+Frame\[ \t\]*\r\n\\* 2 *Thread \[^\r\n\]* at \[^\r\n\]*" \
"Single thread has been left"