Refactor native follow-fork.

This patch reorganizes the code that implements follow-fork and
detach-on-fork in preparation for implementation of those features for the
extended-remote target.  The function linux-nat.c:linux_child_follow_fork
contained target-independent code mixed in with target-dependent code.  The
target-independent pieces need to be accessible for the host-side
implementation of follow-fork for extended-remote Linux targets.

The changes are fairly mechanical.  A new routine, follow_fork_inferior,
is implemented in infrun.c, containing those parts of
linux_child_follow_fork that manage inferiors and the inferior list.  The
parts of linux_child_follow_fork that deal with LWPs and target-specifics
were left in-place.  Although the order of some operations was changed, the
resulting functionality was not.

Modifications were made to the other native target follow-fork functions,
inf_ttrace_follow_fork and inf_ptrace_follow_fork, that should allow them
to work with follow_fork_inferior.  Some other adjustments were necessary
in inf-ttrace.c.  The changes to inf-ttrace.c and inf-ptrace.c were not
tested.

gdb/ChangeLog:

	* 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.
This commit is contained in:
Don Breazeal 2014-09-30 11:01:57 -07:00
parent 63b434a437
commit d83ad864a2
6 changed files with 320 additions and 423 deletions

View File

@ -1,3 +1,25 @@
2014-09-30 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.
2014-09-30 Andreas Arnez <arnez@linux.vnet.ibm.com>
* gdbarch.sh (regset_from_core_section): Remove gdbarch method.

View File

@ -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;
pid_t child_pid = inferior_thread->pending_follow.value.related_pid;
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;
/* 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"));
}

View File

@ -403,128 +403,18 @@ inf_ttrace_stopped_by_watchpoint (struct target_ops *ops)
}
/* 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);
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);
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;
/* 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:

View File

@ -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);
@ -396,6 +401,241 @@ show_follow_fork_mode_string (struct ui_file *file, int from_tty,
}
/* 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. */
static 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);
}
/* Tell the target to follow the fork we're stopped at. Returns true
if the inferior should be resumed; false, if the target for some
reason decided it's best not to resume. */
@ -486,9 +726,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 +801,7 @@ follow_fork (void)
return should_resume;
}
void
static void
follow_inferior_reset_breakpoints (void)
{
struct thread_info *tp = inferior_thread ();

View File

@ -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,

View File

@ -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 ();
}