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>
* 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
&& ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
&& !ptid_equal (ecs->ptid, minus_one_ptid))
&& ecs->ws.kind != TARGET_WAITKIND_SIGNALLED)
{
ecs->event_thread = find_thread_ptid (ecs->ptid);
/* 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:
if (debug_infrun)
fprintf_unfiltered (gdb_stdlog, "infrun: TARGET_WAITKIND_SPURIOUS\n");
if (!ptid_equal (ecs->ptid, inferior_ptid)
&& !ptid_equal (ecs->ptid, minus_one_ptid))
if (!ptid_equal (ecs->ptid, inferior_ptid))
context_switch (ecs->ptid);
resume (0, GDB_SIGNAL_0);
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);
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)
{
/* 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;
}
/* 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)
{
td_thrhandle_t th;
@ -467,22 +462,10 @@ thread_from_lwp (ptid_t ptid)
error (_("Cannot find user-level thread for LWP %ld: %s"),
GET_LWP (ptid), thread_db_err_str (err));
/* Fetch the thread info. If we get back TD_THR_ZOMBIE, then the
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. */
/* Long-winded way of fetching the thread info. */
io.thread_db_info = info;
io.thread_info = NULL;
if (thread_get_info_callback (&th, &io) == TD_THR_ZOMBIE
&& io.thread_info == NULL)
return minus_one_ptid;
gdb_assert (ptid_get_tid (ptid) == 0);
return ptid;
thread_get_info_callback (&th, &io);
}
@ -1260,9 +1243,6 @@ attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
if (target_has_execution)
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. */
if (target_has_execution
&& tp == NULL)
@ -1297,6 +1277,8 @@ attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
gdb_assert (ti_p->ti_tid != 0);
private->th = *th_p;
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. */
if (tp == NULL)
@ -1514,15 +1496,8 @@ thread_db_wait (struct target_ops *ops,
if (have_threads (ptid))
{
/* Change ptids back into the higher level PID + TID format. If
the thread is dead and no longer on the thread list, we will
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;
/* Fill in the thread's user-level thread id. */
thread_from_lwp (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"),
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)
{
/* 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>
* 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"
}
}
}
}