From patchwork Tue Apr 16 15:06:53 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tom de Vries X-Patchwork-Id: 32300 Received: (qmail 29814 invoked by alias); 16 Apr 2019 15:06:59 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 29805 invoked by uid 89); 16 Apr 2019 15:06:59 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-25.7 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, SPF_PASS autolearn=ham version=3.3.1 spammy= X-HELO: mx1.suse.de Received: from mx2.suse.de (HELO mx1.suse.de) (195.135.220.15) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Tue, 16 Apr 2019 15:06:58 +0000 Received: from relay2.suse.de (unknown [195.135.220.254]) by mx1.suse.de (Postfix) with ESMTP id B6040AFC5; Tue, 16 Apr 2019 15:06:55 +0000 (UTC) Date: Tue, 16 Apr 2019 17:06:53 +0200 From: Tom de Vries To: gdb-patches@sourceware.org Cc: Pedro Alves Subject: [PATCH] Handle vfork in thread with follow-fork-mode child Message-ID: <20190416150652.GA4805@delia> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.10.1 (2018-07-13) X-IsSubscribed: yes Hi, When debugging the test-case vfork-follow-child.c (which does a vfork in a thread) with follow-fork-mode child set, we run into this assertion: ... src/gdb/nat/x86-linux-dregs.c:146: internal-error: \ void x86_linux_update_debug_registers(lwp_info*): \ Assertion `lwp_is_stopped (lwp)' failed. ... The assert is caused by the following: the event that the vfork child exits, is handled by handle_vfork_child_exec_or_exit, which calls target_detach to detach from the vfork parent. During target_detach we call linux_nat_target::detach, which: - stops all the threads - waits for all the threads to be stopped - detaches all the threads. However, during the second step we run into this code in stop_wait_callback: ... /* If this is a vfork parent, bail out, it is not going to report any SIGSTOP until the vfork is done with. */ if (inf->vfork_child != NULL) return 0; ... and we don't wait for the threads to be stopped, which resulting in this assert in x86_linux_update_debug_registers triggering during the third step: ... gdb_assert (lwp_is_stopped (lwp)); ... Fix this by resetting the vfork parent's vfork_child field before calling target_detach in handle_vfork_child_exec_or_exit. Tested on x86_64-linux, using native and native-gdbserver. OK for trunk? Thanks, - Tom [gdb] Handle vfork in thread with follow-fork-mode child gdb/ChangeLog: 2019-04-16 Tom de Vries PR gdb/24454 * infrun.c (handle_vfork_child_exec_or_exit): Reset vfork parent's avfork_child field before calling target_detach. gdb/testsuite/ChangeLog: 2019-04-16 Tom de Vries PR gdb/24454 * gdb.threads/vfork-follow-child.c: New test. * gdb.threads/vfork-follow-child.exp: New file. --- gdb/infrun.c | 13 ++++++++++++- gdb/testsuite/gdb.threads/vfork-follow-child.c | 19 +++++++++++++++++++ gdb/testsuite/gdb.threads/vfork-follow-child.exp | 21 +++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/gdb/infrun.c b/gdb/infrun.c index 37713b24fe..b088138250 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -923,6 +923,7 @@ handle_vfork_child_exec_or_exit (int exec) struct thread_info *tp; struct program_space *pspace; struct address_space *aspace; + struct inferior *to_detach; /* follow-fork child, detach-on-fork on. */ @@ -982,7 +983,17 @@ handle_vfork_child_exec_or_exit (int exec) } } - target_detach (inf->vfork_parent, 0); + /* Now that the vfork child has terminated, make sure during detach + that we no longer consider the vfork parent to be a vfork parent, + but just a regular process that we're detaching from. If not, on + linux we would avoid waiting for threads to stop in + linux-nat.c:stop_wait_callback, while that was only necessary when + the vfork child was still active. */ + to_detach = inf->vfork_parent; + inf->vfork_parent->vfork_child = NULL; + inf->vfork_parent = NULL; + + target_detach (to_detach, 0); /* Put it back. */ inf->pspace = pspace; diff --git a/gdb/testsuite/gdb.threads/vfork-follow-child.c b/gdb/testsuite/gdb.threads/vfork-follow-child.c new file mode 100644 index 0000000000..42c76edd01 --- /dev/null +++ b/gdb/testsuite/gdb.threads/vfork-follow-child.c @@ -0,0 +1,19 @@ +#include +#include +#include + +static void * +f (void *arg) +{ + vfork (); + return NULL; +} + +int +main (void) +{ + pthread_t tid; + pthread_create (&tid, NULL, f, NULL); + pthread_join (tid, NULL); + return 0; +} diff --git a/gdb/testsuite/gdb.threads/vfork-follow-child.exp b/gdb/testsuite/gdb.threads/vfork-follow-child.exp new file mode 100644 index 0000000000..26896975af --- /dev/null +++ b/gdb/testsuite/gdb.threads/vfork-follow-child.exp @@ -0,0 +1,21 @@ +if { ! [istarget "*-*-linux*"] } { + return 0 +} + +standard_testfile + +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ + executable {debug}] != "" } { + return -1 +} + +clean_restart ${binfile} + +if ![runto_main] then { + fail "can't run to main" + return 0 +} + +gdb_test "set follow-fork-mode child" + +gdb_test "continue" "" "continue"