From: Hui Zhu <hui_zhu@mentor.com>
To: Pedro Alves <palves@redhat.com>,
gdb-patches ml <gdb-patches@sourceware.org>
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)
Date: Sat, 05 Jul 2014 06:08:00 -0000 [thread overview]
Message-ID: <53B79654.1050603@mentor.com> (raw)
In-Reply-To: <53B6E978.4070903@redhat.com>
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 <hui@codesourcery.com>
>>
>> * 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 <palves@redhat.com>
> 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 <palves@redhat.com>
>
> * 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;
> }
>
prev parent reply other threads:[~2014-07-05 6:08 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-04-03 8:12 [PATCH] Fix gdb.base/watch-vfork.exp: Watchpoint triggers after vfork (sw) (timeout) with Linux 2.6.32 and older version Hui Zhu
2014-05-28 19:19 ` Pedro Alves
2014-06-04 8:43 ` Hui Zhu
2014-06-04 16:11 ` Pedro Alves
2014-06-05 7:48 ` Hui Zhu
2014-06-05 8:43 ` Pedro Alves
2014-06-08 11:16 ` Hui Zhu
2014-06-09 13:58 ` [pushed] Fix a bunch of fork related regressions. (was: Re: [PATCH] Fix gdb.base/watch-vfork.exp: Watchpoint triggers after vfork (sw) (timeout) with Linux 2.6.32 and older version) Pedro Alves
2014-07-03 16:24 ` [PATCH] Fix gdb.base/watch-vfork.exp: Watchpoint triggers after vfork (sw) (timeout) with Linux 2.6.32 and older version Hui Zhu
2014-07-04 17:51 ` [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) Pedro Alves
2014-07-05 6:08 ` Hui Zhu [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=53B79654.1050603@mentor.com \
--to=hui_zhu@mentor.com \
--cc=gdb-patches@sourceware.org \
--cc=palves@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox