aboutsummaryrefslogtreecommitdiff
path: root/gdb/testsuite/gdb.threads/process-dies-while-detaching.c
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2016-07-01 11:16:33 +0100
committerPedro Alves <palves@redhat.com>2016-07-01 11:27:06 +0100
commitced2dffbf17bc661e959da1e39411d706ade9f77 (patch)
tree093cb8ad73368e53d7b30fec408b7ba1bb53a729 /gdb/testsuite/gdb.threads/process-dies-while-detaching.c
parent630008884535a5b26828325e48e729034c110536 (diff)
downloadgdb-ced2dffbf17bc661e959da1e39411d706ade9f77.zip
gdb-ced2dffbf17bc661e959da1e39411d706ade9f77.tar.gz
gdb-ced2dffbf17bc661e959da1e39411d706ade9f77.tar.bz2
Fix failure to detach if process exits while detaching on Linux
This commit fixes detaching on Linux when some thread exits the whole thread group (process) just while we're detaching. On Linux, a ptracer must detach from each LWP individually, with PTRACE_DETACH. Since PTRACE_DETACH sets the thread running free, if one of the already-detached threads causes the whole thread group to exit (e.g., simply calls exit), the kernel force-kills the other threads in the group, making them zombie, just as we're still detaching them. Since PTRACE_DETACH against a zombie thread fails with ESRCH, and gdb/gdbserver are not expecting this, the detach fails with an error like: "Can't detach process: No such process.". This patch detects this detach failure as normal, and instead of erroring out, reaps the now-dead thread. New test included, that exercises several different scenarios that cause GDB/GDBserver to error out when it should not. Tested on x86-64 GNU/Linux with {unix, native-gdbserver, native-extended-gdbserver} Note: without the previous fix, the "single-process + continue" variant of the new test would fail with: (gdb) PASS: gdb.threads/process-dies-while-detaching.exp: single-process: continue: watchpoint: switch to parent continue Continuing. Warning: Could not insert hardware watchpoint 3. Could not insert hardware breakpoints: You may have requested too many hardware breakpoints/watchpoints. Command aborted. (gdb) FAIL: gdb.threads/process-dies-while-detaching.exp: single-process: continue: watchpoint: continue gdb/gdbserver/ChangeLog: 2016-07-01 Pedro Alves <palves@redhat.com> Antoine Tremblay <antoine.tremblay@ericsson.com> * linux-low.c: Change interface to take the target lwp_info pointer directly and return void. Handle detaching from a zombie thread. (linux_detach_lwp_callback): New function. (linux_detach): Detach from the leader thread after detaching from the clone threads. gdb/ChangeLog: 2016-07-01 Pedro Alves <palves@redhat.com> Antoine Tremblay <antoine.tremblay@ericsson.com> * inf-ptrace.c (inf_ptrace_detach_success): New function, factored out from ... (inf_ptrace_detach): ... here. * inf-ptrace.h (inf_ptrace_detach_success): New declaration. * linux-nat.c (get_pending_status): Rename to ... (get_detach_signal): ... this, and return a host signal instead of filling in a wait status. (detach_one_lwp): New function, factored out from detach_callback and adjusted to handle detaching from a zombie thread. (detach_callback): Skip the leader thread. (linux_nat_detach): No longer defer to inf_ptrace_detach to detach the leader thread, nor build a signal string to pass down. Instead, use target_announce_detach, detach_one_lwp and inf_ptrace_detach_success. gdb/testsuite/ChangeLog: 2016-07-01 Pedro Alves <palves@redhat.com> Antoine Tremblay <antoine.tremblay@ericsson.com> * gdb.threads/process-dies-while-detaching.c: New file. * gdb.threads/process-dies-while-detaching.exp: New file.
Diffstat (limited to 'gdb/testsuite/gdb.threads/process-dies-while-detaching.c')
-rw-r--r--gdb/testsuite/gdb.threads/process-dies-while-detaching.c116
1 files changed, 116 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.threads/process-dies-while-detaching.c b/gdb/testsuite/gdb.threads/process-dies-while-detaching.c
new file mode 100644
index 0000000..a28e804
--- /dev/null
+++ b/gdb/testsuite/gdb.threads/process-dies-while-detaching.c
@@ -0,0 +1,116 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2016 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 <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <assert.h>
+
+/* This barrier ensures we only reach the initial breakpoint after all
+ threads have started. */
+pthread_barrier_t start_threads_barrier;
+
+/* Many threads in order to be fairly sure the process exits while GDB
+ is detaching from each thread in the process, on targets that need
+ to detach from each thread individually. */
+#define NTHREADS 256
+
+/* GDB sets a watchpoint here. */
+int globalvar = 1;
+
+/* GDB reads this. */
+int mypid;
+
+/* Threads' entry point. */
+
+void *
+thread_function (void *arg)
+{
+ pthread_barrier_wait (&start_threads_barrier);
+ _exit (0);
+}
+
+/* The fork child's entry point. */
+
+void
+child_function (void)
+{
+ pthread_t threads[NTHREADS];
+ int i;
+
+ pthread_barrier_init (&start_threads_barrier, NULL, NTHREADS + 1);
+
+ for (i = 0; i < NTHREADS; i++)
+ pthread_create (&threads[i], NULL, thread_function, NULL);
+ pthread_barrier_wait (&start_threads_barrier);
+
+ exit (0);
+}
+
+/* This is defined by the .exp file if testing the multi-process
+ variant. */
+#ifdef MULTIPROCESS
+
+/* The fork parent's entry point. */
+
+void
+parent_function (pid_t child)
+{
+ int status, ret;
+
+ alarm (300);
+
+ ret = waitpid (child, &status, 0);
+ if (ret == -1)
+ exit (1);
+ else if (!WIFEXITED (status))
+ exit (2);
+ else
+ {
+ printf ("exited, status=%d\n", WEXITSTATUS (status));
+ exit (0);
+ }
+}
+
+#endif
+
+int
+main (void)
+{
+#ifdef MULTIPROCESS
+ pid_t child;
+
+ child = fork ();
+ if (child == -1)
+ return 1;
+#endif
+
+ mypid = getpid ();
+
+#ifdef MULTIPROCESS
+ if (child != 0)
+ parent_function (child);
+ else
+#endif
+ child_function ();
+
+ /* Not reached. */
+ abort ();
+}