2012-07-20 Pedro Alves <palves@redhat.com>

PR threads/11692
	PR gdb/12203

	gdb/
	* infrun.c (handle_inferior_event) <new thread>: Don't special
	case minus_one_ptid.
	<TARGET_WAITKIND_SPURIOUS>: Ditto.
	* linux-thread-db.c (thread_get_info_callback): Don't return early
	if the thread is zombie.
	(thread_from_lwp): Change return type to void.  Rewrite stale
	comment.
	(attach_thread): Don't return early if the thread is zombie,
	instead set its "dying" flag.
	(thread_db_wait): Don't return TARGET_WAITKIND_SPURIOUS anymore.
	(find_new_threads_callback): Don't return early if the thread is
	zombie.

	gdb/testsuite/
	* gdb.threads/create-fail.c: New file.
	* gdb.threads/create-fail.exp: New file.
This commit is contained in:
Pedro Alves 2012-07-20 17:27:29 +00:00
parent 09826ec59d
commit 64776a0b2d
6 changed files with 208 additions and 40 deletions

View File

@ -1,3 +1,21 @@
2012-07-20 Pedro Alves <palves@redhat.com>
PR threads/11692
PR gdb/12203
* infrun.c (handle_inferior_event) <new thread>: Don't special
case minus_one_ptid.
<TARGET_WAITKIND_SPURIOUS>: Ditto.
* linux-thread-db.c (thread_get_info_callback): Don't return early
if the thread is zombie.
(thread_from_lwp): Change return type to void. Rewrite stale
comment.
(attach_thread): Don't return early if the thread is zombie,
instead set its "dying" flag.
(thread_db_wait): Don't return TARGET_WAITKIND_SPURIOUS anymore.
(find_new_threads_callback): Don't return early if the thread is
zombie.
2012-07-20 Pedro Alves <palves@redhat.com> 2012-07-20 Pedro Alves <palves@redhat.com>
* linux-nat.c (linux_nat_wait): Dump the passed in target options. * linux-nat.c (linux_nat_wait): Dump the passed in target options.

View File

@ -3194,8 +3194,7 @@ handle_inferior_event (struct execution_control_state *ecs)
} }
if (ecs->ws.kind != TARGET_WAITKIND_EXITED if (ecs->ws.kind != TARGET_WAITKIND_EXITED
&& ecs->ws.kind != TARGET_WAITKIND_SIGNALLED && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED)
&& !ptid_equal (ecs->ptid, minus_one_ptid))
{ {
ecs->event_thread = find_thread_ptid (ecs->ptid); ecs->event_thread = find_thread_ptid (ecs->ptid);
/* If it's a new thread, add it to the thread database. */ /* If it's a new thread, add it to the thread database. */
@ -3363,8 +3362,7 @@ handle_inferior_event (struct execution_control_state *ecs)
case TARGET_WAITKIND_SPURIOUS: case TARGET_WAITKIND_SPURIOUS:
if (debug_infrun) if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SPURIOUS\n"); fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SPURIOUS\n");
if (!ptid_equal (ecs->ptid, inferior_ptid) if (!ptid_equal (ecs->ptid, inferior_ptid))
&& !ptid_equal (ecs->ptid, minus_one_ptid))
context_switch (ecs->ptid); context_switch (ecs->ptid);
resume (0, GDB_SIGNAL_0); resume (0, GDB_SIGNAL_0);
prepare_to_wait (ecs); prepare_to_wait (ecs);

View File

@ -422,11 +422,6 @@ thread_get_info_callback (const td_thrhandle_t *thp, void *argp)
thread_ptid = ptid_build (info->pid, ti.ti_lid, 0); thread_ptid = ptid_build (info->pid, ti.ti_lid, 0);
inout->thread_info = find_thread_ptid (thread_ptid); inout->thread_info = find_thread_ptid (thread_ptid);
/* In the case of a zombie thread, don't continue. We don't want to
attach to it thinking it is a new thread. */
if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
return TD_THR_ZOMBIE;
if (inout->thread_info == NULL) if (inout->thread_info == NULL)
{ {
/* New thread. Attach to it now (why wait?). */ /* New thread. Attach to it now (why wait?). */
@ -441,9 +436,9 @@ thread_get_info_callback (const td_thrhandle_t *thp, void *argp)
return 0; return 0;
} }
/* Convert between user-level thread ids and LWP ids. */ /* Fetch the user-level thread id of PTID. */
static ptid_t static void
thread_from_lwp (ptid_t ptid) thread_from_lwp (ptid_t ptid)
{ {
td_thrhandle_t th; td_thrhandle_t th;
@ -467,22 +462,10 @@ thread_from_lwp (ptid_t ptid)
error (_("Cannot find user-level thread for LWP %ld: %s"), error (_("Cannot find user-level thread for LWP %ld: %s"),
GET_LWP (ptid), thread_db_err_str (err)); GET_LWP (ptid), thread_db_err_str (err));
/* Fetch the thread info. If we get back TD_THR_ZOMBIE, then the /* Long-winded way of fetching the thread info. */
event thread has already died. If another gdb interface has called
thread_alive() previously, the thread won't be found on the thread list
anymore. In that case, we don't want to process this ptid anymore
to avoid the possibility of later treating it as a newly
discovered thread id that we should add to the list. Thus,
we return a -1 ptid which is also how the thread list marks a
dead thread. */
io.thread_db_info = info; io.thread_db_info = info;
io.thread_info = NULL; io.thread_info = NULL;
if (thread_get_info_callback (&th, &io) == TD_THR_ZOMBIE thread_get_info_callback (&th, &io);
&& io.thread_info == NULL)
return minus_one_ptid;
gdb_assert (ptid_get_tid (ptid) == 0);
return ptid;
} }
@ -1260,9 +1243,6 @@ attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
if (target_has_execution) if (target_has_execution)
check_thread_signals (); check_thread_signals ();
if (ti_p->ti_state == TD_THR_UNKNOWN || ti_p->ti_state == TD_THR_ZOMBIE)
return 0; /* A zombie thread -- do not attach. */
/* Under GNU/Linux, we have to attach to each and every thread. */ /* Under GNU/Linux, we have to attach to each and every thread. */
if (target_has_execution if (target_has_execution
&& tp == NULL) && tp == NULL)
@ -1297,6 +1277,8 @@ attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
gdb_assert (ti_p->ti_tid != 0); gdb_assert (ti_p->ti_tid != 0);
private->th = *th_p; private->th = *th_p;
private->tid = ti_p->ti_tid; private->tid = ti_p->ti_tid;
if (ti_p->ti_state == TD_THR_UNKNOWN || ti_p->ti_state == TD_THR_ZOMBIE)
private->dying = 1;
/* Add the thread to GDB's thread list. */ /* Add the thread to GDB's thread list. */
if (tp == NULL) if (tp == NULL)
@ -1514,15 +1496,8 @@ thread_db_wait (struct target_ops *ops,
if (have_threads (ptid)) if (have_threads (ptid))
{ {
/* Change ptids back into the higher level PID + TID format. If /* Fill in the thread's user-level thread id. */
the thread is dead and no longer on the thread list, we will thread_from_lwp (ptid);
get back a dead ptid. This can occur if the thread death
event gets postponed by other simultaneous events. In such a
case, we want to just ignore the event and continue on. */
ptid = thread_from_lwp (ptid);
if (GET_PID (ptid) == -1)
ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
} }
return ptid; return ptid;
@ -1567,9 +1542,6 @@ find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
error (_("find_new_threads_callback: cannot get thread info: %s"), error (_("find_new_threads_callback: cannot get thread info: %s"),
thread_db_err_str (err)); thread_db_err_str (err));
if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
return 0; /* A zombie -- ignore. */
if (ti.ti_tid == 0) if (ti.ti_tid == 0)
{ {
/* A thread ID of zero means that this is the main thread, but /* A thread ID of zero means that this is the main thread, but

View File

@ -1,3 +1,11 @@
2012-07-20 Pedro Alves <palves@redhat.com>
PR threads/11692
PR gdb/12203
* gdb.threads/create-fail.c: New file.
* gdb.threads/create-fail.exp: New file.
2012-07-19 Pedro Alves <palves@redhat.com> 2012-07-19 Pedro Alves <palves@redhat.com>
* config/monitor.exp (gdb_load): Remove redundant ';' in for loop. * config/monitor.exp (gdb_load): Remove redundant ';' in for loop.

View File

@ -0,0 +1,119 @@
/* This testcase is part of GDB, the GNU debugger.
Copyright 2012 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/>. */
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <assert.h>
/* Count the number of tasks/threads in the PID thread group. */
static int
count_tasks (pid_t pid)
{
char path[100];
int count;
DIR *d;
snprintf (path, sizeof (path), "/proc/%d/task/", (int) pid);
d = opendir (path);
if (d == NULL)
return -1;
for (count = 0; readdir (d) != NULL; count++)
;
closedir (d);
/* Account for '.' and '..'. */
assert (count > 2);
return count - 2;
}
pthread_attr_t attr[CPU_SETSIZE];
pthread_t thr[CPU_SETSIZE];
static void *
mythread (void *_arg)
{
return NULL;
}
int
main ()
{
int i;
for (i = 0; i < CPU_SETSIZE; i++)
{
cpu_set_t set;
int ret;
pthread_attr_init (&attr[i]);
CPU_ZERO_S (sizeof (set), &set);
CPU_SET_S (i, sizeof (set), &set);
ret = pthread_attr_setaffinity_np (&attr[i], sizeof (set), &set);
if (ret != 0)
{
fprintf (stderr, "set_affinity: %d: %s\n", ret, strerror (ret));
exit (3);
}
ret = pthread_create (&thr[i], &attr[i], mythread, NULL);
/* Should fail with EINVAL at some point. */
if (ret != 0)
{
unsigned long t;
fprintf (stderr, "pthread_create: %d: %s\n", ret, strerror (ret));
/* Wait for all threads to exit. pthread_create spawns a
clone thread even in the failing case, as it can only try
to set the affinity after creating the thread. That new
thread is immediately canceled (because setting the
affinity fails), by killing it with a SIGCANCEL signal,
which may end up in pthread_cancel/unwind paths, which
may trigger a libgcc_s.so load, making the thread hit the
solib-event breakpoint. Now, if we would let the program
exit without waiting, sometimes it would happen that the
inferior exits just while we're handling the solib-event,
resulting in errors being thrown due to failing ptrace
call fails (with ESCHR), breaking the test. */
t = 16;
while (count_tasks (getpid ()) > 1)
{
usleep (t);
if (t < 256)
t *= 2;
}
/* Normal exit, because this is what we are expecting. */
exit (0);
}
}
/* Should not normally be reached. */
exit (1);
}

View File

@ -0,0 +1,53 @@
# Copyright (C) 2012 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/>.
# On GNU/Linux, a creating a thread bound to an unexisting cpu spawns
# the clone child thread for a bit, which is then immediately
# cancelled. The spawned child may trigger a dlopen (for libgcc_s)
# while being cancelled, which results in a trap being reported to
# GDB, for a thread that libthread_db considers to be TD_THR_ZOMBIE.
# Make sure we handle that scenario properly.
standard_testfile
set executable ${testfile}
if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable {debug}] != "" } {
return -1
}
set iterations 10
for {set i 1} {$i <= $iterations} {incr i} {
with_test_prefix "iteration $i" {
clean_restart ${executable}
if ![runto_main] {
return -1
}
set test "run till end"
gdb_test_multiple "continue" "$test" {
-re "exited with code 01.*$gdb_prompt $" {
pass "$test"
}
-re "exited with code 02.*$gdb_prompt $" {
unsupported "$test (too many CPUs for test?)"
}
-re "exited normally.*$gdb_prompt $" {
pass "$test"
}
}
}
}