mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-09 04:21:49 +08:00
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:
parent
63b434a437
commit
d83ad864a2
@ -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.
|
||||
|
@ -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"));
|
||||
}
|
||||
|
||||
|
179
gdb/inf-ttrace.c
179
gdb/inf-ttrace.c
@ -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:
|
||||
|
249
gdb/infrun.c
249
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);
|
||||
|
||||
@ -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 ();
|
||||
|
@ -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,
|
||||
|
243
gdb/linux-nat.c
243
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 ();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user