diff options
-rw-r--r-- | gdb/linux-nat.c | 49 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/traced-thread.c | 105 | ||||
-rw-r--r-- | gdb/testsuite/gdb.base/traced-thread.exp | 54 | ||||
-rw-r--r-- | gdbserver/linux-low.cc | 15 |
4 files changed, 210 insertions, 13 deletions
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 737e0f7..1c46906 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -1037,8 +1037,8 @@ attach_proc_task_lwp_callback (ptid_t ptid) std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err); - warning (_("Cannot attach to lwp %d: %s"), - lwpid, reason.c_str ()); + error (_("Cannot attach to lwp %d: %s"), + lwpid, reason.c_str ()); } } else @@ -1058,13 +1058,6 @@ attach_proc_task_lwp_callback (ptid_t ptid) /* So that wait collects the SIGSTOP. */ lp->resumed = 1; - - /* Also add the LWP to gdb's thread list, in case a - matching libthread_db is not found (or the process uses - raw clone). */ - add_thread (linux_target, lp->ptid); - set_running (linux_target, lp->ptid, true); - set_executing (linux_target, lp->ptid, true); } return 1; @@ -1159,8 +1152,42 @@ linux_nat_target::attach (const char *args, int from_tty) of threads/LWPs, and those structures may well be corrupted. Note that once thread_db is loaded, we'll still use it to list threads and associate pthread info with each LWP. */ - linux_proc_attach_tgid_threads (lp->ptid.pid (), - attach_proc_task_lwp_callback); + try + { + linux_proc_attach_tgid_threads (lp->ptid.pid (), + attach_proc_task_lwp_callback); + } + catch (const gdb_exception_error &) + { + /* Failed to attach to some LWP. Detach any we've already + attached to. */ + iterate_over_lwps (ptid_t (ptid.pid ()), + [] (struct lwp_info *lwp) -> int + { + /* Ignore errors when detaching. */ + ptrace (PTRACE_DETACH, lwp->ptid.lwp (), 0, 0); + delete_lwp (lwp->ptid); + return 0; + }); + + target_terminal::ours (); + target_mourn_inferior (inferior_ptid); + + throw; + } + + /* Add all the LWPs to gdb's thread list. */ + iterate_over_lwps (ptid_t (ptid.pid ()), + [] (struct lwp_info *lwp) -> int + { + if (lwp->ptid.pid () != lwp->ptid.lwp ()) + { + add_thread (linux_target, lwp->ptid); + set_running (linux_target, lwp->ptid, true); + set_executing (linux_target, lwp->ptid, true); + } + return 0; + }); } /* Ptrace-detach the thread with pid PID. */ diff --git a/gdb/testsuite/gdb.base/traced-thread.c b/gdb/testsuite/gdb.base/traced-thread.c new file mode 100644 index 0000000..f30e09b --- /dev/null +++ b/gdb/testsuite/gdb.base/traced-thread.c @@ -0,0 +1,105 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2023 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 <assert.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/ptrace.h> +#include <pthread.h> +#include <stdlib.h> +#include <errno.h> + +int fds[2]; + +_Atomic pid_t bg_tid = 0; + +pthread_barrier_t barrier; + +#define FIVE_MINUTES (5 * 60) + +/* One thread of the child process. This is traced by the parent + process. */ +void * +block (void *ignore) +{ + bg_tid = gettid (); + pthread_barrier_wait (&barrier); + sleep (FIVE_MINUTES); + return 0; +} + +/* The parent process blocks in this function. */ +void +parent_stop (pid_t child_thread_pid) +{ + sleep (FIVE_MINUTES); +} + +int +main () +{ + int result; + + pthread_barrier_init (&barrier, NULL, 2); + + result = pipe (fds); + assert (result != -1); + + pid_t child = fork (); + if (child != 0) + { + /* Parent. */ + close (fds[1]); + + pid_t the_tid; + result = read (fds[0], &the_tid, sizeof (the_tid)); + assert (result == sizeof (the_tid)); + + /* Trace a single, non-main thread of the child. This should + prevent gdb from attaching to the child at all. The bug here + was that gdb would get into an infinite loop repeatedly + trying to attach to this thread. */ + result = ptrace (PTRACE_SEIZE, the_tid, (void *) 0, (void *) 0); + if (result == -1) + perror ("ptrace"); + + parent_stop (child); + } + else + { + /* Child. */ + + close (fds[0]); + + pthread_t thr; + result = pthread_create (&thr, 0, block, 0); + assert (result == 0); + + /* Wait until the TID has been assigned. */ + pthread_barrier_wait (&barrier); + assert (bg_tid != 0); + + result = write (fds[1], &bg_tid, sizeof (bg_tid)); + assert (result == sizeof (bg_tid)); + + sleep (FIVE_MINUTES); + } + + exit (0); +} diff --git a/gdb/testsuite/gdb.base/traced-thread.exp b/gdb/testsuite/gdb.base/traced-thread.exp new file mode 100644 index 0000000..d590ab1 --- /dev/null +++ b/gdb/testsuite/gdb.base/traced-thread.exp @@ -0,0 +1,54 @@ +# Copyright 2023 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/>. */ + +# This test only works on GNU/Linux. +require !use_gdb_stub isnative +require {!is_remote host} +require {istarget *-linux*} + +standard_testfile + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile \ + {debug pthreads}]} { + return -1 +} + +if {![runto "parent_stop"]} { + return -1 +} + +# We don't want to be bothered. +gdb_test_no_output "set confirm off" + +set child_pid [get_integer_valueof child_thread_pid -1 "get child pid"] + +# We should not be able to attach to the child at all, because one +# thread is being traced. The bug here was that gdb would get into an +# infinite loop trying to attach to this thread. +gdb_test "add-inferior" "Added inferior 2.*" "add empty inferior 2" +gdb_test "inferior 2" "Switching to inferior 2.*" "switch to inferior 2" +# Recognize failures from either gdb or gdbserver. +gdb_test "attach $child_pid" \ + "(Cannot attach to|Attaching to process $decimal failed).*" \ + "should not attach to child process" + + +# Now kill the parent process, ending the trace. +gdb_test "inferior 1" "Switching to inferior 1.*" "switch to inferior 1" +gdb_test "kill" ".*" "kill the parent process" + +# Kill the child process as well. Use the shell to avoid funny +# business with gdbserver testing. +remote_exec target "kill -9 $child_pid" diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc index eea2d8a..4aa011c 100644 --- a/gdbserver/linux-low.cc +++ b/gdbserver/linux-low.cc @@ -1154,7 +1154,7 @@ attach_proc_task_lwp_callback (ptid_t ptid) std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err); - warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ()); + error (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ()); } return 1; @@ -1207,7 +1207,18 @@ linux_process_target::attach (unsigned long pid) threads/LWPs, and those structures may well be corrupted. Note that once thread_db is loaded, we'll still use it to list threads and associate pthread info with each LWP. */ - linux_proc_attach_tgid_threads (pid, attach_proc_task_lwp_callback); + try + { + linux_proc_attach_tgid_threads (pid, attach_proc_task_lwp_callback); + } + catch (const gdb_exception_error &) + { + /* Make sure we do not deliver the SIGSTOP to the process. */ + initial_thread->last_resume_kind = resume_continue; + + this->detach (proc); + throw; + } /* GDB will shortly read the xml target description for this process, to figure out the process' architecture. But the target |