Re: [PATCH 01/16 v2] Refactor native follow-fork

Breazeal, Don
2014-09-29T18:08:04Z

On 9/26/2014 11:13 AM, Pedro Alves wrote:
> On 09/12/2014 05:50 PM, Breazeal, Don wrote:
>> On 9/9/2014 4:09 AM, Pedro Alves wrote:
>>> On 09/09/2014 12:54 AM, Breazeal, Don wrote:
>
>>> I'd rather not. That'll just add more technical debt. :-) E.g.,
>>> if we can't model this on the few native targets we have, then that'd
>>> indicate that remote debugging against one of those targets (when
>>> we get to gdb/gdbserver parity), wouldn't be correctly modelled, as
>>> you'd be installing those methods in remote.c too. Plus, if we have
>>> target_follow_fork_inferior as an extra method in addition
>>> to target_follow_fork_inferior, and both are always called in
>>> succession, then we might as well _not_ introduce a new target hook,
>>> and just call infrun_follow_fork_inferior from the
>>> top of linux-nat.c:linux_child_follow_fork, and the top of whatever
>>> other target's to_follow_fork method that wants to use it.
>>
>> I've made modifications to inf_ttrace_follow_fork, inf_ttrace_wait,
>> and inf_ptrace_follow_fork in the included patch that I think should
>> allow them to work with follow_fork_inferior. Some other adjustments
>> were necessary in inf-ttrace.c. Some of the changes weren't strictly
>> necessary to get things working with follow_fork_inferior, but it
>> seemed appropriate to make all the implementations more consistent.
>
> Thanks.
>
>>>> The changes to inf_ptrace_follow_fork to get it to work with
>>>> follow_fork_inferior are straightforward. Making those changes to
>>>> inf_ttrace_follow_fork is problematic, primarily because it handles the
>>>> moral equivalent of the vfork-done event differently.
>>>
>>> Can you summarize the differences ?
>>
>> HP-UX apparently delivers a TTEVT_VFORK event for the parent inferior
>> where Linux would report PTRACE_EVENT_VFORK_DONE -- when the child
>> process has either execd or exited. inf_ttrace_follow_fork was handling
>> the parent VFORK event in-line, where the Linux implementation expects
>> a TARGET_WAITKIND_VFORK_DONE event to be reported so it can be handled
>> in the generic event code.
>>
>
> Right. Linux also used to handle that in-line, before 6c95b8df.
>
>> I've included annotated versions of the original implementations of
>> inf_ttrace_follow_fork and inf-ptrace_follow_fork below for reference,
>> to explain how I made decisions on the changes. Each annotation is
>> a comment beginning with "BLOCK n". If you want to skip past all of
>> that you can just search for "Don". :-)
>
> Excellent. That sure made it easier.
>
>
>>>
>>
>> My assumptions about how HP-UX ttrace events work:
>> - FORK:
>> - TTEVT_FORK is reported for both the parent and child processes.
>> The event can be reported for the parent or child in any order.
>>
>> - VFORK:
>> - TTEVT_VFORK is reported for both the parent and child
>> processes.
>> - TTEVT_VFORK is reported for the child first. It is reported
>> for the parent when the vfork is "done" as with the Linux
>> PTRACE_EVENT_VFORK_DONE event, meaning that the parent has
>> called exec or exited. See this comment in inf_ttrace_follow_fork:
>>
>> /* Wait till we get the TTEVT_VFORK event in the parent.
>> This indicates that the child has called exec(3) or has
>> exited and that the parent is ready to be traced again. */
>>
>> The online HP-UX ttrace documentation doesn't really make this
>> ordering explicit, but it doesn't contradict the implementation.
>
> Yeah, I can't imagine any way a TTEVT_VFORK could ever be reported to
> the parent first. When the parent vforks, it blocks until the child
> execs or exits. And the child won't do that until GDB resumes
> the child after handling the TTEVT_VFORK that is reported for the
> child. So the kernel must always report the child's TTEVT_VFORK first.
>
>
>> if (follow_child)
>> {
>> struct thread_info *ti;
>> @@ -533,17 +423,22 @@ inf_ttrace_follow_fork (struct target_ops *ops,
>> int follow_child,
>> inf_ttrace_num_lwps = 1;
>> inf_ttrace_num_lwps_in_syscall = 0;
>>
>> - /* Delete parent. */
>> - delete_thread_silent (ptid_build (pid, lwpid, 0));
>> - detach_inferior (pid);
>> -
>> - /* Add child thread. inferior_ptid was already set above. */
>> - ti = add_thread_silent (inferior_ptid);
>> + ti = find_thread_ptid (inferior_ptid);
>> + gdb_assert (ti != NULL);
>
> Replace these with:
>
> ti = inferior_thread ();

Done.

>
>> ti->private =
>> xmalloc (sizeof (struct inf_ttrace_private_thread_info));
>> memset (ti->private, 0,
>> sizeof (struct inf_ttrace_private_thread_info));
>> }
>> + else
>> + {
>> + pid_t child_pid;
>> +
>> + /* Following parent. Detach child now. */
>> + child_pid = ptid_get_pid (tp->pending_follow.value.related_pid);
>> + if (ttrace (TT_PROC_DETACH, child_pid, 0, 0, 0, 0) == -1)
>> + perror_with_name (("ttrace"));
>> + }
>>
>> return 0;
>> }
>> @@ -661,7 +556,6 @@ inf_ttrace_create_inferior (struct target_ops *ops,
>> char *exec_file,
>> gdb_assert (inf_ttrace_num_lwps_in_syscall == 0);
>> gdb_assert (inf_ttrace_page_dict.count == 0);
>> gdb_assert (inf_ttrace_reenable_page_protections == 0);
>> - gdb_assert (inf_ttrace_vfork_ppid == -1);
>>
>> pid = fork_inferior (exec_file, allargs, env, inf_ttrace_me, NULL,
>> inf_ttrace_prepare, NULL, NULL);
>> @@ -772,7 +666,6 @@ inf_ttrace_attach (struct target_ops *ops, const
>> char *args, int from_tty)
>>
>> gdb_assert (inf_ttrace_num_lwps == 0);
>> gdb_assert (inf_ttrace_num_lwps_in_syscall == 0);
>> - gdb_assert (inf_ttrace_vfork_ppid == -1);
>>
>> if (ttrace (TT_PROC_ATTACH, pid, 0, TT_KILL_ON_EXIT, TT_VERSION, 0)
>> == -1)
>> perror_with_name (("ttrace"));
>> @@ -822,11 +715,12 @@ inf_ttrace_detach (struct target_ops *ops, const
>> char *args, int from_tty)
>> if (ttrace (TT_PROC_DETACH, pid, 0, 0, sig, 0) == -1)
>> perror_with_name (("ttrace"));
>>
>> - if (inf_ttrace_vfork_ppid != -1)
>> + if (current_inferior ()->vfork_parent != NULL)
>> {
>> - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
>> + pid_t ppid = current_inferior ()->vfork_parent->pid;
>> + if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1)
>> perror_with_name (("ttrace"));
>> - inf_ttrace_vfork_ppid = -1;
>> + detach_inferior (ppid);
>> }
>
> I think we should just delete this block instead.
> We shouldn't be blindly detaching from the parent here.
> The user may well still want to debug it. The case of the
> user doing "detach" on the child when the parent is waiting
> for the the vfork-done should be handled by common code.
> (and we should be going through the whole target_detach).
>

Done.

>>
>> inf_ttrace_num_lwps = 0;
>> @@ -850,11 +744,12 @@ inf_ttrace_kill (struct target_ops *ops)
>> perror_with_name (("ttrace"));
>> /* ??? Is it necessary to call ttrace_wait() here? */
>>
>> - if (inf_ttrace_vfork_ppid != -1)
>> + if (current_inferior ()->vfork_parent != NULL)
>> {
>> - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
>> + pid_t ppid = current_inferior ()->vfork_parent->pid;
>> + if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1)
>> perror_with_name (("ttrace"));
>> - inf_ttrace_vfork_ppid = -1;
>> + detach_inferior (ppid);
>> }
>
> This too.
>
Done.

>>
>> target_mourn_inferior ();
>> @@ -967,20 +862,6 @@ inf_ttrace_wait (struct target_ops *ops,
>> if (ttrace_wait (pid, lwpid, TTRACE_WAITOK, &tts, sizeof tts) == -1)
>> perror_with_name (("ttrace_wait"));
>>
>> - if (tts.tts_event == TTEVT_VFORK && tts.tts_u.tts_fork.tts_isparent)
>> - {
>> - if (inf_ttrace_vfork_ppid != -1)
>> - {
>> - gdb_assert (inf_ttrace_vfork_ppid == tts.tts_pid);
>> -
>> - if (ttrace (TT_PROC_DETACH, tts.tts_pid, 0, 0, 0, 0) == -1)
>> - perror_with_name (("ttrace"));
>> - inf_ttrace_vfork_ppid = -1;
>> - }
>> -
>> - tts.tts_event = TTEVT_NONE;
>> - }
>> -
>> clear_sigint_trap ();
>> }
>> while (tts.tts_event == TTEVT_NONE);
>> @@ -1075,17 +956,23 @@ inf_ttrace_wait (struct target_ops *ops,
>> break;
>>
>> case TTEVT_VFORK:
>> - gdb_assert (!tts.tts_u.tts_fork.tts_isparent);
>> -
>> - related_ptid = ptid_build (tts.tts_u.tts_fork.tts_fpid,
>> - tts.tts_u.tts_fork.tts_flwpid, 0);
>> + if (tts.tts_u.tts_fork.tts_isparent)
>> + {
>> + if (current_inferior ()->waiting_fork_vfork_done)
>
> I don't think we should have this waiting_fork_vfork_done check here.
> This should always be a TARGET_WAITKIND_VFORK_DONE?

You are right, at best this check was redundant. Removed it.

>
>> + ourstatus->kind = TARGET_WAITKIND_VFORK_DONE;
>> + }
>> + else
>> + {
>> + related_ptid = ptid_build (tts.tts_u.tts_fork.tts_fpid,
>> + tts.tts_u.tts_fork.tts_flwpid, 0);
>>
>> - ourstatus->kind = TARGET_WAITKIND_VFORKED;
>> - ourstatus->value.related_pid = related_ptid;
>> + ourstatus->kind = TARGET_WAITKIND_VFORKED;
>> + ourstatus->value.related_pid = related_ptid;
>>
>> - /* HACK: To avoid touching the parent during the vfork, switch
>> - away from it. */
>> - inferior_ptid = ptid;
>> + /* HACK: To avoid touching the parent during the vfork, switch
>> + away from it. */
>> + inferior_ptid = ptid;
>
> The core is now aware of the vfork parent inferior. Just delete
> this hack.

Done.

>
>> + }
>> break;
>>
>> case TTEVT_LWP_CREATE:
>> diff --git a/gdb/infrun.c b/gdb/infrun.c
>> index c18267f..af9cbf8 100644
>> --- a/gdb/infrun.c
>> +++ b/gdb/infrun.c
>> @@ -60,6 +60,7 @@
>> #include "completer.h"
>> #include "target-descriptions.h"
>> #include "target-dcache.h"
>> +#include "terminal.h"
>>
>> /* Prototypes for local functions */
>>
>> @@ -79,6 +80,10 @@ static int restore_selected_frame (void *);
>>
>> static int follow_fork (void);
>>
>> +static int follow_fork_inferior (int follow_child, int detach_fork);
>> +
>> +static void follow_inferior_reset_breakpoints (void);
>> +
>> static void set_schedlock_func (char *args, int from_tty,
>> struct cmd_list_element *c);
>>
>> @@ -486,9 +491,11 @@ follow_fork (void)
>> parent = inferior_ptid;
>> child = tp->pending_follow.value.related_pid;
>>
>> - /* Tell the target to do whatever is necessary to follow
>> - either parent or child. */
>> - if (target_follow_fork (follow_child, detach_fork))
>> + /* Set up inferior(s) as specified by the caller, and tell the
>> + target to do whatever is necessary to follow either parent
>> + or child. */
>> + if (follow_fork_inferior (follow_child, detach_fork)
>> + || target_follow_fork (follow_child, detach_fork))
>
> I don't think we should ever call follow_fork_inferior without
> calling target_follow_fork, right? I think it'd be clearer if
> we tail called target_follow_fork inside follow_fork_inferior.

Done.

>
>> {
>> /* Target refused to follow, or there's some other reason
>> we shouldn't resume. */
>> @@ -560,7 +567,242 @@ follow_fork (void)
>> return should_resume;
>> }
>>
>> -void
>> +/* Handle changes to the inferior list based on the type of fork,
>> + which process is being followed, and whether the other process
>> + should be detached. On entry inferior_ptid must be the ptid of
>> + the fork parent. At return inferior_ptid is the ptid of the
>> + followed inferior. */
>> +
>> +int
>> +follow_fork_inferior (int follow_child, int detach_fork)
>> +{
>> + int has_vforked;
>> + int parent_pid, child_pid;
>> +
>> + has_vforked = (inferior_thread ()->pending_follow.kind
>> + == TARGET_WAITKIND_VFORKED);
>> + parent_pid = ptid_get_lwp (inferior_ptid);
>> + if (parent_pid == 0)
>> + parent_pid = ptid_get_pid (inferior_ptid);
>> + child_pid
>> + = ptid_get_pid (inferior_thread ()->pending_follow.value.related_pid);
>> +
>> + if (has_vforked
>> + && !non_stop /* Non-stop always resumes both branches. */
>> + && (!target_is_async_p () || sync_execution)
>> + && !(follow_child || detach_fork || sched_multi))
>> + {
>> + /* The parent stays blocked inside the vfork syscall until the
>> + child execs or exits. If we don't let the child run, then
>> + the parent stays blocked. If we're telling the parent to run
>> + in the foreground, the user will not be able to ctrl-c to get
>> + back the terminal, effectively hanging the debug session. */
>> + fprintf_filtered (gdb_stderr, _("\
>> +Can not resume the parent process over vfork in the foreground while\n\
>> +holding the child stopped. Try \"set detach-on-fork\" or \
>> +\"set schedule-multiple\".\n"));
>> + /* FIXME output string > 80 columns. */
>> + return 1;
>> + }
>> +
>
> Moving this to common code isn't strictly correct, as we'd probably
> be able to interrupt the vfork parent in this case when remote
> debugging, or not sharing the terminal with the inferior. But let's
> put this here for now.
>
>
>> + else
>> + {
>> + child_inf->aspace = new_address_space ();
>> + child_inf->pspace = add_program_space (child_inf->aspace);
>> + child_inf->removable = 1;
>> + set_current_program_space (child_inf->pspace);
>> + clone_program_space (child_inf->pspace, parent_inf->pspace);
>> +
>> + /* Let the shared library layer (solib-svr4) learn about
>
> It's not always solib-svr4, so make that e.g., "e.g., solib-svr4" now.

Done.

>
>> + else
>> + {
>> + child_inf->aspace = new_address_space ();
>> + child_inf->pspace = add_program_space (child_inf->aspace);
>> + child_inf->removable = 1;
>> + child_inf->symfile_flags = SYMFILE_NO_READ;
>> + set_current_program_space (child_inf->pspace);
>> + clone_program_space (child_inf->pspace, parent_pspace);
>> +
>> + /* Let the shared library layer (solib-svr4) learn about
>
> Likewise.

Done.

>
> Otherwise this looks good to me.

Pedro, thanks for this review. The updated patch is included below.
I have one remaining question: I took this (above) to mean that the patch
was approved with the requested changes, but since this is part of a series
I'm not sure if I have the go-ahead to push it. The patch is standalone
and I believe it has value independent of the rest of the series.

OK to push?

Thanks
--Don

gdb/
2014-09-26 Don Breazeal <donb@codesourcery.com>

* inf-ptrace.c (inf_ptrace_follow_fork): Remove target-independent
code so as to work with follow_fork_inferior.
* inf-ttrace.c (inf_ttrace_follow_fork): Ditto.
(inf_ttrace_create_inferior): Remove reference to
inf_ttrace_vfork_ppid.
(inf_ttrace_attach): Ditto.
(inf_ttrace_detach): Ditto.
(inf_ttrace_kill): Use current_inferior instead of
inf_ttrace_vfork_ppid.
(inf_ttrace_wait): Eliminate use of inf_ttrace_vfork_ppid, report
TARGET_WAITKIND_VFORK_DONE event, delete HACK that switched the
inferior away from the parent.
* infrun.c (follow_fork): Call follow_fork_inferior instead of
target_follow_fork.
(follow_fork_inferior): New function.
(follow_inferior_reset_breakpoints): Make function static.
* infrun.h (follow_inferior_reset_breakpoints): Remove declaration.
* linux-nat.c (linux_child_follow_fork): Move target-independent
code to infrun.c:follow_fork_inferior.

---
gdb/inf-ptrace.c | 48 ++---------
gdb/inf-ttrace.c | 179 +++++----------------------------------
gdb/infrun.c | 249
+++++++++++++++++++++++++++++++++++++++++++++++++++++-
gdb/infrun.h | 2 -
gdb/linux-nat.c | 243
++++++-----------------------------------------------
5 files changed, 298 insertions(+), 423 deletions(-)

diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index dd71b3a..6eb8080 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -36,57 +36,21 @@

#ifdef PT_GET_PROCESS_STATE

+/* Target hook for follow_fork. On entry and at return inferior_ptid is
+ the ptid of the followed inferior. */
+
static int
inf_ptrace_follow_fork (struct target_ops *ops, int follow_child,
int detach_fork)
{
- pid_t pid, fpid;
- ptrace_state_t pe;
-
- pid = ptid_get_pid (inferior_ptid);
-
- if (ptrace (PT_GET_PROCESS_STATE, pid,
- (PTRACE_TYPE_ARG3)&pe, sizeof pe) == -1)
- perror_with_name (("ptrace"));
-
- gdb_assert (pe.pe_report_event == PTRACE_FORK);
- fpid = pe.pe_other_pid;
-
- if (follow_child)
+ if (!follow_child)
{
- struct inferior *parent_inf, *child_inf;
- struct thread_info *tp;
-
- parent_inf = find_inferior_pid (pid);
-
- /* Add the child. */
- child_inf = add_inferior (fpid);
- child_inf->attach_flag = parent_inf->attach_flag;
- copy_terminal_info (child_inf, parent_inf);
- child_inf->pspace = parent_inf->pspace;
- child_inf->aspace = parent_inf->aspace;
+ pid_t child_pid = inferior_thread->pending_follow.value.related_pid;

- /* Before detaching from the parent, remove all breakpoints from
- it. */
- remove_breakpoints ();
-
- if (ptrace (PT_DETACH, pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
- perror_with_name (("ptrace"));
-
- /* Switch inferior_ptid out of the parent's way. */
- inferior_ptid = pid_to_ptid (fpid);
-
- /* Delete the parent. */
- detach_inferior (pid);
-
- add_thread_silent (inferior_ptid);
- }
- else
- {
/* Breakpoints have already been detached from the child by
infrun.c. */

- if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1)
+ if (ptrace (PT_DETACH, child_pid, (PTRACE_TYPE_ARG3)1, 0) == -1)
perror_with_name (("ptrace"));
}

diff --git a/gdb/inf-ttrace.c b/gdb/inf-ttrace.c
index 847beb3..dceea42 100644
--- a/gdb/inf-ttrace.c
+++ b/gdb/inf-ttrace.c
@@ -403,128 +403,18 @@ inf_ttrace_stopped_by_watchpoint (struct
target_ops *ops)
}
\f

-/* When tracking a vfork(2), we cannot detach from the parent until
- after the child has called exec(3) or has exited. If we are still
- attached to the parent, this variable will be set to the process ID
- of the parent. Otherwise it will be set to zero. */
-static pid_t inf_ttrace_vfork_ppid = -1;
+/* Target hook for follow_fork. On entry and at return inferior_ptid
+ is the ptid of the followed inferior. */

static int
inf_ttrace_follow_fork (struct target_ops *ops, int follow_child,
int detach_fork)
{
- pid_t pid, fpid;
- lwpid_t lwpid, flwpid;
- ttstate_t tts;
struct thread_info *tp = inferior_thread ();

gdb_assert (tp->pending_follow.kind == TARGET_WAITKIND_FORKED
|| tp->pending_follow.kind == TARGET_WAITKIND_VFORKED);

- pid = ptid_get_pid (inferior_ptid);
- lwpid = ptid_get_lwp (inferior_ptid);
-
- /* Get all important details that core GDB doesn't (and shouldn't)
- know about. */
- if (ttrace (TT_LWP_GET_STATE, pid, lwpid,
- (uintptr_t)&tts, sizeof tts, 0) == -1)
- perror_with_name (("ttrace"));
-
- gdb_assert (tts.tts_event == TTEVT_FORK || tts.tts_event == TTEVT_VFORK);
-
- if (tts.tts_u.tts_fork.tts_isparent)
- {
- pid = tts.tts_pid;
- lwpid = tts.tts_lwpid;
- fpid = tts.tts_u.tts_fork.tts_fpid;
- flwpid = tts.tts_u.tts_fork.tts_flwpid;
- }
- else
- {
- pid = tts.tts_u.tts_fork.tts_fpid;
- lwpid = tts.tts_u.tts_fork.tts_flwpid;
- fpid = tts.tts_pid;
- flwpid = tts.tts_lwpid;
- }
-
- if (follow_child)
- {
- struct inferior *inf;
- struct inferior *parent_inf;
-
- parent_inf = find_inferior_pid (pid);
-
- inferior_ptid = ptid_build (fpid, flwpid, 0);
- inf = add_inferior (fpid);
- inf->attach_flag = parent_inf->attach_flag;
- inf->pspace = parent_inf->pspace;
- inf->aspace = parent_inf->aspace;
- copy_terminal_info (inf, parent_inf);
- detach_breakpoints (ptid_build (pid, lwpid, 0));
-
- target_terminal_ours ();
- fprintf_unfiltered (gdb_stdlog,
- _("Attaching after fork to child process %ld.\n"),
- (long)fpid);
- }
- else
- {
- inferior_ptid = ptid_build (pid, lwpid, 0);
- /* Detach any remaining breakpoints in the child. In the case
- of fork events, we do not need to do this, because breakpoints
- should have already been removed earlier. */
- if (tts.tts_event == TTEVT_VFORK)
- detach_breakpoints (ptid_build (fpid, flwpid, 0));
-
- target_terminal_ours ();
- fprintf_unfiltered (gdb_stdlog,
- _("Detaching after fork from child process %ld.\n"),
- (long)fpid);
- }
-
- if (tts.tts_event == TTEVT_VFORK)
- {
- gdb_assert (!tts.tts_u.tts_fork.tts_isparent);
-
- if (follow_child)
- {
- /* We can't detach from the parent yet. */
- inf_ttrace_vfork_ppid = pid;
-
- reattach_breakpoints (fpid);
- }
- else
- {
- if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1)
- perror_with_name (("ttrace"));
-
- /* Wait till we get the TTEVT_VFORK event in the parent.
- This indicates that the child has called exec(3) or has
- exited and that the parent is ready to be traced again. */
- if (ttrace_wait (pid, lwpid, TTRACE_WAITOK, &tts, sizeof tts) == -1)
- perror_with_name (("ttrace_wait"));
- gdb_assert (tts.tts_event == TTEVT_VFORK);
- gdb_assert (tts.tts_u.tts_fork.tts_isparent);
-
- reattach_breakpoints (pid);
- }
- }
- else
- {
- gdb_assert (tts.tts_u.tts_fork.tts_isparent);
-
- if (follow_child)
- {
- if (ttrace (TT_PROC_DETACH, pid, 0, 0, 0, 0) == -1)
- perror_with_name (("ttrace"));
- }
- else
- {
- if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1)
- perror_with_name (("ttrace"));
- }
- }
-
if (follow_child)
{
struct thread_info *ti;
@@ -533,17 +423,21 @@ inf_ttrace_follow_fork (struct target_ops *ops,
int follow_child,
inf_ttrace_num_lwps = 1;
inf_ttrace_num_lwps_in_syscall = 0;

- /* Delete parent. */
- delete_thread_silent (ptid_build (pid, lwpid, 0));
- detach_inferior (pid);
-
- /* Add child thread. inferior_ptid was already set above. */
- ti = add_thread_silent (inferior_ptid);
+ ti = inferior_thread ();
ti->private =
xmalloc (sizeof (struct inf_ttrace_private_thread_info));
memset (ti->private, 0,
sizeof (struct inf_ttrace_private_thread_info));
}
+ else
+ {
+ pid_t child_pid;
+
+ /* Following parent. Detach child now. */
+ child_pid = ptid_get_pid (tp->pending_follow.value.related_pid);
+ if (ttrace (TT_PROC_DETACH, child_pid, 0, 0, 0, 0) == -1)
+ perror_with_name (("ttrace"));
+ }

return 0;
}
@@ -661,7 +555,6 @@ inf_ttrace_create_inferior (struct target_ops *ops,
char *exec_file,
gdb_assert (inf_ttrace_num_lwps_in_syscall == 0);
gdb_assert (inf_ttrace_page_dict.count == 0);
gdb_assert (inf_ttrace_reenable_page_protections == 0);
- gdb_assert (inf_ttrace_vfork_ppid == -1);

pid = fork_inferior (exec_file, allargs, env, inf_ttrace_me, NULL,
inf_ttrace_prepare, NULL, NULL);
@@ -772,7 +665,6 @@ inf_ttrace_attach (struct target_ops *ops, const
char *args, int from_tty)

gdb_assert (inf_ttrace_num_lwps == 0);
gdb_assert (inf_ttrace_num_lwps_in_syscall == 0);
- gdb_assert (inf_ttrace_vfork_ppid == -1);

if (ttrace (TT_PROC_ATTACH, pid, 0, TT_KILL_ON_EXIT, TT_VERSION, 0)
== -1)
perror_with_name (("ttrace"));
@@ -822,13 +714,6 @@ inf_ttrace_detach (struct target_ops *ops, const
char *args, int from_tty)
if (ttrace (TT_PROC_DETACH, pid, 0, 0, sig, 0) == -1)
perror_with_name (("ttrace"));

- if (inf_ttrace_vfork_ppid != -1)
- {
- if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
- perror_with_name (("ttrace"));
- inf_ttrace_vfork_ppid = -1;
- }
-
inf_ttrace_num_lwps = 0;
inf_ttrace_num_lwps_in_syscall = 0;

@@ -850,13 +735,6 @@ inf_ttrace_kill (struct target_ops *ops)
perror_with_name (("ttrace"));
/* ??? Is it necessary to call ttrace_wait() here? */

- if (inf_ttrace_vfork_ppid != -1)
- {
- if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
- perror_with_name (("ttrace"));
- inf_ttrace_vfork_ppid = -1;
- }
-
target_mourn_inferior ();
}

@@ -967,20 +845,6 @@ inf_ttrace_wait (struct target_ops *ops,
if (ttrace_wait (pid, lwpid, TTRACE_WAITOK, &tts, sizeof tts) == -1)
perror_with_name (("ttrace_wait"));

- if (tts.tts_event == TTEVT_VFORK && tts.tts_u.tts_fork.tts_isparent)
- {
- if (inf_ttrace_vfork_ppid != -1)
- {
- gdb_assert (inf_ttrace_vfork_ppid == tts.tts_pid);
-
- if (ttrace (TT_PROC_DETACH, tts.tts_pid, 0, 0, 0, 0) == -1)
- perror_with_name (("ttrace"));
- inf_ttrace_vfork_ppid = -1;
- }
-
- tts.tts_event = TTEVT_NONE;
- }
-
clear_sigint_trap ();
}
while (tts.tts_event == TTEVT_NONE);
@@ -1075,17 +939,16 @@ inf_ttrace_wait (struct target_ops *ops,
break;

case TTEVT_VFORK:
- gdb_assert (!tts.tts_u.tts_fork.tts_isparent);
-
- related_ptid = ptid_build (tts.tts_u.tts_fork.tts_fpid,
- tts.tts_u.tts_fork.tts_flwpid, 0);
-
- ourstatus->kind = TARGET_WAITKIND_VFORKED;
- ourstatus->value.related_pid = related_ptid;
+ if (tts.tts_u.tts_fork.tts_isparent)
+ ourstatus->kind = TARGET_WAITKIND_VFORK_DONE;
+ else
+ {
+ related_ptid = ptid_build (tts.tts_u.tts_fork.tts_fpid,
+ tts.tts_u.tts_fork.tts_flwpid, 0);

- /* HACK: To avoid touching the parent during the vfork, switch
- away from it. */
- inferior_ptid = ptid;
+ ourstatus->kind = TARGET_WAITKIND_VFORKED;
+ ourstatus->value.related_pid = related_ptid;
+ }
break;

case TTEVT_LWP_CREATE:
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 5e123be..e8b1f01 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -60,6 +60,7 @@
#include "completer.h"
#include "target-descriptions.h"
#include "target-dcache.h"
+#include "terminal.h"

/* Prototypes for local functions */

@@ -79,6 +80,10 @@ static int restore_selected_frame (void *);

static int follow_fork (void);

+static int follow_fork_inferior (int follow_child, int detach_fork);
+
+static void follow_inferior_reset_breakpoints (void);
+
static void set_schedlock_func (char *args, int from_tty,
struct cmd_list_element *c);

@@ -486,9 +491,10 @@ follow_fork (void)
parent = inferior_ptid;
child = tp->pending_follow.value.related_pid;

- /* Tell the target to do whatever is necessary to follow
- either parent or child. */
- if (target_follow_fork (follow_child, detach_fork))
+ /* Set up inferior(s) as specified by the caller, and tell the
+ target to do whatever is necessary to follow either parent
+ or child. */
+ if (follow_fork_inferior (follow_child, detach_fork))
{
/* Target refused to follow, or there's some other reason
we shouldn't resume. */
@@ -560,7 +566,242 @@ follow_fork (void)
return should_resume;
}

-void
+/* Handle changes to the inferior list based on the type of fork,
+ which process is being followed, and whether the other process
+ should be detached. On entry inferior_ptid must be the ptid of
+ the fork parent. At return inferior_ptid is the ptid of the
+ followed inferior. */
+
+int
+follow_fork_inferior (int follow_child, int detach_fork)
+{
+ int has_vforked;
+ int parent_pid, child_pid;
+
+ has_vforked = (inferior_thread ()->pending_follow.kind
+ == TARGET_WAITKIND_VFORKED);
+ parent_pid = ptid_get_lwp (inferior_ptid);
+ if (parent_pid == 0)
+ parent_pid = ptid_get_pid (inferior_ptid);
+ child_pid
+ = ptid_get_pid (inferior_thread ()->pending_follow.value.related_pid);
+
+ if (has_vforked
+ && !non_stop /* Non-stop always resumes both branches. */
+ && (!target_is_async_p () || sync_execution)
+ && !(follow_child || detach_fork || sched_multi))
+ {
+ /* The parent stays blocked inside the vfork syscall until the
+ child execs or exits. If we don't let the child run, then
+ the parent stays blocked. If we're telling the parent to run
+ in the foreground, the user will not be able to ctrl-c to get
+ back the terminal, effectively hanging the debug session. */
+ fprintf_filtered (gdb_stderr, _("\
+Can not resume the parent process over vfork in the foreground while\n\
+holding the child stopped. Try \"set detach-on-fork\" or \
+\"set schedule-multiple\".\n"));
+ /* FIXME output string > 80 columns. */
+ return 1;
+ }
+
+ if (!follow_child)
+ {
+ /* Detach new forked process? */
+ if (detach_fork)
+ {
+ struct cleanup *old_chain;
+
+ /* Before detaching from the child, remove all breakpoints
+ from it. If we forked, then this has already been taken
+ care of by infrun.c. If we vforked however, any
+ breakpoint inserted in the parent is visible in the
+ child, even those added while stopped in a vfork
+ catchpoint. This will remove the breakpoints from the
+ parent also, but they'll be reinserted below. */
+ if (has_vforked)
+ {
+ /* Keep breakpoints list in sync. */
+ remove_breakpoints_pid (ptid_get_pid (inferior_ptid));
+ }
+
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours ();
+ fprintf_filtered (gdb_stdlog,
+ "Detaching after fork from "
+ "child process %d.\n",
+ child_pid);
+ }
+ }
+ else
+ {
+ struct inferior *parent_inf, *child_inf;
+ struct cleanup *old_chain;
+
+ /* Add process to GDB's tables. */
+ child_inf = add_inferior (child_pid);
+
+ parent_inf = current_inferior ();
+ child_inf->attach_flag = parent_inf->attach_flag;
+ copy_terminal_info (child_inf, parent_inf);
+ child_inf->gdbarch = parent_inf->gdbarch;
+ copy_inferior_target_desc_info (child_inf, parent_inf);
+
+ old_chain = save_inferior_ptid ();
+ save_current_program_space ();
+
+ inferior_ptid = ptid_build (child_pid, child_pid, 0);
+ add_thread (inferior_ptid);
+ child_inf->symfile_flags = SYMFILE_NO_READ;
+
+ /* If this is a vfork child, then the address-space is
+ shared with the parent. */
+ if (has_vforked)
+ {
+ child_inf->pspace = parent_inf->pspace;
+ child_inf->aspace = parent_inf->aspace;
+
+ /* The parent will be frozen until the child is done
+ with the shared region. Keep track of the
+ parent. */
+ child_inf->vfork_parent = parent_inf;
+ child_inf->pending_detach = 0;
+ parent_inf->vfork_child = child_inf;
+ parent_inf->pending_detach = 0;
+ }
+ else
+ {
+ child_inf->aspace = new_address_space ();
+ child_inf->pspace = add_program_space (child_inf->aspace);
+ child_inf->removable = 1;
+ set_current_program_space (child_inf->pspace);
+ clone_program_space (child_inf->pspace, parent_inf->pspace);
+
+ /* Let the shared library layer (e.g., solib-svr4) learn
+ about this new process, relocate the cloned exec, pull
+ in shared libraries, and install the solib event
+ breakpoint. If a "cloned-VM" event was propagated
+ better throughout the core, this wouldn't be
+ required. */
+ solib_create_inferior_hook (0);
+ }
+
+ do_cleanups (old_chain);
+ }
+
+ if (has_vforked)
+ {
+ struct inferior *parent_inf;
+
+ parent_inf = current_inferior ();
+
+ /* If we detached from the child, then we have to be careful
+ to not insert breakpoints in the parent until the child
+ is done with the shared memory region. However, if we're
+ staying attached to the child, then we can and should
+ insert breakpoints, so that we can debug it. A
+ subsequent child exec or exit is enough to know when does
+ the child stops using the parent's address space. */
+ parent_inf->waiting_for_vfork_done = detach_fork;
+ parent_inf->pspace->breakpoints_not_allowed = detach_fork;
+ }
+ }
+ else
+ {
+ /* Follow the child. */
+ struct inferior *parent_inf, *child_inf;
+ struct program_space *parent_pspace;
+
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours ();
+ if (has_vforked)
+ fprintf_filtered (gdb_stdlog,
+ _("Attaching after process %d "
+ "vfork to child process %d.\n"),
+ parent_pid, child_pid);
+ else
+ fprintf_filtered (gdb_stdlog,
+ _("Attaching after process %d "
+ "fork to child process %d.\n"),
+ parent_pid, child_pid);
+ }
+
+ /* Add the new inferior first, so that the target_detach below
+ doesn't unpush the target. */
+
+ child_inf = add_inferior (child_pid);
+
+ parent_inf = current_inferior ();
+ child_inf->attach_flag = parent_inf->attach_flag;
+ copy_terminal_info (child_inf, parent_inf);
+ child_inf->gdbarch = parent_inf->gdbarch;
+ copy_inferior_target_desc_info (child_inf, parent_inf);
+
+ parent_pspace = parent_inf->pspace;
+
+ /* If we're vforking, we want to hold on to the parent until the
+ child exits or execs. At child exec or exit time we can
+ remove the old breakpoints from the parent and detach or
+ resume debugging it. Otherwise, detach the parent now; we'll
+ want to reuse it's program/address spaces, but we can't set
+ them to the child before removing breakpoints from the
+ parent, otherwise, the breakpoints module could decide to
+ remove breakpoints from the wrong process (since they'd be
+ assigned to the same address space). */
+
+ if (has_vforked)
+ {
+ gdb_assert (child_inf->vfork_parent == NULL);
+ gdb_assert (parent_inf->vfork_child == NULL);
+ child_inf->vfork_parent = parent_inf;
+ child_inf->pending_detach = 0;
+ parent_inf->vfork_child = child_inf;
+ parent_inf->pending_detach = detach_fork;
+ parent_inf->waiting_for_vfork_done = 0;
+ }
+ else if (detach_fork)
+ target_detach (NULL, 0);
+
+ /* Note that the detach above makes PARENT_INF dangling. */
+
+ /* Add the child thread to the appropriate lists, and switch to
+ this new thread, before cloning the program space, and
+ informing the solib layer about this new process. */
+
+ inferior_ptid = ptid_build (child_pid, child_pid, 0);
+ add_thread (inferior_ptid);
+
+ /* If this is a vfork child, then the address-space is shared
+ with the parent. If we detached from the parent, then we can
+ reuse the parent's program/address spaces. */
+ if (has_vforked || detach_fork)
+ {
+ child_inf->pspace = parent_pspace;
+ child_inf->aspace = child_inf->pspace->aspace;
+ }
+ else
+ {
+ child_inf->aspace = new_address_space ();
+ child_inf->pspace = add_program_space (child_inf->aspace);
+ child_inf->removable = 1;
+ child_inf->symfile_flags = SYMFILE_NO_READ;
+ set_current_program_space (child_inf->pspace);
+ clone_program_space (child_inf->pspace, parent_pspace);
+
+ /* Let the shared library layer (e.g., solib-svr4) learn
+ about this new process, relocate the cloned exec, pull in
+ shared libraries, and install the solib event breakpoint.
+ If a "cloned-VM" event was propagated better throughout
+ the core, this wouldn't be required. */
+ solib_create_inferior_hook (0);
+ }
+ }
+
+ return target_follow_fork (follow_child, detach_fork);
+}
+
+static void
follow_inferior_reset_breakpoints (void)
{
struct thread_info *tp = inferior_thread ();
diff --git a/gdb/infrun.h b/gdb/infrun.h
index cc9cb33..fb6276b 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -115,8 +115,6 @@ extern void insert_step_resume_breakpoint_at_sal
(struct gdbarch *,
struct symtab_and_line ,
struct frame_id);

-extern void follow_inferior_reset_breakpoints (void);
-
/* Returns true if we're trying to step past the instruction at
ADDRESS in ASPACE. */
extern int stepping_past_instruction_at (struct address_space *aspace,
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 0fe4b0b..df830ae 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -54,7 +54,6 @@
#include <sys/types.h>
#include <dirent.h>
#include "xml-support.h"
-#include "terminal.h"
#include <sys/vfs.h>
#include "solib.h"
#include "nat/linux-osdata.h"
@@ -369,79 +368,41 @@ delete_lwp_cleanup (void *lp_voidp)
delete_lwp (lp->ptid);
}

+/* Target hook for follow_fork. On entry inferior_ptid must be the
+ ptid of the followed inferior. At return, inferior_ptid will be
+ unchanged. */
+
static int
linux_child_follow_fork (struct target_ops *ops, int follow_child,
int detach_fork)
{
- int has_vforked;
- int parent_pid, child_pid;
-
- has_vforked = (inferior_thread ()->pending_follow.kind
- == TARGET_WAITKIND_VFORKED);
- parent_pid = ptid_get_lwp (inferior_ptid);
- if (parent_pid == 0)
- parent_pid = ptid_get_pid (inferior_ptid);
- child_pid
- = ptid_get_pid (inferior_thread ()->pending_follow.value.related_pid);
-
- if (has_vforked
- && !non_stop /* Non-stop always resumes both branches. */
- && (!target_is_async_p () || sync_execution)
- && !(follow_child || detach_fork || sched_multi))
- {
- /* The parent stays blocked inside the vfork syscall until the
- child execs or exits. If we don't let the child run, then
- the parent stays blocked. If we're telling the parent to run
- in the foreground, the user will not be able to ctrl-c to get
- back the terminal, effectively hanging the debug session. */
- fprintf_filtered (gdb_stderr, _("\
-Can not resume the parent process over vfork in the foreground while\n\
-holding the child stopped. Try \"set detach-on-fork\" or \
-\"set schedule-multiple\".\n"));
- /* FIXME output string > 80 columns. */
- return 1;
- }
-
- if (! follow_child)
+ if (!follow_child)
{
struct lwp_info *child_lp = NULL;
+ int status = W_STOPCODE (0);
+ struct cleanup *old_chain;
+ int has_vforked;
+ int parent_pid, child_pid;
+
+ has_vforked = (inferior_thread ()->pending_follow.kind
+ == TARGET_WAITKIND_VFORKED);
+ parent_pid = ptid_get_lwp (inferior_ptid);
+ if (parent_pid == 0)
+ parent_pid = ptid_get_pid (inferior_ptid);
+ child_pid
+ = ptid_get_pid (inferior_thread ()->pending_follow.value.related_pid);
+

/* We're already attached to the parent, by default. */
+ old_chain = save_inferior_ptid ();
+ inferior_ptid = ptid_build (child_pid, child_pid, 0);
+ child_lp = add_lwp (inferior_ptid);
+ child_lp->stopped = 1;
+ child_lp->last_resume_kind = resume_stop;

/* Detach new forked process? */
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
- care of by infrun.c. If we vforked however, any
- breakpoint inserted in the parent is visible in the
- child, even those added while stopped in a vfork
- catchpoint. This will remove the breakpoints from the
- parent also, but they'll be reinserted below. */
- if (has_vforked)
- {
- /* keep breakpoints list in sync. */
- remove_breakpoints_pid (ptid_get_pid (inferior_ptid));
- }
-
- if (info_verbose || debug_linux_nat)
- {
- target_terminal_ours ();
- fprintf_filtered (gdb_stdlog,
- "Detaching after fork from "
- "child process %d.\n",
- child_pid);
- }
-
- old_chain = save_inferior_ptid ();
- inferior_ptid = ptid_build (child_pid, child_pid, 0);
-
- child_lp = add_lwp (inferior_ptid);
- child_lp->stopped = 1;
- child_lp->last_resume_kind = resume_stop;
make_cleanup (delete_lwp_cleanup, child_lp);

if (linux_nat_prepare_to_resume != NULL)
@@ -476,86 +437,20 @@ holding the child stopped. Try \"set
detach-on-fork\" or \
ptrace (PTRACE_DETACH, child_pid, 0, signo);
}

+ /* Resets value of inferior_ptid to parent ptid. */
do_cleanups (old_chain);
}
else
{
- struct inferior *parent_inf, *child_inf;
- struct cleanup *old_chain;
-
- /* Add process to GDB's tables. */
- child_inf = add_inferior (child_pid);
-
- parent_inf = current_inferior ();
- child_inf->attach_flag = parent_inf->attach_flag;
- copy_terminal_info (child_inf, parent_inf);
- child_inf->gdbarch = parent_inf->gdbarch;
- copy_inferior_target_desc_info (child_inf, parent_inf);
-
- old_chain = save_inferior_ptid ();
- save_current_program_space ();
-
- inferior_ptid = ptid_build (child_pid, child_pid, 0);
- add_thread (inferior_ptid);
- child_lp = add_lwp (inferior_ptid);
- child_lp->stopped = 1;
- child_lp->last_resume_kind = resume_stop;
- child_inf->symfile_flags = SYMFILE_NO_READ;
-
- /* If this is a vfork child, then the address-space is
- shared with the parent. */
- if (has_vforked)
- {
- child_inf->pspace = parent_inf->pspace;
- child_inf->aspace = parent_inf->aspace;
-
- /* The parent will be frozen until the child is done
- with the shared region. Keep track of the
- parent. */
- child_inf->vfork_parent = parent_inf;
- child_inf->pending_detach = 0;
- parent_inf->vfork_child = child_inf;
- parent_inf->pending_detach = 0;
- }
- else
- {
- child_inf->aspace = new_address_space ();
- child_inf->pspace = add_program_space (child_inf->aspace);
- child_inf->removable = 1;
- set_current_program_space (child_inf->pspace);
- clone_program_space (child_inf->pspace, parent_inf->pspace);
-
- /* Let the shared library layer (solib-svr4) learn about
- this new process, relocate the cloned exec, pull in
- shared libraries, and install the solib event
- breakpoint. If a "cloned-VM" event was propagated
- better throughout the core, this wouldn't be
- required. */
- solib_create_inferior_hook (0);
- }
-
/* Let the thread_db layer learn about this new process. */
check_for_thread_db ();
-
- do_cleanups (old_chain);
}

+ do_cleanups (old_chain);
+
if (has_vforked)
{
struct lwp_info *parent_lp;
- struct inferior *parent_inf;
-
- parent_inf = current_inferior ();
-
- /* If we detached from the child, then we have to be careful
- to not insert breakpoints in the parent until the child
- is done with the shared memory region. However, if we're
- staying attached to the child, then we can and should
- insert breakpoints, so that we can debug it. A
- subsequent child exec or exit is enough to know when does
- the child stops using the parent's address space. */
- parent_inf->waiting_for_vfork_done = detach_fork;
- parent_inf->pspace->breakpoints_not_allowed = detach_fork;

parent_lp = find_lwp_pid (pid_to_ptid (parent_pid));
gdb_assert (linux_supports_tracefork () >= 0);
@@ -628,98 +523,12 @@ holding the child stopped. Try \"set
detach-on-fork\" or \
}
else
{
- struct inferior *parent_inf, *child_inf;
struct lwp_info *child_lp;
- struct program_space *parent_pspace;
-
- if (info_verbose || debug_linux_nat)
- {
- target_terminal_ours ();
- if (has_vforked)
- fprintf_filtered (gdb_stdlog,
- _("Attaching after process %d "
- "vfork to child process %d.\n"),
- parent_pid, child_pid);
- else
- fprintf_filtered (gdb_stdlog,
- _("Attaching after process %d "
- "fork to child process %d.\n"),
- parent_pid, child_pid);
- }
-
- /* Add the new inferior first, so that the target_detach below
- doesn't unpush the target. */
-
- child_inf = add_inferior (child_pid);
-
- parent_inf = current_inferior ();
- child_inf->attach_flag = parent_inf->attach_flag;
- copy_terminal_info (child_inf, parent_inf);
- child_inf->gdbarch = parent_inf->gdbarch;
- copy_inferior_target_desc_info (child_inf, parent_inf);
-
- parent_pspace = parent_inf->pspace;
-
- /* If we're vforking, we want to hold on to the parent until the
- child exits or execs. At child exec or exit time we can
- remove the old breakpoints from the parent and detach or
- resume debugging it. Otherwise, detach the parent now; we'll
- want to reuse it's program/address spaces, but we can't set
- them to the child before removing breakpoints from the
- parent, otherwise, the breakpoints module could decide to
- remove breakpoints from the wrong process (since they'd be
- assigned to the same address space). */

- if (has_vforked)
- {
- gdb_assert (child_inf->vfork_parent == NULL);
- gdb_assert (parent_inf->vfork_child == NULL);
- child_inf->vfork_parent = parent_inf;
- child_inf->pending_detach = 0;
- parent_inf->vfork_child = child_inf;
- parent_inf->pending_detach = detach_fork;
- parent_inf->waiting_for_vfork_done = 0;
- }
- else if (detach_fork)
- target_detach (NULL, 0);
-
- /* Note that the detach above makes PARENT_INF dangling. */
-
- /* Add the child thread to the appropriate lists, and switch to
- this new thread, before cloning the program space, and
- informing the solib layer about this new process. */
-
- inferior_ptid = ptid_build (child_pid, child_pid, 0);
- add_thread (inferior_ptid);
child_lp = add_lwp (inferior_ptid);
child_lp->stopped = 1;
child_lp->last_resume_kind = resume_stop;

- /* If this is a vfork child, then the address-space is shared
- with the parent. If we detached from the parent, then we can
- reuse the parent's program/address spaces. */
- if (has_vforked || detach_fork)
- {
- child_inf->pspace = parent_pspace;
- child_inf->aspace = child_inf->pspace->aspace;
- }
- else
- {
- child_inf->aspace = new_address_space ();
- child_inf->pspace = add_program_space (child_inf->aspace);
- child_inf->removable = 1;
- child_inf->symfile_flags = SYMFILE_NO_READ;
- set_current_program_space (child_inf->pspace);
- clone_program_space (child_inf->pspace, parent_pspace);
-
- /* Let the shared library layer (solib-svr4) learn about
- this new process, relocate the cloned exec, pull in
- shared libraries, and install the solib event breakpoint.
- If a "cloned-VM" event was propagated better throughout
- the core, this wouldn't be required. */
- solib_create_inferior_hook (0);
- }
-
/* Let the thread_db layer learn about this new process. */
check_for_thread_db ();
}
--
1.7.0.4