From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 12993 invoked by alias); 5 Jul 2014 06:08:34 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Received: (qmail 12971 invoked by uid 89); 5 Jul 2014 06:08:32 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.9 required=5.0 tests=AWL,BAYES_00 autolearn=ham version=3.3.2 X-HELO: relay1.mentorg.com Received: from relay1.mentorg.com (HELO relay1.mentorg.com) (192.94.38.131) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 05 Jul 2014 06:08:28 +0000 Received: from svr-orw-exc-10.mgc.mentorg.com ([147.34.98.58]) by relay1.mentorg.com with esmtp id 1X3J96-0003ah-CK from Hui_Zhu@mentor.com ; Fri, 04 Jul 2014 23:08:24 -0700 Received: from SVR-ORW-FEM-02.mgc.mentorg.com ([147.34.96.206]) by SVR-ORW-EXC-10.mgc.mentorg.com with Microsoft SMTPSVC(6.0.3790.4675); Fri, 4 Jul 2014 23:08:24 -0700 Received: from localhost.localdomain (147.34.91.1) by svr-orw-fem-02.mgc.mentorg.com (147.34.96.168) with Microsoft SMTP Server id 14.2.247.3; Fri, 4 Jul 2014 23:07:16 -0700 Message-ID: <53B79654.1050603@mentor.com> Date: Sat, 05 Jul 2014 06:08:00 -0000 From: Hui Zhu User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Thunderbird/24.6.0 MIME-Version: 1.0 To: Pedro Alves , gdb-patches ml Subject: Re: [PATCH] Handle signals sent to a fork/vfork child before it has a chance to first run (Re: [PATCH] Fix gdb.base/watch-vfork.exp: Watchpoint triggers after vfork (sw) (timeout) with Linux 2.6.32 and older version) References: <533D17E2.9070402@mentor.com> <538636AF.9040208@redhat.com> <539020AB.8050105@mentor.com> <53902DAA.4090602@redhat.com> <5394460F.7060201@mentor.com> <53B583B2.1040407@mentor.com> <53B6E978.4070903@redhat.com> In-Reply-To: <53B6E978.4070903@redhat.com> Content-Type: text/plain; charset="ISO-8859-1"; format=flowed Content-Transfer-Encoding: 7bit X-IsSubscribed: yes X-SW-Source: 2014-07/txt/msg00098.txt.bz2 On 07/05/14 01:50, Pedro Alves wrote: > On 07/03/2014 05:24 PM, Hui Zhu wrote: > >> After this patch, I still got fail with this test in Linux 2.6.32. >> The cause this: >> signo = WSTOPSIG (status); >> #In linux 2.6.32, signo will be SIGSTOP. >> if (signo != 0 >> && !signal_pass_state (gdb_signal_from_host (signo))) >> signo = 0; >> #SIGSTOP will send to child and make it stop. >> ptrace (PTRACE_DETACH, child_pid, 0, signo); >> >> So I make a patch to fix it. >> >> Thanks, >> Hui >> >> 2014-07-04 Hui Zhu >> >> * linux-nat.c(linux_child_follow_fork): Add check if signo >> is SIGSTOP. >> >> --- a/gdb/linux-nat.c >> +++ b/gdb/linux-nat.c >> @@ -472,8 +472,9 @@ holding the child stopped. Try \"set de >> int signo; >> >> signo = WSTOPSIG (status); >> - if (signo != 0 >> - && !signal_pass_state (gdb_signal_from_host (signo))) >> + if (signo == SIGSTOP >> + || (signo != 0 >> + && !signal_pass_state (gdb_signal_from_host (signo)))) >> signo = 0; >> ptrace (PTRACE_DETACH, child_pid, 0, signo); >> } >> > > Hmm. We should actually be passing the signal the fork child had stopped > for earlier, when we step it for the workaround. But we lose that signal... > > /me hacks. > > Does this patch fix the issue too ? > > 8<----------- > From 4184b4d0ffca89776d06d6b7d1241e9c0d01017a Mon Sep 17 00:00:00 2001 > From: Pedro Alves > Date: Fri, 4 Jul 2014 17:31:41 +0100 > Subject: [PATCH] Handle signals sent to a fork/vfork child before it has a > chance to first run > > This is a WIP. > > We handle this for clone, but not for fork/vfork. For clone it's > easier as we just store the status in the new child's lwp immediately. > But for fork/vfork we don't add the child to the list until the next > resume, when we either follow or detach the child, depending on > follow-fork mode, in linux_child_follow_fork. So store the child's > status in a new simple_pid_list list. > > I haven't tried to write a test yet. I think we have one for clones > somewhere, which we can model on. > > Tested on x86_64 Fedora 20 with no regressions. Hi Pedro, Thanks for your work. The patch fixed the issue and pass regression test in Ubuntu 10.04 that use Linux 2.6.32. Best, Hui > > gdb/ > 2014-07-04 Pedro Alves > > * linux-nat.c (forked_pids): New global. > (save_new_child_stop_status): New function. > (get_status_pass_signal): New function. > (linux_child_follow_fork): Retrieve the child's stop status from > the forked pids list, and pass the signal to the child if > detaching from it, or leave it pending if not detaching. > (cancel_pending_sigstop): New function, factored out from > detach_callback. > (detach_callback): Adjust to use cancel_pending_sigstop. > (linux_handle_extended_wait): Store the fork/vfork child's > status. Adjust comment. > --- > gdb/linux-nat.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++---------- > 1 file changed, 118 insertions(+), 25 deletions(-) > > diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c > index 0ab0362..1435079 100644 > --- a/gdb/linux-nat.c > +++ b/gdb/linux-nat.c > @@ -223,6 +223,10 @@ struct simple_pid_list > }; > struct simple_pid_list *stopped_pids; > > +/* The status of forked pids we've already seen the > + PTRACE_EVENT_FORK/VFORK event for, but haven't followed yet. */ > +static struct simple_pid_list *forked_pids; > + > /* Async mode support. */ > > /* The read/write ends of the pipe registered as waitable file in the > @@ -371,12 +375,71 @@ delete_lwp_cleanup (void *lp_voidp) > delete_lwp (lp->ptid); > } > > +/* CHILD_LP is a new clone/fork child that stopped with STATUS. If > + STATUS is not SIGSTOP, store the status for later, and mark the lwp > + as signalled (there's a SIGSTOP pending in the kernel queue). This > + can happen if someone starts sending signals to the new thread > + before it gets a chance to run, which have a lower number than > + SIGSTOP (e.g. SIGUSR1). */ > + > +static void > +save_new_child_stop_status (struct lwp_info *child_lp, int *status_p) > +{ > + int status = *status_p; > + > + if (status != 0) > + { > + gdb_assert (WIFSTOPPED (status)); > + > + if (WSTOPSIG (status) != SIGSTOP) > + { > + child_lp->signalled = 1; > + > + /* Save the wait status to report later. */ > + if (debug_linux_nat) > + fprintf_unfiltered (gdb_stdlog, > + "SCSS: waitpid of new LWP %ld, " > + "saving status %s\n", > + ptid_get_lwp (child_lp->ptid), > + status_to_str (status)); > + } > + else > + status = 0; > + } > + > + child_lp->status = status; > + *status_p = status; > +} > + > +/* We're about to detach from a child that had stopped with STATUS. > + Return the host signal number to pass, if any. */ > + > +static int > +get_pass_signal_from_status (int status) > +{ > + if (status != 0 && WIFSTOPPED (status)) > + { > + int signo; > + > + signo = WSTOPSIG (status); > + if (signo != 0 > + && !signal_pass_state (gdb_signal_from_host (signo))) > + signo = 0; > + return signo; > + } > + > + return 0; > +} > + > +static void cancel_pending_sigstop (struct lwp_info *lp); > + > static int > linux_child_follow_fork (struct target_ops *ops, int follow_child, > int detach_fork) > { > int has_vforked; > int parent_pid, child_pid; > + int child_status; > > has_vforked = (inferior_thread ()->pending_follow.kind > == TARGET_WAITKIND_VFORKED); > @@ -404,6 +467,11 @@ holding the child stopped. Try \"set detach-on-fork\" or \ > return 1; > } > > + /* If we haven't already seen the new PID stop, wait for it now. */ > + if (!pull_pid_from_list (&forked_pids, child_pid, &child_status)) > + internal_error (__FILE__, __LINE__, > + _("forked pid %d not found in forked list"), child_pid); > + > if (! follow_child) > { > struct lwp_info *child_lp = NULL; > @@ -414,7 +482,6 @@ holding the child stopped. Try \"set detach-on-fork\" or \ > if (detach_fork) > { > struct cleanup *old_chain; > - int status = W_STOPCODE (0); > > /* Before detaching from the child, remove all breakpoints > from it. If we forked, then this has already been taken > @@ -444,8 +511,12 @@ holding the child stopped. Try \"set detach-on-fork\" or \ > child_lp = add_lwp (inferior_ptid); > child_lp->stopped = 1; > child_lp->last_resume_kind = resume_stop; > + save_new_child_stop_status (child_lp, &child_status); > make_cleanup (delete_lwp_cleanup, child_lp); > > + /* If there is a pending SIGSTOP, get rid of it. */ > + cancel_pending_sigstop (child_lp); > + > if (linux_nat_prepare_to_resume != NULL) > linux_nat_prepare_to_resume (child_lp); > > @@ -455,26 +526,26 @@ holding the child stopped. Try \"set detach-on-fork\" or \ > process starts with the TIF_SINGLESTEP/X86_EFLAGS_TF bits > set if the parent process had them set. > To work around this, single step the child process > - once before detaching to clear the flags. */ > + once before detaching to clear the flags. > + Be careful not to lose any signal though. */ > > if (!gdbarch_software_single_step_p (target_thread_architecture > - (child_lp->ptid))) > + (child_lp->ptid))) > { > + int signo = get_pass_signal_from_status (child_status); > + > linux_disable_event_reporting (child_pid); > - if (ptrace (PTRACE_SINGLESTEP, child_pid, 0, 0) < 0) > + signo = get_pass_signal_from_status (child_status); > + if (ptrace (PTRACE_SINGLESTEP, child_pid, 0, signo) < 0) > perror_with_name (_("Couldn't do single step")); > - if (my_waitpid (child_pid, &status, 0) < 0) > + if (my_waitpid (child_pid, &child_status, 0) < 0) > perror_with_name (_("Couldn't wait vfork process")); > } > > - if (WIFSTOPPED (status)) > + if (child_status != 0 && WIFSTOPPED (child_status)) > { > - int signo; > + int signo = get_pass_signal_from_status (child_status); > > - signo = WSTOPSIG (status); > - if (signo != 0 > - && !signal_pass_state (gdb_signal_from_host (signo))) > - signo = 0; > ptrace (PTRACE_DETACH, child_pid, 0, signo); > } > > @@ -502,6 +573,7 @@ holding the child stopped. Try \"set detach-on-fork\" or \ > child_lp = add_lwp (inferior_ptid); > child_lp->stopped = 1; > child_lp->last_resume_kind = resume_stop; > + save_new_child_stop_status (child_lp, &child_status); > child_inf->symfile_flags = SYMFILE_NO_READ; > > /* If this is a vfork child, then the address-space is > @@ -696,6 +768,7 @@ holding the child stopped. Try \"set detach-on-fork\" or \ > child_lp = add_lwp (inferior_ptid); > child_lp->stopped = 1; > child_lp->last_resume_kind = resume_stop; > + save_new_child_stop_status (child_lp, &child_status); > > /* If this is a vfork child, then the address-space is shared > with the parent. If we detached from the parent, then we can > @@ -1510,27 +1583,41 @@ get_pending_status (struct lwp_info *lp, int *status) > return 0; > } > > -static int > -detach_callback (struct lwp_info *lp, void *data) > -{ > - gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status)); > - > - if (debug_linux_nat && lp->status) > - fprintf_unfiltered (gdb_stdlog, "DC: Pending %s for %s on detach.\n", > - strsignal (WSTOPSIG (lp->status)), > - target_pid_to_str (lp->ptid)); > +/* If LP was signalled, can't the pending SIGSTOP with a SIGCONT. */ > > +static void > +cancel_pending_sigstop (struct lwp_info *lp) > +{ > /* If there is a pending SIGSTOP, get rid of it. */ > if (lp->signalled) > { > + /* This can happen if someone starts sending signals to > + the new thread before it gets a chance to run, which > + have a lower number than SIGSTOP (e.g. SIGUSR1). */ > + > + /* There is a pending SIGSTOP; get rid of it. */ > if (debug_linux_nat) > fprintf_unfiltered (gdb_stdlog, > - "DC: Sending SIGCONT to %s\n", > + "Sending SIGCONT to %s\n", > target_pid_to_str (lp->ptid)); > > kill_lwp (ptid_get_lwp (lp->ptid), SIGCONT); > lp->signalled = 0; > } > +} > + > +static int > +detach_callback (struct lwp_info *lp, void *data) > +{ > + gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status)); > + > + if (debug_linux_nat && lp->status) > + fprintf_unfiltered (gdb_stdlog, "DC: Pending %s for %s on detach.\n", > + strsignal (WSTOPSIG (lp->status)), > + target_pid_to_str (lp->ptid)); > + > + /* If there is a pending SIGSTOP, get rid of it. */ > + cancel_pending_sigstop (lp); > > /* We don't actually detach from the LWP that has an id equal to the > overall process id just yet. */ > @@ -2061,6 +2148,14 @@ linux_handle_extended_wait (struct lwp_info *lp, int status, > return 0; > } > > + if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK) > + { > + /* The child may have stopped with a signal other than > + SIGSTOP. Store the status for later, so we don't lose > + the signal. */ > + add_to_pid_list (&forked_pids, new_pid, status); > + } > + > if (event == PTRACE_EVENT_FORK) > ourstatus->kind = TARGET_WAITKIND_FORKED; > else if (event == PTRACE_EVENT_VFORK) > @@ -2086,10 +2181,8 @@ linux_handle_extended_wait (struct lwp_info *lp, int status, > /* This can happen if someone starts sending signals to > the new thread before it gets a chance to run, which > have a lower number than SIGSTOP (e.g. SIGUSR1). > - This is an unlikely case, and harder to handle for > - fork / vfork than for clone, so we do not try - but > - we handle it for clone events here. We'll send > - the other signal on to the thread below. */ > + We'll send the other signal on to the thread > + below. */ > > new_lp->signalled = 1; > } >