From patchwork Sat Dec 6 00:31:03 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Don Breazeal X-Patchwork-Id: 4099 Received: (qmail 20127 invoked by alias); 6 Dec 2014 00:31:11 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 20117 invoked by uid 89); 6 Dec 2014 00:31:11 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-1.8 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.2 X-HELO: relay1.mentorg.com Received: from relay1.mentorg.com (HELO relay1.mentorg.com) (192.94.38.131) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 06 Dec 2014 00:31:08 +0000 Received: from svr-orw-fem-06.mgc.mentorg.com ([147.34.97.120]) by relay1.mentorg.com with esmtp id 1Xx3H6-0002oX-Q0 from Don_Breazeal@mentor.com ; Fri, 05 Dec 2014 16:31:04 -0800 Received: from [172.30.1.174] (147.34.91.1) by svr-orw-fem-06.mgc.mentorg.com (147.34.97.120) with Microsoft SMTP Server (TLS) id 14.3.181.6; Fri, 5 Dec 2014 16:31:03 -0800 Message-ID: <54824E47.7020907@codesourcery.com> Date: Fri, 5 Dec 2014 16:31:03 -0800 From: "Breazeal, Don" User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Thunderbird/31.3.0 MIME-Version: 1.0 To: Pedro Alves , Subject: Re: [PATCH 06/16 v3] Extended-remote Linux follow fork References: <1408580964-27916-1-git-send-email-donb@codesourcery.com> <1414798134-11536-4-git-send-email-donb@codesourcery.com> <5464AB62.5040100@redhat.com> <5464FE11.1080001@codesourcery.com> <5464FF7D.6060103@redhat.com> <5465012C.9060708@codesourcery.com> In-Reply-To: <5465012C.9060708@codesourcery.com> X-IsSubscribed: yes On 11/13/2014 11:06 AM, Breazeal, Don wrote: > On 11/13/2014 10:59 AM, Pedro Alves wrote: >> On 11/13/2014 06:53 PM, Breazeal, Don wrote: >>> My initial approach was to do just that, but I ended up with >>> linux-specific code in remote.c (the code that lives in linux-nat.c >>> for the native implementation). I guess the direction of recent >>> changes would be to put that code into a common file in gdb/nat, >>> if possible. Would that be the approach you would recommend? >> >> I'm not seeing what would be linux-specific? On remote_follow_fork >> fork, we switch the current remote thread to gdb's current >> thread (either parent or child), by >> calling 'set_general_thread (inferior_ptid);' >> And then if we need to detach parent or child, we detach it with >> the D;PID packet. >> >> Thanks, >> Pedro Alves >> > I don't recall the details at this point. I'll proceed > with your recommendation, assuming I don't need any > common code, and if I run into a problem I'll post a > question about it. > > Thanks > --Don > > Hi Pedro, I have finally had a chance to spend a chunk of time on this. I have removed the vFollowFork packet and enabled fork events for remote as well as extended-remote. When gdbserver detaches an inferior, it only exits if it's not in extended mode *and* there are no more inferiors. This version of the patch is *mostly working*, but should still be considered WIP. I wanted to get it posted to make sure that we are aligned on the direction that the implementation is taking before going too much farther with it. BTW this has made it back to the top of my priority list, so I should be more responsive to reviews for the next month or so. One WIP issue: extended-remote targets are working as well as before, but there are still a few failures with 'target remote': an assertion in multi-forks.exp and a few failures in gdb.threads/linux-dp.exp and gdb.trace/report.exp. Another WIP issue: I have a question about how to deal with the workaround for the hardware single-step kernel bug, as in linux-nat.c:linux_child_follow_fork where it manually steps the inferior before detaching. In the remote implementation I put the workaround in a function linux-low.c:linux_detach_fork and called it from linux_detach_one_lwp. I experimented with keeping some state in the thread_info structure flagging the "pending follow", but since gdbserver doesn't know which inferiors are being followed, there was no way to clean up the threads that weren't detached. (I hope that makes sense). In the current implementation it just executes linux_detach_fork for all detach requests, whether they have anything to do with a fork or not. That seems to work, but one might consider it a questionable solution. Another option could be to just not work around the kernel bug in the remote case. It looks like the kernel fix may be as old as December of 2009 -- is that old enough so that for a new feature we can just skip it? Or are there other options I should be considering? The revised patch is below, including an updated commit message. Thanks! --Don This patch implements basic support for follow-fork and detach-on-fork on remote and extended-remote Linux targets. Only 'fork' is supported in this patch; 'vfork' support is added n a subsequent patch. Sufficient extended-remote functionality has been implemented here to pass gdb.base/foll-fork.exp with the catchpoint tests commented out. The implementation follows the same general structure as for the native implementation as much as possible. This implementation included: * enabling fork events in linux-low.c in initialize_low and linux_enable_extended_features * handling fork events in gdbserver/linux-low.c:handle_extended_wait - when a fork event occurs in gdbserver, we must do the full creation of the new process, thread, lwp, and breakpoint lists. This is required whether or not the new child is destined to be detached-on-fork, because GDB will make target calls that require all the structures. In particular we need the breakpoint lists in order to remove the breakpoints from a detaching child. If we are not detaching the child we will need all these structures anyway. - as part of this event handling we store the target_waitstatus in a new member of the parent thread_info structure, 'pending_follow'. This is used to store extended event information for reporting to GDB. - handle_extended_wait is given a return value, denoting whether the handled event should be reported to GDB. Previously it had only handled clone events, which were never reported. * using a new predicate in gdbserver to control handling of the fork event (and eventually all extended events) in linux_wait_1. The predicate, extended_event_reported, checks a target_waitstatus.kind for an extended ptrace event. * adds a function linux_detach_fork to the gdbserver detach procedure. This function implements the workaround for the kernel bug related to inheritance of hardware singlestep settings. * implementing a new RSP 'T' Stop Reply Packet stop reason: "fork", in gdbserver/remote-utils.c and remote.c. * implementing new target and RSP support for target_follow_fork with target extended-remote. (The RSP components were actually defined in patch 4, but they see their first use here). - remote target routine remote_follow_fork, which just sends the 'D;pid' detach packet to detach the new fork child cleanly. We can't just call target_detach because the fork child data structures have not been allocated on the host side. Tested on x64 Ubuntu Lucid, native, remote, extended-remote. gdb/gdbserver/ 2014-12-05 Don Breazeal * gdbthread.h (struct thread_info) : New member. * linux-low.c (handle_extended_wait): Implement return value, rename event_child to event_lwp, handle PTRACE_EVENT_FORK. (linux_detach_fork): New function. (linux_detach_one_lwp): Call linux_detach_fork. (linux_low_filter_event): Use return value from handle_extended_wait. (extended_event_reported): New function. (linux_write_memory): Add 'process ' to debug message. * remote-utils.c (prepare_resume_reply): Implement stop reason 'fork' for 'T' stop message. * server.c (process_serial_event): Don't exit if there is still one inferior. gdb/ 2014-12-05 Don Breazeal * remote.c (remote_fork_event_p): Move out of #ifdef. (remote_follow_fork): New function. (set_general_process): Remove extended-mode restriction. (remote_detach_1): Add struct target_ops argument, don't mourn detached process if it was detach-on-fork. (remote_detach, extended_remote_detach): Pass struct target_ops argument to remote_detach_1. (remote_parse_stop_reply): Handle new 'T' stop reason 'fork'. (extended_remote_kill): Remove extended-mode restriction. (remote_pid_to_str): Print 'process' strings for pid/0/0 ptids. (remote_supports_multi_process): Remove extended-mode restriction. --- gdb/gdbserver/gdbthread.h | 4 + gdb/gdbserver/linux-low.c | 172 +++++++++++++++++++++++++++++++++++++++---- gdb/gdbserver/remote-utils.c | 14 +++- gdb/gdbserver/server.c | 7 +- gdb/remote.c | 97 +++++++++++++++++++----- 5 files changed, 259 insertions(+), 35 deletions(-) /* Same as remote_detach, but don't send the "D" packet; just disconnect. */ @@ -5561,7 +5618,8 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event) as a register number. */ if (strncmp (p, "awatch", strlen("awatch")) != 0 - && strncmp (p, "core", strlen ("core") != 0)) + && strncmp (p, "core", strlen ("core") != 0) + && strncmp (p, "fork", strlen ("fork") != 0)) { /* Read the ``P'' register number. */ pnum = strtol (p, &p_temp, 16); @@ -5613,6 +5671,11 @@ Packet: '%s'\n"), p = unpack_varlen_hex (++p1, &c); event->core = c; } + else if (strncmp (p, "fork", p1 - p) == 0) + { + event->ws.value.related_pid = read_ptid (++p1, &p); + event->ws.kind = TARGET_WAITKIND_FORKED; + } else { /* Silently skip unknown optional info. */ @@ -7877,7 +7940,7 @@ extended_remote_kill (struct target_ops *ops) struct remote_state *rs = get_remote_state (); res = remote_vkill (pid, rs); - if (res == -1 && !(rs->extended && remote_multi_process_p (rs))) + if (res == -1 && !(remote_multi_process_p (rs))) { /* Don't try 'k' on a multi-process aware stub -- it has no way to specify the pid. */ @@ -9423,9 +9486,12 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid) { if (ptid_equal (magic_null_ptid, ptid)) xsnprintf (buf, sizeof buf, "Thread
"); - else if (rs->extended && remote_multi_process_p (rs)) - xsnprintf (buf, sizeof buf, "Thread %d.%ld", - ptid_get_pid (ptid), ptid_get_lwp (ptid)); + else if (remote_multi_process_p (rs)) + if (ptid_get_lwp (ptid) == 0) + return normal_pid_to_str (ptid); + else + xsnprintf (buf, sizeof buf, "Thread %d.%ld", + ptid_get_pid (ptid), ptid_get_lwp (ptid)); else xsnprintf (buf, sizeof buf, "Thread %ld", ptid_get_lwp (ptid)); @@ -10422,11 +10488,7 @@ remote_supports_multi_process (struct target_ops *self) { struct remote_state *rs = get_remote_state (); - /* Only extended-remote handles being attached to multiple - processes, even though plain remote can use the multi-process - thread id extensions, so that GDB knows the target process's - PID. */ - return rs->extended && remote_multi_process_p (rs); + return remote_multi_process_p (rs); } static int @@ -11559,6 +11621,7 @@ Specify the serial device it is connected to\n\ remote_ops.to_remove_watchpoint = remote_remove_watchpoint; remote_ops.to_kill = remote_kill; remote_ops.to_load = remote_load; + remote_ops.to_follow_fork = remote_follow_fork; remote_ops.to_mourn_inferior = remote_mourn; remote_ops.to_pass_signals = remote_pass_signals; remote_ops.to_program_signals = remote_program_signals; diff --git a/gdb/gdbserver/gdbthread.h b/gdb/gdbserver/gdbthread.h index 8290ec1..aa77cd9 100644 --- a/gdb/gdbserver/gdbthread.h +++ b/gdb/gdbserver/gdbthread.h @@ -41,6 +41,10 @@ struct thread_info /* True if LAST_STATUS hasn't been reported to GDB yet. */ int status_pending_p; + /* This is used to store fork and exec event information until + it is reported to GDB. */ + struct target_waitstatus pending_follow; + /* Given `while-stepping', a thread may be collecting data for more than one tracepoint simultaneously. E.g.: diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index a13631a..a18e8ee 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -20,6 +20,7 @@ #include "linux-low.h" #include "nat/linux-osdata.h" #include "agent.h" +#include "tdesc.h" #include "nat/linux-nat.h" #include "nat/linux-waitpid.h" @@ -364,22 +365,23 @@ linux_add_process (int pid, int attached) } /* Handle a GNU/Linux extended wait response. If we see a clone - event, we need to add the new LWP to our list (and not report the - trap to higher layers). */ + event, we need to add the new LWP to our list (and return 0 so as + not to report the trap to higher layers). */ -static void -handle_extended_wait (struct lwp_info *event_child, int wstat) +static int +handle_extended_wait (struct lwp_info *event_lwp, int wstat) { int event = linux_ptrace_get_extended_event (wstat); - struct thread_info *event_thr = get_lwp_thread (event_child); + struct thread_info *event_thr = get_lwp_thread (event_lwp); struct lwp_info *new_lwp; - if (event == PTRACE_EVENT_CLONE) + if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_CLONE) { ptid_t ptid; unsigned long new_pid; int ret, status; + /* Get the pid of the new lwp. */ ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0, &new_pid); @@ -399,6 +401,52 @@ handle_extended_wait (struct lwp_info *event_child, int wstat) warning ("wait returned unexpected status 0x%x", status); } + if (event == PTRACE_EVENT_FORK) + { + struct process_info *parent_proc; + struct process_info *child_proc; + struct lwp_info *child_lwp; + struct target_desc *tdesc; + + ptid = ptid_build (new_pid, new_pid, 0); + + if (debug_threads) + { + debug_printf ("HEW: Got fork event " + "from LWP %ld, new child is %d\n", + ptid_get_lwp (ptid_of (event_thr)), + ptid_get_pid (ptid)); + } + + /* Add the new process to the tables and clone the breakpoint + lists of the parent. We need to do this even if the new process + will be detached, since we will need the process object and the + breakpoints to remove any breakpoints from memory when we + detach, and the host side will access registers. */ + child_proc = linux_add_process (new_pid, 0); + gdb_assert (child_proc != NULL); + child_lwp = add_lwp (ptid); + gdb_assert (child_lwp != NULL); + child_lwp->stopped = 1; + parent_proc = get_thread_process (event_thr); + child_proc->attached = parent_proc->attached; + clone_all_breakpoints (&child_proc->breakpoints, + &child_proc->raw_breakpoints, + parent_proc->breakpoints); + + tdesc = xmalloc (sizeof (struct target_desc)); + copy_target_description (tdesc, parent_proc->tdesc); + child_proc->tdesc = tdesc; + child_lwp->must_set_ptrace_flags = 1; + + /* Save fork info in the parent thread. */ + event_thr->pending_follow.kind = TARGET_WAITKIND_FORKED; + event_thr->pending_follow.value.related_pid = ptid; + + /* Report the event. */ + return 0; + } + if (debug_threads) debug_printf ("HEW: Got clone event " "from LWP %ld, new child is LWP %ld\n", @@ -447,8 +495,13 @@ handle_extended_wait (struct lwp_info *event_child, int wstat) /* Always resume the current thread. If we are stopping threads, it will have a pending SIGSTOP; we may as well collect it now. */ - linux_resume_one_lwp (event_child, event_child->stepping, 0, NULL); + linux_resume_one_lwp (event_lwp, event_lwp->stepping, 0, NULL); + + /* Don't report the event. */ + return 1; } + + internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event); } /* Return the PC as read from the regcache of LWP, without any @@ -1100,6 +1153,55 @@ get_detach_signal (struct thread_info *thread) } } +/* Prepare a fork child to be detached, if necessary. If THREAD is + not a fork child, just return OK. */ + +static int +linux_detach_fork (struct thread_info *thread) +{ + ptid_t child_ptid; + pid_t child_pid; + int status = W_STOPCODE (0); + + /* Here we know that we are in a follow-fork, that we are following + the parent, and that we are going to detach the child. */ + child_ptid = thread->entry.id; + child_pid = ptid_get_pid (child_ptid); + + /* When debugging an inferior in an architecture that supports + hardware single stepping on a kernel without commit + 6580807da14c423f0d0a708108e6df6ebc8bc83d, the vfork child + process starts with the TIF_SINGLESTEP/X86_EFLAGS_TF bits + set if the parent process had them set. + To work around this, single step the child process + once before detaching to clear the flags. */ + + if (can_hardware_single_step ()) + { + linux_disable_event_reporting (child_pid); + if (ptrace (PTRACE_SINGLESTEP, child_pid, 0, 0) < 0) + perror_with_name (_("Couldn't do single step")); + if (my_waitpid (child_pid, &status, 0) < 0) + perror_with_name (_("Couldn't wait vfork process")); + } + + if (WIFSTOPPED (status)) + { + int signo; + struct lwp_info *child_lwp = find_lwp_pid (child_ptid); + + signo = WSTOPSIG (status); + if (signo == SIGSTOP + || (signo != 0 && !pass_signals[gdb_signal_from_host (signo)])) + { + /* Clear the SIGSTOP. */ + WSETSTOP (child_lwp->status_pending, 0); + } + } + + return 0; +} + static int linux_detach_one_lwp (struct inferior_list_entry *entry, void *args) { @@ -1125,6 +1227,9 @@ linux_detach_one_lwp (struct inferior_list_entry *entry, void *args) /* Flush any pending changes to the process's registers. */ regcache_invalidate_thread (thread); + /* Prepare fork child for detach, if necessary. */ + linux_detach_fork (thread); + /* Pass on any pending signal for this thread. */ sig = get_detach_signal (thread); @@ -1885,8 +1990,10 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat) if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP && linux_is_extended_waitstatus (wstat)) { - handle_extended_wait (child, wstat); - return NULL; + if (handle_extended_wait (child, wstat)) + return NULL; + else + return child; } if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGSTOP @@ -2484,6 +2591,19 @@ linux_stabilize_threads (void) } } +/* Return non-zero if WAITSTATUS reflects an extended linux + event that gdbserver supports. Otherwise, return zero. */ + +static int +extended_event_reported (const struct target_waitstatus *waitstatus) +{ + + if (waitstatus == NULL) + return 0; + + return (waitstatus->kind == TARGET_WAITKIND_FORKED); +} + /* Wait for process, returns status. */ static ptid_t @@ -2819,7 +2939,8 @@ retry: && !bp_explains_trap && !trace_event) || (gdb_breakpoint_here (event_child->stop_pc) && gdb_condition_true_at_breakpoint (event_child->stop_pc) - && gdb_no_commands_at_breakpoint (event_child->stop_pc))); + && gdb_no_commands_at_breakpoint (event_child->stop_pc)) + || extended_event_reported (¤t_thread->pending_follow)); run_breakpoint_commands (event_child->stop_pc); @@ -2841,6 +2962,13 @@ retry: paddress (event_child->stop_pc), paddress (event_child->step_range_start), paddress (event_child->step_range_end)); + if (extended_event_reported (¤t_thread->pending_follow)) + { + char *str = target_waitstatus_to_string (ourstatus); + debug_printf ("LWP %ld: extended event with waitstatus %s\n", + lwpid_of (get_lwp_thread (event_child)), str); + xfree (str); + } } /* We're not reporting this breakpoint to GDB, so apply the @@ -2939,7 +3067,17 @@ retry: unstop_all_lwps (1, event_child); } - ourstatus->kind = TARGET_WAITKIND_STOPPED; + if (extended_event_reported (¤t_thread->pending_follow)) + { + /* If the reported event is a fork, vfork or exec, let GDB know. */ + ourstatus->kind = current_thread->pending_follow.kind; + ourstatus->value = current_thread->pending_follow.value; + + /* Reset the event lwp's waitstatus since we handled it already. */ + current_thread->pending_follow.kind = TARGET_WAITKIND_SPURIOUS; + } + else + ourstatus->kind = TARGET_WAITKIND_STOPPED; if (current_thread->last_resume_kind == resume_stop && WSTOPSIG (w) == SIGSTOP) @@ -2956,7 +3094,7 @@ retry: but, it stopped for other reasons. */ ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w)); } - else + else if (ourstatus->kind == TARGET_WAITKIND_STOPPED) { ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w)); } @@ -4748,8 +4886,8 @@ linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len) val = val & 0xffff; else if (len == 3) val = val & 0xffffff; - debug_printf ("Writing %0*x to 0x%08lx\n", 2 * ((len < 4) ? len : 4), - val, (long)memaddr); + debug_printf ("Writing %0*x to 0x%08lx in process %d\n", + 2 * ((len < 4) ? len : 4), val, (long)memaddr, pid); } /* Fill start and end extra bytes of buffer with existing memory data. */ @@ -6147,13 +6285,17 @@ initialize_low (void) initialize_low_arch (); -#if PTRACE_EVENT_SUPPORT +#if FULL_PTRACE_EVENT_SUPPORT /* Always try to enable ptrace event extensions. Leave PTRACE_O_TRACESYSGOOD out until it is supported. */ linux_ptrace_set_additional_flags (PTRACE_O_TRACEVFORKDONE | PTRACE_O_TRACEVFORK | PTRACE_O_TRACEFORK | PTRACE_O_TRACEEXEC); +#else + /* Enable fork events. */ + linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK); #endif linux_test_for_event_reporting (); + /* Enable extended events. */ } diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c index 373fc15..e62b4b8 100644 --- a/gdb/gdbserver/remote-utils.c +++ b/gdb/gdbserver/remote-utils.c @@ -1105,12 +1105,24 @@ prepare_resume_reply (char *buf, ptid_t ptid, switch (status->kind) { case TARGET_WAITKIND_STOPPED: + case TARGET_WAITKIND_FORKED: { struct thread_info *saved_thread; const char **regp; struct regcache *regcache; - sprintf (buf, "T%02x", status->value.sig); + if (status->kind == TARGET_WAITKIND_FORKED && multi_process) + { + enum gdb_signal signal = GDB_SIGNAL_TRAP; + const char *event = "fork"; + + sprintf (buf, "T%02x%s:p%x.%lx;", signal, event, + ptid_get_pid (status->value.related_pid), + ptid_get_lwp (status->value.related_pid)); + } + else + sprintf (buf, "T%02x", status->value.sig); + buf += strlen (buf); saved_thread = current_thread; diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 71c954f..39c9fbd 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -3546,9 +3546,12 @@ process_serial_event (void) discard_queued_stop_replies (pid); write_ok (own_buf); - if (extended_protocol) + if ((get_first_inferior (&all_threads) != NULL) + || extended_protocol) { - /* Treat this like a normal program exit. */ + /* There is still at least one inferior remaining, or we + are in extended mode, so don't terminate gdbserver and + treat this like a normal program exit. */ last_status.kind = TARGET_WAITKIND_EXITED; last_status.value.integer = 0; last_ptid = pid_to_ptid (pid); diff --git a/gdb/remote.c b/gdb/remote.c index cb40955..d809fcd 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -24,6 +24,7 @@ #include #include "inferior.h" #include "infrun.h" +#include "inf-child.h" #include "bfd.h" #include "symfile.h" #include "target.h" @@ -1441,7 +1442,6 @@ remote_multi_process_p (struct remote_state *rs) return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE; } -#if PTRACE_EVENT_SUPPORT /* Returns true if fork events are supported. */ static int @@ -1450,6 +1450,7 @@ remote_fork_event_p (struct remote_state *rs) return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE; } +#if PTRACE_EVENT_SUPPORT /* Returns true if vfork events are supported. */ static int @@ -1467,6 +1468,51 @@ remote_exec_event_p (struct remote_state *rs) } #endif +/* Target follow-fork function for remote targets. On entry, and + at return, the current inferior is the fork parent. */ + +static int +remote_follow_fork (struct target_ops *target, int follow_child, + int detach_fork) +{ + struct remote_state *rs = get_remote_state (); + + /* Checking the fork event is sufficient for both fork and vfork. */ + if (remote_fork_event_p (rs)) + { + if (detach_fork && !follow_child) + { + ptid_t child_ptid; + pid_t child_pid; + + gdb_assert ((inferior_thread ()->pending_follow.kind + == TARGET_WAITKIND_FORKED) + || (inferior_thread ()->pending_follow.kind + == TARGET_WAITKIND_VFORKED)); + child_ptid = inferior_thread ()->pending_follow.value.related_pid; + child_pid = ptid_get_pid (child_ptid); + + /* Tell the remote target to detach. */ + xsnprintf (rs->buf, get_remote_packet_size (), "D;%x", child_pid); + + putpkt (rs->buf); + getpkt (&rs->buf, &rs->buf_size, 0); + + if (rs->buf[0] == 'O' && rs->buf[1] == 'K') + ; + else if (rs->buf[0] == '\0') + error (_("Remote doesn't know how to detach")); + else + error (_("Can't detach process.")); + + inferior_ptid = null_ptid; + detach_inferior (child_pid); + inf_child_maybe_unpush_target (target); + } + } + return 0; +} + /* Tokens for use by the asynchronous signal handlers for SIGINT. */ static struct async_signal_handler *async_sigint_remote_twice_token; static struct async_signal_handler *async_sigint_remote_token; @@ -1867,7 +1913,7 @@ set_general_process (void) struct remote_state *rs = get_remote_state (); /* If the remote can't handle multiple processes, don't bother. */ - if (!rs->extended || !remote_multi_process_p (rs)) + if (!remote_multi_process_p (rs)) return; /* We only need to change the remote current thread if it's pointing @@ -4413,10 +4459,12 @@ remote_open_1 (const char *name, int from_tty, die when it hits one. */ static void -remote_detach_1 (const char *args, int from_tty, int extended) +remote_detach_1 (struct target_ops *ops, const char *args, + int from_tty, int extended) { int pid = ptid_get_pid (inferior_ptid); struct remote_state *rs = get_remote_state (); + struct thread_info *tp = first_thread_of_process (pid); if (args) error (_("Argument given to \"detach\" when remotely debugging.")); @@ -4450,22 +4498,31 @@ remote_detach_1 (const char *args, int from_tty, int extended) else error (_("Can't detach process.")); - if (from_tty && !extended) + if (from_tty && !extended && (number_of_inferiors () > 0)) puts_filtered (_("Ending remote debugging.\n")); - target_mourn_inferior (); + /* If doing detach-on-fork, we don't mourn, because that will delete + breakpoints that should be available for the child. */ + if (tp->pending_follow.kind != TARGET_WAITKIND_FORKED) + target_mourn_inferior (); + else + { + inferior_ptid = null_ptid; + detach_inferior (pid); + inf_child_maybe_unpush_target (ops); + } } static void remote_detach (struct target_ops *ops, const char *args, int from_tty) { - remote_detach_1 (args, from_tty, 0); + remote_detach_1 (ops, args, from_tty, 0); } static void extended_remote_detach (struct target_ops *ops, const char *args, int from_tty) { - remote_detach_1 (args, from_tty, 1); + remote_detach_1 (ops, args, from_tty, 1); }