diff options
author | Don Breazeal <donb@codesourcery.com> | 2014-09-30 11:01:57 -0700 |
---|---|---|
committer | Don Breazeal <donb@codesourcery.com> | 2014-09-30 11:01:57 -0700 |
commit | d83ad864a285fe3127e1a98830197e8461ad2745 (patch) | |
tree | 17fc116484b87e08c0a992063513490208c3e1ef /gdb/infrun.c | |
parent | 63b434a4374b61aa34b095a789cd0d03b2a5a526 (diff) | |
download | gdb-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/infrun.c')
-rw-r--r-- | gdb/infrun.c | 249 |
1 files changed, 245 insertions, 4 deletions
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 (); |