From patchwork Fri Oct 31 23:28:46 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Don Breazeal X-Patchwork-Id: 3523 Received: (qmail 28525 invoked by alias); 31 Oct 2014 23:29:58 -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 28516 invoked by uid 89); 31 Oct 2014 23:29:57 -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; Fri, 31 Oct 2014 23:29:55 +0000 Received: from svr-orw-fem-06.mgc.mentorg.com ([147.34.97.120]) by relay1.mentorg.com with esmtp id 1XkLdg-00027H-Or from Don_Breazeal@mentor.com for gdb-patches@sourceware.org; Fri, 31 Oct 2014 16:29:52 -0700 Received: from build4-lucid-cs (147.34.91.1) by SVR-ORW-FEM-06.mgc.mentorg.com (147.34.97.120) with Microsoft SMTP Server id 14.3.181.6; Fri, 31 Oct 2014 16:29:52 -0700 Received: by build4-lucid-cs (Postfix, from userid 1905) id 277CA40DDD; Fri, 31 Oct 2014 16:29:51 -0700 (PDT) From: Don Breazeal To: Subject: [PATCH 08/16 v3] Extended-remote follow vfork Date: Fri, 31 Oct 2014 16:28:46 -0700 Message-ID: <1414798134-11536-6-git-send-email-donb@codesourcery.com> In-Reply-To: <1408580964-27916-1-git-send-email-donb@codesourcery.com> References: <1408580964-27916-1-git-send-email-donb@codesourcery.com> MIME-Version: 1.0 X-IsSubscribed: yes This patch implements follow-fork for vfork on extended-remote linux targets. The implementation follows the native implementation as much as possible. Most of the work is done on the GDB side in the existing code now in infrun.c. GDBserver just has to report the events and do a little bookkeeping. Implementation was almost entirely in gdbserver, excepting changes to gdb/remote.c, and included: * enabling VFORK events - by adding ptrace options for VFORK and VFORK_DONE as 'additional options' in linux-low.c:initialize_low, so that they are available for extended-mode. - by adding ptrace options for VFORK and VFORK_DONE as 'base options' in linux-low.c:linux_enable_extended_features. In this function we know we are in extended-mode, so we enable these options if they are supported. * handling VFORK and VFORK_DONE events in linux-low.c:handle_extended_wait by saving the event information for event reporting and for the follow_fork request expected to come from GDB. * handling VFORK in linux-low.c:linux_follow_fork, for the case where VFORK_DONE is supported and the case where it is not. This code is very similar to the code in linux-nat.c:linux_child_follow_fork, but slight differences in the data structures between the two implementations led me to keep them separate. * include VFORK and VFORK_DONE events in the predicate linux-low.c:extended_event_reported. * add support for VFORK and VFORK_DONE events in RSP by adding stop reasons "vfork" and "vforkdone" to the 'T' Stop Reply Packet in both gdbserver/remote-utils.c and gdb/remote.c. Tested on x64 Ubuntu Lucid, native, remote, extended-remote. Also tested (on the same system) support for systems lacking VFORK_DONE events by modifying gdbserver to simulate that case. Thanks --Don gdb/gdbserver/ 2014-10-31 Don Breazeal * linux-low.c (handle_extended_wait): Handle PTRACE_EVENT_FORK and PTRACE_EVENT_VFORK_DONE. (linux_follow_fork): Implement follow vfork. (extended_event_reported): Add vfork and vfork-done to the list of extended events. (initialize_low): Enable PTRACE_EVENT_FORK and PTRACE_EVENT_VFORK_DONE. * remote-utils.c (prepare_resume_reply): New stop reasons "vfork" and "vforkdone" for RSP 'T' Stop Reply Packet. gdb/ 2014-10-31 Don Breazeal * remote.c (remote_parse_stop_reply): New stop reasons "vfork" and "vforkdone" for RSP 'T' Stop Reply Packet. --- gdb/gdbserver/linux-low.c | 108 +++++++++++++++++++++++++++++++++++------ gdb/gdbserver/remote-utils.c | 16 +++++- gdb/remote.c | 15 ++++++ 3 files changed, 121 insertions(+), 18 deletions(-) diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 4f6d664..8fc831c 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -220,6 +220,7 @@ static void mark_lwp_dead (struct lwp_info *lwp, int wstat); static void proceed_all_lwps (void); static int finish_step_over (struct lwp_info *lwp); static CORE_ADDR get_stop_pc (struct lwp_info *lwp); +static void async_file_mark (void); static int kill_lwp (unsigned long lwpid, int signo); /* True if the low target can hardware single-step. Such targets @@ -375,7 +376,8 @@ handle_extended_wait (struct lwp_info *event_child, int wstat) struct thread_info *event_thr = get_lwp_thread (event_child); struct lwp_info *new_lwp; - if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_CLONE) + if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK + || event == PTRACE_EVENT_CLONE) { ptid_t ptid; unsigned long new_pid; @@ -400,7 +402,7 @@ handle_extended_wait (struct lwp_info *event_child, int wstat) warning ("wait returned unexpected status 0x%x", status); } - if (event == PTRACE_EVENT_FORK) + if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK) { struct process_info *parent_proc; struct process_info *child_proc; @@ -410,12 +412,10 @@ handle_extended_wait (struct lwp_info *event_child, int wstat) 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)); - } + 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 @@ -442,12 +442,22 @@ handle_extended_wait (struct lwp_info *event_child, int wstat) if (the_low_target.new_fork != NULL) the_low_target.new_fork (parent_proc, child_proc); - /* Save fork info for target processing. */ - current_thread->pending_follow.kind = TARGET_WAITKIND_FORKED; - current_thread->pending_follow.value.related_pid = ptid; + /* Clone arch-specific process data. */ + if (the_low_target.new_fork != NULL) + the_low_target.new_fork (parent_proc, child_proc); - /* Save fork info for reporting to GDB. */ - event_child->waitstatus.kind = TARGET_WAITKIND_FORKED; + /* Save fork info for target processing and reporting to GDB. */ + if (event == PTRACE_EVENT_FORK) + { + current_thread->pending_follow.kind = TARGET_WAITKIND_FORKED; + event_child->waitstatus.kind = TARGET_WAITKIND_FORKED; + } + else if (event == PTRACE_EVENT_VFORK) + { + current_thread->pending_follow.kind = TARGET_WAITKIND_VFORKED; + event_child->waitstatus.kind = TARGET_WAITKIND_VFORKED; + } + current_thread->pending_follow.value.related_pid = ptid; event_child->waitstatus.value.related_pid = ptid; /* Report the event. */ @@ -507,6 +517,11 @@ handle_extended_wait (struct lwp_info *event_child, int wstat) /* Don't report the event. */ return 1; } + else if (event == PTRACE_EVENT_VFORK_DONE) + { + event_child->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE; + return 0; + } internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event); } @@ -1258,6 +1273,7 @@ linux_follow_fork (int follow_child, int detach_fork) { struct inferior_list_entry *parent_inf; struct thread_info *parent_thread; + int has_vforked; parent_inf = find_inferior (&all_threads, is_parent_callback, NULL); @@ -1267,7 +1283,8 @@ linux_follow_fork (int follow_child, int detach_fork) return 0; parent_thread = (struct thread_info *)parent_inf; - parent_thread->pending_follow.kind = TARGET_WAITKIND_IGNORE; + has_vforked = (parent_thread->pending_follow.kind + == TARGET_WAITKIND_VFORKED); if (!follow_child) { @@ -1319,8 +1336,63 @@ linux_follow_fork (int follow_child, int detach_fork) current_thread = NULL; } } + + if (has_vforked) + { + struct lwp_info *parent_lp; + + parent_lp = get_thread_lwp (parent_thread); + gdb_assert (linux_supports_tracefork () >= 0); + + if (linux_supports_tracevforkdone ()) + { + if (debug_threads) + { + debug_printf ("LFF: waiting for VFORK_DONE on %d\n", + ptid_get_pid (parent_thread->entry.id)); + } + parent_lp->stopped = 1; + + /* We'll handle the VFORK_DONE event like any other + event, in target_wait. */ + } + else + { + /* We can't insert breakpoints until the child has + finished with the shared memory region. Without + VFORK_DONE events, the best we can do is to make sure it + runs for a little while. Hopefully it will be out of + range of any breakpoints we reinsert. Usually this + is only the single-step breakpoint at vfork's return + point. + + See linux-nat.c:linux_child_follow_vfork for a more + detailed discussion of this. */ + + if (debug_threads) + debug_printf ("LFF: no VFORK_DONE support, sleeping a bit\n"); + + usleep (10000); + + /* Pretend we've seen a PTRACE_EVENT_VFORK_DONE event, + and leave it pending. The next linux_resume_one_lwp call + will notice a pending event, and bypasses actually + resuming the inferior. */ + parent_lp->status_pending_p = 1; + parent_lp->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE; + parent_lp->stopped = 1; + + /* If we're in async mode, need to tell the event loop + there's something here to process. */ + if (target_is_async_p ()) + async_file_mark (); + } + } } + parent_thread->pending_follow.kind = TARGET_WAITKIND_IGNORE; + parent_thread->pending_follow.value.related_pid = null_ptid; + return 0; } @@ -2647,7 +2719,9 @@ extended_event_reported (const struct target_waitstatus *waitstatus) if (waitstatus == NULL) return 0; - return (waitstatus->kind == TARGET_WAITKIND_FORKED); + return (waitstatus->kind == TARGET_WAITKIND_FORKED + || waitstatus->kind == TARGET_WAITKIND_VFORKED + || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE); } /* Wait for process, returns status. */ @@ -6388,6 +6462,8 @@ initialize_low (void) initialize_low_arch (); /* Enable extended events. */ - linux_ptrace_set_requested_options (PTRACE_O_TRACEFORK); + linux_ptrace_set_requested_options (PTRACE_O_TRACEFORK + | PTRACE_O_TRACEVFORK + | PTRACE_O_TRACEVFORKDONE); linux_ptrace_check_options (); } diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c index e62b4b8..b4a523a 100644 --- a/gdb/gdbserver/remote-utils.c +++ b/gdb/gdbserver/remote-utils.c @@ -1106,15 +1106,18 @@ prepare_resume_reply (char *buf, ptid_t ptid, { case TARGET_WAITKIND_STOPPED: case TARGET_WAITKIND_FORKED: + case TARGET_WAITKIND_VFORKED: { struct thread_info *saved_thread; const char **regp; struct regcache *regcache; - if (status->kind == TARGET_WAITKIND_FORKED && multi_process) + if ((status->kind == TARGET_WAITKIND_FORKED + || status->kind == TARGET_WAITKIND_VFORKED) && multi_process) { enum gdb_signal signal = GDB_SIGNAL_TRAP; - const char *event = "fork"; + const char *event = (status->kind == TARGET_WAITKIND_FORKED + ?"fork":"vfork"); sprintf (buf, "T%02x%s:p%x.%lx;", signal, event, ptid_get_pid (status->value.related_pid), @@ -1225,6 +1228,15 @@ prepare_resume_reply (char *buf, ptid_t ptid, else sprintf (buf, "X%02x", status->value.sig); break; + case TARGET_WAITKIND_VFORK_DONE: + if (multi_process) + { + enum gdb_signal signal = GDB_SIGNAL_TRAP; + const char *event = "vforkdone"; + + sprintf (buf, "T%02x%s:;", signal, event); + } + break; default: error ("unhandled waitkind"); break; diff --git a/gdb/remote.c b/gdb/remote.c index 1d110f9..a210f30 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -5636,6 +5636,21 @@ Packet: '%s'\n"), event->ws.value.related_pid = read_ptid (++p1, &p); event->ws.kind = TARGET_WAITKIND_FORKED; } + else if (strncmp (p, "vfork", p1 - p) == 0) + { + event->ws.value.related_pid = read_ptid (++p1, &p); + event->ws.kind = TARGET_WAITKIND_VFORKED; + } + else if (strncmp (p, "vforkdone", p1 - p) == 0) + { + p1++; + p_temp = p1; + while (*p_temp && *p_temp != ';') + p_temp++; + + event->ws.kind = TARGET_WAITKIND_VFORK_DONE; + p = p_temp; + } else { /* Silently skip unknown optional info. */