From 64776a0b2d88d40f308304194d26b766bb12b7e3 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Fri, 20 Jul 2012 17:27:29 +0000 Subject: [PATCH] 2012-07-20 Pedro Alves PR threads/11692 PR gdb/12203 gdb/ * infrun.c (handle_inferior_event) : Don't special case minus_one_ptid. : 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. --- gdb/ChangeLog | 18 ++++ gdb/infrun.c | 6 +- gdb/linux-thread-db.c | 44 ++------ gdb/testsuite/ChangeLog | 8 ++ gdb/testsuite/gdb.threads/create-fail.c | 119 ++++++++++++++++++++++ gdb/testsuite/gdb.threads/create-fail.exp | 53 ++++++++++ 6 files changed, 208 insertions(+), 40 deletions(-) create mode 100644 gdb/testsuite/gdb.threads/create-fail.c create mode 100644 gdb/testsuite/gdb.threads/create-fail.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 4b660e4c61..7e3b4f992f 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,21 @@ +2012-07-20 Pedro Alves + + PR threads/11692 + PR gdb/12203 + + * infrun.c (handle_inferior_event) : Don't special + case minus_one_ptid. + : 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 * linux-nat.c (linux_nat_wait): Dump the passed in target options. diff --git a/gdb/infrun.c b/gdb/infrun.c index 49a442c871..efc41627e9 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -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); diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c index bdf14dfeb4..ef704ec425 100644 --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -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 diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 66ea5b80cb..d4656b7239 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2012-07-20 Pedro Alves + + 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 * config/monitor.exp (gdb_load): Remove redundant ';' in for loop. diff --git a/gdb/testsuite/gdb.threads/create-fail.c b/gdb/testsuite/gdb.threads/create-fail.c new file mode 100644 index 0000000000..b926cf5c78 --- /dev/null +++ b/gdb/testsuite/gdb.threads/create-fail.c @@ -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 . */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); +} diff --git a/gdb/testsuite/gdb.threads/create-fail.exp b/gdb/testsuite/gdb.threads/create-fail.exp new file mode 100644 index 0000000000..0cdacf62ef --- /dev/null +++ b/gdb/testsuite/gdb.threads/create-fail.exp @@ -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 . + +# 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" + } + } + } +}