From patchwork Fri Sep 19 20:57:30 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Don Breazeal X-Patchwork-Id: 2930 Received: (qmail 31842 invoked by alias); 19 Sep 2014 20:57:41 -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 31808 invoked by uid 89); 19 Sep 2014 20:57:38 -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 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; Fri, 19 Sep 2014 20:57:35 +0000 Received: from svr-orw-exc-10.mgc.mentorg.com ([147.34.98.58]) by relay1.mentorg.com with esmtp id 1XV5FE-0000yU-Eg from Don_Breazeal@mentor.com for gdb-patches@sourceware.org; Fri, 19 Sep 2014 13:57:32 -0700 Received: from [127.0.0.1] ([172.30.12.90]) by SVR-ORW-EXC-10.mgc.mentorg.com with Microsoft SMTPSVC(6.0.3790.4675); Fri, 19 Sep 2014 13:57:31 -0700 Message-ID: <541C98BA.2040904@codesourcery.com> Date: Fri, 19 Sep 2014 13:57:30 -0700 From: "Breazeal, Don" User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:31.0) Gecko/20100101 Thunderbird/31.1.1 MIME-Version: 1.0 To: gdb-patches@sourceware.org Subject: Re: [PATCH 06/16 v2] Extended-remote Linux follow fork References: <1407434395-19089-1-git-send-email-donb@codesourcery.com> <1408580964-27916-7-git-send-email-donb@codesourcery.com> In-Reply-To: <1408580964-27916-7-git-send-email-donb@codesourcery.com> X-IsSubscribed: yes This is a small update to this patch so that it will work on targets where hardware single step is not supported. I had noted previously that I had not tested inheritance of hardware watchpoints across a fork. I have now completed that testing, which exposed a couple of problems, one of which is fixed by this update. The only change from the previous version of this patch is to initialize the variable 'status' in linux-low.c:linux_follow_fork. Thanks, --Don On 8/20/2014 5:29 PM, Don Breazeal wrote: > This patch implements basic support for follow-fork and detach-on-fork on > 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 > > - this adds the ptrace option to trace fork events to the new functions > from patch 4 that set up ptrace options. > > * 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 > mimics a similar mechanism used in the native implementation. Here is > it used in several ways: > - in remote_detach_1 to distinguish between a process that is being > detached-on-fork vs. just detached. In the fork case we don't want > to mourn the process because we want to keep the inferior around in > case the user decides to run the inferior again. > - to record the child pid for the expected follow_fork request. > - to find the parent in follow_fork (and later, elsewhere). > > - we also store the waitstatus in a new lwp_info member, 'waitstatus', > which is used in controlling the reporting of the event in > linux_wait_1. We cannot re-use pending_follow for this because we > need to mark this one with 'ignored' status as part of the > linux_wait_1 procedure to stop event processing, and we need to keep > pending_follow intact for use later on. We will also need this later > on for exec event handling, where pending_follow makes no sense. > > - 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 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. > > * 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). > > - extended_remote target routine extended_remote_follow_fork > > - RSP packet vFollowFork > > - in gdbserver struct target_ops, add functions linux_supports_follow_fork > and linux_follow_fork. The linux_follow_fork routine mimics the > implementation of linux-nat.c:linux_child_follow_fork, but the data > structures in use prevented turning this into a common function for now. > > Tested on x64 Ubuntu Lucid, native, remote, extended-remote. > > Thanks > --Don > > gdb/ > 2014-08-20 Don Breazeal > > * remote.c (remote_detach_1): Add target_ops argument, handle > detach-on-fork. > (remote_detach, extended_remote_detach): Call remote_detach_1 > with target_ops argument. > (remote_parse_stop_reply): Handle new RSP stop reason "fork" in > 'T' stop reply packet. > (extended_remote_follow_fork): Implement follow-fork support. > (remote_pid_to_str): Print process. > (_initialize_remote): Call add_packet_config_cmd for new RSP packet. > > gdb/gdbserver/ > 2014-08-20 Don Breazeal > > * gdbthread.h (struct thread_info) : New member. > * linux-low.c (handle_extended_wait): Change function type from > void to int, handle PTRACE_EVENT_FORK, call internal_error. > (is_parent_callback): New function. > (linux_follow_fork): New function. > (linux_low_filter_event): Handle return value from > handle_extended_wait. > (extended_event_reported): New function. > (linux_write_memory): Add pid to debug print. > (linux_target_ops) : Initialize new member. > (initialize_low): Add PTRACE_O_TRACEFORK option. > * linux-low.h (struct lwp_info) : New member. > * lynx-low.c (lynx_target_ops) : Initialize new member. > * nto-low.c (nto_target_ops) : Initialize new member. > * remote-utils.c (prepare_resume_reply): New RSP stop reason "fork" > for 'T' stop reply. > * server.c (handle_v_follow_fork): New function. > (handle_v_requests): Handle vFollowFork packet, call > handle_v_follow_fork. > * spu-low.c (spu_target_ops) : Initialize new member. > * target.h (struct target_ops) : New member. > (target_follow_fork): Define macro. > * win32-low.c (win32_target_ops) : Initialize new member. > --- gdb/gdbserver/gdbthread.h | 5 + gdb/gdbserver/linux-low.c | 207 +++++++++++++++++++++++++++++++++++++++--- gdb/gdbserver/linux-low.h | 5 + gdb/gdbserver/lynx-low.c | 1 + gdb/gdbserver/nto-low.c | 1 + gdb/gdbserver/remote-utils.c | 14 +++- gdb/gdbserver/server.c | 41 ++++++++ gdb/gdbserver/spu-low.c | 1 + gdb/gdbserver/target.h | 13 +++ gdb/gdbserver/win32-low.c | 1 + gdb/remote.c | 80 ++++++++++++---- 11 files changed, 334 insertions(+), 35 deletions(-) PACKET_qXfer_siginfo_read }, @@ -4333,10 +4336,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.")); @@ -4373,19 +4378,28 @@ remote_detach_1 (const char *args, int from_tty, int extended) if (from_tty && !extended) 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); } /* Same as remote_detach, but don't send the "D" packet; just disconnect. */ @@ -5481,7 +5495,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); @@ -5533,6 +5548,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. */ @@ -7820,6 +7840,32 @@ extended_remote_kill (struct target_ops *ops) target_mourn_inferior (); } +/* Target routine for follow-fork. */ + +static int +extended_remote_follow_fork (struct target_ops *ops, int follow_child, + int detach_fork) +{ + struct remote_state *rs = get_remote_state (); + + if (extended_remote_feature_supported (FORK_EVENT)) + { + char *p = rs->buf; + char *endbuf = rs->buf + get_remote_packet_size (); + + xsnprintf (rs->buf, get_remote_packet_size (), "vFollowFork;%d;%d", + follow_child, detach_fork); + + putpkt (rs->buf); + getpkt (&rs->buf, &rs->buf_size, 0); + + if (rs->buf[0] == 'E') + return 1; + } + + return 0; +} + static void remote_mourn (struct target_ops *ops) { @@ -7836,20 +7882,6 @@ remote_mourn_1 (struct target_ops *target) generic_mourn_inferior (); } -/* Target follow-fork function for extended-remote targets. */ - -static int -extended_remote_follow_fork (struct target_ops *target, int follow_child, - int detach_fork) -{ - if (extended_remote_feature_supported (FORK_EVENT)) - { - /* FIXME: Implement follow-fork here. */ - return -1; - } - return 0; -} - static void extended_remote_mourn_1 (struct target_ops *target) { @@ -9356,8 +9388,11 @@ 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)); + 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)); @@ -12184,6 +12219,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, add_packet_config_cmd (&remote_protocol_packets[PACKET_vKill], "vKill", "kill", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_vFollowFork], + "vFollowFork", "follow-fork", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_qAttached], "qAttached", "query-attached", 0); diff --git a/gdb/gdbserver/gdbthread.h b/gdb/gdbserver/gdbthread.h index 8290ec1..3d003b9 100644 --- a/gdb/gdbserver/gdbthread.h +++ b/gdb/gdbserver/gdbthread.h @@ -41,6 +41,11 @@ struct thread_info /* True if LAST_STATUS hasn't been reported to GDB yet. */ int status_pending_p; + /* This is used to remember when a fork or vfork event was caught by + a catchpoint, and thus the event is to be followed at the next + resume of the thread, and not immediately. */ + 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 045d507..56993bd 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" @@ -368,17 +369,17 @@ 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 +static int handle_extended_wait (struct lwp_info *event_child, int wstat) { int event = linux_ptrace_get_extended_event (wstat); struct thread_info *event_thr = get_lwp_thread (event_child); 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; @@ -403,6 +404,56 @@ 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 (current_inferior); + 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 for target processing. */ + current_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED; + current_inferior->pending_follow.value.related_pid = ptid; + + /* Save fork info for reporting to GDB. */ + event_child->waitstatus.kind = TARGET_WAITKIND_FORKED; + event_child->waitstatus.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", @@ -452,7 +503,12 @@ handle_extended_wait (struct lwp_info *event_child, int wstat) 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); + + /* 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 @@ -1177,6 +1233,97 @@ linux_detach (int pid) return 0; } +/* Callback used to find the parent process of a fork. */ + +static int +is_parent_callback (struct inferior_list_entry *entry, void *ignore) +{ + struct thread_info *thread = (struct thread_info *) entry; + + if (thread->pending_follow.kind == TARGET_WAITKIND_FORKED + || thread->pending_follow.kind == TARGET_WAITKIND_VFORKED) + return 1; + + return 0; +} + +/* Handle a fork in the inferior process. Mainly this consists of + handling the case where we are detaching the new child process by + cleaning up its state so it can proceed. Note that if we are + detaching the parent process, GDB has already done that via + target_detach. */ + +static int +linux_follow_fork (int follow_child, int detach_fork) +{ + struct inferior_list_entry *parent_inf; + struct thread_info *parent_thread; + + parent_inf = find_inferior (&all_threads, is_parent_callback, NULL); + + /* If we can't find the parent, we are following the child and the + parent has already been detached. Nothing to do, so return OK. */ + if (parent_inf == NULL) + return 0; + + parent_thread = (struct thread_info *)parent_inf; + parent_thread->pending_follow.kind = TARGET_WAITKIND_IGNORE; + + if (!follow_child) + { + if (detach_fork) + { + int status = W_STOPCODE (0); + ptid_t child_ptid = parent_thread->pending_follow.value.related_pid; + pid_t child_pid = ptid_get_pid (child_ptid); + struct lwp_info *child_lwp = find_lwp_pid (child_ptid); + + if (the_low_target.prepare_to_resume != NULL) + the_low_target.prepare_to_resume (child_lwp); + + /* 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_ptrace_disable_options (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 process_info *child_proc; + + signo = WSTOPSIG (status); + if (signo == SIGSTOP + || (signo != 0 + && !pass_signals[gdb_signal_from_host (signo)])) + signo = 0; + + ptrace (PTRACE_DETACH, child_pid, 0, signo); + + /* Deallocate all process-related storage. */ + child_proc = find_process_pid (child_pid); + if (child_proc != NULL) + the_target->mourn (child_proc); + + current_inferior = NULL; + } + } + } + + return 0; +} + /* Remove all LWPs that belong to process PROC from the lwp list. */ static int @@ -1875,8 +2022,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 @@ -2477,6 +2626,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 @@ -2840,7 +3002,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 (&event_child->waitstatus)); run_breakpoint_commands (event_child->stop_pc); @@ -2862,6 +3025,13 @@ retry: paddress (event_child->stop_pc), paddress (event_child->step_range_start), paddress (event_child->step_range_end)); + if (extended_event_reported (&event_child->waitstatus)) + { + 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 @@ -2960,7 +3130,17 @@ retry: unstop_all_lwps (1, event_child); } - ourstatus->kind = TARGET_WAITKIND_STOPPED; + if (extended_event_reported (&event_child->waitstatus)) + { + /* If the reported event is a fork, vfork or exec, let GDB know. */ + ourstatus->kind = event_child->waitstatus.kind; + ourstatus->value = event_child->waitstatus.value; + + /* Reset the event child's waitstatus since we handled it already. */ + event_child->waitstatus.kind = TARGET_WAITKIND_IGNORE; + } + else + ourstatus->kind = TARGET_WAITKIND_STOPPED; if (current_thread->last_resume_kind == resume_stop && WSTOPSIG (w) == SIGSTOP) @@ -2977,7 +3157,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)); } @@ -4769,8 +4949,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. */ @@ -6065,6 +6245,7 @@ static struct target_ops linux_target_ops = { linux_supports_follow_fork, linux_supports_follow_exec, linux_enable_extended_features, + linux_follow_fork, linux_mourn, linux_join, linux_thread_alive, @@ -6180,7 +6361,7 @@ initialize_low (void) initialize_low_arch (); - /* Placeholder to enable extended events. */ - linux_ptrace_set_requested_options (0); + /* Enable extended events. */ + linux_ptrace_set_requested_options (PTRACE_O_TRACEFORK); linux_ptrace_check_options (); } diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index 4820929..a903430 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -266,6 +266,11 @@ struct lwp_info status_pending). */ int dead; + /* If WAITSTATUS->KIND != TARGET_WAITKIND_IGNORE, the waitstatus for + this LWP's last event. This is used to maintain the current status + during event processing. */ + struct target_waitstatus waitstatus; + /* When stopped is set, the last wait status recorded for this lwp. */ int last_status; diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c index d399e67..902302a 100644 --- a/gdb/gdbserver/lynx-low.c +++ b/gdb/gdbserver/lynx-low.c @@ -725,6 +725,7 @@ static struct target_ops lynx_target_ops = { NULL, /* supports_follow_fork */ NULL, /* supports_follow_exec */ NULL, /* enable_extended_features */ + NULL, /* follow_fork */ lynx_mourn, lynx_join, lynx_thread_alive, diff --git a/gdb/gdbserver/nto-low.c b/gdb/gdbserver/nto-low.c index 17f2a14..934657f 100644 --- a/gdb/gdbserver/nto-low.c +++ b/gdb/gdbserver/nto-low.c @@ -931,6 +931,7 @@ static struct target_ops nto_target_ops = { NULL, /* supports_follow_fork */ NULL, /* supports_follow_exec */ NULL, /* enable_extended_features */ + NULL, /* follow_fork */ nto_mourn, NULL, /* nto_join */ nto_thread_alive, 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 a13d2d5..d756c94 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -2558,6 +2558,29 @@ handle_v_kill (char *own_buf) } } +/* Handle forked process. */ + +static void +handle_v_follow_fork (char *own_buf) +{ + int follow_child; + int detach_fork; + char *p = &own_buf[12]; + int ret; + + gdb_assert (extended_protocol); + + follow_child = strtol (p, NULL, 16); + p = strchr (p, ';') + 1; + detach_fork = strtol (p, NULL, 16); + + ret = target_follow_fork (follow_child, detach_fork); + if (ret == 0) + write_ok (own_buf); + else + write_enn (own_buf); +} + /* Handle all of the extended 'v' packets. */ void handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) @@ -2623,6 +2646,24 @@ handle_v_requests (char *own_buf, int packet_len, int *new_packet_len) return; } + if (strncmp (own_buf, "vFollowFork;", 6) == 0) + { + if (!target_running ()) + { + fprintf (stderr, "No process to follow\n"); + write_enn (own_buf); + return; + } + if (!extended_protocol || !multi_process) + { + fprintf (stderr, "Target doesn't support follow-fork\n"); + write_enn (own_buf); + return; + } + handle_v_follow_fork (own_buf); + return; + } + if (handle_notif_ack (own_buf, packet_len)) return; diff --git a/gdb/gdbserver/spu-low.c b/gdb/gdbserver/spu-low.c index d4c5429..9becef6 100644 --- a/gdb/gdbserver/spu-low.c +++ b/gdb/gdbserver/spu-low.c @@ -644,6 +644,7 @@ static struct target_ops spu_target_ops = { NULL, /* supports_follow_fork */ NULL, /* supports_follow_exec */ NULL, /* enable_extended_features */ + NULL, /* follow_fork */ spu_mourn, spu_join, spu_thread_alive, diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index abce9b3..03bfe76 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -105,6 +105,11 @@ struct target_ops void (*enable_extended_features) (void); + /* Handle a call to fork as specified by follow-fork-mode and + detach-on-fork. */ + + int (*follow_fork) (int follow_child, int detach_fork); + /* The inferior process has died. Do what is right. */ void (*mourn) (struct process_info *proc); @@ -438,6 +443,14 @@ int kill_inferior (int); (the_target->supports_multi_process ? \ (*the_target->supports_multi_process) () : 0) +#define target_supports_follow_fork() \ + (the_target->supports_follow_fork ? \ + (*the_target->supports_follow_fork) () : 0) + +#define target_follow_fork(follow_child, detach_fork) \ + (the_target->follow_fork ? \ + (*the_target->follow_fork) (follow_child, detach_fork) : 0) + #define target_process_qsupported(query) \ do \ { \ diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c index 42cbf32..944d06b 100644 --- a/gdb/gdbserver/win32-low.c +++ b/gdb/gdbserver/win32-low.c @@ -1770,6 +1770,7 @@ static struct target_ops win32_target_ops = { NULL, /* supports_follow_fork */ NULL, /* supports_follow_exec */ NULL, /* enable_extended_features */ + NULL, /* follow_fork */ win32_mourn, win32_join, win32_thread_alive, diff --git a/gdb/remote.c b/gdb/remote.c index f3076b3..19f3efb 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 "exceptions.h" @@ -3916,6 +3917,8 @@ static const struct protocol_feature remote_protocol_features[] = { PACKET_QStartNoAckMode }, { "multiprocess", PACKET_DISABLE, remote_supported_packet, PACKET_multiprocess_feature }, + { "vFollowFork", PACKET_DISABLE, remote_supported_packet, + PACKET_vFollowFork }, { "QNonStop", PACKET_DISABLE, remote_supported_packet, PACKET_QNonStop }, { "qXfer:siginfo:read", PACKET_DISABLE, remote_supported_packet,