aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
authorDon Breazeal <donb@codesourcery.com>2014-09-30 11:01:57 -0700
committerDon Breazeal <donb@codesourcery.com>2014-09-30 11:01:57 -0700
commitd83ad864a285fe3127e1a98830197e8461ad2745 (patch)
tree17fc116484b87e08c0a992063513490208c3e1ef /gdb
parent63b434a4374b61aa34b095a789cd0d03b2a5a526 (diff)
downloadgdb-d83ad864a285fe3127e1a98830197e8461ad2745.zip
gdb-d83ad864a285fe3127e1a98830197e8461ad2745.tar.gz
gdb-d83ad864a285fe3127e1a98830197e8461ad2745.tar.bz2
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.
Diffstat (limited to 'gdb')
-rw-r--r--gdb/ChangeLog22
-rw-r--r--gdb/inf-ptrace.c48
-rw-r--r--gdb/inf-ttrace.c179
-rw-r--r--gdb/infrun.c249
-rw-r--r--gdb/infrun.h2
-rw-r--r--gdb/linux-nat.c243
6 files changed, 320 insertions, 423 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index f328214..c967a93 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -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.
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)
}
-/* 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..728c160 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);
@@ -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 ();
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 7f0f4f8..60f6a84 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 ();
}