From: Tom de Vries Date: Tue, 21 Apr 2020 13:45:57 +0000 (+0200) Subject: [gdb] Fix hang after ext sigkill X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4778a5f87d253399083565b4919816f541ebe414;p=binutils-gdb.git [gdb] Fix hang after ext sigkill Consider the test-case from this patch, compiled with pthread support: ... $ gcc gdb/testsuite/gdb.threads/killed-outside.c -lpthread -g ... After running to all_started, we can print pid: ... $ gdb a.out -ex "b all_started" -ex run -ex "delete 1" -ex "p pid" ... Reading symbols from a.out... Breakpoint 1 at 0x40072b: file killed-outside.c, line 29. Starting program: /data/gdb_versions/devel/a.out [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib64/libthread_db.so.1". [New Thread 0x7ffff77fc700 (LWP 3155)] Thread 1 "a.out" hit Breakpoint 1, all_started () at killed-outside.c:29 29 } $1 = 3151 (gdb) ... If we then kill the inferior using an external SIGKILL: ... (gdb) shell kill -9 3151 ... and subsequently continue: ... (gdb) c Continuing. Couldn't get registers: No such process. Couldn't get registers: No such process. (gdb) Couldn't get registers: No such process. (gdb) Couldn't get registers: No such process. (gdb) Couldn't get registers: No such process. ... gdb hangs repeating the same warning. Typing control-C no longer helps, and we have to kill gdb. This is a regression since commit 873657b9e8 "Preserve selected thread in all-stop w/ background execution". The commit adds a scoped_restore_current_thread typed variable restore_thread to fetch_inferior_event, and the hang is caused by the constructor throwing an exception. Fix this by catching the exception in the constructor. Build and reg-tested on x86_64-linux. gdb/ChangeLog: 2020-04-21 Tom de Vries PR gdb/25471 * thread.c (scoped_restore_current_thread::scoped_restore_current_thread): Catch exception in get_frame_id. gdb/testsuite/ChangeLog: 2020-04-21 Tom de Vries PR gdb/25471 * gdb.threads/killed-outside.c: New test. * gdb.threads/killed-outside.exp: New file. --- diff --git a/gdb/ChangeLog b/gdb/ChangeLog index aa99da0c4d9..0e80cdb2d16 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,10 @@ +2020-04-21 Tom de Vries + + PR gdb/25471 + * thread.c + (scoped_restore_current_thread::scoped_restore_current_thread): Catch + exception in get_frame_id. + 2020-04-20 Tom Tromey * python/python.c (struct gdbpy_event): Mark move constructor as diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index eee1fa375a8..f72256730f3 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2020-04-21 Tom de Vries + + PR gdb/25471 + * gdb.threads/killed-outside.c: New test. + * gdb.threads/killed-outside.exp: New file. + 2020-04-20 Gary Benson * gdb.base/nested-subp1.exp: Use support_nested_function_tests. diff --git a/gdb/testsuite/gdb.threads/killed-outside.c b/gdb/testsuite/gdb.threads/killed-outside.c new file mode 100644 index 00000000000..ac4cf0b07aa --- /dev/null +++ b/gdb/testsuite/gdb.threads/killed-outside.c @@ -0,0 +1,64 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2020 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 . */ + +#include +#include +#include + +pid_t pid; + +static pthread_barrier_t threads_started_barrier; + +static void +all_started (void) +{ +} + +static void * +fun (void *dummy) +{ + int i; + + pthread_barrier_wait (&threads_started_barrier); + + for (i = 0; i < 180; i++) + sleep (1); + + pthread_exit (NULL); +} + +int +main (void) +{ + int i; + pthread_t thread; + + pid = getpid (); + + pthread_barrier_init (&threads_started_barrier, NULL, 2); + + pthread_create (&thread, NULL, fun, NULL); + + pthread_barrier_wait (&threads_started_barrier); + + all_started (); + + for (i = 0; i < 180; i++) + sleep (1); + + exit (EXIT_SUCCESS); +} diff --git a/gdb/testsuite/gdb.threads/killed-outside.exp b/gdb/testsuite/gdb.threads/killed-outside.exp new file mode 100644 index 00000000000..ff5a1157285 --- /dev/null +++ b/gdb/testsuite/gdb.threads/killed-outside.exp @@ -0,0 +1,57 @@ +# Copyright (C) 2020 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 . + +# This test-case tests that continuing an inferior that has been killed +# using an external sigkill does not make gdb misbehave. + +standard_testfile + +if {[prepare_for_testing "failed to prepare" $testfile $srcfile \ + {pthreads debug}] == -1} { + return -1 +} + +if { ![runto "all_started"] } { + return -1 +} +delete_breakpoints + +set testpid [get_valueof "" "pid" -1 "get pid of inferior"] +if { $testpid == -1 } { + return -1 +} +remote_exec target "kill -9 ${testpid}" + +# Give it some time to die. +sleep 2 + +set no_such_process_msg "Couldn't get registers: No such process\." +set killed_msg "Program terminated with signal SIGKILL, Killed\." +set no_longer_exists_msg "The program no longer exists\." +set not_being_run_msg "The program is not being run\." + +gdb_test_multiple "continue" "prompt after first continue" { + -re "Continuing\.\r\n$no_such_process_msg\r\n$no_such_process_msg\r\n$gdb_prompt " { + pass $gdb_test_name + # Two times $no_such_process_msg. The bug condition was triggered, go + # check for it. + gdb_test_multiple "" "messages" { + -re ".*$killed_msg.*$no_longer_exists_msg\r\n" { + pass $gdb_test_name + gdb_test "continue" $not_being_run_msg "second continue" + } + } + } +} diff --git a/gdb/thread.c b/gdb/thread.c index c6e3d356a5a..03805bd2565 100644 --- a/gdb/thread.c +++ b/gdb/thread.c @@ -1488,8 +1488,16 @@ scoped_restore_current_thread::scoped_restore_current_thread () else frame = NULL; - m_selected_frame_id = get_frame_id (frame); - m_selected_frame_level = frame_relative_level (frame); + try + { + m_selected_frame_id = get_frame_id (frame); + m_selected_frame_level = frame_relative_level (frame); + } + catch (const gdb_exception_error &ex) + { + m_selected_frame_id = null_frame_id; + m_selected_frame_level = -1; + } tp->incref (); m_thread = tp;