diff options
author | Pedro Alves <palves@redhat.com> | 2015-12-17 14:20:51 +0000 |
---|---|---|
committer | Pedro Alves <palves@redhat.com> | 2015-12-17 14:20:51 +0000 |
commit | 4a6ed09b0f70c79b11bc1e0973a7333d9316a287 (patch) | |
tree | ae9cc273c4c4d5239b525a2c2941b4671d7fc712 | |
parent | c3c874459bf57a70ccbf71a39a3a7dc3c472a201 (diff) | |
download | gdb-4a6ed09b0f70c79b11bc1e0973a7333d9316a287.zip gdb-4a6ed09b0f70c79b11bc1e0973a7333d9316a287.tar.gz gdb-4a6ed09b0f70c79b11bc1e0973a7333d9316a287.tar.bz2 |
Remove support for LinuxThreads and vendor 2.4 kernels w/ backported NPTL
Since we now rely on PTRACE_EVENT_CLONE being available (added in
Linux 2.5.46), we're relying on NPTL.
This commit removes the support for older LinuxThreads, as well as the
workarounds for vendor 2.4 kernels with NPTL backported.
- Rely on tkill being available.
- Assume gdb doesn't get cancel signals.
- Remove code that checks the LinuxThreads restart and cancel signals
in the inferior.
- Assume that __WALL is available.
- Assume that non-leader threads report WIFEXITED.
- Thus, no longer need to send signal 0 to check whether threads are
still alive.
- Update comments throughout.
Tested on x86_64 Fedora 20, native and gdbserver.
gdb/ChangeLog:
* configure.ac: Remove tkill checks.
* configure, config.in: Regenerate.
* linux-nat.c: Remove HAVE_TKILL_SYSCALL check. Update top level
comments.
(linux_nat_post_attach_wait): Remove 'cloned' parameter. Use
__WALL.
(attach_proc_task_lwp_callback): Don't set the cloned flag.
(linux_nat_attach): Adjust.
(kill_lwp): Remove HAVE_TKILL_SYSCALL check. No longer fall back
to 'kill'.
(linux_handle_extended_wait): Use __WALL. Don't set the cloned
flag.
(wait_lwp): Use __WALL. Update comments.
(running_callback, stop_and_resume_callback): Delete.
(linux_nat_filter_event): Don't stop and resume all lwps. Don't
check if the event LWP has previously exited.
(check_zombie_leaders): Update comments.
(linux_nat_wait_1): Use __WALL.
(kill_wait_callback): Don't handle clone processes separately.
Use __WALL instead.
(linux_thread_alive): Delete.
(linux_nat_thread_alive): Return true as long as the LWP is in the
LWP list.
(linux_nat_update_thread_list): Assume the kernel supports
PTRACE_EVENT_CLONE.
(get_signo): Delete.
(lin_thread_get_thread_signals): Remove LinuxThreads references.
No longer check __pthread_sig_restart / __pthread_sig_cancel in
the inferior.
* linux-nat.h (struct lwp_info) <cloned>: Delete field.
* linux-thread-db.c: Update comments.
(_initialize_thread_db): Remove LinuxThreads references.
* nat/linux-waitpid.c (my_waitpid): No longer emulate __WALL.
Pass down flags unmodified.
* linux-waitpid.h (my_waitpid): Update documentation.
gdb/gdbserver/ChangeLog:
* linux-low.c (linux_kill_one_lwp): Remove references to
LinuxThreads.
(kill_lwp): Remove HAVE_TKILL_SYSCALL check. No longer fall back
to 'kill'.
(linux_init_signals): Delete.
(initialize_low): Adjust.
* thread-db.c (thread_db_init): Remove LinuxThreads reference.
-rw-r--r-- | gdb/ChangeLog | 38 | ||||
-rw-r--r-- | gdb/config.in | 3 | ||||
-rwxr-xr-x | gdb/configure | 40 | ||||
-rw-r--r-- | gdb/configure.ac | 19 | ||||
-rw-r--r-- | gdb/gdbserver/ChangeLog | 10 | ||||
-rw-r--r-- | gdb/gdbserver/linux-low.c | 60 | ||||
-rw-r--r-- | gdb/gdbserver/thread-db.c | 3 | ||||
-rw-r--r-- | gdb/linux-nat.c | 450 | ||||
-rw-r--r-- | gdb/linux-nat.h | 5 | ||||
-rw-r--r-- | gdb/linux-thread-db.c | 18 | ||||
-rw-r--r-- | gdb/nat/linux-waitpid.c | 63 | ||||
-rw-r--r-- | gdb/nat/linux-waitpid.h | 3 |
12 files changed, 171 insertions, 541 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1d511ac..aafe95a 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,41 @@ +2015-12-17 Pedro Alves <palves@redhat.com> + + * configure.ac: Remove tkill checks. + * configure, config.in: Regenerate. + * linux-nat.c: Remove HAVE_TKILL_SYSCALL check. Update top level + comments. + (linux_nat_post_attach_wait): Remove 'cloned' parameter. Use + __WALL. + (attach_proc_task_lwp_callback): Don't set the cloned flag. + (linux_nat_attach): Adjust. + (kill_lwp): Remove HAVE_TKILL_SYSCALL check. No longer fall back + to 'kill'. + (linux_handle_extended_wait): Use __WALL. Don't set the cloned + flag. + (wait_lwp): Use __WALL. Update comments. + (running_callback, stop_and_resume_callback): Delete. + (linux_nat_filter_event): Don't stop and resume all lwps. Don't + check if the event LWP has previously exited. + (check_zombie_leaders): Update comments. + (linux_nat_wait_1): Use __WALL. + (kill_wait_callback): Don't handle clone processes separately. + Use __WALL instead. + (linux_thread_alive): Delete. + (linux_nat_thread_alive): Return true as long as the LWP is in the + LWP list. + (linux_nat_update_thread_list): Assume the kernel supports + PTRACE_EVENT_CLONE. + (get_signo): Delete. + (lin_thread_get_thread_signals): Remove LinuxThreads references. + No longer check __pthread_sig_restart / __pthread_sig_cancel in + the inferior. + * linux-nat.h (struct lwp_info) <cloned>: Delete field. + * linux-thread-db.c: Update comments. + (_initialize_thread_db): Remove LinuxThreads references. + * nat/linux-waitpid.c (my_waitpid): No longer emulate __WALL. + Pass down flags unmodified. + * linux-waitpid.h (my_waitpid): Update documentation. + 2015-12-17 Yao Qi <yao.qi@linaro.org> * aarch64-tdep.c (struct stack_item_t): Update comments. diff --git a/gdb/config.in b/gdb/config.in index 97ce5b6..ccc0098 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -549,9 +549,6 @@ /* Define if using Solaris thread debugging. */ #undef HAVE_THREAD_DB_LIB -/* Define if you support the tkill syscall. */ -#undef HAVE_TKILL_SYSCALL - /* Define to 1 if you have the `ttrace' function. */ #undef HAVE_TTRACE diff --git a/gdb/configure b/gdb/configure index 4c308cb..5afdbbc 100755 --- a/gdb/configure +++ b/gdb/configure @@ -14057,46 +14057,6 @@ $as_echo "#define THREAD_DB_HAS_TD_NOTLS 1" >>confdefs.h fi -if test "x$ac_cv_header_sys_syscall_h" = "xyes"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether <sys/syscall.h> has __NR_tkill" >&5 -$as_echo_n "checking whether <sys/syscall.h> has __NR_tkill... " >&6; } -if test "${gdb_cv_sys_syscall_h_has_tkill+set}" = set; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <sys/syscall.h> -int -main () -{ -int i = __NR_tkill; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - gdb_cv_sys_syscall_h_has_tkill=yes -else - gdb_cv_sys_syscall_h_has_tkill=no - -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gdb_cv_sys_syscall_h_has_tkill" >&5 -$as_echo "$gdb_cv_sys_syscall_h_has_tkill" >&6; } -fi -if test "x$gdb_cv_sys_syscall_h_has_tkill" = "xyes"; then - ac_fn_c_check_func "$LINENO" "syscall" "ac_cv_func_syscall" -if test "x$ac_cv_func_syscall" = x""yes; then : - -$as_echo "#define HAVE_TKILL_SYSCALL 1" >>confdefs.h - - -fi - -fi - ac_fn_c_check_decl "$LINENO" "ADDR_NO_RANDOMIZE" "ac_cv_have_decl_ADDR_NO_RANDOMIZE" "#include <sys/personality.h> " if test "x$ac_cv_have_decl_ADDR_NO_RANDOMIZE" = x""yes; then : diff --git a/gdb/configure.ac b/gdb/configure.ac index b9d8f8b..00a3dd1 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -1850,25 +1850,6 @@ if test "x$gdb_cv_thread_db_h_has_td_notls" = "xyes"; then [Define if <thread_db.h> has the TD_NOTLS error code.]) fi -dnl See if we have a sys/syscall header file that has __NR_tkill. -if test "x$ac_cv_header_sys_syscall_h" = "xyes"; then - AC_CACHE_CHECK([whether <sys/syscall.h> has __NR_tkill], - gdb_cv_sys_syscall_h_has_tkill, - AC_TRY_COMPILE( - [#include <sys/syscall.h>], - [int i = __NR_tkill;], - gdb_cv_sys_syscall_h_has_tkill=yes, - gdb_cv_sys_syscall_h_has_tkill=no - ) - ) -fi -dnl See if we can issue tkill syscall. -if test "x$gdb_cv_sys_syscall_h_has_tkill" = "xyes"; then - AC_CHECK_FUNC(syscall, - AC_DEFINE(HAVE_TKILL_SYSCALL, 1, [Define if you support the tkill syscall.]) - ) -fi - dnl Check if we can disable the virtual address space randomization. dnl The functionality of setarch -R. AC_CHECK_DECLS([ADDR_NO_RANDOMIZE],,, [#include <sys/personality.h>]) diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index d54bb74..44a94f0 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,13 @@ +2015-12-17 Pedro Alves <palves@redhat.com> + + * linux-low.c (linux_kill_one_lwp): Remove references to + LinuxThreads. + (kill_lwp): Remove HAVE_TKILL_SYSCALL check. No longer fall back + to 'kill'. + (linux_init_signals): Delete. + (initialize_low): Adjust. + * thread-db.c (thread_db_init): Remove LinuxThreads reference. + 2015-12-16 Pedro Alves <palves@redhat.com> * configure.ac (compiler warning flags): When testing a diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 5e2dc58..edff916 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -878,10 +878,6 @@ linux_create_inferior (char *program, char **allargs) close_most_fds (); ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0); -#ifndef __ANDROID__ /* Bionic doesn't use SIGRTMIN the way glibc does. */ - signal (__SIGRTMIN + 1, SIG_DFL); -#endif - setpgid (0, 0); /* If gdbserver is connected to gdb via stdio, redirect the inferior's @@ -1183,13 +1179,12 @@ linux_kill_one_lwp (struct lwp_info *lwp) ptrace(CONT, pid, 0,0) and just resumes the tracee. A better alternative is to kill with SIGKILL. We only need one SIGKILL per process, not one for each thread. But since we still support - linuxthreads, and we also support debugging programs using raw - clone without CLONE_THREAD, we send one for each thread. For - years, we used PTRACE_KILL only, so we're being a bit paranoid - about some old kernels where PTRACE_KILL might work better - (dubious if there are any such, but that's why it's paranoia), so - we try SIGKILL first, PTRACE_KILL second, and so we're fine - everywhere. */ + support debugging programs using raw clone without CLONE_THREAD, + we send one for each thread. For years, we used PTRACE_KILL + only, so we're being a bit paranoid about some old kernels where + PTRACE_KILL might work better (dubious if there are any such, but + that's why it's paranoia), so we try SIGKILL first, PTRACE_KILL + second, and so we're fine everywhere. */ errno = 0; kill_lwp (pid, SIGKILL); @@ -3320,8 +3315,6 @@ linux_wait_1 (ptid_t ptid, stepping - they may require special handling to skip the signal handler. Also never ignore signals that could be caused by a breakpoint. */ - /* FIXME drow/2002-06-09: Get signal numbers from the inferior's - thread library? */ if (WIFSTOPPED (w) && current_thread->last_resume_kind != resume_step && ( @@ -3663,27 +3656,17 @@ linux_wait (ptid_t ptid, static int kill_lwp (unsigned long lwpid, int signo) { - /* Use tkill, if possible, in case we are using nptl threads. If tkill - fails, then we are not using nptl threads and we should be using kill. */ - -#ifdef __NR_tkill - { - static int tkill_failed; - - if (!tkill_failed) - { - int ret; - - errno = 0; - ret = syscall (__NR_tkill, lwpid, signo); - if (errno != ENOSYS) - return ret; - tkill_failed = 1; - } - } -#endif + int ret; - return kill (lwpid, signo); + errno = 0; + ret = syscall (__NR_tkill, lwpid, signo); + if (errno == ENOSYS) + { + /* If tkill fails, then we are not using nptl threads, a + configuration we no longer support. */ + perror_with_name (("tkill")); + } + return ret; } void @@ -7196,16 +7179,6 @@ static struct target_ops linux_target_ops = { linux_supports_software_single_step }; -static void -linux_init_signals () -{ - /* FIXME drow/2002-06-09: As above, we should check with LinuxThreads - to find what the cancel signal actually is. */ -#ifndef __ANDROID__ /* Bionic doesn't use SIGRTMIN the way glibc does. */ - signal (__SIGRTMIN+1, SIG_IGN); -#endif -} - #ifdef HAVE_LINUX_REGSETS void initialize_regsets_info (struct regsets_info *info) @@ -7225,7 +7198,6 @@ initialize_low (void) memset (&sigchld_action, 0, sizeof (sigchld_action)); set_target_ops (&linux_target_ops); - linux_init_signals (); linux_ptrace_init_warnings (); sigchld_action.sa_handler = sigchld_handler; diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c index 8b3057a..64cd7a6 100644 --- a/gdb/gdbserver/thread-db.c +++ b/gdb/gdbserver/thread-db.c @@ -716,9 +716,6 @@ thread_db_init (void) GNU/Linux calls tgid, "thread group ID". When we support attaching to threads, the original thread may not be the correct thread. We would have to get the process ID from /proc for NPTL. - For LinuxThreads we could do something similar: follow the chain - of parent processes until we find the highest one we're attached - to, and use its tgid. This isn't the only place in gdbserver that assumes that the first process in the list is the thread group leader. */ diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 95192f6..af1b764 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -24,10 +24,8 @@ #include "nat/linux-nat.h" #include "nat/linux-waitpid.h" #include "gdb_wait.h" -#ifdef HAVE_TKILL_SYSCALL #include <unistd.h> #include <sys/syscall.h> -#endif #include "nat/gdb_ptrace.h" #include "linux-nat.h" #include "nat/linux-ptrace.h" @@ -78,29 +76,35 @@ Waiting for events in sync mode =============================== -When waiting for an event in a specific thread, we just use waitpid, passing -the specific pid, and not passing WNOHANG. - -When waiting for an event in all threads, waitpid is not quite good. Prior to -version 2.4, Linux can either wait for event in main thread, or in secondary -threads. (2.4 has the __WALL flag). So, if we use blocking waitpid, we might -miss an event. The solution is to use non-blocking waitpid, together with -sigsuspend. First, we use non-blocking waitpid to get an event in the main -process, if any. Second, we use non-blocking waitpid with the __WCLONED -flag to check for events in cloned processes. If nothing is found, we use -sigsuspend to wait for SIGCHLD. When SIGCHLD arrives, it means something -happened to a child process -- and SIGCHLD will be delivered both for events -in main debugged process and in cloned processes. As soon as we know there's -an event, we get back to calling nonblocking waitpid with and without -__WCLONED. - -Note that SIGCHLD should be blocked between waitpid and sigsuspend calls, -so that we don't miss a signal. If SIGCHLD arrives in between, when it's -blocked, the signal becomes pending and sigsuspend immediately -notices it and returns. - -Waiting for events in async mode -================================ +When waiting for an event in a specific thread, we just use waitpid, +passing the specific pid, and not passing WNOHANG. + +When waiting for an event in all threads, waitpid is not quite good: + +- If the thread group leader exits while other threads in the thread + group still exist, waitpid(TGID, ...) hangs. That waitpid won't + return an exit status until the other threads in the group are + reaped. + +- When a non-leader thread execs, that thread just vanishes without + reporting an exit (so we'd hang if we waited for it explicitly in + that case). The exec event is instead reported to the TGID pid. + +The solution is to always use -1 and WNOHANG, together with +sigsuspend. + +First, we use non-blocking waitpid to check for events. If nothing is +found, we use sigsuspend to wait for SIGCHLD. When SIGCHLD arrives, +it means something happened to a child process. As soon as we know +there's an event, we get back to calling nonblocking waitpid. + +Note that SIGCHLD should be blocked between waitpid and sigsuspend +calls, so that we don't miss a signal. If SIGCHLD arrives in between, +when it's blocked, the signal becomes pending and sigsuspend +immediately notices it and returns. + +Waiting for events in async mode (TARGET_WNOHANG) +================================================= In async mode, GDB should always be ready to handle both user input and target events, so neither blocking waitpid nor sigsuspend are @@ -159,7 +163,24 @@ We could use a real-time signal instead. This would solve those problems; we could use PTRACE_GETSIGINFO to locate the specific stop signals sent by GDB. But we would still have to have some support for SIGSTOP, since PTRACE_ATTACH generates it, and there are races with trying to find a signal that is not -blocked. */ +blocked. + +Exec events +=========== + +The case of a thread group (process) with 3 or more threads, and a +thread other than the leader execs is worth detailing: + +On an exec, the Linux kernel destroys all threads except the execing +one in the thread group, and resets the execing thread's tid to the +tgid. No exit notification is sent for the execing thread -- from the +ptracer's perspective, it appears as though the execing thread just +vanishes. Until we reap all other threads except the leader and the +execing thread, the leader will be zombie, and the execing thread will +be in `D (disc sleep)' state. As soon as all other threads are +reaped, the execing thread changes its tid to the tgid, and the +previous (zombie) leader vanishes, giving place to the "new" +leader. */ #ifndef O_LARGEFILE #define O_LARGEFILE 0 @@ -652,39 +673,6 @@ linux_child_set_syscall_catchpoint (struct target_ops *self, return 0; } -/* On GNU/Linux there are no real LWP's. The closest thing to LWP's - are processes sharing the same VM space. A multi-threaded process - is basically a group of such processes. However, such a grouping - is almost entirely a user-space issue; the kernel doesn't enforce - such a grouping at all (this might change in the future). In - general, we'll rely on the threads library (i.e. the GNU/Linux - Threads library) to provide such a grouping. - - It is perfectly well possible to write a multi-threaded application - without the assistance of a threads library, by using the clone - system call directly. This module should be able to give some - rudimentary support for debugging such applications if developers - specify the CLONE_PTRACE flag in the clone system call, and are - using the Linux kernel 2.4 or above. - - Note that there are some peculiarities in GNU/Linux that affect - this code: - - - In general one should specify the __WCLONE flag to waitpid in - order to make it report events for any of the cloned processes - (and leave it out for the initial process). However, if a cloned - process has exited the exit status is only reported if the - __WCLONE flag is absent. Linux kernel 2.4 has a __WALL flag, but - we cannot use it since GDB must work on older systems too. - - - When a traced, cloned process exits and is waited for by the - debugger, the kernel reassigns it to the original parent and - keeps it around as a "zombie". Somehow, the GNU/Linux Threads - library doesn't notice this, which leads to the "zombie problem": - When debugged a multi-threaded process that spawns a lot of - threads will run out of processes, even if the threads exit, - because the "zombies" stay around. */ - /* List of known LWPs. */ struct lwp_info *lwp_list; @@ -748,7 +736,6 @@ linux_nat_pass_signals (struct target_ops *self, /* Prototypes for local functions. */ static int stop_wait_callback (struct lwp_info *lp, void *data); -static int linux_thread_alive (ptid_t ptid); static char *linux_child_pid_to_exec_file (struct target_ops *self, int pid); static int resume_stopped_resumed_lwps (struct lwp_info *lp, void *data); @@ -964,8 +951,7 @@ exit_lwp (struct lwp_info *lp) Returns a wait status for that LWP, to cache. */ static int -linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned, - int *signalled) +linux_nat_post_attach_wait (ptid_t ptid, int first, int *signalled) { pid_t new_pid, pid = ptid_get_lwp (ptid); int status; @@ -999,17 +985,7 @@ linux_nat_post_attach_wait (ptid_t ptid, int first, int *cloned, /* Make sure the initial process is stopped. The user-level threads layer might want to poke around in the inferior, and that won't work if things haven't stabilized yet. */ - new_pid = my_waitpid (pid, &status, 0); - if (new_pid == -1 && errno == ECHILD) - { - if (first) - warning (_("%s is a cloned process"), target_pid_to_str (ptid)); - - /* Try again with __WCLONE to check cloned processes. */ - new_pid = my_waitpid (pid, &status, __WCLONE); - *cloned = 1; - } - + new_pid = my_waitpid (pid, &status, __WALL); gdb_assert (pid == new_pid); if (!WIFSTOPPED (status)) @@ -1103,7 +1079,6 @@ attach_proc_task_lwp_callback (ptid_t ptid) target_pid_to_str (ptid)); lp = add_lwp (ptid); - lp->cloned = 1; /* The next time we wait for this LWP we'll see a SIGSTOP as PTRACE_ATTACH brings it to a halt. */ @@ -1166,8 +1141,7 @@ linux_nat_attach (struct target_ops *ops, const char *args, int from_tty) /* Add the initial process as the first LWP to the list. */ lp = add_initial_lwp (ptid); - status = linux_nat_post_attach_wait (lp->ptid, 1, &lp->cloned, - &lp->signalled); + status = linux_nat_post_attach_wait (lp->ptid, 1, &lp->signalled); if (!WIFSTOPPED (status)) { if (WIFEXITED (status)) @@ -1700,27 +1674,17 @@ linux_nat_resume (struct target_ops *ops, static int kill_lwp (int lwpid, int signo) { - /* Use tkill, if possible, in case we are using nptl threads. If tkill - fails, then we are not using nptl threads and we should be using kill. */ - -#ifdef HAVE_TKILL_SYSCALL - { - static int tkill_failed; - - if (!tkill_failed) - { - int ret; - - errno = 0; - ret = syscall (__NR_tkill, lwpid, signo); - if (errno != ENOSYS) - return ret; - tkill_failed = 1; - } - } -#endif + int ret; - return kill (lwpid, signo); + errno = 0; + ret = syscall (__NR_tkill, lwpid, signo); + if (errno == ENOSYS) + { + /* If tkill fails, then we are not using nptl threads, a + configuration we no longer support. */ + perror_with_name (("tkill")); + } + return ret; } /* Handle a GNU/Linux syscall trap wait response. If we see a syscall @@ -1880,8 +1844,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status) { /* The new child has a pending SIGSTOP. We can't affect it until it hits the SIGSTOP, but we're already attached. */ - ret = my_waitpid (new_pid, &status, - (event == PTRACE_EVENT_CLONE) ? __WCLONE : 0); + ret = my_waitpid (new_pid, &status, __WALL); if (ret == -1) perror_with_name (_("waiting for new child")); else if (ret != new_pid) @@ -1944,7 +1907,6 @@ linux_handle_extended_wait (struct lwp_info *lp, int status) pid, new_pid); new_lp = add_lwp (ptid_build (ptid_get_pid (lp->ptid), new_pid, 0)); - new_lp->cloned = 1; new_lp->stopped = 1; new_lp->resumed = 1; @@ -2059,19 +2021,13 @@ wait_lwp (struct lwp_info *lp) for (;;) { - /* If my_waitpid returns 0 it means the __WCLONE vs. non-__WCLONE kind - was right and we should just call sigsuspend. */ - - pid = my_waitpid (ptid_get_lwp (lp->ptid), &status, WNOHANG); - if (pid == -1 && errno == ECHILD) - pid = my_waitpid (ptid_get_lwp (lp->ptid), &status, __WCLONE | WNOHANG); + pid = my_waitpid (ptid_get_lwp (lp->ptid), &status, __WALL | WNOHANG); 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. */ + now because if this was a non-leader thread execing, we + won't get an exit event. See comments on exec events at + the top of the file. */ thread_dead = 1; if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, "WL: %s vanished.\n", @@ -2084,9 +2040,8 @@ wait_lwp (struct lwp_info *lp) 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. + tkill(pid,0) cannot be used here as it gets ESRCH for both + for zombie and 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. @@ -2572,15 +2527,6 @@ status_callback (struct lwp_info *lp, void *data) return 1; } -/* Return non-zero if LP isn't stopped. */ - -static int -running_callback (struct lwp_info *lp, void *data) -{ - return (!lp->stopped - || (lwp_status_pending_p (lp) && lp->resumed)); -} - /* Count the LWP's that have had events. */ static int @@ -2860,59 +2806,6 @@ resumed_callback (struct lwp_info *lp, void *data) return lp->resumed; } -/* Stop an active thread, verify it still exists, then resume it. If - the thread ends up with a pending status, then it is not resumed, - and *DATA (really a pointer to int), is set. */ - -static int -stop_and_resume_callback (struct lwp_info *lp, void *data) -{ - if (!lp->stopped) - { - ptid_t ptid = lp->ptid; - - stop_callback (lp, NULL); - stop_wait_callback (lp, NULL); - - /* Resume if the lwp still exists, and the core wanted it - running. */ - lp = find_lwp_pid (ptid); - if (lp != NULL) - { - if (lp->last_resume_kind == resume_stop - && !lwp_status_pending_p (lp)) - { - /* The core wanted the LWP to stop. Even if it stopped - cleanly (with SIGSTOP), leave the event pending. */ - if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "SARC: core wanted LWP %ld stopped " - "(leaving SIGSTOP pending)\n", - ptid_get_lwp (lp->ptid)); - lp->status = W_STOPCODE (SIGSTOP); - } - - if (!lwp_status_pending_p (lp)) - { - if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "SARC: re-resuming LWP %ld\n", - ptid_get_lwp (lp->ptid)); - resume_lwp (lp, lp->step, GDB_SIGNAL_0); - } - else - { - if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "SARC: not re-resuming LWP %ld " - "(has pending)\n", - ptid_get_lwp (lp->ptid)); - } - } - } - return 0; -} - /* Check if we should go on and pass this event to common code. Return the affected lwp if we are, or NULL otherwise. */ @@ -3017,35 +2910,16 @@ linux_nat_filter_event (int lwpid, int status) { if (num_lwps (ptid_get_pid (lp->ptid)) > 1) { - /* If this is the main thread, we must stop all threads and - verify if they are still alive. This is because in the - nptl thread model on Linux 2.4, there is no signal issued - for exiting LWPs other than the main thread. We only get - the main thread exit signal once all child threads have - already exited. If we stop all the threads and use the - stop_wait_callback to check if they have exited we can - determine whether this signal should be ignored or - whether it means the end of the debugged application, - regardless of which threading model is being used. */ - if (ptid_get_pid (lp->ptid) == ptid_get_lwp (lp->ptid)) - { - iterate_over_lwps (pid_to_ptid (ptid_get_pid (lp->ptid)), - stop_and_resume_callback, NULL); - } - if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, "LLW: %s exited.\n", target_pid_to_str (lp->ptid)); - if (num_lwps (ptid_get_pid (lp->ptid)) > 1) - { - /* If there is at least one more LWP, then the exit signal - was not the end of the debugged application and should be - ignored. */ - exit_lwp (lp); - return NULL; - } + /* If there is at least one more LWP, then the exit signal + was not the end of the debugged application and should be + ignored. */ + exit_lwp (lp); + return NULL; } /* Note that even if the leader was ptrace-stopped, it can still @@ -3072,28 +2946,6 @@ linux_nat_filter_event (int lwpid, int status) return lp; } - /* Check if the current LWP has previously exited. In the nptl - thread model, LWPs other than the main thread do not issue - signals when they exit so we must check whenever the thread has - stopped. A similar check is made in stop_wait_callback(). */ - if (num_lwps (ptid_get_pid (lp->ptid)) > 1 && !linux_thread_alive (lp->ptid)) - { - ptid_t ptid = pid_to_ptid (ptid_get_pid (lp->ptid)); - - if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "LLW: %s exited.\n", - target_pid_to_str (lp->ptid)); - - exit_lwp (lp); - - /* Make sure there is at least one thread running. */ - gdb_assert (iterate_over_lwps (ptid, running_callback, NULL)); - - /* Discard the event. */ - return NULL; - } - /* Make sure we don't report a SIGSTOP that we sent ourselves in an attempt to stop an LWP. */ if (lp->signalled @@ -3245,18 +3097,8 @@ check_zombie_leaders (void) leader's exit status until all other threads are gone. - There are 3 or more threads in the group, and a thread - other than the leader exec'd. On an exec, the Linux - kernel destroys all other threads (except the execing - one) in the thread group, and resets the execing thread's - tid to the tgid. No exit notification is sent for the - execing thread -- from the ptracer's perspective, it - appears as though the execing thread just vanishes. - Until we reap all other threads except the leader and the - execing thread, the leader will be zombie, and the - execing thread will be in `D (disc sleep)'. As soon as - all other threads are reaped, the execing thread changes - it's tid to the tgid, and the previous (zombie) leader - vanishes, giving place to the "new" leader. We could try + other than the leader exec'd. See comments on exec + events at the top of the file. We could try distinguishing the exit and exec cases, by waiting once more, and seeing if something comes out, but it doesn't sound useful. The previous leader _does_ go away, and @@ -3337,9 +3179,7 @@ linux_nat_wait_1 (struct target_ops *ops, the TGID pid. */ errno = 0; - lwpid = my_waitpid (-1, &status, __WCLONE | WNOHANG); - if (lwpid == 0 || (lwpid == -1 && errno == ECHILD)) - lwpid = my_waitpid (-1, &status, WNOHANG); + lwpid = my_waitpid (-1, &status, __WALL | WNOHANG); if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, @@ -3669,43 +3509,19 @@ kill_wait_callback (struct lwp_info *lp, void *data) SIGSTOPs, pending SIGTRAPs, etc.) to make sure the current program doesn't interfere with any following debugging session. */ - /* For cloned processes we must check both with __WCLONE and - without, since the exit status of a cloned process isn't reported - with __WCLONE. */ - if (lp->cloned) - { - do - { - pid = my_waitpid (ptid_get_lwp (lp->ptid), NULL, __WCLONE); - if (pid != (pid_t) -1) - { - if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "KWC: wait %s received unknown.\n", - target_pid_to_str (lp->ptid)); - /* The Linux kernel sometimes fails to kill a thread - completely after PTRACE_KILL; that goes from the stop - point in do_fork out to the one in - get_signal_to_deliever and waits again. So kill it - again. */ - kill_callback (lp, NULL); - } - } - while (pid == ptid_get_lwp (lp->ptid)); - - gdb_assert (pid == -1 && errno == ECHILD); - } - do { - pid = my_waitpid (ptid_get_lwp (lp->ptid), NULL, 0); + pid = my_waitpid (ptid_get_lwp (lp->ptid), NULL, __WALL); if (pid != (pid_t) -1) { if (debug_linux_nat) fprintf_unfiltered (gdb_stdlog, - "KWC: wait %s received unk.\n", + "KWC: wait %s received unknown.\n", target_pid_to_str (lp->ptid)); - /* See the call to kill_callback above. */ + /* The Linux kernel sometimes fails to kill a thread + completely after PTRACE_KILL; that goes from the stop + point in do_fork out to the one in get_signal_to_deliver + and waits again. So kill it again. */ kill_callback (lp, NULL); } } @@ -3891,33 +3707,10 @@ linux_nat_xfer_partial (struct target_ops *ops, enum target_object object, } static int -linux_thread_alive (ptid_t ptid) -{ - int err, tmp_errno; - - gdb_assert (ptid_lwp_p (ptid)); - - /* Send signal 0 instead of anything ptrace, because ptracing a - running thread errors out claiming that the thread doesn't - exist. */ - err = kill_lwp (ptid_get_lwp (ptid), 0); - tmp_errno = errno; - if (debug_linux_nat) - fprintf_unfiltered (gdb_stdlog, - "LLTA: KILL(SIG0) %s (%s)\n", - target_pid_to_str (ptid), - err ? safe_strerror (tmp_errno) : "OK"); - - if (err != 0) - return 0; - - return 1; -} - -static int linux_nat_thread_alive (struct target_ops *ops, ptid_t ptid) { - return linux_thread_alive (ptid); + /* As long as a PTID is in lwp list, consider it alive. */ + return find_lwp_pid (ptid) != NULL; } /* Implement the to_update_thread_list target method for this @@ -3926,15 +3719,10 @@ linux_nat_thread_alive (struct target_ops *ops, ptid_t ptid) static void linux_nat_update_thread_list (struct target_ops *ops) { - if (linux_supports_traceclone ()) - { - /* With support for clone events, we add/delete threads from the - list as clone/exit events are processed, so just try deleting - exited threads still in the thread list. */ - delete_exited_threads (); - } - else - prune_threads (); + /* We add/delete threads from the list as clone/exit events are + processed, so just try deleting exited threads still in the + thread list. */ + delete_exited_threads (); } static char * @@ -5011,70 +4799,16 @@ Enables printf debugging output."), the GNU/Linux Threads library and therefore doesn't really belong here. */ -/* Read variable NAME in the target and return its value if found. - Otherwise return zero. It is assumed that the type of the variable - is `int'. */ - -static int -get_signo (const char *name) -{ - struct bound_minimal_symbol ms; - int signo; - - ms = lookup_minimal_symbol (name, NULL, NULL); - if (ms.minsym == NULL) - return 0; - - if (target_read_memory (BMSYMBOL_VALUE_ADDRESS (ms), (gdb_byte *) &signo, - sizeof (signo)) != 0) - return 0; - - return signo; -} - /* Return the set of signals used by the threads library in *SET. */ void lin_thread_get_thread_signals (sigset_t *set) { - struct sigaction action; - int restart, cancel; - - sigemptyset (&blocked_mask); sigemptyset (set); - restart = get_signo ("__pthread_sig_restart"); - cancel = get_signo ("__pthread_sig_cancel"); - - /* LinuxThreads normally uses the first two RT signals, but in some legacy - cases may use SIGUSR1/SIGUSR2. NPTL always uses RT signals, but does - not provide any way for the debugger to query the signal numbers - - fortunately they don't change! */ - - if (restart == 0) - restart = __SIGRTMIN; - - if (cancel == 0) - cancel = __SIGRTMIN + 1; - - sigaddset (set, restart); - sigaddset (set, cancel); - - /* The GNU/Linux Threads library makes terminating threads send a - special "cancel" signal instead of SIGCHLD. Make sure we catch - those (to prevent them from terminating GDB itself, which is - likely to be their default action) and treat them the same way as - SIGCHLD. */ - - action.sa_handler = sigchld_handler; - sigemptyset (&action.sa_mask); - action.sa_flags = SA_RESTART; - sigaction (cancel, &action, NULL); - - /* We block the "cancel" signal throughout this code ... */ - sigaddset (&blocked_mask, cancel); - sigprocmask (SIG_BLOCK, &blocked_mask, NULL); - - /* ... except during a sigsuspend. */ - sigdelset (&suspend_mask, cancel); + /* NPTL reserves the first two RT signals, but does not provide any + way for the debugger to query the signal numbers - fortunately + they don't change. */ + sigaddset (set, __SIGRTMIN); + sigaddset (set, __SIGRTMIN + 1); } diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h index f5ec74e..24cd54a 100644 --- a/gdb/linux-nat.h +++ b/gdb/linux-nat.h @@ -37,11 +37,6 @@ struct lwp_info next time we see this LWP stop. */ int must_set_ptrace_flags; - /* Non-zero if this LWP is cloned. In this context "cloned" means - that the LWP is reporting to its parent using a signal other than - SIGCHLD. */ - int cloned; - /* Non-zero if we sent this LWP a SIGSTOP (but the LWP didn't report it back yet). */ int signalled; diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c index 4e51c74..ce3f6a1 100644 --- a/gdb/linux-thread-db.c +++ b/gdb/linux-thread-db.c @@ -54,11 +54,12 @@ created, thread IDs (usually, the result of pthread_self), and thread-local variables. - The libthread_db interface originates on Solaris, where it is - both more powerful and more complicated. This implementation - only works for LinuxThreads and NPTL, the two glibc threading - libraries. It assumes that each thread is permanently assigned - to a single light-weight process (LWP). + The libthread_db interface originates on Solaris, where it is both + more powerful and more complicated. This implementation only works + for NPTL, the glibc threading library. It assumes that each thread + is permanently assigned to a single light-weight process (LWP). At + some point it also supported the older LinuxThreads library, but it + no longer does. libthread_db-specific information is stored in the "private" field of struct thread_info. When the field is NULL we do not yet have @@ -1717,10 +1718,9 @@ _initialize_thread_db (void) /* Defer loading of libthread_db.so until inferior is running. This allows gdb to load correct libthread_db for a given - executable -- there could be mutiple versions of glibc, - compiled with LinuxThreads or NPTL, and until there is - a running inferior, we can't tell which libthread_db is - the correct one to load. */ + executable -- there could be multiple versions of glibc, + and until there is a running inferior, we can't tell which + libthread_db is the correct one to load. */ libthread_db_search_path = xstrdup (LIBTHREAD_DB_SEARCH_PATH); diff --git a/gdb/nat/linux-waitpid.c b/gdb/nat/linux-waitpid.c index cbcdd95..2d80e0a 100644 --- a/gdb/nat/linux-waitpid.c +++ b/gdb/nat/linux-waitpid.c @@ -74,8 +74,7 @@ status_to_str (int status) return buf; } -/* Wrapper function for waitpid which handles EINTR, and emulates - __WALL for systems where that is not available. */ +/* See linux-waitpid.h. */ int my_waitpid (int pid, int *status, int flags) @@ -84,64 +83,12 @@ my_waitpid (int pid, int *status, int flags) linux_debug ("my_waitpid (%d, 0x%x)\n", pid, flags); - if (flags & __WALL) + do { - sigset_t block_mask, org_mask, wake_mask; - int wnohang; - - wnohang = (flags & WNOHANG) != 0; - flags &= ~(__WALL | __WCLONE); - - if (!wnohang) - { - flags |= WNOHANG; - - /* Block all signals while here. This avoids knowing about - LinuxThread's signals. */ - sigfillset (&block_mask); - sigprocmask (SIG_BLOCK, &block_mask, &org_mask); - - /* ... except during the sigsuspend below. */ - sigemptyset (&wake_mask); - } - - while (1) - { - /* Since all signals are blocked, there's no need to check - for EINTR here. */ - ret = waitpid (pid, status, flags); - out_errno = errno; - - if (ret == -1 && out_errno != ECHILD) - break; - else if (ret > 0) - break; - - if (flags & __WCLONE) - { - /* We've tried both flavors now. If WNOHANG is set, - there's nothing else to do, just bail out. */ - if (wnohang) - break; - - linux_debug ("blocking\n"); - - /* Block waiting for signals. */ - sigsuspend (&wake_mask); - } - flags ^= __WCLONE; - } - - if (!wnohang) - sigprocmask (SIG_SETMASK, &org_mask, NULL); - } - else - { - do - ret = waitpid (pid, status, flags); - while (ret == -1 && errno == EINTR); - out_errno = errno; + ret = waitpid (pid, status, flags); } + while (ret == -1 && errno == EINTR); + out_errno = errno; linux_debug ("my_waitpid (%d, 0x%x): status(%x), %d\n", pid, flags, (ret > 0 && status != NULL) ? *status : -1, ret); diff --git a/gdb/nat/linux-waitpid.h b/gdb/nat/linux-waitpid.h index bc5e8d2..1a15fdc 100644 --- a/gdb/nat/linux-waitpid.h +++ b/gdb/nat/linux-waitpid.h @@ -20,8 +20,7 @@ #ifndef LINUX_WAITPID_H #define LINUX_WAITPID_H -/* Wrapper function for waitpid which handles EINTR, and emulates - __WALL for systems where that is not available. */ +/* Wrapper function for waitpid which handles EINTR. */ extern int my_waitpid (int pid, int *status, int flags); /* Convert wait status STATUS to a string. Used for printing debug |