* [PATCH 03/10] Refactor extended ptrace event status
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
@ 2014-08-07 18:00 ` Don Breazeal
2014-08-07 18:00 ` [PATCH 06/10] Extended-remote follow fork Don Breazeal
` (25 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-07 18:00 UTC (permalink / raw)
To: gdb-patches
This patch implements functions for identifying and extracting extended ptrace event information from a Linux wait status. These are just convenience functions intended to hide the ">> 16" used to extract the event from the wait status word, replacing the hard-coded shift with a more descriptive function call. This is preparatory work for implementation of follow-fork and detach-on-fork for extended-remote linux targets.
The functions linux_is_extended_waitstatus and linux_ptrace_get_extended_event are defined in nat/linux-ptrace.c, and called in linux-nat.c and gdbserver/linux-low.c.
My initial approach was to implement predicates for every extended event, e.g. linux_is_traced_clone (status), linux_is_traced_fork (status), but that didn't fit the current implementation as well, bloated the code a bit, and didn't add anything to readability, so I went with just extracting the event bits from the status instead.
Tested on x64 Ubuntu Lucid, native only.
Thanks,
--Don
gdb/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* linux-nat.c (linux_handle_extended_wait): Call
linux_ptrace_get_extended_event.
(wait_lwp): Call linux_is_extended_waitstatus.
(linux_nat_filter_event): Call linux_ptrace_get_extended_event
and linux_is_extended_waitstatus.
* nat/linux-ptrace.c (linux_test_for_tracefork): Call
linux_ptrace_get_extended_event.
(linux_ptrace_get_extended_event): New function.
(linux_is_extended_waitstatus): New function.
* nat/linux-ptrace.h: Declare new function.
gdb/gdbserver/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* linux-low.c (handle_extended_wait): Call
linux_ptrace_get_extended_event.
(get_stop_pc, get_detach_signal, linux_low_filter_event): Call
linux_is_extended_waitstatus.
---
gdb/gdbserver/linux-low.c | 8 ++++----
gdb/linux-nat.c | 11 +++++++----
gdb/nat/linux-ptrace.c | 18 +++++++++++++++++-
gdb/nat/linux-ptrace.h | 2 ++
4 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index e65e276..db22e92 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -374,7 +374,7 @@ linux_add_process (int pid, int attached)
static void
handle_extended_wait (struct lwp_info *event_child, int wstat)
{
- int event = wstat >> 16;
+ int event = linux_ptrace_get_extended_event (wstat);
struct thread_info *event_thr = get_lwp_thread (event_child);
struct lwp_info *new_lwp;
@@ -516,7 +516,7 @@ get_stop_pc (struct lwp_info *lwp)
if (WSTOPSIG (lwp->last_status) == SIGTRAP
&& !lwp->stepping
&& !lwp->stopped_by_watchpoint
- && lwp->last_status >> 16 == 0)
+ && !linux_is_extended_waitstatus (lwp->last_status))
stop_pc -= the_low_target.decr_pc_after_break;
if (debug_threads)
@@ -1060,7 +1060,7 @@ get_detach_signal (struct thread_info *thread)
}
/* Extended wait statuses aren't real SIGTRAPs. */
- if (WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ if (WSTOPSIG (status) == SIGTRAP && linux_is_extended_waitstatus (status))
{
if (debug_threads)
debug_printf ("GPS: lwp %s had stopped with extended "
@@ -1873,7 +1873,7 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
}
if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
- && wstat >> 16 != 0)
+ && linux_is_extended_waitstatus (wstat))
{
handle_extended_wait (child, wstat);
return NULL;
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 7788c75..b72c066 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1802,7 +1802,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
{
int pid = ptid_get_lwp (lp->ptid);
struct target_waitstatus *ourstatus = &lp->waitstatus;
- int event = status >> 16;
+ int event = linux_ptrace_get_extended_event (status);
if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK
|| event == PTRACE_EVENT_CLONE)
@@ -2166,7 +2166,8 @@ wait_lwp (struct lwp_info *lp)
}
/* Handle GNU/Linux's extended waitstatus for trace events. */
- if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP
+ && linux_is_extended_waitstatus (status))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
@@ -2725,6 +2726,7 @@ static struct lwp_info *
linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
{
struct lwp_info *lp;
+ int event = linux_ptrace_get_extended_event (status);
*new_pending_p = 0;
@@ -2744,7 +2746,7 @@ linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
thread changes its tid to the tgid. */
if (WIFSTOPPED (status) && lp == NULL
- && (WSTOPSIG (status) == SIGTRAP && status >> 16 == PTRACE_EVENT_EXEC))
+ && (WSTOPSIG (status) == SIGTRAP && event == PTRACE_EVENT_EXEC))
{
/* A multi-thread exec after we had seen the leader exiting. */
if (debug_linux_nat)
@@ -2788,7 +2790,8 @@ linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
}
/* Handle GNU/Linux's extended waitstatus for trace events. */
- if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP
+ && linux_is_extended_waitstatus (status))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 24b02b9..69eb83c 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -423,7 +423,7 @@ linux_test_for_tracefork (int child_pid)
/* Check if we received a fork event notification. */
if (ret == child_pid && WIFSTOPPED (status)
- && status >> 16 == PTRACE_EVENT_FORK)
+ && linux_ptrace_get_extended_event (status) == PTRACE_EVENT_FORK)
{
/* We did receive a fork event notification. Make sure its PID
is reported. */
@@ -557,3 +557,19 @@ linux_ptrace_set_additional_flags (int flags)
{
additional_flags = flags;
}
+
+/* Extract extended ptrace event from wait status. */
+
+int
+linux_ptrace_get_extended_event (int wstat)
+{
+ return (wstat >> 16);
+}
+
+/* Determine whether wait status denotes an extended event. */
+
+int
+linux_is_extended_waitstatus (int wstat)
+{
+ return (linux_ptrace_get_extended_event (wstat) != 0);
+}
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 41b3198..31a77cd 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -92,5 +92,7 @@ extern int linux_supports_traceclone (void);
extern int linux_supports_tracevforkdone (void);
extern int linux_supports_tracesysgood (void);
extern void linux_ptrace_set_additional_flags (int);
+extern int linux_ptrace_get_extended_event (int wstat);
+extern int linux_is_extended_waitstatus (int wstat);
#endif /* COMMON_LINUX_PTRACE_H */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 06/10] Extended-remote follow fork
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
2014-08-07 18:00 ` [PATCH 03/10] Refactor extended ptrace event status Don Breazeal
@ 2014-08-07 18:00 ` Don Breazeal
2014-08-07 18:00 ` [PATCH 04/10] Enhance extended ptrace event setup Don Breazeal
` (24 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-07 18:00 UTC (permalink / raw)
To: gdb-patches
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.
This implementation required
* 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.
- 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.
- new extended_remote target routine extended_remote_follow_fork
- new RSP packet vFollowFork and support for it in the qSupported response. (Note that we always mark it as supported, since this is checked when the connection is made with GDB, and we may not know yet whether we will be using extended-mode. We depend on other means to only support it in extended-mode.)
- 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-06 Don Breazeal <donb@codesourcery.com>
* remote.c (PACKET_vFollowFork): New enum value.
(remote_protocol_features): Initialize vFollowFork element.
(remote_detach_1): Add target_ops argument, handle detach-on-fork
case differently.
(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): New function.
(remote_pid_to_str): Print process
(Show the maximum size of the address):
gdb/gdbserver/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* gdbthread.h (struct thread_info) <pending_follow>: New member.
* linux-low.c (handle_extended_wait): Change function type from
void to int, handle PTRACE_EVENT_FORK, call internal_error.
(linux_enable_extended_features): Add PTRACE_O_TRACEFORK ptrace
option.
(linux_supports_follow_fork): New function.
(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_wait_1): Call extended_event_reported.
(linux_write_memory): Add pid to debug print.
(linux_target_ops): Initialize new members.
(initialize_low): Add PTRACE_O_TRACEFORK ptrace option.
* linux-low.h (struct lwp_info) <waitstatus>: New member.
* lynx-low.c (lynx_target_ops): Initialize new members.
* nto-low.c (nto_target_ops): Initialize new members.
* remote-utils.c (prepare_resume_reply): New RSP stop reason
"fork" for 'T' stop reply.
* server.c (handle_query): Add vFollowFork packet to qSupported
response.
(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 members.
* target.h (struct target_ops) <supports_follow_fork>: New
member.
<follow_fork>: New member.
(target_supports_follow_fork): Define macro.
(target_follow_fork): Define macro.
* win32-low.c: (win32_target_ops): Initialize new members.
---
gdb/gdbserver/gdbthread.h | 5 +
gdb/gdbserver/linux-low.c | 222 +++++++++++++++++++++++++++++++++++++++---
gdb/gdbserver/linux-low.h | 5 +
gdb/gdbserver/lynx-low.c | 2 +
gdb/gdbserver/nto-low.c | 2 +
gdb/gdbserver/remote-utils.c | 14 +++-
gdb/gdbserver/server.c | 44 ++++++++
gdb/gdbserver/spu-low.c | 2 +
gdb/gdbserver/target.h | 16 +++
gdb/gdbserver/win32-low.c | 2 +
gdb/remote.c | 71 ++++++++++++--
11 files changed, 364 insertions(+), 21 deletions(-)
diff --git a/gdb/gdbserver/gdbthread.h b/gdb/gdbserver/gdbthread.h
index fe0a75e..3691772 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 2a346d9..9382141 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,54 @@ 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 +501,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
@@ -1191,7 +1245,8 @@ linux_enable_extended_features (void)
{
/* There is no process yet, so include extended options in the
base options for subsequent ptrace configuration. */
- linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE, 0);
+ linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE
+ | PTRACE_O_TRACEFORK, 0);
}
else
{
@@ -1201,6 +1256,110 @@ linux_enable_extended_features (void)
}
}
+/* Target routine to determine if follow-fork is supported. */
+
+static int
+linux_supports_follow_fork (void)
+{
+ /* Always returns true, although follow-fork is not supported unless
+ using the extended protocol. Since we may not know yet if we are
+ going to use the extended protocol, we assume that we will and
+ leave it to GDB and internal error checking to determine whether
+ we are in extended mode. */
+ return 1;
+}
+
+/* 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. */
+
+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;
+ 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_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 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
@@ -1899,8 +2058,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
@@ -2500,6 +2661,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
@@ -2863,7 +3037,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);
@@ -2885,6 +3060,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
@@ -2983,7 +3165,19 @@ 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_inferior->last_resume_kind == resume_stop
&& WSTOPSIG (w) == SIGSTOP)
@@ -3000,7 +3194,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));
}
@@ -4789,8 +4983,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. */
@@ -6043,6 +6237,8 @@ static struct target_ops linux_target_ops = {
linux_kill,
linux_detach,
linux_enable_extended_features,
+ linux_supports_follow_fork,
+ linux_follow_fork,
linux_mourn,
linux_join,
linux_thread_alive,
@@ -6158,5 +6354,5 @@ initialize_low (void)
initialize_low_arch ();
- linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE, 0);
+ linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE, PTRACE_O_TRACEFORK);
}
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index 4820929..bcdb713 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 may correspond to LAST_STATUS above,
+ or to 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 750503d..0234051 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -723,6 +723,8 @@ static struct target_ops lynx_target_ops = {
lynx_kill,
lynx_detach,
NULL, /* enable_extended_features */
+ NULL, /* supports_follow_fork */
+ 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 729328f..ec9ad97 100644
--- a/gdb/gdbserver/nto-low.c
+++ b/gdb/gdbserver/nto-low.c
@@ -930,6 +930,8 @@ static struct target_ops nto_target_ops = {
nto_kill,
nto_detach,
NULL, /* enable_extended_features */
+ NULL, /* supports_follow_fork */
+ 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 4fcafa0..a3b12dd 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1111,12 +1111,24 @@ prepare_resume_reply (char *buf, ptid_t ptid,
switch (status->kind)
{
case TARGET_WAITKIND_STOPPED:
+ case TARGET_WAITKIND_FORKED:
{
struct thread_info *saved_inferior;
const char **regp;
struct regcache *regcache;
+
+ 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);
- sprintf (buf, "T%02x", status->value.sig);
buf += strlen (buf);
saved_inferior = current_inferior;
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index e42e596..eb95dbf 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1916,6 +1916,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
strcat (own_buf, ";qXfer:threads:read+");
+ if (target_supports_follow_fork ())
+ strcat (own_buf, ";vFollowFork+");
+
if (target_supports_tracepoints ())
{
strcat (own_buf, ";ConditionalTracepoints+");
@@ -2540,6 +2543,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)
@@ -2605,6 +2631,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 cfd33b0..38d0bd2 100644
--- a/gdb/gdbserver/spu-low.c
+++ b/gdb/gdbserver/spu-low.c
@@ -646,6 +646,8 @@ static struct target_ops spu_target_ops = {
spu_kill,
spu_detach,
NULL, /* enable_extended_features */
+ NULL, /* supports_follow_fork */
+ NULL, /* follow_fork */
spu_mourn,
spu_join,
spu_thread_alive,
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index e09e885..7369eb0 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -96,6 +96,14 @@ struct target_ops
void (*enable_extended_features) (void);
+ /* Returns true if follow fork is supported. */
+ int (*supports_follow_fork) (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);
@@ -422,6 +430,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 d09cec3..fb920f4 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1769,6 +1769,8 @@ static struct target_ops win32_target_ops = {
win32_kill,
win32_detach,
NULL, /* enable_extended_features */
+ NULL, /* supports_follow_fork */
+ NULL, /* follow_fork */
win32_mourn,
win32_join,
win32_thread_alive,
diff --git a/gdb/remote.c b/gdb/remote.c
index fcddd1c..d9989db 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -25,6 +25,7 @@
#include <fcntl.h>
#include "inferior.h"
#include "infrun.h"
+#include "inf-child.h"
#include "bfd.h"
#include "symfile.h"
#include "exceptions.h"
@@ -111,6 +112,9 @@ static void remote_mourn (struct target_ops *ops);
static void extended_remote_restart (void);
+static int extended_remote_follow_fork (struct target_ops *ops,
+ int follow_child, int detach_fork);
+
static void extended_remote_mourn (struct target_ops *);
static void remote_mourn_1 (struct target_ops *);
@@ -1277,6 +1281,7 @@ enum {
PACKET_vRun,
PACKET_QStartNoAckMode,
PACKET_vKill,
+ PACKET_vFollowFork,
PACKET_qXfer_siginfo_read,
PACKET_qXfer_siginfo_write,
PACKET_qAttached,
@@ -3886,6 +3891,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,
PACKET_qXfer_siginfo_read },
@@ -4286,10 +4293,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."));
@@ -4326,19 +4335,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. */
@@ -5434,7 +5452,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);
@@ -5486,6 +5505,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. */
@@ -7773,6 +7797,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 (remote_protocol_packets[PACKET_vFollowFork].support != PACKET_DISABLE)
+ {
+ 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)
{
@@ -9299,8 +9349,11 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
if (ptid_equal (magic_null_ptid, ptid))
xsnprintf (buf, sizeof buf, "Thread <main>");
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));
@@ -11541,6 +11594,7 @@ init_extended_remote_ops (void)
Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_open = extended_remote_open;
extended_remote_ops.to_create_inferior = extended_remote_create_inferior;
+ extended_remote_ops.to_follow_fork = extended_remote_follow_fork;
extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
extended_remote_ops.to_detach = extended_remote_detach;
extended_remote_ops.to_attach = extended_remote_attach;
@@ -12057,6 +12111,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);
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 04/10] Enhance extended ptrace event setup
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
2014-08-07 18:00 ` [PATCH 03/10] Refactor extended ptrace event status Don Breazeal
2014-08-07 18:00 ` [PATCH 06/10] Extended-remote follow fork Don Breazeal
@ 2014-08-07 18:00 ` Don Breazeal
2014-08-13 17:50 ` Breazeal, Don
2014-08-07 18:00 ` [PATCH 01/10] Refactor native follow-fork Don Breazeal
` (23 subsequent siblings)
26 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-08-07 18:00 UTC (permalink / raw)
To: gdb-patches
This patch revises nat/linux-ptrace.c:linux_test_for_tracefork and linux_test_for_syscallgood. The change implements a mechanism for modifying the ptrace options after initialization is complete.
This is a variation on the patch that Gary Benson implemented to reduce the use of #ifdef GDBSERVER in linux-ptrace.c here:
https://sourceware.org/ml/gdb-patches/2014-07/msg00633.html.
This is needed because gdbserver doesn't always know whether it should enable extended-mode features until GDB connects. There is a new function that allows the caller to specify both "base" options and "additional" options, where base options are set at initialization, and additional options can be added later at the caller's discretion using another new function. The additional options can be used to enable options only available in extended-mode.
Tested on x64 Ubuntu Lucid, native only. The 'additional option' mechanism was tested as part of the patch 6, the follow-fork patch.
Thanks,
--Don
gdb/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* linux-nat.c (_initialize_linux_nat): Replace call to
linux_ptrace_set_additional_flags with call to
linux_ptrace_set_desired_options.
* nat/linux-ptrace.c (additional_flags): Delete static variable.
(base_ptrace_options): New static variable.
(additional_ptrace_options): New static variable.
(linux_test_for_tracesysgood, linux_test_for_tracefork): Use
base_ptrace_options and additional_ptrace_options.
(linux_ptrace_set_additional_options): Deleted function.
(linux_ptrace_set_desired_options): New function.
(linux_enable_additional_options): New function.
* nat/linux-ptrace.h (linux_ptrace_set_additional_flags):
Delete function declaration.
(linux_ptrace_set_desired_options): Declare new function.
(linux_enable_additional_options): Declare new function.
gdb/gdbserver/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* linux-low.c (linux_enable_extended_features): New function.
(initialize_low): Call linux_ptrace_set_desired_options.
(linux_target_ops): Initialize new member.
* server.c (process_serial_event): Call new target routine
enable_extended_features.
* target.h (struct target_ops) <enable_extended_features>: New
member.
* lynx-low.c (lynx_target_ops): Initialize new member.
* nto-low.c (nto_target_ops): Ditto.
* spu-low.c (spu_target_ops): Ditto.
* win32-low.c (win32_target_ops): Ditto.
---
gdb/gdbserver/linux-low.c | 27 ++++++++++++++
gdb/gdbserver/lynx-low.c | 1 +
gdb/gdbserver/nto-low.c | 1 +
gdb/gdbserver/server.c | 4 ++
gdb/gdbserver/spu-low.c | 1 +
gdb/gdbserver/target.h | 5 +++
gdb/gdbserver/win32-low.c | 1 +
gdb/linux-nat.c | 13 +++----
gdb/nat/linux-ptrace.c | 86 ++++++++++++++++++++++++++++++--------------
gdb/nat/linux-ptrace.h | 4 ++-
10 files changed, 107 insertions(+), 36 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index db22e92..2a346d9 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -1177,6 +1177,30 @@ linux_detach (int pid)
return 0;
}
+/* Enable extended mode features by enabling extended ptrace events. */
+
+static void
+linux_enable_extended_features (void)
+{
+ struct thread_info *tp = (struct thread_info *) current_inferior;
+
+ if (tp == NULL)
+ tp = get_first_thread ();
+
+ if (tp == NULL)
+ {
+ /* There is no process yet, so include extended options in the
+ base options for subsequent ptrace configuration. */
+ linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE, 0);
+ }
+ else
+ {
+ /* Re-initialize ptrace options with extended options. */
+ pid_t pid = ptid_get_lwp (tp->entry.id);
+ linux_enable_additional_options (pid);
+ }
+}
+
/* Remove all LWPs that belong to process PROC from the lwp list. */
static int
@@ -6018,6 +6042,7 @@ static struct target_ops linux_target_ops = {
linux_attach,
linux_kill,
linux_detach,
+ linux_enable_extended_features,
linux_mourn,
linux_join,
linux_thread_alive,
@@ -6132,4 +6157,6 @@ initialize_low (void)
sigaction (SIGCHLD, &sigchld_action, NULL);
initialize_low_arch ();
+
+ linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE, 0);
}
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 0b0ff47..750503d 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -722,6 +722,7 @@ static struct target_ops lynx_target_ops = {
lynx_attach,
lynx_kill,
lynx_detach,
+ NULL, /* enable_extended_features */
lynx_mourn,
lynx_join,
lynx_thread_alive,
diff --git a/gdb/gdbserver/nto-low.c b/gdb/gdbserver/nto-low.c
index 3587156..729328f 100644
--- a/gdb/gdbserver/nto-low.c
+++ b/gdb/gdbserver/nto-low.c
@@ -929,6 +929,7 @@ static struct target_ops nto_target_ops = {
nto_attach,
nto_kill,
nto_detach,
+ NULL, /* enable_extended_features */
nto_mourn,
NULL, /* nto_join */
nto_thread_alive,
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index cf1dffe..e42e596 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -3542,6 +3542,10 @@ process_serial_event (void)
break;
case '!':
extended_protocol = 1;
+
+ if (the_target->enable_extended_features != NULL)
+ (*the_target->enable_extended_features) ();
+
write_ok (own_buf);
break;
case '?':
diff --git a/gdb/gdbserver/spu-low.c b/gdb/gdbserver/spu-low.c
index 9bb0c40..cfd33b0 100644
--- a/gdb/gdbserver/spu-low.c
+++ b/gdb/gdbserver/spu-low.c
@@ -645,6 +645,7 @@ static struct target_ops spu_target_ops = {
spu_attach,
spu_kill,
spu_detach,
+ NULL, /* enable_extended_features */
spu_mourn,
spu_join,
spu_thread_alive,
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index f5eda8a..e09e885 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -92,11 +92,16 @@ struct target_ops
int (*detach) (int pid);
+ /* Enable target specific features for extended mode. */
+
+ void (*enable_extended_features) (void);
+
/* The inferior process has died. Do what is right. */
void (*mourn) (struct process_info *proc);
/* Wait for inferior PID to exit. */
+
void (*join) (int pid);
/* Return 1 iff the thread with process ID PID is alive. */
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index bc2506c..d09cec3 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1768,6 +1768,7 @@ static struct target_ops win32_target_ops = {
win32_attach,
win32_kill,
win32_detach,
+ NULL, /* enable_extended_features */
win32_mourn,
win32_join,
win32_thread_alive,
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index b72c066..bda535c 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -4849,13 +4849,12 @@ Enables printf debugging output."),
sigemptyset (&blocked_mask);
- /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
- support read-only process state. */
- linux_ptrace_set_additional_flags (PTRACE_O_TRACESYSGOOD
- | PTRACE_O_TRACEVFORKDONE
- | PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEEXEC);
+ linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE
+ | PTRACE_O_TRACESYSGOOD
+ | PTRACE_O_TRACEVFORKDONE
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEEXEC, 0);
}
\f
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 69eb83c..a9b80a0 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -37,9 +37,14 @@
there are no supported features. */
static int current_ptrace_options = -1;
-/* Additional flags to test. */
+/* Stores the ptrace options that the debugger wants enabled if
+ the operating system supports them. */
+static int base_ptrace_options;
-static int additional_flags;
+/* Stores ptrace options that the ptrace caller may wish to enable
+ after initial ptrace option checking is complete, e.g. for
+ gdbserver extended-remote targets. */
+static int additional_ptrace_options;
/* Find all possible reasons we could fail to attach PID and append
these as strings to the already initialized BUFFER. '\0'
@@ -357,22 +362,27 @@ linux_check_ptrace_features (void)
while (WIFSTOPPED (status));
}
-/* Determine if PTRACE_O_TRACESYSGOOD can be used to catch
- syscalls. */
+/* Determine if PTRACE_O_TRACESYSGOOD can be used to catch syscalls.
+ Enable if it is in the base options, otherwise save it. */
static void
linux_test_for_tracesysgood (int child_pid)
{
int ret;
- if ((additional_flags & PTRACE_O_TRACESYSGOOD) == 0)
+ /* Check if the caller want PTRACE_O_TRACESYSGOOD under any
+ circumstances. */
+ if (((base_ptrace_options | additional_ptrace_options)
+ & PTRACE_O_TRACESYSGOOD) == 0)
return;
ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
- (PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
+ (PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
if (ret == 0)
- current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
+ current_ptrace_options |= (PTRACE_O_TRACESYSGOOD & base_ptrace_options);
+ else
+ additional_ptrace_options &= ~PTRACE_O_TRACESYSGOOD;
}
/* Determine if PTRACE_O_TRACEFORK can be used to follow fork
@@ -392,15 +402,14 @@ linux_test_for_tracefork (int child_pid)
if (ret != 0)
return;
- if ((additional_flags & PTRACE_O_TRACEVFORKDONE) != 0)
- {
- /* Check if the target supports PTRACE_O_TRACEVFORKDONE. */
- ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
- (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEVFORKDONE));
- if (ret == 0)
- current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
- }
+ /* Check if the target supports PTRACE_O_TRACEVFORKDONE. */
+ ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
+ (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORKDONE));
+ if (ret == 0)
+ current_ptrace_options |= (PTRACE_O_TRACEVFORKDONE & base_ptrace_options);
+ else
+ additional_ptrace_options &= ~PTRACE_O_TRACEVFORKDONE;
/* Setting PTRACE_O_TRACEFORK did not cause an error, however we
don't know for sure that the feature is available; old
@@ -432,14 +441,19 @@ linux_test_for_tracefork (int child_pid)
(PTRACE_TYPE_ARG4) &second_pid);
if (ret == 0 && second_pid != 0)
{
+ /* We got the PID from the grandchild, which means fork
+ tracing and related options are supported by the OS. */
+ int supported_options = (PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACECLONE
+ | PTRACE_O_TRACEEXEC);
int second_status;
- /* We got the PID from the grandchild, which means fork
- tracing is supported. */
- current_ptrace_options |= PTRACE_O_TRACECLONE;
- current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEEXEC));
+ if (ret == 0)
+ current_ptrace_options |= (supported_options
+ & base_ptrace_options);
+ else
+ additional_ptrace_options &= ~supported_options;
/* Do some cleanup and kill the grandchild. */
my_waitpid (second_pid, &second_status, 0);
@@ -547,15 +561,31 @@ linux_ptrace_init_warnings (void)
linux_ptrace_test_ret_to_nx ();
}
-/* Set additional ptrace flags to use. Some such flags may be checked
- by the implementation above. This function must be called before
- any other function in this file; otherwise the flags may not take
- effect appropriately. */
+/* Set masks to be used for initializing ptrace options. Options in BASE
+ will always be enabled if the OS supports them. Options in ADDITIONAL
+ will only be enabled if linux_ptrace_enable_additional_options is
+ called. This function must be called before testing for ptrace
+ option support; otherwise the options may not be enabled. */
void
-linux_ptrace_set_additional_flags (int flags)
+linux_ptrace_set_desired_options (int base, int additional)
{
- additional_flags = flags;
+ base_ptrace_options = base;
+ additional_ptrace_options = additional;
+}
+
+/* Enable the additional ptrace options requested by a previous call to
+ linux_ptrace_set_desired_options. */
+
+void
+linux_enable_additional_options (pid_t pid)
+{
+ if (additional_ptrace_options != 0)
+ {
+ current_ptrace_options |= additional_ptrace_options;
+ ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
+ (PTRACE_TYPE_ARG4) (uintptr_t) current_ptrace_options);
+ }
}
/* Extract extended ptrace event from wait status. */
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 31a77cd..140a86b 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -91,7 +91,9 @@ extern int linux_supports_tracefork (void);
extern int linux_supports_traceclone (void);
extern int linux_supports_tracevforkdone (void);
extern int linux_supports_tracesysgood (void);
-extern void linux_ptrace_set_additional_flags (int);
+extern void linux_ptrace_set_desired_options (int base_options,
+ int additional_options);
+extern void linux_enable_additional_options (pid_t pid);
extern int linux_ptrace_get_extended_event (int wstat);
extern int linux_is_extended_waitstatus (int wstat);
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/10] Enhance extended ptrace event setup
2014-08-07 18:00 ` [PATCH 04/10] Enhance extended ptrace event setup Don Breazeal
@ 2014-08-13 17:50 ` Breazeal, Don
0 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-08-13 17:50 UTC (permalink / raw)
To: gdb-patches
Hi
It turns out that this patch doesn't work well for remote exec
catchpoints. In the event anybody is reviewing this patch series,
please don't spend any time on patch 4 until I post an updated version.
The rest of the patch series will remain relatively unchanged as a
result of this.
Sorry about that.
--Don
On 8/7/2014 10:59 AM, Don Breazeal wrote:
> This patch revises nat/linux-ptrace.c:linux_test_for_tracefork and linux_test_for_syscallgood. The change implements a mechanism for modifying the ptrace options after initialization is complete.
>
> This is a variation on the patch that Gary Benson implemented to reduce the use of #ifdef GDBSERVER in linux-ptrace.c here:
>
> https://sourceware.org/ml/gdb-patches/2014-07/msg00633.html.
>
> This is needed because gdbserver doesn't always know whether it should enable extended-mode features until GDB connects. There is a new function that allows the caller to specify both "base" options and "additional" options, where base options are set at initialization, and additional options can be added later at the caller's discretion using another new function. The additional options can be used to enable options only available in extended-mode.
>
> Tested on x64 Ubuntu Lucid, native only. The 'additional option' mechanism was tested as part of the patch 6, the follow-fork patch.
>
> Thanks,
> --Don
>
> gdb/
> 2014-08-06 Don Breazeal <donb@codesourcery.com>
> * linux-nat.c (_initialize_linux_nat): Replace call to
> linux_ptrace_set_additional_flags with call to
> linux_ptrace_set_desired_options.
> * nat/linux-ptrace.c (additional_flags): Delete static variable.
> (base_ptrace_options): New static variable.
> (additional_ptrace_options): New static variable.
> (linux_test_for_tracesysgood, linux_test_for_tracefork): Use
> base_ptrace_options and additional_ptrace_options.
> (linux_ptrace_set_additional_options): Deleted function.
> (linux_ptrace_set_desired_options): New function.
> (linux_enable_additional_options): New function.
> * nat/linux-ptrace.h (linux_ptrace_set_additional_flags):
> Delete function declaration.
> (linux_ptrace_set_desired_options): Declare new function.
> (linux_enable_additional_options): Declare new function.
>
> gdb/gdbserver/
> 2014-08-06 Don Breazeal <donb@codesourcery.com>
> * linux-low.c (linux_enable_extended_features): New function.
> (initialize_low): Call linux_ptrace_set_desired_options.
> (linux_target_ops): Initialize new member.
> * server.c (process_serial_event): Call new target routine
> enable_extended_features.
> * target.h (struct target_ops) <enable_extended_features>: New
> member.
> * lynx-low.c (lynx_target_ops): Initialize new member.
> * nto-low.c (nto_target_ops): Ditto.
> * spu-low.c (spu_target_ops): Ditto.
> * win32-low.c (win32_target_ops): Ditto.
>
> ---
> gdb/gdbserver/linux-low.c | 27 ++++++++++++++
> gdb/gdbserver/lynx-low.c | 1 +
> gdb/gdbserver/nto-low.c | 1 +
> gdb/gdbserver/server.c | 4 ++
> gdb/gdbserver/spu-low.c | 1 +
> gdb/gdbserver/target.h | 5 +++
> gdb/gdbserver/win32-low.c | 1 +
> gdb/linux-nat.c | 13 +++----
> gdb/nat/linux-ptrace.c | 86 ++++++++++++++++++++++++++++++--------------
> gdb/nat/linux-ptrace.h | 4 ++-
> 10 files changed, 107 insertions(+), 36 deletions(-)
>
> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index db22e92..2a346d9 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -1177,6 +1177,30 @@ linux_detach (int pid)
> return 0;
> }
>
> +/* Enable extended mode features by enabling extended ptrace events. */
> +
> +static void
> +linux_enable_extended_features (void)
> +{
> + struct thread_info *tp = (struct thread_info *) current_inferior;
> +
> + if (tp == NULL)
> + tp = get_first_thread ();
> +
> + if (tp == NULL)
> + {
> + /* There is no process yet, so include extended options in the
> + base options for subsequent ptrace configuration. */
> + linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE, 0);
> + }
> + else
> + {
> + /* Re-initialize ptrace options with extended options. */
> + pid_t pid = ptid_get_lwp (tp->entry.id);
> + linux_enable_additional_options (pid);
> + }
> +}
> +
> /* Remove all LWPs that belong to process PROC from the lwp list. */
>
> static int
> @@ -6018,6 +6042,7 @@ static struct target_ops linux_target_ops = {
> linux_attach,
> linux_kill,
> linux_detach,
> + linux_enable_extended_features,
> linux_mourn,
> linux_join,
> linux_thread_alive,
> @@ -6132,4 +6157,6 @@ initialize_low (void)
> sigaction (SIGCHLD, &sigchld_action, NULL);
>
> initialize_low_arch ();
> +
> + linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE, 0);
> }
> diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
> index 0b0ff47..750503d 100644
> --- a/gdb/gdbserver/lynx-low.c
> +++ b/gdb/gdbserver/lynx-low.c
> @@ -722,6 +722,7 @@ static struct target_ops lynx_target_ops = {
> lynx_attach,
> lynx_kill,
> lynx_detach,
> + NULL, /* enable_extended_features */
> lynx_mourn,
> lynx_join,
> lynx_thread_alive,
> diff --git a/gdb/gdbserver/nto-low.c b/gdb/gdbserver/nto-low.c
> index 3587156..729328f 100644
> --- a/gdb/gdbserver/nto-low.c
> +++ b/gdb/gdbserver/nto-low.c
> @@ -929,6 +929,7 @@ static struct target_ops nto_target_ops = {
> nto_attach,
> nto_kill,
> nto_detach,
> + NULL, /* enable_extended_features */
> nto_mourn,
> NULL, /* nto_join */
> nto_thread_alive,
> diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> index cf1dffe..e42e596 100644
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -3542,6 +3542,10 @@ process_serial_event (void)
> break;
> case '!':
> extended_protocol = 1;
> +
> + if (the_target->enable_extended_features != NULL)
> + (*the_target->enable_extended_features) ();
> +
> write_ok (own_buf);
> break;
> case '?':
> diff --git a/gdb/gdbserver/spu-low.c b/gdb/gdbserver/spu-low.c
> index 9bb0c40..cfd33b0 100644
> --- a/gdb/gdbserver/spu-low.c
> +++ b/gdb/gdbserver/spu-low.c
> @@ -645,6 +645,7 @@ static struct target_ops spu_target_ops = {
> spu_attach,
> spu_kill,
> spu_detach,
> + NULL, /* enable_extended_features */
> spu_mourn,
> spu_join,
> spu_thread_alive,
> diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
> index f5eda8a..e09e885 100644
> --- a/gdb/gdbserver/target.h
> +++ b/gdb/gdbserver/target.h
> @@ -92,11 +92,16 @@ struct target_ops
>
> int (*detach) (int pid);
>
> + /* Enable target specific features for extended mode. */
> +
> + void (*enable_extended_features) (void);
> +
> /* The inferior process has died. Do what is right. */
>
> void (*mourn) (struct process_info *proc);
>
> /* Wait for inferior PID to exit. */
> +
> void (*join) (int pid);
>
> /* Return 1 iff the thread with process ID PID is alive. */
> diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
> index bc2506c..d09cec3 100644
> --- a/gdb/gdbserver/win32-low.c
> +++ b/gdb/gdbserver/win32-low.c
> @@ -1768,6 +1768,7 @@ static struct target_ops win32_target_ops = {
> win32_attach,
> win32_kill,
> win32_detach,
> + NULL, /* enable_extended_features */
> win32_mourn,
> win32_join,
> win32_thread_alive,
> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
> index b72c066..bda535c 100644
> --- a/gdb/linux-nat.c
> +++ b/gdb/linux-nat.c
> @@ -4849,13 +4849,12 @@ Enables printf debugging output."),
>
> sigemptyset (&blocked_mask);
>
> - /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
> - support read-only process state. */
> - linux_ptrace_set_additional_flags (PTRACE_O_TRACESYSGOOD
> - | PTRACE_O_TRACEVFORKDONE
> - | PTRACE_O_TRACEVFORK
> - | PTRACE_O_TRACEFORK
> - | PTRACE_O_TRACEEXEC);
> + linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE
> + | PTRACE_O_TRACESYSGOOD
> + | PTRACE_O_TRACEVFORKDONE
> + | PTRACE_O_TRACEVFORK
> + | PTRACE_O_TRACEFORK
> + | PTRACE_O_TRACEEXEC, 0);
> }
> \f
>
> diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
> index 69eb83c..a9b80a0 100644
> --- a/gdb/nat/linux-ptrace.c
> +++ b/gdb/nat/linux-ptrace.c
> @@ -37,9 +37,14 @@
> there are no supported features. */
> static int current_ptrace_options = -1;
>
> -/* Additional flags to test. */
> +/* Stores the ptrace options that the debugger wants enabled if
> + the operating system supports them. */
> +static int base_ptrace_options;
>
> -static int additional_flags;
> +/* Stores ptrace options that the ptrace caller may wish to enable
> + after initial ptrace option checking is complete, e.g. for
> + gdbserver extended-remote targets. */
> +static int additional_ptrace_options;
>
> /* Find all possible reasons we could fail to attach PID and append
> these as strings to the already initialized BUFFER. '\0'
> @@ -357,22 +362,27 @@ linux_check_ptrace_features (void)
> while (WIFSTOPPED (status));
> }
>
> -/* Determine if PTRACE_O_TRACESYSGOOD can be used to catch
> - syscalls. */
> +/* Determine if PTRACE_O_TRACESYSGOOD can be used to catch syscalls.
> + Enable if it is in the base options, otherwise save it. */
>
> static void
> linux_test_for_tracesysgood (int child_pid)
> {
> int ret;
>
> - if ((additional_flags & PTRACE_O_TRACESYSGOOD) == 0)
> + /* Check if the caller want PTRACE_O_TRACESYSGOOD under any
> + circumstances. */
> + if (((base_ptrace_options | additional_ptrace_options)
> + & PTRACE_O_TRACESYSGOOD) == 0)
> return;
>
> ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
> - (PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
> + (PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
>
> if (ret == 0)
> - current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
> + current_ptrace_options |= (PTRACE_O_TRACESYSGOOD & base_ptrace_options);
> + else
> + additional_ptrace_options &= ~PTRACE_O_TRACESYSGOOD;
> }
>
> /* Determine if PTRACE_O_TRACEFORK can be used to follow fork
> @@ -392,15 +402,14 @@ linux_test_for_tracefork (int child_pid)
> if (ret != 0)
> return;
>
> - if ((additional_flags & PTRACE_O_TRACEVFORKDONE) != 0)
> - {
> - /* Check if the target supports PTRACE_O_TRACEVFORKDONE. */
> - ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
> - (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
> - | PTRACE_O_TRACEVFORKDONE));
> - if (ret == 0)
> - current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
> - }
> + /* Check if the target supports PTRACE_O_TRACEVFORKDONE. */
> + ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
> + (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
> + | PTRACE_O_TRACEVFORKDONE));
> + if (ret == 0)
> + current_ptrace_options |= (PTRACE_O_TRACEVFORKDONE & base_ptrace_options);
> + else
> + additional_ptrace_options &= ~PTRACE_O_TRACEVFORKDONE;
>
> /* Setting PTRACE_O_TRACEFORK did not cause an error, however we
> don't know for sure that the feature is available; old
> @@ -432,14 +441,19 @@ linux_test_for_tracefork (int child_pid)
> (PTRACE_TYPE_ARG4) &second_pid);
> if (ret == 0 && second_pid != 0)
> {
> + /* We got the PID from the grandchild, which means fork
> + tracing and related options are supported by the OS. */
> + int supported_options = (PTRACE_O_TRACEFORK
> + | PTRACE_O_TRACEVFORK
> + | PTRACE_O_TRACECLONE
> + | PTRACE_O_TRACEEXEC);
> int second_status;
>
> - /* We got the PID from the grandchild, which means fork
> - tracing is supported. */
> - current_ptrace_options |= PTRACE_O_TRACECLONE;
> - current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
> - | PTRACE_O_TRACEVFORK
> - | PTRACE_O_TRACEEXEC));
> + if (ret == 0)
> + current_ptrace_options |= (supported_options
> + & base_ptrace_options);
> + else
> + additional_ptrace_options &= ~supported_options;
>
> /* Do some cleanup and kill the grandchild. */
> my_waitpid (second_pid, &second_status, 0);
> @@ -547,15 +561,31 @@ linux_ptrace_init_warnings (void)
> linux_ptrace_test_ret_to_nx ();
> }
>
> -/* Set additional ptrace flags to use. Some such flags may be checked
> - by the implementation above. This function must be called before
> - any other function in this file; otherwise the flags may not take
> - effect appropriately. */
> +/* Set masks to be used for initializing ptrace options. Options in BASE
> + will always be enabled if the OS supports them. Options in ADDITIONAL
> + will only be enabled if linux_ptrace_enable_additional_options is
> + called. This function must be called before testing for ptrace
> + option support; otherwise the options may not be enabled. */
>
> void
> -linux_ptrace_set_additional_flags (int flags)
> +linux_ptrace_set_desired_options (int base, int additional)
> {
> - additional_flags = flags;
> + base_ptrace_options = base;
> + additional_ptrace_options = additional;
> +}
> +
> +/* Enable the additional ptrace options requested by a previous call to
> + linux_ptrace_set_desired_options. */
> +
> +void
> +linux_enable_additional_options (pid_t pid)
> +{
> + if (additional_ptrace_options != 0)
> + {
> + current_ptrace_options |= additional_ptrace_options;
> + ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
> + (PTRACE_TYPE_ARG4) (uintptr_t) current_ptrace_options);
> + }
> }
>
> /* Extract extended ptrace event from wait status. */
> diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
> index 31a77cd..140a86b 100644
> --- a/gdb/nat/linux-ptrace.h
> +++ b/gdb/nat/linux-ptrace.h
> @@ -91,7 +91,9 @@ extern int linux_supports_tracefork (void);
> extern int linux_supports_traceclone (void);
> extern int linux_supports_tracevforkdone (void);
> extern int linux_supports_tracesysgood (void);
> -extern void linux_ptrace_set_additional_flags (int);
> +extern void linux_ptrace_set_desired_options (int base_options,
> + int additional_options);
> +extern void linux_enable_additional_options (pid_t pid);
> extern int linux_ptrace_get_extended_event (int wstat);
> extern int linux_is_extended_waitstatus (int wstat);
>
>
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 01/10] Refactor native follow-fork
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (2 preceding siblings ...)
2014-08-07 18:00 ` [PATCH 04/10] Enhance extended ptrace event setup Don Breazeal
@ 2014-08-07 18:00 ` Don Breazeal
2014-08-07 18:00 ` [PATCH 07/10] Extended-remote arch-specific follow fork Don Breazeal
` (22 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-07 18:00 UTC (permalink / raw)
To: gdb-patches
This patch reorganizes some of 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 contains target-independent code mixed in with target-dependent code. The target-independent pieces need to be accessible in order to implement the remote target without duplicating them.
The changes are fairly mechanical. A new routine, follow_fork_inferior, was 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. Once the target-independent pieces were removed from linux_child_follow_fork, it was possible to consolidate it a bit.
Tested on x64 Ubuntu Lucid, native only.
Thanks,
--Don
gdb/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* infrun.c (follow_fork): Call follow_fork_inferior.
(follow_fork_inferior): New function.
(follow_inferior_reset_breakpoints): Make function static.
* infrun.h: Remove declaration of follow_inferior_reset_breakpoints.
* linux-nat.c (linux_child_follow_fork): Move target-independent
code to infrun.c:follow_fork_inferior.
---
gdb/infrun.c | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
gdb/infrun.h | 2 -
gdb/linux-nat.c | 216 ++---------------------------------------------
3 files changed, 254 insertions(+), 212 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 33aa674..1de14ed 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -62,6 +62,7 @@
#include "completer.h"
#include "target-descriptions.h"
#include "target-dcache.h"
+#include "terminal.h"
/* Prototypes for local functions */
@@ -81,6 +82,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);
@@ -488,9 +493,11 @@ 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_follow_fork (follow_child, detach_fork))
{
/* Target refused to follow, or there's some other reason
we shouldn't resume. */
@@ -562,7 +569,240 @@ follow_fork (void)
return should_resume;
}
-void
+/* 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. */
+
+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 (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 (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 0;
+}
+
+static void
follow_inferior_reset_breakpoints (void)
{
struct thread_info *tp = inferior_thread ();
diff --git a/gdb/infrun.h b/gdb/infrun.h
index 35b2246..8c1350a 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -116,8 +116,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 5a791bc..7788c75 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -56,7 +56,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"
@@ -386,64 +385,22 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child,
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;
/* 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)
@@ -482,82 +439,15 @@ holding the child stopped. Try \"set detach-on-fork\" or \
}
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);
@@ -630,98 +520,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 ();
}
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 07/10] Extended-remote arch-specific follow fork
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (3 preceding siblings ...)
2014-08-07 18:00 ` [PATCH 01/10] Refactor native follow-fork Don Breazeal
@ 2014-08-07 18:00 ` Don Breazeal
2014-08-07 18:00 ` [PATCH 05/10] GDBserver clone breakpoint list Don Breazeal
` (21 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-07 18:00 UTC (permalink / raw)
To: gdb-patches
This patch implements the architecture-specific pieces of follow-fork, which in the current implementation copyies the parent's debug register state into the new child's data structures. This is required for x86, arm, aarch64, and mips.
I followed the native implementation as closely as I could by implementing a new linux_target_ops function 'new_fork', which is analogous to 'linux_new_fork' in linux-nat.c. In gdbserver, the debug registers are stored in the process list, instead of an architecture-specific list, so the function arguments are process_info pointers instead of an lwp_info and a pid as in the native implementation.
In the MIPS implementation the debug register mirror is stored differently from x86, ARM, and aarch64, so instead of doing a simple structure assignment I had to clone the list of watchpoint structures.
Tested using gdb.threads/watchpoint-fork.exp on x86, and ran manual tests on a MIPS board.
I ran manual tests on an ARM board, but on the setup I had, hardware watchpoints did not work, even in the unmodified version of the debugger. The kernel on the board should have been new enough to provide the support. I didn't debug it further at the time. If someone is able to test this easily, please do.
I don't currently have access to an aarch64 board, so again if someone is able to test this easily, please do.
Thanks,
--Don
gdb/gdbserver/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* linux-aarch64-low.c (aarch64_linux_new_fork): New function.
(the_low_target) <new_fork>: Initialize new member.
* linux-arm-low.c (arm_new_fork): New function.
(the_low_target) <new_fork>: Initialize new member.
* linux-low.c (handle_extended_wait): Call new target function.
* linux-low.h (struct linux_target_ops) <new_fork>: New member.
* linux-mips-low.c (mips_add_watchpoint): New function.
(the_low_target) <new_fork>: Initialize new member.
(mips_linux_new_fork): New function.
(mips_insert_point): Call mips_add_watchpoint.
* linux-x86-low.c (x86_linux_new_fork): New function.
---
gdb/gdbserver/linux-aarch64-low.c | 28 +++++++++++++
gdb/gdbserver/linux-arm-low.c | 26 +++++++++++++
gdb/gdbserver/linux-low.c | 4 ++
gdb/gdbserver/linux-low.h | 3 +
gdb/gdbserver/linux-mips-low.c | 76 +++++++++++++++++++++++++++++++------
gdb/gdbserver/linux-x86-low.c | 29 ++++++++++++++
6 files changed, 154 insertions(+), 12 deletions(-)
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 6066e15..915bc21 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -1132,6 +1132,33 @@ aarch64_linux_new_thread (void)
return info;
}
+static void
+aarch64_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
@@ -1296,6 +1323,7 @@ struct linux_target_ops the_low_target =
NULL,
aarch64_linux_new_process,
aarch64_linux_new_thread,
+ aarch64_linux_new_fork,
aarch64_linux_prepare_to_resume,
};
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index c4cfbd4..bfc9c24 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -717,6 +717,31 @@ arm_new_thread (void)
return info;
}
+static void
+arm_new_fork (struct process_info *parent, struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
static void
@@ -920,6 +945,7 @@ struct linux_target_ops the_low_target = {
NULL, /* siginfo_fixup */
arm_new_process,
arm_new_thread,
+ arm_new_fork,
arm_prepare_to_resume,
};
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 9382141..4958fdb 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -440,6 +440,10 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
child_proc->tdesc = tdesc;
child_lwp->must_set_ptrace_flags = 1;
+ /* 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 target processing. */
current_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED;
current_inferior->pending_follow.value.related_pid = ptid;
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index bcdb713..be0285b 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -185,6 +185,9 @@ struct linux_target_ops
allocate it here. */
struct arch_lwp_info * (*new_thread) (void);
+ /* Hook to call, if any, when a new fork is attached. */
+ void (*new_fork) (struct process_info *parent, struct process_info *child);
+
/* Hook to call prior to resuming a thread. */
void (*prepare_to_resume) (struct lwp_info *);
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index 1b2160b..693a653 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -343,6 +343,68 @@ mips_linux_new_thread (void)
return info;
}
+/* Create a new mips_watchpoint and add it to the list. */
+
+static void
+mips_add_watchpoint (struct arch_process_info *private, CORE_ADDR addr,
+ int len, int watch_type)
+{
+ struct mips_watchpoint *new_watch;
+ struct mips_watchpoint **pw;
+
+ new_watch = xmalloc (sizeof (struct mips_watchpoint));
+ new_watch->addr = addr;
+ new_watch->len = len;
+ new_watch->type = watch_type;
+ new_watch->next = NULL;
+
+ pw = &private->current_watches;
+ while (*pw != NULL)
+ pw = &(*pw)->next;
+ *pw = new_watch;
+}
+
+/* Hook to call when a new fork is attached. */
+
+static void
+mips_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ struct arch_process_info *parent_private;
+ struct arch_process_info *child_private;
+ struct mips_watchpoint *wp;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ parent_private = parent->private->arch_private;
+ child_private = child->private->arch_private;
+
+ child_private->watch_readback_valid = parent_private->watch_readback_valid;
+ child_private->watch_readback = parent_private->watch_readback;
+
+ for (wp = parent_private->current_watches; wp != NULL; wp = wp->next)
+ mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type);
+
+ child_private->watch_mirror = parent_private->watch_mirror;
+}
/* This is the implementation of linux_target_ops method
prepare_to_resume. If the watch regs have changed, update the
thread's copies. */
@@ -396,8 +458,6 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
struct process_info *proc = current_process ();
struct arch_process_info *private = proc->private->arch_private;
struct pt_watch_regs regs;
- struct mips_watchpoint *new_watch;
- struct mips_watchpoint **pw;
int pid;
long lwpid;
enum target_hw_bp_type watch_type;
@@ -424,16 +484,7 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
return -1;
/* It fit. Stick it on the end of the list. */
- new_watch = xmalloc (sizeof (struct mips_watchpoint));
- new_watch->addr = addr;
- new_watch->len = len;
- new_watch->type = watch_type;
- new_watch->next = NULL;
-
- pw = &private->current_watches;
- while (*pw != NULL)
- pw = &(*pw)->next;
- *pw = new_watch;
+ mips_add_watchpoint (private, addr, len, watch_type);
private->watch_mirror = regs;
@@ -844,6 +895,7 @@ struct linux_target_ops the_low_target = {
NULL, /* siginfo_fixup */
mips_linux_new_process,
mips_linux_new_thread,
+ mips_linux_new_fork,
mips_linux_prepare_to_resume
};
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 850ed7c..4f2b30c 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -771,6 +771,34 @@ x86_linux_new_thread (void)
return info;
}
+/* linux_new_fork hook. */
+
+static void
+x86_linux_new_fork (struct process_info *parent, struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
@@ -3433,6 +3461,7 @@ struct linux_target_ops the_low_target =
x86_siginfo_fixup,
x86_linux_new_process,
x86_linux_new_thread,
+ x86_linux_new_fork,
x86_linux_prepare_to_resume,
x86_linux_process_qsupported,
x86_supports_tracepoints,
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 05/10] GDBserver clone breakpoint list
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (4 preceding siblings ...)
2014-08-07 18:00 ` [PATCH 07/10] Extended-remote arch-specific follow fork Don Breazeal
@ 2014-08-07 18:00 ` Don Breazeal
2014-08-07 18:00 ` [PATCH 02/10] Refactor follow-fork message printing Don Breazeal
` (20 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-07 18:00 UTC (permalink / raw)
To: gdb-patches
This patch implements gdbserver routines to clone the breakpoint lists of a process, duplicating them for another process. In gdbserver, each process maintains its own independent breakpoint list. When a fork call creates a child, all of the breakpoints currently inserted in the parent process are also inserted in the child process, but there is nothing to describe them in the context of the child. The child must have a breakpoint list describing them so that they can be removed (if detaching) or recognized (if following). Implementation is a mechanical process of just cloning the lists in several new functions in gdbserver/mem-break.c.
Tested by building, since none of the new functions are called yet. This was tested with the subsequent patch 6, the follow-fork patch.
Thanks,
--Don
gdb/gdbserver/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* mem-break.c (APPEND_TO_LIST): Define macro.
(clone_agent_expr): New function.
(clone_one_breakpoint): New function.
(clone_all_breakpoints): New function.
* mem-break.h (clone_all_breakpoints): Declare.
---
gdb/gdbserver/mem-break.c | 104 +++++++++++++++++++++++++++++++++++++++++++++
gdb/gdbserver/mem-break.h | 6 +++
2 files changed, 110 insertions(+), 0 deletions(-)
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index 2ce3ab2..d433493 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -28,6 +28,22 @@ int breakpoint_len;
#define MAX_BREAKPOINT_LEN 8
+/* Helper macro used in loops that append multiple items to a singly-linked
+ list instead of inserting items at the head of the list, as, say, in the
+ breakpoint lists. LISTPP is a pointer to the pointer that is the head of
+ the new list. ITEMP is a pointer to the item to be added to the list.
+ TAILP must be defined to be the same type as ITEMP, and initialized to
+ NULL. */
+
+#define APPEND_TO_LIST(listpp, itemp, tailp) \
+ { \
+ if (tailp == NULL) \
+ *(listpp) = itemp; \
+ else \
+ tailp->next = itemp; \
+ tailp = itemp; \
+ }
+
/* GDB will never try to install multiple breakpoints at the same
address. However, we can see GDB requesting to insert a breakpoint
at an address is had already inserted one previously in a few
@@ -1878,3 +1894,91 @@ free_all_breakpoints (struct process_info *proc)
while (proc->breakpoints)
delete_breakpoint_1 (proc, proc->breakpoints);
}
+
+/* Clone an agent expression. */
+
+static void
+clone_agent_expr (struct agent_expr **new_ax, struct agent_expr *src_ax)
+{
+ struct agent_expr *ax;
+
+ ax = xcalloc (1, sizeof (*ax));
+ ax->length = src_ax->length;
+ ax->bytes = xcalloc (ax->length, 1);
+ memcpy (ax->bytes, src_ax->bytes, ax->length);
+ *new_ax = ax;
+}
+
+/* Deep-copy the contents of one breakpoint to another. */
+
+static void
+clone_one_breakpoint (struct breakpoint **new_bkpt,
+ struct raw_breakpoint **new_raw, struct breakpoint *src)
+{
+ struct breakpoint *dest;
+ struct raw_breakpoint *dest_raw;
+ struct point_cond_list *current_cond;
+ struct point_cond_list *new_cond;
+ struct point_cond_list *cond_tail = NULL;
+ struct point_command_list *current_cmd;
+ struct point_command_list *new_cmd;
+ struct point_command_list *cmd_tail = NULL;
+
+ /* Clone the raw breakpoint. */
+ dest_raw = xcalloc (1, sizeof (*dest_raw));
+ dest_raw->raw_type = src->raw->raw_type;
+ dest_raw->refcount = src->raw->refcount;
+ dest_raw->pc = src->raw->pc;
+ dest_raw->size = src->raw->size;
+ memcpy (dest_raw->old_data, src->raw->old_data, MAX_BREAKPOINT_LEN);
+ dest_raw->inserted = src->raw->inserted;
+
+ /* Clone the high-level breakpoint. */
+ dest = xcalloc (1, sizeof (*dest));
+ dest->type = src->type;
+ dest->raw = dest_raw;
+ dest->handler = src->handler;
+
+ /* Clone the condition list. */
+ for (current_cond = src->cond_list; current_cond != NULL;
+ current_cond = current_cond->next)
+ {
+ new_cond = xcalloc (1, sizeof (*new_cond));
+ clone_agent_expr (&new_cond->cond, current_cond->cond);
+ APPEND_TO_LIST (&dest->cond_list, new_cond, cond_tail);
+ }
+
+ /* Clone the command list. */
+ for (current_cmd = src->command_list; current_cmd != NULL;
+ current_cmd = current_cmd->next)
+ {
+ new_cmd = xcalloc (1, sizeof (*new_cmd));
+ clone_agent_expr (&new_cmd->cmd, current_cmd->cmd);
+ new_cmd->persistence = current_cmd->persistence;
+ APPEND_TO_LIST (&dest->command_list, new_cmd, cmd_tail);
+ }
+ *new_bkpt = dest;
+ *new_raw = dest_raw;
+}
+
+/* Create a new breakpoint list NEW_LIST that is a copy of SRC. Create
+ the corresponding new raw_breakpoint list NEW_RAW_LIST as well. */
+
+void
+clone_all_breakpoints (struct breakpoint **new_list,
+ struct raw_breakpoint **new_raw_list,
+ struct breakpoint *src)
+{
+ struct breakpoint *bp;
+ struct breakpoint *new_bkpt;
+ struct raw_breakpoint *new_raw_bkpt;
+ struct breakpoint *bkpt_tail = NULL;
+ struct raw_breakpoint *raw_bkpt_tail = NULL;
+
+ for (bp = src; bp != NULL; bp = bp->next)
+ {
+ clone_one_breakpoint (&new_bkpt, &new_raw_bkpt, bp);
+ APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);
+ APPEND_TO_LIST (new_raw_list, new_raw_bkpt, raw_bkpt_tail);
+ }
+}
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index c84c688..439792f 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -243,4 +243,10 @@ int insert_memory_breakpoint (struct raw_breakpoint *bp);
int remove_memory_breakpoint (struct raw_breakpoint *bp);
+/* Create a new breakpoint list NEW_BKPT_LIST that is a copy of SRC. */
+
+void clone_all_breakpoints (struct breakpoint **new_bkpt_list,
+ struct raw_breakpoint **new_raw_bkpt_list,
+ struct breakpoint *src);
+
#endif /* MEM_BREAK_H */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 02/10] Refactor follow-fork message printing
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (5 preceding siblings ...)
2014-08-07 18:00 ` [PATCH 05/10] GDBserver clone breakpoint list Don Breazeal
@ 2014-08-07 18:00 ` Don Breazeal
2014-08-07 18:01 ` [PATCH 08/10] Extended-remote follow vfork Don Breazeal
` (19 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-07 18:00 UTC (permalink / raw)
To: gdb-patches
This patch refactors the code that prints messages related to follow-fork into functions, and adds a call so that a message is now printed when the parent process is detached. Previously in this case the only message was notification of attaching to the child. We still do not print any messages when following the parent and detaching the child (the default). My rationale for this is that from the user's perspective the new child was never attached.
Note that all of these messages are only printed when 'verbose' is set or when debugging is turned on.
This is preparatory work for follow-fork and detach-on-fork on extended-remote linux targets.
The test gdb.base/foll-fork.exp was modified to check for the new message.
Tested on x64 Ubuntu Lucid, native only.
Thanks
--Don
gdb/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* infrun.c (print_fork_attach): New function.
(print_fork_detach): New function.
(follow_fork_inferior): Call print_fork_attach and print_fork_detach.
(handle_vfork_child_exec_or_exit): Ditto.
gdb/testsuite/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* gdb.base/foll-fork.exp (test_follow_fork): Add check for new
detach message.
(catch_fork_child_follow): Ditto.
* gdb.base/foll-vfork.exp (vfork_parent_follow_through_step):
Modify to check for "vfork" instead of "fork".
(vfork_parent_follow_to_bp): Ditto.
(vfork_and_exec_child_follow_through_step): Ditto.
(vfork_and_exec_child_follow_to_main_bp): Ditto, plus add check
for new detach message.
---
gdb/infrun.c | 94 +++++++++++++++++++-------------
gdb/testsuite/gdb.base/foll-fork.exp | 12 +++--
gdb/testsuite/gdb.base/foll-vfork.exp | 8 ++--
3 files changed, 68 insertions(+), 46 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 1de14ed..482fa68 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -569,6 +569,49 @@ follow_fork (void)
return should_resume;
}
+/* Print details about attaching to a process after a fork call. */
+
+static void
+print_fork_attach (pid_t child_pid, pid_t parent_pid, int is_vfork)
+{
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours ();
+ fprintf_filtered (gdb_stdlog,
+ _("Attaching after process %d "
+ "%s to child process %d.\n"),
+ parent_pid, is_vfork?"vfork":"fork", child_pid);
+ }
+}
+
+/* Print details about detaching from a process after a fork call. */
+
+static void
+print_fork_detach (pid_t pid, int is_parent, int is_vfork, char *vfork_action)
+{
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours ();
+
+ if (is_parent && is_vfork)
+ {
+ /* Detaching a vfork parent, so print what the child did
+ that allows the parent to resume. */
+ gdb_assert (vfork_action != NULL && strlen (vfork_action) > 0);
+ fprintf_filtered (gdb_stdlog,
+ "Detaching vfork parent process %d after"
+ " child %s.\n", pid, vfork_action);
+ }
+ else
+ {
+ fprintf_filtered (gdb_stdlog,
+ _("Detaching after %s from %s process %d.\n"),
+ is_vfork?"vfork":"fork",
+ is_parent?"parent":"child", pid);
+ }
+ }
+}
+
/* 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. */
@@ -625,14 +668,7 @@ holding the child stopped. Try \"set detach-on-fork\" or \
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);
- }
+ print_fork_detach (child_pid, follow_child, has_vforked, "");
}
else
{
@@ -713,20 +749,7 @@ holding the child stopped. Try \"set detach-on-fork\" or \
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);
- }
+ print_fork_attach (child_pid, parent_pid, has_vforked);
/* Add the new inferior first, so that the target_detach below
doesn't unpush the target. */
@@ -762,7 +785,10 @@ holding the child stopped. Try \"set detach-on-fork\" or \
parent_inf->waiting_for_vfork_done = 0;
}
else if (detach_fork)
- target_detach (NULL, 0);
+ {
+ print_fork_detach (parent_pid, follow_child, has_vforked, "");
+ target_detach (NULL, 0);
+ }
/* Note that the detach above makes PARENT_INF dangling. */
@@ -929,21 +955,13 @@ handle_vfork_child_exec_or_exit (int exec)
inf->aspace = NULL;
inf->pspace = NULL;
- if (debug_infrun || info_verbose)
- {
- target_terminal_ours ();
-
- if (exec)
- fprintf_filtered (gdb_stdlog,
- "Detaching vfork parent process "
- "%d after child exec.\n",
- inf->vfork_parent->pid);
- else
- fprintf_filtered (gdb_stdlog,
- "Detaching vfork parent process "
- "%d after child exit.\n",
- inf->vfork_parent->pid);
- }
+ /* Print verbose/debug info message. Hardcoded 1's designate
+ that we are detaching a parent and that it is after a vfork,
+ respectively. */
+ if (exec)
+ print_fork_detach (inf->vfork_parent->pid, 1, 1, "exec");
+ else
+ print_fork_detach (inf->vfork_parent->pid, 1, 1, "exit");
target_detach (NULL, 0);
diff --git a/gdb/testsuite/gdb.base/foll-fork.exp b/gdb/testsuite/gdb.base/foll-fork.exp
index ad8b750..06ba1b5 100644
--- a/gdb/testsuite/gdb.base/foll-fork.exp
+++ b/gdb/testsuite/gdb.base/foll-fork.exp
@@ -115,7 +115,11 @@ proc test_follow_fork { who detach cmd } {
# Set up the output we expect to see after we run.
set expected_re ""
if {$who == "child"} {
- set expected_re "Attaching after.* fork to.*set breakpoint here.*"
+ set expected_re "Attaching after.* fork to.*"
+ if {$detach == "on"} {
+ append expected_re "Detaching after fork from .*"
+ }
+ append expected_re "set breakpoint here.*"
} elseif {$who == "parent" && $detach == "on"} {
set expected_re "Detaching after fork from .*set breakpoint here.*"
} else {
@@ -218,9 +222,9 @@ proc catch_fork_child_follow {} {
"Temporary breakpoint.*, line $bp_after_fork.*" \
"set follow-fork child, tbreak"
- gdb_test "continue" \
- "Attaching after.* fork to.* at .*$bp_after_fork.*" \
- "set follow-fork child, hit tbreak"
+ set expected_re "Attaching after.* fork to.*Detaching after fork from"
+ append expected_re " .* at .*$bp_after_fork.*"
+ gdb_test "continue" $expected_re "set follow-fork child, hit tbreak"
# The parent has been detached; allow time for any output it might
# generate to arrive, so that output doesn't get confused with
diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp b/gdb/testsuite/gdb.base/foll-vfork.exp
index fe3663c..968db13 100644
--- a/gdb/testsuite/gdb.base/foll-vfork.exp
+++ b/gdb/testsuite/gdb.base/foll-vfork.exp
@@ -121,7 +121,7 @@ proc vfork_parent_follow_through_step {} {
set test "step"
gdb_test_multiple "next" $test {
- -re "Detaching after fork from.*if \\(pid == 0\\).*$gdb_prompt " {
+ -re "Detaching after vfork from.*if \\(pid == 0\\).*$gdb_prompt " {
pass $test
}
}
@@ -146,7 +146,7 @@ proc vfork_parent_follow_to_bp {} {
set test "continue to bp"
gdb_test_multiple "continue" $test {
- -re ".*Detaching after fork from child process.*Breakpoint.*${bp_location}.*$gdb_prompt " {
+ -re ".*Detaching after vfork from child process.*Breakpoint.*${bp_location}.*$gdb_prompt " {
pass $test
}
}
@@ -195,7 +195,7 @@ proc vfork_and_exec_child_follow_to_main_bp {} {
set test "continue to bp"
gdb_test_multiple "continue" $test {
- -re "Attaching after.* vfork to.*xecuting new program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
+ -re "Attaching after.* vfork to.*Detaching vfork parent.*xecuting new program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
pass $test
}
}
@@ -239,7 +239,7 @@ proc vfork_and_exec_child_follow_through_step {} {
#
set linenum [gdb_get_line_number "printf(\"Hello from vforked-prog" ${srcfile2}]
gdb_test_multiple "next" $test {
- -re "Attaching after fork to.*Executing new program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
+ -re "Attaching after vfork to.*Executing new program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
pass "$test"
}
}
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 08/10] Extended-remote follow vfork
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (6 preceding siblings ...)
2014-08-07 18:00 ` [PATCH 02/10] Refactor follow-fork message printing Don Breazeal
@ 2014-08-07 18:01 ` Don Breazeal
2014-08-07 18:01 ` [PATCH 10/10] Extended-remote fork event documentation Don Breazeal
` (18 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-07 18:01 UTC (permalink / raw)
To: gdb-patches
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/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* remote.c: New stop reasons "vfork"
and "vforkdone" for RSP 'T' Stop Reply Packet.
gdb/gdbserver/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* linux-low.c (handle_extended_wait): Handle PTRACE_EVENT_VFORK and
PTRACE_EVENT_VFORK_DONE.
(linux_enable_extended_features): Enable PTRACE_EVENT_VFORK and
PTRACE_EVENT_VFORK_DONE.
(linux_follow_fork): Handle following a vfork.
(extended_event_reported): Add vfork and vfork-done events to
the list of extended events.
(initialize_low): Enable PTRACE_EVENT_VFORK and
PTRACE_EVENT_VFORK_DONE.
* remote-utils.c (prepare_resume_reply): New stop reasons "vfork"
and "vforkdone" for RSP 'T' Stop Reply Packet.
---
gdb/gdbserver/linux-low.c | 107 ++++++++++++++++++++++++++++++++++++------
gdb/gdbserver/remote-utils.c | 16 ++++++-
gdb/remote.c | 15 ++++++
3 files changed, 121 insertions(+), 17 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 4958fdb..69cad15 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -210,6 +210,7 @@ int using_threads = 1;
jump pads). */
static int stabilizing_threads;
+static void async_file_mark (void);
static void linux_resume_one_lwp (struct lwp_info *lwp,
int step, int signal, siginfo_t *info);
static void linux_resume (struct thread_resume *resume_info, size_t n);
@@ -218,6 +219,9 @@ static void unstop_all_lwps (int unsuspend, struct lwp_info *except);
static int linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
int *wstat, int options);
static int linux_wait_for_event (ptid_t ptid, int *wstat, int options);
+static ptid_t linux_wait_1 (ptid_t ptid,
+ struct target_waitstatus *ourstatus,
+ int target_options);
static struct lwp_info *add_lwp (ptid_t ptid);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
@@ -379,7 +383,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;
@@ -404,7 +409,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;
@@ -444,12 +449,18 @@ 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_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED;
+ /* Save fork info for target processing and reporting to GDB. */
+ if (event == PTRACE_EVENT_FORK)
+ {
+ current_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED;
+ event_child->waitstatus.kind = TARGET_WAITKIND_FORKED;
+ }
+ else if (event == PTRACE_EVENT_VFORK)
+ {
+ current_inferior->pending_follow.kind = TARGET_WAITKIND_VFORKED;
+ event_child->waitstatus.kind = TARGET_WAITKIND_VFORKED;
+ }
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. */
@@ -509,6 +520,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);
}
@@ -1250,7 +1266,9 @@ linux_enable_extended_features (void)
/* There is no process yet, so include extended options in the
base options for subsequent ptrace configuration. */
linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE
- | PTRACE_O_TRACEFORK, 0);
+ | PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEVFORKDONE, 0);
}
else
{
@@ -1298,6 +1316,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);
@@ -1307,7 +1326,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)
{
@@ -1350,7 +1370,7 @@ linux_follow_fork (int follow_child, int detach_fork)
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)
@@ -1359,8 +1379,63 @@ linux_follow_fork (int follow_child, int detach_fork)
current_inferior = 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;
}
@@ -2560,9 +2635,6 @@ static void move_out_of_jump_pad_callback (struct inferior_list_entry *entry);
static int stuck_in_jump_pad_callback (struct inferior_list_entry *entry,
void *data);
static int lwp_running (struct inferior_list_entry *entry, void *data);
-static ptid_t linux_wait_1 (ptid_t ptid,
- struct target_waitstatus *ourstatus,
- int target_options);
/* Stabilize threads (move out of jump pads).
@@ -2675,7 +2747,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. */
@@ -6358,5 +6432,8 @@ initialize_low (void)
initialize_low_arch ();
- linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE, PTRACE_O_TRACEFORK);
+ linux_ptrace_set_desired_options (PTRACE_O_TRACECLONE,
+ PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEVFORKDONE);
}
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index a3b12dd..00df57c 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1112,15 +1112,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_inferior;
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),
@@ -1231,6 +1234,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 d9989db..1304949 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5510,6 +5510,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. */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 10/10] Extended-remote fork event documentation
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (7 preceding siblings ...)
2014-08-07 18:01 ` [PATCH 08/10] Extended-remote follow vfork Don Breazeal
@ 2014-08-07 18:01 ` Don Breazeal
2014-08-07 19:31 ` Eli Zaretskii
2014-08-07 18:01 ` [PATCH 09/10] Extended-remote fork catchpoints Don Breazeal
` (17 subsequent siblings)
26 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-08-07 18:01 UTC (permalink / raw)
To: gdb-patches
This patch adds documentation for fork events on extended-remote linux targets in the gdb manual and the NEWS file.
Thanks,
--Don
gdb/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* NEWS: Describe new gdbserver support for fork events and new
fork event support in RSP.
gdb/doc/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* gdb.texinfo (Forks): Describe fork debugging support in gdbserver.
(Set Catchpoints): Describe fork catchpoint support in gdbserver.
(Stop Reply Packets): Describe fork event support in RSP.
---
gdb/NEWS | 15 +++++++++++++++
gdb/doc/gdb.texinfo | 33 +++++++++++++++++++++++++++++----
2 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index d603cf7..c3b1d23 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -14,6 +14,21 @@
confirmation if the program had stopped for a signal and the user
switched threads meanwhile.
+* Remote fork events
+
+ GDBserver extended-remote Linux targets now support fork events.
+ This enables follow-fork-mode, detach-on-fork, catch fork, and
+ catch vfork for those targets with Linux kernels 2.5.60 and later.
+
+* New remote packets
+
+T Stop Reply Packet's reason
+ The T stop reply packet supports new stop reasons 'fork', 'vfork'
+ and 'vforkdone'. The 'fork' and 'vfork' reasons signify that the
+ specified inferior has executed a fork or vfork. The 'vforkdone'
+ reason signifies that a vforked child process has executed either
+ an exec or an exit.
+
*** Changes in GDB 7.8
* New command line options
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 8d9148c..07c151b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3107,6 +3107,9 @@ create additional processes using the @code{fork} or @code{vfork} functions.
Currently, the only platforms with this feature are HP-UX (11.x and later
only?) and @sc{gnu}/Linux (kernel version 2.5.60 and later).
+The fork debugging commands are supported in both native mode and when
+connected to @code{gdbserver} using @kbd{target extended-remote}.
+
By default, when a program forks, @value{GDBN} will continue to debug
the parent process and the child process will run unimpeded.
@@ -4395,13 +4398,15 @@ Again, in this case @value{GDBN} would not be able to display syscall's names.
@item fork
@kindex catch fork
-A call to @code{fork}. This is currently only available for HP-UX
-and @sc{gnu}/Linux.
+A call to @code{fork}. This is currently only available for native
+HP-UX and @sc{gnu}/Linux, and when connected to @code{gdbserver} running
+on @sc{gnu}/Linux with @kbd{target extended-remote}.
@item vfork
@kindex catch vfork
-A call to @code{vfork}. This is currently only available for HP-UX
-and @sc{gnu}/Linux.
+A call to @code{vfork}. This is currently only available for native
+HP-UX and @sc{gnu}/Linux, and when connected to @code{gdbserver} running
+on @sc{gnu}/Linux with @kbd{target extended-remote}.
@item load @r{[}regexp@r{]}
@itemx unload @r{[}regexp@r{]}
@@ -34721,6 +34726,26 @@ The packet indicates that the loaded libraries have changed.
@value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new
list of loaded libraries. The @var{r} part is ignored.
+@cindex fork events, remote reply
+@item fork
+The packet indicates that @code{fork} was called, and @var{r}
+is the ptid of the new child process. This packet is only
+applicable to targets that support fork events.
+
+@cindex vfork events, remote reply
+@item vfork
+The packet indicates that @code{vfork} was called, and @var{r}
+is the ptid of the new child process. This packet is only
+applicable to targets that support vfork events.
+
+@cindex vforkdone events, remote reply
+@item vforkdone
+The packet indicates that a child process created by a vfork
+has either called @code{exec} or terminated, so that the
+address spaces of the parent and child process are no longer
+shared. The @var{r} part is ignored. This packet is only
+applicable to targets that support vforkdone events.
+
@cindex replay log events, remote reply
@item replaylog
The packet indicates that the target cannot continue replaying
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 10/10] Extended-remote fork event documentation
2014-08-07 18:01 ` [PATCH 10/10] Extended-remote fork event documentation Don Breazeal
@ 2014-08-07 19:31 ` Eli Zaretskii
2014-08-08 15:35 ` Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Eli Zaretskii @ 2014-08-07 19:31 UTC (permalink / raw)
To: Don Breazeal; +Cc: gdb-patches
> From: Don Breazeal <donb@codesourcery.com>
> Date: Thu, 7 Aug 2014 10:59:55 -0700
>
> This patch adds documentation for fork events on extended-remote linux targets in the gdb manual and the NEWS file.
Thanks, this patch is OK, with these two gotchas:
> +T Stop Reply Packet's reason
> + The T stop reply packet supports new stop reasons 'fork', 'vfork'
> + and 'vforkdone'. The 'fork' and 'vfork' reasons signify that the
^^
Two spaces, please.
> +@cindex vforkdone events, remote reply
> +@item vforkdone
> +The packet indicates that a child process created by a vfork
> +has either called @code{exec} or terminated, so that the
> +address spaces of the parent and child process are no longer
> +shared. The @var{r} part is ignored. This packet is only
^^
And here.
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 10/10] Extended-remote fork event documentation
2014-08-07 19:31 ` Eli Zaretskii
@ 2014-08-08 15:35 ` Breazeal, Don
0 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-08-08 15:35 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
On 8/7/2014 12:31 PM, Eli Zaretskii wrote:
>> From: Don Breazeal <donb@codesourcery.com>
>> Date: Thu, 7 Aug 2014 10:59:55 -0700
>>
>> This patch adds documentation for fork events on extended-remote linux targets in the gdb manual and the NEWS file.
>
> Thanks, this patch is OK, with these two gotchas:
>
>> +T Stop Reply Packet's reason
>> + The T stop reply packet supports new stop reasons 'fork', 'vfork'
>> + and 'vforkdone'. The 'fork' and 'vfork' reasons signify that the
> ^^
> Two spaces, please.
>
>> +@cindex vforkdone events, remote reply
>> +@item vforkdone
>> +The packet indicates that a child process created by a vfork
>> +has either called @code{exec} or terminated, so that the
>> +address spaces of the parent and child process are no longer
>> +shared. The @var{r} part is ignored. This packet is only
> ^^
> And here.
>
Thank you, Eli. I will take care of these.
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 09/10] Extended-remote fork catchpoints
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (8 preceding siblings ...)
2014-08-07 18:01 ` [PATCH 10/10] Extended-remote fork event documentation Don Breazeal
@ 2014-08-07 18:01 ` Don Breazeal
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (16 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-07 18:01 UTC (permalink / raw)
To: gdb-patches
This patch implements catchpoints for fork events on extended-remote linux targets.
Implementation appeared to be straightforward, requiring four new functions in remote.c to implement insert/remove of fork/vfork catchpoints. These functions are essentially stubs that just return 0 ('success'). If the fork events were being reported, then catchpoints were set and hit.
However, there are some extra issues that arise with catchpoints.
1) Thread creation reporting -- fork catchpoints are hit before the follow_fork has been completed. In the native implementation, the new process is not 'reported' until after the follow is done. It doesn't show up in the inferiors list or the threads list. However, in gdbserver, an 'info threads' will retrieve the new thread info from the target and add it to GDB's data structures. Because of this premature report, things on the GDB side eventually get very confused.
So in gdbserver, in server.c:handle_qxfer_threads_worker, we check 'last_status' and if it shows a FORK event, we know that we are in an unfollowed fork and we do not report the new (forked) thread to GDB.
2) Kill process before fork is followed -- on the native side in linux-nat.c:linux_nat_kill, there is some code to handle the case where a fork has occurred but follow_fork hasn't been called yet. It does this by using the last status to determine if a follow is pending, and if it is, to kill the child task. I implemented similar code in linux-low.c:linux_kill.
3) One of the tests related to fork catchpoints, gdb.threads/fork-thread-pending.exp, depended on the threads being reported in a specific order. GDBserver reported the threads in a different order, that is, 'info threads' showed the same threads, but in a different order. The test used a hard-coded thread number to find a threads that (a) was not the main thread (blocked in pthread_join), and (b) was not the forking thread (stopped in fork).
I implemented a new proc, find_unforked_thread, that uses a pretty brute-force method of finding a thread. I considered just hard-coding another number (the native case used thread 2, which was the forking thread in the remote case), but that didn't seem future-proof. Suggestions on how to do this better would be welcome.
Tested on x64 Ubuntu Lucid, native, remote, extended-remote. Tested the case of killing the forking process before the fork has been followed manually. It wasn't clear to me how to check that a process had actually been killed from a dejagnu test.
Thanks,
--Don
gdb/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* remote.c (extended_remote_insert_fork_catchpoint): New function.
(extended_remote_remove_fork_catchpoint): New function.
(extended_remote_insert_vfork_catchpoint): New function.
(extended_remote_remove_vfork_catchpoint): New function.
(init_extended_remote_ops): Initialize target vector with
new fork catchpoint functions.
gdb/gdbserver/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* linux-low.c (linux_kill): Kill fork child when between fork and
follow_fork.
* server.c (handle_qxfer_threads_worker): Skip fork child thread
when between fork and follow_fork.
(get_last_target_status): New function.
* server.h:
gdb/testsuite/
2014-08-06 Don Breazeal <donb@codesourcery.com>
* gdb.threads/fork-thread-pending.exp (find_unforked_thread):
New proc.
(main): Call new proc instead of using hard-coded thread number.
---
gdb/gdbserver/linux-low.c | 18 ++++++
gdb/gdbserver/server.c | 18 ++++++
gdb/gdbserver/server.h | 2 +
gdb/remote.c | 66 +++++++++++++++++++++
gdb/testsuite/gdb.threads/fork-thread-pending.exp | 23 +++++++-
5 files changed, 126 insertions(+), 1 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 69cad15..64b5528 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -1066,6 +1066,24 @@ linux_kill (int pid)
{
struct process_info *process;
struct lwp_info *lwp;
+ struct target_waitstatus last;
+ ptid_t last_ptid;
+
+ /* If we're stopped while forking and we haven't followed yet,
+ kill the child task. We need to do this first because the
+ parent will be sleeping if this is a vfork. */
+
+ get_last_target_status (&last_ptid, &last);
+
+ if (last.kind == TARGET_WAITKIND_FORKED
+ || last.kind == TARGET_WAITKIND_VFORKED)
+ {
+ lwp = find_lwp_pid (last.value.related_pid);
+ gdb_assert (lwp != NULL);
+ kill_wait_lwp (lwp);
+ process = find_process_pid (ptid_get_pid (last.value.related_pid));
+ the_target->mourn (process);
+ }
process = find_process_pid (pid);
if (process == NULL)
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index eb95dbf..66a53ca 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1316,6 +1316,15 @@ handle_qxfer_threads_worker (struct inferior_list_entry *inf, void *arg)
int core = target_core_of_thread (ptid);
char core_s[21];
+ /* Skip new threads created as the result of a fork if we are not done
+ handling that fork event. We won't know whether to tell GDB about
+ the new thread until we are done following the fork. */
+ if ((last_status.kind == TARGET_WAITKIND_FORKED
+ || last_status.kind == TARGET_WAITKIND_VFORKED)
+ && (ptid_get_pid (last_status.value.related_pid)
+ == ptid_get_pid (ptid)))
+ return;
+
write_ptid (ptid_s, ptid);
if (core != -1)
@@ -4021,3 +4030,12 @@ handle_target_event (int err, gdb_client_data client_data)
return 0;
}
+
+/* Retrieve the last waitstatus reported to GDB. */
+
+void
+get_last_target_status (ptid_t *ptid, struct target_waitstatus *last)
+{
+ *ptid = last_ptid;
+ *last = last_status;
+}
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index ef66a32..e17eb04 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -139,6 +139,8 @@ typedef int gdb_fildes_t;
/* Functions from server.c. */
extern int handle_serial_event (int err, gdb_client_data client_data);
extern int handle_target_event (int err, gdb_client_data client_data);
+extern void get_last_target_status (ptid_t *ptid,
+ struct target_waitstatus *last);
#include "remote-utils.h"
diff --git a/gdb/remote.c b/gdb/remote.c
index 1304949..2918e54 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -112,6 +112,18 @@ static void remote_mourn (struct target_ops *ops);
static void extended_remote_restart (void);
+static int extended_remote_insert_fork_catchpoint (struct target_ops *ops,
+ int pid);
+
+static int extended_remote_remove_fork_catchpoint (struct target_ops *ops,
+ int pid);
+
+static int extended_remote_insert_vfork_catchpoint (struct target_ops *ops,
+ int pid);
+
+static int extended_remote_remove_vfork_catchpoint (struct target_ops *ops,
+ int pid);
+
static int extended_remote_follow_fork (struct target_ops *ops,
int follow_child, int detach_fork);
@@ -7812,6 +7824,52 @@ extended_remote_kill (struct target_ops *ops)
target_mourn_inferior ();
}
+/* Insert fork catchpoint target routine. If follow-fork is enabled
+ then fork events are supported by the OS, and if we are in this
+ routine then extended mode is enabled, so the fork events will be
+ reported. Nothing further is required. */
+
+static int
+extended_remote_insert_fork_catchpoint (struct target_ops *ops, int pid)
+{
+ if (remote_protocol_packets[PACKET_vFollowFork].support != PACKET_DISABLE)
+ return 0;
+
+ return 1;
+}
+
+/* Remove fork catchpoint target routine. Nothing to do, just
+ return success. */
+
+static int
+extended_remote_remove_fork_catchpoint (struct target_ops *ops, int pid)
+{
+ return 0;
+}
+
+/* Insert vfork catchpoint target routine. If follow-fork is enabled
+ then fork events are supported by the OS, and if we are in this
+ routine then extended mode is enabled, so fork events will be
+ reported. Nothing further is required. */
+
+static int
+extended_remote_insert_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+ if (remote_protocol_packets[PACKET_vFollowFork].support != PACKET_DISABLE)
+ return 0;
+
+ return 1;
+}
+
+/* Remove vfork catchpoint target routine. Nothing to do, just
+ return success. */
+
+static int
+extended_remote_remove_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+ return 0;
+}
+
/* Target routine for follow-fork. */
static int
@@ -11609,6 +11667,14 @@ init_extended_remote_ops (void)
Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_open = extended_remote_open;
extended_remote_ops.to_create_inferior = extended_remote_create_inferior;
+ extended_remote_ops.to_insert_fork_catchpoint
+ = extended_remote_insert_fork_catchpoint;
+ extended_remote_ops.to_remove_fork_catchpoint
+ = extended_remote_remove_fork_catchpoint;
+ extended_remote_ops.to_insert_vfork_catchpoint
+ = extended_remote_insert_vfork_catchpoint;
+ extended_remote_ops.to_remove_vfork_catchpoint
+ = extended_remote_remove_vfork_catchpoint;
extended_remote_ops.to_follow_fork = extended_remote_follow_fork;
extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
extended_remote_ops.to_detach = extended_remote_detach;
diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.exp b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
index 57e45c9..e8c4830 100644
--- a/gdb/testsuite/gdb.threads/fork-thread-pending.exp
+++ b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
@@ -31,6 +31,26 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
+# Find a thread that did not fork and is not the main thread and
+# return its thread number. We can't just hard-code the thread
+# number since we have no guarantee as to the ordering of the threads
+# in gdb. We know that the main thread is in pthread_join and the
+# forking thread is in fork, so we use this rather ungainly regexp
+# to capture an entry from 'info threads' that doesn't show one of
+# those routines, then extract the thread number.
+
+proc find_unforked_thread { } {
+ gdb_test_multiple "info threads" "find unforked thread" {
+ -re "(\[^\r]*Thread\[^\r]* in \[^fp]\[^ot]\[^rh]\[^kr]\[^e]\[^a]\[^d]\[^_]\[^j]\[^\r]*\r\n)" {
+ regexp "(\[ ]*)(\[0-9]*)(\[ ]*Thread\[^\r]*\r\n)" $expect_out(0,string) ignore lead_spc threadnum rest
+ }
+ timeout {
+ set threadnum -1
+ }
+ }
+ return $threadnum
+}
+
clean_restart ${binfile}
if ![runto_main] then {
@@ -46,7 +66,8 @@ gdb_test "continue" "Catchpoint.*" "1, get to the fork event"
gdb_test "info threads" " Thread .* Thread .* Thread .* Thread .*" "1, multiple threads found"
-gdb_test "thread 2" ".*" "1, switched away from event thread"
+set threadnum [find_unforked_thread]
+gdb_test "thread $threadnum" ".*" "1, switched away from event thread to thread $threadnum"
gdb_test "continue" "Not resuming.*" "1, refused to resume"
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [Patch 00/16 v2] Linux extended-remote fork and exec events
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (9 preceding siblings ...)
2014-08-07 18:01 ` [PATCH 09/10] Extended-remote fork catchpoints Don Breazeal
@ 2014-08-21 0:29 ` Don Breazeal
2014-09-04 20:57 ` Breazeal, Don
` (14 more replies)
2014-08-21 0:29 ` [PATCH 01/16 v2] Refactor native follow-fork Don Breazeal
` (15 subsequent siblings)
26 siblings, 15 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:29 UTC (permalink / raw)
To: gdb-patches
This is an update to the patch series implementing fork events for
extended-remote Linux targets. Changes from the previous version:
* Exec Events: integrated the previous patch series that implemented exec
events for extended-remote Linux targets. The exec patches had
significant overlap with the fork event patch series, and it no longer
made sense to maintain them separately.
* Exec Catchpoints: the previous exec event patch series did not
implement 'catch exec'. That is included here.
* GDBserver Extended Feature Query: patch 4 has been completely rewritten.
The previous version attempted to determine whether extended events
were supported by gdbserver using information only on the host. This
approach was flawed and did not work at all once follow-exec was
introduced. This mechanism now uses an new RSP message to determine
which extended events are supported prior to attempting any activity
that would require that support. There is more detail in the patch
4 description.
* Coding Style: fixed a number of coding style violations.
* Line Breaks in Descriptions: formatted the patch descriptions with line
breaks in each paragraph, which might make them more accessible via
web pages.
Maintainers may find it useful to consider this patch series as having
three parts:
1) Patches 1-3: these refactor existing code without making any
significant functional changes (aside from some new verbose
messages).
2) Patches 4-10: these implement all the fork event functionality.
3) Patches 11-16: these implement all the exec event functionality.
Dividing it like this may help make the large size of the patch series
more manageable. Again, please let me know if there is anything I can do
to make this easier to review.
Eli approved patches 10 and 15 when they were posted previously. The only
changes to these patches since then were those that he requested.
====================================================================
This patch series implements fork and exec events for extended-remote linux
targets. Features that are enabled include:
* follow-fork-mode
* detach-on-fork
* follow-exec-mode
* catch fork/vfork/exec
This work addresses PR gdb/13584, and is part of the local/remote debugging
feature parity project
(https://sourceware.org/gdb/wiki/LocalRemoteFeatureParity).
My final test results showed the following changes:
* native: PASS +46
- this is due the addition of non-stop mode tests to
gdb.threads/non-ldr-exc-*.exp.
* remote: no change
* extended-remote: PASS: +329, FAIL: -134, KFAIL: +1,
UNTESTED: +8, UNSUPPORTED: -6
Some items to note about the extended-remote results:
- there are some extended-remote failures in
gdb.base/disp-step-syscall.exp that don't show up in the unmodified
version on the native target. Investigation shows that these tests
are producing a bogus PASS result on the native target. I did not
pursue this further.
- the new non-stop tests in gdb.threads/non-ldr-exc-*.exp give an
UNTESTED result for extended-remote. This is due to an RSP
error in non-stop mode when running to main. I spent some time
investigating this and concluded that I should leave it alone for now,
for fear of having an ever-growing, never-ending project. My plan is
to track this with a bug report once that is appropriate.
- gdb.threads/thread-execl.exp gives a couple of failures related to
scheduler locking. As with the previous item, after spending some
time on this I concluded that pursuing it further now would be
feature-creep, and that this should be tracked with a bug report.
- gdb.trace/tspeed.exp got some timeout failures one time, but I
was unable to reproduce it. I'm unsure whether this is significant.
I can provide test logs or .sum diffs if anybody is interested in
specifics.
The contents of the patch series are as follows:
Patch 1: refactor native follow-fork implementation to move
target-independent code from target-dependent file (linux-nat.c)
to make it useful for extended-remote targets.
Patch 2: encapsulate the code used to print verbose/debug messages
related to folow-fork into functions, and add a message in one case.
Also make messages distinguish between fork and vfork.
Patch 3: encapsulate code that identifies and extracts extended
ptrace events found in the Linux wait status into functions, and call
them everywhere that the hard-coded wait status processing was used.
Patch 4: implements a mechanism for determining what extended-mode
features gdbserver supports. There are several issues that this addresses,
which are detailed in the patch 4 description. Basically, the patch
implements a new RSP packet "qExtendedFeatures" that allows GDB to ask
gdbserver for the list of extended features that are enabled prior to
doing anything that would require such a feature. In gdbserver, it splits
checking whether the OS supports the features from actually enabling
them, and enables them only when it is clear that extended mode is
active. To facilitate this the patch also extends the changes that Gary
Benson implemented to reduce the use of #ifdef GDBSERVER in linux-ptrace.c
here:
https://support urceware.org/ml/gdb-patches/2014-07/msg00633.html.
Patch 5: implements some functions to clone the breakpoint lists in
gdbserver. These are needed because in gdbserver, each process maintains a
separate breakpoint list. When a fork occurs, the child process needs a
copy of the parent's breakpoint list so that it can manage the breakpoints
using existing mechanisms in gdbserver.
Patch 6: implements follow-fork, but only for 'fork', not 'vfork'. I split
these apart in an attempt to keep the size of the patches down to a
reviewable size.
Patch 7: adds the architecture-specific pieces of follow-fork. This is the
mechanism that handles copying the debug register state from the parent to
the child.
Patch 8: adds follow-fork for vfork.
Patch 9: adds 'catch fork' and 'catch vfork', along with some code to make
sure that killing a process that has forked, but before the fork is
followed, also kills the child process.
Patch 10: implements changes to the manual and the NEWS file for the fork
event changes.
Patch 11: implements support for the extended ptrace event
PTRACE_EVENT_EXIT, which is a prerequisite for the exec event support.
Patch 12: implements exec event support and follow-exec.
Patch 13: implements exec catchpoints.
Patch 14: suppresses some spurious warnings that were generated after
an exec. These warnings were caused when gdbserver attempted to check
the version number from the r_debug structure in the new image before
the structure had been initialized.
Patch 15: implements changed to the manual and NEWS file for the exec
event changes.
Patch 16: changes how gdb.base/foll-exec.exp starts GDB and loads the
program so that it will work with extended-remote. It also extends the
tests gdb.threads/non-ldr-exc-[1-4].exp to test in non-stop mode as well as
in all-stop mode.
thanks
--Don
gdb/NEWS | 18 +
gdb/doc/gdb.texinfo | 42 ++-
gdb/gdbserver/gdbthread.h | 5 +
gdb/gdbserver/linux-aarch64-low.c | 28 ++
gdb/gdbserver/linux-arm-low.c | 26 +
gdb/gdbserver/linux-low.c | 511 +++++++++++++++++++--
gdb/gdbserver/linux-low.h | 8 +
gdb/gdbserver/linux-mips-low.c | 76 +++-
gdb/gdbserver/linux-x86-low.c | 29 ++
gdb/gdbserver/lynx-low.c | 4 +
gdb/gdbserver/mem-break.c | 104 +++++
gdb/gdbserver/mem-break.h | 6 +
gdb/gdbserver/nto-low.c | 4 +
gdb/gdbserver/remote-utils.c | 45 ++-
gdb/gdbserver/server.c | 92 ++++
gdb/gdbserver/server.h | 3 +
gdb/gdbserver/spu-low.c | 4 +
gdb/gdbserver/target.h | 33 ++
gdb/gdbserver/win32-low.c | 4 +
gdb/infrun.c | 296 +++++++++++-
gdb/infrun.h | 2 -
gdb/linux-nat.c | 264 ++----------
gdb/nat/linux-procfs.c | 18 +
gdb/nat/linux-procfs.h | 4 +
gdb/nat/linux-ptrace.c | 150 +++++--
gdb/nat/linux-ptrace.h | 10 +-
gdb/remote.c | 337 +++++++++++++--
gdb/testsuite/gdb.base/foll-exec.exp | 44 +--
gdb/testsuite/gdb.base/foll-fork.exp | 12 +-
gdb/testsuite/gdb.base/foll-vfork.exp | 8 +-
gdb/testsuite/gdb.threads/fork-thread-pending.exp | 23 +-
gdb/testsuite/gdb.threads/non-ldr-exc-1.exp | 20 +-
gdb/testsuite/gdb.threads/non-ldr-exc-2.exp | 36 ++-
gdb/testsuite/gdb.threads/non-ldr-exc-3.exp | 36 ++-
gdb/testsuite/gdb.threads/non-ldr-exc-4.exp | 20 +-
35 files changed, 1891 insertions(+), 431 deletions(-)
^ permalink raw reply [flat|nested] 110+ messages in thread
* RE: [Patch 00/16 v2] Linux extended-remote fork and exec events
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
@ 2014-09-04 20:57 ` Breazeal, Don
2014-10-31 23:29 ` [PATCH 08/16 v3] Extended-remote follow vfork Don Breazeal
` (13 subsequent siblings)
14 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-09-04 20:57 UTC (permalink / raw)
To: gdb-patches
Ping..
Thanks
--Don
> -----Original Message-----
> From: gdb-patches-owner@sourceware.org [mailto:gdb-patches-
> owner@sourceware.org] On Behalf Of Breazeal, Don
> Sent: Wednesday, August 20, 2014 5:29 PM
> To: gdb-patches@sourceware.org
> Subject: [Patch 00/16 v2] Linux extended-remote fork and exec events
>
> This is an update to the patch series implementing fork events for
> extended-remote Linux targets. Changes from the previous version:
>
> * Exec Events: integrated the previous patch series that implemented
> exec
> events for extended-remote Linux targets. The exec patches had
> significant overlap with the fork event patch series, and it no
> longer
> made sense to maintain them separately.
>
> * Exec Catchpoints: the previous exec event patch series did not
> implement 'catch exec'. That is included here.
>
> * GDBserver Extended Feature Query: patch 4 has been completely
> rewritten.
> The previous version attempted to determine whether extended events
> were supported by gdbserver using information only on the host. This
> approach was flawed and did not work at all once follow-exec was
> introduced. This mechanism now uses an new RSP message to determine
> which extended events are supported prior to attempting any activity
> that would require that support. There is more detail in the patch
> 4 description.
>
> * Coding Style: fixed a number of coding style violations.
>
> * Line Breaks in Descriptions: formatted the patch descriptions with
> line
> breaks in each paragraph, which might make them more accessible via
> web pages.
>
> Maintainers may find it useful to consider this patch series as having
> three parts:
>
> 1) Patches 1-3: these refactor existing code without making any
> significant functional changes (aside from some new verbose
> messages).
>
> 2) Patches 4-10: these implement all the fork event functionality.
>
> 3) Patches 11-16: these implement all the exec event functionality.
>
> Dividing it like this may help make the large size of the patch series
> more manageable. Again, please let me know if there is anything I can
> do to make this easier to review.
>
> Eli approved patches 10 and 15 when they were posted previously. The
> only changes to these patches since then were those that he requested.
>
> ====================================================================
>
> This patch series implements fork and exec events for extended-remote
> linux targets. Features that are enabled include:
>
> * follow-fork-mode
> * detach-on-fork
> * follow-exec-mode
> * catch fork/vfork/exec
>
> This work addresses PR gdb/13584, and is part of the local/remote
> debugging feature parity project
> (https://sourceware.org/gdb/wiki/LocalRemoteFeatureParity).
>
> My final test results showed the following changes:
>
> * native: PASS +46
> - this is due the addition of non-stop mode tests to
> gdb.threads/non-ldr-exc-*.exp.
>
> * remote: no change
>
> * extended-remote: PASS: +329, FAIL: -134, KFAIL: +1,
> UNTESTED: +8, UNSUPPORTED: -6
>
> Some items to note about the extended-remote results:
>
> - there are some extended-remote failures in
> gdb.base/disp-step-syscall.exp that don't show up in the unmodified
> version on the native target. Investigation shows that these tests
> are producing a bogus PASS result on the native target. I did not
> pursue this further.
>
> - the new non-stop tests in gdb.threads/non-ldr-exc-*.exp give an
> UNTESTED result for extended-remote. This is due to an RSP
> error in non-stop mode when running to main. I spent some time
> investigating this and concluded that I should leave it alone for
> now,
> for fear of having an ever-growing, never-ending project. My plan is
> to track this with a bug report once that is appropriate.
>
> - gdb.threads/thread-execl.exp gives a couple of failures related to
> scheduler locking. As with the previous item, after spending some
> time on this I concluded that pursuing it further now would be
> feature-creep, and that this should be tracked with a bug report.
>
> - gdb.trace/tspeed.exp got some timeout failures one time, but I
> was unable to reproduce it. I'm unsure whether this is significant.
>
> I can provide test logs or .sum diffs if anybody is interested in
> specifics.
>
> The contents of the patch series are as follows:
>
> Patch 1: refactor native follow-fork implementation to move target-
> independent code from target-dependent file (linux-nat.c) to make it
> useful for extended-remote targets.
>
> Patch 2: encapsulate the code used to print verbose/debug messages
> related to folow-fork into functions, and add a message in one case.
> Also make messages distinguish between fork and vfork.
>
> Patch 3: encapsulate code that identifies and extracts extended ptrace
> events found in the Linux wait status into functions, and call them
> everywhere that the hard-coded wait status processing was used.
>
> Patch 4: implements a mechanism for determining what extended-mode
> features gdbserver supports. There are several issues that this
> addresses, which are detailed in the patch 4 description. Basically,
> the patch implements a new RSP packet "qExtendedFeatures" that allows
> GDB to ask gdbserver for the list of extended features that are enabled
> prior to doing anything that would require such a feature. In
> gdbserver, it splits checking whether the OS supports the features from
> actually enabling them, and enables them only when it is clear that
> extended mode is active. To facilitate this the patch also extends the
> changes that Gary Benson implemented to reduce the use of #ifdef
> GDBSERVER in linux-ptrace.c
> here:
> https://support urceware.org/ml/gdb-patches/2014-07/msg00633.html.
>
> Patch 5: implements some functions to clone the breakpoint lists in
> gdbserver. These are needed because in gdbserver, each process
> maintains a separate breakpoint list. When a fork occurs, the child
> process needs a copy of the parent's breakpoint list so that it can
> manage the breakpoints using existing mechanisms in gdbserver.
>
> Patch 6: implements follow-fork, but only for 'fork', not 'vfork'. I
> split these apart in an attempt to keep the size of the patches down to
> a reviewable size.
>
> Patch 7: adds the architecture-specific pieces of follow-fork. This is
> the mechanism that handles copying the debug register state from the
> parent to the child.
>
> Patch 8: adds follow-fork for vfork.
>
> Patch 9: adds 'catch fork' and 'catch vfork', along with some code to
> make sure that killing a process that has forked, but before the fork is
> followed, also kills the child process.
>
> Patch 10: implements changes to the manual and the NEWS file for the
> fork event changes.
>
> Patch 11: implements support for the extended ptrace event
> PTRACE_EVENT_EXIT, which is a prerequisite for the exec event support.
>
> Patch 12: implements exec event support and follow-exec.
>
> Patch 13: implements exec catchpoints.
>
> Patch 14: suppresses some spurious warnings that were generated after an
> exec. These warnings were caused when gdbserver attempted to check the
> version number from the r_debug structure in the new image before the
> structure had been initialized.
>
> Patch 15: implements changed to the manual and NEWS file for the exec
> event changes.
>
> Patch 16: changes how gdb.base/foll-exec.exp starts GDB and loads the
> program so that it will work with extended-remote. It also extends the
> tests gdb.threads/non-ldr-exc-[1-4].exp to test in non-stop mode as well
> as in all-stop mode.
>
> thanks
> --Don
>
> gdb/NEWS | 18 +
> gdb/doc/gdb.texinfo | 42 ++-
> gdb/gdbserver/gdbthread.h | 5 +
> gdb/gdbserver/linux-aarch64-low.c | 28 ++
> gdb/gdbserver/linux-arm-low.c | 26 +
> gdb/gdbserver/linux-low.c | 511
> +++++++++++++++++++--
> gdb/gdbserver/linux-low.h | 8 +
> gdb/gdbserver/linux-mips-low.c | 76 +++-
> gdb/gdbserver/linux-x86-low.c | 29 ++
> gdb/gdbserver/lynx-low.c | 4 +
> gdb/gdbserver/mem-break.c | 104 +++++
> gdb/gdbserver/mem-break.h | 6 +
> gdb/gdbserver/nto-low.c | 4 +
> gdb/gdbserver/remote-utils.c | 45 ++-
> gdb/gdbserver/server.c | 92 ++++
> gdb/gdbserver/server.h | 3 +
> gdb/gdbserver/spu-low.c | 4 +
> gdb/gdbserver/target.h | 33 ++
> gdb/gdbserver/win32-low.c | 4 +
> gdb/infrun.c | 296 +++++++++++-
> gdb/infrun.h | 2 -
> gdb/linux-nat.c | 264 ++----------
> gdb/nat/linux-procfs.c | 18 +
> gdb/nat/linux-procfs.h | 4 +
> gdb/nat/linux-ptrace.c | 150 +++++--
> gdb/nat/linux-ptrace.h | 10 +-
> gdb/remote.c | 337 +++++++++++++-
> -
> gdb/testsuite/gdb.base/foll-exec.exp | 44 +--
> gdb/testsuite/gdb.base/foll-fork.exp | 12 +-
> gdb/testsuite/gdb.base/foll-vfork.exp | 8 +-
> gdb/testsuite/gdb.threads/fork-thread-pending.exp | 23 +-
> gdb/testsuite/gdb.threads/non-ldr-exc-1.exp | 20 +-
> gdb/testsuite/gdb.threads/non-ldr-exc-2.exp | 36 ++-
> gdb/testsuite/gdb.threads/non-ldr-exc-3.exp | 36 ++-
> gdb/testsuite/gdb.threads/non-ldr-exc-4.exp | 20 +-
> 35 files changed, 1891 insertions(+), 431 deletions(-)
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 08/16 v3] Extended-remote follow vfork
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
2014-09-04 20:57 ` Breazeal, Don
@ 2014-10-31 23:29 ` Don Breazeal
2014-10-31 23:29 ` [PATCH 07/16 v3] Extended-remote arch-specific follow fork Don Breazeal
` (12 subsequent siblings)
14 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:29 UTC (permalink / raw)
To: gdb-patches
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 <donb@codesourcery.com>
* 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 <donb@codesourcery.com>
* 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. */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 07/16 v3] Extended-remote arch-specific follow fork
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
2014-09-04 20:57 ` Breazeal, Don
2014-10-31 23:29 ` [PATCH 08/16 v3] Extended-remote follow vfork Don Breazeal
@ 2014-10-31 23:29 ` Don Breazeal
2014-10-31 23:29 ` [PATCH 06/16 v3] Extended-remote Linux " Don Breazeal
` (11 subsequent siblings)
14 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:29 UTC (permalink / raw)
To: gdb-patches
This patch implements the architecture-specific pieces of follow-fork,
which in the current implementation copyies the parent's debug register
state into the new child's data structures. This is required for x86,
arm, aarch64, and mips.
I followed the native implementation as closely as I could by
implementing a new linux_target_ops function 'new_fork', which is
analogous to 'linux_nat_new_fork' in linux-nat.c. In gdbserver, the debug
registers are stored in the process list, instead of an
architecture-specific list, so the function arguments are process_info
pointers instead of an lwp_info and a pid as in the native implementation.
In the MIPS implementation the debug register mirror is stored differently
from x86, ARM, and aarch64, so instead of doing a simple structure assignment
I had to clone the list of watchpoint structures.
Tested using gdb.threads/watchpoint-fork.exp on x86, and ran manual tests
on a MIPS board and an ARM board.
I don't currently have access to an aarch64 board, so again if someone is
able to test this easily, please do.
Thanks
--Don
gdb/gdbserver/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* linux-aarch64-low.c (aarch64_linux_new_fork): New function.
(the_low_target) <new_fork>: Initialize new member.
* linux-arm-low.c (arm_new_fork): New function.
(the_low_target) <new_fork>: Initialize new member.
* linux-low.c (handle_extended_wait): Call new target function
new_fork.
* linux-low.h (struct linux_target_ops) <new_fork>: New member.
* linux-mips-low.c (mips_add_watchpoint): New function
extracted from mips_insert_point.
(the_low_target) <new_fork>: Initialize new member.
(mips_linux_new_fork): New function.
(mips_insert_point): Call mips_add_watchpoint.
* linux-x86-low.c (x86_linux_new_fork): New function.
(the_low_target) <new_fork>: Initialize new member.
---
gdb/gdbserver/linux-aarch64-low.c | 28 +++++++++++++
gdb/gdbserver/linux-arm-low.c | 42 ++++++++++++++++++++
gdb/gdbserver/linux-low.c | 4 ++
gdb/gdbserver/linux-low.h | 3 +
gdb/gdbserver/linux-mips-low.c | 76 +++++++++++++++++++++++++++++++------
gdb/gdbserver/linux-x86-low.c | 29 ++++++++++++++
6 files changed, 170 insertions(+), 12 deletions(-)
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 654b319..31ec0d7 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -1129,6 +1129,33 @@ aarch64_linux_new_thread (void)
return info;
}
+static void
+aarch64_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
@@ -1293,6 +1320,7 @@ struct linux_target_ops the_low_target =
NULL,
aarch64_linux_new_process,
aarch64_linux_new_thread,
+ aarch64_linux_new_fork,
aarch64_linux_prepare_to_resume,
};
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 8b72523..fe9c34e 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -717,6 +717,47 @@ arm_new_thread (void)
return info;
}
+static void
+arm_new_fork (struct process_info *parent, struct process_info *child)
+{
+ struct arch_process_info *parent_proc_info = parent->private->arch_private;
+ struct arch_process_info *child_proc_info = child->private->arch_private;
+ struct lwp_info *child_lwp;
+ struct arch_lwp_info *child_lwp_info;
+ int i;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child_proc_info = *parent_proc_info;
+
+ /* Mark all the hardware breakpoints and watchpoints as changed to
+ make sure that the registers will be updated. */
+ child_lwp = find_lwp_pid (ptid_of (child));
+ child_lwp_info = child_lwp->arch_private;
+ for (i = 0; i < MAX_BPTS; i++)
+ child_lwp_info->bpts_changed[i] = 1;
+ for (i = 0; i < MAX_WPTS; i++)
+ child_lwp_info->wpts_changed[i] = 1;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
static void
@@ -920,6 +961,7 @@ struct linux_target_ops the_low_target = {
NULL, /* siginfo_fixup */
arm_new_process,
arm_new_thread,
+ arm_new_fork,
arm_prepare_to_resume,
};
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index db7ef09..4f6d664 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -438,6 +438,10 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
child_proc->tdesc = tdesc;
child_lwp->must_set_ptrace_flags = 1;
+ /* 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 target processing. */
current_thread->pending_follow.kind = TARGET_WAITKIND_FORKED;
current_thread->pending_follow.value.related_pid = ptid;
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index a903430..53d1c24 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -185,6 +185,9 @@ struct linux_target_ops
allocate it here. */
struct arch_lwp_info * (*new_thread) (void);
+ /* Hook to call, if any, when a new fork is attached. */
+ void (*new_fork) (struct process_info *parent, struct process_info *child);
+
/* Hook to call prior to resuming a thread. */
void (*prepare_to_resume) (struct lwp_info *);
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index 0fc8cb4..d5db79d 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -344,6 +344,68 @@ mips_linux_new_thread (void)
return info;
}
+/* Create a new mips_watchpoint and add it to the list. */
+
+static void
+mips_add_watchpoint (struct arch_process_info *private, CORE_ADDR addr,
+ int len, int watch_type)
+{
+ struct mips_watchpoint *new_watch;
+ struct mips_watchpoint **pw;
+
+ new_watch = xmalloc (sizeof (struct mips_watchpoint));
+ new_watch->addr = addr;
+ new_watch->len = len;
+ new_watch->type = watch_type;
+ new_watch->next = NULL;
+
+ pw = &private->current_watches;
+ while (*pw != NULL)
+ pw = &(*pw)->next;
+ *pw = new_watch;
+}
+
+/* Hook to call when a new fork is attached. */
+
+static void
+mips_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ struct arch_process_info *parent_private;
+ struct arch_process_info *child_private;
+ struct mips_watchpoint *wp;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ parent_private = parent->private->arch_private;
+ child_private = child->private->arch_private;
+
+ child_private->watch_readback_valid = parent_private->watch_readback_valid;
+ child_private->watch_readback = parent_private->watch_readback;
+
+ for (wp = parent_private->current_watches; wp != NULL; wp = wp->next)
+ mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type);
+
+ child_private->watch_mirror = parent_private->watch_mirror;
+}
/* This is the implementation of linux_target_ops method
prepare_to_resume. If the watch regs have changed, update the
thread's copies. */
@@ -397,8 +459,6 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
struct process_info *proc = current_process ();
struct arch_process_info *private = proc->private->arch_private;
struct pt_watch_regs regs;
- struct mips_watchpoint *new_watch;
- struct mips_watchpoint **pw;
int pid;
long lwpid;
enum target_hw_bp_type watch_type;
@@ -425,16 +485,7 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
return -1;
/* It fit. Stick it on the end of the list. */
- new_watch = xmalloc (sizeof (struct mips_watchpoint));
- new_watch->addr = addr;
- new_watch->len = len;
- new_watch->type = watch_type;
- new_watch->next = NULL;
-
- pw = &private->current_watches;
- while (*pw != NULL)
- pw = &(*pw)->next;
- *pw = new_watch;
+ mips_add_watchpoint (private, addr, len, watch_type);
private->watch_mirror = regs;
@@ -845,6 +896,7 @@ struct linux_target_ops the_low_target = {
NULL, /* siginfo_fixup */
mips_linux_new_process,
mips_linux_new_thread,
+ mips_linux_new_fork,
mips_linux_prepare_to_resume
};
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 838e7c9..10b8a56 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -768,6 +768,34 @@ x86_linux_new_thread (void)
return info;
}
+/* Target routine for linux_new_fork. */
+
+static void
+x86_linux_new_fork (struct process_info *parent, struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
@@ -3428,6 +3456,7 @@ struct linux_target_ops the_low_target =
x86_siginfo_fixup,
x86_linux_new_process,
x86_linux_new_thread,
+ x86_linux_new_fork,
x86_linux_prepare_to_resume,
x86_linux_process_qsupported,
x86_supports_tracepoints,
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 06/16 v3] Extended-remote Linux follow fork
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (2 preceding siblings ...)
2014-10-31 23:29 ` [PATCH 07/16 v3] Extended-remote arch-specific follow fork Don Breazeal
@ 2014-10-31 23:29 ` Don Breazeal
2014-11-13 13:00 ` Pedro Alves
2014-10-31 23:29 ` [PATCH 00/16 v3] Linux extended-remote fork and exec events Don Breazeal
` (10 subsequent siblings)
14 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:29 UTC (permalink / raw)
To: gdb-patches
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/gdbserver/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* gdbthread.h (struct thread_info) <pending_follow>: 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) <follow_fork>: Initialize new member.
(initialize_low): Add PTRACE_O_TRACEFORK option.
* linux-low.h (struct lwp_info) <waitstatus>: New member.
* lynx-low.c (lynx_target_ops) <follow_fork>: 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.
* target.h (struct target_ops) <follow_fork>: New member.
(target_follow_fork): Define macro.
* win32-low.c (win32_target_ops) <follow_fork>: Initialize new member.
gdb/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* gdb/nat/linux-ptrace.c (current_ptrace_options): Update comment.
* remote.c (extended_remote_follow_fork): Implement follow-fork.
(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.
(remote_pid_to_str): Print process.
(_initialize_remote): Call add_packet_config_cmd for new RSP packet.
---
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/remote-utils.c | 14 +++-
gdb/gdbserver/server.c | 41 ++++++++
gdb/gdbserver/target.h | 13 +++
gdb/gdbserver/win32-low.c | 1 +
gdb/nat/linux-ptrace.c | 4 +-
gdb/remote.c | 53 +++++++++--
10 files changed, 319 insertions(+), 25 deletions(-)
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 8334578..db7ef09 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,17 +365,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;
@@ -399,6 +400,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_thread);
+ 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_thread->pending_follow.kind = TARGET_WAITKIND_FORKED;
+ current_thread->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",
@@ -448,7 +499,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
@@ -1173,6 +1229,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_thread = NULL;
+ }
+ }
+ }
+
+ return 0;
+}
+
/* Remove all LWPs that belong to process PROC from the lwp list. */
static int
@@ -1885,8 +2032,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 +2633,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
@@ -2847,7 +3009,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);
@@ -2869,6 +3032,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
@@ -2967,7 +3137,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)
@@ -2984,7 +3164,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));
}
@@ -4776,8 +4956,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. */
@@ -6126,6 +6306,7 @@ static struct target_ops linux_target_ops = {
linux_supports_vfork_events,
linux_supports_exec_events,
linux_enable_extended_features,
+ linux_follow_fork,
#ifdef USE_THREAD_DB
thread_db_handle_monitor_command,
#else
@@ -6202,7 +6383,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 3ccc032..c2d0ee7 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -755,6 +755,7 @@ static struct target_ops lynx_target_ops = {
NULL, /* supports_vfork_events */
NULL, /* supports_exec_events */
NULL, /* enable_extended_features */
+ NULL, /* follow_fork */
NULL, /* handle_monitor_command */
};
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 d297403..b1720de 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2544,6 +2544,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)
@@ -2609,6 +2632,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/target.h b/gdb/gdbserver/target.h
index 570f57d..58afc6a 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -93,6 +93,7 @@ struct target_ops
int (*detach) (int pid);
+
/* The inferior process has died. Do what is right. */
void (*mourn) (struct process_info *proc);
@@ -274,6 +275,10 @@ struct target_ops
/* Enable features that are only available in extended mode. */
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);
+
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
@@ -450,6 +455,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 9ae2f94..6185e15 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1827,6 +1827,7 @@ static struct target_ops win32_target_ops = {
NULL, /* supports_vfork_events */
NULL, /* supports_exec_events */
NULL, /* enable_extended_features */
+ NULL, /* follow_fork */
NULL, /* handle_monitor_command */
NULL, /* core_of_thread */
NULL, /* read_loadmap */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 7871d95..5f3f123 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -36,8 +36,8 @@ static int requested_ptrace_options;
of the requested options are supported. */
static int available_ptrace_options = -1;
-/* Stores the currently enabled ptrace options, or the default
- option(s) that will be enabled once a process is loaded. */
+/* Stores the currently enabled ptrace options, or the options
+ that will be enabled once a process is loaded. */
static int current_ptrace_options;
/* Find all possible reasons we could fail to attach PID and append
diff --git a/gdb/remote.c b/gdb/remote.c
index 77c68d8..1d110f9 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -24,6 +24,7 @@
#include <fcntl.h>
#include "inferior.h"
#include "infrun.h"
+#include "inf-child.h"
#include "bfd.h"
#include "symfile.h"
#include "target.h"
@@ -1484,8 +1485,17 @@ extended_remote_follow_fork (struct target_ops *target, int follow_child,
We will also follow vforks if they are supported. */
if (extended_remote_fork_event_p (rs))
{
- /* FIXME: Implement follow-fork here. */
- return -1;
+ 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;
}
@@ -3999,6 +4009,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,
PACKET_qXfer_siginfo_read },
@@ -4407,10 +4419,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."));
@@ -4447,19 +4461,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. */
@@ -5555,7 +5578,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);
@@ -5607,6 +5631,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. */
@@ -9418,8 +9447,11 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
if (ptid_equal (magic_null_ptid, ptid))
xsnprintf (buf, sizeof buf, "Thread <main>");
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));
@@ -12177,6 +12209,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);
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 06/16 v3] Extended-remote Linux follow fork
2014-10-31 23:29 ` [PATCH 06/16 v3] Extended-remote Linux " Don Breazeal
@ 2014-11-13 13:00 ` Pedro Alves
2014-11-13 18:53 ` Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-11-13 13:00 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 10/31/2014 11:28 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.
>
Thanks.
Let's try to shake out the higher-level concepts first before
focusing on specific details.
>
> * implementing a new RSP 'T' Stop Reply Packet stop reason: "fork", in
> gdbserver/remote-utils.c and remote.c.
"f" is an hexadecimal digit. Last time we added such a packet,
"core", we broke backwards compatibility with older GDBs... :-/
We need to careful with that.
>
> * 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
Reading through this, I don't think this is the model we should be exposing
at the RSP level, and requiring servers to support. The hiding of the child
fork until the users resumes is a current detail that we may want to change in
the future. It seems better to me to _not_ hide the child from GDB,
and then implement the hide-child-until-resume detail in GDB. That is,
I think we should model fork events at the RSP level similarly to
how ptrace and ttrace expose them. So, e.g., I think switching to the
child to write to its memory should be done with the regular Hg packet.
Handling detach_fork would be done by GDB calling the regular
detach packet (D;PID), etc. I'm not even seeing a fundamental need
to keep this for "extended-remote" alone, given gdb nowadays supports
the multiprocess extensions with "target remote" too.
Also, I don't see how this packet could work correctly with non-stop
mode. You're assuming only one thread/process has stopped for a fork.
> else
> {
> /* Silently skip unknown optional info. */
> @@ -9418,8 +9447,11 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
> if (ptid_equal (magic_null_ptid, ptid))
> xsnprintf (buf, sizeof buf, "Thread <main>");
> 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);
Can you explain this bit?
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 06/16 v3] Extended-remote Linux follow fork
2014-11-13 13:00 ` Pedro Alves
@ 2014-11-13 18:53 ` Breazeal, Don
2014-11-13 18:59 ` Pedro Alves
0 siblings, 1 reply; 110+ messages in thread
From: Breazeal, Don @ 2014-11-13 18:53 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 11/13/2014 5:00 AM, Pedro Alves wrote:
> On 10/31/2014 11:28 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.
>>
>
> Thanks.
Thanks for the review.
>
> Let's try to shake out the higher-level concepts first before
> focusing on specific details.
>
>>
>> * implementing a new RSP 'T' Stop Reply Packet stop reason: "fork", in
>> gdbserver/remote-utils.c and remote.c.
>
> "f" is an hexadecimal digit. Last time we added such a packet,
> "core", we broke backwards compatibility with older GDBs... :-/
> We need to careful with that.
Yes, I assume you saw the check in remote_parse_stop_reply next to the
checks for "core" and "awatch".
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))
I ran into the same thing with "exec". Maybe these stop reason strings
should all have a common prefix, like "stop_fork" and "stop_core".
>
>>
>> * 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
>
> Reading through this, I don't think this is the model we should be exposing
> at the RSP level, and requiring servers to support. The hiding of the child
> fork until the users resumes is a current detail that we may want to change in
> the future. It seems better to me to _not_ hide the child from GDB,
> and then implement the hide-child-until-resume detail in GDB. That is,
> I think we should model fork events at the RSP level similarly to
> how ptrace and ttrace expose them. So, e.g., I think switching to the
> child to write to its memory should be done with the regular Hg packet.
> Handling detach_fork would be done by GDB calling the regular
> detach packet (D;PID), etc. I'm not even seeing a fundamental need
> to keep this for "extended-remote" alone, given gdb nowadays supports
> the multiprocess extensions with "target remote" too.
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?
>
> Also, I don't see how this packet could work correctly with non-stop
> mode. You're assuming only one thread/process has stopped for a fork.
>
>> else
>> {
>> /* Silently skip unknown optional info. */
>> @@ -9418,8 +9447,11 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
>> if (ptid_equal (magic_null_ptid, ptid))
>> xsnprintf (buf, sizeof buf, "Thread <main>");
>> 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);
>
> Can you explain this bit?
>
> Thanks,
> Pedro Alves
>
To goal was to make the messages printed in the remote case match the
messages printed in the native case by printing "Process" ID info
instead of "Thread" ID info given a process-style ptid (pid, 0, 0).
It was not entirely successful, but if I recall correctly it was
sufficient for matching test results.
BTW, sorry for the extra noise on the list with my duplicated
response to your comments on patch #4. I have had some
mail server issues today, and mistakenly thought the first
copy had not gone out.
The changes requested here will probably take me a little while to
implement and test.
Thanks
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 06/16 v3] Extended-remote Linux follow fork
2014-11-13 18:53 ` Breazeal, Don
@ 2014-11-13 18:59 ` Pedro Alves
2014-11-13 19:06 ` Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-11-13 18:59 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
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
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 06/16 v3] Extended-remote Linux follow fork
2014-11-13 18:59 ` Pedro Alves
@ 2014-11-13 19:06 ` Breazeal, Don
2014-12-06 0:31 ` Breazeal, Don
2015-01-12 22:39 ` [PATCH 06/16 v3] Extended-remote Linux " Don Breazeal
0 siblings, 2 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-11-13 19:06 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
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
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 06/16 v3] Extended-remote Linux follow fork
2014-11-13 19:06 ` Breazeal, Don
@ 2014-12-06 0:31 ` Breazeal, Don
2015-01-23 12:53 ` Pedro Alves
2015-01-12 22:39 ` [PATCH 06/16 v3] Extended-remote Linux " Don Breazeal
1 sibling, 1 reply; 110+ messages in thread
From: Breazeal, Don @ 2014-12-06 0:31 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
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 <donb@codesourcery.com>
* gdbthread.h (struct thread_info) <pending_follow>: 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 <pid>' 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 <donb@codesourcery.com>
* 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(-)
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 <fcntl.h>
#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);
}
/* 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 <main>");
- 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;
--
1.9.1
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 06/16 v3] Extended-remote Linux follow fork
2014-12-06 0:31 ` Breazeal, Don
@ 2015-01-23 12:53 ` Pedro Alves
2015-01-23 17:18 ` Breazeal, Don
[not found] ` <1422222420-25421-1-git-send-email-donb@codesourcery.com>
0 siblings, 2 replies; 110+ messages in thread
From: Pedro Alves @ 2015-01-23 12:53 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
Hi Don,
I need to play with this in order to review it, and so
I'm trying to reconstruct the current series in my sandbox, but
I'm having troubling figuring out the pieces. Do you keep
the updated series in a branch somewhere?
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 06/16 v3] Extended-remote Linux follow fork
2015-01-23 12:53 ` Pedro Alves
@ 2015-01-23 17:18 ` Breazeal, Don
[not found] ` <1422222420-25421-1-git-send-email-donb@codesourcery.com>
1 sibling, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2015-01-23 17:18 UTC (permalink / raw)
To: Pedro Alves, Breazeal, Don, gdb-patches
On 1/23/2015 3:55 AM, Pedro Alves wrote:
> Hi Don,
>
> I need to play with this in order to review it, and so
> I'm trying to reconstruct the current series in my sandbox, but
> I'm having troubling figuring out the pieces. Do you keep
> the updated series in a branch somewhere?
>
> Thanks,
> Pedro Alves
>
Hi Pedro,
Sorry the currently posted stuff isn't working for you. My most recent
merge from upstream, yesterday, resulted in some conflicts and broke
some things like follow-vfork. You would probably run into the same
conflicts if I posted an older (working) version. I will post a new
version of the follow-fork part of the patch series as soon as I work
through the merge issue(s).
Thanks,
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
[parent not found: <1422222420-25421-1-git-send-email-donb@codesourcery.com>]
* [PATCH v4 5/7] Arch-specific remote follow fork
[not found] ` <1422222420-25421-1-git-send-email-donb@codesourcery.com>
@ 2015-01-25 21:49 ` Don Breazeal
2015-02-10 16:37 ` Pedro Alves
2015-01-25 21:49 ` [PATCH v4 6/7] Remote follow vfork Don Breazeal
` (5 subsequent siblings)
6 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2015-01-25 21:49 UTC (permalink / raw)
To: gdb-patches, palves
This patch implements the architecture-specific pieces of follow-fork
for remote and extended-remote Linux targets, which in the current
implementation copyies the parent's debug register state into the new
child's data structures. This is required for x86, arm, aarch64, and
mips.
This follows the native implementation as closely as possible by
implementing a new linux_target_ops function 'new_fork', which is
analogous to 'linux_nat_new_fork' in linux-nat.c. In gdbserver, the debug
registers are stored in the process list, instead of an
architecture-specific list, so the function arguments are process_info
pointers instead of an lwp_info and a pid as in the native implementation.
In the MIPS implementation the debug register mirror is stored differently
from x86, ARM, and aarch64, so instead of doing a simple structure assignment
I had to clone the list of watchpoint structures.
Tested using gdb.threads/watchpoint-fork.exp on x86, and ran manual tests
on a MIPS board and an ARM board.
I don't currently have access to an aarch64 board, so if someone is able
to test this easily, please do.
Thanks
--Don
gdb/gdbserver/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* linux-aarch64-low.c (aarch64_linux_new_fork): New function.
(the_low_target) <new_fork>: Initialize new member.
* linux-arm-low.c (arm_new_fork): New function.
(the_low_target) <new_fork>: Initialize new member.
* linux-low.c (handle_extended_wait): Call new target function
new_fork.
* linux-low.h (struct linux_target_ops) <new_fork>: New member.
* linux-mips-low.c (mips_add_watchpoint): New function
extracted from mips_insert_point.
(the_low_target) <new_fork>: Initialize new member.
(mips_linux_new_fork): New function.
(mips_insert_point): Call mips_add_watchpoint.
* linux-x86-low.c (x86_linux_new_fork): New function.
(the_low_target) <new_fork>: Initialize new member.
---
gdb/gdbserver/linux-aarch64-low.c | 28 +++++++++++++
gdb/gdbserver/linux-arm-low.c | 42 ++++++++++++++++++++
gdb/gdbserver/linux-low.c | 4 ++
gdb/gdbserver/linux-low.h | 3 +
gdb/gdbserver/linux-mips-low.c | 76 +++++++++++++++++++++++++++++++------
gdb/gdbserver/linux-x86-low.c | 29 ++++++++++++++
6 files changed, 170 insertions(+), 12 deletions(-)
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 6b84042..f0cd339 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -1135,6 +1135,33 @@ aarch64_linux_new_thread (void)
return info;
}
+static void
+aarch64_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
@@ -1299,6 +1326,7 @@ struct linux_target_ops the_low_target =
NULL,
aarch64_linux_new_process,
aarch64_linux_new_thread,
+ aarch64_linux_new_fork,
aarch64_linux_prepare_to_resume,
};
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 303d9c8..97f3848 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -717,6 +717,47 @@ arm_new_thread (void)
return info;
}
+static void
+arm_new_fork (struct process_info *parent, struct process_info *child)
+{
+ struct arch_process_info *parent_proc_info = parent->private->arch_private;
+ struct arch_process_info *child_proc_info = child->private->arch_private;
+ struct lwp_info *child_lwp;
+ struct arch_lwp_info *child_lwp_info;
+ int i;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child_proc_info = *parent_proc_info;
+
+ /* Mark all the hardware breakpoints and watchpoints as changed to
+ make sure that the registers will be updated. */
+ child_lwp = find_lwp_pid (ptid_of (child));
+ child_lwp_info = child_lwp->arch_private;
+ for (i = 0; i < MAX_BPTS; i++)
+ child_lwp_info->bpts_changed[i] = 1;
+ for (i = 0; i < MAX_WPTS; i++)
+ child_lwp_info->wpts_changed[i] = 1;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
static void
@@ -920,6 +961,7 @@ struct linux_target_ops the_low_target = {
NULL, /* siginfo_fixup */
arm_new_process,
arm_new_thread,
+ arm_new_fork,
arm_prepare_to_resume,
};
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 6feebd0..5af02ac 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -445,6 +445,10 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
child_proc->tdesc = tdesc;
child_lwp->must_set_ptrace_flags = 1;
+ /* Clone arch-specific process data. */
+ if (the_low_target.new_fork != NULL)
+ the_low_target.new_fork (parent_proc, child_proc);
+
/* Save fork info in the parent thread. */
event_thr->pending_follow.kind = TARGET_WAITKIND_FORKED;
event_thr->pending_follow.value.related_pid = ptid;
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index bea4a37..e65fb35 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -185,6 +185,9 @@ struct linux_target_ops
allocate it here. */
struct arch_lwp_info * (*new_thread) (void);
+ /* Hook to call, if any, when a new fork is attached. */
+ void (*new_fork) (struct process_info *parent, struct process_info *child);
+
/* Hook to call prior to resuming a thread. */
void (*prepare_to_resume) (struct lwp_info *);
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index 478bb6e..500cf3e 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -344,6 +344,68 @@ mips_linux_new_thread (void)
return info;
}
+/* Create a new mips_watchpoint and add it to the list. */
+
+static void
+mips_add_watchpoint (struct arch_process_info *private, CORE_ADDR addr,
+ int len, int watch_type)
+{
+ struct mips_watchpoint *new_watch;
+ struct mips_watchpoint **pw;
+
+ new_watch = xmalloc (sizeof (struct mips_watchpoint));
+ new_watch->addr = addr;
+ new_watch->len = len;
+ new_watch->type = watch_type;
+ new_watch->next = NULL;
+
+ pw = &private->current_watches;
+ while (*pw != NULL)
+ pw = &(*pw)->next;
+ *pw = new_watch;
+}
+
+/* Hook to call when a new fork is attached. */
+
+static void
+mips_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ struct arch_process_info *parent_private;
+ struct arch_process_info *child_private;
+ struct mips_watchpoint *wp;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ parent_private = parent->private->arch_private;
+ child_private = child->private->arch_private;
+
+ child_private->watch_readback_valid = parent_private->watch_readback_valid;
+ child_private->watch_readback = parent_private->watch_readback;
+
+ for (wp = parent_private->current_watches; wp != NULL; wp = wp->next)
+ mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type);
+
+ child_private->watch_mirror = parent_private->watch_mirror;
+}
/* This is the implementation of linux_target_ops method
prepare_to_resume. If the watch regs have changed, update the
thread's copies. */
@@ -397,8 +459,6 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
struct process_info *proc = current_process ();
struct arch_process_info *private = proc->private->arch_private;
struct pt_watch_regs regs;
- struct mips_watchpoint *new_watch;
- struct mips_watchpoint **pw;
int pid;
long lwpid;
enum target_hw_bp_type watch_type;
@@ -425,16 +485,7 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
return -1;
/* It fit. Stick it on the end of the list. */
- new_watch = xmalloc (sizeof (struct mips_watchpoint));
- new_watch->addr = addr;
- new_watch->len = len;
- new_watch->type = watch_type;
- new_watch->next = NULL;
-
- pw = &private->current_watches;
- while (*pw != NULL)
- pw = &(*pw)->next;
- *pw = new_watch;
+ mips_add_watchpoint (private, addr, len, watch_type);
private->watch_mirror = regs;
@@ -845,6 +896,7 @@ struct linux_target_ops the_low_target = {
NULL, /* siginfo_fixup */
mips_linux_new_process,
mips_linux_new_thread,
+ mips_linux_new_fork,
mips_linux_prepare_to_resume
};
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 2c3fccc..b7138e9 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -768,6 +768,34 @@ x86_linux_new_thread (void)
return info;
}
+/* Target routine for linux_new_fork. */
+
+static void
+x86_linux_new_fork (struct process_info *parent, struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
@@ -3428,6 +3456,7 @@ struct linux_target_ops the_low_target =
x86_siginfo_fixup,
x86_linux_new_process,
x86_linux_new_thread,
+ x86_linux_new_fork,
x86_linux_prepare_to_resume,
x86_linux_process_qsupported,
x86_supports_tracepoints,
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH v4 5/7] Arch-specific remote follow fork
2015-01-25 21:49 ` [PATCH v4 5/7] Arch-specific remote " Don Breazeal
@ 2015-02-10 16:37 ` Pedro Alves
0 siblings, 0 replies; 110+ messages in thread
From: Pedro Alves @ 2015-02-10 16:37 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 01/25/2015 09:46 PM, Don Breazeal wrote:
> This patch implements the architecture-specific pieces of follow-fork
> for remote and extended-remote Linux targets, which in the current
> implementation copyies the parent's debug register state into the new
> child's data structures. This is required for x86, arm, aarch64, and
> mips.
>
> This follows the native implementation as closely as possible by
> implementing a new linux_target_ops function 'new_fork', which is
> analogous to 'linux_nat_new_fork' in linux-nat.c. In gdbserver, the debug
> registers are stored in the process list, instead of an
> architecture-specific list, so the function arguments are process_info
> pointers instead of an lwp_info and a pid as in the native implementation.
>
> In the MIPS implementation the debug register mirror is stored differently
> from x86, ARM, and aarch64, so instead of doing a simple structure assignment
> I had to clone the list of watchpoint structures.
>
> Tested using gdb.threads/watchpoint-fork.exp on x86, and ran manual tests
> on a MIPS board and an ARM board.
>
> I don't currently have access to an aarch64 board, so if someone is able
> to test this easily, please do.
LGTM. I'd prefer moving this earlier, before the "target remote"
changes; I think we may need to iterate those a bit more.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH v4 6/7] Remote follow vfork
[not found] ` <1422222420-25421-1-git-send-email-donb@codesourcery.com>
2015-01-25 21:49 ` [PATCH v4 5/7] Arch-specific remote " Don Breazeal
@ 2015-01-25 21:49 ` Don Breazeal
2015-02-10 16:39 ` Pedro Alves
2015-01-25 21:50 ` [PATCH v4 2/7] Clone remote breakpoints Don Breazeal
` (4 subsequent siblings)
6 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2015-01-25 21:49 UTC (permalink / raw)
To: gdb-patches, palves
This patch implements follow-fork for vfork on remote and 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.
* including VFORK and VFORK_DONE events in the predicate
linux-low.c:extended_event_reported.
* adding 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.
Thanks
--Don
gdb/gdbserver/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* linux-low.c (handle_extended_wait): Handle PTRACE_EVENT_FORK and
PTRACE_EVENT_VFORK_DONE.
(linux_low_enable_events): Prevent enablement of VFORK events if
GDB has not requested them.
(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.
* server.h (report_vfork_events): Declare
global variable.
gdb/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* remote.c (remote_follow_fork): Add vfork event type to assert.
(remote_detach_1): Don't call inf_child_maybe_unpush_target for
extended-mode targets.
(remote_parse_stop_reply): New stop reasons "vfork" and
"vforkdone" for RSP 'T' Stop Reply Packet.
(remote_kill): Don't call inf_child_maybe_unpush_target for
extended-mode targets.
---
gdb/gdbserver/linux-low.c | 28 ++++++++++++++++++++++------
gdb/gdbserver/remote-utils.c | 16 ++++++++++++++--
gdb/gdbserver/server.h | 1 +
gdb/remote.c | 27 ++++++++++++++++++++++++---
4 files changed, 61 insertions(+), 11 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 5af02ac..66b1729 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -381,7 +381,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;
@@ -407,7 +408,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;
@@ -449,8 +450,12 @@ 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 in the parent thread. */
- event_thr->pending_follow.kind = TARGET_WAITKIND_FORKED;
+ /* Save fork info for target processing and reporting to GDB. */
+ if (event == PTRACE_EVENT_FORK)
+ event_thr->pending_follow.kind = TARGET_WAITKIND_FORKED;
+ else if (event == PTRACE_EVENT_VFORK)
+ event_thr->pending_follow.kind = TARGET_WAITKIND_VFORKED;
+
event_thr->pending_follow.value.related_pid = ptid;
/* Report the event. */
@@ -507,6 +512,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_thr->pending_follow.kind = TARGET_WAITKIND_VFORK_DONE;
+ return 0;
+ }
internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
}
@@ -1883,6 +1893,8 @@ linux_low_enable_events (pid_t pid, int attached)
/* If GDB doesn't want fork events, don't enable them. */
if (!report_fork_events)
linux_ptrace_clear_additional_flags (PTRACE_O_TRACEFORK);
+ if (!report_vfork_events)
+ linux_ptrace_clear_additional_flags (PTRACE_O_TRACEVFORK);
/* Enable all supported and requested events. */
linux_enable_event_reporting (pid, attached);
@@ -2541,7 +2553,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. */
@@ -6246,6 +6260,8 @@ initialize_low (void)
initialize_low_arch ();
/* Enable any extended ptrace events that are supported. */
- linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK);
+ linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEVFORKDONE);
linux_test_for_event_reporting ();
}
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index f0cc882..0a4b405 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1115,15 +1115,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),
@@ -1234,6 +1237,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/gdbserver/server.h b/gdb/gdbserver/server.h
index ce6ffb9..5284dac 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -85,6 +85,7 @@ extern int disable_packet_qfThreadInfo;
extern int run_once;
extern int multi_process;
extern int report_fork_events;
+extern int report_vfork_events;
extern int non_stop;
extern int disable_randomization;
diff --git a/gdb/remote.c b/gdb/remote.c
index 9e48205..6ea3c60 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1481,7 +1481,10 @@ remote_follow_fork (struct target_ops *target, int follow_child,
pid_t child_pid;
gdb_assert ((inferior_thread ()->pending_follow.kind
- == TARGET_WAITKIND_FORKED));
+ == 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);
@@ -4502,7 +4505,8 @@ remote_detach_1 (struct target_ops *ops, const char *args,
{
inferior_ptid = null_ptid;
detach_inferior (pid);
- inf_child_maybe_unpush_target (ops);
+ if (!extended)
+ inf_child_maybe_unpush_target (ops);
}
}
@@ -5669,6 +5673,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. */
@@ -7929,6 +7948,7 @@ remote_kill (struct target_ops *ops)
{
volatile struct gdb_exception ex;
int pid = ptid_get_pid (inferior_ptid);
+ struct remote_state *rs = get_remote_state ();
struct thread_info *tp = first_thread_of_process (pid);
/* Catch errors so the user can quit from gdb even when we
@@ -7965,7 +7985,8 @@ remote_kill (struct target_ops *ops)
{
inferior_ptid = null_ptid;
detach_inferior (pid);
- inf_child_maybe_unpush_target (ops);
+ if (!rs->extended)
+ inf_child_maybe_unpush_target (ops);
}
}
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH v4 6/7] Remote follow vfork
2015-01-25 21:49 ` [PATCH v4 6/7] Remote follow vfork Don Breazeal
@ 2015-02-10 16:39 ` Pedro Alves
0 siblings, 0 replies; 110+ messages in thread
From: Pedro Alves @ 2015-02-10 16:39 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
Hi Don,
No major comments here, other than what's been said in
previous patches. A couple remarks only.
On 01/25/2015 09:46 PM, Don Breazeal wrote:
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -1481,7 +1481,10 @@ remote_follow_fork (struct target_ops *target, int follow_child,
> pid_t child_pid;
>
> gdb_assert ((inferior_thread ()->pending_follow.kind
> - == TARGET_WAITKIND_FORKED));
> + == TARGET_WAITKIND_FORKED)
> + || (inferior_thread ()->pending_follow.kind
> + == TARGET_WAITKIND_VFORKED));
> +
Please use a temporary variable, something like:
kind = inferior_thread ()->pending_follow.kind
gdb_assert (kind == TARGET_WAITKIND_FORKED)
|| (kind == TARGET_WAITKIND_VFORKED));
Makes it easier to debug a core dump caused by this assert.
> child_ptid = inferior_thread ()->pending_follow.value.related_pid;
> child_pid = ptid_get_pid (child_ptid);
>
> @@ -4502,7 +4505,8 @@ remote_detach_1 (struct target_ops *ops, const char *args,
> {
> inferior_ptid = null_ptid;
> detach_inferior (pid);
> - inf_child_maybe_unpush_target (ops);
> + if (!extended)
> + inf_child_maybe_unpush_target (ops);
Hmm, I'm afraid I don't understand why is this part of this patch?
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH v4 2/7] Clone remote breakpoints
[not found] ` <1422222420-25421-1-git-send-email-donb@codesourcery.com>
2015-01-25 21:49 ` [PATCH v4 5/7] Arch-specific remote " Don Breazeal
2015-01-25 21:49 ` [PATCH v4 6/7] Remote follow vfork Don Breazeal
@ 2015-01-25 21:50 ` Don Breazeal
2015-01-25 21:50 ` [PATCH v4 1/7] Identify remote fork event support Don Breazeal
` (3 subsequent siblings)
6 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2015-01-25 21:50 UTC (permalink / raw)
To: gdb-patches, palves
This patch implements gdbserver routines to clone the breakpoint lists of a
process, duplicating them for another process. In gdbserver, each process
maintains its own independent breakpoint list. When a fork call creates a
child, all of the breakpoints currently inserted in the parent process are
also inserted in the child process, but there is nothing to describe them
in the data structures related to the child. The child must have a
breakpoint list describing them so that they can be removed (if detaching)
or recognized (if following). Implementation is a mechanical process of
just cloning the lists in several new functions in gdbserver/mem-break.c.
Tested by building, since none of the new functions are called yet. This
was tested with another patch in the series that implements follow-fork.
Thanks
--Don
gdb/gdbserver/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* gdb/gdbserver/mem-break.c (APPEND_TO_LIST): Define macro.
(clone_agent_expr): New function.
(clone_one_breakpoint): New function.
(clone_all_breakpoints): New function.
* gdb/gdbserver/mem-break.h: Declare new functions.
---
gdb/gdbserver/mem-break.c | 105 +++++++++++++++++++++++++++++++++++++++++++++
gdb/gdbserver/mem-break.h | 6 +++
2 files changed, 111 insertions(+), 0 deletions(-)
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index 70fab2e..d1b66bf 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -28,6 +28,24 @@ int breakpoint_len;
#define MAX_BREAKPOINT_LEN 8
+/* Helper macro used in loops that append multiple items to a singly-linked
+ list instead of inserting items at the head of the list, as, say, in the
+ breakpoint lists. LISTPP is a pointer to the pointer that is the head of
+ the new list. ITEMP is a pointer to the item to be added to the list.
+ TAILP must be defined to be the same type as ITEMP, and initialized to
+ NULL. */
+
+#define APPEND_TO_LIST(listpp, itemp, tailp) \
+ do \
+ { \
+ if ((tailp) == NULL) \
+ *(listpp) = (itemp); \
+ else \
+ (tailp)->next = (itemp); \
+ (tailp) = (itemp); \
+ } \
+ while (0)
+
/* GDB will never try to install multiple breakpoints at the same
address. However, we can see GDB requesting to insert a breakpoint
at an address is had already inserted one previously in a few
@@ -1913,3 +1931,90 @@ free_all_breakpoints (struct process_info *proc)
while (proc->breakpoints)
delete_breakpoint_1 (proc, proc->breakpoints);
}
+
+/* Clone an agent expression. */
+
+static struct agent_expr *
+clone_agent_expr (const struct agent_expr *src_ax)
+{
+ struct agent_expr *ax;
+
+ ax = xcalloc (1, sizeof (*ax));
+ ax->length = src_ax->length;
+ ax->bytes = xcalloc (ax->length, 1);
+ memcpy (ax->bytes, src_ax->bytes, ax->length);
+ return ax;
+}
+
+/* Deep-copy the contents of one breakpoint to another. */
+
+static struct breakpoint *
+clone_one_breakpoint (const struct breakpoint *src)
+{
+ struct breakpoint *dest;
+ struct raw_breakpoint *dest_raw;
+ struct point_cond_list *current_cond;
+ struct point_cond_list *new_cond;
+ struct point_cond_list *cond_tail = NULL;
+ struct point_command_list *current_cmd;
+ struct point_command_list *new_cmd;
+ struct point_command_list *cmd_tail = NULL;
+
+ /* Clone the raw breakpoint. */
+ dest_raw = xcalloc (1, sizeof (*dest_raw));
+ dest_raw->raw_type = src->raw->raw_type;
+ dest_raw->refcount = src->raw->refcount;
+ dest_raw->pc = src->raw->pc;
+ dest_raw->size = src->raw->size;
+ memcpy (dest_raw->old_data, src->raw->old_data, MAX_BREAKPOINT_LEN);
+ dest_raw->inserted = src->raw->inserted;
+
+ /* Clone the high-level breakpoint. */
+ dest = xcalloc (1, sizeof (*dest));
+ dest->type = src->type;
+ dest->raw = dest_raw;
+ dest->handler = src->handler;
+
+ /* Clone the condition list. */
+ for (current_cond = src->cond_list; current_cond != NULL;
+ current_cond = current_cond->next)
+ {
+ new_cond = xcalloc (1, sizeof (*new_cond));
+ new_cond->cond = clone_agent_expr (current_cond->cond);
+ APPEND_TO_LIST (&dest->cond_list, new_cond, cond_tail);
+ }
+
+ /* Clone the command list. */
+ for (current_cmd = src->command_list; current_cmd != NULL;
+ current_cmd = current_cmd->next)
+ {
+ new_cmd = xcalloc (1, sizeof (*new_cmd));
+ new_cmd->cmd = clone_agent_expr (current_cmd->cmd);
+ new_cmd->persistence = current_cmd->persistence;
+ APPEND_TO_LIST (&dest->command_list, new_cmd, cmd_tail);
+ }
+
+ return dest;
+}
+
+/* Create a new breakpoint list NEW_LIST that is a copy of the
+ list starting at SRC_LIST. Create the corresponding new
+ raw_breakpoint list NEW_RAW_LIST as well. */
+
+void
+clone_all_breakpoints (struct breakpoint **new_list,
+ struct raw_breakpoint **new_raw_list,
+ const struct breakpoint *src_list)
+{
+ const struct breakpoint *bp;
+ struct breakpoint *new_bkpt;
+ struct breakpoint *bkpt_tail = NULL;
+ struct raw_breakpoint *raw_bkpt_tail = NULL;
+
+ for (bp = src_list; bp != NULL; bp = bp->next)
+ {
+ new_bkpt = clone_one_breakpoint (bp);
+ APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);
+ APPEND_TO_LIST (new_raw_list, new_bkpt->raw, raw_bkpt_tail);
+ }
+}
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index 8b010c1..b5a3208 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -253,4 +253,10 @@ int insert_memory_breakpoint (struct raw_breakpoint *bp);
int remove_memory_breakpoint (struct raw_breakpoint *bp);
+/* Create a new breakpoint list NEW_BKPT_LIST that is a copy of SRC. */
+
+void clone_all_breakpoints (struct breakpoint **new_bkpt_list,
+ struct raw_breakpoint **new_raw_bkpt_list,
+ const struct breakpoint *src);
+
#endif /* MEM_BREAK_H */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH v4 1/7] Identify remote fork event support
[not found] ` <1422222420-25421-1-git-send-email-donb@codesourcery.com>
` (2 preceding siblings ...)
2015-01-25 21:50 ` [PATCH v4 2/7] Clone remote breakpoints Don Breazeal
@ 2015-01-25 21:50 ` Don Breazeal
2015-02-10 16:34 ` Pedro Alves
2015-01-25 21:58 ` [PATCH v4 7/7] Remote fork catch Don Breazeal
` (2 subsequent siblings)
6 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2015-01-25 21:50 UTC (permalink / raw)
To: gdb-patches, palves
This patch implements a mechanism for GDB to determine whether fork
events are supported in gdbserver. This is a preparatory patch for
remote fork and exec event support.
Two new RSP packets are defined to represent fork and vfork event
support. These packets are used just like PACKET_multiprocess_feature
to denote whether the corresponding event is supported. GDB sends
fork-events+ and vfork-events+ to gdbserver to inquire about fork
event support. If the response enables these packets, then GDB
knows that gdbserver supports the corresponding events and will
enable them.
Target functions used to query for support are included along with
each new packet.
In order for gdbserver to know whether the events are supported at the
point where the qSupported packet arrives, the code in nat/linux-ptrace.c
had to be reorganized. Previously it would test for fork/exec event
support, then enable the events using the pid of the inferior. When the
qSupported packet arrives there may not be an inferior. So the mechanism
was split into two parts: a function that checks whether the events are
supported, called when gdbserver starts up, and another that enables the
events when the inferior stops for the first time.
Another gdbserver change was to add some global variables similar to
multi_process, one per new packet. These are used to control whether
the corresponsing fork events are enabled. If GDB does not inquire
about the event support in the qSupported packet, then gdbserver will
not set these "report the event" flags. If the flags are not set, the
events are ignored like they were in the past.
There is an #if that has been added temporarily to allow the code for
managing the events to be included in this patch without enabling the
events. See PTRACE_FORK_EVENTS in gdb/remote.c. This #if will be
removed later in the patch series as the events are enabled.
Tested on Ubuntu x64, native/remote/extended-remote, and as part of
subsequent patches in the series.
Thanks,
--Don
gdb/gdbserver/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* linux-low.c (linux_supports_fork_events): New function.
(linux_supports_vfork_events): New function.
(linux_target_ops): Initialize new structure members.
(initialize_low): Call linux_ptrace_set_additional_flags
and linux_test_for_event_reporting.
* lynx-low.c (lynx_target_ops): Initialize new structure
members.
* server.c (report_fork_events, report_vfork_events):
New global flags.
(handle_query): Add new features to qSupported packet.
(captured_main): Initialize new global variables.
* target.h (struct target_ops) <supports_fork_events>:
New member.
<supports_vfork_events>: New member.
(target_supports_fork_events): New macro.
(target_supports_vfork_events): New macro.
* win32-low.c (win32_target_ops): Initialize new structure
members.
gdb/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* nat/linux-ptrace.c (linux_test_for_event_reporting): Rename
from linux_check_ptrace_features and make it extern.
(linux_test_for_tracefork): Reformat code.
(linux_enable_event_reporting): Change name of called function
to linux_check_ptrace_features.
(ptrace_supports_feature): Call linux_test_for_event_reporting
instead of linux_check_ptrace_features.
* nat/linux-ptrace.h: Declare linux_test_for_event_reporting.
* remote.c (anonymous enum) <PACKET_fork_event_feature,
PACKET_vfork_event_feature>: New enumeration constants.
(remote_fork_event_p): New function.
(remote_vfork_event_p): New function.
(remote_query_supported): Add new feature queries to qSupported
packet.
(remote_protocol_features): Add table entries for new packets.
(_initialize_remote): Exempt new packets from the requirement
to have 'set remote' commands.
---
gdb/gdbserver/linux-low.c | 22 ++++++++++++++++++++++
gdb/gdbserver/lynx-low.c | 2 ++
gdb/gdbserver/server.c | 22 ++++++++++++++++++++++
gdb/gdbserver/target.h | 14 ++++++++++++++
gdb/gdbserver/win32-low.c | 2 ++
gdb/nat/linux-ptrace.c | 25 +++++++++++++------------
gdb/nat/linux-ptrace.h | 1 +
gdb/remote.c | 41 +++++++++++++++++++++++++++++++++++++++--
8 files changed, 115 insertions(+), 14 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 5e37dd5..e31844c 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5123,6 +5123,22 @@ linux_supports_multi_process (void)
return 1;
}
+/* Check if fork events are supported. */
+
+static int
+linux_supports_fork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if vfork events are supported. */
+
+static int
+linux_supports_vfork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
static int
linux_supports_disable_randomization (void)
{
@@ -6034,6 +6050,8 @@ static struct target_ops linux_target_ops = {
linux_async,
linux_start_non_stop,
linux_supports_multi_process,
+ linux_supports_fork_events,
+ linux_supports_vfork_events,
#ifdef USE_THREAD_DB
thread_db_handle_monitor_command,
#else
@@ -6108,4 +6126,8 @@ initialize_low (void)
sigaction (SIGCHLD, &sigchld_action, NULL);
initialize_low_arch ();
+
+ /* Enable any extended ptrace events that are supported. */
+ linux_ptrace_set_additional_flags (0);
+ linux_test_for_event_reporting ();
}
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 98797ba..1126103 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -754,6 +754,8 @@ static struct target_ops lynx_target_ops = {
NULL, /* async */
NULL, /* start_non_stop */
NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
NULL, /* handle_monitor_command */
};
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 0e72cf1..48cc363 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -57,6 +57,8 @@ static int exit_requested;
int run_once;
int multi_process;
+int report_fork_events;
+int report_vfork_events;
int non_stop;
/* Whether we should attempt to disable the operating system's address
@@ -1841,6 +1843,18 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
/* GDB supports relocate instruction requests. */
gdb_supports_qRelocInsn = 1;
}
+ if (strcmp (p, "fork-events+") == 0)
+ {
+ /* GDB supports and wants fork events if possible. */
+ if (target_supports_fork_events ())
+ report_fork_events = 1;
+ }
+ if (strcmp (p, "vfork-events+") == 0)
+ {
+ /* GDB supports and wants vfork events if possible. */
+ if (target_supports_vfork_events ())
+ report_vfork_events = 1;
+ }
else
target_process_qsupported (p);
@@ -1891,6 +1905,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_multi_process ())
strcat (own_buf, ";multiprocess+");
+ if (target_supports_fork_events ())
+ strcat (own_buf, ";fork-events+");
+
+ if (target_supports_vfork_events ())
+ strcat (own_buf, ";vfork-events+");
+
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
@@ -3242,6 +3262,8 @@ captured_main (int argc, char *argv[])
noack_mode = 0;
multi_process = 0;
+ report_fork_events = 0;
+ report_vfork_events = 0;
/* Be sure we're out of tfind mode. */
current_traceframe = -1;
cont_thread = null_ptid;
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index bbb0567..8af76d9 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -262,6 +262,12 @@ struct target_ops
/* Returns true if the target supports multi-process debugging. */
int (*supports_multi_process) (void);
+ /* Returns true if fork events are supported. */
+ int (*supports_fork_events) (void);
+
+ /* Returns true if vfork events are supported. */
+ int (*supports_vfork_events) (void);
+
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
@@ -387,6 +393,14 @@ void set_target_ops (struct target_ops *);
int kill_inferior (int);
+#define target_supports_fork_events() \
+ (the_target->supports_fork_events ? \
+ (*the_target->supports_fork_events) () : 0)
+
+#define target_supports_vfork_events() \
+ (the_target->supports_vfork_events ? \
+ (*the_target->supports_vfork_events) () : 0)
+
#define detach_inferior(pid) \
(*the_target->detach) (pid)
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index e3fb618..210a747 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1823,6 +1823,8 @@ static struct target_ops win32_target_ops = {
NULL, /* async */
NULL, /* start_non_stop */
NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
NULL, /* handle_monitor_command */
NULL, /* core_of_thread */
NULL, /* read_loadmap */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 0ce258f..3e1fcd5 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -299,7 +299,7 @@ linux_fork_to_function (gdb_byte *child_stack, void (*function) (gdb_byte *))
return child_pid;
}
-/* A helper function for linux_check_ptrace_features, called after
+/* A helper function for linux_test_for_event_reporting, called after
the child forks a grandchild. */
static void
@@ -313,7 +313,7 @@ linux_grandchild_function (gdb_byte *child_stack)
_exit (0);
}
-/* A helper function for linux_check_ptrace_features, called after
+/* A helper function for linux_test_for_event_reporting, called after
the parent process forks a child. The child allows itself to
be traced by its parent. */
@@ -337,8 +337,8 @@ static void linux_test_for_exitkill (int child_pid);
/* Determine ptrace features available on this target. */
-static void
-linux_check_ptrace_features (void)
+void
+linux_test_for_event_reporting (void)
{
int child_pid, ret, status;
@@ -355,10 +355,10 @@ linux_check_ptrace_features (void)
if (ret == -1)
perror_with_name (("waitpid"));
else if (ret != child_pid)
- error (_("linux_check_ptrace_features: waitpid: unexpected result %d."),
+ error (_("linux_test_for_event_reporting: waitpid: unexpected result %d."),
ret);
if (! WIFSTOPPED (status))
- error (_("linux_check_ptrace_features: waitpid: unexpected status %d."),
+ error (_("linux_test_for_event_reporting: waitpid: unexpected status %d."),
status);
linux_test_for_tracesysgood (child_pid);
@@ -373,7 +373,7 @@ linux_check_ptrace_features (void)
ret = ptrace (PTRACE_KILL, child_pid, (PTRACE_TYPE_ARG3) 0,
(PTRACE_TYPE_ARG4) 0);
if (ret != 0)
- warning (_("linux_check_ptrace_features: failed to kill child"));
+ warning (_("linux_test_for_event_reporting: failed to kill child"));
my_waitpid (child_pid, &status, 0);
}
while (WIFSTOPPED (status));
@@ -459,9 +459,10 @@ linux_test_for_tracefork (int child_pid)
/* We got the PID from the grandchild, which means fork
tracing is supported. */
current_ptrace_options |= PTRACE_O_TRACECLONE;
- current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEEXEC));
+ current_ptrace_options |= (additional_flags
+ & (PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEEXEC));
/* Do some cleanup and kill the grandchild. */
my_waitpid (second_pid, &second_status, 0);
@@ -503,7 +504,7 @@ linux_enable_event_reporting (pid_t pid, int attached)
/* Check if we have initialized the ptrace features for this
target. If not, do it now. */
if (current_ptrace_options == -1)
- linux_check_ptrace_features ();
+ linux_test_for_event_reporting ();
ptrace_options = current_ptrace_options;
if (attached)
@@ -535,7 +536,7 @@ static int
ptrace_supports_feature (int ptrace_options)
{
if (current_ptrace_options == -1)
- linux_check_ptrace_features ();
+ linux_test_for_event_reporting ();
return ((current_ptrace_options & ptrace_options) == ptrace_options);
}
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 137b61a..d75b37c 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -98,6 +98,7 @@ extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
extern char *linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err);
extern void linux_ptrace_init_warnings (void);
+extern void linux_test_for_event_reporting (void);
extern void linux_enable_event_reporting (pid_t pid, int attached);
extern void linux_disable_event_reporting (pid_t pid);
extern int linux_supports_tracefork (void);
diff --git a/gdb/remote.c b/gdb/remote.c
index c4d09ba..ec1f1d2 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1327,6 +1327,12 @@ enum {
/* Support for qXfer:libraries-svr4:read with a non-empty annex. */
PACKET_augmented_libraries_svr4_read_feature,
+ /* Support for fork events. */
+ PACKET_fork_event_feature,
+
+ /* Support for vfork events. */
+ PACKET_vfork_event_feature,
+
PACKET_MAX
};
@@ -1432,6 +1438,24 @@ remote_multi_process_p (struct remote_state *rs)
return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
}
+#if PTRACE_FORK_EVENTS
+/* Returns true if fork events are supported. */
+
+static int
+remote_fork_event_p (struct remote_state *rs)
+{
+ return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
+}
+
+/* Returns true if vfork events are supported. */
+
+static int
+remote_vfork_event_p (struct remote_state *rs)
+{
+ return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
+}
+#endif
+
/* 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;
@@ -4010,7 +4034,11 @@ static const struct protocol_feature remote_protocol_features[] = {
{ "Qbtrace:off", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_off },
{ "Qbtrace:bts", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_bts },
{ "qXfer:btrace:read", PACKET_DISABLE, remote_supported_packet,
- PACKET_qXfer_btrace }
+ PACKET_qXfer_btrace },
+ { "fork-events", PACKET_DISABLE, remote_supported_packet,
+ PACKET_fork_event_feature },
+ { "vfork-events", PACKET_DISABLE, remote_supported_packet,
+ PACKET_vfork_event_feature },
};
static char *remote_support_xml;
@@ -4084,6 +4112,12 @@ remote_query_supported (void)
q = remote_query_supported_append (q, "qRelocInsn+");
+ if (rs->extended)
+ {
+ q = remote_query_supported_append (q, "fork-events+");
+ q = remote_query_supported_append (q, "vfork-events+");
+ }
+
q = reconcat (q, "qSupported:", q, (char *) NULL);
putpkt (q);
@@ -12191,7 +12225,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace],
"qXfer:btrace", "read-btrace", 0);
- /* Assert that we've registered commands for all packet configs. */
+ /* Assert that we've registered "set remote foo-packet" commands
+ for all packet configs. */
{
int i;
@@ -12210,6 +12245,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
case PACKET_DisconnectedTracing_feature:
case PACKET_augmented_libraries_svr4_read_feature:
case PACKET_qCRC:
+ case PACKET_fork_event_feature:
+ case PACKET_vfork_event_feature:
/* Additions to this list need to be well justified:
pre-existing packets are OK; new packets are not. */
excepted = 1;
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH v4 1/7] Identify remote fork event support
2015-01-25 21:50 ` [PATCH v4 1/7] Identify remote fork event support Don Breazeal
@ 2015-02-10 16:34 ` Pedro Alves
0 siblings, 0 replies; 110+ messages in thread
From: Pedro Alves @ 2015-02-10 16:34 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
Hi Don,
On 01/25/2015 09:46 PM, Don Breazeal wrote:
> This patch implements a mechanism for GDB to determine whether fork
> events are supported in gdbserver. This is a preparatory patch for
> remote fork and exec event support.
>
> Two new RSP packets are defined to represent fork and vfork event
> support. These packets are used just like PACKET_multiprocess_feature
> to denote whether the corresponding event is supported. GDB sends
> fork-events+ and vfork-events+ to gdbserver to inquire about fork
> event support. If the response enables these packets, then GDB
> knows that gdbserver supports the corresponding events and will
> enable them.
>
> Target functions used to query for support are included along with
> each new packet.
>
> In order for gdbserver to know whether the events are supported at the
> point where the qSupported packet arrives, the code in nat/linux-ptrace.c
> had to be reorganized. Previously it would test for fork/exec event
> support, then enable the events using the pid of the inferior. When the
> qSupported packet arrives there may not be an inferior. So the mechanism
> was split into two parts: a function that checks whether the events are
> supported, called when gdbserver starts up, and another that enables the
> events when the inferior stops for the first time.
>
> Another gdbserver change was to add some global variables similar to
> multi_process, one per new packet. These are used to control whether
> the corresponsing fork events are enabled. If GDB does not inquire
> about the event support in the qSupported packet, then gdbserver will
> not set these "report the event" flags. If the flags are not set, the
> events are ignored like they were in the past.
>
> There is an #if that has been added temporarily to allow the code for
> managing the events to be included in this patch without enabling the
> events. See PTRACE_FORK_EVENTS in gdb/remote.c. This #if will be
> removed later in the patch series as the events are enabled.
(Nit: I'd prefer that these code bits / hunks were simply moved
to the patches that make use of them.)
>
> Tested on Ubuntu x64, native/remote/extended-remote, and as part of
> subsequent patches in the series.
>
> Thanks,
> --Don
>
> gdb/gdbserver/
> 2015-01-25 Don Breazeal <donb@codesourcery.com>
>
> * linux-low.c (linux_supports_fork_events): New function.
> (linux_supports_vfork_events): New function.
> (linux_target_ops): Initialize new structure members.
> (initialize_low): Call linux_ptrace_set_additional_flags
> and linux_test_for_event_reporting.
> * lynx-low.c (lynx_target_ops): Initialize new structure
> members.
> * server.c (report_fork_events, report_vfork_events):
> New global flags.
> (handle_query): Add new features to qSupported packet.
> (captured_main): Initialize new global variables.
> * target.h (struct target_ops) <supports_fork_events>:
> New member.
> <supports_vfork_events>: New member.
> (target_supports_fork_events): New macro.
> (target_supports_vfork_events): New macro.
> * win32-low.c (win32_target_ops): Initialize new structure
> members.
>
> gdb/
> 2015-01-25 Don Breazeal <donb@codesourcery.com>
>
> * nat/linux-ptrace.c (linux_test_for_event_reporting): Rename
> from linux_check_ptrace_features and make it extern.
> (linux_test_for_tracefork): Reformat code.
> (linux_enable_event_reporting): Change name of called function
> to linux_check_ptrace_features.
> (ptrace_supports_feature): Call linux_test_for_event_reporting
> instead of linux_check_ptrace_features.
> * nat/linux-ptrace.h: Declare linux_test_for_event_reporting.
> * remote.c (anonymous enum) <PACKET_fork_event_feature,
> PACKET_vfork_event_feature>: New enumeration constants.
> (remote_fork_event_p): New function.
> (remote_vfork_event_p): New function.
> (remote_query_supported): Add new feature queries to qSupported
> packet.
> (remote_protocol_features): Add table entries for new packets.
> (_initialize_remote): Exempt new packets from the requirement
> to have 'set remote' commands.
>
> ---
> gdb/gdbserver/linux-low.c | 22 ++++++++++++++++++++++
> gdb/gdbserver/lynx-low.c | 2 ++
> gdb/gdbserver/server.c | 22 ++++++++++++++++++++++
> gdb/gdbserver/target.h | 14 ++++++++++++++
> gdb/gdbserver/win32-low.c | 2 ++
> gdb/nat/linux-ptrace.c | 25 +++++++++++++------------
> gdb/nat/linux-ptrace.h | 1 +
> gdb/remote.c | 41 +++++++++++++++++++++++++++++++++++++++--
> 8 files changed, 115 insertions(+), 14 deletions(-)
>
> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index 5e37dd5..e31844c 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -5123,6 +5123,22 @@ linux_supports_multi_process (void)
> return 1;
> }
>
> +/* Check if fork events are supported. */
> +
> +static int
> +linux_supports_fork_events (void)
> +{
> + return linux_supports_tracefork ();
> +}
> +
> +/* Check if vfork events are supported. */
> +
> +static int
> +linux_supports_vfork_events (void)
> +{
> + return linux_supports_tracefork ();
> +}
> +
> static int
> linux_supports_disable_randomization (void)
> {
> @@ -6034,6 +6050,8 @@ static struct target_ops linux_target_ops = {
> linux_async,
> linux_start_non_stop,
> linux_supports_multi_process,
> + linux_supports_fork_events,
> + linux_supports_vfork_events,
> #ifdef USE_THREAD_DB
> thread_db_handle_monitor_command,
> #else
> @@ -6108,4 +6126,8 @@ initialize_low (void)
> sigaction (SIGCHLD, &sigchld_action, NULL);
>
> initialize_low_arch ();
> +
> + /* Enable any extended ptrace events that are supported. */
> + linux_ptrace_set_additional_flags (0);
Likewise, seems pointless add this call now.
> + linux_test_for_event_reporting ();
> }
> diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
> index 98797ba..1126103 100644
> --- a/gdb/gdbserver/lynx-low.c
> +++ b/gdb/gdbserver/lynx-low.c
> @@ -754,6 +754,8 @@ static struct target_ops lynx_target_ops = {
> NULL, /* async */
> NULL, /* start_non_stop */
> NULL, /* supports_multi_process */
> + NULL, /* supports_fork_events */
> + NULL, /* supports_vfork_events */
> NULL, /* handle_monitor_command */
> };
>
> diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> index 0e72cf1..48cc363 100644
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -57,6 +57,8 @@ static int exit_requested;
> int run_once;
>
> int multi_process;
> +int report_fork_events;
> +int report_vfork_events;
> int non_stop;
>
> /* Whether we should attempt to disable the operating system's address
> @@ -1841,6 +1843,18 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> /* GDB supports relocate instruction requests. */
> gdb_supports_qRelocInsn = 1;
> }
> + if (strcmp (p, "fork-events+") == 0)
> + {
> + /* GDB supports and wants fork events if possible. */
> + if (target_supports_fork_events ())
> + report_fork_events = 1;
> + }
> + if (strcmp (p, "vfork-events+") == 0)
> + {
> + /* GDB supports and wants vfork events if possible. */
> + if (target_supports_vfork_events ())
> + report_vfork_events = 1;
> + }
> else
> target_process_qsupported (p);
>
> @@ -1891,6 +1905,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> if (target_supports_multi_process ())
> strcat (own_buf, ";multiprocess+");
>
> + if (target_supports_fork_events ())
> + strcat (own_buf, ";fork-events+");
> +
> + if (target_supports_vfork_events ())
> + strcat (own_buf, ";vfork-events+");
> +
> if (target_supports_non_stop ())
> strcat (own_buf, ";QNonStop+");
>
> @@ -3242,6 +3262,8 @@ captured_main (int argc, char *argv[])
>
> noack_mode = 0;
> multi_process = 0;
> + report_fork_events = 0;
> + report_vfork_events = 0;
> /* Be sure we're out of tfind mode. */
> current_traceframe = -1;
> cont_thread = null_ptid;
> diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
> index bbb0567..8af76d9 100644
> --- a/gdb/gdbserver/target.h
> +++ b/gdb/gdbserver/target.h
> @@ -262,6 +262,12 @@ struct target_ops
> /* Returns true if the target supports multi-process debugging. */
> int (*supports_multi_process) (void);
>
> + /* Returns true if fork events are supported. */
> + int (*supports_fork_events) (void);
> +
> + /* Returns true if vfork events are supported. */
> + int (*supports_vfork_events) (void);
> +
> /* If not NULL, target-specific routine to process monitor command.
> Returns 1 if handled, or 0 to perform default processing. */
> int (*handle_monitor_command) (char *);
> @@ -387,6 +393,14 @@ void set_target_ops (struct target_ops *);
>
> int kill_inferior (int);
>
> +#define target_supports_fork_events() \
> + (the_target->supports_fork_events ? \
> + (*the_target->supports_fork_events) () : 0)
> +
> +#define target_supports_vfork_events() \
> + (the_target->supports_vfork_events ? \
> + (*the_target->supports_vfork_events) () : 0)
> +
> #define detach_inferior(pid) \
> (*the_target->detach) (pid)
>
> diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
> index e3fb618..210a747 100644
> --- a/gdb/gdbserver/win32-low.c
> +++ b/gdb/gdbserver/win32-low.c
> @@ -1823,6 +1823,8 @@ static struct target_ops win32_target_ops = {
> NULL, /* async */
> NULL, /* start_non_stop */
> NULL, /* supports_multi_process */
> + NULL, /* supports_fork_events */
> + NULL, /* supports_vfork_events */
> NULL, /* handle_monitor_command */
> NULL, /* core_of_thread */
> NULL, /* read_loadmap */
> diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
> index 0ce258f..3e1fcd5 100644
> --- a/gdb/nat/linux-ptrace.c
> +++ b/gdb/nat/linux-ptrace.c
> @@ -299,7 +299,7 @@ linux_fork_to_function (gdb_byte *child_stack, void (*function) (gdb_byte *))
> return child_pid;
> }
>
> -/* A helper function for linux_check_ptrace_features, called after
> +/* A helper function for linux_test_for_event_reporting, called after
> the child forks a grandchild. */
>
> static void
> @@ -313,7 +313,7 @@ linux_grandchild_function (gdb_byte *child_stack)
> _exit (0);
> }
>
> -/* A helper function for linux_check_ptrace_features, called after
> +/* A helper function for linux_test_for_event_reporting, called after
> the parent process forks a child. The child allows itself to
> be traced by its parent. */
>
> @@ -337,8 +337,8 @@ static void linux_test_for_exitkill (int child_pid);
>
> /* Determine ptrace features available on this target. */
>
> -static void
> -linux_check_ptrace_features (void)
> +void
> +linux_test_for_event_reporting (void)
> {
> int child_pid, ret, status;
>
> @@ -355,10 +355,10 @@ linux_check_ptrace_features (void)
> if (ret == -1)
> perror_with_name (("waitpid"));
> else if (ret != child_pid)
> - error (_("linux_check_ptrace_features: waitpid: unexpected result %d."),
> + error (_("linux_test_for_event_reporting: waitpid: unexpected result %d."),
> ret);
> if (! WIFSTOPPED (status))
> - error (_("linux_check_ptrace_features: waitpid: unexpected status %d."),
> + error (_("linux_test_for_event_reporting: waitpid: unexpected status %d."),
> status);
>
> linux_test_for_tracesysgood (child_pid);
> @@ -373,7 +373,7 @@ linux_check_ptrace_features (void)
> ret = ptrace (PTRACE_KILL, child_pid, (PTRACE_TYPE_ARG3) 0,
> (PTRACE_TYPE_ARG4) 0);
> if (ret != 0)
> - warning (_("linux_check_ptrace_features: failed to kill child"));
> + warning (_("linux_test_for_event_reporting: failed to kill child"));
> my_waitpid (child_pid, &status, 0);
> }
> while (WIFSTOPPED (status));
> @@ -459,9 +459,10 @@ linux_test_for_tracefork (int child_pid)
> /* We got the PID from the grandchild, which means fork
> tracing is supported. */
> current_ptrace_options |= PTRACE_O_TRACECLONE;
> - current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
> - | PTRACE_O_TRACEVFORK
> - | PTRACE_O_TRACEEXEC));
> + current_ptrace_options |= (additional_flags
> + & (PTRACE_O_TRACEFORK
> + | PTRACE_O_TRACEVFORK
> + | PTRACE_O_TRACEEXEC));
This looks like a spurious change?
>
> /* Do some cleanup and kill the grandchild. */
> my_waitpid (second_pid, &second_status, 0);
> @@ -503,7 +504,7 @@ linux_enable_event_reporting (pid_t pid, int attached)
> /* Check if we have initialized the ptrace features for this
> target. If not, do it now. */
> if (current_ptrace_options == -1)
> - linux_check_ptrace_features ();
> + linux_test_for_event_reporting ();
>
> ptrace_options = current_ptrace_options;
> if (attached)
> @@ -535,7 +536,7 @@ static int
> ptrace_supports_feature (int ptrace_options)
> {
> if (current_ptrace_options == -1)
> - linux_check_ptrace_features ();
> + linux_test_for_event_reporting ();
I don't really understand the motivation for the renaming.
The function still tests all kinds of ptrace features, not just
event-related features. E.g., it tests PTRACE_O_EXITKILL. And the
variable it controls is called "ptrace_options". How about
leaving the function's name as is?
>
> return ((current_ptrace_options & ptrace_options) == ptrace_options);
> }
> diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
> index 137b61a..d75b37c 100644
> --- a/gdb/nat/linux-ptrace.h
> +++ b/gdb/nat/linux-ptrace.h
> @@ -98,6 +98,7 @@ extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
> extern char *linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err);
>
> extern void linux_ptrace_init_warnings (void);
> +extern void linux_test_for_event_reporting (void);
> extern void linux_enable_event_reporting (pid_t pid, int attached);
> extern void linux_disable_event_reporting (pid_t pid);
> extern int linux_supports_tracefork (void);
> diff --git a/gdb/remote.c b/gdb/remote.c
> index c4d09ba..ec1f1d2 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -1327,6 +1327,12 @@ enum {
> /* Support for qXfer:libraries-svr4:read with a non-empty annex. */
> PACKET_augmented_libraries_svr4_read_feature,
>
> + /* Support for fork events. */
> + PACKET_fork_event_feature,
> +
> + /* Support for vfork events. */
> + PACKET_vfork_event_feature,
> +
> PACKET_MAX
> };
>
> @@ -1432,6 +1438,24 @@ remote_multi_process_p (struct remote_state *rs)
> return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
> }
>
> +#if PTRACE_FORK_EVENTS
> +/* Returns true if fork events are supported. */
> +
> +static int
> +remote_fork_event_p (struct remote_state *rs)
> +{
> + return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
> +}
> +
> +/* Returns true if vfork events are supported. */
> +
> +static int
> +remote_vfork_event_p (struct remote_state *rs)
> +{
> + return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
> +}
> +#endif
> +
> /* 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;
> @@ -4010,7 +4034,11 @@ static const struct protocol_feature remote_protocol_features[] = {
> { "Qbtrace:off", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_off },
> { "Qbtrace:bts", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_bts },
> { "qXfer:btrace:read", PACKET_DISABLE, remote_supported_packet,
> - PACKET_qXfer_btrace }
> + PACKET_qXfer_btrace },
> + { "fork-events", PACKET_DISABLE, remote_supported_packet,
> + PACKET_fork_event_feature },
> + { "vfork-events", PACKET_DISABLE, remote_supported_packet,
> + PACKET_vfork_event_feature },
> };
>
> static char *remote_support_xml;
> @@ -4084,6 +4112,12 @@ remote_query_supported (void)
>
> q = remote_query_supported_append (q, "qRelocInsn+");
>
> + if (rs->extended)
> + {
> + q = remote_query_supported_append (q, "fork-events+");
> + q = remote_query_supported_append (q, "vfork-events+");
> + }
> +
> q = reconcat (q, "qSupported:", q, (char *) NULL);
> putpkt (q);
>
> @@ -12191,7 +12225,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
> add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace],
> "qXfer:btrace", "read-btrace", 0);
>
> - /* Assert that we've registered commands for all packet configs. */
> + /* Assert that we've registered "set remote foo-packet" commands
> + for all packet configs. */
> {
> int i;
>
> @@ -12210,6 +12245,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
> case PACKET_DisconnectedTracing_feature:
> case PACKET_augmented_libraries_svr4_read_feature:
> case PACKET_qCRC:
> + case PACKET_fork_event_feature:
> + case PACKET_vfork_event_feature:
So what's the justification for not adding the commands? :-)
> /* Additions to this list need to be well justified:
> pre-existing packets are OK; new packets are not. */
> excepted = 1;
The patch should also include a GDB manual change describing the
new RSP features, and a NEWS change mentioning them.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH v4 7/7] Remote fork catch
[not found] ` <1422222420-25421-1-git-send-email-donb@codesourcery.com>
` (3 preceding siblings ...)
2015-01-25 21:50 ` [PATCH v4 1/7] Identify remote fork event support Don Breazeal
@ 2015-01-25 21:58 ` Don Breazeal
2015-01-26 0:07 ` [PATCH v4 3/7 v3] Extended-remote Linux follow fork Don Breazeal
2015-01-26 0:20 ` [PATCH v4 4/7] Target remote " Don Breazeal
6 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2015-01-25 21:58 UTC (permalink / raw)
To: gdb-patches, palves
This patch implements catchpoints for fork events on remote and
extended-remote Linux targets.
Implementation appeared to be straightforward, requiring four new functions
in remote.c to implement insert/remove of fork/vfork catchpoints. These
functions are essentially stubs that just return 0 ('success') if the
required events are enabled. If the fork events are being reported, then
catchpoints are set and hit.
However, there are some extra issues that arise with catchpoints.
1) Thread creation reporting -- fork catchpoints are hit before the
follow_fork has been completed. In the native implementation, the new
process is not 'reported' until after the follow is done. It doesn't
show up in the inferiors list or the threads list. However, in
gdbserver, an 'info threads' will retrieve the new thread info from the
target and add it to GDB's data structures. Because of this premature
report, things on the GDB side eventually get very confused.
So in gdbserver, in server.c:handle_qxfer_threads_worker, we check
'last_status' and if it shows a FORK event, we know that we are in an
unfollowed fork and we do not report the new (forked) thread to GDB.
2) Kill process before fork is followed -- on the native side in
linux-nat.c:linux_nat_kill, there is some code to handle the case where
a fork has occurred but follow_fork hasn't been called yet. It does
this by using the last status to determine if a follow is pending, and
if it is, to kill the child task. This patch uses similar code in
linux-low.c:linux_kill.
3) One of the tests related to fork catchpoints,
gdb.threads/fork-thread-pending.exp, depended on the threads being
reported in a specific order. GDBserver reported the threads in a
different order, that is, 'info threads' showed the same threads, but in
a different order. The test used a hard-coded thread number to find a
threads that (a) was not the main thread (blocked in pthread_join), and
(b) was not the forking thread (stopped in fork).
I implemented a new proc, find_unforked_thread, that uses a pretty
brute-force method of finding a thread. I considered just hard-coding
another number (the native case used thread 2, which was the forking
thread in the remote case), but that didn't seem future-proof.
Suggestions on how to do this better would be welcome.
Tested on x64 Ubuntu Lucid, native, remote, extended-remote. Tested the
case of killing the forking process before the fork has been followed
manually. It wasn't clear to me how to check that a process had actually
been killed from a dejagnu test.
Thanks
--Don
gdb/gdbserver/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* linux-low.c (handle_extended_wait): Fix braces in multi-line if.
(linux_kill): Kill forked child when between fork and follow_fork.
* server.c (handle_qxfer_threads_worker): Skip forked child thread
when between fork and follow_fork.
(get_last_target_status): New function.
* server.h (get_last_target_status): Declare new function.
gdb/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* remote.c (extended_remote_insert_fork_catchpoint): New function.
(extended_remote_remove_fork_catchpoint): New function.
(extended_remote_insert_vfork_catchpoint): New function.
(extended_remote_remove_vfork_catchpoint): New function.
(init_extended_remote_ops): Initialize target vector with
new fork catchpoint functions.
gdb/testsuite/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* gdb/testsuite/gdb.base/foll-fork.exp: Remove restriction for
remote target.
* gdb/testsuite/gdb.base/foll-vfork.exp: Likewise.
* gdb/testsuite/gdb.threads/fork-child-threads.exp: Likewise.
* gdb.threads/fork-thread-pending.exp: Likewise.
(find_unforked_thread): New proc.
* gdb/testsuite/gdb.threads/watchpoint-fork.exp: Remove restriction
for remote target.
---
gdb/gdbserver/linux-low.c | 22 +++++++++-
gdb/gdbserver/server.c | 18 ++++++++
gdb/gdbserver/server.h | 2 +
gdb/remote.c | 47 ++++++++++++++++++++-
gdb/testsuite/gdb.base/foll-fork.exp | 4 +-
gdb/testsuite/gdb.base/foll-vfork.exp | 4 +-
gdb/testsuite/gdb.threads/fork-child-threads.exp | 6 +-
gdb/testsuite/gdb.threads/fork-thread-pending.exp | 27 ++++++++++--
gdb/testsuite/gdb.threads/watchpoint-fork.exp | 5 +-
9 files changed, 117 insertions(+), 18 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 66b1729..eabf262 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -419,8 +419,8 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
if (debug_threads)
{
- debug_printf ("HEW: Got fork event from LWP %ld, "
- "new child is %d\n",
+ 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));
}
@@ -1045,6 +1045,24 @@ linux_kill (int pid)
{
struct process_info *process;
struct lwp_info *lwp;
+ struct target_waitstatus last;
+ ptid_t last_ptid;
+
+ /* If we're stopped while forking and we haven't followed yet,
+ kill the child task. We need to do this first because the
+ parent will be sleeping if this is a vfork. */
+
+ get_last_target_status (&last_ptid, &last);
+
+ if (last.kind == TARGET_WAITKIND_FORKED
+ || last.kind == TARGET_WAITKIND_VFORKED)
+ {
+ lwp = find_lwp_pid (last.value.related_pid);
+ gdb_assert (lwp != NULL);
+ kill_wait_lwp (lwp);
+ process = find_process_pid (ptid_get_pid (last.value.related_pid));
+ the_target->mourn (process);
+ }
process = find_process_pid (pid);
if (process == NULL)
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 57190f6..f525d3e 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1301,6 +1301,15 @@ handle_qxfer_threads_worker (struct inferior_list_entry *inf, void *arg)
int core = target_core_of_thread (ptid);
char core_s[21];
+ /* Skip new threads created as the result of a fork if we are not done
+ handling that fork event. We won't know whether to tell GDB about
+ the new thread until we are done following the fork. */
+ if ((last_status.kind == TARGET_WAITKIND_FORKED
+ || last_status.kind == TARGET_WAITKIND_VFORKED)
+ && (ptid_get_pid (last_status.value.related_pid)
+ == ptid_get_pid (ptid)))
+ return;
+
write_ptid (ptid_s, ptid);
if (core != -1)
@@ -3992,3 +4001,12 @@ handle_target_event (int err, gdb_client_data client_data)
return 0;
}
+
+/* Retrieve the last waitstatus reported to GDB. */
+
+void
+get_last_target_status (ptid_t *ptid, struct target_waitstatus *last)
+{
+ *ptid = last_ptid;
+ *last = last_status;
+}
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 5284dac..86e584e 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -102,6 +102,8 @@ typedef int gdb_fildes_t;
/* Functions from server.c. */
extern int handle_serial_event (int err, gdb_client_data client_data);
extern int handle_target_event (int err, gdb_client_data client_data);
+extern void get_last_target_status (ptid_t *ptid,
+ struct target_waitstatus *last);
#include "remote-utils.h"
diff --git a/gdb/remote.c b/gdb/remote.c
index 6ea3c60..d59309f 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1449,7 +1449,6 @@ remote_fork_event_p (struct remote_state *rs)
return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
}
-#if PTRACE_FORK_EVENTS
/* Returns true if vfork events are supported. */
static int
@@ -1457,7 +1456,7 @@ remote_vfork_event_p (struct remote_state *rs)
{
return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
}
-#endif
+
/* Target follow-fork function for remote targets. On entry, and
at return, the current inferior is the fork parent.
@@ -1509,6 +1508,46 @@ remote_follow_fork (struct target_ops *target, int follow_child,
return 0;
}
+/* Insert fork catchpoint target routine. If fork events are enabled
+ then return success, nothing more to do. */
+
+static int
+remote_insert_fork_catchpoint (struct target_ops *ops, int pid)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ return !remote_fork_event_p (rs);
+}
+
+/* Remove fork catchpoint target routine. Nothing to do, just
+ return success. */
+
+static int
+remote_remove_fork_catchpoint (struct target_ops *ops, int pid)
+{
+ return 0;
+}
+
+/* Insert vfork catchpoint target routine. If vfork events are enabled
+ then return success, nothing more to do. */
+
+static int
+remote_insert_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ return !remote_vfork_event_p (rs);
+}
+
+/* Remove vfork catchpoint target routine. Nothing to do, just
+ return success. */
+
+static int
+remote_remove_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+ 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;
@@ -11766,6 +11805,10 @@ Specify the serial device it is connected to\n\
remote_ops.to_augmented_libraries_svr4_read =
remote_augmented_libraries_svr4_read;
remote_ops.to_follow_fork = remote_follow_fork;
+ remote_ops.to_insert_fork_catchpoint = remote_insert_fork_catchpoint;
+ remote_ops.to_remove_fork_catchpoint = remote_remove_fork_catchpoint;
+ remote_ops.to_insert_vfork_catchpoint = remote_insert_vfork_catchpoint;
+ remote_ops.to_remove_vfork_catchpoint = remote_remove_vfork_catchpoint;
}
/* Set up the extended remote vector by making a copy of the standard
diff --git a/gdb/testsuite/gdb.base/foll-fork.exp b/gdb/testsuite/gdb.base/foll-fork.exp
index 3f2d224..697570d 100644
--- a/gdb/testsuite/gdb.base/foll-fork.exp
+++ b/gdb/testsuite/gdb.base/foll-fork.exp
@@ -13,8 +13,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-if { [is_remote target] || ![isnative] } then {
- continue
+if {![isnative]} then {
+ return 0
}
# Until "set follow-fork-mode" and "catch fork" are implemented on
diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp b/gdb/testsuite/gdb.base/foll-vfork.exp
index 78c5cc8..ec8db33 100644
--- a/gdb/testsuite/gdb.base/foll-vfork.exp
+++ b/gdb/testsuite/gdb.base/foll-vfork.exp
@@ -18,8 +18,8 @@
# either execs or exits --- since those events take somewhat different
# code paths in GDB, both variants are exercised.
-if { [is_remote target] || ![isnative] } then {
- continue
+if {![isnative]} then {
+ return 0
}
# Until "set follow-fork-mode" and "catch vfork" are implemented on
diff --git a/gdb/testsuite/gdb.threads/fork-child-threads.exp b/gdb/testsuite/gdb.threads/fork-child-threads.exp
index 75e60e1..3815c92 100644
--- a/gdb/testsuite/gdb.threads/fork-child-threads.exp
+++ b/gdb/testsuite/gdb.threads/fork-child-threads.exp
@@ -13,12 +13,12 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# Only GNU/Linux is known to support `set follow-fork-mode child'.
-if { ! [istarget "*-*-linux*"] } {
+if {![isnative] } then {
return 0
}
-if { [is_remote target] || ![isnative] } then {
+# Only GNU/Linux is known to support `set follow-fork-mode child'.
+if { ! [istarget "*-*-linux*"] } {
return 0
}
diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.exp b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
index d229232..bbd980a 100644
--- a/gdb/testsuite/gdb.threads/fork-thread-pending.exp
+++ b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
@@ -13,9 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# There's no support for `set follow-fork-mode' in the remote
-# protocol.
-if { [is_remote target] } {
+if {![isnative]} {
return 0
}
@@ -31,6 +29,26 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
+# Find a thread that did not fork and is not the main thread and
+# return its thread number. We can't just hard-code the thread
+# number since we have no guarantee as to the ordering of the threads
+# in gdb. We know that the main thread is in pthread_join and the
+# forking thread is in fork, so we use this rather ungainly regexp
+# to capture an entry from 'info threads' that doesn't show one of
+# those routines, then extract the thread number.
+
+proc find_unforked_thread { } {
+ gdb_test_multiple "info threads" "find unforked thread" {
+ -re "(\[^\r]*Thread\[^\r]* in \[^fp]\[^ot]\[^rh]\[^kr]\[^e]\[^a]\[^d]\[^_]\[^j]\[^\r]*\r\n)" {
+ regexp "(\[ ]*)(\[0-9]*)(\[ ]*Thread\[^\r]*\r\n)" $expect_out(0,string) ignore lead_spc threadnum rest
+ }
+ timeout {
+ set threadnum -1
+ }
+ }
+ return $threadnum
+}
+
clean_restart ${binfile}
if ![runto_main] then {
@@ -46,7 +64,8 @@ gdb_test "continue" "Catchpoint.*" "1, get to the fork event"
gdb_test "info threads" " Thread .* Thread .* Thread .* Thread .*" "1, multiple threads found"
-gdb_test "thread 1" ".*" "1, switched away from event thread"
+set threadnum [find_unforked_thread]
+gdb_test "thread $threadnum" ".*" "1, switched away from event thread to thread $threadnum"
gdb_test "continue" "Not resuming.*" "1, refused to resume"
diff --git a/gdb/testsuite/gdb.threads/watchpoint-fork.exp b/gdb/testsuite/gdb.threads/watchpoint-fork.exp
index a4557f8..a1dbd10 100644
--- a/gdb/testsuite/gdb.threads/watchpoint-fork.exp
+++ b/gdb/testsuite/gdb.threads/watchpoint-fork.exp
@@ -17,9 +17,8 @@
set testfile watchpoint-fork
-if [is_remote target] {
- kfail "remote/13584" "gdbserver does not support debugging across fork"
- return
+if {![isnative]} {
+ return 0
}
proc test {type symbol} {
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH v4 3/7 v3] Extended-remote Linux follow fork
[not found] ` <1422222420-25421-1-git-send-email-donb@codesourcery.com>
` (4 preceding siblings ...)
2015-01-25 21:58 ` [PATCH v4 7/7] Remote fork catch Don Breazeal
@ 2015-01-26 0:07 ` Don Breazeal
2015-02-10 16:36 ` Pedro Alves
2015-01-26 0:20 ` [PATCH v4 4/7] Target remote " Don Breazeal
6 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2015-01-26 0:07 UTC (permalink / raw)
To: gdb-patches, palves
Pedro Alves wrote:
> Let's try to shake out the higher-level concepts first before
> focusing on specific details.
---snip---
>>
>> * 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
>
> Reading through this, I don't think this is the model we should be exposing
> at the RSP level, and requiring servers to support. The hiding of the
> child fork until the users resumes is a current detail that we may want to
> change in the future. It seems better to me to _not_ hide the child from
> GDB, and then implement the hide-child-until-resume detail in GDB. That is,
> I think we should model fork events at the RSP level similarly to
> how ptrace and ttrace expose them. So, e.g., I think switching to the
> child to write to its memory should be done with the regular Hg packet.
> Handling detach_fork would be done by GDB calling the regular
> detach packet (D;PID), etc.
I have updated this patch to eliminate the vFollowFork packet and to handle
detach_fork using a regular detach packet as you describe above.
>> 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.
There are two linux-specific issues, both of which are workarounds for
kernel issues.
1) workaround for the hardware single-step kernel bug that causes the
hardware single-step flag to be inherited across a vfork. In
linux-nat.c:linux_child_follow_fork we manually step the inferior
before detaching.
2) workaround for kernels that support PTRACE_O_TRACEVFORK but not
PTRACE_O_TRACEVFORKDONE. In linux-nat.c:linux_child_follow_fork
we run the vfork child for a little while, then create a fake
vfork-done event.
I propose that we don't do either of these workarounds for remote
follow fork. For (1), the kernel fix that eliminates the need for
this looks like it is from 2009. For (2), the workaround is for
kernels between versions 2.5.46 (04.Nov.2002) and 2.6.18 (20.Sep.2006).
Is it necessary to provide workarounds in a new feature for kernels
as old as these? (1) in particular is difficult to implement, since
gdbserver has no way to know whether detach-on-fork has been set, or
whether a given detach has anything to do with a vfork that preceded
it. OK to drop these?
> I'm not even seeing a fundamental need
> to keep this for "extended-remote" alone, given gdb nowadays supports
> the multiprocess extensions with "target remote" too.
I agree that we should do this. I've implemented just extended-remote
support in this patch, then added 'target remote' support in the
patch immediately after this one in the new version of the series.
Updated patch and commit message below.
Thanks!
--Don
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. This patch depends on
the previous patches in the patch series.
Sufficient extended-remote functionality has been implemented here to pass
gdb.base/multi-forks.exp, as well as gdb.base/foll-fork.exp with the
catchpoint tests commented out. Some other fork tests fail with this
patch because it doesn't provide the architecture support needed for
watchpoint inheritance or fork catchpoints.
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.
* 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 data structures for the forked child
have not been allocated on the host side.
Tested on x64 Ubuntu Lucid, native, remote, extended-remote.
gdb/gdbserver/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* gdbthread.h (struct thread_info) <pending_follow>: New member.
* linux-low.c (handle_extended_wait): Implement return value,
handle PTRACE_EVENT_FORK.
(linux_low_enable_events): New function.
(linux_low_filter_event): Use return value from
handle_extended_wait.
(extended_event_reported): New function.
(linux_wait_1): Call extended_event_reported.
(linux_write_memory): Add pid to debug message.
(initialize_low): Pass PTRACE_O_TRACEFORK to set_additional_flags.
* remote-utils.c (prepare_resume_reply): Implement stop reason
"fork" for "T" stop message.
* server.h (report_fork_events): Declare global flag.
gdb/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* nat/linux-ptrace.c (linux_ptrace_clear_additional_flags): New
function.
* nat/linux-ptrace.h: (linux_ptrace_clear_additional_flags):
Declare new function.
* remote.c (remote_follow_fork): New function.
(remote_detach_1): Add target_ops argument, don't mourn inferior
if doing detach-on-fork.
(remote_detach): Add target_ops argument, pass to remote_detach_1.
(extended_remote_detach): Add target_ops argument, pass to
remote_detach_1.
(remote_parse_stop_reply): Handle new "T" stop reason "fork".
(remote_pid_to_str): Print "process" strings for pid/0/0 ptids.
(init_extended_remote_ops): Initialize to_follow_fork.
gdb/testsuite/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* gdb.base/multi-forks.exp (continue_to_exit_bp_loc):
Use '-i' in expect statement to include input from forked child.
* lib/gdb.exp (server_spawn_id): Declare new global
variable.
* lib/gdbserver-support.exp (server_spawn_id): Make
variable global.
---
gdb/gdbserver/gdbthread.h | 4 +
gdb/gdbserver/linux-low.c | 129 +++++++++++++++++++++++++++---
gdb/gdbserver/remote-utils.c | 14 +++-
gdb/gdbserver/server.h | 1 +
gdb/nat/linux-ptrace.c | 10 +++
gdb/nat/linux-ptrace.h | 1 +
gdb/remote.c | 89 +++++++++++++++++++---
gdb/testsuite/gdb.base/multi-forks.exp | 15 ++++-
gdb/testsuite/lib/gdb.exp | 2 +
gdb/testsuite/lib/gdbserver-support.exp | 2 +-
10 files changed, 241 insertions(+), 26 deletions(-)
diff --git a/gdb/gdbserver/gdbthread.h b/gdb/gdbserver/gdbthread.h
index b3e3e9d..33643af 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 e31844c..9fda25d 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"
@@ -370,22 +371,23 @@ linux_add_process (int pid, int attached)
static CORE_ADDR get_pc (struct lwp_info *lwp);
/* 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;
int ret, status;
+ /* Get the pid of the new lwp. */
ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0,
&new_pid);
@@ -405,6 +407,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",
@@ -451,7 +499,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
@@ -1810,6 +1863,20 @@ check_stopped_by_watchpoint (struct lwp_info *child)
return child->stop_reason == LWP_STOPPED_BY_WATCHPOINT;
}
+/* Wrapper for linux_enable_event_reporting that supports disabling
+ supported events if we have determined we don't want to report
+ them. This will not be needed once follow fork is implemented
+ for target remote as well as extended-remote. */
+
+static void
+linux_low_enable_events (pid_t pid, int attached)
+{
+ if (!report_fork_events)
+ linux_ptrace_clear_additional_flags (PTRACE_O_TRACEFORK);
+
+ linux_enable_event_reporting (pid, attached);
+}
+
/* Do low-level handling of the event, and check if we should go on
and pass it to caller code. Return the affected lwp if we are, or
NULL otherwise. */
@@ -1898,7 +1965,7 @@ linux_low_filter_event (int lwpid, int wstat)
{
struct process_info *proc = find_process_pid (pid_of (thread));
- linux_enable_event_reporting (lwpid, proc->attached);
+ linux_low_enable_events (lwpid, proc->attached);
child->must_set_ptrace_flags = 0;
}
@@ -1908,8 +1975,14 @@ linux_low_filter_event (int lwpid, int wstat)
&& linux_is_extended_waitstatus (wstat))
{
child->stop_pc = get_pc (child);
- handle_extended_wait (child, wstat);
- return NULL;
+ if (handle_extended_wait (child, wstat))
+ return NULL;
+ else
+ {
+ child->status_pending_p = 1;
+ child->status_pending = wstat;
+ return child;
+ }
}
if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
@@ -2444,6 +2517,18 @@ ignore_event (struct target_waitstatus *ourstatus)
return null_ptid;
}
+/* 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
@@ -2778,7 +2863,8 @@ linux_wait_1 (ptid_t ptid,
&& !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);
@@ -2800,6 +2886,13 @@ linux_wait_1 (ptid_t ptid,
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
@@ -2909,7 +3002,17 @@ linux_wait_1 (ptid_t ptid,
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;
/* Now that we've selected our final event LWP, un-adjust its PC if
it was a software breakpoint. */
@@ -2940,7 +3043,7 @@ linux_wait_1 (ptid_t ptid,
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));
}
@@ -4738,8 +4841,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. */
@@ -6128,6 +6231,6 @@ initialize_low (void)
initialize_low_arch ();
/* Enable any extended ptrace events that are supported. */
- linux_ptrace_set_additional_flags (0);
+ linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK);
linux_test_for_event_reporting ();
}
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 8854c4c..f0cc882 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1114,12 +1114,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.h b/gdb/gdbserver/server.h
index dbf31d5..ce6ffb9 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -84,6 +84,7 @@ extern int disable_packet_qfThreadInfo;
extern int run_once;
extern int multi_process;
+extern int report_fork_events;
extern int non_stop;
extern int disable_randomization;
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 3e1fcd5..dcdf89e 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -607,6 +607,16 @@ linux_ptrace_set_additional_flags (int flags)
additional_flags = flags;
}
+/* Clear FLAGS in current_ptrace_options. This will not be needed once
+ follow fork et al are supported by target remote as well as extended-
+ remote. */
+
+void
+linux_ptrace_clear_additional_flags (int flags)
+{
+ current_ptrace_options &= ~flags;
+}
+
/* Extract extended ptrace event from wait status. */
int
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index d75b37c..830372e 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -106,6 +106,7 @@ extern int linux_supports_traceclone (void);
extern int linux_supports_tracevforkdone (void);
extern int linux_supports_tracesysgood (void);
extern void linux_ptrace_set_additional_flags (int);
+extern void linux_ptrace_clear_additional_flags (int flags);
extern int linux_ptrace_get_extended_event (int wstat);
extern int linux_is_extended_waitstatus (int wstat);
diff --git a/gdb/remote.c b/gdb/remote.c
index ec1f1d2..eb540d9 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -24,6 +24,7 @@
#include <fcntl.h>
#include "inferior.h"
#include "infrun.h"
+#include "inf-child.h"
#include "bfd.h"
#include "symfile.h"
#include "target.h"
@@ -1438,7 +1439,6 @@ remote_multi_process_p (struct remote_state *rs)
return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
}
-#if PTRACE_FORK_EVENTS
/* Returns true if fork events are supported. */
static int
@@ -1447,6 +1447,7 @@ remote_fork_event_p (struct remote_state *rs)
return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
}
+#if PTRACE_FORK_EVENTS
/* Returns true if vfork events are supported. */
static int
@@ -1456,6 +1457,53 @@ remote_vfork_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.
+
+ Note that although this is currently only used for extended-remote,
+ it is named remote_follow_fork in anticipation of using it for the
+ remote target as well. */
+
+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));
+ 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;
@@ -4402,10 +4450,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."));
@@ -4442,19 +4492,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. */
@@ -5546,11 +5605,12 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
pnum and set p1 to point to the character following it.
Otherwise p1 points to p. */
- /* If this packet is an awatch packet, don't parse the 'a'
- as a register number. */
+ /* If this packet's stop reason starts with a hex digit,
+ don't parse it 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);
@@ -5602,6 +5662,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. */
@@ -9413,8 +9478,11 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
if (ptid_equal (magic_null_ptid, ptid))
xsnprintf (buf, sizeof buf, "Thread <main>");
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));
@@ -11662,6 +11730,7 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_kill = extended_remote_kill;
extended_remote_ops.to_supports_disable_randomization
= extended_remote_supports_disable_randomization;
+ extended_remote_ops.to_follow_fork = remote_follow_fork;
}
static int
diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp
index e95cb4b..5682730 100644
--- a/gdb/testsuite/gdb.base/multi-forks.exp
+++ b/gdb/testsuite/gdb.base/multi-forks.exp
@@ -62,6 +62,18 @@ proc continue_to_exit_bp_loc {} {
set seen_break 0
set seen_prompt 0
set seen_timeout 0
+
+ # If we are running with gdbserver, the output ($decimal done) will
+ # come via the spawn_id of gdbserver, not the spawn_id of gdb (the
+ # default). So we grab the spawn_id of gdbserver, if it exists, and
+ # add it to the gdb_expect statement below using "-i", allowing us
+ # to apply the expect statement to the output of both spawn_ids.
+ global server_spawn_id
+ set current_spawn_id [board_info host fileid]
+ if {![info exists server_spawn_id]} {
+ set server_spawn_id ""
+ }
+
while { ($seen_done < 16 || ! $seen_prompt) && ! $seen_timeout } {
# We don't know what order the interesting things will arrive in.
# Using a pattern of the form 'x|y|z' instead of -re x ... -re y
@@ -70,7 +82,8 @@ proc continue_to_exit_bp_loc {} {
# first in the script that occurs anywhere in the input, so that
# we don't skip anything.
gdb_expect {
- -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
+ -i "$current_spawn_id $server_spawn_id" \
+ -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
if {[info exists expect_out(1,string)]} {
incr seen_done
} elseif {[info exists expect_out(2,string)]} {
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index d3a3350..4d7557c 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -29,6 +29,8 @@ load_lib libgloss.exp
load_lib cache.exp
load_lib gdb-utils.exp
+global server_spawn_id
+
global GDB
if [info exists TOOL_EXECUTABLE] {
diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp
index 4a59154..9238ee3 100644
--- a/gdb/testsuite/lib/gdbserver-support.exp
+++ b/gdb/testsuite/lib/gdbserver-support.exp
@@ -39,7 +39,6 @@
# After GDB starts you should check global $gdbserver_gdbport for the
# real port used. It is not useful if $gdbserver_reconnect_p was not set.
#
-
#
# gdb_target_cmd
# Send gdb the "target" command
@@ -270,6 +269,7 @@ proc gdbserver_start { options arguments } {
append gdbserver_command " $arguments"
}
+ global server_spawn_id
set server_spawn_id [remote_spawn target $gdbserver_command]
# Wait for the server to open its TCP socket, so that GDB can connect.
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH v4 3/7 v3] Extended-remote Linux follow fork
2015-01-26 0:07 ` [PATCH v4 3/7 v3] Extended-remote Linux follow fork Don Breazeal
@ 2015-02-10 16:36 ` Pedro Alves
0 siblings, 0 replies; 110+ messages in thread
From: Pedro Alves @ 2015-02-10 16:36 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 01/25/2015 09:46 PM, Don Breazeal wrote:
> Pedro Alves wrote:
>> Let's try to shake out the higher-level concepts first before
>> focusing on specific details.
> ---snip---
>>>
>>> * 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
>>
>> Reading through this, I don't think this is the model we should be exposing
>> at the RSP level, and requiring servers to support. The hiding of the
>> child fork until the users resumes is a current detail that we may want to
>> change in the future. It seems better to me to _not_ hide the child from
>> GDB, and then implement the hide-child-until-resume detail in GDB. That is,
>> I think we should model fork events at the RSP level similarly to
>> how ptrace and ttrace expose them. So, e.g., I think switching to the
>> child to write to its memory should be done with the regular Hg packet.
>> Handling detach_fork would be done by GDB calling the regular
>> detach packet (D;PID), etc.
>
> I have updated this patch to eliminate the vFollowFork packet and to handle
> detach_fork using a regular detach packet as you describe above.
>
>>> 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.
>
> There are two linux-specific issues, both of which are workarounds for
> kernel issues.
>
> 1) workaround for the hardware single-step kernel bug that causes the
> hardware single-step flag to be inherited across a vfork. In
> linux-nat.c:linux_child_follow_fork we manually step the inferior
> before detaching.
>
> 2) workaround for kernels that support PTRACE_O_TRACEVFORK but not
> PTRACE_O_TRACEVFORKDONE. In linux-nat.c:linux_child_follow_fork
> we run the vfork child for a little while, then create a fake
> vfork-done event.
>
> I propose that we don't do either of these workarounds for remote
> follow fork. For (1), the kernel fix that eliminates the need for
> this looks like it is from 2009. For (2), the workaround is for
> kernels between versions 2.5.46 (04.Nov.2002) and 2.6.18 (20.Sep.2006).
> Is it necessary to provide workarounds in a new feature for kernels
> as old as these? (1) in particular is difficult to implement, since
> gdbserver has no way to know whether detach-on-fork has been set, or
> whether a given detach has anything to do with a vfork that preceded
> it. OK to drop these?
OK, let's do that, even if just to focus on the essence first.
>
>> I'm not even seeing a fundamental need
>> to keep this for "extended-remote" alone, given gdb nowadays supports
>> the multiprocess extensions with "target remote" too.
>
> I agree that we should do this. I've implemented just extended-remote
> support in this patch, then added 'target remote' support in the
> patch immediately after this one in the new version of the series.
>
> Updated patch and commit message below.
> Thanks!
> --Don
>
> 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. This patch depends on
> the previous patches in the patch series.
>
> Sufficient extended-remote functionality has been implemented here to pass
> gdb.base/multi-forks.exp, as well as gdb.base/foll-fork.exp with the
> catchpoint tests commented out. Some other fork tests fail with this
> patch because it doesn't provide the architecture support needed for
> watchpoint inheritance or fork catchpoints.
>
> 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.
This may need to be adjusted a bit when rebased on mainline.
handle_extended_wait now never resumes any LWP.
>
> * 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.
>
> * implementing a new RSP 'T' Stop Reply Packet stop reason: "fork", in
> gdbserver/remote-utils.c and remote.c.
We never, ever, send "T05 fork" to a GDB that does not understand that, right?
Because although GDB skips magic register "numbers", "fork" starts with
a hex digit and a GDB that doesn't understand it would be very confused.
We did that mistake with "core", and broke backwards compatibility then.
>
> * 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 data structures for the forked child
> have not been allocated on the host side.
>
> Tested on x64 Ubuntu Lucid, native, remote, extended-remote.
>
> gdb/gdbserver/
> 2015-01-25 Don Breazeal <donb@codesourcery.com>
>
> * gdbthread.h (struct thread_info) <pending_follow>: New member.
> * linux-low.c (handle_extended_wait): Implement return value,
> handle PTRACE_EVENT_FORK.
> (linux_low_enable_events): New function.
> (linux_low_filter_event): Use return value from
> handle_extended_wait.
> (extended_event_reported): New function.
> (linux_wait_1): Call extended_event_reported.
> (linux_write_memory): Add pid to debug message.
> (initialize_low): Pass PTRACE_O_TRACEFORK to set_additional_flags.
> * remote-utils.c (prepare_resume_reply): Implement stop reason
> "fork" for "T" stop message.
> * server.h (report_fork_events): Declare global flag.
>
> gdb/
> 2015-01-25 Don Breazeal <donb@codesourcery.com>
>
> * nat/linux-ptrace.c (linux_ptrace_clear_additional_flags): New
> function.
> * nat/linux-ptrace.h: (linux_ptrace_clear_additional_flags):
> Declare new function.
> * remote.c (remote_follow_fork): New function.
> (remote_detach_1): Add target_ops argument, don't mourn inferior
> if doing detach-on-fork.
> (remote_detach): Add target_ops argument, pass to remote_detach_1.
> (extended_remote_detach): Add target_ops argument, pass to
> remote_detach_1.
> (remote_parse_stop_reply): Handle new "T" stop reason "fork".
> (remote_pid_to_str): Print "process" strings for pid/0/0 ptids.
> (init_extended_remote_ops): Initialize to_follow_fork.
>
> gdb/testsuite/
> 2015-01-25 Don Breazeal <donb@codesourcery.com>
>
> * gdb.base/multi-forks.exp (continue_to_exit_bp_loc):
> Use '-i' in expect statement to include input from forked child.
> * lib/gdb.exp (server_spawn_id): Declare new global
> variable.
> * lib/gdbserver-support.exp (server_spawn_id): Make
> variable global.
>
> ---
> gdb/gdbserver/gdbthread.h | 4 +
> gdb/gdbserver/linux-low.c | 129 +++++++++++++++++++++++++++---
> gdb/gdbserver/remote-utils.c | 14 +++-
> gdb/gdbserver/server.h | 1 +
> gdb/nat/linux-ptrace.c | 10 +++
> gdb/nat/linux-ptrace.h | 1 +
> gdb/remote.c | 89 +++++++++++++++++++---
> gdb/testsuite/gdb.base/multi-forks.exp | 15 ++++-
> gdb/testsuite/lib/gdb.exp | 2 +
> gdb/testsuite/lib/gdbserver-support.exp | 2 +-
> 10 files changed, 241 insertions(+), 26 deletions(-)
>
> diff --git a/gdb/gdbserver/gdbthread.h b/gdb/gdbserver/gdbthread.h
> index b3e3e9d..33643af 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;
> +
I'm not sure this interact correctly with status_pending_p. On the
GDB/linux-nat.c side, the equivalent would be lp->waitstatus, not
thread->pending_follow, and the former is considered a pending status.
See linux-nat.c:lwp_status_pending_p.
Consider the case of debugging more than one process simulatenously,
and two forking at the same time. GDB is only going to report one
fork event at a time. So the other event will need to stay pending.
But I don't see anything that would make status_pending_p_callback
understand that the forked LWP has a pending event to report, but
that it has already been processed by handle_extended_wait.
We can't process the same extended wait twice. GDBserver would
get confused and likely hang (waiting for the wait status of the child).
> /* 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 e31844c..9fda25d 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"
> @@ -370,22 +371,23 @@ linux_add_process (int pid, int attached)
> static CORE_ADDR get_pc (struct lwp_info *lwp);
>
> /* 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;
> int ret, status;
>
> + /* Get the pid of the new lwp. */
> ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0,
> &new_pid);
>
> @@ -405,6 +407,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",
> @@ -451,7 +499,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
> @@ -1810,6 +1863,20 @@ check_stopped_by_watchpoint (struct lwp_info *child)
> return child->stop_reason == LWP_STOPPED_BY_WATCHPOINT;
> }
>
> +/* Wrapper for linux_enable_event_reporting that supports disabling
> + supported events if we have determined we don't want to report
> + them. This will not be needed once follow fork is implemented
> + for target remote as well as extended-remote. */
> +
> +static void
> +linux_low_enable_events (pid_t pid, int attached)
> +{
> + if (!report_fork_events)
> + linux_ptrace_clear_additional_flags (PTRACE_O_TRACEFORK);
I'm not sure about this bit. We should also consider cases like:
1. A GDB that supports extended-remote and fork events starts
a program.
2. GDB disconnects
3. A new GDB connection comes along. But this time, this GDB
does not understand fork events.
And the reverse too.
Also, if a non-stop or disconnected tracing connection
disconnects, it'll leave the program running. When a new
GDB connects, we may need to change the ptrace options
to enable/disable these events.
Alternatively, always enable the events at the ptrace
level, and then filter them out if the connected
GDB doesn't support them.
Or even, report a T05 / SIGTRAP to GDBs that don't support
the events. That may actually be better than not reporting
anything like today, because what we do today (ignore the
fork child) is just a recipe for it crashing when it trips
on a breakpoint that was set on the parent. A random SIGTRAP
at least gives the user a chance of doing something.
Hmm. Maybe we should rename "T05 fork" to something that
doesn't start with "f" (and hex char), and then that'd be
the simplest. We'd always send the event over. That may
be best, as it gives us more leverage even if we decide to
not report the events to old GDB, and then change our minds.
(Even with that, I think it'd be good to keep GDB / GDBserver
broadcasting the support in qSupported.)
> +
> + linux_enable_event_reporting (pid, attached);
> +}
> +
> /* Do low-level handling of the event, and check if we should go on
> and pass it to caller code. Return the affected lwp if we are, or
> NULL otherwise. */
> @@ -1898,7 +1965,7 @@ linux_low_filter_event (int lwpid, int wstat)
> {
> struct process_info *proc = find_process_pid (pid_of (thread));
>
> - linux_enable_event_reporting (lwpid, proc->attached);
> + linux_low_enable_events (lwpid, proc->attached);
> child->must_set_ptrace_flags = 0;
> }
>
> @@ -1908,8 +1975,14 @@ linux_low_filter_event (int lwpid, int wstat)
> && linux_is_extended_waitstatus (wstat))
> {
> child->stop_pc = get_pc (child);
> - handle_extended_wait (child, wstat);
> - return NULL;
> + if (handle_extended_wait (child, wstat))
> + return NULL;
> + else
> + {
> + child->status_pending_p = 1;
> + child->status_pending = wstat;
> + return child;
> + }
> }
>
> if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
> @@ -2444,6 +2517,18 @@ ignore_event (struct target_waitstatus *ourstatus)
> return null_ptid;
> }
>
> +/* Return non-zero if WAITSTATUS reflects an extended linux
> + event that gdbserver supports. Otherwise, return zero. */
I don't understand "gdbserver supports" here. This is gdbserver
code, so anything gdbserver doesn't support shouldn't even
compile, right? :-) Can you clarify this comment please?
> +
> +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
> @@ -2778,7 +2863,8 @@ linux_wait_1 (ptid_t ptid,
> && !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);
>
> @@ -2800,6 +2886,13 @@ linux_wait_1 (ptid_t ptid,
> 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
> @@ -2909,7 +3002,17 @@ linux_wait_1 (ptid_t ptid,
> 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;
>
> /* Now that we've selected our final event LWP, un-adjust its PC if
> it was a software breakpoint. */
> @@ -2940,7 +3043,7 @@ linux_wait_1 (ptid_t ptid,
> 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));
> }
> @@ -4738,8 +4841,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. */
> @@ -6128,6 +6231,6 @@ initialize_low (void)
> initialize_low_arch ();
>
> /* Enable any extended ptrace events that are supported. */
> - linux_ptrace_set_additional_flags (0);
> + linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK);
> linux_test_for_event_reporting ();
> }
> diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
> index 8854c4c..f0cc882 100644
> --- a/gdb/gdbserver/remote-utils.c
> +++ b/gdb/gdbserver/remote-utils.c
> @@ -1114,12 +1114,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)
I'm not sure this "multi_process" check here is correct.
Shouldn't it be "report_fork_events" instead?
> + {
> + 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));
Can this use write_ptid?
> + }
> + else
> + sprintf (buf, "T%02x", status->value.sig);
> +
> buf += strlen (buf);
>
> saved_thread = current_thread;
> diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
> index dbf31d5..ce6ffb9 100644
> --- a/gdb/gdbserver/server.h
> +++ b/gdb/gdbserver/server.h
> @@ -84,6 +84,7 @@ extern int disable_packet_qfThreadInfo;
>
> extern int run_once;
> extern int multi_process;
> +extern int report_fork_events;
> extern int non_stop;
>
> extern int disable_randomization;
> diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
> index 3e1fcd5..dcdf89e 100644
> --- a/gdb/nat/linux-ptrace.c
> +++ b/gdb/nat/linux-ptrace.c
> @@ -607,6 +607,16 @@ linux_ptrace_set_additional_flags (int flags)
> additional_flags = flags;
> }
>
> +/* Clear FLAGS in current_ptrace_options. This will not be needed once
> + follow fork et al are supported by target remote as well as extended-
> + remote. */
> +
> +void
> +linux_ptrace_clear_additional_flags (int flags)
> +{
> + current_ptrace_options &= ~flags;
> +}
> +
> /* Extract extended ptrace event from wait status. */
>
> int
> diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
> index d75b37c..830372e 100644
> --- a/gdb/nat/linux-ptrace.h
> +++ b/gdb/nat/linux-ptrace.h
> @@ -106,6 +106,7 @@ extern int linux_supports_traceclone (void);
> extern int linux_supports_tracevforkdone (void);
> extern int linux_supports_tracesysgood (void);
> extern void linux_ptrace_set_additional_flags (int);
> +extern void linux_ptrace_clear_additional_flags (int flags);
> extern int linux_ptrace_get_extended_event (int wstat);
> extern int linux_is_extended_waitstatus (int wstat);
>
> diff --git a/gdb/remote.c b/gdb/remote.c
> index ec1f1d2..eb540d9 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -24,6 +24,7 @@
> #include <fcntl.h>
> #include "inferior.h"
> #include "infrun.h"
> +#include "inf-child.h"
That's a layering violation. inf-child.h is the
base/prototype target for default child (native) targets.
> #include "bfd.h"
> #include "symfile.h"
> #include "target.h"
> @@ -1438,7 +1439,6 @@ remote_multi_process_p (struct remote_state *rs)
> return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
> }
>
> -#if PTRACE_FORK_EVENTS
> /* Returns true if fork events are supported. */
>
> static int
> @@ -1447,6 +1447,7 @@ remote_fork_event_p (struct remote_state *rs)
> return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
> }
>
> +#if PTRACE_FORK_EVENTS
> /* Returns true if vfork events are supported. */
>
> static int
> @@ -1456,6 +1457,53 @@ remote_vfork_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.
> +
> + Note that although this is currently only used for extended-remote,
> + it is named remote_follow_fork in anticipation of using it for the
> + remote target as well. */
> +
> +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. */
Why?
> + 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));
> + 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."));
Instead of copying/inlining this part of remote_detach_1 here,
can you please factor out this bit to a function that is used
both here and in remote_detach_1 ?
> +
> + inferior_ptid = null_ptid;
> + detach_inferior (child_pid);
> + inf_child_maybe_unpush_target (target);
Please just do:
if (!have_inferiors ())
unpush_target (target);
inf_child_maybe_unpush_target checks the inf_child_explicitly_opened
flag that is completely unrelated to "target remote". That's for
"target native".
But wait, why would you ever want to unpush the target here?
> + }
> + }
> + 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;
> @@ -4402,10 +4450,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."));
> @@ -4442,19 +4492,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);
Likewise. If this is the last process, and we're doing
extended-remote, this will disconnect from the target, but
extended-remote shouldn't disconnect. This doesn't look like
will do the right thing?
> + }
> }
>
> 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. */
> @@ -5546,11 +5605,12 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
> pnum and set p1 to point to the character following it.
> Otherwise p1 points to p. */
>
> - /* If this packet is an awatch packet, don't parse the 'a'
> - as a register number. */
> + /* If this packet's stop reason starts with a hex digit,
> + don't parse it 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);
> @@ -5602,6 +5662,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. */
> @@ -9413,8 +9478,11 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
> if (ptid_equal (magic_null_ptid, ptid))
> xsnprintf (buf, sizeof buf, "Thread <main>");
> 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));
> @@ -11662,6 +11730,7 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
> extended_remote_ops.to_kill = extended_remote_kill;
> extended_remote_ops.to_supports_disable_randomization
> = extended_remote_supports_disable_randomization;
> + extended_remote_ops.to_follow_fork = remote_follow_fork;
> }
>
> static int
> diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp
> index e95cb4b..5682730 100644
> --- a/gdb/testsuite/gdb.base/multi-forks.exp
> +++ b/gdb/testsuite/gdb.base/multi-forks.exp
> @@ -62,6 +62,18 @@ proc continue_to_exit_bp_loc {} {
> set seen_break 0
> set seen_prompt 0
> set seen_timeout 0
> +
> + # If we are running with gdbserver, the output ($decimal done) will
> + # come via the spawn_id of gdbserver, not the spawn_id of gdb (the
> + # default). So we grab the spawn_id of gdbserver, if it exists, and
> + # add it to the gdb_expect statement below using "-i", allowing us
> + # to apply the expect statement to the output of both spawn_ids.
> + global server_spawn_id
> + set current_spawn_id [board_info host fileid]
> + if {![info exists server_spawn_id]} {
> + set server_spawn_id ""
> + }
> +
Clever. :-) But it also means that the test will fail on
true remote testing, right?
ISTM that if we don't have a server_spawn_id, the test should
not be expecting the "done"s when gdb,noinferiorio is set.
> while { ($seen_done < 16 || ! $seen_prompt) && ! $seen_timeout } {
> # We don't know what order the interesting things will arrive in.
> # Using a pattern of the form 'x|y|z' instead of -re x ... -re y
> @@ -70,7 +82,8 @@ proc continue_to_exit_bp_loc {} {
> # first in the script that occurs anywhere in the input, so that
> # we don't skip anything.
> gdb_expect {
> - -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
> + -i "$current_spawn_id $server_spawn_id" \
> + -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
> if {[info exists expect_out(1,string)]} {
> incr seen_done
> } elseif {[info exists expect_out(2,string)]} {
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index d3a3350..4d7557c 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -29,6 +29,8 @@ load_lib libgloss.exp
> load_lib cache.exp
> load_lib gdb-utils.exp
>
> +global server_spawn_id
This should have a describing comment.
> +
> global GDB
>
> if [info exists TOOL_EXECUTABLE] {
> diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp
> index 4a59154..9238ee3 100644
> --- a/gdb/testsuite/lib/gdbserver-support.exp
> +++ b/gdb/testsuite/lib/gdbserver-support.exp
> @@ -39,7 +39,6 @@
> # After GDB starts you should check global $gdbserver_gdbport for the
> # real port used. It is not useful if $gdbserver_reconnect_p was not set.
> #
> -
> #
Please don't remove that line. It was splitting different sections.
Above is baseboard config options. Below is description of the
following procedure.
> # gdb_target_cmd
> # Send gdb the "target" command
> @@ -270,6 +269,7 @@ proc gdbserver_start { options arguments } {
> append gdbserver_command " $arguments"
> }
>
> + global server_spawn_id
> set server_spawn_id [remote_spawn target $gdbserver_command]
>
> # Wait for the server to open its TCP socket, so that GDB can connect.
>
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH v4 4/7] Target remote follow fork
[not found] ` <1422222420-25421-1-git-send-email-donb@codesourcery.com>
` (5 preceding siblings ...)
2015-01-26 0:07 ` [PATCH v4 3/7 v3] Extended-remote Linux follow fork Don Breazeal
@ 2015-01-26 0:20 ` Don Breazeal
6 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2015-01-26 0:20 UTC (permalink / raw)
To: gdb-patches, palves
This patch enables follow fork in gdbserver for remote (as in 'target
remote') targets. In addition, it enables some multiprocess functionality
that was previously only available in extended-remote targets, and
prevents gdbserver from exiting after an inferior exit or detach if there
is another active inferior.
This patch also implements a mechanism in linux_low_enable_events to
disable for events if GDB did not inquire about them in the qSupported
packet.
Thanks,
--Don
gdb/gdbserver/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* linux-low.c (linux_low_enable_events): Change function
type from void to int.
(linux_low_filter_event): Handle return value from
linux_low_enable_events, only set must_set_ptrace_flags on success.
* server.c (process_serial_event): Do not exit when target remote
if there is still an active inferior.
gdb/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* remote.c (set_general_process): Remove requirement for
extended mode.
(remote_query_supported): Remove requirement for extended
mode for fork and vfork event support.
(remote_detach_1): If target remote, prevent final cleanup
and exit if there is still an active inferior.
(remote_kill): Call new function remote_kill_1, move below
definition of remote_vkill.
(remote_kill_1): New function.
(extended_remote_kill): Call new function remote_kill_1.
(remote_pid_to_str): Remove requirement for extended mode
for multiprocess-style IDs.
(remote_supports_multi_process): Remove requirement for
extended mode.
gdb/testsuite/
2015-01-25 Don Breazeal <donb@codesourcery.com>
* gdb.base/multi-forks.exp: Remove restriction on remote target.
* gdb.threads/linux-dp.exp: Accept multiprocess-style thread IDs.
* gdb.trace/report.exp: Accept multiprocess-style thread IDs.
---
gdb/gdbserver/linux-low.c | 21 ++++-
gdb/gdbserver/server.c | 6 +-
gdb/remote.c | 137 ++++++++++++++++++++-----------
gdb/testsuite/gdb.base/multi-forks.exp | 4 -
gdb/testsuite/gdb.threads/linux-dp.exp | 4 +-
gdb/testsuite/gdb.trace/report.exp | 2 +-
6 files changed, 111 insertions(+), 63 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 9fda25d..6feebd0 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -1865,16 +1865,25 @@ check_stopped_by_watchpoint (struct lwp_info *child)
/* Wrapper for linux_enable_event_reporting that supports disabling
supported events if we have determined we don't want to report
- them. This will not be needed once follow fork is implemented
- for target remote as well as extended-remote. */
+ them. Return 0 if this function needs to be called again, 1 if
+ it doesn't. */
-static void
+static int
linux_low_enable_events (pid_t pid, int attached)
{
+ /* If GDB has not connected yet, then we don't know if it wants
+ us to report fork events. Try again later. */
+ if (!gdb_connected ())
+ return 0;
+
+ /* If GDB doesn't want fork events, don't enable them. */
if (!report_fork_events)
linux_ptrace_clear_additional_flags (PTRACE_O_TRACEFORK);
+ /* Enable all supported and requested events. */
linux_enable_event_reporting (pid, attached);
+
+ return 1;
}
/* Do low-level handling of the event, and check if we should go on
@@ -1964,9 +1973,11 @@ linux_low_filter_event (int lwpid, int wstat)
if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
{
struct process_info *proc = find_process_pid (pid_of (thread));
+ int done;
- linux_low_enable_events (lwpid, proc->attached);
- child->must_set_ptrace_flags = 0;
+ done = linux_low_enable_events (lwpid, proc->attached);
+ if (done)
+ child->must_set_ptrace_flags = 0;
}
/* Be careful to not overwrite stop_pc until
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 48cc363..57190f6 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -3535,9 +3535,11 @@ process_serial_event (void)
discard_queued_stop_replies (pid);
write_ok (own_buf);
- if (extended_protocol)
+ if (extended_protocol || get_first_inferior (&all_threads) != NULL)
{
- /* Treat this like a normal program exit. */
+ /* There is still at least one inferior remaining, 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 eb540d9..9e48205 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -121,6 +121,8 @@ static void remote_serial_write (const char *str, int len);
static void remote_kill (struct target_ops *ops);
+static void extended_remote_kill (struct target_ops *ops);
+
static int remote_can_async_p (struct target_ops *);
static int remote_is_async_p (struct target_ops *);
@@ -1904,7 +1906,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
@@ -4160,11 +4162,8 @@ remote_query_supported (void)
q = remote_query_supported_append (q, "qRelocInsn+");
- if (rs->extended)
- {
- q = remote_query_supported_append (q, "fork-events+");
- q = remote_query_supported_append (q, "vfork-events+");
- }
+ q = remote_query_supported_append (q, "fork-events+");
+ q = remote_query_supported_append (q, "vfork-events+");
q = reconcat (q, "qSupported:", q, (char *) NULL);
putpkt (q);
@@ -4489,12 +4488,15 @@ remote_detach_1 (struct target_ops *ops, const char *args,
else
error (_("Can't detach process."));
- if (from_tty && !extended)
+ /* Are we detaching the last inferior in remote mode? */
+ if (from_tty && !extended && (number_of_inferiors () == 1))
puts_filtered (_("Ending remote debugging.\n"));
+ /* XXX */
/* 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)
+ if ((tp->pending_follow.kind != TARGET_WAITKIND_FORKED)
+ && (number_of_inferiors () == 1))
target_mourn_inferior ();
else
{
@@ -7862,41 +7864,7 @@ getpkt_or_notif_sane (char **buf, long *sizeof_buf, int forever,
}
\f
-static void
-remote_kill (struct target_ops *ops)
-{
- volatile struct gdb_exception ex;
-
- /* Catch errors so the user can quit from gdb even when we
- aren't on speaking terms with the remote system. */
- TRY_CATCH (ex, RETURN_MASK_ERROR)
- {
- putpkt ("k");
- }
- if (ex.reason < 0)
- {
- if (ex.error == TARGET_CLOSE_ERROR)
- {
- /* If we got an (EOF) error that caused the target
- to go away, then we're done, that's what we wanted.
- "k" is susceptible to cause a premature EOF, given
- that the remote server isn't actually required to
- reply to "k", and it can happen that it doesn't
- even get to reply ACK to the "k". */
- return;
- }
-
- /* Otherwise, something went wrong. We didn't actually kill
- the target. Just propagate the exception, and let the
- user or higher layers decide what to do. */
- throw_exception (ex);
- }
-
- /* We've killed the remote end, we get to mourn it. Since this is
- target remote, single-process, mourning the inferior also
- unpushes remote_ops. */
- target_mourn_inferior ();
-}
+/* Use the vKill packet to perform a 'kill' operation. */
static int
remote_vkill (int pid, struct remote_state *rs)
@@ -7923,15 +7891,17 @@ remote_vkill (int pid, struct remote_state *rs)
}
}
+/* Worker function for remote_kill and extended_remote_kill. */
+
static void
-extended_remote_kill (struct target_ops *ops)
+remote_kill_1 (struct target_ops *ops)
{
int res;
int pid = ptid_get_pid (inferior_ptid);
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. */
@@ -7950,8 +7920,77 @@ extended_remote_kill (struct target_ops *ops)
if (res != 0)
error (_("Can't kill process"));
+}
+
+/* Remote target hook for 'kill'. */
+
+static void
+remote_kill (struct target_ops *ops)
+{
+ volatile struct gdb_exception ex;
+ int pid = ptid_get_pid (inferior_ptid);
+ struct thread_info *tp = first_thread_of_process (pid);
+
+ /* Catch errors so the user can quit from gdb even when we
+ aren't on speaking terms with the remote system. */
+ TRY_CATCH (ex, RETURN_MASK_ERROR)
+ {
+ remote_kill_1 (ops);
+ }
+ if (ex.reason < 0)
+ {
+ if (ex.error == TARGET_CLOSE_ERROR)
+ {
+ /* If we got an (EOF) error that caused the target
+ to go away, then we're done, that's what we wanted.
+ "k" is susceptible to cause a premature EOF, given
+ that the remote server isn't actually required to
+ reply to "k", and it can happen that it doesn't
+ even get to reply ACK to the "k". */
+ return;
+ }
+
+ /* Otherwise, something went wrong. We didn't actually kill
+ the target. Just propagate the exception, and let the
+ user or higher layers decide what to do. */
+ throw_exception (ex);
+ }
+
+ /* 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)
+ && (number_of_inferiors () == 1))
+ target_mourn_inferior ();
+ else
+ {
+ inferior_ptid = null_ptid;
+ detach_inferior (pid);
+ inf_child_maybe_unpush_target (ops);
+ }
+}
+
+/* Extended-remote target hook for 'kill'. */
+
+static void
+extended_remote_kill (struct target_ops *ops)
+{
+ int pid = ptid_get_pid (inferior_ptid);
+ struct thread_info *tp = first_thread_of_process (pid);
- target_mourn_inferior ();
+ remote_kill_1 (ops);
+
+ /* 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)
+ && (number_of_inferiors () == 1))
+ target_mourn_inferior ();
+ else
+ {
+ /* Don't unpush the target here (as in remote_kill) since in
+ extended mode gdbserver continues running without any inferior. */
+ inferior_ptid = null_ptid;
+ detach_inferior (pid);
+ }
}
static void
@@ -9477,7 +9516,7 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
{
if (ptid_equal (magic_null_ptid, ptid))
xsnprintf (buf, sizeof buf, "Thread <main>");
- else if (rs->extended && remote_multi_process_p (rs))
+ else if (remote_multi_process_p (rs))
if (ptid_get_lwp (ptid) == 0)
return normal_pid_to_str (ptid);
else
@@ -10483,7 +10522,7 @@ remote_supports_multi_process (struct target_ops *self)
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
@@ -11705,6 +11744,7 @@ Specify the serial device it is connected to\n\
remote_ops.to_read_btrace = remote_read_btrace;
remote_ops.to_augmented_libraries_svr4_read =
remote_augmented_libraries_svr4_read;
+ remote_ops.to_follow_fork = remote_follow_fork;
}
/* Set up the extended remote vector by making a copy of the standard
@@ -11730,7 +11770,6 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_kill = extended_remote_kill;
extended_remote_ops.to_supports_disable_randomization
= extended_remote_supports_disable_randomization;
- extended_remote_ops.to_follow_fork = remote_follow_fork;
}
static int
diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp
index 5682730..5f64410 100644
--- a/gdb/testsuite/gdb.base/multi-forks.exp
+++ b/gdb/testsuite/gdb.base/multi-forks.exp
@@ -13,10 +13,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-if { [is_remote target] || ![isnative] } then {
- continue
-}
-
# Until "set follow-fork-mode" and "catch fork" are implemented on
# other targets...
#
diff --git a/gdb/testsuite/gdb.threads/linux-dp.exp b/gdb/testsuite/gdb.threads/linux-dp.exp
index e612978..cb93706 100644
--- a/gdb/testsuite/gdb.threads/linux-dp.exp
+++ b/gdb/testsuite/gdb.threads/linux-dp.exp
@@ -64,7 +64,7 @@ for {set i 0} {$i < 5} {incr i} {
-re "^ *Id.*Frame *\[\r\n\]+" {
exp_continue
}
- -re "^. +(\[0-9\]+ *Thread \[-0-9a-fx\]+) \[^\n\]*\n" {
+ -re "^. +(\[0-9\]+ *Thread \[-0-9a-fx\.\]+) \[^\n\]*\n" {
verbose -log "found thread $expect_out(1,string)" 2
lappend threads_before $expect_out(1,string)
exp_continue
@@ -126,7 +126,7 @@ for {set i 0} {$i < 5} {incr i} {
-re "^ *Id.*Frame *\[\r\n\]+" {
exp_continue
}
- -re "^. +(\[0-9\]+ *Thread \[-0-9a-fx\]+) \[^\n\]*\n" {
+ -re "^. +(\[0-9\]+ *Thread \[-0-9a-fx\.\]+) \[^\n\]*\n" {
set name $expect_out(1,string)
for {set j 0} {$j != [llength $threads_before] } {incr j} {
if {$name == [lindex $threads_before $j]} {
diff --git a/gdb/testsuite/gdb.trace/report.exp b/gdb/testsuite/gdb.trace/report.exp
index 2fa676b..c1de422 100644
--- a/gdb/testsuite/gdb.trace/report.exp
+++ b/gdb/testsuite/gdb.trace/report.exp
@@ -400,7 +400,7 @@ proc use_collected_data { data_source } {
# There is always a thread of an inferior, either a live one or
# a faked one.
- gdb_test "info threads" "\\* ${decimal} (process|Thread) ${decimal}\[ \t\].*"
+ gdb_test "info threads" "\\* ${decimal} (process|Thread) \[0-9\.]*\[ \t\].*"
gdb_test "info inferiors" "\\* 1 process ${decimal} \[ \t\]+${binfile}.*"
}
}
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 06/16 v3] Extended-remote Linux follow fork
2014-11-13 19:06 ` Breazeal, Don
2014-12-06 0:31 ` Breazeal, Don
@ 2015-01-12 22:39 ` Don Breazeal
2015-01-12 22:49 ` Breazeal, Don
1 sibling, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2015-01-12 22:39 UTC (permalink / raw)
To: gdb-patches, palves
Pedro Alves wrote:
> Let's try to shake out the higher-level concepts first before
> focusing on specific details.
---snip---
>>
>> * 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
>
> Reading through this, I don't think this is the model we should be exposing
> at the RSP level, and requiring servers to support. The hiding of the
> child fork until the users resumes is a current detail that we may want to
> change in the future. It seems better to me to _not_ hide the child from
> GDB, and then implement the hide-child-until-resume detail in GDB. That is,
> I think we should model fork events at the RSP level similarly to
> how ptrace and ttrace expose them. So, e.g., I think switching to the
> child to write to its memory should be done with the regular Hg packet.
> Handling detach_fork would be done by GDB calling the regular
> detach packet (D;PID), etc.
I have updated the patch to eliminate the vFollowFork packet and to handle
detach_fork using a regular detach packet as you describe above.
>> 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.
There are two linux-specific issues, both of which are workarounds for
kernel issues.
1) workaround for the hardware single-step kernel bug that causes the
hardware single-step flag to be inherited across a vfork. In
linux-nat.c:linux_child_follow_fork we manually step the inferior
before detaching.
2) workaround for kernels that support PTRACE_O_TRACEVFORK but not
PTRACE_O_TRACEVFORKDONE. In linux-nat.c:linux_child_follow_fork
we run the vfork child for a little while, then create a fake
vfork-done event.
I propose that we don't do either of these workarounds for remote
follow fork. For (1), the kernel fix that eliminates the need for
this looks like it is from 2009. For (2), the workaround is for
kernels between versions 2.5.46 (04.Nov.2002) and 2.6.18 (20.Sep.2006).
Is it necessary to provide workarounds in a new feature for kernels
as old as these? (1) in particular is difficult to implement, since
gdbserver has no way to know whether detach-on-fork has been set, or
whether a given detach has anything to do with a vfork that preceded
it. OK to drop these?
> I'm not even seeing a fundamental need
> to keep this for "extended-remote" alone, given gdb nowadays supports
> the multiprocess extensions with "target remote" too.
I agree that we should do this. As I described in the update to patch 04
(https://sourceware.org/ml/gdb-patches/2015-01/msg00320.html), I ran into
several issues with implementing follow-fork for target remote. I think
the underlying target remote support, eliminating "gdbserver exit when
inferior terminates", deserves its own patch, and I'd like to defer that in
favor of making progress on the extended-remote piece.
Updated patch and commit message below.
Thanks!
--Don
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. This patch depends on
patches 04 and 05 of the patch series.
Sufficient extended-remote functionality has been implemented here to pass
gdb.base/multi-forks.exp and gdb.base/foll-fork.exp with the catchpoint
tests commented out. Some other fork tests fail with this patch because it
doesn't provide the architecture support needed for watchpoint inheritance
or fork catchpoints.
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.
* 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/
2015-01-12 Don Breazeal <donb@codesourcery.com>
* gdbthread.h (struct thread_info) <pending_follow>: New member.
* linux-low.c (handle_extended_wait): Implement return value,
handle PTRACE_EVENT_FORK.
(linux_low_enable_events): New function.
(linux_low_filter_event): Use return value from
handle_extended_wait.
(extended_event_reported): New function.
(linux_write_memory): Add pid to debug message.
(initialize_low): Pass PTRACE_O_TRACEFORK to set_additional_flags.
* remote-utils.c (prepare_resume_reply): Implement stop reason
"fork" for "T" stop message.
* server.h (report_fork_events): Declare global flag.
gdb/
2015-01-12 Don Breazeal <donb@codesourcery.com>
* nat/linux-ptrace.c (linux_ptrace_clear_additional_flags): New
function.
* nat/linux-ptrace.h: (linux_ptrace_clear_additional_flags):
Declare new function.
* remote.c (remote_follow_fork): New function.
(remote_detach_1): Add target_ops argument, don't mourn inferior
if doing detach-on-fork.
(remote_detach): Add target_ops argument, pass to remote_detach_1.
(extended_remote_detach): Add target_ops argument, pass to
remote_detach_1.
(remote_parse_stop_reply): Handle new "T" stop reason "fork".
(remote_pid_to_str): Print "process" strings for pid/0/0 ptids.
(init_extended_remote_ops): Initialize to_follow_fork.
---
gdb/gdbserver/gdbthread.h | 4 +
gdb/gdbserver/linux-low.c | 125 +++++++++++++++++++++++++++++++++++++----
gdb/gdbserver/remote-utils.c | 14 ++++-
gdb/gdbserver/server.h | 1 +
gdb/nat/linux-ptrace.c | 10 +++
gdb/nat/linux-ptrace.h | 1 +
gdb/remote.c | 89 ++++++++++++++++++++++++++---
7 files changed, 220 insertions(+), 24 deletions(-)
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 b1201b3..7068696 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
+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;
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",
@@ -448,7 +496,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
@@ -1753,6 +1806,20 @@ lp_status_maybe_breakpoint (struct lwp_info *lp)
|| WSTOPSIG (lp->status_pending) == SIGSEGV));
}
+/* Wrapper for linux_enable_event_reporting that supports disabling
+ supported events if we have determined we don't want to report
+ them. This will not be needed once follow fork is implemented
+ for target remote as well as extended-remote. */
+
+static void
+linux_low_enable_events (pid_t pid, int attached)
+{
+ if (!report_fork_events)
+ linux_ptrace_clear_additional_flags (PTRACE_O_TRACEFORK);
+
+ linux_enable_event_reporting (pid, attached);
+}
+
/* Do low-level handling of the event, and check if we should go on
and pass it to caller code. Return the affected lwp if we are, or
NULL otherwise. */
@@ -1880,15 +1947,17 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
{
struct process_info *proc = find_process_pid (pid_of (thread));
- linux_enable_event_reporting (lwpid, proc->attached);
+ linux_low_enable_events (lwpid, proc->attached);
child->must_set_ptrace_flags = 0;
}
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
@@ -2486,6 +2555,18 @@ 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
@@ -2821,7 +2902,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);
@@ -2843,6 +2925,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
@@ -2941,7 +3030,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)
@@ -2958,7 +3057,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));
}
@@ -4744,8 +4843,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. */
@@ -6135,6 +6234,6 @@ initialize_low (void)
initialize_low_arch ();
/* Enable any extended ptrace events that are supported. */
- linux_ptrace_set_additional_flags (0);
+ linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK);
linux_test_for_event_reporting ();
}
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 373fc15..148b1fa 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.h b/gdb/gdbserver/server.h
index 02d751a..98d1b3c 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -84,6 +84,7 @@ extern int disable_packet_qfThreadInfo;
extern int run_once;
extern int multi_process;
+extern int report_fork_events;
extern int non_stop;
extern int disable_randomization;
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 542e762..89314b1 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -580,6 +580,16 @@ linux_ptrace_set_additional_flags (int flags)
additional_flags = flags;
}
+/* Clear FLAGS in current_ptrace_options. This will not be needed once
+ follow fork et al are supported by target remote as well as extended-
+ remote. */
+
+void
+linux_ptrace_clear_additional_flags (int flags)
+{
+ current_ptrace_options &= ~flags;
+}
+
/* Extract extended ptrace event from wait status. */
int
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index edbacfd..f7fe6bf 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -98,6 +98,7 @@ extern int linux_supports_traceclone (void);
extern int linux_supports_tracevforkdone (void);
extern int linux_supports_tracesysgood (void);
extern void linux_ptrace_set_additional_flags (int);
+extern void linux_ptrace_clear_additional_flags (int flags);
extern int linux_ptrace_get_extended_event (int wstat);
extern int linux_is_extended_waitstatus (int wstat);
diff --git a/gdb/remote.c b/gdb/remote.c
index 891329a..172943c 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -24,6 +24,7 @@
#include <fcntl.h>
#include "inferior.h"
#include "infrun.h"
+#include "inf-child.h"
#include "bfd.h"
#include "symfile.h"
#include "target.h"
@@ -1438,7 +1439,6 @@ remote_multi_process_p (struct remote_state *rs)
return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
}
-#if PTRACE_FORK_EVENTS
/* Returns true if fork events are supported. */
static int
@@ -1447,6 +1447,7 @@ remote_fork_event_p (struct remote_state *rs)
return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
}
+#if PTRACE_FORK_EVENTS
/* Returns true if vfork events are supported. */
static int
@@ -1456,6 +1457,53 @@ remote_vfork_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.
+
+ Note that although this is currently only used for extended-remote,
+ it is named remote_follow_fork in anticipation of using it for the
+ remote target as well. */
+
+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));
+ 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;
@@ -4402,10 +4450,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."));
@@ -4442,19 +4492,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. */
@@ -5546,11 +5605,12 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
pnum and set p1 to point to the character following it.
Otherwise p1 points to p. */
- /* If this packet is an awatch packet, don't parse the 'a'
- as a register number. */
+ /* If this packet's stop reason starts with a hex digit,
+ don't parse it 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);
@@ -5602,6 +5662,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. */
@@ -9413,8 +9478,11 @@ remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
if (ptid_equal (magic_null_ptid, ptid))
xsnprintf (buf, sizeof buf, "Thread <main>");
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));
@@ -11662,6 +11730,7 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_kill = extended_remote_kill;
extended_remote_ops.to_supports_disable_randomization
= extended_remote_supports_disable_randomization;
+ extended_remote_ops.to_follow_fork = remote_follow_fork;
}
static int
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 06/16 v3] Extended-remote Linux follow fork
2015-01-12 22:39 ` [PATCH 06/16 v3] Extended-remote Linux " Don Breazeal
@ 2015-01-12 22:49 ` Breazeal, Don
0 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2015-01-12 22:49 UTC (permalink / raw)
To: gdb-patches, palves
On 1/12/2015 2:39 PM, Don Breazeal wrote:
> Pedro Alves wrote:
>> I'm not even seeing a fundamental need
>> to keep this for "extended-remote" alone, given gdb nowadays supports
>> the multiprocess extensions with "target remote" too.
>
> I agree that we should do this. As I described in the update to patch 04
> (https://sourceware.org/ml/gdb-patches/2015-01/msg00320.html), I ran into
> several issues with implementing follow-fork for target remote. I think
> the underlying target remote support, eliminating "gdbserver exit when
> inferior terminates", deserves its own patch, and I'd like to defer that in
> favor of making progress on the extended-remote piece.
>
BTW, it would be helpful to me to have an explanation of why the two
remote protocols exist (remote and extended-remote) and what the
long-term plan or vision is for each of them. Like, will they
eventually just be merged into a single protocol? I'm pretty sure I've
had an incorrect view of this, and I haven't found an answer to "why"
by searching for it.
Thanks!
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 00/16 v3] Linux extended-remote fork and exec events
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (3 preceding siblings ...)
2014-10-31 23:29 ` [PATCH 06/16 v3] Extended-remote Linux " Don Breazeal
@ 2014-10-31 23:29 ` Don Breazeal
2014-11-12 15:54 ` Pedro Alves
` (2 more replies)
2014-10-31 23:29 ` [PATCH 04/16 v3] Determine supported extended-remote features Don Breazeal
` (9 subsequent siblings)
14 siblings, 3 replies; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:29 UTC (permalink / raw)
To: gdb-patches
This is an update to the patch series implementing fork and exec events
for extended-remote Linux targets. The updates include changes requested
by Pedro for patches 4 and 5, plus minor changes throughout to catch up
to changes made upstream since the last version of this series was posted.
The most significant of those changes were related to upstream refactoring
that changed names from "xxx_inferior" to "xxx_thread".
Patch 4 was rewritten to use qSupported as the mechanism for determining
which extended-mode-only events are supported by gdbserver, instead of
using a new packet for this.
Patch 5 received a little refactoring and polishing.
Patches 1-3 have been approved and pushed, so they aren't included.
Patches 10 and 15 were approved by Eli some time ago, but are still
included here since they haven't been pushed.
Thanks,
--Don
====================================================================
This patch series implements fork and exec events for extended-remote linux
targets. Features that are enabled include:
* follow-fork-mode
* detach-on-fork
* follow-exec-mode
* catch fork/vfork/exec
This work addresses PR gdb/13584, and is part of the local/remote debugging
feature parity project
(https://sourceware.org/gdb/wiki/LocalRemoteFeatureParity).
My patch v2 test results showed the following changes:
* native: PASS +46
- this is due the addition of non-stop mode tests to
gdb.threads/non-ldr-exc-*.exp.
* remote: no change
* extended-remote: PASS: +329, FAIL: -134, KFAIL: +1,
UNTESTED: +8, UNSUPPORTED: -6
Some items to note about the extended-remote results:
- there are some extended-remote failures in
gdb.base/disp-step-syscall.exp that don't show up in the unmodified
version on the native target. Investigation shows that these tests
are producing a bogus PASS result on the native target. I did not
pursue this further.
- the new non-stop tests in gdb.threads/non-ldr-exc-*.exp give an
UNTESTED result for extended-remote. This is due to an RSP
error in non-stop mode when running to main. I spent some time
investigating this and concluded that I should leave it alone for now,
for fear of having an ever-growing, never-ending project. My plan is
to track this with a bug report once that is appropriate.
- gdb.threads/thread-execl.exp gives a couple of failures related to
scheduler locking. As with the previous item, after spending some
time on this I concluded that pursuing it further now would be
feature-creep, and that this should be tracked with a bug report.
- gdb.trace/tspeed.exp got some timeout failures one time, but I
was unable to reproduce it. I'm unsure whether this is significant.
I can provide test logs or .sum diffs if anybody is interested in
specifics.
The contents of the patch series are as follows:
Patch 1: refactor native follow-fork implementation to move
target-independent code from target-dependent file (linux-nat.c)
to make it useful for extended-remote targets.
Patch 2: encapsulate the code used to print verbose/debug messages
related to folow-fork into functions, and add a message in one case.
Also make messages distinguish between fork and vfork.
Patch 3: encapsulate code that identifies and extracts extended
ptrace events found in the Linux wait status into functions, and call
them everywhere that the hard-coded wait status processing was used.
Patch 4: implements a mechanism for determining what extended-mode
features gdbserver supports. There are several issues that this addresses,
which are detailed in the patch 4 description. Basically, the patch
adds some new items to the qSupported response denoting extended-mode-only
event support, and defines new packets to represent those features. In
gdbserver, it splits checking whether the OS supports the features from
actually enabling them, and enables them only when it is clear that
extended mode is active.
Patch 5: implements some functions to clone the breakpoint lists in
gdbserver. These are needed because in gdbserver, each process maintains a
separate breakpoint list. When a fork occurs, the child process needs a
copy of the parent's breakpoint list so that it can manage the breakpoints
using existing mechanisms in gdbserver.
Patch 6: implements follow-fork, but only for 'fork', not 'vfork'. I split
these apart in an attempt to keep the size of the patches down to a
reviewable size.
Patch 7: adds the architecture-specific pieces of follow-fork. This is the
mechanism that handles copying the debug register state from the parent to
the child.
Patch 8: adds follow-fork for vfork.
Patch 9: adds 'catch fork' and 'catch vfork', along with some code to make
sure that killing a process that has forked, but before the fork is
followed, also kills the child process.
Patch 10: implements changes to the manual and the NEWS file for the fork
event changes.
Patch 11: implements support for the extended ptrace event
PTRACE_EVENT_EXIT, which is a prerequisite for the exec event support.
Patch 12: implements exec event support and follow-exec.
Patch 13: implements exec catchpoints.
Patch 14: suppresses some spurious warnings that were generated after
an exec. These warnings were caused when gdbserver attempted to check
the version number from the r_debug structure in the new image before
the structure had been initialized.
Patch 15: implements changed to the manual and NEWS file for the exec
event changes.
Patch 16: changes how gdb.base/foll-exec.exp starts GDB and loads the
program so that it will work with extended-remote. It also extends the
tests gdb.threads/non-ldr-exc-[1-4].exp to test in non-stop mode as well as
in all-stop mode.
thanks
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 00/16 v3] Linux extended-remote fork and exec events
2014-10-31 23:29 ` [PATCH 00/16 v3] Linux extended-remote fork and exec events Don Breazeal
@ 2014-11-12 15:54 ` Pedro Alves
2014-11-13 13:41 ` Pedro Alves
2014-11-13 19:14 ` Pedro Alves
2 siblings, 0 replies; 110+ messages in thread
From: Pedro Alves @ 2014-11-12 15:54 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
FYI, I just started looking at / playing with this.
Thanks for persevering. Really sorry for the slowness in getting the
through... This feature is super important, but for one reason or another
it ends up pushed down on my TODO. :-/
Thanks,
Pedro Alves
On 10/31/2014 11:28 PM, Don Breazeal wrote:
> This is an update to the patch series implementing fork and exec events
> for extended-remote Linux targets. The updates include changes requested
> by Pedro for patches 4 and 5, plus minor changes throughout to catch up
> to changes made upstream since the last version of this series was posted.
> The most significant of those changes were related to upstream refactoring
> that changed names from "xxx_inferior" to "xxx_thread".
>
> Patch 4 was rewritten to use qSupported as the mechanism for determining
> which extended-mode-only events are supported by gdbserver, instead of
> using a new packet for this.
>
> Patch 5 received a little refactoring and polishing.
>
> Patches 1-3 have been approved and pushed, so they aren't included.
>
> Patches 10 and 15 were approved by Eli some time ago, but are still
> included here since they haven't been pushed.
>
> Thanks,
> --Don
>
> ====================================================================
>
> This patch series implements fork and exec events for extended-remote linux
> targets. Features that are enabled include:
>
> * follow-fork-mode
> * detach-on-fork
> * follow-exec-mode
> * catch fork/vfork/exec
>
> This work addresses PR gdb/13584, and is part of the local/remote debugging
> feature parity project
> (https://sourceware.org/gdb/wiki/LocalRemoteFeatureParity).
>
> My patch v2 test results showed the following changes:
>
> * native: PASS +46
> - this is due the addition of non-stop mode tests to
> gdb.threads/non-ldr-exc-*.exp.
>
> * remote: no change
>
> * extended-remote: PASS: +329, FAIL: -134, KFAIL: +1,
> UNTESTED: +8, UNSUPPORTED: -6
>
> Some items to note about the extended-remote results:
>
> - there are some extended-remote failures in
> gdb.base/disp-step-syscall.exp that don't show up in the unmodified
> version on the native target. Investigation shows that these tests
> are producing a bogus PASS result on the native target. I did not
> pursue this further.
>
> - the new non-stop tests in gdb.threads/non-ldr-exc-*.exp give an
> UNTESTED result for extended-remote. This is due to an RSP
> error in non-stop mode when running to main. I spent some time
> investigating this and concluded that I should leave it alone for now,
> for fear of having an ever-growing, never-ending project. My plan is
> to track this with a bug report once that is appropriate.
>
> - gdb.threads/thread-execl.exp gives a couple of failures related to
> scheduler locking. As with the previous item, after spending some
> time on this I concluded that pursuing it further now would be
> feature-creep, and that this should be tracked with a bug report.
>
> - gdb.trace/tspeed.exp got some timeout failures one time, but I
> was unable to reproduce it. I'm unsure whether this is significant.
>
> I can provide test logs or .sum diffs if anybody is interested in
> specifics.
>
> The contents of the patch series are as follows:
>
> Patch 1: refactor native follow-fork implementation to move
> target-independent code from target-dependent file (linux-nat.c)
> to make it useful for extended-remote targets.
>
> Patch 2: encapsulate the code used to print verbose/debug messages
> related to folow-fork into functions, and add a message in one case.
> Also make messages distinguish between fork and vfork.
>
> Patch 3: encapsulate code that identifies and extracts extended
> ptrace events found in the Linux wait status into functions, and call
> them everywhere that the hard-coded wait status processing was used.
>
> Patch 4: implements a mechanism for determining what extended-mode
> features gdbserver supports. There are several issues that this addresses,
> which are detailed in the patch 4 description. Basically, the patch
> adds some new items to the qSupported response denoting extended-mode-only
> event support, and defines new packets to represent those features. In
> gdbserver, it splits checking whether the OS supports the features from
> actually enabling them, and enables them only when it is clear that
> extended mode is active.
>
> Patch 5: implements some functions to clone the breakpoint lists in
> gdbserver. These are needed because in gdbserver, each process maintains a
> separate breakpoint list. When a fork occurs, the child process needs a
> copy of the parent's breakpoint list so that it can manage the breakpoints
> using existing mechanisms in gdbserver.
>
> Patch 6: implements follow-fork, but only for 'fork', not 'vfork'. I split
> these apart in an attempt to keep the size of the patches down to a
> reviewable size.
>
> Patch 7: adds the architecture-specific pieces of follow-fork. This is the
> mechanism that handles copying the debug register state from the parent to
> the child.
>
> Patch 8: adds follow-fork for vfork.
>
> Patch 9: adds 'catch fork' and 'catch vfork', along with some code to make
> sure that killing a process that has forked, but before the fork is
> followed, also kills the child process.
>
> Patch 10: implements changes to the manual and the NEWS file for the fork
> event changes.
>
> Patch 11: implements support for the extended ptrace event
> PTRACE_EVENT_EXIT, which is a prerequisite for the exec event support.
>
> Patch 12: implements exec event support and follow-exec.
>
> Patch 13: implements exec catchpoints.
>
> Patch 14: suppresses some spurious warnings that were generated after
> an exec. These warnings were caused when gdbserver attempted to check
> the version number from the r_debug structure in the new image before
> the structure had been initialized.
>
> Patch 15: implements changed to the manual and NEWS file for the exec
> event changes.
>
> Patch 16: changes how gdb.base/foll-exec.exp starts GDB and loads the
> program so that it will work with extended-remote. It also extends the
> tests gdb.threads/non-ldr-exc-[1-4].exp to test in non-stop mode as well as
> in all-stop mode.
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 00/16 v3] Linux extended-remote fork and exec events
2014-10-31 23:29 ` [PATCH 00/16 v3] Linux extended-remote fork and exec events Don Breazeal
2014-11-12 15:54 ` Pedro Alves
@ 2014-11-13 13:41 ` Pedro Alves
2014-11-13 13:51 ` Pedro Alves
2014-11-13 19:14 ` Pedro Alves
2 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-11-13 13:41 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 10/31/2014 11:28 PM, Don Breazeal wrote:
>
> - gdb.threads/thread-execl.exp gives a couple of failures related to
> scheduler locking. As with the previous item, after spending some
> time on this I concluded that pursuing it further now would be
> feature-creep, and that this should be tracked with a bug report.
Do you have more details on this?
Looking at the exec race you mentioned, I thought that thread-execl.exp should
expose it, given that the point of the test is exactly a thread other than
the main thread execing. But then I stumbled on the fact that running it with
your series on top of currently mainline often crashes gdb:
$ make check RUNTESTFLAGS="--target_board=native-extended-gdbserver thread-execl.exp"
...
Running /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.threads/thread-execl.exp ...
ERROR: Process no longer exists
=== gdb Summary ===
# of expected passes 9
# of unresolved testcases 1
Odd that this doesn't trigger with native testing.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 00/16 v3] Linux extended-remote fork and exec events
2014-11-13 13:41 ` Pedro Alves
@ 2014-11-13 13:51 ` Pedro Alves
2014-11-13 14:58 ` Pedro Alves
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-11-13 13:51 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 11/13/2014 01:38 PM, Pedro Alves wrote:
> On 10/31/2014 11:28 PM, Don Breazeal wrote:
>>
>> - gdb.threads/thread-execl.exp gives a couple of failures related to
>> scheduler locking. As with the previous item, after spending some
>> time on this I concluded that pursuing it further now would be
>> feature-creep, and that this should be tracked with a bug report.
>
> Do you have more details on this?
>
> Looking at the exec race you mentioned, I thought that thread-execl.exp should
> expose it, given that the point of the test is exactly a thread other than
> the main thread execing. But then I stumbled on the fact that running it with
> your series on top of currently mainline often crashes gdb:
>
> $ make check RUNTESTFLAGS="--target_board=native-extended-gdbserver thread-execl.exp"
> ...
> Running /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.threads/thread-execl.exp ...
> ERROR: Process no longer exists
>
> === gdb Summary ===
>
> # of expected passes 9
> # of unresolved testcases 1
>
> Odd that this doesn't trigger with native testing.
Hmm, here's what valgrind shows (against gdbserver):
$ valgrind ./gdb -data-directory=data-directory ./testsuite/gdb.threads/thread-execl -ex "tar extended-remote :9999" -ex "b thread_execler" -ex "c" -ex "set scheduler-locking on"
...
Breakpoint 1, thread_execler (arg=0x0) at /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.threads/thread-execl.c:29
29 if (execl (image, image, NULL) == -1)
(gdb) n
Thread 32509.32509 is executing new program: /home/pedro/gdb/mygit/build/gdb/testsuite/gdb.threads/thread-execl
[New Thread 32509.32532]
==32510== Invalid read of size 4
==32510== at 0x5AA7D8: delete_breakpoint (breakpoint.c:13989)
==32510== by 0x6285D3: delete_thread_breakpoint (thread.c:100)
==32510== by 0x628603: delete_step_resume_breakpoint (thread.c:109)
==32510== by 0x61622B: delete_thread_infrun_breakpoints (infrun.c:2928)
==32510== by 0x6162EF: for_each_just_stopped_thread (infrun.c:2958)
==32510== by 0x616311: delete_just_stopped_threads_infrun_breakpoints (infrun.c:2969)
==32510== by 0x616C96: fetch_inferior_event (infrun.c:3267)
==32510== by 0x63A2DE: inferior_event_handler (inf-loop.c:57)
==32510== by 0x4E0E56: remote_async_serial_handler (remote.c:11877)
==32510== by 0x4AF620: run_async_handler_and_reschedule (ser-base.c:137)
==32510== by 0x4AF6F0: fd_event (ser-base.c:182)
==32510== by 0x63806D: handle_file_event (event-loop.c:762)
==32510== Address 0xcf333e0 is 16 bytes inside a block of size 200 free'd
==32510== at 0x4A07577: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==32510== by 0x77CB74: xfree (common-utils.c:98)
==32510== by 0x5AA954: delete_breakpoint (breakpoint.c:14056)
==32510== by 0x5988BD: update_breakpoints_after_exec (breakpoint.c:3765)
==32510== by 0x61360F: follow_exec (infrun.c:1091)
==32510== by 0x6186FA: handle_inferior_event (infrun.c:4061)
==32510== by 0x616C55: fetch_inferior_event (infrun.c:3261)
==32510== by 0x63A2DE: inferior_event_handler (inf-loop.c:57)
==32510== by 0x4E0E56: remote_async_serial_handler (remote.c:11877)
==32510== by 0x4AF620: run_async_handler_and_reschedule (ser-base.c:137)
==32510== by 0x4AF6F0: fd_event (ser-base.c:182)
==32510== by 0x63806D: handle_file_event (event-loop.c:762)
==32510==
[Switching to Thread 32509.32532]
Breakpoint 1, thread_execler (arg=0x0) at /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.threads/thread-execl.c:29
29 if (execl (image, image, NULL) == -1)
(gdb)
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 00/16 v3] Linux extended-remote fork and exec events
2014-11-13 13:51 ` Pedro Alves
@ 2014-11-13 14:58 ` Pedro Alves
0 siblings, 0 replies; 110+ messages in thread
From: Pedro Alves @ 2014-11-13 14:58 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 11/13/2014 01:48 PM, Pedro Alves wrote:
> On 11/13/2014 01:38 PM, Pedro Alves wrote:
>> On 10/31/2014 11:28 PM, Don Breazeal wrote:
>>>
>>> - gdb.threads/thread-execl.exp gives a couple of failures related to
>>> scheduler locking. As with the previous item, after spending some
>>> time on this I concluded that pursuing it further now would be
>>> feature-creep, and that this should be tracked with a bug report.
>>
>> Do you have more details on this?
>>
>> Looking at the exec race you mentioned, I thought that thread-execl.exp should
>> expose it, given that the point of the test is exactly a thread other than
>> the main thread execing. But then I stumbled on the fact that running it with
>> your series on top of currently mainline often crashes gdb:
>>
>> $ make check RUNTESTFLAGS="--target_board=native-extended-gdbserver thread-execl.exp"
>> ...
>> Running /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.threads/thread-execl.exp ...
>> ERROR: Process no longer exists
>>
>> === gdb Summary ===
>>
>> # of expected passes 9
>> # of unresolved testcases 1
>>
>> Odd that this doesn't trigger with native testing.
>
> Hmm, here's what valgrind shows (against gdbserver):
> $ valgrind ./gdb -data-directory=data-directory ./testsuite/gdb.threads/thread-execl -ex "tar extended-remote :9999" -ex "b thread_execler" -ex "c" -ex "set scheduler-locking on"
> ...
> Breakpoint 1, thread_execler (arg=0x0) at /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.threads/thread-execl.c:29
> 29 if (execl (image, image, NULL) == -1)
> (gdb) n
> Thread 32509.32509 is executing new program: /home/pedro/gdb/mygit/build/gdb/testsuite/gdb.threads/thread-execl
> [New Thread 32509.32532]
> ==32510== Invalid read of size 4
> ==32510== at 0x5AA7D8: delete_breakpoint (breakpoint.c:13989)
> ==32510== by 0x6285D3: delete_thread_breakpoint (thread.c:100)
> ==32510== by 0x628603: delete_step_resume_breakpoint (thread.c:109)
> ==32510== by 0x61622B: delete_thread_infrun_breakpoints (infrun.c:2928)
> ==32510== by 0x6162EF: for_each_just_stopped_thread (infrun.c:2958)
> ==32510== by 0x616311: delete_just_stopped_threads_infrun_breakpoints (infrun.c:2969)
> ==32510== by 0x616C96: fetch_inferior_event (infrun.c:3267)
> ==32510== by 0x63A2DE: inferior_event_handler (inf-loop.c:57)
> ==32510== by 0x4E0E56: remote_async_serial_handler (remote.c:11877)
> ==32510== by 0x4AF620: run_async_handler_and_reschedule (ser-base.c:137)
> ==32510== by 0x4AF6F0: fd_event (ser-base.c:182)
> ==32510== by 0x63806D: handle_file_event (event-loop.c:762)
> ==32510== Address 0xcf333e0 is 16 bytes inside a block of size 200 free'd
> ==32510== at 0x4A07577: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
> ==32510== by 0x77CB74: xfree (common-utils.c:98)
> ==32510== by 0x5AA954: delete_breakpoint (breakpoint.c:14056)
> ==32510== by 0x5988BD: update_breakpoints_after_exec (breakpoint.c:3765)
> ==32510== by 0x61360F: follow_exec (infrun.c:1091)
> ==32510== by 0x6186FA: handle_inferior_event (infrun.c:4061)
> ==32510== by 0x616C55: fetch_inferior_event (infrun.c:3261)
> ==32510== by 0x63A2DE: inferior_event_handler (inf-loop.c:57)
> ==32510== by 0x4E0E56: remote_async_serial_handler (remote.c:11877)
> ==32510== by 0x4AF620: run_async_handler_and_reschedule (ser-base.c:137)
> ==32510== by 0x4AF6F0: fd_event (ser-base.c:182)
> ==32510== by 0x63806D: handle_file_event (event-loop.c:762)
> ==32510==
> [Switching to Thread 32509.32532]
>
> Breakpoint 1, thread_execler (arg=0x0) at /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.threads/thread-execl.c:29
> 29 if (execl (image, image, NULL) == -1)
> (gdb)
Ah. The breakpoint in question is the step-resume breakpoint
of the non-main thread, the one we "nexted".
And the issue is that with native debugging, the target deletes
all threads from GDB's list _before_ the exec event is reported:
...
infrun: stop_pc = 0x400640
infrun: stepped into subroutine
infrun: inserting step-resume breakpoint at 0x40076f <<<<<<
infrun: resume (step=0, signal=GDB_SIGNAL_0), trap_expected=0, current thread [Thread 0x7ffff7fc4700 (LWP 555)] at 0x400640
infrun: prepare_to_wait
infrun: target_wait (-1, status) =
infrun: -1 [process -1],
infrun: status->kind = ignore
infrun: TARGET_WAITKIND_IGNORE
infrun: prepare_to_wait
infrun: target_wait (-1, status) =
infrun: -1 [process -1],
infrun: status->kind = ignore
infrun: TARGET_WAITKIND_IGNORE
infrun: prepare_to_wait
[Thread 0x7ffff7fc4700 (LWP 555) exited]
Breakpoint 3, delete_thread (ptid=...) at /home/pedro/gdb/mygit/src/gdb/thread.c:371
371 delete_thread_1 (ptid, 0 /* not silent */);
(top-gdb)
But when remote debugging, there are no thread exit events,
so GDB never deletes the thread that was "nexted". And that
thread still has a dangling reference to the step-resume
breakpoint.
With this hack:
---
gdb/linux-nat.c | 2 ++
gdb/linux-thread-db.c | 2 ++
2 files changed, 4 insertions(+)
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index e81a560..df1d6e7 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -892,7 +892,9 @@ exit_lwp (struct lwp_info *lp)
if (print_thread_events)
printf_unfiltered (_("[%s exited]\n"), target_pid_to_str (lp->ptid));
+#if 0
delete_thread (lp->ptid);
+#endif
}
delete_lwp (lp->ptid);
diff --git a/gdb/linux-thread-db.c b/gdb/linux-thread-db.c
index c49b567..59f8ec1 100644
--- a/gdb/linux-thread-db.c
+++ b/gdb/linux-thread-db.c
@@ -597,6 +597,8 @@ enable_thread_event_reporting (void)
td_err_e err;
struct thread_db_info *info;
+ return;
+
info = get_thread_db_info (ptid_get_pid (inferior_ptid));
/* We cannot use the thread event reporting facility if these
--
1.9.3
We see the same bad references with native debugging:
Breakpoint 1, thread_execler (arg=0x0) at /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.threads/thread-execl.c:29
29 if (execl (image, image, NULL) == -1)
(gdb) n
[Thread 0x7ffff7fc4700 (LWP 28506) exited]
process 24152 is executing new program: /home/pedro/gdb/mygit/build/gdb/testsuite/gdb.threads/thread-execl
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
[New Thread 0x7ffff7fc4700 (LWP 4647)]
==12378== Invalid read of size 4
==12378== at 0x5AA5E8: delete_breakpoint (breakpoint.c:13989)
==12378== by 0x6283E3: delete_thread_breakpoint (thread.c:100)
==12378== by 0x628413: delete_step_resume_breakpoint (thread.c:109)
==12378== by 0x61603B: delete_thread_infrun_breakpoints (infrun.c:2928)
==12378== by 0x6160FF: for_each_just_stopped_thread (infrun.c:2958)
==12378== by 0x616121: delete_just_stopped_threads_infrun_breakpoints (infrun.c:2969)
==12378== by 0x616AA6: fetch_inferior_event (infrun.c:3267)
==12378== by 0x63A0EE: inferior_event_handler (inf-loop.c:57)
==12378== by 0x4BF44A: handle_target_event (linux-nat.c:4439)
==12378== by 0x637E7D: handle_file_event (event-loop.c:762)
==12378== by 0x637364: process_event (event-loop.c:339)
==12378== by 0x637406: gdb_do_one_event (event-loop.c:391)
==12378== Address 0xcebf910 is 16 bytes inside a block of size 200 free'd
==12378== at 0x4A07577: free (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==12378== by 0x77C984: xfree (common-utils.c:98)
==12378== by 0x5AA764: delete_breakpoint (breakpoint.c:14056)
==12378== by 0x5986CD: update_breakpoints_after_exec (breakpoint.c:3765)
==12378== by 0x61341F: follow_exec (infrun.c:1091)
==12378== by 0x61850A: handle_inferior_event (infrun.c:4061)
==12378== by 0x616A65: fetch_inferior_event (infrun.c:3261)
==12378== by 0x63A0EE: inferior_event_handler (inf-loop.c:57)
==12378== by 0x4BF44A: handle_target_event (linux-nat.c:4439)
==12378== by 0x637E7D: handle_file_event (event-loop.c:762)
==12378== by 0x637364: process_event (event-loop.c:339)
==12378== by 0x637406: gdb_do_one_event (event-loop.c:391)
==12378==
[Switching to Thread 0x7ffff7fc4700 (LWP 4647)]
Breakpoint 1, thread_execler (arg=0x0) at /home/pedro/gdb/mygit/src/gdb/testsuite/gdb.threads/thread-execl.c:29
29 if (execl (image, image, NULL) == -1)
(gdb)
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 00/16 v3] Linux extended-remote fork and exec events
2014-10-31 23:29 ` [PATCH 00/16 v3] Linux extended-remote fork and exec events Don Breazeal
2014-11-12 15:54 ` Pedro Alves
2014-11-13 13:41 ` Pedro Alves
@ 2014-11-13 19:14 ` Pedro Alves
2 siblings, 0 replies; 110+ messages in thread
From: Pedro Alves @ 2014-11-13 19:14 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 10/31/2014 11:28 PM, Don Breazeal wrote:
>
> - gdb.threads/thread-execl.exp gives a couple of failures related to
> scheduler locking. As with the previous item, after spending some
> time on this I concluded that pursuing it further now would be
> feature-creep, and that this should be tracked with a bug report.
These really are problems with the series that should be fixed.
The next problem I saw with thread-execl.exp after fixing the stale
threads issue was that the inferior would sometimes crash after execing.
Funny, because I hit this similar problem on the linux-nat.c target
when working on all-stop-on-top-of-non-stop (but I haven't posted a
fix for the linux-nat.c side yet).
From 3ee65262da9d72ab6efd97aed538f1f436436c3d Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 13 Nov 2014 17:52:38 +0000
Subject: [PATCH] stale thread state and regcaches when a non-leader thread
execs
---
gdb/gdbserver/linux-low.c | 12 ++++++++++++
gdb/remote.c | 10 ++++++++++
2 files changed, 22 insertions(+)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 316b302..e4e4231 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -546,6 +546,18 @@ handle_extended_wait (struct lwp_info *event_child, int *wstatp)
lwpid_of (event_thr));
}
+ /* This may have been a non-leader thread execing, while the
+ previous incarnation of the leader was stopped (e.g., we
+ resumed the thread that execs with schedlock on). Clear the
+ now stale register cache. */
+ get_thread_regcache (event_thr, 0)->registers_valid = 0;
+ /* ... and mark the thread as resumed, as otherwise we'd
+ consider that GDB had explicitly interrupted it. */
+ event_thr->last_resume_kind = resume_continue;
+ /* and clear this so we don't confuse things like decr_pc
+ adjustment. */
+ event_child->stepping = 0;
+
event_child->waitstatus.kind = TARGET_WAITKIND_EXECD;
event_child->waitstatus.value.execd_pathname
= xstrdup (linux_pid_to_exec_file (NULL, lwpid_of (event_thr)));
diff --git a/gdb/remote.c b/gdb/remote.c
index 9623bd6..c529a37 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5973,6 +5973,16 @@ process_stop_reply (struct stop_reply *stop_reply,
if (ptid_equal (ptid, null_ptid))
ptid = inferior_ptid;
+ if (status->kind == TARGET_WAITKIND_EXECD)
+ {
+ /* This may have been a non-leader thread execing, while the
+ previous incarnation of the leader was stopped (e.g., we
+ resumed the thread that execs with schedlock on). Clear the
+ now stale register cache before re-filling with the expedite
+ registers. */
+ registers_changed_ptid (ptid);
+ }
+
if (status->kind != TARGET_WAITKIND_EXITED
&& status->kind != TARGET_WAITKIND_SIGNALLED)
{
--
1.9.3
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 04/16 v3] Determine supported extended-remote features
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (4 preceding siblings ...)
2014-10-31 23:29 ` [PATCH 00/16 v3] Linux extended-remote fork and exec events Don Breazeal
@ 2014-10-31 23:29 ` Don Breazeal
2014-11-13 12:59 ` Pedro Alves
2014-10-31 23:29 ` [PATCH 05/16 v3] GDBserver clone breakpoint list Don Breazeal
` (8 subsequent siblings)
14 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:29 UTC (permalink / raw)
To: gdb-patches
This patch implements a mechanism for GDB to determine what
extended-mode features are enabled in gdbserver. This is
a preparatory patch for extended-remote fork and exec event
support.
Several new features are included in the potential response to
the qSupported packet, denoting fork, vfork, and exec events.
and exec events. Note that vfork_done events are not represented
here, because if vfork events are supported, gdbserver will fake
up a vfork_done event for GDB even if the target doesn't provide
direct support for vfork_done.
A number of changes were required to make this work:
1) Sending the "!" (use extended protocol) packet before sending the
qSupported packet so that gdbserver knows it is in extended mode
when responding to qSupported. Previously qSupported was the
first packet sent.
2) Splitting nat/linux-ptrace.c:linux_enable_event_reporting into two
functions. Previously that function did two things: it checked
which extended ptrace events were supported, and then enabled any
of those events that were supported by the OS. The new functions,
linux_ptrace_check_options and linux_ptrace_enable options, each do
one of those operations. This allows gdbserver to call
linux_ptrace_check_options during initialization, prior to
accepting a connection from GDB. It saves the information about
which events are available, so that if and when gdbserver goes into
extended mode, those events can be enabled, and GDB and gdbserver
will agree about which events are enabled.
There are some changes to linux-ptrace.c that build on the changes
in commit 8009206ae2dec541b55edc488103c6c1ccb1416a. That commit
reduced the use of #ifdef GDBSERVER in linux-ptrace.c by allowing
the ptrace client (GDB or gdbserver) to request support for
certain ptrace options by maintaining static variables
current_ptrace_options (enabled options) and additional_options
(non-default options that the ptrace client has requested).
This patch modifies that to maintain three static variables:
current_ptrace_options (enabled options), requested_ptrace_options
(non-default options that the ptrace client has requested), and
available_ptrace_options (options from requested_ptrace_options
that are supported by the OS). available_ptrace_options is used
in linux_ptrace_enable_options when extended-mode is enabled.
3) Defining a new packet for each of the extended-mode events along
with corresponding "supports" predicates for each of them.
Some support for ptrace exit events is introduced in this patch,
although they can't be enabled until linux_ptrace_test_for_traceexit
is implemented in a subsequent patch. Exit events are used in the
implementation of remote follow exec later in the patch series, so
they are included in the infrastructure here with the other events.
Tested on x64 Ubuntu Lucid, native only, and as part of testing of
subsequent patches.
gdb/gdbserver/ChangeLog:
2014-10-31 Don Breazeal <donb@codesourcery.com>
* linux-low.c (linux_low_filter_event): Replace call to
linux_enable_event_reporting with call to
linux_ptrace_enable_options.
(linux_supports_fork_events): New function.
(linux_supports_vfork_events): New function.
(linux_supports_exec_events): New function.
(linux_enable_extended_features): New function.
(linux_target_ops): Add entries for the four new functions in
the target vector.
(initialize_low): Add calls to linux_ptrace_set_requested_options
and linux_ptrace_check_options.
* lynx-low.c (lynx_target_ops): Add NULL entries for the four
new functions in the target vector.
* server.c (handle_query): Add fork_events, vfork_events, and
exec_events to features that can be listed in qSupported response.
(process_serial_event): Call target function to enable extended
features when "!" extended mode packet is received.
(using_extended_protocol): New function.
* server.h (using_extended_protocol): Declare new function.
* target.h (struct target_ops) <supports_fork_events>: New member.
<supports_vfork_events>: New member.
<supports_exec_events>: New member.
<enable_extended_features>: New member.
* win32-low.c (win32_target_ops): Add NULL entries for the four
new functions in the target vector.
gdb/ChangeLog:
2014-10-31 Don Breazeal <donb@codesourcery.com>
* linux-nat.c (linux_init_ptrace): Replace call to
linux_enable_event_reporting with calls to
linux_ptrace_check_options and linux_ptrace_enable_options.
(linux_child_follow_fork): Replace call to
linux_disable_event_reporting with a call to
linux_ptrace_disable_options.
* nat/linux-ptrace.c (linux_ptrace_check_options): New function,
derived from linux_enable_event_reporting.
(linux_test_for_tracesysgood): Replace additional_flags with
requested_ptrace_options.
(linux_ptrace_check_options): Rename from linux_check_ptrace_features,
make it extern, use requested_ptrace_options and
available_ptrace_options in place of additional_flags.
(linux_ptrace_enable_options): New function, derived from
linux_enable_event_reporting.
(linux_ptrace_disable_options): Rename from
linux_disable_event_reporting.
(linux_supports_traceexit): New function.
(linux_ptrace_set_requested_options): Rename from
linux_ptrace_set_additional_flags, use requested_ptrace_options
instead of additional_flags.
* nat/linux-ptrace.h (linux_enable_event_reporting,
linux_disable_event_reporting): Remove renamed functions.
(linux_ptrace_check_options, linux_ptrace_enable_options,
linux_ptrace_disable_options, linux_supports_traceexit): Declare
new functions.
(linux_ptrace_set_additional_flags): Remove renamed function.
(linux_ptrace_set_requested_options): Declare new function.
* remote.c (anonymous enum) <PACKET_vFollowFork>: New constant.
<PACKET_fork_event_feature>: New constant.
<PACKET_vfork_event_feature>: New constant.
<PACKET_exec_event_feature>: New constant.
(extended_remote_fork_event_p): New function.
(extended_remote_vfork_event_p): New function.
(extended_remote_exec_event_p): New function.
(extended_remote_follow_fork): New function.
(remote_protocol_features): Add entries for the four new packets.
(remote_start_remote): Send "!" extended packet before qSupported.
(_initialize_remote): Add three packets to the list of packets
excepted from having config commands.
---
gdb/gdbserver/linux-low.c | 53 +++++++++++++++++++++++-
gdb/gdbserver/lynx-low.c | 4 ++
gdb/gdbserver/server.c | 19 +++++++++
gdb/gdbserver/server.h | 1 +
gdb/gdbserver/target.h | 32 ++++++++++++++
gdb/gdbserver/win32-low.c | 4 ++
gdb/linux-nat.c | 15 ++++---
gdb/nat/linux-ptrace.c | 87 ++++++++++++++++++++++++++-------------
gdb/nat/linux-ptrace.h | 8 ++-
gdb/remote.c | 100 +++++++++++++++++++++++++++++++++++++++-----
10 files changed, 271 insertions(+), 52 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 8776670..8334578 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -1878,7 +1878,7 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
{
- linux_enable_event_reporting (lwpid);
+ linux_ptrace_enable_options (lwpid, using_extended_protocol ());
child->must_set_ptrace_flags = 0;
}
@@ -5168,6 +5168,33 @@ linux_supports_multi_process (void)
return 1;
}
+/* Check if fork events are supported. */
+
+static int
+linux_supports_fork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if vfork events are supported. */
+
+static int
+linux_supports_vfork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if exec events are supported. */
+
+static int
+linux_supports_exec_events (void)
+{
+ /* Check for PTRACE_O_TRACEEXIT, since our implementation of follow
+ exec depends on this option, which was implemented in a later
+ kernel version than PTRACE_O_TRACEFORK et al. */
+ return linux_supports_traceexit ();
+}
+
static int
linux_supports_disable_randomization (void)
{
@@ -6035,6 +6062,22 @@ linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
}
#endif /* HAVE_LINUX_BTRACE */
+/* Enable any available extended-mode-only options. */
+
+static void
+linux_enable_extended_features (void)
+{
+ long lwp = 0;
+
+ if (current_thread != NULL)
+ {
+ /* There is an inferior, so set the lwp argument. */
+ lwp = ptid_get_lwp (current_thread->entry.id);
+ }
+
+ linux_ptrace_enable_options (lwp, using_extended_protocol ());
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
@@ -6079,6 +6122,10 @@ static struct target_ops linux_target_ops = {
linux_async,
linux_start_non_stop,
linux_supports_multi_process,
+ linux_supports_fork_events,
+ linux_supports_vfork_events,
+ linux_supports_exec_events,
+ linux_enable_extended_features,
#ifdef USE_THREAD_DB
thread_db_handle_monitor_command,
#else
@@ -6154,4 +6201,8 @@ initialize_low (void)
sigaction (SIGCHLD, &sigchld_action, NULL);
initialize_low_arch ();
+
+ /* Placeholder to enable extended events. */
+ linux_ptrace_set_requested_options (0);
+ linux_ptrace_check_options ();
}
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 96dea03..3ccc032 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -751,6 +751,10 @@ static struct target_ops lynx_target_ops = {
NULL, /* async */
NULL, /* start_non_stop */
NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
+ NULL, /* supports_exec_events */
+ NULL, /* enable_extended_features */
NULL, /* handle_monitor_command */
};
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 522d6f6..d297403 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1903,6 +1903,15 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_multi_process ())
strcat (own_buf, ";multiprocess+");
+ if (target_supports_fork_events ())
+ strcat (own_buf, ";fork_events+");
+
+ if (target_supports_vfork_events ())
+ strcat (own_buf, ";vfork_events+");
+
+ if (target_supports_exec_events ())
+ strcat (own_buf, ";exec_events+");
+
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
@@ -3559,6 +3568,10 @@ process_serial_event (void)
break;
case '!':
extended_protocol = 1;
+
+ if (the_target->enable_extended_features != NULL)
+ (*the_target->enable_extended_features) ();
+
write_ok (own_buf);
break;
case '?':
@@ -3990,3 +4003,9 @@ handle_target_event (int err, gdb_client_data client_data)
return 0;
}
+
+int
+using_extended_protocol (void)
+{
+ return extended_protocol;
+}
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index cac73e9..f4f192c 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -109,6 +109,7 @@ typedef int gdb_fildes_t;
/* Functions from server.c. */
extern int handle_serial_event (int err, gdb_client_data client_data);
extern int handle_target_event (int err, gdb_client_data client_data);
+extern int using_extended_protocol (void);
#include "remote-utils.h"
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 5e29b7f..570f57d 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -262,6 +262,18 @@ struct target_ops
/* Returns true if the target supports multi-process debugging. */
int (*supports_multi_process) (void);
+ /* Returns true if fork events are supported. */
+ int (*supports_fork_events) (void);
+
+ /* Returns true if vfork events are supported. */
+ int (*supports_vfork_events) (void);
+
+ /* Returns true if exec events are supported. */
+ int (*supports_exec_events) (void);
+
+ /* Enable features that are only available in extended mode. */
+ void (*enable_extended_features) (void);
+
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
@@ -390,6 +402,26 @@ void set_target_ops (struct target_ops *);
int kill_inferior (int);
+#define target_supports_follow_fork() \
+ (the_target->supports_follow_fork ? \
+ (*the_target->supports_follow_fork) () : 0)
+
+#define target_supports_follow_exec() \
+ (the_target->supports_follow_exec ? \
+ (*the_target->supports_follow_exec) () : 0)
+
+#define target_supports_fork_events() \
+ (the_target->supports_fork_events ? \
+ (*the_target->supports_fork_events) () : 0)
+
+#define target_supports_vfork_events() \
+ (the_target->supports_vfork_events ? \
+ (*the_target->supports_vfork_events) () : 0)
+
+#define target_supports_exec_events() \
+ (the_target->supports_exec_events ? \
+ (*the_target->supports_exec_events) () : 0)
+
#define detach_inferior(pid) \
(*the_target->detach) (pid)
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index e714933..9ae2f94 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1823,6 +1823,10 @@ static struct target_ops win32_target_ops = {
NULL, /* async */
NULL, /* start_non_stop */
NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
+ NULL, /* supports_exec_events */
+ NULL, /* enable_extended_features */
NULL, /* handle_monitor_command */
NULL, /* core_of_thread */
NULL, /* read_loadmap */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 21797c1..f0487e9 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -326,7 +326,8 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
static void
linux_init_ptrace (pid_t pid)
{
- linux_enable_event_reporting (pid);
+ linux_ptrace_check_options ();
+ linux_ptrace_enable_options (pid, 1);
linux_ptrace_init_warnings ();
}
@@ -418,7 +419,7 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child,
if (!gdbarch_software_single_step_p (target_thread_architecture
(child_lp->ptid)))
{
- linux_disable_event_reporting (child_pid);
+ 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)
@@ -4803,11 +4804,11 @@ Enables printf debugging output."),
/* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
support read-only process state. */
- linux_ptrace_set_additional_flags (PTRACE_O_TRACESYSGOOD
- | PTRACE_O_TRACEVFORKDONE
- | PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEEXEC);
+ linux_ptrace_set_requested_options (PTRACE_O_TRACESYSGOOD
+ | PTRACE_O_TRACEVFORKDONE
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEEXEC);
}
\f
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 8bc3f16..7871d95 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -25,14 +25,20 @@
#include <stdint.h>
-/* Stores the currently supported ptrace options. A value of
- -1 means we did not check for features yet. A value of 0 means
- there are no supported features. */
-static int current_ptrace_options = -1;
+/* Stores the ptrace options that have been requested by the
+ ptrace client beyond the default options that we attempt
+ to enable for all ptrace clients. */
+static int requested_ptrace_options;
-/* Additional flags to test. */
+/* Stores the ptrace options from REQUESTED_PTRACE_OPTIONS
+ that are supported by the OS. A value of -1 means we did
+ not check for features yet. A value of 0 means that none
+ of the requested options are supported. */
+static int available_ptrace_options = -1;
-static int additional_flags;
+/* Stores the currently enabled ptrace options, or the default
+ option(s) that will be enabled once a process is loaded. */
+static int current_ptrace_options;
/* Find all possible reasons we could fail to attach PID and append
these as strings to the already initialized BUFFER. '\0'
@@ -310,13 +316,19 @@ static void linux_test_for_tracefork (int child_pid);
/* Determine ptrace features available on this target. */
-static void
-linux_check_ptrace_features (void)
+void
+linux_ptrace_check_options (void)
{
int child_pid, ret, status;
+ /* Check if we have initialized the ptrace features for this
+ target. If not, proceed. */
+ if (available_ptrace_options != -1)
+ return;
+
/* Initialize the options. */
current_ptrace_options = 0;
+ available_ptrace_options = 0;
/* Fork a child so we can do some testing. The child will call
linux_child_function and will get traced. The child will
@@ -358,14 +370,14 @@ linux_test_for_tracesysgood (int child_pid)
{
int ret;
- if ((additional_flags & PTRACE_O_TRACESYSGOOD) == 0)
+ if ((requested_ptrace_options & PTRACE_O_TRACESYSGOOD) == 0)
return;
ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
(PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
if (ret == 0)
- current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
+ available_ptrace_options |= PTRACE_O_TRACESYSGOOD;
}
/* Determine if PTRACE_O_TRACEFORK can be used to follow fork
@@ -385,14 +397,14 @@ linux_test_for_tracefork (int child_pid)
if (ret != 0)
return;
- if ((additional_flags & PTRACE_O_TRACEVFORKDONE) != 0)
+ if ((requested_ptrace_options & PTRACE_O_TRACEVFORKDONE) != 0)
{
/* Check if the target supports PTRACE_O_TRACEVFORKDONE. */
ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
(PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORKDONE));
if (ret == 0)
- current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
+ available_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
}
/* Setting PTRACE_O_TRACEFORK did not cause an error, however we
@@ -428,11 +440,14 @@ linux_test_for_tracefork (int child_pid)
int second_status;
/* We got the PID from the grandchild, which means fork
- tracing is supported. */
+ tracing is supported. Include default options in
+ current_ptrace_options and save the rest as
+ available options. */
current_ptrace_options |= PTRACE_O_TRACECLONE;
- current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEEXEC));
+ available_ptrace_options |= (requested_ptrace_options
+ & (PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEEXEC));
/* Do some cleanup and kill the grandchild. */
my_waitpid (second_pid, &second_status, 0);
@@ -449,25 +464,29 @@ linux_test_for_tracefork (int child_pid)
"(%d, status 0x%x)"), ret, status);
}
-/* Enable reporting of all currently supported ptrace events. */
+/* Enable reporting of supported ptrace events. If
+ USE_AVAILABLE_OPTIONS is false, then exclude the events
+ specified in available_ptrace_options. If PID is non-zero,
+ set the ptrace options using that process. */
void
-linux_enable_event_reporting (pid_t pid)
+linux_ptrace_enable_options (pid_t pid, int use_available_options)
{
- /* Check if we have initialized the ptrace features for this
- target. If not, do it now. */
- if (current_ptrace_options == -1)
- linux_check_ptrace_features ();
+ if (use_available_options)
+ current_ptrace_options |= available_ptrace_options;
- /* Set the options. */
- ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
- (PTRACE_TYPE_ARG4) (uintptr_t) current_ptrace_options);
+ if (pid != 0)
+ {
+ /* Set the options. */
+ ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
+ (PTRACE_TYPE_ARG4) (uintptr_t) current_ptrace_options);
+ }
}
/* Disable reporting of all currently supported ptrace events. */
void
-linux_disable_event_reporting (pid_t pid)
+linux_ptrace_disable_options (pid_t pid)
{
/* Set the options. */
ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0, 0);
@@ -516,6 +535,15 @@ linux_supports_tracevforkdone (void)
return ptrace_supports_feature (PTRACE_O_TRACEVFORKDONE);
}
+/* Returns non-zero if PTRACE_O_TRACEEXIT is supported by ptrace,
+ 0 otherwise. */
+
+int
+linux_supports_traceexit (void)
+{
+ return ptrace_supports_feature (PTRACE_O_TRACEEXIT);
+}
+
/* Returns non-zero if PTRACE_O_TRACESYSGOOD is supported by ptrace,
0 otherwise. */
@@ -540,15 +568,16 @@ linux_ptrace_init_warnings (void)
linux_ptrace_test_ret_to_nx ();
}
-/* Set additional ptrace flags to use. Some such flags may be checked
+/* Set additional ptrace flags to use, beyond the default options
+ checked for every ptrace client. Some such flags may be checked
by the implementation above. This function must be called before
any other function in this file; otherwise the flags may not take
effect appropriately. */
void
-linux_ptrace_set_additional_flags (int flags)
+linux_ptrace_set_requested_options (int flags)
{
- additional_flags = flags;
+ requested_ptrace_options = flags;
}
/* Extract extended ptrace event from wait status. */
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 31a77cd..03d27e1 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -85,13 +85,15 @@ struct buffer;
extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
extern void linux_ptrace_init_warnings (void);
-extern void linux_enable_event_reporting (pid_t pid);
-extern void linux_disable_event_reporting (pid_t pid);
+extern void linux_ptrace_check_options (void);
+extern void linux_ptrace_enable_options (pid_t pid, int use_available_options);
+extern void linux_ptrace_disable_options (pid_t pid);
extern int linux_supports_tracefork (void);
extern int linux_supports_traceclone (void);
extern int linux_supports_tracevforkdone (void);
+extern int linux_supports_traceexit (void);
extern int linux_supports_tracesysgood (void);
-extern void linux_ptrace_set_additional_flags (int);
+extern void linux_ptrace_set_requested_options (int);
extern int linux_ptrace_get_extended_event (int wstat);
extern int linux_is_extended_waitstatus (int wstat);
diff --git a/gdb/remote.c b/gdb/remote.c
index 20f2988..77c68d8 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1327,6 +1327,18 @@ enum {
/* Support for qXfer:libraries-svr4:read with a non-empty annex. */
PACKET_augmented_libraries_svr4_read_feature,
+ /* Support for follow fork. */
+ PACKET_vFollowFork,
+
+ /* Support for fork events. */
+ PACKET_fork_event_feature,
+
+ /* Support for vfork events. */
+ PACKET_vfork_event_feature,
+
+ /* Support for exec events. */
+ PACKET_exec_event_feature,
+
PACKET_MAX
};
@@ -1432,6 +1444,52 @@ remote_multi_process_p (struct remote_state *rs)
return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
}
+/* Returns true if fork events are supported. */
+
+static int
+extended_remote_fork_event_p (struct remote_state *rs)
+{
+ return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
+}
+
+/* Ifdef out the two functions below until they are needed for vfork
+ and exec catchpoints. */
+#if 0
+/* Returns true if vfork events are supported. */
+
+static int
+extended_remote_vfork_event_p (struct remote_state *rs)
+{
+ return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
+}
+
+/* Returns true if exec events are supported. */
+
+static int
+extended_remote_exec_event_p (struct remote_state *rs)
+{
+ return packet_support (PACKET_exec_event_feature) == PACKET_ENABLE;
+}
+#endif
+
+/* Target follow-fork function for extended-remote targets. */
+
+static int
+extended_remote_follow_fork (struct target_ops *target, int follow_child,
+ int detach_fork)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ /* There is no need to check for anything other than the fork event.
+ We will also follow vforks if they are supported. */
+ if (extended_remote_fork_event_p (rs))
+ {
+ /* FIXME: Implement follow-fork here. */
+ return -1;
+ }
+ 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;
@@ -3386,9 +3444,18 @@ remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
and so things may not be stable yet. */
rs->starting_up = 1;
- /* The first packet we send to the target is the optional "supported
- packets" request. If the target can answer this, it will tell us
- which later probes to skip. */
+ if (extended_p)
+ {
+ /* Tell the remote that we are using the extended protocol
+ before making any queries about supported features, since
+ some features may only be supported in extended mode. */
+ putpkt ("!");
+ getpkt (&rs->buf, &rs->buf_size, 0);
+ }
+
+ /* The first query packet we send to the target is the optional
+ "supported packets" request. If the target can answer this, it
+ will tell us which later probes to skip. */
remote_query_supported ();
/* If the stub wants to get a QAllow, compose one and send it. */
@@ -3417,13 +3484,6 @@ remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
rs->noack_mode = 1;
}
- if (extended_p)
- {
- /* Tell the remote that we are using the extended protocol. */
- putpkt ("!");
- getpkt (&rs->buf, &rs->buf_size, 0);
- }
-
/* Let the target know which signals it is allowed to pass down to
the program. */
update_signals_program_target ();
@@ -3981,7 +4041,15 @@ static const struct protocol_feature remote_protocol_features[] = {
{ "Qbtrace:off", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_off },
{ "Qbtrace:bts", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_bts },
{ "qXfer:btrace:read", PACKET_DISABLE, remote_supported_packet,
- PACKET_qXfer_btrace }
+ PACKET_qXfer_btrace },
+ { "vFollowFork", PACKET_DISABLE, remote_supported_packet,
+ PACKET_vFollowFork },
+ { "fork_events", PACKET_DISABLE, remote_supported_packet,
+ PACKET_fork_event_feature },
+ { "vfork_events", PACKET_DISABLE, remote_supported_packet,
+ PACKET_vfork_event_feature },
+ { "exec_events", PACKET_DISABLE, remote_supported_packet,
+ PACKET_exec_event_feature }
};
static char *remote_support_xml;
@@ -11592,6 +11660,7 @@ init_extended_remote_ops (void)
Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_open = extended_remote_open;
extended_remote_ops.to_create_inferior = extended_remote_create_inferior;
+ extended_remote_ops.to_follow_fork = extended_remote_follow_fork;
extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
extended_remote_ops.to_detach = extended_remote_detach;
extended_remote_ops.to_attach = extended_remote_attach;
@@ -12162,7 +12231,11 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace],
"qXfer:btrace", "read-btrace", 0);
- /* Assert that we've registered commands for all packet configs. */
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_vFollowFork],
+ "vFollowFork", "follow-fork", 0);
+
+ /* Assert that we've registered "set remote foo-packet" commands
+ for all packet configs. */
{
int i;
@@ -12181,6 +12254,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
case PACKET_DisconnectedTracing_feature:
case PACKET_augmented_libraries_svr4_read_feature:
case PACKET_qCRC:
+ case PACKET_fork_event_feature:
+ case PACKET_vfork_event_feature:
+ case PACKET_exec_event_feature:
/* Additions to this list need to be well justified:
pre-existing packets are OK; new packets are not. */
excepted = 1;
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v3] Determine supported extended-remote features
2014-10-31 23:29 ` [PATCH 04/16 v3] Determine supported extended-remote features Don Breazeal
@ 2014-11-13 12:59 ` Pedro Alves
2014-11-13 18:28 ` Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-11-13 12:59 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 10/31/2014 11:28 PM, Don Breazeal wrote:
> This patch implements a mechanism for GDB to determine what
> extended-mode features are enabled in gdbserver. This is
> a preparatory patch for extended-remote fork and exec event
> support.
>
> Several new features are included in the potential response to
> the qSupported packet, denoting fork, vfork, and exec events.
> and exec events. Note that vfork_done events are not represented
> here, because if vfork events are supported, gdbserver will fake
> up a vfork_done event for GDB even if the target doesn't provide
> direct support for vfork_done.
>
> A number of changes were required to make this work:
>
> 1) Sending the "!" (use extended protocol) packet before sending the
> qSupported packet so that gdbserver knows it is in extended mode
> when responding to qSupported. Previously qSupported was the
> first packet sent.
I don't think we should do this. What's wrong with always
reporting support for the feature ?
If GDB doesn't want them with "target remote", then it won't use
them.
If the server needs to know whether GDB supports or wants
the feature before activating it, then GDB's qSupported query
should indicate it. See the "multiprocess+" feature.
As I mentioned before, I'd rather have fewer differences between
"remote" and "extended-remote", not more.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v3] Determine supported extended-remote features
2014-11-13 12:59 ` Pedro Alves
@ 2014-11-13 18:28 ` Breazeal, Don
2014-11-13 18:33 ` Pedro Alves
2014-11-13 18:37 ` Breazeal, Don
0 siblings, 2 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-11-13 18:28 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 11/13/2014 4:59 AM, Pedro Alves wrote:
> On 10/31/2014 11:28 PM, Don Breazeal wrote:
>> This patch implements a mechanism for GDB to determine what
>> extended-mode features are enabled in gdbserver. This is
>> a preparatory patch for extended-remote fork and exec event
>> support.
>>
>> Several new features are included in the potential response to
>> the qSupported packet, denoting fork, vfork, and exec events.
>> and exec events. Note that vfork_done events are not represented
>> here, because if vfork events are supported, gdbserver will fake
>> up a vfork_done event for GDB even if the target doesn't provide
>> direct support for vfork_done.
>>
>> A number of changes were required to make this work:
>>
>> 1) Sending the "!" (use extended protocol) packet before sending the
>> qSupported packet so that gdbserver knows it is in extended mode
>> when responding to qSupported. Previously qSupported was the
>> first packet sent.
>
> I don't think we should do this. What's wrong with always
> reporting support for the feature ?
>
> If GDB doesn't want them with "target remote", then it won't use
> them.
>
> If the server needs to know whether GDB supports or wants
> the feature before activating it, then GDB's qSupported query
> should indicate it. See the "multiprocess+" feature.
>
> As I mentioned before, I'd rather have fewer differences between
> "remote" and "extended-remote", not more.
>
> Thanks,
> Pedro Alves
>
Given your comment on patch 6 saying that there isn't any
reason not to support follow fork in remote as well as
extended-remote, the problem I was solving goes away.
The problem I thought I had was that if there was no
agreement between GDB and gdbserver on whether the
features were supported, then gdbserver would send
events to GDB that it wasn't expecting in remote mode,
and handling those looked problematic.
Those bad assumptions get me every time.
Thanks,
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v3] Determine supported extended-remote features
2014-11-13 18:28 ` Breazeal, Don
@ 2014-11-13 18:33 ` Pedro Alves
2014-11-13 19:08 ` Pedro Alves
2014-11-13 18:37 ` Breazeal, Don
1 sibling, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-11-13 18:33 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
On 11/13/2014 06:28 PM, Breazeal, Don wrote:
> On 11/13/2014 4:59 AM, Pedro Alves wrote:
>> On 10/31/2014 11:28 PM, Don Breazeal wrote:
>>> This patch implements a mechanism for GDB to determine what
>>> extended-mode features are enabled in gdbserver. This is
>>> a preparatory patch for extended-remote fork and exec event
>>> support.
>>>
>>> Several new features are included in the potential response to
>>> the qSupported packet, denoting fork, vfork, and exec events.
>>> and exec events. Note that vfork_done events are not represented
>>> here, because if vfork events are supported, gdbserver will fake
>>> up a vfork_done event for GDB even if the target doesn't provide
>>> direct support for vfork_done.
>>>
>>> A number of changes were required to make this work:
>>>
>>> 1) Sending the "!" (use extended protocol) packet before sending the
>>> qSupported packet so that gdbserver knows it is in extended mode
>>> when responding to qSupported. Previously qSupported was the
>>> first packet sent.
>>
>> I don't think we should do this. What's wrong with always
>> reporting support for the feature ?
>>
>> If GDB doesn't want them with "target remote", then it won't use
>> them.
>>
>> If the server needs to know whether GDB supports or wants
>> the feature before activating it, then GDB's qSupported query
>> should indicate it. See the "multiprocess+" feature.
>>
>> As I mentioned before, I'd rather have fewer differences between
>> "remote" and "extended-remote", not more.
> Given your comment on patch 6 saying that there isn't any
> reason not to support follow fork in remote as well as
> extended-remote, the problem I was solving goes away.
>
> The problem I thought I had was that if there was no
> agreement between GDB and gdbserver on whether the
> features were supported, then gdbserver would send
> events to GDB that it wasn't expecting in remote mode,
> and handling those looked problematic.
Yeah, but that's a problem that has to be handled
when new gdbserver is connected to old gdb anyway, even
in extended-remote mode.
> Those bad assumptions get me every time.
Sorry about that. :-/
I'm testing a fix for the stale-threads issue, that I think
we can just go ahead and install, and I have a few other
fixes to send you.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v3] Determine supported extended-remote features
2014-11-13 18:33 ` Pedro Alves
@ 2014-11-13 19:08 ` Pedro Alves
0 siblings, 0 replies; 110+ messages in thread
From: Pedro Alves @ 2014-11-13 19:08 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
On 11/13/2014 06:33 PM, Pedro Alves wrote:
>
> I'm testing a fix for the stale-threads issue, that I think
> we can just go ahead and install.
I just sent the email, but it looks like it got stuck in RHs
mail servers... Happens sometimes. It should show up in a while.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v3] Determine supported extended-remote features
2014-11-13 18:28 ` Breazeal, Don
2014-11-13 18:33 ` Pedro Alves
@ 2014-11-13 18:37 ` Breazeal, Don
2014-11-13 18:48 ` Pedro Alves
2015-01-12 22:36 ` Don Breazeal
1 sibling, 2 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-11-13 18:37 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 11/13/2014 4:59 AM, Pedro Alves wrote:
> On 10/31/2014 11:28 PM, Don Breazeal wrote:
>> This patch implements a mechanism for GDB to determine what
>> extended-mode features are enabled in gdbserver. This is
>> a preparatory patch for extended-remote fork and exec event
>> support.
>>
>> Several new features are included in the potential response to
>> the qSupported packet, denoting fork, vfork, and exec events.
>> and exec events. Note that vfork_done events are not represented
>> here, because if vfork events are supported, gdbserver will fake
>> up a vfork_done event for GDB even if the target doesn't provide
>> direct support for vfork_done.
>>
>> A number of changes were required to make this work:
>>
>> 1) Sending the "!" (use extended protocol) packet before sending the
>> qSupported packet so that gdbserver knows it is in extended mode
>> when responding to qSupported. Previously qSupported was the
>> first packet sent.
>
> I don't think we should do this. What's wrong with always
> reporting support for the feature ?
>
> If GDB doesn't want them with "target remote", then it won't use
> them.
>
> If the server needs to know whether GDB supports or wants
> the feature before activating it, then GDB's qSupported query
> should indicate it. See the "multiprocess+" feature.
>
> As I mentioned before, I'd rather have fewer differences between
> "remote" and "extended-remote", not more.
>
> Thanks,
> Pedro Alves
>
Given your comment on patch 6 saying that there isn't any
reason not to support follow fork in remote as well as
extended-remote, the problem I was solving goes away.
The problem I thought I had was that if there was no
agreement between GDB and gdbserver on whether the
features were supported, then gdbserver would send
events to GDB that it wasn't expecting in remote mode,
and handling those looked problematic.
Those bad assumptions get me every time. This change
should make things simpler as long as I don't run into
troubles with "remote".
Thanks,
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v3] Determine supported extended-remote features
2014-11-13 18:37 ` Breazeal, Don
@ 2014-11-13 18:48 ` Pedro Alves
2014-12-06 0:30 ` Breazeal, Don
2015-01-12 22:36 ` Don Breazeal
1 sibling, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-11-13 18:48 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
On 11/13/2014 06:37 PM, Breazeal, Don wrote:
> Given your comment on patch 6 saying that there isn't any
> reason not to support follow fork in remote as well as
> extended-remote, the problem I was solving goes away.
>
> The problem I thought I had was that if there was no
> agreement between GDB and gdbserver on whether the
> features were supported, then gdbserver would send
> events to GDB that it wasn't expecting in remote mode,
> and handling those looked problematic.
>
> Those bad assumptions get me every time. This change
> should make things simpler as long as I don't run into
> troubles with "remote".
The only thing I'm imagining at the moment, is that
in "remote" mode, gdbserver always disconnects after
a "D" / detach packet, even if there are other processes
to debug. But that seems silly anyway and we should
probably fix it.
As I mentioned, you'll need to make old GDB against new
GDBserver work correctly, that is, make gdbserver not send these new
events if debugging with old GDB, which is not expecting them.
That can be handled by making GDB send a "fork-events+" in its
own qSupported packet, so that GDBserver knows its talking to a
new GDB. See how the "multiprocess+" feature is handled in both
GDB and GDBserver. For a while, GDB didn't
broadcast "multiprocess+" when connecting with "target remote". See:
https://sourceware.org/ml/gdb-patches/2012-01/msg00490.html
One of the reasons I wanted that on "remote" was exactly to
make it possible to have fork events without caring for
remote vs extended-remote. :-)
If there are bigger problems, we can make GDB not broadcast
the support with plain remote for starters. But I'd be curious
to hear what the problems are.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v3] Determine supported extended-remote features
2014-11-13 18:48 ` Pedro Alves
@ 2014-12-06 0:30 ` Breazeal, Don
0 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-12-06 0:30 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 11/13/2014 10:48 AM, Pedro Alves wrote:
> On 11/13/2014 06:37 PM, Breazeal, Don wrote:
>
>> Given your comment on patch 6 saying that there isn't any
>> reason not to support follow fork in remote as well as
>> extended-remote, the problem I was solving goes away.
>>
>> The problem I thought I had was that if there was no
>> agreement between GDB and gdbserver on whether the
>> features were supported, then gdbserver would send
>> events to GDB that it wasn't expecting in remote mode,
>> and handling those looked problematic.
>>
>> Those bad assumptions get me every time. This change
>> should make things simpler as long as I don't run into
>> troubles with "remote".
>
> The only thing I'm imagining at the moment, is that
> in "remote" mode, gdbserver always disconnects after
> a "D" / detach packet, even if there are other processes
> to debug. But that seems silly anyway and we should
> probably fix it.
>
> As I mentioned, you'll need to make old GDB against new
> GDBserver work correctly, that is, make gdbserver not send these new
> events if debugging with old GDB, which is not expecting them.
> That can be handled by making GDB send a "fork-events+" in its
> own qSupported packet, so that GDBserver knows its talking to a
> new GDB. See how the "multiprocess+" feature is handled in both
> GDB and GDBserver. For a while, GDB didn't
> broadcast "multiprocess+" when connecting with "target remote". See:
>
> https://sourceware.org/ml/gdb-patches/2012-01/msg00490.html
>
> One of the reasons I wanted that on "remote" was exactly to
> make it possible to have fork events without caring for
> remote vs extended-remote. :-)
>
> If there are bigger problems, we can make GDB not broadcast
> the support with plain remote for starters. But I'd be curious
> to hear what the problems are.
>
> Thanks,
> Pedro Alves
>
Hi Pedro,
I have revised this patch to use the qSupported packet as you described,
and to eliminate the vFollowFork packet. There is a mechanism to
prevent reporting the events unless GDB sends e.g. "fork-events+".
Is this more in line with what you were thinking of?
thanks!
--Don
This patch implements a mechanism for GDB to determine whether fork
and exec events are supported in gdbserver. This is a preparatory
patch for remote fork and exec event support.
Three new RSP packets are defined, one each for fork, vfork, and exec
events. Other events like vfork-done may be added later in the patch
series if needed.
These packets are used just like PACKET_multiprocess_feature to denote
whether the corresponding event is supported by inquiring about packet
support in the qSupported packet, then enabling or disabling the packet
based on the qSupported response. Target functions used to query for
support are included for each new packet.
In order for gdbserver to know whether the events are supported at the
point where the qSupported packet arrives, the code in nat/linux-ptrace.c
had to be reorganized. Previously it would test for fork/exec event
support, then enable the events using the pid of the inferior. When the
qSupported packet arrives there may not be an inferior. So the mechanism
was split into two parts: a function that checks whether the events are
supported, called when gdbserver starts up, and another that enables the
events when the inferior stops for the first time.
Another gdbserver change was to add some global variables similar to
multi_process, one per new packet. These are used to control whether
the events are reported. If GDB does not inquire about the event
support in the qSupported packet, then gdbserver will not set these
"report the event" flags. If the flags are not set, the events are
ignored like they were in the past.
There are a couple of #ifdefs that have been added to allow the code for
managing the events to be included in this patch without enabling the
events. See PTRACE_EVENT_SUPPORT. These #ifdefrs will be removed later in
the patch series as the events are enabled.
Tested on Ubuntu x64, native only, and as part of subsequent patches in the
series.
gdb/gdbserver/
2014-12-05 Don Breazeal <donb@codesourcery.com>
* linux-low.c (linux_supports_fork_events): New function.
(linux_supports_vfork_events): New function.
(linux_supports_exec_events): New function.
(linux_target_ops): Initialize new structure members.
(initialize_low): Call linux_ptrace_set_additional_flags,
disabled for now, and linux_test_for_event_reporting.
* lynx-low.c: Initialize new structure members.
* server.c (report_fork_events, report_vfork_events,
report_exec_events): New global flags.
(handle_query): Add new feature queries to handling of
qSupported packet.
(captured_main): Initialize new global variables.
* target.h (struct target_ops) <supports_fork_events>:
New member.
<supports_vfork_events>: New member.
<supports_exec_events>: New member.
(target_supports_fork_events): New macro.
(target_supports_vfork_events): New macro.
(target_supports_exec_events): New macro.
* win32-low.c: Initialize new structure members.
gdb/
2014-12-05 Don Breazeal <donb@codesourcery.com>
* nat/linux-ptrace.c (linux_test_for_event_reporting): Rename
from linux_check_ptrace_features.
(linux_enable_event_reporting): Remove call to
linux_check_ptrace_features.
* nat/linux-ptrace.h (linux_test_for_event_reporting): Declare.
* remote.c (anonymous enum) <PACKET_fork_event_feature,
PACKET_vfork_event_feature, PACKET_exec_event_feature>: New
enumeration constants.
(remote_fork_event_p): New function.
(remote_vfork_event_p): New function.
(remote_exec_event_p): New function.
(remote_query_supported): Add new feature queries to qSupported
packet.
(remote_protocol_features): Add table entries for new packets.
(_initialize_remote): Exempt new packets from the requirement
to have 'set remote' commands.
---
gdb/gdbserver/linux-low.c | 37 +++++++++++++++++++++++++++++++++
gdb/gdbserver/lynx-low.c | 3 +++
gdb/gdbserver/server.c | 33 +++++++++++++++++++++++++++++
gdb/gdbserver/target.h | 21 +++++++++++++++++++
gdb/gdbserver/win32-low.c | 3 +++
gdb/nat/linux-ptrace.c | 13 ++++++------
gdb/nat/linux-ptrace.h | 1 +
gdb/remote.c | 53
+++++++++++++++++++++++++++++++++++++++++++++--
8 files changed, 156 insertions(+), 8 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 01f11b7..a13631a 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5133,6 +5133,30 @@ linux_supports_multi_process (void)
return 1;
}
+/* Check if fork events are supported. */
+
+static int
+linux_supports_fork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if vfork events are supported. */
+
+static int
+linux_supports_vfork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if exec events are supported. */
+
+static int
+linux_supports_exec_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
static int
linux_supports_disable_randomization (void)
{
@@ -6044,6 +6068,9 @@ static struct target_ops linux_target_ops = {
linux_async,
linux_start_non_stop,
linux_supports_multi_process,
+ linux_supports_fork_events,
+ linux_supports_vfork_events,
+ linux_supports_exec_events,
#ifdef USE_THREAD_DB
thread_db_handle_monitor_command,
#else
@@ -6119,4 +6146,14 @@ initialize_low (void)
sigaction (SIGCHLD, &sigchld_action, NULL);
initialize_low_arch ();
+
+#if 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);
+#endif
+ linux_test_for_event_reporting ();
}
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 6178e03..dc4ffb2 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -753,6 +753,9 @@ static struct target_ops lynx_target_ops = {
NULL, /* async */
NULL, /* start_non_stop */
NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
+ NULL, /* supports_exec_events */
NULL, /* handle_monitor_command */
};
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 173a22e..71c954f 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -57,6 +57,9 @@ static int exit_requested;
int run_once;
int multi_process;
+int report_fork_events;
+int report_vfork_events;
+int report_exec_events;
int non_stop;
/* Whether we should attempt to disable the operating system's address
@@ -1841,6 +1844,24 @@ handle_query (char *own_buf, int packet_len, int
*new_packet_len_p)
/* GDB supports relocate instruction requests. */
gdb_supports_qRelocInsn = 1;
}
+ if (strcmp (p, "fork-events+") == 0)
+ {
+ /* GDB supports and wants fork events if possible. */
+ if (target_supports_fork_events ())
+ report_fork_events = 1;
+ }
+ if (strcmp (p, "vfork-events+") == 0)
+ {
+ /* GDB supports and wants vfork events if possible. */
+ if (target_supports_vfork_events ())
+ report_vfork_events = 1;
+ }
+ if (strcmp (p, "exec-events+") == 0)
+ {
+ /* GDB supports and wants fork events if possible. */
+ if (target_supports_exec_events ())
+ report_exec_events = 1;
+ }
else
target_process_qsupported (p);
@@ -1891,6 +1912,15 @@ handle_query (char *own_buf, int packet_len, int
*new_packet_len_p)
if (target_supports_multi_process ())
strcat (own_buf, ";multiprocess+");
+ if (target_supports_fork_events ())
+ strcat (own_buf, ";fork-events+");
+
+ if (target_supports_vfork_events ())
+ strcat (own_buf, ";vfork-events+");
+
+ if (target_supports_exec_events ())
+ strcat (own_buf, ";exec-events+");
+
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
@@ -3242,6 +3272,9 @@ captured_main (int argc, char *argv[])
noack_mode = 0;
multi_process = 0;
+ report_fork_events = 0;
+ report_vfork_events = 0;
+ report_exec_events = 0;
/* Be sure we're out of tfind mode. */
current_traceframe = -1;
cont_thread = null_ptid;
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 5e29b7f..a427840 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -262,6 +262,15 @@ struct target_ops
/* Returns true if the target supports multi-process debugging. */
int (*supports_multi_process) (void);
+ /* Returns true if fork events are supported. */
+ int (*supports_fork_events) (void);
+
+ /* Returns true if vfork events are supported. */
+ int (*supports_vfork_events) (void);
+
+ /* Returns true if exec events are supported. */
+ int (*supports_exec_events) (void);
+
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
@@ -390,6 +399,18 @@ void set_target_ops (struct target_ops *);
int kill_inferior (int);
+#define target_supports_fork_events() \
+ (the_target->supports_fork_events ? \
+ (*the_target->supports_fork_events) () : 0)
+
+#define target_supports_vfork_events() \
+ (the_target->supports_vfork_events ? \
+ (*the_target->supports_vfork_events) () : 0)
+
+#define target_supports_exec_events() \
+ (the_target->supports_exec_events ? \
+ (*the_target->supports_exec_events) () : 0)
+
#define detach_inferior(pid) \
(*the_target->detach) (pid)
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index e714933..bf95db4 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1823,6 +1823,9 @@ static struct target_ops win32_target_ops = {
NULL, /* async */
NULL, /* start_non_stop */
NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
+ NULL, /* supports_exec_events */
NULL, /* handle_monitor_command */
NULL, /* core_of_thread */
NULL, /* read_loadmap */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 8bc3f16..2760ddd 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -310,8 +310,8 @@ static void linux_test_for_tracefork (int child_pid);
/* Determine ptrace features available on this target. */
-static void
-linux_check_ptrace_features (void)
+void
+linux_test_for_event_reporting (void)
{
int child_pid, ret, status;
@@ -430,9 +430,10 @@ linux_test_for_tracefork (int child_pid)
/* We got the PID from the grandchild, which means fork
tracing is supported. */
current_ptrace_options |= PTRACE_O_TRACECLONE;
- current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
- |
PTRACE_O_TRACEVFORK
- |
PTRACE_O_TRACEEXEC));
+ current_ptrace_options |= (additional_flags
+ & (PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEEXEC));
/* Do some cleanup and kill the grandchild. */
my_waitpid (second_pid, &second_status, 0);
@@ -457,7 +458,7 @@ linux_enable_event_reporting (pid_t pid)
/* Check if we have initialized the ptrace features for this
target. If not, do it now. */
if (current_ptrace_options == -1)
- linux_check_ptrace_features ();
+ linux_test_for_event_reporting ();
/* Set the options. */
ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 31a77cd..437907e 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -85,6 +85,7 @@ struct buffer;
extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer
*buffer);
extern void linux_ptrace_init_warnings (void);
+extern void linux_test_for_event_reporting (void);
extern void linux_enable_event_reporting (pid_t pid);
extern void linux_disable_event_reporting (pid_t pid);
extern int linux_supports_tracefork (void);
diff --git a/gdb/remote.c b/gdb/remote.c
index 4b9b099..cb40955 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1327,6 +1327,15 @@ enum {
/* Support for qXfer:libraries-svr4:read with a non-empty annex. */
PACKET_augmented_libraries_svr4_read_feature,
+ /* Support for fork events. */
+ PACKET_fork_event_feature,
+
+ /* Support for vfork events. */
+ PACKET_vfork_event_feature,
+
+ /* Support for exec events. */
+ PACKET_exec_event_feature,
+
PACKET_MAX
};
@@ -1432,6 +1441,32 @@ 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
+remote_fork_event_p (struct remote_state *rs)
+{
+ return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
+}
+
+/* Returns true if vfork events are supported. */
+
+static int
+remote_vfork_event_p (struct remote_state *rs)
+{
+ return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
+}
+
+/* Returns true if exec events are supported. */
+
+static int
+remote_exec_event_p (struct remote_state *rs)
+{
+ return packet_support (PACKET_exec_event_feature) == PACKET_ENABLE;
+}
+#endif
+
/* 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;
@@ -4010,7 +4045,13 @@ static const struct protocol_feature
remote_protocol_features[] = {
{ "Qbtrace:off", PACKET_DISABLE, remote_supported_packet,
PACKET_Qbtrace_off },
{ "Qbtrace:bts", PACKET_DISABLE, remote_supported_packet,
PACKET_Qbtrace_bts },
{ "qXfer:btrace:read", PACKET_DISABLE, remote_supported_packet,
- PACKET_qXfer_btrace }
+ PACKET_qXfer_btrace },
+ { "fork-events", PACKET_DISABLE, remote_supported_packet,
+ PACKET_fork_event_feature },
+ { "vfork-events", PACKET_DISABLE, remote_supported_packet,
+ PACKET_vfork_event_feature },
+ { "exec-events", PACKET_DISABLE, remote_supported_packet,
+ PACKET_exec_event_feature }
};
static char *remote_support_xml;
@@ -4084,6 +4125,10 @@ remote_query_supported (void)
q = remote_query_supported_append (q, "qRelocInsn+");
+ q = remote_query_supported_append (q, "fork-events+");
+ q = remote_query_supported_append (q, "vfork-events+");
+ q = remote_query_supported_append (q, "exec-events+");
+
q = reconcat (q, "qSupported:", q, (char *) NULL);
putpkt (q);
@@ -12191,7 +12236,8 @@ Show the maximum size of the address (in bits)
in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace],
"qXfer:btrace", "read-btrace", 0);
- /* Assert that we've registered commands for all packet configs. */
+ /* Assert that we've registered "set remote foo-packet" commands
+ for all packet configs. */
{
int i;
@@ -12210,6 +12256,9 @@ Show the maximum size of the address (in bits)
in a memory packet."), NULL,
case PACKET_DisconnectedTracing_feature:
case PACKET_augmented_libraries_svr4_read_feature:
case PACKET_qCRC:
+ case PACKET_fork_event_feature:
+ case PACKET_vfork_event_feature:
+ case PACKET_exec_event_feature:
/* Additions to this list need to be well justified:
pre-existing packets are OK; new packets are not. */
excepted = 1;
--
1.9.1
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v3] Determine supported extended-remote features
2014-11-13 18:37 ` Breazeal, Don
2014-11-13 18:48 ` Pedro Alves
@ 2015-01-12 22:36 ` Don Breazeal
2015-01-21 21:02 ` Breazeal, Don
1 sibling, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2015-01-12 22:36 UTC (permalink / raw)
To: gdb-patches, palves
> As I mentioned, you'll need to make old GDB against new
> GDBserver work correctly, that is, make gdbserver not send these new
> events if debugging with old GDB, which is not expecting them.
> That can be handled by making GDB send a "fork-events+" in its
> own qSupported packet, so that GDBserver knows its talking to a
> new GDB. See how the "multiprocess+" feature is handled in both
> GDB and GDBserver. For a while, GDB didn't
> broadcast "multiprocess+" when connecting with "target remote".
Hi Pedro,
This patch implements fork-events+ and vfork-events+ in the
qSupported packet as you described.
> One of the reasons I wanted that on "remote" was exactly to
> make it possible to have fork events without caring for
> remote vs extended-remote.
I share this goal.
>
> If there are bigger problems, we can make GDB not broadcast
> the support with plain remote for starters. But I'd be curious
> to hear what the problems are.
My previous response included two patches that "almost worked" for
target remote. Please disregard those. As I went through the remaining
issues, it became clear to me that there are enough underlying changes
needed in the target remote multiprocess support that they deserve a
separate patch. I would like to proceed with the extended-remote support
and address target remote issue in a subsequent patch.
The issues that I ran into with target remote and follow fork
were mostly related to detach and kill in making decisions about
when and where to mourn an inferior and keeping things in sync
between GDB and gdbserver. The issues aren't insurmountable, but
I'm proposing that try to make progress by taking things in smaller
bites: first the extended-remote support, then the underlying target
remote multiprocess additions, then target remote follow fork.
Note that the remote vs. extended-remote issue is moot for this
patch except in the construction of the qSupported packet.
Let me know what you think.
Thanks,
--Don
This patch implements a mechanism for GDB to determine whether fork
events are supported in gdbserver. This is a preparatory patch for
remote fork and exec event support.
Two new RSP packets are defined for fork and vfork events. Other
events like vfork-done may be added later in the patch series if
needed.
These packets are used just like PACKET_multiprocess_feature to denote
whether the corresponding event is supported by inquiring about packet
support in the qSupported packet, then enabling or disabling the packet
based on the qSupported response. Target functions used to query for
support are included for each new packet.
In order for gdbserver to know whether the events are supported at the
point where the qSupported packet arrives, the code in nat/linux-ptrace.c
had to be reorganized. Previously it would test for fork/exec event
support, then enable the events using the pid of the inferior. When the
qSupported packet arrives there may not be an inferior. So the mechanism
was split into two parts: a function that checks whether the events are
supported, called when gdbserver starts up, and another that enables the
events when the inferior stops for the first time.
Another gdbserver change was to add some global variables similar to
multi_process, one per new packet. These are used to control whether
the events are reported. If GDB does not inquire about the event
support in the qSupported packet, then gdbserver will not set these
"report the event" flags. If the flags are not set, the events are
ignored like they were in the past.
There is an #ifdef that have been added temporarily to allow the code for
managing the events to be included in this patch without enabling the
events. See PTRACE_EVENT_SUPPORT. This #ifdef will be removed later in
the patch series as the events are enabled.
Tested on Ubuntu x64, native/remote/extended-remote, and as part of
subsequent patches in the series.
gdb/gdbserver/
2015-01-12 Don Breazeal <donb@codesourcery.com>
* linux-low.c (linux_supports_fork_events): New function.
(linux_supports_vfork_events): New function.
(linux_target_ops): Initialize new structure members.
(initialize_low): Call linux_ptrace_set_additional_flags
and linux_test_for_event_reporting.
* lynx-low.c (lynx_target_ops): Initialize new structure
members.
* server.c (report_fork_events, report_vfork_events,
report_exec_events): New global flags.
(handle_query): Add new features to qSupported packet.
(captured_main): Initialize new global variables.
* target.h (struct target_ops) <supports_fork_events>:
New member.
<supports_vfork_events>: New member.
(target_supports_fork_events): New macro.
(target_supports_vfork_events): New macro.
* win32-low.c (win32_target_ops): Initialize new structure
members.
gdb/
2015-01-12 Don Breazeal <donb@codesourcery.com>
* nat/linux-ptrace.c (linux_test_for_event_reporting): Rename
from linux_check_ptrace_features and make it extern.
(linux_test_for_tracefork): Reformat code.
(linux_enable_event_reporting): Change name of called function
to linux_check_ptrace_features.
* nat/linux-ptrace.h: Declare linux_test_for_event_reporting.
* remote.c (anonymous enum) <PACKET_fork_event_feature,
PACKET_vfork_event_feature>: New enumeration constants.
* remote.c (remote_fork_event_p): New function.
(remote_vfork_event_p): New function.
(remote_query_supported): Add new feature queries to qSupported
packet.
(remote_protocol_features): Add table entries for new packets.
(_initialize_remote): Exempt new packets from the requirement
to have 'set remote' commands.
---
gdb/gdbserver/linux-low.c | 22 ++++++++++++++++++++++
gdb/gdbserver/lynx-low.c | 2 ++
gdb/gdbserver/server.c | 22 ++++++++++++++++++++++
gdb/gdbserver/target.h | 14 ++++++++++++++
gdb/gdbserver/win32-low.c | 2 ++
gdb/nat/linux-ptrace.c | 13 +++++++------
gdb/nat/linux-ptrace.h | 1 +
gdb/remote.c | 41 +++++++++++++++++++++++++++++++++++++++--
8 files changed, 109 insertions(+), 8 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 65f72a2..b1201b3 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -5129,6 +5129,22 @@ linux_supports_multi_process (void)
return 1;
}
+/* Check if fork events are supported. */
+
+static int
+linux_supports_fork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if vfork events are supported. */
+
+static int
+linux_supports_vfork_events (void)
+{
+ return linux_supports_tracefork ();
+}
+
static int
linux_supports_disable_randomization (void)
{
@@ -6040,6 +6056,8 @@ static struct target_ops linux_target_ops = {
linux_async,
linux_start_non_stop,
linux_supports_multi_process,
+ linux_supports_fork_events,
+ linux_supports_vfork_events,
#ifdef USE_THREAD_DB
thread_db_handle_monitor_command,
#else
@@ -6115,4 +6133,8 @@ initialize_low (void)
sigaction (SIGCHLD, &sigchld_action, NULL);
initialize_low_arch ();
+
+ /* Enable any extended ptrace events that are supported. */
+ linux_ptrace_set_additional_flags (0);
+ linux_test_for_event_reporting ();
}
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 3b83669..e3fb788 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -754,6 +754,8 @@ static struct target_ops lynx_target_ops = {
NULL, /* async */
NULL, /* start_non_stop */
NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
NULL, /* handle_monitor_command */
};
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 173a22e..b092020 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -57,6 +57,8 @@ static int exit_requested;
int run_once;
int multi_process;
+int report_fork_events;
+int report_vfork_events;
int non_stop;
/* Whether we should attempt to disable the operating system's address
@@ -1841,6 +1843,18 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
/* GDB supports relocate instruction requests. */
gdb_supports_qRelocInsn = 1;
}
+ if (strcmp (p, "fork-events+") == 0)
+ {
+ /* GDB supports and wants fork events if possible. */
+ if (target_supports_fork_events ())
+ report_fork_events = 1;
+ }
+ if (strcmp (p, "vfork-events+") == 0)
+ {
+ /* GDB supports and wants vfork events if possible. */
+ if (target_supports_vfork_events ())
+ report_vfork_events = 1;
+ }
else
target_process_qsupported (p);
@@ -1891,6 +1905,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
if (target_supports_multi_process ())
strcat (own_buf, ";multiprocess+");
+ if (target_supports_fork_events ())
+ strcat (own_buf, ";fork-events+");
+
+ if (target_supports_vfork_events ())
+ strcat (own_buf, ";vfork-events+");
+
if (target_supports_non_stop ())
strcat (own_buf, ";QNonStop+");
@@ -3242,6 +3262,8 @@ captured_main (int argc, char *argv[])
noack_mode = 0;
multi_process = 0;
+ report_fork_events = 0;
+ report_vfork_events = 0;
/* Be sure we're out of tfind mode. */
current_traceframe = -1;
cont_thread = null_ptid;
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 5e29b7f..e25e5f8 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -262,6 +262,12 @@ struct target_ops
/* Returns true if the target supports multi-process debugging. */
int (*supports_multi_process) (void);
+ /* Returns true if fork events are supported. */
+ int (*supports_fork_events) (void);
+
+ /* Returns true if vfork events are supported. */
+ int (*supports_vfork_events) (void);
+
/* If not NULL, target-specific routine to process monitor command.
Returns 1 if handled, or 0 to perform default processing. */
int (*handle_monitor_command) (char *);
@@ -390,6 +396,14 @@ void set_target_ops (struct target_ops *);
int kill_inferior (int);
+#define target_supports_fork_events() \
+ (the_target->supports_fork_events ? \
+ (*the_target->supports_fork_events) () : 0)
+
+#define target_supports_vfork_events() \
+ (the_target->supports_vfork_events ? \
+ (*the_target->supports_vfork_events) () : 0)
+
#define detach_inferior(pid) \
(*the_target->detach) (pid)
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index e714933..a35ff91 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1823,6 +1823,8 @@ static struct target_ops win32_target_ops = {
NULL, /* async */
NULL, /* start_non_stop */
NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
NULL, /* handle_monitor_command */
NULL, /* core_of_thread */
NULL, /* read_loadmap */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index a0e0c32..542e762 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -311,8 +311,8 @@ static void linux_test_for_exitkill (int child_pid);
/* Determine ptrace features available on this target. */
-static void
-linux_check_ptrace_features (void)
+void
+linux_test_for_event_reporting (void)
{
int child_pid, ret, status;
@@ -433,9 +433,10 @@ linux_test_for_tracefork (int child_pid)
/* We got the PID from the grandchild, which means fork
tracing is supported. */
current_ptrace_options |= PTRACE_O_TRACECLONE;
- current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEEXEC));
+ current_ptrace_options |= (additional_flags
+ & (PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEEXEC));
/* Do some cleanup and kill the grandchild. */
my_waitpid (second_pid, &second_status, 0);
@@ -477,7 +478,7 @@ linux_enable_event_reporting (pid_t pid, int attached)
/* Check if we have initialized the ptrace features for this
target. If not, do it now. */
if (current_ptrace_options == -1)
- linux_check_ptrace_features ();
+ linux_test_for_event_reporting ();
ptrace_options = current_ptrace_options;
if (attached)
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 588d38a..edbacfd 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -90,6 +90,7 @@ struct buffer;
extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
extern void linux_ptrace_init_warnings (void);
+extern void linux_test_for_event_reporting (void);
extern void linux_enable_event_reporting (pid_t pid, int attached);
extern void linux_disable_event_reporting (pid_t pid);
extern int linux_supports_tracefork (void);
diff --git a/gdb/remote.c b/gdb/remote.c
index 4b9b099..891329a 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1327,6 +1327,12 @@ enum {
/* Support for qXfer:libraries-svr4:read with a non-empty annex. */
PACKET_augmented_libraries_svr4_read_feature,
+ /* Support for fork events. */
+ PACKET_fork_event_feature,
+
+ /* Support for vfork events. */
+ PACKET_vfork_event_feature,
+
PACKET_MAX
};
@@ -1432,6 +1438,24 @@ remote_multi_process_p (struct remote_state *rs)
return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
}
+#if PTRACE_FORK_EVENTS
+/* Returns true if fork events are supported. */
+
+static int
+remote_fork_event_p (struct remote_state *rs)
+{
+ return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
+}
+
+/* Returns true if vfork events are supported. */
+
+static int
+remote_vfork_event_p (struct remote_state *rs)
+{
+ return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
+}
+#endif
+
/* 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;
@@ -4010,7 +4034,11 @@ static const struct protocol_feature remote_protocol_features[] = {
{ "Qbtrace:off", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_off },
{ "Qbtrace:bts", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_bts },
{ "qXfer:btrace:read", PACKET_DISABLE, remote_supported_packet,
- PACKET_qXfer_btrace }
+ PACKET_qXfer_btrace },
+ { "fork-events", PACKET_DISABLE, remote_supported_packet,
+ PACKET_fork_event_feature },
+ { "vfork-events", PACKET_DISABLE, remote_supported_packet,
+ PACKET_vfork_event_feature },
};
static char *remote_support_xml;
@@ -4084,6 +4112,12 @@ remote_query_supported (void)
q = remote_query_supported_append (q, "qRelocInsn+");
+ if (rs->extended)
+ {
+ q = remote_query_supported_append (q, "fork-events+");
+ q = remote_query_supported_append (q, "vfork-events+");
+ }
+
q = reconcat (q, "qSupported:", q, (char *) NULL);
putpkt (q);
@@ -12191,7 +12225,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace],
"qXfer:btrace", "read-btrace", 0);
- /* Assert that we've registered commands for all packet configs. */
+ /* Assert that we've registered "set remote foo-packet" commands
+ for all packet configs. */
{
int i;
@@ -12210,6 +12245,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
case PACKET_DisconnectedTracing_feature:
case PACKET_augmented_libraries_svr4_read_feature:
case PACKET_qCRC:
+ case PACKET_fork_event_feature:
+ case PACKET_vfork_event_feature:
/* Additions to this list need to be well justified:
pre-existing packets are OK; new packets are not. */
excepted = 1;
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v3] Determine supported extended-remote features
2015-01-12 22:36 ` Don Breazeal
@ 2015-01-21 21:02 ` Breazeal, Don
0 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2015-01-21 21:02 UTC (permalink / raw)
To: gdb-patches, palves
Ping.
The two patches from this series that are currently under review are:
https://sourceware.org/ml/gdb-patches/2015-01/msg00320.html
https://sourceware.org/ml/gdb-patches/2015-01/msg00321.html
The second of these depends on this:
https://sourceware.org/ml/gdb-patches/2014-10/msg00870.html
Also, I have made some progress on follow fork for target 'remote'.
I'll post that as a separate patch once I have it all working and
cleaned up.
thanks
--Don
On 1/12/2015 2:36 PM, Don Breazeal wrote:
>> As I mentioned, you'll need to make old GDB against new
>> GDBserver work correctly, that is, make gdbserver not send these new
>> events if debugging with old GDB, which is not expecting them.
>> That can be handled by making GDB send a "fork-events+" in its
>> own qSupported packet, so that GDBserver knows its talking to a
>> new GDB. See how the "multiprocess+" feature is handled in both
>> GDB and GDBserver. For a while, GDB didn't
>> broadcast "multiprocess+" when connecting with "target remote".
>
> Hi Pedro,
> This patch implements fork-events+ and vfork-events+ in the
> qSupported packet as you described.
>
>> One of the reasons I wanted that on "remote" was exactly to
>> make it possible to have fork events without caring for
>> remote vs extended-remote.
>
> I share this goal.
>
>>
>> If there are bigger problems, we can make GDB not broadcast
>> the support with plain remote for starters. But I'd be curious
>> to hear what the problems are.
>
> My previous response included two patches that "almost worked" for
> target remote. Please disregard those. As I went through the remaining
> issues, it became clear to me that there are enough underlying changes
> needed in the target remote multiprocess support that they deserve a
> separate patch. I would like to proceed with the extended-remote support
> and address target remote issue in a subsequent patch.
>
> The issues that I ran into with target remote and follow fork
> were mostly related to detach and kill in making decisions about
> when and where to mourn an inferior and keeping things in sync
> between GDB and gdbserver. The issues aren't insurmountable, but
> I'm proposing that try to make progress by taking things in smaller
> bites: first the extended-remote support, then the underlying target
> remote multiprocess additions, then target remote follow fork.
>
> Note that the remote vs. extended-remote issue is moot for this
> patch except in the construction of the qSupported packet.
>
> Let me know what you think.
> Thanks,
> --Don
>
> This patch implements a mechanism for GDB to determine whether fork
> events are supported in gdbserver. This is a preparatory patch for
> remote fork and exec event support.
>
> Two new RSP packets are defined for fork and vfork events. Other
> events like vfork-done may be added later in the patch series if
> needed.
>
> These packets are used just like PACKET_multiprocess_feature to denote
> whether the corresponding event is supported by inquiring about packet
> support in the qSupported packet, then enabling or disabling the packet
> based on the qSupported response. Target functions used to query for
> support are included for each new packet.
>
> In order for gdbserver to know whether the events are supported at the
> point where the qSupported packet arrives, the code in nat/linux-ptrace.c
> had to be reorganized. Previously it would test for fork/exec event
> support, then enable the events using the pid of the inferior. When the
> qSupported packet arrives there may not be an inferior. So the mechanism
> was split into two parts: a function that checks whether the events are
> supported, called when gdbserver starts up, and another that enables the
> events when the inferior stops for the first time.
>
> Another gdbserver change was to add some global variables similar to
> multi_process, one per new packet. These are used to control whether
> the events are reported. If GDB does not inquire about the event
> support in the qSupported packet, then gdbserver will not set these
> "report the event" flags. If the flags are not set, the events are
> ignored like they were in the past.
>
> There is an #ifdef that have been added temporarily to allow the code for
> managing the events to be included in this patch without enabling the
> events. See PTRACE_EVENT_SUPPORT. This #ifdef will be removed later in
> the patch series as the events are enabled.
>
> Tested on Ubuntu x64, native/remote/extended-remote, and as part of
> subsequent patches in the series.
>
> gdb/gdbserver/
> 2015-01-12 Don Breazeal <donb@codesourcery.com>
>
> * linux-low.c (linux_supports_fork_events): New function.
> (linux_supports_vfork_events): New function.
> (linux_target_ops): Initialize new structure members.
> (initialize_low): Call linux_ptrace_set_additional_flags
> and linux_test_for_event_reporting.
> * lynx-low.c (lynx_target_ops): Initialize new structure
> members.
> * server.c (report_fork_events, report_vfork_events,
> report_exec_events): New global flags.
> (handle_query): Add new features to qSupported packet.
> (captured_main): Initialize new global variables.
> * target.h (struct target_ops) <supports_fork_events>:
> New member.
> <supports_vfork_events>: New member.
> (target_supports_fork_events): New macro.
> (target_supports_vfork_events): New macro.
> * win32-low.c (win32_target_ops): Initialize new structure
> members.
>
> gdb/
> 2015-01-12 Don Breazeal <donb@codesourcery.com>
>
> * nat/linux-ptrace.c (linux_test_for_event_reporting): Rename
> from linux_check_ptrace_features and make it extern.
> (linux_test_for_tracefork): Reformat code.
> (linux_enable_event_reporting): Change name of called function
> to linux_check_ptrace_features.
> * nat/linux-ptrace.h: Declare linux_test_for_event_reporting.
> * remote.c (anonymous enum) <PACKET_fork_event_feature,
> PACKET_vfork_event_feature>: New enumeration constants.
> * remote.c (remote_fork_event_p): New function.
> (remote_vfork_event_p): New function.
> (remote_query_supported): Add new feature queries to qSupported
> packet.
> (remote_protocol_features): Add table entries for new packets.
> (_initialize_remote): Exempt new packets from the requirement
> to have 'set remote' commands.
>
> ---
> gdb/gdbserver/linux-low.c | 22 ++++++++++++++++++++++
> gdb/gdbserver/lynx-low.c | 2 ++
> gdb/gdbserver/server.c | 22 ++++++++++++++++++++++
> gdb/gdbserver/target.h | 14 ++++++++++++++
> gdb/gdbserver/win32-low.c | 2 ++
> gdb/nat/linux-ptrace.c | 13 +++++++------
> gdb/nat/linux-ptrace.h | 1 +
> gdb/remote.c | 41 +++++++++++++++++++++++++++++++++++++++--
> 8 files changed, 109 insertions(+), 8 deletions(-)
>
> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index 65f72a2..b1201b3 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -5129,6 +5129,22 @@ linux_supports_multi_process (void)
> return 1;
> }
>
> +/* Check if fork events are supported. */
> +
> +static int
> +linux_supports_fork_events (void)
> +{
> + return linux_supports_tracefork ();
> +}
> +
> +/* Check if vfork events are supported. */
> +
> +static int
> +linux_supports_vfork_events (void)
> +{
> + return linux_supports_tracefork ();
> +}
> +
> static int
> linux_supports_disable_randomization (void)
> {
> @@ -6040,6 +6056,8 @@ static struct target_ops linux_target_ops = {
> linux_async,
> linux_start_non_stop,
> linux_supports_multi_process,
> + linux_supports_fork_events,
> + linux_supports_vfork_events,
> #ifdef USE_THREAD_DB
> thread_db_handle_monitor_command,
> #else
> @@ -6115,4 +6133,8 @@ initialize_low (void)
> sigaction (SIGCHLD, &sigchld_action, NULL);
>
> initialize_low_arch ();
> +
> + /* Enable any extended ptrace events that are supported. */
> + linux_ptrace_set_additional_flags (0);
> + linux_test_for_event_reporting ();
> }
> diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
> index 3b83669..e3fb788 100644
> --- a/gdb/gdbserver/lynx-low.c
> +++ b/gdb/gdbserver/lynx-low.c
> @@ -754,6 +754,8 @@ static struct target_ops lynx_target_ops = {
> NULL, /* async */
> NULL, /* start_non_stop */
> NULL, /* supports_multi_process */
> + NULL, /* supports_fork_events */
> + NULL, /* supports_vfork_events */
> NULL, /* handle_monitor_command */
> };
>
> diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> index 173a22e..b092020 100644
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -57,6 +57,8 @@ static int exit_requested;
> int run_once;
>
> int multi_process;
> +int report_fork_events;
> +int report_vfork_events;
> int non_stop;
>
> /* Whether we should attempt to disable the operating system's address
> @@ -1841,6 +1843,18 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> /* GDB supports relocate instruction requests. */
> gdb_supports_qRelocInsn = 1;
> }
> + if (strcmp (p, "fork-events+") == 0)
> + {
> + /* GDB supports and wants fork events if possible. */
> + if (target_supports_fork_events ())
> + report_fork_events = 1;
> + }
> + if (strcmp (p, "vfork-events+") == 0)
> + {
> + /* GDB supports and wants vfork events if possible. */
> + if (target_supports_vfork_events ())
> + report_vfork_events = 1;
> + }
> else
> target_process_qsupported (p);
>
> @@ -1891,6 +1905,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
> if (target_supports_multi_process ())
> strcat (own_buf, ";multiprocess+");
>
> + if (target_supports_fork_events ())
> + strcat (own_buf, ";fork-events+");
> +
> + if (target_supports_vfork_events ())
> + strcat (own_buf, ";vfork-events+");
> +
> if (target_supports_non_stop ())
> strcat (own_buf, ";QNonStop+");
>
> @@ -3242,6 +3262,8 @@ captured_main (int argc, char *argv[])
>
> noack_mode = 0;
> multi_process = 0;
> + report_fork_events = 0;
> + report_vfork_events = 0;
> /* Be sure we're out of tfind mode. */
> current_traceframe = -1;
> cont_thread = null_ptid;
> diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
> index 5e29b7f..e25e5f8 100644
> --- a/gdb/gdbserver/target.h
> +++ b/gdb/gdbserver/target.h
> @@ -262,6 +262,12 @@ struct target_ops
> /* Returns true if the target supports multi-process debugging. */
> int (*supports_multi_process) (void);
>
> + /* Returns true if fork events are supported. */
> + int (*supports_fork_events) (void);
> +
> + /* Returns true if vfork events are supported. */
> + int (*supports_vfork_events) (void);
> +
> /* If not NULL, target-specific routine to process monitor command.
> Returns 1 if handled, or 0 to perform default processing. */
> int (*handle_monitor_command) (char *);
> @@ -390,6 +396,14 @@ void set_target_ops (struct target_ops *);
>
> int kill_inferior (int);
>
> +#define target_supports_fork_events() \
> + (the_target->supports_fork_events ? \
> + (*the_target->supports_fork_events) () : 0)
> +
> +#define target_supports_vfork_events() \
> + (the_target->supports_vfork_events ? \
> + (*the_target->supports_vfork_events) () : 0)
> +
> #define detach_inferior(pid) \
> (*the_target->detach) (pid)
>
> diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
> index e714933..a35ff91 100644
> --- a/gdb/gdbserver/win32-low.c
> +++ b/gdb/gdbserver/win32-low.c
> @@ -1823,6 +1823,8 @@ static struct target_ops win32_target_ops = {
> NULL, /* async */
> NULL, /* start_non_stop */
> NULL, /* supports_multi_process */
> + NULL, /* supports_fork_events */
> + NULL, /* supports_vfork_events */
> NULL, /* handle_monitor_command */
> NULL, /* core_of_thread */
> NULL, /* read_loadmap */
> diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
> index a0e0c32..542e762 100644
> --- a/gdb/nat/linux-ptrace.c
> +++ b/gdb/nat/linux-ptrace.c
> @@ -311,8 +311,8 @@ static void linux_test_for_exitkill (int child_pid);
>
> /* Determine ptrace features available on this target. */
>
> -static void
> -linux_check_ptrace_features (void)
> +void
> +linux_test_for_event_reporting (void)
> {
> int child_pid, ret, status;
>
> @@ -433,9 +433,10 @@ linux_test_for_tracefork (int child_pid)
> /* We got the PID from the grandchild, which means fork
> tracing is supported. */
> current_ptrace_options |= PTRACE_O_TRACECLONE;
> - current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
> - | PTRACE_O_TRACEVFORK
> - | PTRACE_O_TRACEEXEC));
> + current_ptrace_options |= (additional_flags
> + & (PTRACE_O_TRACEFORK
> + | PTRACE_O_TRACEVFORK
> + | PTRACE_O_TRACEEXEC));
>
> /* Do some cleanup and kill the grandchild. */
> my_waitpid (second_pid, &second_status, 0);
> @@ -477,7 +478,7 @@ linux_enable_event_reporting (pid_t pid, int attached)
> /* Check if we have initialized the ptrace features for this
> target. If not, do it now. */
> if (current_ptrace_options == -1)
> - linux_check_ptrace_features ();
> + linux_test_for_event_reporting ();
>
> ptrace_options = current_ptrace_options;
> if (attached)
> diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
> index 588d38a..edbacfd 100644
> --- a/gdb/nat/linux-ptrace.h
> +++ b/gdb/nat/linux-ptrace.h
> @@ -90,6 +90,7 @@ struct buffer;
>
> extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
> extern void linux_ptrace_init_warnings (void);
> +extern void linux_test_for_event_reporting (void);
> extern void linux_enable_event_reporting (pid_t pid, int attached);
> extern void linux_disable_event_reporting (pid_t pid);
> extern int linux_supports_tracefork (void);
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 4b9b099..891329a 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -1327,6 +1327,12 @@ enum {
> /* Support for qXfer:libraries-svr4:read with a non-empty annex. */
> PACKET_augmented_libraries_svr4_read_feature,
>
> + /* Support for fork events. */
> + PACKET_fork_event_feature,
> +
> + /* Support for vfork events. */
> + PACKET_vfork_event_feature,
> +
> PACKET_MAX
> };
>
> @@ -1432,6 +1438,24 @@ remote_multi_process_p (struct remote_state *rs)
> return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
> }
>
> +#if PTRACE_FORK_EVENTS
> +/* Returns true if fork events are supported. */
> +
> +static int
> +remote_fork_event_p (struct remote_state *rs)
> +{
> + return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
> +}
> +
> +/* Returns true if vfork events are supported. */
> +
> +static int
> +remote_vfork_event_p (struct remote_state *rs)
> +{
> + return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
> +}
> +#endif
> +
> /* 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;
> @@ -4010,7 +4034,11 @@ static const struct protocol_feature remote_protocol_features[] = {
> { "Qbtrace:off", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_off },
> { "Qbtrace:bts", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_bts },
> { "qXfer:btrace:read", PACKET_DISABLE, remote_supported_packet,
> - PACKET_qXfer_btrace }
> + PACKET_qXfer_btrace },
> + { "fork-events", PACKET_DISABLE, remote_supported_packet,
> + PACKET_fork_event_feature },
> + { "vfork-events", PACKET_DISABLE, remote_supported_packet,
> + PACKET_vfork_event_feature },
> };
>
> static char *remote_support_xml;
> @@ -4084,6 +4112,12 @@ remote_query_supported (void)
>
> q = remote_query_supported_append (q, "qRelocInsn+");
>
> + if (rs->extended)
> + {
> + q = remote_query_supported_append (q, "fork-events+");
> + q = remote_query_supported_append (q, "vfork-events+");
> + }
> +
> q = reconcat (q, "qSupported:", q, (char *) NULL);
> putpkt (q);
>
> @@ -12191,7 +12225,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
> add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace],
> "qXfer:btrace", "read-btrace", 0);
>
> - /* Assert that we've registered commands for all packet configs. */
> + /* Assert that we've registered "set remote foo-packet" commands
> + for all packet configs. */
> {
> int i;
>
> @@ -12210,6 +12245,8 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
> case PACKET_DisconnectedTracing_feature:
> case PACKET_augmented_libraries_svr4_read_feature:
> case PACKET_qCRC:
> + case PACKET_fork_event_feature:
> + case PACKET_vfork_event_feature:
> /* Additions to this list need to be well justified:
> pre-existing packets are OK; new packets are not. */
> excepted = 1;
>
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 05/16 v3] GDBserver clone breakpoint list
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (5 preceding siblings ...)
2014-10-31 23:29 ` [PATCH 04/16 v3] Determine supported extended-remote features Don Breazeal
@ 2014-10-31 23:29 ` Don Breazeal
2014-10-31 23:30 ` [PATCH 12/16 v3] Extended-remote follow exec Don Breazeal
` (7 subsequent siblings)
14 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:29 UTC (permalink / raw)
To: gdb-patches
This patch implements gdbserver routines to clone the breakpoint lists of a
process, duplicating them for another process. In gdbserver, each process
maintains its own independent breakpoint list. When a fork call creates a
child, all of the breakpoints currently inserted in the parent process are
also inserted in the child process, but there is nothing to describe them
in the data structures related to the child. The child must have a
breakpoint list describing them so that they can be removed (if detaching)
or recognized (if following). Implementation is a mechanical process of
just cloning the lists in several new functions in gdbserver/mem-break.c.
Tested by building, since none of the new functions are called yet. This
was tested with another patch in the series that implements follow-fork.
Thanks
--Don
gdb/gdbserver/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* gdb/gdbserver/mem-break.c (APPEND_TO_LIST): Define macro.
(clone_agent_expr): New function.
(clone_one_breakpoint): New function.
(clone_all_breakpoints): New function.
* gdb/gdbserver/mem-break.h: Declare new functions.
---
gdb/gdbserver/mem-break.c | 105 +++++++++++++++++++++++++++++++++++++++++++++
gdb/gdbserver/mem-break.h | 6 +++
2 files changed, 111 insertions(+), 0 deletions(-)
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index d7481c4..a91b37f 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -28,6 +28,24 @@ int breakpoint_len;
#define MAX_BREAKPOINT_LEN 8
+/* Helper macro used in loops that append multiple items to a singly-linked
+ list instead of inserting items at the head of the list, as, say, in the
+ breakpoint lists. LISTPP is a pointer to the pointer that is the head of
+ the new list. ITEMP is a pointer to the item to be added to the list.
+ TAILP must be defined to be the same type as ITEMP, and initialized to
+ NULL. */
+
+#define APPEND_TO_LIST(listpp, itemp, tailp) \
+ do \
+ { \
+ if ((tailp) == NULL) \
+ *(listpp) = (itemp); \
+ else \
+ (tailp)->next = (itemp); \
+ (tailp) = (itemp); \
+ } \
+ while (0)
+
/* GDB will never try to install multiple breakpoints at the same
address. However, we can see GDB requesting to insert a breakpoint
at an address is had already inserted one previously in a few
@@ -1879,3 +1897,90 @@ free_all_breakpoints (struct process_info *proc)
while (proc->breakpoints)
delete_breakpoint_1 (proc, proc->breakpoints);
}
+
+/* Clone an agent expression. */
+
+static struct agent_expr *
+clone_agent_expr (const struct agent_expr *src_ax)
+{
+ struct agent_expr *ax;
+
+ ax = xcalloc (1, sizeof (*ax));
+ ax->length = src_ax->length;
+ ax->bytes = xcalloc (ax->length, 1);
+ memcpy (ax->bytes, src_ax->bytes, ax->length);
+ return ax;
+}
+
+/* Deep-copy the contents of one breakpoint to another. */
+
+static struct breakpoint *
+clone_one_breakpoint (const struct breakpoint *src)
+{
+ struct breakpoint *dest;
+ struct raw_breakpoint *dest_raw;
+ struct point_cond_list *current_cond;
+ struct point_cond_list *new_cond;
+ struct point_cond_list *cond_tail = NULL;
+ struct point_command_list *current_cmd;
+ struct point_command_list *new_cmd;
+ struct point_command_list *cmd_tail = NULL;
+
+ /* Clone the raw breakpoint. */
+ dest_raw = xcalloc (1, sizeof (*dest_raw));
+ dest_raw->raw_type = src->raw->raw_type;
+ dest_raw->refcount = src->raw->refcount;
+ dest_raw->pc = src->raw->pc;
+ dest_raw->size = src->raw->size;
+ memcpy (dest_raw->old_data, src->raw->old_data, MAX_BREAKPOINT_LEN);
+ dest_raw->inserted = src->raw->inserted;
+
+ /* Clone the high-level breakpoint. */
+ dest = xcalloc (1, sizeof (*dest));
+ dest->type = src->type;
+ dest->raw = dest_raw;
+ dest->handler = src->handler;
+
+ /* Clone the condition list. */
+ for (current_cond = src->cond_list; current_cond != NULL;
+ current_cond = current_cond->next)
+ {
+ new_cond = xcalloc (1, sizeof (*new_cond));
+ new_cond->cond = clone_agent_expr (current_cond->cond);
+ APPEND_TO_LIST (&dest->cond_list, new_cond, cond_tail);
+ }
+
+ /* Clone the command list. */
+ for (current_cmd = src->command_list; current_cmd != NULL;
+ current_cmd = current_cmd->next)
+ {
+ new_cmd = xcalloc (1, sizeof (*new_cmd));
+ new_cmd->cmd = clone_agent_expr (current_cmd->cmd);
+ new_cmd->persistence = current_cmd->persistence;
+ APPEND_TO_LIST (&dest->command_list, new_cmd, cmd_tail);
+ }
+
+ return dest;
+}
+
+/* Create a new breakpoint list NEW_LIST that is a copy of the
+ list starting at SRC_LIST. Create the corresponding new
+ raw_breakpoint list NEW_RAW_LIST as well. */
+
+void
+clone_all_breakpoints (struct breakpoint **new_list,
+ struct raw_breakpoint **new_raw_list,
+ const struct breakpoint *src_list)
+{
+ const struct breakpoint *bp;
+ struct breakpoint *new_bkpt;
+ struct breakpoint *bkpt_tail = NULL;
+ struct raw_breakpoint *raw_bkpt_tail = NULL;
+
+ for (bp = src_list; bp != NULL; bp = bp->next)
+ {
+ new_bkpt = clone_one_breakpoint (bp);
+ APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);
+ APPEND_TO_LIST (new_raw_list, new_bkpt->raw, raw_bkpt_tail);
+ }
+}
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index c84c688..3a5cb6d 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -243,4 +243,10 @@ int insert_memory_breakpoint (struct raw_breakpoint *bp);
int remove_memory_breakpoint (struct raw_breakpoint *bp);
+/* Create a new breakpoint list NEW_BKPT_LIST that is a copy of SRC. */
+
+void clone_all_breakpoints (struct breakpoint **new_bkpt_list,
+ struct raw_breakpoint **new_raw_bkpt_list,
+ const struct breakpoint *src);
+
#endif /* MEM_BREAK_H */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 12/16 v3] Extended-remote follow exec
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (6 preceding siblings ...)
2014-10-31 23:29 ` [PATCH 05/16 v3] GDBserver clone breakpoint list Don Breazeal
@ 2014-10-31 23:30 ` Don Breazeal
2014-10-31 23:30 ` [PATCH 09/16 v3] Extended-remote fork catchpoints Don Breazeal
` (6 subsequent siblings)
14 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:30 UTC (permalink / raw)
To: gdb-patches
This patch implements support for exec events in gdbserver on linux, in
multiprocess mode (target extended-remote). Follow-exec-mode and rerun
behave as expected. Catchpoints for exec are not yet implemented since it
will be easier to implement catchpoints for fork, vfork, and exec all at
the same time.
TESTING
---------
The patch was tested on GNU/Linux x86_64 for native, remote, and
extended-remote targets. The test results for native-gdbserver were
unchanged. Thirteen tests that used to fail for native-extended-gdbserver
on Linux pass with this patch, and the non-ldr-exc-*.exp tests all pass in
all-stop mode and extended-remote.
One caveat: when an exec is detected, gdbserver emits a couple of warnings:
gdbserver: unexpected r_debug version 0
gdbserver: unexpected r_debug version 0
However, debugging of shared libraries that are loaded by the exec'd
program works just fine. These messages are caused by gdbserver making
an attempt to initialize the solib hook before the r_debug structure has
been initialized. A fix is provided in patch 14 of this patch series.
IMPLEMENTATION
----------------
Support for exec events in single-threaded programs was a fairly
straightforward replication of the implementation in native GDB:
1) Enable exec events via ptrace options.
2) Add support for handling the exec events to the handle_extended_wait and
linux_wait_for_event_filtered. Detect the exec event, then find and save
the pathname of the executable file being exec'd.
3) Implement an additional "stop reason", "exec", in the RSP stop reply
packet "T".
Existing GDB code takes care of handling the exec event on the host side
without modification.
Support for exec events in multi-threaded programs required some additional
work that required a couple of significant changes to existing code. In a
nutshell, the changes are to:
4) Use the PTRACE_EVENT_EXIT extended event to handle thread exit, while
not exposing any change in exit handling to the user.
5) Recognize when the exec'ing thread has vanished (become the thread group
leader) in send_sigstop. Native GDB does this differently.
The rationale for #4 is discussed in the previous patch of this series.
Rationale for item 5: when a non-leader thread exec's, all the other
threads are terminated and the exec'ing thread changes its thread id to
that of the old leader (the process id) as part of the exec. There is no
event reported for the "exit" of the exec'ing thread; it appears to have
vanished.
Determining that the exec'ing thread has "vanished" in native GDB is done
by calling waitpid(PID), and if it returns ECHILD it means that the thread
is gone. We don't want to use waitpid(PID) in gdbserver, based on the
discussion in
https://www.sourceware.org/ml/gdb-patches/2014-02/msg00828.html.
An alternative is to send a signal to each thread and look for an ESRCH (No
such process) error. In all-stop mode this can be done in the normal
course of events, since when gdbserver reports an exec event it stops all
the other threads with a SIGSTOP. In non-stop mode, when an exec event has
been detected, we can call stop_all_lwps/unstop_all_lwps to accomplish the
same thing.
Thanks
--Don
gdb/gdbserver/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* linux-low.c (linux_pid_to_exec_file): New function.
(handle_extended_wait): Handle PTRACE_EVENT_EXEC.
(check_zombie_leaders): Update comment.
(linux_low_filter_event): Handle exec event.
(linux_wait_for_event_filtered): Update comment.
(extended_event_reported): Add exec event to the list of extended
events.
(send_sigstop): Check return value from kill_lwp and delete lwp
on 'No such process' error.
(initialize_low): Add PTRACE_O_TRACEEXIT.
* remote-utils.c (prepare_resume_reply): New stop reason "exec" for
'T' Stop Reply Packet.
gdb/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* linux-nat.c (linux_handle_extended_wait): Change call to
linux_child_pid_to_exec_file to call to linux_proc_pid_to_exec_file.
(linux_child_pid_to_exec_file): Deleted function, moved to
common code.
(linux_target_install_ops): Change linux_child_pid_to_exec_file to
linux_proc_pid_to_exec_file.
* nat/linux-procfs.c (linux_proc_pid_to_exec_file): New function.
* nat/linux-procfs.h (linux_proc_pid_to_exec_file): Declare.
* nat/linux-ptrace.c (linux_test_for_traceexit): Add
PTRACE_O_TRACEEXEC.
* remote.c (remote_parse_stop_reply): New stop reason "exec" for
'T' Stop Reply Packet.
---
gdb/gdbserver/linux-low.c | 99 +++++++++++++++++++++++++++++++++++++++--
gdb/gdbserver/remote-utils.c | 19 ++++++++
gdb/linux-nat.c | 30 ++++--------
gdb/nat/linux-procfs.c | 17 +++++++
gdb/nat/linux-procfs.h | 4 ++
gdb/nat/linux-ptrace.c | 9 +++-
gdb/remote.c | 25 ++++++++++-
7 files changed, 174 insertions(+), 29 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index d5f6fe0..e7a8bfb 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -366,6 +366,14 @@ linux_add_process (int pid, int attached)
return proc;
}
+/* Find the main executable for the process with PID. */
+
+static
+char *linux_pid_to_exec_file (struct target_ops *self, int pid)
+{
+ return linux_proc_pid_to_exec_file (pid);
+}
+
/* Handle a GNU/Linux extended wait response. If we see a clone
event, we need to add the new lwp EVENT_CHILD to our list (and not
report the trap to higher layers). This function returns non-zero
@@ -562,6 +570,21 @@ handle_extended_wait (struct lwp_info *event_child, int *wstatp)
ptrace (PTRACE_CONT, lwpid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
return ret;
}
+ else if (event == PTRACE_EVENT_EXEC)
+ {
+ if (debug_threads)
+ {
+ debug_printf ("HEW: Got exec event from LWP %ld\n",
+ lwpid_of (event_thr));
+ }
+
+ event_child->waitstatus.kind = TARGET_WAITKIND_EXECD;
+ event_child->waitstatus.value.execd_pathname
+ = xstrdup (linux_pid_to_exec_file (NULL, lwpid_of (event_thr)));
+
+ /* Report the event. */
+ return 0;
+ }
internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
}
@@ -2047,6 +2070,56 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int *wstatp)
child = find_lwp_pid (pid_to_ptid (lwpid));
+ /* Check for stop events reported by a process we didn't already
+ know about - anything not already in our LWP list.
+
+ If we're expecting to receive stopped processes after
+ fork, vfork, and clone events, then we'll just add the
+ new one to our list and go back to waiting for the event
+ to be reported - the stopped process might be returned
+ from waitpid before or after the event is.
+
+ But note the case of a non-leader thread exec'ing after the
+ leader having exited, and gone from our lists. On an exec,
+ the Linux kernel destroys all other threads (except the execing
+ one) in the thread group, and resets the execing thread's tid
+ to the tgid. No exit notification is sent for the execing
+ thread -- from the ptracer's perspective, it appears as though
+ the execing thread just vanishes. When they are available, we
+ use exit events (PTRACE_EVENT_EXIT) to detect thread exit
+ reliably. As soon as all other threads (if any) are reaped or
+ have reported their PTRACE_EVENT_EXIT events, the execing
+ thread changes it's tid to the tgid, and the previous (zombie)
+ leader vanishes, giving place to the "new" leader. The lwp
+ entry for the previous leader is deleted when we handle its
+ exit event, and we re-add the new one here. */
+
+ if (WIFSTOPPED (wstat) && (child == NULL) && (WSTOPSIG (wstat) == SIGTRAP)
+ && (linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC))
+ {
+ ptid_t child_ptid;
+
+ /* A multi-thread exec after we had seen the leader exiting. */
+ if (debug_threads)
+ debug_printf ("LLW: Re-adding thread group leader LWP %d.\n", lwpid);
+
+ child_ptid = ptid_build (lwpid, lwpid, 0);
+ child = add_lwp (child_ptid);
+ child->stopped = 1;
+ current_thread = child->thread;
+
+ if (non_stop && stopping_threads == NOT_STOPPING_THREADS)
+ {
+ /* Make sure we delete the lwp entry for the exec'ing thread,
+ which will have vanished. We do this by sending a signal
+ to all the other threads in the lwp list, deleting any
+ that are not found. Note that in all-stop mode this will
+ happen before reporting the event. */
+ stop_all_lwps (0, child);
+ unstop_all_lwps (0, child);
+ }
+ }
+
/* If we didn't find a process, one of two things presumably happened:
- A process we started and then detached from has exited. Ignore it.
- A process we are controlling has forked and the new child's stop
@@ -2383,8 +2456,7 @@ linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
- When a non-leader thread execs, that thread just vanishes
without reporting an exit (so we'd hang if we waited for it
explicitly in that case). The exec event is reported to
- the TGID pid (although we don't currently enable exec
- events). */
+ the TGID pid. */
errno = 0;
ret = my_waitpid (-1, wstatp, options | WNOHANG);
@@ -2786,7 +2858,8 @@ extended_event_reported (const struct target_waitstatus *waitstatus)
return (waitstatus->kind == TARGET_WAITKIND_FORKED
|| waitstatus->kind == TARGET_WAITKIND_VFORKED
- || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
+ || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE
+ || waitstatus->kind == TARGET_WAITKIND_EXECD);
}
/* Wait for process, returns status. */
@@ -3413,6 +3486,7 @@ static void
send_sigstop (struct lwp_info *lwp)
{
int pid;
+ int ret;
pid = lwpid_of (get_lwp_thread (lwp));
@@ -3430,7 +3504,21 @@ send_sigstop (struct lwp_info *lwp)
debug_printf ("Sending sigstop to lwp %d\n", pid);
lwp->stop_expected = 1;
- kill_lwp (pid, SIGSTOP);
+ errno = 0;
+ ret = kill_lwp (pid, SIGSTOP);
+ if (ret == -1 && errno == ESRCH)
+ {
+ /* If the kill fails with "No such process", on GNU/Linux we know
+ that the LWP has vanished - it is not a zombie, it is gone.
+ This is due to a thread other than the thread group leader
+ calling exec. See comments in linux_low_filter_event regarding
+ PTRACE_EVENT_EXEC. */
+ delete_lwp (lwp);
+ set_desired_thread (0);
+
+ if (debug_threads)
+ debug_printf ("send_sigstop: lwp %d has vanished\n", pid);
+ }
}
static int
@@ -6530,6 +6618,7 @@ initialize_low (void)
linux_ptrace_set_requested_options (PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORK
| PTRACE_O_TRACEVFORKDONE
- | PTRACE_O_TRACEEXIT);
+ | PTRACE_O_TRACEEXIT
+ | PTRACE_O_TRACEEXEC);
linux_ptrace_check_options ();
}
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index b4a523a..35784b5 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1107,6 +1107,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
case TARGET_WAITKIND_STOPPED:
case TARGET_WAITKIND_FORKED:
case TARGET_WAITKIND_VFORKED:
+ case TARGET_WAITKIND_EXECD:
{
struct thread_info *saved_thread;
const char **regp;
@@ -1123,6 +1124,24 @@ prepare_resume_reply (char *buf, ptid_t ptid,
ptid_get_pid (status->value.related_pid),
ptid_get_lwp (status->value.related_pid));
}
+ else if ((status->kind == TARGET_WAITKIND_EXECD) && multi_process)
+ {
+ enum gdb_signal signal = GDB_SIGNAL_TRAP;
+ const char *event = "exec";
+ char hexified_pathname[PATH_MAX];
+
+ sprintf (buf, "T%02x%s:", signal, event);
+ buf += strlen (buf);
+
+ /* Encode pathname to hexified format. */
+ bin2hex ((const gdb_byte *) status->value.execd_pathname,
+ hexified_pathname, strlen(status->value.execd_pathname));
+
+ sprintf (buf, "%s;", hexified_pathname);
+ xfree (status->value.execd_pathname);
+ status->value.execd_pathname = NULL;
+ buf += strlen (buf);
+ }
else
sprintf (buf, "T%02x", status->value.sig);
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index f0487e9..e81a560 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -686,7 +686,6 @@ linux_nat_pass_signals (struct target_ops *self,
/* Prototypes for local functions. */
static int stop_wait_callback (struct lwp_info *lp, void *data);
static int linux_thread_alive (ptid_t ptid);
-static char *linux_child_pid_to_exec_file (struct target_ops *self, int pid);
\f
@@ -1793,6 +1792,14 @@ linux_handle_syscall_trap (struct lwp_info *lp, int stopping)
return 1;
}
+/* Find the main executable for the process with PID. */
+
+static
+char *linux_pid_to_exec_file (struct target_ops *self, int pid)
+{
+ return linux_proc_pid_to_exec_file (pid);
+}
+
/* 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). This function returns non-zero if the
@@ -2015,7 +2022,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
ourstatus->kind = TARGET_WAITKIND_EXECD;
ourstatus->value.execd_pathname
- = xstrdup (linux_child_pid_to_exec_file (NULL, pid));
+ = xstrdup (linux_pid_to_exec_file (NULL, pid));
return 0;
}
@@ -3862,23 +3869,6 @@ linux_nat_thread_name (struct target_ops *self, struct thread_info *thr)
return result;
}
-/* Accepts an integer PID; Returns a string representing a file that
- can be opened to get the symbols for the child process. */
-
-static char *
-linux_child_pid_to_exec_file (struct target_ops *self, int pid)
-{
- static char buf[PATH_MAX];
- char name[PATH_MAX];
-
- xsnprintf (name, PATH_MAX, "/proc/%d/exe", pid);
- memset (buf, 0, PATH_MAX);
- if (readlink (name, buf, PATH_MAX - 1) <= 0)
- strcpy (buf, name);
-
- return buf;
-}
-
/* Implement the to_xfer_partial interface for memory reads using the /proc
filesystem. Because we can use a single read() call for /proc, this
can be much more efficient than banging away at PTRACE_PEEKTEXT,
@@ -4271,7 +4261,7 @@ linux_target_install_ops (struct target_ops *t)
t->to_insert_exec_catchpoint = linux_child_insert_exec_catchpoint;
t->to_remove_exec_catchpoint = linux_child_remove_exec_catchpoint;
t->to_set_syscall_catchpoint = linux_child_set_syscall_catchpoint;
- t->to_pid_to_exec_file = linux_child_pid_to_exec_file;
+ t->to_pid_to_exec_file = linux_pid_to_exec_file;
t->to_post_startup_inferior = linux_child_post_startup_inferior;
t->to_post_attach = linux_child_post_attach;
t->to_follow_fork = linux_child_follow_fork;
diff --git a/gdb/nat/linux-procfs.c b/gdb/nat/linux-procfs.c
index 30797da..bdbe10b 100644
--- a/gdb/nat/linux-procfs.c
+++ b/gdb/nat/linux-procfs.c
@@ -113,3 +113,20 @@ linux_proc_pid_is_zombie (pid_t pid)
{
return linux_proc_pid_has_state (pid, "Z (zombie)");
}
+
+/* Accepts an integer PID; Returns a string representing a file that
+ can be opened to get the symbols for the child process. */
+
+char *
+linux_proc_pid_to_exec_file (int pid)
+{
+ static char buf[PATH_MAX];
+ char name[PATH_MAX];
+
+ xsnprintf (name, PATH_MAX, "/proc/%d/exe", pid);
+ memset (buf, 0, PATH_MAX);
+ if (readlink (name, buf, PATH_MAX - 1) <= 0)
+ strcpy (buf, name);
+
+ return buf;
+}
diff --git a/gdb/nat/linux-procfs.h b/gdb/nat/linux-procfs.h
index d13fff7..e6ee9a8 100644
--- a/gdb/nat/linux-procfs.h
+++ b/gdb/nat/linux-procfs.h
@@ -40,4 +40,8 @@ extern int linux_proc_pid_is_stopped (pid_t pid);
extern int linux_proc_pid_is_zombie (pid_t pid);
+/* Return pathname of exec file for process with PID. */
+
+extern char *linux_proc_pid_to_exec_file (int pid);
+
#endif /* COMMON_LINUX_PROCFS_H */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index e8fd58c..741b4f1 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -488,6 +488,7 @@ linux_test_for_traceexit (int child_pid)
if (ret != 0)
return;
+
/* We don't know for sure that the feature is available; old
versions of PTRACE_SETOPTIONS ignored unknown options. So
see if the process exit will generate a PTRACE_EVENT_EXIT.
@@ -508,8 +509,12 @@ linux_test_for_traceexit (int child_pid)
if (ret == child_pid && WIFSTOPPED (status)
&& linux_ptrace_get_extended_event (status) == PTRACE_EVENT_EXIT)
{
- /* PTRACE_O_TRACEEXIT is supported. */
- available_ptrace_options |= PTRACE_O_TRACEEXIT;
+ /* PTRACE_O_TRACEEXIT is supported.
+
+ We use exit events to implement support for exec events.
+ Because exit events are supported, we can assume exec events
+ are also supported, so we add them as well. */
+ available_ptrace_options |= PTRACE_O_TRACEEXIT | PTRACE_O_TRACEEXEC;
}
}
}
diff --git a/gdb/remote.c b/gdb/remote.c
index de715f5..7409860 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5614,11 +5614,13 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
pnum and set p1 to point to the character following it.
Otherwise p1 points to p. */
- /* If this packet is an awatch packet, don't parse the 'a'
- as a register number. */
+ /* If this packet has a stop reason string that starts
+ with a character that could be a hex digit, don't parse
+ it as a register number. */
if (strncmp (p, "awatch", strlen("awatch")) != 0
&& strncmp (p, "core", strlen ("core") != 0)
+ && strncmp (p, "exec", strlen ("exec") != 0)
&& strncmp (p, "fork", strlen ("fork") != 0))
{
/* Read the ``P'' register number. */
@@ -5691,6 +5693,25 @@ Packet: '%s'\n"),
event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
p = p_temp;
}
+ else if (strncmp (p, "exec", p1 - p) == 0)
+ {
+ ULONGEST pid;
+ char pathname[PATH_MAX];
+
+ p = unpack_varlen_hex (++p1, &pid);
+
+ /* Save the pathname for event reporting and for
+ the next run command. */
+ hex2bin (p1, (gdb_byte *) pathname, (p - p1)/2);
+ /* Add the null terminator. */
+ pathname[(p - p1)/2] = '\0';
+ /* This is freed during event handling. */
+ event->ws.value.execd_pathname = xstrdup (pathname);
+ event->ws.kind = TARGET_WAITKIND_EXECD;
+ /* Save the pathname for the next run command. */
+ xfree (remote_exec_file);
+ remote_exec_file = xstrdup (pathname);
+ }
else
{
/* Silently skip unknown optional info. */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 09/16 v3] Extended-remote fork catchpoints
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (7 preceding siblings ...)
2014-10-31 23:30 ` [PATCH 12/16 v3] Extended-remote follow exec Don Breazeal
@ 2014-10-31 23:30 ` Don Breazeal
2014-10-31 23:30 ` [PATCH 10/16 v3] Extended-remote fork event documentation Don Breazeal
` (5 subsequent siblings)
14 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:30 UTC (permalink / raw)
To: gdb-patches
This patch implements catchpoints for fork events on extended-remote linux
targets.
Implementation appeared to be straightforward, requiring four new functions
in remote.c to implement insert/remove of fork/vfork catchpoints. These
functions are essentially stubs that just return 0 ('success'). If the
fork events were being reported, then catchpoints were set and hit.
However, there are some extra issues that arise with catchpoints.
1) Thread creation reporting -- fork catchpoints are hit before the
follow_fork has been completed. In the native implementation, the new
process is not 'reported' until after the follow is done. It doesn't
show up in the inferiors list or the threads list. However, in
gdbserver, an 'info threads' will retrieve the new thread info from the
target and add it to GDB's data structures. Because of this premature
report, things on the GDB side eventually get very confused.
So in gdbserver, in server.c:handle_qxfer_threads_worker, we check
'last_status' and if it shows a FORK event, we know that we are in an
unfollowed fork and we do not report the new (forked) thread to GDB.
2) Kill process before fork is followed -- on the native side in
linux-nat.c:linux_nat_kill, there is some code to handle the case where
a fork has occurred but follow_fork hasn't been called yet. It does
this by using the last status to determine if a follow is pending, and
if it is, to kill the child task. I implemented similar code in
linux-low.c:linux_kill.
3) One of the tests related to fork catchpoints,
gdb.threads/fork-thread-pending.exp, depended on the threads being
reported in a specific order. GDBserver reported the threads in a
different order, that is, 'info threads' showed the same threads, but in
a different order. The test used a hard-coded thread number to find a
threads that (a) was not the main thread (blocked in pthread_join), and
(b) was not the forking thread (stopped in fork).
I implemented a new proc, find_unforked_thread, that uses a pretty
brute-force method of finding a thread. I considered just hard-coding
another number (the native case used thread 2, which was the forking
thread in the remote case), but that didn't seem future-proof.
Suggestions on how to do this better would be welcome.
Tested on x64 Ubuntu Lucid, native, remote, extended-remote. Tested the
case of killing the forking process before the fork has been followed
manually. It wasn't clear to me how to check that a process had actually
been killed from a dejagnu test.
Thanks
--Don
gdb/gdbserver/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* linux-low.c (handle_extended_wait): Fix braces in multi-line if.
(linux_kill): Kill forked child when between fork and follow_fork.
* server.c (handle_qxfer_threads_worker): Skip forked child thread
when between fork and follow_fork.
(get_last_target_status): New function.
* server.h (get_last_target_status): Declare new function.
gdb/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* remote.c (extended_remote_insert_fork_catchpoint): New function.
(extended_remote_remove_fork_catchpoint): New function.
(extended_remote_insert_vfork_catchpoint): New function.
(extended_remote_remove_vfork_catchpoint): New function.
(init_extended_remote_ops): Initialize target vector with
new fork catchpoint functions.
gdb/testsuite/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* gdb.threads/fork-thread-pending.exp (find_unforked_thread):
New proc.
---
gdb/gdbserver/linux-low.c | 28 +++++++++--
gdb/gdbserver/server.c | 18 +++++++
gdb/gdbserver/server.h | 2 +
gdb/remote.c | 54 +++++++++++++++++++-
gdb/testsuite/gdb.threads/fork-thread-pending.exp | 23 ++++++++-
5 files changed, 117 insertions(+), 8 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 8fc831c..369bfbe 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -412,10 +412,12 @@ 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
@@ -1063,6 +1065,24 @@ linux_kill (int pid)
{
struct process_info *process;
struct lwp_info *lwp;
+ struct target_waitstatus last;
+ ptid_t last_ptid;
+
+ /* If we're stopped while forking and we haven't followed yet,
+ kill the child task. We need to do this first because the
+ parent will be sleeping if this is a vfork. */
+
+ get_last_target_status (&last_ptid, &last);
+
+ if (last.kind == TARGET_WAITKIND_FORKED
+ || last.kind == TARGET_WAITKIND_VFORKED)
+ {
+ lwp = find_lwp_pid (last.value.related_pid);
+ gdb_assert (lwp != NULL);
+ kill_wait_lwp (lwp);
+ process = find_process_pid (ptid_get_pid (last.value.related_pid));
+ the_target->mourn (process);
+ }
process = find_process_pid (pid);
if (process == NULL)
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index b1720de..38aa88f 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1311,6 +1311,15 @@ handle_qxfer_threads_worker (struct inferior_list_entry *inf, void *arg)
int core = target_core_of_thread (ptid);
char core_s[21];
+ /* Skip new threads created as the result of a fork if we are not done
+ handling that fork event. We won't know whether to tell GDB about
+ the new thread until we are done following the fork. */
+ if ((last_status.kind == TARGET_WAITKIND_FORKED
+ || last_status.kind == TARGET_WAITKIND_VFORKED)
+ && (ptid_get_pid (last_status.value.related_pid)
+ == ptid_get_pid (ptid)))
+ return;
+
write_ptid (ptid_s, ptid);
if (core != -1)
@@ -4050,3 +4059,12 @@ using_extended_protocol (void)
{
return extended_protocol;
}
+
+/* Retrieve the last waitstatus reported to GDB. */
+
+void
+get_last_target_status (ptid_t *ptid, struct target_waitstatus *last)
+{
+ *ptid = last_ptid;
+ *last = last_status;
+}
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index f4f192c..02fdbc2 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -110,6 +110,8 @@ typedef int gdb_fildes_t;
extern int handle_serial_event (int err, gdb_client_data client_data);
extern int handle_target_event (int err, gdb_client_data client_data);
extern int using_extended_protocol (void);
+extern void get_last_target_status (ptid_t *ptid,
+ struct target_waitstatus *last);
#include "remote-utils.h"
diff --git a/gdb/remote.c b/gdb/remote.c
index a210f30..de715f5 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1453,9 +1453,6 @@ extended_remote_fork_event_p (struct remote_state *rs)
return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
}
-/* Ifdef out the two functions below until they are needed for vfork
- and exec catchpoints. */
-#if 0
/* Returns true if vfork events are supported. */
static int
@@ -1464,6 +1461,9 @@ extended_remote_vfork_event_p (struct remote_state *rs)
return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
}
+/* Ifdef out the function below until it is needed for
+ exec catchpoints. */
+#if 0
/* Returns true if exec events are supported. */
static int
@@ -1500,6 +1500,46 @@ extended_remote_follow_fork (struct target_ops *target, int follow_child,
return 0;
}
+/* Insert fork catchpoint target routine. If fork events are enabled
+ then return success, nothing more to do. */
+
+static int
+extended_remote_insert_fork_catchpoint (struct target_ops *ops, int pid)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ return !extended_remote_fork_event_p (rs);
+}
+
+/* Remove fork catchpoint target routine. Nothing to do, just
+ return success. */
+
+static int
+extended_remote_remove_fork_catchpoint (struct target_ops *ops, int pid)
+{
+ return 0;
+}
+
+/* Insert vfork catchpoint target routine. If vfork events are enabled
+ then return success, nothing more to do. */
+
+static int
+extended_remote_insert_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ return !extended_remote_vfork_event_p (rs);
+}
+
+/* Remove vfork catchpoint target routine. Nothing to do, just
+ return success. */
+
+static int
+extended_remote_remove_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+ 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;
@@ -11707,6 +11747,14 @@ init_extended_remote_ops (void)
Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_open = extended_remote_open;
extended_remote_ops.to_create_inferior = extended_remote_create_inferior;
+ extended_remote_ops.to_insert_fork_catchpoint
+ = extended_remote_insert_fork_catchpoint;
+ extended_remote_ops.to_remove_fork_catchpoint
+ = extended_remote_remove_fork_catchpoint;
+ extended_remote_ops.to_insert_vfork_catchpoint
+ = extended_remote_insert_vfork_catchpoint;
+ extended_remote_ops.to_remove_vfork_catchpoint
+ = extended_remote_remove_vfork_catchpoint;
extended_remote_ops.to_follow_fork = extended_remote_follow_fork;
extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
extended_remote_ops.to_detach = extended_remote_detach;
diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.exp b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
index 57e45c9..e8c4830 100644
--- a/gdb/testsuite/gdb.threads/fork-thread-pending.exp
+++ b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
@@ -31,6 +31,26 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
+# Find a thread that did not fork and is not the main thread and
+# return its thread number. We can't just hard-code the thread
+# number since we have no guarantee as to the ordering of the threads
+# in gdb. We know that the main thread is in pthread_join and the
+# forking thread is in fork, so we use this rather ungainly regexp
+# to capture an entry from 'info threads' that doesn't show one of
+# those routines, then extract the thread number.
+
+proc find_unforked_thread { } {
+ gdb_test_multiple "info threads" "find unforked thread" {
+ -re "(\[^\r]*Thread\[^\r]* in \[^fp]\[^ot]\[^rh]\[^kr]\[^e]\[^a]\[^d]\[^_]\[^j]\[^\r]*\r\n)" {
+ regexp "(\[ ]*)(\[0-9]*)(\[ ]*Thread\[^\r]*\r\n)" $expect_out(0,string) ignore lead_spc threadnum rest
+ }
+ timeout {
+ set threadnum -1
+ }
+ }
+ return $threadnum
+}
+
clean_restart ${binfile}
if ![runto_main] then {
@@ -46,7 +66,8 @@ gdb_test "continue" "Catchpoint.*" "1, get to the fork event"
gdb_test "info threads" " Thread .* Thread .* Thread .* Thread .*" "1, multiple threads found"
-gdb_test "thread 2" ".*" "1, switched away from event thread"
+set threadnum [find_unforked_thread]
+gdb_test "thread $threadnum" ".*" "1, switched away from event thread to thread $threadnum"
gdb_test "continue" "Not resuming.*" "1, refused to resume"
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 10/16 v3] Extended-remote fork event documentation
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (8 preceding siblings ...)
2014-10-31 23:30 ` [PATCH 09/16 v3] Extended-remote fork catchpoints Don Breazeal
@ 2014-10-31 23:30 ` Don Breazeal
2014-10-31 23:30 ` [PATCH 13/16 v3] Extended-remote exec catchpoints Don Breazeal
` (4 subsequent siblings)
14 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:30 UTC (permalink / raw)
To: gdb-patches
This patch adds documentation for fork events on extended-remote linux
targets in the gdb manual and the NEWS file.
[This patch was approved by Eli:
https://sourceware.org/ml/gdb-patches/2014-08/msg00157.html]
Thanks
--Don
gdb/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* gdb/NEWS: Describe new gdbserver support for fork events and new
fork event support in RSP.
gdb/doc
2014-08-20 Don Breazeal <donb@codesourcery.com>
* gdb/doc/gdb.texinfo (Forks): Describe fork debugging support in
gdbserver.
(Set Catchpoints): Describe fork catchpoint support in gdbserver.
(Stop Reply Packets): Describe fork event support in RSP.
---
gdb/NEWS | 15 +++++++++++++++
gdb/doc/gdb.texinfo | 33 +++++++++++++++++++++++++++++----
2 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 01cdc36..1c07663 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -59,6 +59,21 @@ SGI Irix-6.x mips-*-irix6*
VAX running (4.2 - 4.3 Reno) BSD vax-*-bsd*
VAX running Ultrix vax-*-ultrix*
+* Remote fork events
+
+ GDBserver extended-remote Linux targets now support fork events.
+ This enables follow-fork-mode, detach-on-fork, catch fork, and
+ catch vfork for those targets with Linux kernels 2.5.60 and later.
+
+* New remote packets
+
+T Stop Reply Packet's reason
+ The T stop reply packet supports new stop reasons 'fork', 'vfork'
+ and 'vforkdone'. The 'fork' and 'vfork' reasons signify that the
+ specified inferior has executed a fork or vfork. The 'vforkdone'
+ reason signifies that a vforked child process has executed either
+ an exec or an exit.
+
*** Changes in GDB 7.8
* New command line options
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 15c2908..9787623 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3107,6 +3107,9 @@ create additional processes using the @code{fork} or @code{vfork} functions.
Currently, the only platforms with this feature are HP-UX (11.x and later
only?) and @sc{gnu}/Linux (kernel version 2.5.60 and later).
+The fork debugging commands are supported in both native mode and when
+connected to @code{gdbserver} using @kbd{target extended-remote}.
+
By default, when a program forks, @value{GDBN} will continue to debug
the parent process and the child process will run unimpeded.
@@ -4387,13 +4390,15 @@ Again, in this case @value{GDBN} would not be able to display syscall's names.
@item fork
@kindex catch fork
-A call to @code{fork}. This is currently only available for HP-UX
-and @sc{gnu}/Linux.
+A call to @code{fork}. This is currently only available for native
+HP-UX and @sc{gnu}/Linux, and when connected to @code{gdbserver} running
+on @sc{gnu}/Linux with @kbd{target extended-remote}.
@item vfork
@kindex catch vfork
-A call to @code{vfork}. This is currently only available for HP-UX
-and @sc{gnu}/Linux.
+A call to @code{vfork}. This is currently only available for native
+HP-UX and @sc{gnu}/Linux, and when connected to @code{gdbserver} running
+on @sc{gnu}/Linux with @kbd{target extended-remote}.
@item load @r{[}regexp@r{]}
@itemx unload @r{[}regexp@r{]}
@@ -34700,6 +34705,26 @@ The packet indicates that the loaded libraries have changed.
@value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new
list of loaded libraries. The @var{r} part is ignored.
+@cindex fork events, remote reply
+@item fork
+The packet indicates that @code{fork} was called, and @var{r}
+is the ptid of the new child process. This packet is only
+applicable to targets that support fork events.
+
+@cindex vfork events, remote reply
+@item vfork
+The packet indicates that @code{vfork} was called, and @var{r}
+is the ptid of the new child process. This packet is only
+applicable to targets that support vfork events.
+
+@cindex vforkdone events, remote reply
+@item vforkdone
+The packet indicates that a child process created by a vfork
+has either called @code{exec} or terminated, so that the
+address spaces of the parent and child process are no longer
+shared. The @var{r} part is ignored. This packet is only
+applicable to targets that support vforkdone events.
+
@cindex replay log events, remote reply
@item replaylog
The packet indicates that the target cannot continue replaying
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 13/16 v3] Extended-remote exec catchpoints
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (9 preceding siblings ...)
2014-10-31 23:30 ` [PATCH 10/16 v3] Extended-remote fork event documentation Don Breazeal
@ 2014-10-31 23:30 ` Don Breazeal
2014-10-31 23:30 ` [PATCH 11/16 v3] Extended-remote Linux exit events Don Breazeal
` (3 subsequent siblings)
14 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:30 UTC (permalink / raw)
To: gdb-patches
This patch implements exec catchpoints for extended-remote Linux targets.
The implementation follows the same appraoch used in patch 9 of this
series for fork catchpoints, implementing extended_remote target
routines for insert/remove. Existing host-side code and the support
for exec events introduced in patch 12 of this series takes care of
the rest.
Tested native, remote, extended-remote on x64 Ubuntu.
Thanks
--Don
gdb/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* remote.c (extended_remote_insert_exec_catchpoint): New function.
(extended_remote_remove_exec_catchpoint): New function.
(_initialize_remote): Initialize target vector with new exec
catchpoint functions.
---
gdb/remote.c | 28 ++++++++++++++++++++++++----
1 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/gdb/remote.c b/gdb/remote.c
index 7409860..1973433 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1461,9 +1461,6 @@ extended_remote_vfork_event_p (struct remote_state *rs)
return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
}
-/* Ifdef out the function below until it is needed for
- exec catchpoints. */
-#if 0
/* Returns true if exec events are supported. */
static int
@@ -1471,7 +1468,6 @@ extended_remote_exec_event_p (struct remote_state *rs)
{
return packet_support (PACKET_exec_event_feature) == PACKET_ENABLE;
}
-#endif
/* Target follow-fork function for extended-remote targets. */
@@ -1540,6 +1536,26 @@ extended_remote_remove_vfork_catchpoint (struct target_ops *ops, int pid)
return 0;
}
+/* Insert exec catchpoint target routine. If exec events are
+ enabled, just return success. */
+
+static int
+extended_remote_insert_exec_catchpoint (struct target_ops *ops, int pid)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ return !extended_remote_exec_event_p (rs);
+}
+
+/* Remove exec catchpoint target routine. Nothing to do, just
+ return success. */
+
+static int
+extended_remote_remove_exec_catchpoint (struct target_ops *ops, int pid)
+{
+ 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;
@@ -11777,6 +11793,10 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_remove_vfork_catchpoint
= extended_remote_remove_vfork_catchpoint;
extended_remote_ops.to_follow_fork = extended_remote_follow_fork;
+ extended_remote_ops.to_insert_exec_catchpoint
+ = extended_remote_insert_exec_catchpoint;
+ extended_remote_ops.to_remove_exec_catchpoint
+ = extended_remote_remove_exec_catchpoint;
extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
extended_remote_ops.to_detach = extended_remote_detach;
extended_remote_ops.to_attach = extended_remote_attach;
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 11/16 v3] Extended-remote Linux exit events
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (10 preceding siblings ...)
2014-10-31 23:30 ` [PATCH 13/16 v3] Extended-remote exec catchpoints Don Breazeal
@ 2014-10-31 23:30 ` Don Breazeal
2014-11-13 19:18 ` Pedro Alves
2014-10-31 23:31 ` [PATCH 14/16 v3] Suppress spurious warnings with extended-remote follow exec Don Breazeal
` (2 subsequent siblings)
14 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:30 UTC (permalink / raw)
To: gdb-patches
This patch implements support for the extended ptrace event
PTRACE_EVENT_EXIT on Linux. This is a preparatory patch for exec event
support.
The use of this event is entirely internal to gdbserver; these events are
not reported to GDB or the user. When this event occurs, if the reporting
thread is not the last thread in a process, its lwp entry is simply
deleted, since this is what happens in the absence of exit events. If it
is the last thread of a process, the wait status is set to the actual wait
status of the thread, retrieved by PTRACE_O_GETMESSAGE, instead of the
status that indicates the extended event, and the existing mechanisms for
handling thread exit proceed as usual.
The only purpose in using the exit events instead of the existing wait
mechanisms is to ensure that the exit of a thread group leader is detected
reliably when a non-leader thread calls exec.
This was tested on x64 Ubuntu using extended-remote.
Rationale for Using Exit Events
--------------------------------
In brief, there is a race condition in the current implementation that can
leave a dangling entry in the lwp list (an entry that doesn't have a
corresponding actual lwp). In this case gdbserver will hang waiting for
the non-existent lwp to stop. Using the exit events eliminates this race
condition.
The same race may exist in the native implementation, since the two
implementations are similar, but I haven't verified that. It may be
difficult to concoct a test case that demonstrates the race since the
window is so small.
Now for the long answer: in my testing I ran into a race condition in
check_zombie_leaders, which detects when a thread group leader has exited
and other threads still exist. On the Linux kernel, ptrace/waitpid don't
allow reaping the leader thread until all other threads in the group are
reaped. When the leader exits, it goes zombie, but waitpid will not return
an exit status until the other threads are gone. When a non-leader thread
calls exec, all other non-leader threads are destroyed, the leader becomes
a zombie, and once the "other" threads have been reaped, the execing thread
takes over the leader's pid (tgid) and appears to vanish. In order to
handle this situation in the current implementation, check_zombie_leaders
polls the process state in /proc and deletes thread group leaders that are
in a zombie state. The replacement is added to the lwp list when the exec
event is reported.
See https://sourceware.org/ml/gdb-patches/2011-10/msg00704.html for a more
detailed explanation of how this works.
Here is the relevant part of check_zombie_leaders:
if (leader_lp != NULL
/* Check if there are other threads in the group, as we may
have raced with the inferior simply exiting. */
&& !last_thread_of_process_p (leader_pid)
&& linux_proc_pid_is_zombie (leader_pid))
{
/* ...large informative comment block... */
delete_lwp (leader_lp);
The race occurred when there were two threads in the program, and the
non-leader thread called exec. In this case the leader thread passed
through a very brief zombie state before being replaced by the exec'ing
thread as the thread group leader. This state transition was asynchronous,
with no dependency on anything gdbserver did. Because there were no other
threads, there were no thread exit events, and thus there was no
synchronization with the leader passing through the zombie state and the
exec completing. If there had been more threads, the leader would remain
in the zombie state until they were waited for. In the two-thread case,
sometimes the leader exit was detected and sometimes it wasn't. (Recall
that check_zombie_leaders is polling the state, via
linux_proc_pid_is_zombie. The race is between the leader thread passing
through the zombie state and check_zombie_leaders testing for zombie
state.) If leader exit wasn't detected, gdbserver would end up with a
dangling lwp entry that didn't correspond to any real lwp, and would hang
waiting for that lwp to stop. Using PTRACE_EVENT_EXIT guarantees that the
leader exit will be detected.
Note that check_zombie_leaders works just fine for the scenarios where the
leader thread exits and the other threads continue to run, with no exec
calls. It is required for systems that don't support the extended ptrace
events.
The sequence of events resulting in the race condition was this:
1) In the program, a CLONE event for a new thread occurs.
2) In the program, both threads are resumed once gdbserver has
completed the new thread processing.
3) In gdbserver, the function linux_wait_for_event_filtered loops until
waitpid returns "no more events" for the SIGCHLD generated by the
CLONE event. Then linux_wait_for_event_filtered calls
check_zombie_leaders.
4) In the program, the new thread is doing the exec. During the exec
the leader thread will pass through a transitory zombie state. If
there were more than two threads, the leader thread would remain a
zombie until all the non-leader, non-exec'ing threads were reaped by
gdbserver. Since there are no such threads to reap, the leader just
becomes a zombie and is replaced by the exec'ing thread on-the-fly.
(Note that it appears that the leader thread is a zombie just for a
very brief instant.)
5) In gdbserver, check_zombie_leaders checks whether an lwp entry
corresponds to a zombie leader thread, and if so, deletes it. Here
is the race: in (4) above, the leader may or may not be in the
transitory zombie state. In the case where a zombie isn't detected,
delete_lwp is not called.
6) In gdbserver, an EXEC event is detected and processed. When it gets
ready to report the event to GDB, it calls stop_all_lwps, which sends
a SIGSTOP to each lwp in the list and the waits until all the lwps in
the list have reported a stop event. If the zombie leader wasn't
detected and processed in step (5), gdbserver blocks forever in
linux_wait_for_event_filtered, waiting for the undeleted lwp to be
stopped, which will never happen.
Thanks
--Don
gdb/gdbserver/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* linux-low.c (handle_extended_wait): Support PTRACE_EVENT_EXIT.
(linux_low_filter_event): Change wstat argument to a pointer,
making it an input/output argument.
(linux_wait_for_event_filtered): Handle wstat point argument in
call to linux_low_filter_event.
(initialize_low): Add PTRACE_O_TRACEEXIT.
gdb/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* nat/linux-ptrace.c (linux_ptrace_check_options): Call
linux_test_for_traceexit.
(linux_test_for_traceexit): New function.
---
gdb/gdbserver/linux-low.c | 68 +++++++++++++++++++++++++++++++++++++-------
gdb/nat/linux-ptrace.c | 50 +++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+), 11 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 369bfbe..d5f6fe0 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -222,6 +222,7 @@ 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);
+static int num_lwps (int pid);
/* True if the low target can hardware single-step. Such targets
don't need a BREAKPOINT_REINSERT_ADDR callback. */
@@ -366,12 +367,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 return 0 so as
- not to report the trap to higher layers). */
+ event, we need to add the new lwp EVENT_CHILD to our list (and not
+ report the trap to higher layers). This function returns non-zero
+ if the event should be ignored and we should wait again. If an
+ exit event occurred in the last thread of a process, the wait
+ status in WSTATP will be updated with the exit status of the
+ process. */
static int
-handle_extended_wait (struct lwp_info *event_child, int wstat)
+handle_extended_wait (struct lwp_info *event_child, int *wstatp)
{
+ int wstat = *wstatp;
int event = linux_ptrace_get_extended_event (wstat);
struct thread_info *event_thr = get_lwp_thread (event_child);
struct lwp_info *new_lwp;
@@ -524,6 +530,38 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
event_child->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
return 0;
}
+ else if (event == PTRACE_EVENT_EXIT)
+ {
+ unsigned long exit_status;
+ unsigned long lwpid = lwpid_of (event_thr);
+ int ret;
+
+ if (debug_threads)
+ debug_printf ("HEW: Got exit event from LWP %ld\n", lwpid );
+
+ ptrace (PTRACE_GETEVENTMSG, lwpid, (PTRACE_TYPE_ARG3) 0, &exit_status);
+
+ if (num_lwps (pid_of (event_thr)) > 1)
+ {
+ /* If there is at least one more LWP, then the program still
+ exists and the exit should not be reported to GDB. */
+ delete_lwp (event_child);
+ ret = 1;
+ }
+ else
+ {
+ /* Set the exit status to the actual exit status, so normal
+ WIFEXITED/WIFSIGNALLED processing and reporting for the
+ last lwp in the process can proceed from here. */
+ *wstatp = exit_status;
+ ret = 0;
+ }
+
+ /* Resume the thread so that it actually exits. Subsequent exit
+ events for LWPs that were deleted above will be ignored. */
+ ptrace (PTRACE_CONT, lwpid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
+ return ret;
+ }
internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
}
@@ -2001,8 +2039,9 @@ lp_status_maybe_breakpoint (struct lwp_info *lp)
NULL otherwise. */
static struct lwp_info *
-linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
+linux_low_filter_event (ptid_t filter_ptid, int lwpid, int *wstatp)
{
+ int wstat = *wstatp;
struct lwp_info *child;
struct thread_info *thread;
@@ -2054,7 +2093,8 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
architecture being defined already (so that CHILD has a valid
regcache), and on LAST_STATUS being set (to check for SIGTRAP or
not). */
- if (WIFSTOPPED (wstat))
+ if (WIFSTOPPED (wstat)
+ && (linux_ptrace_get_extended_event (wstat) != PTRACE_EVENT_EXIT))
{
if (debug_threads
&& the_low_target.get_pc != NULL)
@@ -2090,7 +2130,8 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
changes the debug registers meanwhile, we have the cached data we
can rely on. */
- if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP)
+ if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
+ && !linux_is_extended_waitstatus (wstat))
{
if (the_low_target.stopped_by_watchpoint == NULL)
{
@@ -2128,10 +2169,15 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
&& linux_is_extended_waitstatus (wstat))
{
- if (handle_extended_wait (child, wstat))
+ if (handle_extended_wait (child, &wstat))
return NULL;
else
- return child;
+ {
+ /* Update caller's wstat in case handle_extended_wait
+ changed it. */
+ *wstatp = wstat;
+ return child;
+ }
}
if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGSTOP
@@ -2354,8 +2400,7 @@ linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
(long) ret, status_to_str (*wstatp));
}
- event_child = linux_low_filter_event (filter_ptid,
- ret, *wstatp);
+ event_child = linux_low_filter_event (filter_ptid, ret, wstatp);
if (event_child != NULL)
{
/* We got an event to report to the core. */
@@ -6484,6 +6529,7 @@ initialize_low (void)
/* Enable extended events. */
linux_ptrace_set_requested_options (PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEVFORKDONE);
+ | PTRACE_O_TRACEVFORKDONE
+ | PTRACE_O_TRACEEXIT);
linux_ptrace_check_options ();
}
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 5f3f123..e8fd58c 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -313,6 +313,7 @@ linux_child_function (gdb_byte *child_stack)
static void linux_test_for_tracesysgood (int child_pid);
static void linux_test_for_tracefork (int child_pid);
+static void linux_test_for_traceexit (int child_pid);
/* Determine ptrace features available on this target. */
@@ -350,6 +351,8 @@ linux_ptrace_check_options (void)
linux_test_for_tracefork (child_pid);
+ linux_test_for_traceexit (child_pid);
+
/* Clean things up and kill any pending children. */
do
{
@@ -464,6 +467,53 @@ linux_test_for_tracefork (int child_pid)
"(%d, status 0x%x)"), ret, status);
}
+/* Determine if PTRACE_O_TRACEEXIT can be used to follow exit
+ events. */
+
+static void
+linux_test_for_traceexit (int child_pid)
+{
+ int ret;
+ int status;
+ int i;
+
+ /* Check if the caller wants PTRACE_O_TRACEEXIT. */
+ if ((requested_ptrace_options & PTRACE_O_TRACEEXIT) == 0)
+ return;
+
+ /* Test for PTRACE_O_TRACEEXIT. First try to set the option.
+ If this fails, we know for sure that it is not supported. */
+ ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
+ (PTRACE_TYPE_ARG4) PTRACE_O_TRACEEXIT);
+ if (ret != 0)
+ return;
+
+ /* We don't know for sure that the feature is available; old
+ versions of PTRACE_SETOPTIONS ignored unknown options. So
+ see if the process exit will generate a PTRACE_EVENT_EXIT.
+
+ We try twice (at most) to handle the case where the grandchild
+ process exits, causing a SIGCHLD to be delivered to the child
+ process, stopping it before exit. */
+ for (i = 0; i < 2; i++)
+ {
+ ret = ptrace (PTRACE_CONT, child_pid, (PTRACE_TYPE_ARG3) 0,
+ (PTRACE_TYPE_ARG4) 0);
+ if (ret != 0)
+ warning (_("linux_test_for_traceexit: failed to resume child"));
+
+ ret = my_waitpid (child_pid, &status, 0);
+
+ /* Check if we received an exit event notification. */
+ if (ret == child_pid && WIFSTOPPED (status)
+ && linux_ptrace_get_extended_event (status) == PTRACE_EVENT_EXIT)
+ {
+ /* PTRACE_O_TRACEEXIT is supported. */
+ available_ptrace_options |= PTRACE_O_TRACEEXIT;
+ }
+ }
+}
+
/* Enable reporting of supported ptrace events. If
USE_AVAILABLE_OPTIONS is false, then exclude the events
specified in available_ptrace_options. If PID is non-zero,
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 11/16 v3] Extended-remote Linux exit events
2014-10-31 23:30 ` [PATCH 11/16 v3] Extended-remote Linux exit events Don Breazeal
@ 2014-11-13 19:18 ` Pedro Alves
0 siblings, 0 replies; 110+ messages in thread
From: Pedro Alves @ 2014-11-13 19:18 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 10/31/2014 11:28 PM, Don Breazeal wrote:
>
> Now for the long answer: in my testing I ran into a race condition in
> check_zombie_leaders, which detects when a thread group leader has exited
> and other threads still exist. On the Linux kernel, ptrace/waitpid don't
> allow reaping the leader thread until all other threads in the group are
> reaped. When the leader exits, it goes zombie, but waitpid will not return
> an exit status until the other threads are gone. When a non-leader thread
> calls exec, all other non-leader threads are destroyed, the leader becomes
> a zombie, and once the "other" threads have been reaped, the execing thread
> takes over the leader's pid (tgid) and appears to vanish. In order to
> handle this situation in the current implementation, check_zombie_leaders
> polls the process state in /proc and deletes thread group leaders that are
> in a zombie state. The replacement is added to the lwp list when the exec
> event is reported.
>
> See https://sourceware.org/ml/gdb-patches/2011-10/msg00704.html for a more
> detailed explanation of how this works.
>
> Here is the relevant part of check_zombie_leaders:
>
> if (leader_lp != NULL
> /* Check if there are other threads in the group, as we may
> have raced with the inferior simply exiting. */
> && !last_thread_of_process_p (leader_pid)
> && linux_proc_pid_is_zombie (leader_pid))
> {
> /* ...large informative comment block... */
> delete_lwp (leader_lp);
>
> The race occurred when there were two threads in the program, and the
> non-leader thread called exec. In this case the leader thread passed
> through a very brief zombie state before being replaced by the exec'ing
> thread as the thread group leader. This state transition was asynchronous,
> with no dependency on anything gdbserver did. Because there were no other
> threads, there were no thread exit events, and thus there was no
> synchronization with the leader passing through the zombie state and the
> exec completing. If there had been more threads, the leader would remain
> in the zombie state until they were waited for. In the two-thread case,
> sometimes the leader exit was detected and sometimes it wasn't. (Recall
> that check_zombie_leaders is polling the state, via
> linux_proc_pid_is_zombie. The race is between the leader thread passing
> through the zombie state and check_zombie_leaders testing for zombie
> state.) If leader exit wasn't detected, gdbserver would end up with a
> dangling lwp entry that didn't correspond to any real lwp, and would hang
> waiting for that lwp to stop. Using PTRACE_EVENT_EXIT guarantees that the
> leader exit will be detected.
>
> Note that check_zombie_leaders works just fine for the scenarios where the
> leader thread exits and the other threads continue to run, with no exec
> calls. It is required for systems that don't support the extended ptrace
> events.
>
> The sequence of events resulting in the race condition was this:
>
> 1) In the program, a CLONE event for a new thread occurs.
>
> 2) In the program, both threads are resumed once gdbserver has
> completed the new thread processing.
>
> 3) In gdbserver, the function linux_wait_for_event_filtered loops until
> waitpid returns "no more events" for the SIGCHLD generated by the
> CLONE event. Then linux_wait_for_event_filtered calls
> check_zombie_leaders.
>
> 4) In the program, the new thread is doing the exec. During the exec
> the leader thread will pass through a transitory zombie state. If
> there were more than two threads, the leader thread would remain a
> zombie until all the non-leader, non-exec'ing threads were reaped by
> gdbserver. Since there are no such threads to reap, the leader just
> becomes a zombie and is replaced by the exec'ing thread on-the-fly.
> (Note that it appears that the leader thread is a zombie just for a
> very brief instant.)
>
> 5) In gdbserver, check_zombie_leaders checks whether an lwp entry
> corresponds to a zombie leader thread, and if so, deletes it. Here
> is the race: in (4) above, the leader may or may not be in the
> transitory zombie state. In the case where a zombie isn't detected,
> delete_lwp is not called.
>
> 6) In gdbserver, an EXEC event is detected and processed. When it gets
> ready to report the event to GDB, it calls stop_all_lwps, which sends
> a SIGSTOP to each lwp in the list and the waits until all the lwps in
> the list have reported a stop event. If the zombie leader wasn't
> detected and processed in step (5), gdbserver blocks forever in
> linux_wait_for_event_filtered, waiting for the undeleted lwp to be
> stopped, which will never happen.
This is easy to see with thread-execl.exp, if we revert the PTRACE_EVENT_EXIT
parts:
linux_low_filter_event: pc is 0x3b37209237
pc is 0x3b37209237
stop pc is 0x3b37209237
HEW: Got exec event from LWP 2721
Hit a non-gdbserver trap event.
>>>> entering stop_all_lwps
stop_all_lwps (stop, except=none)
Sending sigstop to lwp 23192
wait_for_sigstop: pulling events
my_waitpid (-1, 0x40000001)
my_waitpid (-1, 0x80000001): status(0), -1
LWFE: waitpid(-1, ...) returned -1, No child processes
leader_pid=2721, leader_lp!=NULL=1, num_lwps=2, zombie=0
sigsuspend'ing
*** hang ***
But I'm having trouble convincing myself that we do need
the PTRACE_EVENT_EXIT handling.
This seems to work for me just as well. The fix is in
making check_zombie_leaders ignore stopped threads.
@@ -1629,7 +1597,7 @@ check_zombie_leaders (void)
leader_pid, leader_lp!= NULL, num_lwps (leader_pid),
linux_proc_pid_is_zombie (leader_pid));
- if (leader_lp != NULL
+ if (leader_lp != NULL && !leader_lp->stopped
But I'm in a hurry to leave now. Maybe I'm missing something else.
From ab07ee8afc3c2358cd6b23474655fde087e2b36e Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Thu, 13 Nov 2014 17:53:43 +0000
Subject: [PATCH] revert PTRACE_EVENT_EXIT bits
---
gdb/gdbserver/linux-low.c | 56 +++--------------------------------------------
1 file changed, 3 insertions(+), 53 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index f308333..316b302 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -538,38 +538,6 @@ handle_extended_wait (struct lwp_info *event_child, int *wstatp)
event_child->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
return 0;
}
- else if (event == PTRACE_EVENT_EXIT)
- {
- unsigned long exit_status;
- unsigned long lwpid = lwpid_of (event_thr);
- int ret;
-
- if (debug_threads)
- debug_printf ("HEW: Got exit event from LWP %ld\n", lwpid );
-
- ptrace (PTRACE_GETEVENTMSG, lwpid, (PTRACE_TYPE_ARG3) 0, &exit_status);
-
- if (num_lwps (pid_of (event_thr)) > 1)
- {
- /* If there is at least one more LWP, then the program still
- exists and the exit should not be reported to GDB. */
- delete_lwp (event_child);
- ret = 1;
- }
- else
- {
- /* Set the exit status to the actual exit status, so normal
- WIFEXITED/WIFSIGNALLED processing and reporting for the
- last lwp in the process can proceed from here. */
- *wstatp = exit_status;
- ret = 0;
- }
-
- /* Resume the thread so that it actually exits. Subsequent exit
- events for LWPs that were deleted above will be ignored. */
- ptrace (PTRACE_CONT, lwpid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
- return ret;
- }
else if (event == PTRACE_EVENT_EXEC)
{
if (debug_threads)
@@ -1629,7 +1597,7 @@ check_zombie_leaders (void)
leader_pid, leader_lp!= NULL, num_lwps (leader_pid),
linux_proc_pid_is_zombie (leader_pid));
- if (leader_lp != NULL
+ if (leader_lp != NULL && !leader_lp->stopped
/* Check if there are other threads in the group, as we may
have raced with the inferior simply exiting. */
&& !last_thread_of_process_p (leader_pid)
@@ -2107,17 +2075,6 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int *wstatp)
child = add_lwp (child_ptid);
child->stopped = 1;
current_thread = child->thread;
-
- if (non_stop && stopping_threads == NOT_STOPPING_THREADS)
- {
- /* Make sure we delete the lwp entry for the exec'ing thread,
- which will have vanished. We do this by sending a signal
- to all the other threads in the lwp list, deleting any
- that are not found. Note that in all-stop mode this will
- happen before reporting the event. */
- stop_all_lwps (0, child);
- unstop_all_lwps (0, child);
- }
}
/* If we didn't find a process, one of two things presumably happened:
@@ -2166,8 +2123,7 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int *wstatp)
architecture being defined already (so that CHILD has a valid
regcache), and on LAST_STATUS being set (to check for SIGTRAP or
not). */
- if (WIFSTOPPED (wstat)
- && (linux_ptrace_get_extended_event (wstat) != PTRACE_EVENT_EXIT))
+ if (WIFSTOPPED (wstat))
{
if (debug_threads
&& the_low_target.get_pc != NULL)
@@ -3486,8 +3442,6 @@ send_sigstop (struct lwp_info *lwp)
calling exec. See comments in linux_low_filter_event regarding
PTRACE_EVENT_EXEC. */
delete_lwp (lwp);
- set_desired_thread (0);
-
if (debug_threads)
debug_printf ("send_sigstop: lwp %d has vanished\n", pid);
}
@@ -5565,10 +5519,7 @@ linux_supports_vfork_events (void)
static int
linux_supports_exec_events (void)
{
- /* Check for PTRACE_O_TRACEEXIT, since our implementation of follow
- exec depends on this option, which was implemented in a later
- kernel version than PTRACE_O_TRACEFORK et al. */
- return linux_supports_traceexit ();
+ return linux_supports_tracefork ();
}
static int
@@ -6588,7 +6539,6 @@ initialize_low (void)
linux_ptrace_set_requested_options (PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORK
| PTRACE_O_TRACEVFORKDONE
- | PTRACE_O_TRACEEXIT
| PTRACE_O_TRACEEXEC);
linux_ptrace_check_options ();
}
--
1.9.3
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 14/16 v3] Suppress spurious warnings with extended-remote follow exec
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (11 preceding siblings ...)
2014-10-31 23:30 ` [PATCH 11/16 v3] Extended-remote Linux exit events Don Breazeal
@ 2014-10-31 23:31 ` Don Breazeal
2014-10-31 23:31 ` [PATCH 15/16 v3] Extended-remote exec event documentation Don Breazeal
2014-10-31 23:31 ` [PATCH 16/16 v3] Non-stop follow exec tests Don Breazeal
14 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:31 UTC (permalink / raw)
To: gdb-patches
This patch eliminates some spurious gdbserver warnings that occur when
following an exec on an extended-remote Linux target.
When gdbserver on Linux sets up the hook for shared library load
detection, an initial step is to read the version number from the
r_debug structure in memory. In the current implementation, if the
version number is not equal to one, a warning was printed by gdbserver.
However, the number can be zero if the structure has not been
initialized yet.
To suppress the warnings the error check was changed so that if
the version number is not equal to one the function silently returns
-1. Subsequent calls to the routine find an initialized r_debug
structure.
Tested on x64 Ubuntu, both GDB tests and manual testing of following
an exec, then debugging a shared library loaded by the exec'd program
to ensure that there were no warnings and that debugging shared libs
was not adversely affected.
Thanks
--Don
gdb/gdbserver/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* linux-low.c (linux_qxfer_libraries_svr4): Change
handling of r_debug version mismatch.
---
gdb/gdbserver/linux-low.c | 11 ++++++++---
1 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index e7a8bfb..9e3c64a 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -6287,10 +6287,15 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
{
if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
(unsigned char *) &r_version,
- sizeof (r_version)) != 0
- || r_version != 1)
+ sizeof (r_version)) != 0)
+ warning ("error reading r_debug version from memory");
+ else if (r_version != 1)
{
- warning ("unexpected r_debug version %d", r_version);
+ /* If the version is incorrect, it probably means that
+ r_debug hasn't been initialized yet. Just silently
+ return an error. We will read it in a subsequent pass
+ through here. */
+ return -1;
}
else if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
&lm_addr, ptr_size) != 0)
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 15/16 v3] Extended-remote exec event documentation
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (12 preceding siblings ...)
2014-10-31 23:31 ` [PATCH 14/16 v3] Suppress spurious warnings with extended-remote follow exec Don Breazeal
@ 2014-10-31 23:31 ` Don Breazeal
2014-10-31 23:31 ` [PATCH 16/16 v3] Non-stop follow exec tests Don Breazeal
14 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:31 UTC (permalink / raw)
To: gdb-patches
This patch adds documentation of the new RSP support for exec events.
[This was previously approved by Eli:
https://sourceware.org/ml/gdb-patches/2014-05/msg00690.html]
Thanks
--Don
gdb/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* NEWS: Mention RSP Stop Reply Packet, new stop reason 'exec'.
Mention gdbserver support for exec events on Linux.
gdb/doc/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* gdb.texinfo (Stop Reply Packets): Document RSP support
for exec events.
---
gdb/NEWS | 21 ++++++++++++---------
gdb/doc/gdb.texinfo | 9 ++++++++-
2 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 1c07663..777b182 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -59,20 +59,23 @@ SGI Irix-6.x mips-*-irix6*
VAX running (4.2 - 4.3 Reno) BSD vax-*-bsd*
VAX running Ultrix vax-*-ultrix*
-* Remote fork events
+* Remote fork and exec events
- GDBserver extended-remote Linux targets now support fork events.
- This enables follow-fork-mode, detach-on-fork, catch fork, and
- catch vfork for those targets with Linux kernels 2.5.60 and later.
+ GDBserver extended-remote Linux targets now support fork and exec
+ events. This enables follow-fork-mode, detach-on-fork, follow-exec-mode,
+ catch fork, catch vfork, and catch exec for those targets with Linux
+ kernels that support these events. Linux kernel versions 2.5.60 and
+ later support all of these events.
* New remote packets
T Stop Reply Packet's reason
- The T stop reply packet supports new stop reasons 'fork', 'vfork'
- and 'vforkdone'. The 'fork' and 'vfork' reasons signify that the
- specified inferior has executed a fork or vfork. The 'vforkdone'
- reason signifies that a vforked child process has executed either
- an exec or an exit.
+ The T stop reply packet supports new stop reasons 'fork', 'vfork',
+ 'vforkdone', and 'exec'. The 'fork' and 'vfork' reasons signify
+ that the specified inferior has executed a fork or vfork. The
+ 'vforkdone' reason signifies that a vforked child process has
+ executed either an exec or an exit. The 'exec' reason signifies
+ that the specified inferior executed a call to execve.
*** Changes in GDB 7.8
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9787623..f6b39ba 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3196,7 +3196,8 @@ process, use the @code{file} command with the parent executable name
as its argument. By default, after an @code{exec} call executes,
@value{GDBN} discards the symbols of the previous executable image.
You can change this behaviour with the @w{@code{set follow-exec-mode}}
-command.
+command. This command is supported when connected to @code{gdbserver}
++using @kbd{target extended-remote} as well as in native mode.
@table @code
@kindex set follow-exec-mode
@@ -34725,6 +34726,12 @@ address spaces of the parent and child process are no longer
shared. The @var{r} part is ignored. This packet is only
applicable to targets that support vforkdone events.
+@cindex exec events, remote reply
+@item exec
+The packet indicates that @code{execve} was called, and @var{r} is the
+absolute pathname of the file that was executed, in hex. This packet
+is only applicable to targets that support exec events.
+
@cindex replay log events, remote reply
@item replaylog
The packet indicates that the target cannot continue replaying
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 16/16 v3] Non-stop follow exec tests
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
` (13 preceding siblings ...)
2014-10-31 23:31 ` [PATCH 15/16 v3] Extended-remote exec event documentation Don Breazeal
@ 2014-10-31 23:31 ` Don Breazeal
14 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-10-31 23:31 UTC (permalink / raw)
To: gdb-patches
This patch modifies some follow-exec tests, getting one to work with
extended-remote targets and extending another for more coverage.
First, gdb.base/foll-exec.exp is changed to use clean_restart for
the initial test startup and to replace the proc zap_session
everywhere in the test. This provides for extended-remote targets
to be able to do runto_main.
The functionality is changed slightly by this, in that zap_session
did not exit and restart the debugger as clean_restart does.
However, none of the tests are checking for the interaction of
follow-exec with re-running the program, so the functionality is
tested in the same way.
The other change extends the non-ldr-exc-*.exp tests so that they run all
their cases in non-stop mode as well as all-stop mode. These tests cover
handling of exec events when non-leader threads call exec.
The tests now report 'untested when 'runto_main' fails. In non-stop mode
with 'target extended-remote', runto_main always fails with something like:
(gdb) run
Starting program: /home/me/gdb/testsuite/gdb.threads/non-ldr-exc-4
Unexpected vCont reply in non-stop mode: T0506:10e0ffffff7f0000;07:c8deffffff7f0000;10:c1a6abaaaa2a0000;thread:p5ee.5ee;core:0;
This happens in other tests as well (e.g. gdb.threads/thread_events.exp),
so I copied the error handling from that test so that the non-stop tests
report 'untested' for target extended-remote. I couldn't find anything
related to this in the gdb bug database, but I assume it is a known problem
since the other tests handle it.
Thanks
--Don
gdb/testsuite/
2014-10-31 Don Breazeal <donb@codesourcery.com>
* gdb.base/foll-exec.exp (zap_session): Delete proc.
(do_exec_tests): Replace zap_session with clean_restart.
(main): Use clean_restart for initial GDB startup.
* gdb.threads/non-ldr-exc-1.exp: Add non-stop cases.
* gdb.threads/non-ldr-exc-2.exp: Ditto.
* gdb.threads/non-ldr-exc-3.exp: Ditto.
* gdb.threads/non-ldr-exc-4.exp: Ditto.
---
gdb/testsuite/gdb.base/foll-exec.exp | 44 ++++-----------------------
gdb/testsuite/gdb.threads/non-ldr-exc-1.exp | 20 ++++++++++--
gdb/testsuite/gdb.threads/non-ldr-exc-2.exp | 36 +++++++++++++++++++---
gdb/testsuite/gdb.threads/non-ldr-exc-3.exp | 36 +++++++++++++++++++---
gdb/testsuite/gdb.threads/non-ldr-exc-4.exp | 20 ++++++++++--
5 files changed, 100 insertions(+), 56 deletions(-)
diff --git a/gdb/testsuite/gdb.base/foll-exec.exp b/gdb/testsuite/gdb.base/foll-exec.exp
index c1b1354..6051d29 100644
--- a/gdb/testsuite/gdb.base/foll-exec.exp
+++ b/gdb/testsuite/gdb.base/foll-exec.exp
@@ -44,32 +44,6 @@ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable $com
return -1
}
-proc zap_session {} {
- global gdb_prompt
- global binfile
-
- send_gdb "kill\n"
- gdb_expect {
- -re ".*Kill the program being debugged.*y or n. $" {
- gdb_test_no_output "y" ""
- send_gdb "file $binfile\n"
- gdb_expect {
- -re ".*Load new symbol table from.*y or n. $" {
- send_gdb "y\n"
- gdb_expect {
- -re "Reading symbols from.*$gdb_prompt $" {}
- timeout { fail "loading symbols (timeout)"; return }
- }
- }
- -re ".*gdb_prompt $" {}
- timeout { fail "loading symbols (timeout)"; return }
- }
- }
- -re ".*$gdb_prompt $" {}
- timeout { fail "killing inferior (timeout)" ; return }
- }
-}
-
proc do_exec_tests {} {
global gdb_prompt
global binfile
@@ -103,7 +77,7 @@ proc do_exec_tests {} {
return
}
- zap_session
+ clean_restart ${binfile}
# Start the program running, and stop at main.
#
@@ -191,7 +165,7 @@ proc do_exec_tests {} {
# Explicitly kill this program, or a subsequent rerun actually runs
# the exec'd program, not the original program...
- zap_session
+ clean_restart ${binfile}
# Start the program running, and stop at main.
#
@@ -264,7 +238,7 @@ proc do_exec_tests {} {
# Explicitly kill this program, or a subsequent rerun actually runs
# the exec'd program, not the original program...
- zap_session
+ clean_restart ${binfile}
# Start the program running, and stop at main.
#
@@ -324,7 +298,7 @@ proc do_exec_tests {} {
# Explicitly kill this program, or a subsequent rerun actually runs
# the exec'd program, not the original program...
- zap_session
+ clean_restart ${binfile}
# Start the program running, and stop at main.
#
@@ -376,9 +350,7 @@ proc do_exec_tests {} {
timeout {fail "(timeout) print execd-program/local_j (after execv)"}
}
- # Explicitly kill this program, or a subsequent rerun actually runs
- # the exec'd program, not the original program...
- zap_session
+ clean_restart ${binfile}
# Start the program running, and stop at main.
#
@@ -402,11 +374,7 @@ proc do_exec_tests {} {
# Start with a fresh gdb
-gdb_exit
-gdb_start
-gdb_reinitialize_dir $srcdir/$subdir
-gdb_load ${binfile}
-
+clean_restart $binfile
# This is a test of gdb's ability to follow a process through a
# Unix exec() system call.
diff --git a/gdb/testsuite/gdb.threads/non-ldr-exc-1.exp b/gdb/testsuite/gdb.threads/non-ldr-exc-1.exp
index 8123a99..e35236a 100644
--- a/gdb/testsuite/gdb.threads/non-ldr-exc-1.exp
+++ b/gdb/testsuite/gdb.threads/non-ldr-exc-1.exp
@@ -28,13 +28,19 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
-proc do_test { lock_sched } {
- with_test_prefix "lock-sched$lock_sched" {
+proc do_test { lock_sched mode } {
+ with_test_prefix "lock-sched$lock_sched,$mode" {
global executable
clean_restart ${executable}
+ if { $mode == "non-stop" } {
+ gdb_test_no_output "set target-async on" "set async mode"
+ gdb_test_no_output "set non-stop on" "set non-stop mode"
+ }
+
if ![runto_main] {
+ untested "could not run to main"
return -1
}
@@ -48,11 +54,17 @@ proc do_test { lock_sched } {
gdb_test_no_output "set scheduler-locking on"
}
+ if { $mode == "non-stop" } {
+ gdb_test "thread 2" "Switching.*"
+ }
+
gdb_test "continue" \
".*is executing new program.*Breakpoint 1, main.* at .*" \
"continue over exec"
}
}
-do_test 0
-do_test 1
+do_test 0 "all-stop"
+do_test 1 "all-stop"
+do_test 0 "non-stop"
+do_test 1 "non-stop"
diff --git a/gdb/testsuite/gdb.threads/non-ldr-exc-2.exp b/gdb/testsuite/gdb.threads/non-ldr-exc-2.exp
index 857e7bc..a0281d6 100644
--- a/gdb/testsuite/gdb.threads/non-ldr-exc-2.exp
+++ b/gdb/testsuite/gdb.threads/non-ldr-exc-2.exp
@@ -29,18 +29,42 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
-proc do_test { lock_sched } {
- with_test_prefix "lock-sched$lock_sched" {
+# Test for breakpoint event among async thread events.
+# gdb_continue_to_breakpoint requires "$gdb_prompt $", but
+# here we may get a thread event message instead of EOL.
+proc gdb_continue_to_breakpoint_async { name pattern } {
+ global gdb_prompt
+
+ gdb_test_multiple "continue" $name {
+ -re ".*Breakpoint .* (at|in) ($pattern)$gdb_prompt " {
+ pass $name
+ }
+ }
+}
+
+proc do_test { lock_sched mode } {
+ with_test_prefix "lock-sched$lock_sched,$mode" {
global executable
clean_restart ${executable}
+ if { $mode == "non-stop" } {
+ gdb_test_no_output "set target-async on" "set async mode"
+ gdb_test_no_output "set non-stop on" "set non-stop mode"
+ }
+
if ![runto_main] {
+ untested "could not run to main"
return -1
}
gdb_breakpoint [gdb_get_line_number "break-here"]
- gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+ if { $mode == "non-stop" } {
+ gdb_continue_to_breakpoint_async "break-here" ".* break-here .*"
+ gdb_test "thread 2" "Switching.*"
+ } else {
+ gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+ }
gdb_test "info threads" \
"\r\n\[ \t\]*Id\[ \t\]+Target\[ \t\]+Id\[ \t\]+Frame\[ \t\]*\r\n\\* 2 *Thread \[^\r\n\]* at \[^\r\n\]*" \
@@ -59,5 +83,7 @@ proc do_test { lock_sched } {
}
}
-do_test 0
-do_test 1
+do_test 0 "all-stop"
+do_test 1 "all-stop"
+do_test 0 "non-stop"
+do_test 1 "non-stop"
diff --git a/gdb/testsuite/gdb.threads/non-ldr-exc-3.exp b/gdb/testsuite/gdb.threads/non-ldr-exc-3.exp
index 7f33d39..69c27d8 100644
--- a/gdb/testsuite/gdb.threads/non-ldr-exc-3.exp
+++ b/gdb/testsuite/gdb.threads/non-ldr-exc-3.exp
@@ -31,18 +31,42 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
-proc do_test { lock_sched } {
- with_test_prefix "lock-sched$lock_sched" {
+# Test for breakpoint event among async thread events.
+# gdb_continue_to_breakpoint requires "$gdb_prompt $", but
+# here we may get a thread event message instead of EOL.
+proc gdb_continue_to_breakpoint_async { name pattern } {
+ global gdb_prompt
+
+ gdb_test_multiple "continue" $name {
+ -re ".*Breakpoint .* (at|in) ($pattern)$gdb_prompt " {
+ pass $name
+ }
+ }
+}
+
+proc do_test { lock_sched mode } {
+ with_test_prefix "lock-sched$lock_sched,$mode" {
global executable
clean_restart ${executable}
+ if { $mode == "non-stop" } {
+ gdb_test_no_output "set target-async on" "set async mode"
+ gdb_test_no_output "set non-stop on" "set non-stop mode"
+ }
+
if ![runto_main] {
+ untested "could not run to main"
return -1
}
gdb_breakpoint [gdb_get_line_number "break-here"]
- gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+ if { $mode == "non-stop" } {
+ gdb_continue_to_breakpoint_async "break-here" ".* break-here .*"
+ gdb_test "thread 2" "Switching.*"
+ } else {
+ gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+ }
# Also test with sched-lock to make sure we can follow the
# non-leader thread execing even though the main thread wasn't
@@ -57,5 +81,7 @@ proc do_test { lock_sched } {
}
}
-do_test 0
-do_test 1
+do_test 0 "all-stop"
+do_test 1 "all-stop"
+do_test 0 "non-stop"
+do_test 1 "non-stop"
diff --git a/gdb/testsuite/gdb.threads/non-ldr-exc-4.exp b/gdb/testsuite/gdb.threads/non-ldr-exc-4.exp
index a5e88bb..f83577d 100644
--- a/gdb/testsuite/gdb.threads/non-ldr-exc-4.exp
+++ b/gdb/testsuite/gdb.threads/non-ldr-exc-4.exp
@@ -30,13 +30,19 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
-proc do_test { lock_sched } {
- with_test_prefix "lock-sched$lock_sched" {
+proc do_test { lock_sched mode } {
+ with_test_prefix "lock-sched$lock_sched,$mode" {
global executable
clean_restart ${executable}
+ if { $mode == "non-stop" } {
+ gdb_test_no_output "set target-async on" "set async mode"
+ gdb_test_no_output "set non-stop on" "set non-stop mode"
+ }
+
if ![runto_main] {
+ untested "could not run to main"
return -1
}
@@ -50,11 +56,17 @@ proc do_test { lock_sched } {
gdb_test_no_output "set scheduler-locking on"
}
+ if { $mode == "non-stop" } {
+ gdb_test "thread 2" "Switching.*"
+ }
+
gdb_test "continue" \
".*is executing new program.*Breakpoint 1, main.* at .*" \
"continue over exec"
}
}
-do_test 0
-do_test 1
+do_test 0 "all-stop"
+do_test 1 "all-stop"
+do_test 0 "non-stop"
+do_test 1 "non-stop"
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 01/16 v2] Refactor native follow-fork
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (10 preceding siblings ...)
2014-08-21 0:29 ` [Patch 00/16 v2] Linux extended-remote fork and exec events Don Breazeal
@ 2014-08-21 0:29 ` Don Breazeal
2014-09-05 14:20 ` Pedro Alves
2014-08-21 0:30 ` [PATCH 03/16 v2] Refactor ptrace extended event status Don Breazeal
` (14 subsequent siblings)
26 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:29 UTC (permalink / raw)
To: gdb-patches
This patch reorganizes some of 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
contains 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,
was 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. Once the target-independent pieces were removed from
linux_child_follow_fork, it was possible to consolidate it a bit. Although
the order of some operations was changed, the resulting functionality was
not.
Tested on x64 Ubuntu Lucid, native only.
Thanks,
--Don
gdb/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* infrun.c (follow_fork): Call follow_fork_inferior.
(follow_fork_inferior): New function.
(follow_inferior_reset_breakpoints): Make function static.
* infrun.h: Remove declaration of follow_inferior_reset_breakpoints.
* linux-nat.c (linux_child_follow_fork): Move target-independent
code to infrun.c:follow_fork_inferior.
---
gdb/infrun.c | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
gdb/infrun.h | 2 -
gdb/linux-nat.c | 216 ++---------------------------------------------
3 files changed, 254 insertions(+), 212 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index c18267f..a51c759 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);
@@ -486,9 +491,11 @@ 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_follow_fork (follow_child, detach_fork))
{
/* Target refused to follow, or there's some other reason
we shouldn't resume. */
@@ -560,7 +567,240 @@ follow_fork (void)
return should_resume;
}
-void
+/* 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. */
+
+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 (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 (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 0;
+}
+
+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 1e8991d..ab287bf 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"
@@ -384,64 +383,22 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child,
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;
/* 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)
@@ -480,82 +437,15 @@ holding the child stopped. Try \"set detach-on-fork\" or \
}
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 +518,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 ();
}
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-08-21 0:29 ` [PATCH 01/16 v2] Refactor native follow-fork Don Breazeal
@ 2014-09-05 14:20 ` Pedro Alves
2014-09-05 18:56 ` Breazeal, Don
2014-09-08 23:54 ` Breazeal, Don
0 siblings, 2 replies; 110+ messages in thread
From: Pedro Alves @ 2014-09-05 14:20 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
linux_child_follow_fork ends up with:
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 (!follow_child)
{
...
}
else
{
struct lwp_info *child_lp;
child_lp = add_lwp (inferior_ptid);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
child_lp->stopped = 1;
child_lp->last_resume_kind = resume_stop;
/* Let the thread_db layer learn about this new process. */
check_for_thread_db ();
}
}
Nothing appears to switch inferior_ptid to the child, so seems
like we're adding the child_lp with the wrong lwp (and calling
check_for_thread_db in the wrong context) ? Is this managing
to work by chance because follow_fork_inferior leaves inferior_ptid
pointing to the child? Then this at the top uses the wrong
inferior_thread ():
has_vforked = (inferior_thread ()->pending_follow.kind
== TARGET_WAITKIND_VFORKED);
and we're lucky that nothing end up using has_vforked in the
follow child path?
I'd much rather we don't have these assumptions in place.
These files / targets also have to_follow_fork implementations:
inf-ptrace.c: t->to_follow_fork = inf_ptrace_follow_fork;
inf-ttrace.c: t->to_follow_fork = inf_ttrace_follow_fork;
which will break if we don't adjust them as well. Did you
check whether the refactored code (follow_fork_inferior)
makes sense for those?
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-09-05 14:20 ` Pedro Alves
@ 2014-09-05 18:56 ` Breazeal, Don
2014-09-05 20:20 ` Breazeal, Don
2014-09-09 10:57 ` Pedro Alves
2014-09-08 23:54 ` Breazeal, Don
1 sibling, 2 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-09-05 18:56 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
Hi Pedro,
Thanks for reviewing this.
On 9/5/2014 7:20 AM, Pedro Alves wrote:
> linux_child_follow_fork ends up with:
>
> 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 (!follow_child)
> {
> ...
> }
> else
> {
> struct lwp_info *child_lp;
>
> child_lp = add_lwp (inferior_ptid);
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> child_lp->stopped = 1;
> child_lp->last_resume_kind = resume_stop;
>
> /* Let the thread_db layer learn about this new process. */
> check_for_thread_db ();
> }
> }
>
> Nothing appears to switch inferior_ptid to the child, so seems
> like we're adding the child_lp with the wrong lwp (and calling
> check_for_thread_db in the wrong context) ? Is this managing
> to work by chance because follow_fork_inferior leaves inferior_ptid
> pointing to the child?
Yes, follow_fork_inferior always sets inferior_ptid to the followed
inferior. On entry, linux_child_follow_fork expects inferior_ptid to be
the followed inferior. So I think it is getting the correct inferior
from inferior_ptid in these cases. I can change that if you prefer; see
my question below about acceptable solutions.
Regarding check_for_thread_db, there is something unrelated that I don't
understand here. If we have reached this function, then aren't we
guaranteed that PTRACE_O_TRACECLONE is supported, and that we are using
that instead of libthread_db for detecting thread events? If so, why do
we need to call check_for_thread_db at all?
Then this at the top uses the wrong
> inferior_thread ():
>
> has_vforked = (inferior_thread ()->pending_follow.kind
> == TARGET_WAITKIND_VFORKED);
>
>
> and we're lucky that nothing end up using has_vforked in the
> follow child path?
You are right, this is incorrect and unnecessary in the case where we
are following the child.
>
> I'd much rather we don't have these assumptions in place.
Would an acceptable solution be to move the definitions and assignments
of has_vforked, parent_pid, and child_pid into the follow-parent case,
as below?
Would you also prefer that on entry to linux_child_follow_fork,
inferior_ptid is set to the parent like it was before, or would a
comment explaining that inferior_ptid is expected to be the followed
inferior be sufficient?
static int
linux_child_follow_fork (struct target_ops *ops, int follow_child,
int detach_fork)
{
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);
>
> These files / targets also have to_follow_fork implementations:
>
> inf-ptrace.c: t->to_follow_fork = inf_ptrace_follow_fork;
> inf-ttrace.c: t->to_follow_fork = inf_ttrace_follow_fork;
>
> which will break if we don't adjust them as well. Did you
> check whether the refactored code (follow_fork_inferior)
> makes sense for those?
I completely missed these; sorry about that. In theory I should be able
to make similar changes to these that maintains the existing
functionality. I don't currently have a way (that I know of) to test
either of them. Testing requires a non-Linux version of Unix and an
HP-UX system, correct? I'll start work on the changes in spite of that.
>
> Thanks,
> Pedro Alves
>
Thanks,
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-09-05 18:56 ` Breazeal, Don
@ 2014-09-05 20:20 ` Breazeal, Don
2014-09-09 10:57 ` Pedro Alves
1 sibling, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-09-05 20:20 UTC (permalink / raw)
To: gdb-patches
One clarification...
On 9/5/2014 11:56 AM, Breazeal, Don wrote:
> Hi Pedro,
> Thanks for reviewing this.
>
> On 9/5/2014 7:20 AM, Pedro Alves wrote:
>> linux_child_follow_fork ends up with:
>>
>> 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 (!follow_child)
>> {
>> ...
>> }
>> else
>> {
>> struct lwp_info *child_lp;
>>
>> child_lp = add_lwp (inferior_ptid);
>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>> child_lp->stopped = 1;
>> child_lp->last_resume_kind = resume_stop;
>>
>> /* Let the thread_db layer learn about this new process. */
>> check_for_thread_db ();
>> }
>> }
>>
>> Nothing appears to switch inferior_ptid to the child, so seems
>> like we're adding the child_lp with the wrong lwp (and calling
>> check_for_thread_db in the wrong context) ? Is this managing
>> to work by chance because follow_fork_inferior leaves inferior_ptid
>> pointing to the child?
>
> Yes, follow_fork_inferior always sets inferior_ptid to the followed
> inferior. On entry, linux_child_follow_fork expects inferior_ptid to be
> the followed inferior. So I think it is getting the correct inferior
> from inferior_ptid in these cases. I can change that if you prefer; see
> my question below about acceptable solutions.
Er... I can change how inferior_ptid is passed to
linux_child_follow_fork, not whether the correct ptid is used. :-P
>
> Regarding check_for_thread_db, there is something unrelated that I don't
> understand here. If we have reached this function, then aren't we
> guaranteed that PTRACE_O_TRACECLONE is supported, and that we are using
> that instead of libthread_db for detecting thread events? If so, why do
> we need to call check_for_thread_db at all?
>
> Then this at the top uses the wrong
>> inferior_thread ():
>>
>> has_vforked = (inferior_thread ()->pending_follow.kind
>> == TARGET_WAITKIND_VFORKED);
>>
>>
>> and we're lucky that nothing end up using has_vforked in the
>> follow child path?
>
> You are right, this is incorrect and unnecessary in the case where we
> are following the child.
>
>>
>> I'd much rather we don't have these assumptions in place.
>
> Would an acceptable solution be to move the definitions and assignments
> of has_vforked, parent_pid, and child_pid into the follow-parent case,
> as below?
>
> Would you also prefer that on entry to linux_child_follow_fork,
> inferior_ptid is set to the parent like it was before, or would a
> comment explaining that inferior_ptid is expected to be the followed
> inferior be sufficient?
>
> static int
> linux_child_follow_fork (struct target_ops *ops, int follow_child,
> int detach_fork)
> {
> 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);
>
>>
>> These files / targets also have to_follow_fork implementations:
>>
>> inf-ptrace.c: t->to_follow_fork = inf_ptrace_follow_fork;
>> inf-ttrace.c: t->to_follow_fork = inf_ttrace_follow_fork;
>>
>> which will break if we don't adjust them as well. Did you
>> check whether the refactored code (follow_fork_inferior)
>> makes sense for those?
>
> I completely missed these; sorry about that. In theory I should be able
> to make similar changes to these that maintains the existing
> functionality. I don't currently have a way (that I know of) to test
> either of them. Testing requires a non-Linux version of Unix and an
> HP-UX system, correct? I'll start work on the changes in spite of that.
>
>>
>> Thanks,
>> Pedro Alves
>>
>
> Thanks,
> --Don
>
>
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-09-05 18:56 ` Breazeal, Don
2014-09-05 20:20 ` Breazeal, Don
@ 2014-09-09 10:57 ` Pedro Alves
1 sibling, 0 replies; 110+ messages in thread
From: Pedro Alves @ 2014-09-09 10:57 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
On 09/05/2014 07:56 PM, Breazeal, Don wrote:
> Hi Pedro,
> Thanks for reviewing this.
>
> On 9/5/2014 7:20 AM, Pedro Alves wrote:
>> linux_child_follow_fork ends up with:
>>
>> 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 (!follow_child)
>> {
>> ...
>> }
>> else
>> {
>> struct lwp_info *child_lp;
>>
>> child_lp = add_lwp (inferior_ptid);
>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>> child_lp->stopped = 1;
>> child_lp->last_resume_kind = resume_stop;
>>
>> /* Let the thread_db layer learn about this new process. */
>> check_for_thread_db ();
>> }
>> }
>>
>> Nothing appears to switch inferior_ptid to the child, so seems
>> like we're adding the child_lp with the wrong lwp (and calling
>> check_for_thread_db in the wrong context) ? Is this managing
>> to work by chance because follow_fork_inferior leaves inferior_ptid
>> pointing to the child?
>
> Yes, follow_fork_inferior always sets inferior_ptid to the followed
> inferior.
Ah.
> On entry, linux_child_follow_fork expects inferior_ptid to be
> the followed inferior.
I see. It does make sense.
> So I think it is getting the correct inferior
> from inferior_ptid in these cases. I can change that if you prefer; see
> my question below about acceptable solutions.
>
> Regarding check_for_thread_db, there is something unrelated that I don't
> understand here. If we have reached this function, then aren't we
> guaranteed that PTRACE_O_TRACECLONE is supported, and that we are using
> that instead of libthread_db for detecting thread events? If so, why do
> we need to call check_for_thread_db at all?
Unlike GDBserver, GDB actually still use libthread_db's create/exit event
breakpoints even if PTRACE_O_TRACECLONE is supported. I think this is
just historical at this point, and we could skip those event breakpoints,
optimizing out a bunch of internal stops.
The check_for_thread_db call has another effect -- as mentioned in the
comment:
/* Let the thread_db layer learn about this new process. */
check_for_thread_db ();
if we don't call it, then linux-thread-db.c never adds the child
process to its global thread_db_list list:
/* List of known processes using thread_db, and the required
bookkeeping. */
struct thread_db_info *thread_db_list;
We don't really need to try all the available libthread_db's found
in the path, we could just try try_thread_db_load with the same
libthread_db we had loaded for the parent, or even skip that
and share/refcount the bookkeeping in 'struct thread_db_info'
(dlopen handle, functions pointers, etc.) between parent and child.
This is about the same issue as mentioned just above:
/* 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. */
>
> Then this at the top uses the wrong
>> inferior_thread ():
>>
>> has_vforked = (inferior_thread ()->pending_follow.kind
>> == TARGET_WAITKIND_VFORKED);
>>
>>
>> and we're lucky that nothing end up using has_vforked in the
>> follow child path?
>
> You are right, this is incorrect and unnecessary in the case where we
> are following the child.
>
>>
>> I'd much rather we don't have these assumptions in place.
>
> Would an acceptable solution be to move the definitions and assignments
> of has_vforked, parent_pid, and child_pid into the follow-parent case,
> as below?
Yes.
>
> Would you also prefer that on entry to linux_child_follow_fork,
> inferior_ptid is set to the parent like it was before, or would a
> comment explaining that inferior_ptid is expected to be the followed
> inferior be sufficient?
The comment would be sufficient.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-09-05 14:20 ` Pedro Alves
2014-09-05 18:56 ` Breazeal, Don
@ 2014-09-08 23:54 ` Breazeal, Don
2014-09-09 11:09 ` Pedro Alves
1 sibling, 1 reply; 110+ messages in thread
From: Breazeal, Don @ 2014-09-08 23:54 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
Hi Pedro,
I've consolidated my responses to the issues you raised in this email,
and attached an updated patch containing the proposed solutions.
On 9/5/2014 7:20 AM, Pedro Alves wrote:
> linux_child_follow_fork ends up with:
>
> 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 (!follow_child)
> {
> ...
> }
> else
> {
> struct lwp_info *child_lp;
>
> child_lp = add_lwp (inferior_ptid);
> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> child_lp->stopped = 1;
> child_lp->last_resume_kind = resume_stop;
>
> /* Let the thread_db layer learn about this new process. */
> check_for_thread_db ();
> }
> }
>
> Nothing appears to switch inferior_ptid to the child, so seems
> like we're adding the child_lp with the wrong lwp (and calling
> check_for_thread_db in the wrong context) ? Is this managing
> to work by chance because follow_fork_inferior leaves inferior_ptid
> pointing to the child?
Yes, follow_fork_inferior always sets inferior_ptid to the followed
inferior. Then on entry, linux_child_follow_fork expects inferior_ptid
to be the followed inferior. So I think it is getting the expected
inferior from inferior_ptid in these cases.
In the original code, inferior_ptid was the parent on entry to
target_follow_fork, and the followed inferior after return. I made
follow_fork_inferior do the same thing (return the followed inferior),
but it would be no problem to have it return the parent and have
linux_child_follow_fork expect inferior_ptid to be the parent and return
the followed inferior (like it did before) if that would be preferable.
Let me know if you'd like me to make that change.
Regarding check_for_thread_db, there is something unrelated that I don't
understand. Maybe you can clear this up for me. If we have reached
linux_child_follow_fork, then aren't we guaranteed that
PTRACE_O_TRACECLONE is supported, and that we are using that instead of
libthread_db for detecting thread events? If so, why do we need to call
check_for_thread_db at all?
Then this at the top uses the wrong
> inferior_thread ():
>
> has_vforked = (inferior_thread ()->pending_follow.kind
> == TARGET_WAITKIND_VFORKED);
>
>
> and we're lucky that nothing end up using has_vforked in the
> follow child path?
You are right, this is incorrect and unnecessary in the case where we
are following the child.
>
> I'd much rather we don't have these assumptions in place.
Would an acceptable solution be to move the definitions and assignments
of has_vforked, parent_pid, and child_pid into the follow-parent case,
as below?
static int
linux_child_follow_fork (struct target_ops *ops, int follow_child,
int detach_fork)
{
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
>
> These files / targets also have to_follow_fork implementations:
>
> inf-ptrace.c: t->to_follow_fork = inf_ptrace_follow_fork;
> inf-ttrace.c: t->to_follow_fork = inf_ttrace_follow_fork;
>
> which will break if we don't adjust them as well. Did you
> check whether the refactored code (follow_fork_inferior)
> makes sense for those?
I completely missed these; sorry about that. In my initial response to
your review, I thought I should be able to make similar changes to these
functions that maintained the existing functionality. After digging into
this, I still think it is possible, but that a more prudent approach
would be to make follow_fork_inferior into a new target hook that just
returns zero for the non-Linux cases.
The changes to inf_ptrace_follow_fork to get it to work with
follow_fork_inferior are straightforward. Making those changes to
inf_ttrace_follow_fork is problematic, primarily because it handles the
moral equivalent of the vfork-done event differently. The changes
required to make it work with follow_fork_inferior are more significant
than I'd like, given that I don't have any way to test how the OS
reports the events. So I opted for turning follow_fork_inferior into a
target routine.
The updated patch is below. I tested it on native x64 Ubuntu. Let me
know what you think.
>
> Thanks,
> Pedro Alves
>
Thanks again for your comments,
--Don
gdb/
2014-09-08 Don Breazeal <donb@codesourcery.com>
* infrun.c (follow_fork): Call target_follow_fork_inferior.
(infrun_follow_fork_inferior): New function.
(follow_inferior_reset_breakpoints): Make function static.
* infrun.h (infrun_follow_fork_inferior): Declare.
(follow_inferior_reset_breakpoints): Remove declaration.
* linux-nat.c (linux_child_follow_fork): Move target-independent
code to infrun.c:infrun_follow_fork_inferior.
(linux_target_install_ops): Initialize new target_ops member.
* target-delegates.c (delegate_follow_fork_inferior): New
generated function.
(debug_follow_fork_inferior): New generated function.
(delegate_follow_fork): New generated function.
(install_delegators): Initialize new target_ops member.
(install_dummy_methods): Initialize new target_ops member.
(init_debug_target): Initialize new target_ops member.
* target.c (default_follow_fork_inferior): New function.
(target_follow_fork_inferior): New function.
* target.h (struct target_ops)<to_follow_fork_inferior>: New
member.
---
gdb/infrun.c | 247
++++++++++++++++++++++++++++++++++++++++++++++++-
gdb/infrun.h | 5 +-
gdb/linux-nat.c | 238
+++++------------------------------------------
gdb/target-delegates.c | 29 ++++++
gdb/target.c | 23 +++++
gdb/target.h | 7 +-
6 files changed, 324 insertions(+), 225 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index c18267f..b4c10de 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,8 @@ static int restore_selected_frame (void *);
static int follow_fork (void);
+static void follow_inferior_reset_breakpoints (void);
+
static void set_schedlock_func (char *args, int from_tty,
struct cmd_list_element *c);
@@ -486,9 +489,11 @@ 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 (target_follow_fork_inferior (follow_child, detach_fork)
+ || target_follow_fork (follow_child, detach_fork))
{
/* Target refused to follow, or there's some other reason
we shouldn't resume. */
@@ -560,7 +565,241 @@ follow_fork (void)
return should_resume;
}
-void
+/* 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. */
+
+int
+infrun_follow_fork_inferior (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)
+ {
+ /* 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 (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 (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 0;
+}
+
+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..bc1ce52 100644
--- a/gdb/infrun.h
+++ b/gdb/infrun.h
@@ -80,6 +80,9 @@ extern int execution_direction;
register). */
extern struct regcache *stop_registers;
+extern int infrun_follow_fork_inferior (struct target_ops *ops,
+ int follow_child, int detach_fork);
+
extern void start_remote (int from_tty);
/* Clear out all variables saying what to do when inferior is
@@ -115,8 +118,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 1e8991d..d82dfca 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"
@@ -373,75 +372,32 @@ 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)
@@ -480,82 +436,15 @@ holding the child stopped. Try \"set
detach-on-fork\" or \
}
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 +517,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 ();
}
@@ -4510,6 +4313,7 @@ linux_target_install_ops (struct target_ops *t)
t->to_pid_to_exec_file = linux_child_pid_to_exec_file;
t->to_post_startup_inferior = linux_child_post_startup_inferior;
t->to_post_attach = linux_child_post_attach;
+ t->to_follow_fork_inferior = infrun_follow_fork_inferior;
t->to_follow_fork = linux_child_follow_fork;
t->to_make_corefile_notes = linux_nat_make_corefile_notes;
diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
index 843a954..4469394 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.c
@@ -1046,6 +1046,31 @@ debug_remove_vfork_catchpoint (struct target_ops
*self, int arg1)
}
static int
+delegate_follow_fork_inferior (struct target_ops *self, int arg1, int arg2)
+{
+ self = self->beneath;
+ return self->to_follow_fork_inferior (self, arg1, arg2);
+}
+
+static int
+debug_follow_fork_inferior (struct target_ops *self, int arg1, int arg2)
+{
+ int result;
+ fprintf_unfiltered (gdb_stdlog, "-> %s->to_follow_fork_inferior
(...)\n", debug_target.to_shortname);
+ result = debug_target.to_follow_fork_inferior (&debug_target, arg1,
arg2);
+ fprintf_unfiltered (gdb_stdlog, "<- %s->to_follow_fork_inferior (",
debug_target.to_shortname);
+ target_debug_print_struct_target_ops_p (&debug_target);
+ fputs_unfiltered (", ", gdb_stdlog);
+ target_debug_print_int (arg1);
+ fputs_unfiltered (", ", gdb_stdlog);
+ target_debug_print_int (arg2);
+ fputs_unfiltered (") = ", gdb_stdlog);
+ target_debug_print_int (result);
+ fputs_unfiltered ("\n", gdb_stdlog);
+ return result;
+}
+
+static int
delegate_follow_fork (struct target_ops *self, int arg1, int arg2)
{
self = self->beneath;
@@ -3832,6 +3857,8 @@ install_delegators (struct target_ops *ops)
ops->to_insert_vfork_catchpoint = delegate_insert_vfork_catchpoint;
if (ops->to_remove_vfork_catchpoint == NULL)
ops->to_remove_vfork_catchpoint = delegate_remove_vfork_catchpoint;
+ if (ops->to_follow_fork_inferior == NULL)
+ ops->to_follow_fork_inferior = delegate_follow_fork_inferior;
if (ops->to_follow_fork == NULL)
ops->to_follow_fork = delegate_follow_fork;
if (ops->to_insert_exec_catchpoint == NULL)
@@ -4077,6 +4104,7 @@ install_dummy_methods (struct target_ops *ops)
ops->to_remove_fork_catchpoint = tdefault_remove_fork_catchpoint;
ops->to_insert_vfork_catchpoint = tdefault_insert_vfork_catchpoint;
ops->to_remove_vfork_catchpoint = tdefault_remove_vfork_catchpoint;
+ ops->to_follow_fork_inferior = default_follow_fork_inferior;
ops->to_follow_fork = default_follow_fork;
ops->to_insert_exec_catchpoint = tdefault_insert_exec_catchpoint;
ops->to_remove_exec_catchpoint = tdefault_remove_exec_catchpoint;
@@ -4221,6 +4249,7 @@ init_debug_target (struct target_ops *ops)
ops->to_remove_fork_catchpoint = debug_remove_fork_catchpoint;
ops->to_insert_vfork_catchpoint = debug_insert_vfork_catchpoint;
ops->to_remove_vfork_catchpoint = debug_remove_vfork_catchpoint;
+ ops->to_follow_fork_inferior = debug_follow_fork_inferior;
ops->to_follow_fork = debug_follow_fork;
ops->to_insert_exec_catchpoint = debug_insert_exec_catchpoint;
ops->to_remove_exec_catchpoint = debug_remove_exec_catchpoint;
diff --git a/gdb/target.c b/gdb/target.c
index 8bf6031..53fb9bc 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -62,6 +62,9 @@ static void default_rcmd (struct target_ops *, const
char *, struct ui_file *);
static ptid_t default_get_ada_task_ptid (struct target_ops *self,
long lwp, long tid);
+static int default_follow_fork_inferior (struct target_ops *self,
+ int follow_child, int detach_fork);
+
static int default_follow_fork (struct target_ops *self, int follow_child,
int detach_fork);
@@ -2098,6 +2101,26 @@ target_program_signals (int numsigs, unsigned
char *program_signals)
numsigs, program_signals);
}
+/* Default target function for follow_fork_inferior hook. Some targets
+ follow forks without using follow_fork_inferior functionality, so we
+ just return success. */
+
+static int
+default_follow_fork_inferior (struct target_ops *self, int follow_child,
+ int detach_fork)
+{
+ return 0;
+}
+
+/* Target wrapper for follow_fork_inferior hook. */
+
+int
+target_follow_fork_inferior (int follow_child, int detach_fork)
+{
+ return current_target.to_follow_fork_inferior (¤t_target,
+ follow_child, detach_fork);
+}
+
static int
default_follow_fork (struct target_ops *self, int follow_child,
int detach_fork)
diff --git a/gdb/target.h b/gdb/target.h
index 4d91b6b..bdba55f 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -531,6 +531,8 @@ struct target_ops
TARGET_DEFAULT_RETURN (1);
int (*to_remove_vfork_catchpoint) (struct target_ops *, int)
TARGET_DEFAULT_RETURN (1);
+ int (*to_follow_fork_inferior) (struct target_ops *, int, int)
+ TARGET_DEFAULT_FUNC (default_follow_fork_inferior);
int (*to_follow_fork) (struct target_ops *, int, int)
TARGET_DEFAULT_FUNC (default_follow_fork);
int (*to_insert_exec_catchpoint) (struct target_ops *, int)
@@ -1478,14 +1480,15 @@ extern void target_load (const char *arg, int
from_tty);
#define target_remove_vfork_catchpoint(pid) \
(*current_target.to_remove_vfork_catchpoint) (¤t_target, pid)
-/* If the inferior forks or vforks, this function will be called at
+/* If the inferior forks or vforks, these functions will be called at
the next resume in order to perform any bookkeeping and fiddling
necessary to continue debugging either the parent or child, as
requested, and releasing the other. Information about the fork
or vfork event is available via get_last_target_status ().
- This function returns 1 if the inferior should not be resumed
+ The functions return 1 if the inferior should not be resumed
(i.e. there is another event pending). */
+int target_follow_fork_inferior (int follow_child, int detach_fork);
int target_follow_fork (int follow_child, int detach_fork);
/* On some targets, we can catch an inferior exec event when it
--
1.8.1.1
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-09-08 23:54 ` Breazeal, Don
@ 2014-09-09 11:09 ` Pedro Alves
2014-09-12 16:50 ` Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-09-09 11:09 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
On 09/09/2014 12:54 AM, Breazeal, Don wrote:
> Hi Pedro,
>
> I've consolidated my responses to the issues you raised in this email,
> and attached an updated patch containing the proposed solutions.
>
> On 9/5/2014 7:20 AM, Pedro Alves wrote:
>> linux_child_follow_fork ends up with:
>>
>> 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 (!follow_child)
>> {
>> ...
>> }
>> else
>> {
>> struct lwp_info *child_lp;
>>
>> child_lp = add_lwp (inferior_ptid);
>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>> child_lp->stopped = 1;
>> child_lp->last_resume_kind = resume_stop;
>>
>> /* Let the thread_db layer learn about this new process. */
>> check_for_thread_db ();
>> }
>> }
>>
>> Nothing appears to switch inferior_ptid to the child, so seems
>> like we're adding the child_lp with the wrong lwp (and calling
>> check_for_thread_db in the wrong context) ? Is this managing
>> to work by chance because follow_fork_inferior leaves inferior_ptid
>> pointing to the child?
>
> Yes, follow_fork_inferior always sets inferior_ptid to the followed
> inferior. Then on entry, linux_child_follow_fork expects inferior_ptid
> to be the followed inferior. So I think it is getting the expected
> inferior from inferior_ptid in these cases.
>
> In the original code, inferior_ptid was the parent on entry to
> target_follow_fork, and the followed inferior after return. I made
> follow_fork_inferior do the same thing (return the followed inferior),
> but it would be no problem to have it return the parent and have
> linux_child_follow_fork expect inferior_ptid to be the parent and return
> the followed inferior (like it did before) if that would be preferable.
> Let me know if you'd like me to make that change.
>
> Regarding check_for_thread_db, there is something unrelated that I don't
> understand. Maybe you can clear this up for me. If we have reached
> linux_child_follow_fork, then aren't we guaranteed that
> PTRACE_O_TRACECLONE is supported, and that we are using that instead of
> libthread_db for detecting thread events? If so, why do we need to call
> check_for_thread_db at all?
>
> Then this at the top uses the wrong
>> inferior_thread ():
>>
>> has_vforked = (inferior_thread ()->pending_follow.kind
>> == TARGET_WAITKIND_VFORKED);
>>
>>
>> and we're lucky that nothing end up using has_vforked in the
>> follow child path?
>
> You are right, this is incorrect and unnecessary in the case where we
> are following the child.
>
>>
>> I'd much rather we don't have these assumptions in place.
>
> Would an acceptable solution be to move the definitions and assignments
> of has_vforked, parent_pid, and child_pid into the follow-parent case,
> as below?
>
> static int
> linux_child_follow_fork (struct target_ops *ops, int follow_child,
> int detach_fork)
> {
> 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
>>
>> These files / targets also have to_follow_fork implementations:
>>
>> inf-ptrace.c: t->to_follow_fork = inf_ptrace_follow_fork;
>> inf-ttrace.c: t->to_follow_fork = inf_ttrace_follow_fork;
>>
>> which will break if we don't adjust them as well. Did you
>> check whether the refactored code (follow_fork_inferior)
>> makes sense for those?
>
> I completely missed these; sorry about that. In my initial response to
> your review, I thought I should be able to make similar changes to these
> functions that maintained the existing functionality. After digging into
> this, I still think it is possible, but that a more prudent approach
> would be to make follow_fork_inferior into a new target hook that just
> returns zero for the non-Linux cases.
I'd rather not. That'll just add more technical debt. :-) E.g.,
if we can't model this on the few native targets we have, then that'd
indicate that remote debugging against one of those targets (when
we get to gdb/gdbserver parity), wouldn't be correctly modelled, as
you'd be installing those methods in remote.c too. Plus, if we have
target_follow_fork_inferior as an extra method in addition
to target_follow_fork_inferior, and both are always called in
succession, then we might as well _not_ introduce a new target hook,
and just call infrun_follow_fork_inferior from the
top of linux-nat.c:linux_child_follow_fork, and the top of whatever
other target's to_follow_fork method that wants to use it.
> The changes to inf_ptrace_follow_fork to get it to work with
> follow_fork_inferior are straightforward. Making those changes to
> inf_ttrace_follow_fork is problematic, primarily because it handles the
> moral equivalent of the vfork-done event differently.
Can you summarize the differences ?
> The changes
> required to make it work with follow_fork_inferior are more significant
> than I'd like, given that I don't have any way to test how the OS
> reports the events.
Don't worry so much about the testing. We can go best effort, and
if someone can help with testing, good. If not, I'd still push
forward and have interested parties fix things up when they
stumble on issues. I'm mostly interested in checking whether
the model we're committing to makes sense and is at the right level.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-09-09 11:09 ` Pedro Alves
@ 2014-09-12 16:50 ` Breazeal, Don
2014-09-22 15:53 ` Breazeal, Don
2014-09-26 18:13 ` Pedro Alves
0 siblings, 2 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-09-12 16:50 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 9/9/2014 4:09 AM, Pedro Alves wrote:
> On 09/09/2014 12:54 AM, Breazeal, Don wrote:
>> Hi Pedro,
>>
>> I've consolidated my responses to the issues you raised in this email,
>> and attached an updated patch containing the proposed solutions.
>>
>> On 9/5/2014 7:20 AM, Pedro Alves wrote:
>>> linux_child_follow_fork ends up with:
>>>
>>> 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 (!follow_child)
>>> {
>>> ...
>>> }
>>> else
>>> {
>>> struct lwp_info *child_lp;
>>>
>>> child_lp = add_lwp (inferior_ptid);
>>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>>> child_lp->stopped = 1;
>>> child_lp->last_resume_kind = resume_stop;
>>>
>>> /* Let the thread_db layer learn about this new process. */
>>> check_for_thread_db ();
>>> }
>>> }
>>>
>>> Nothing appears to switch inferior_ptid to the child, so seems
>>> like we're adding the child_lp with the wrong lwp (and calling
>>> check_for_thread_db in the wrong context) ? Is this managing
>>> to work by chance because follow_fork_inferior leaves inferior_ptid
>>> pointing to the child?
>>
>> Yes, follow_fork_inferior always sets inferior_ptid to the followed
>> inferior. Then on entry, linux_child_follow_fork expects inferior_ptid
>> to be the followed inferior. So I think it is getting the expected
>> inferior from inferior_ptid in these cases.
>>
>> In the original code, inferior_ptid was the parent on entry to
>> target_follow_fork, and the followed inferior after return. I made
>> follow_fork_inferior do the same thing (return the followed inferior),
>> but it would be no problem to have it return the parent and have
>> linux_child_follow_fork expect inferior_ptid to be the parent and return
>> the followed inferior (like it did before) if that would be preferable.
>> Let me know if you'd like me to make that change.
>>
>> Regarding check_for_thread_db, there is something unrelated that I don't
>> understand. Maybe you can clear this up for me. If we have reached
>> linux_child_follow_fork, then aren't we guaranteed that
>> PTRACE_O_TRACECLONE is supported, and that we are using that instead of
>> libthread_db for detecting thread events? If so, why do we need to call
>> check_for_thread_db at all?
>>
>> Then this at the top uses the wrong
>>> inferior_thread ():
>>>
>>> has_vforked = (inferior_thread ()->pending_follow.kind
>>> == TARGET_WAITKIND_VFORKED);
>>>
>>>
>>> and we're lucky that nothing end up using has_vforked in the
>>> follow child path?
>>
>> You are right, this is incorrect and unnecessary in the case where we
>> are following the child.
>>
>>>
>>> I'd much rather we don't have these assumptions in place.
>>
>> Would an acceptable solution be to move the definitions and assignments
>> of has_vforked, parent_pid, and child_pid into the follow-parent case,
>> as below?
This change was made per your previous comments, and comments added to
specify how inferior_ptid is changed by follow_fork_inferior and the
target follow_fork routines.
>>
>> static int
>> linux_child_follow_fork (struct target_ops *ops, int follow_child,
>> int detach_fork)
>> {
>> 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
>>>
>>> These files / targets also have to_follow_fork implementations:
>>>
>>> inf-ptrace.c: t->to_follow_fork = inf_ptrace_follow_fork;
>>> inf-ttrace.c: t->to_follow_fork = inf_ttrace_follow_fork;
>>>
>>> which will break if we don't adjust them as well. Did you
>>> check whether the refactored code (follow_fork_inferior)
>>> makes sense for those?
>>
>> I completely missed these; sorry about that. In my initial response to
>> your review, I thought I should be able to make similar changes to these
>> functions that maintained the existing functionality. After digging into
>> this, I still think it is possible, but that a more prudent approach
>> would be to make follow_fork_inferior into a new target hook that just
>> returns zero for the non-Linux cases.
>
> I'd rather not. That'll just add more technical debt. :-) E.g.,
> if we can't model this on the few native targets we have, then that'd
> indicate that remote debugging against one of those targets (when
> we get to gdb/gdbserver parity), wouldn't be correctly modelled, as
> you'd be installing those methods in remote.c too. Plus, if we have
> target_follow_fork_inferior as an extra method in addition
> to target_follow_fork_inferior, and both are always called in
> succession, then we might as well _not_ introduce a new target hook,
> and just call infrun_follow_fork_inferior from the
> top of linux-nat.c:linux_child_follow_fork, and the top of whatever
> other target's to_follow_fork method that wants to use it.
I've made modifications to inf_ttrace_follow_fork, inf_ttrace_wait,
and inf_ptrace_follow_fork in the included patch that I think should
allow them to work with follow_fork_inferior. Some other adjustments
were necessary in inf-ttrace.c. Some of the changes weren't strictly
necessary to get things working with follow_fork_inferior, but it
seemed appropriate to make all the implementations more consistent.
>
>> The changes to inf_ptrace_follow_fork to get it to work with
>> follow_fork_inferior are straightforward. Making those changes to
>> inf_ttrace_follow_fork is problematic, primarily because it handles the
>> moral equivalent of the vfork-done event differently.
>
> Can you summarize the differences ?
HP-UX apparently delivers a TTEVT_VFORK event for the parent inferior
where Linux would report PTRACE_EVENT_VFORK_DONE -- when the child
process has either execd or exited. inf_ttrace_follow_fork was handling
the parent VFORK event in-line, where the Linux implementation expects
a TARGET_WAITKIND_VFORK_DONE event to be reported so it can be handled
in the generic event code.
>
>> The changes
>> required to make it work with follow_fork_inferior are more significant
>> than I'd like, given that I don't have any way to test how the OS
>> reports the events.
>
> Don't worry so much about the testing. We can go best effort, and
> if someone can help with testing, good. If not, I'd still push
> forward and have interested parties fix things up when they
> stumble on issues. I'm mostly interested in checking whether
> the model we're committing to makes sense and is at the right level.
I haven't tested the changes to either of inf-ptrace.c or inf-ttrace.c,
or even built them, lacking a suitable environment. I did re-test on
x64 Ubuntu.
I've included annotated versions of the original implementations of
inf_ttrace_follow_fork and inf-ptrace_follow_fork below for reference,
to explain how I made decisions on the changes. Each annotation is
a comment beginning with "BLOCK n". If you want to skip past all of
that you can just search for "Don". :-)
>
> Thanks,
> Pedro Alves
>
My assumptions about how HP-UX ttrace events work:
- FORK:
- TTEVT_FORK is reported for both the parent and child processes.
The event can be reported for the parent or child in any order.
- VFORK:
- TTEVT_VFORK is reported for both the parent and child
processes.
- TTEVT_VFORK is reported for the child first. It is reported
for the parent when the vfork is "done" as with the Linux
PTRACE_EVENT_VFORK_DONE event, meaning that the parent has
called exec or exited. See this comment in inf_ttrace_follow_fork:
/* 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. */
The online HP-UX ttrace documentation doesn't really make this
ordering explicit, but it doesn't contradict the implementation.
===============================================
Annotated version of inf_ttrace_follow_fork:
/* 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;
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);
/* BLOCK 1: Since we know that on entry inferior_ptid will be the
ptid of the followed inferior, we can use that to derive the pid
information we need without any system calls. Delete this block,
use inferior_ptid for the child ptid when following the child, and
use inferior_thread ()->pending_follow to find the child ptid
when following the parent. */
/* 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;
}
/* BLOCK 1 END. */
if (follow_child)
{
/* BLOCK 2: this block is replaced almost verbatim by equivalent
code in follow_fork_inferior. We delete this block, except
for the line that switches inferior_ptid to the 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);
/* END BLOCK 2. */
/* BLOCK 3: inf_ttrace_follow_fork deals with breakpoints by
using detach_breakpoints on the unfollowed inferior, then just
before returning it calls detach_inferior to clean up all of the
data structures related to the inferior, including the breakpoint
structures associated withthe inferior. By contrast,
follow_fork_inferior calls remove_breakpoints_pid to clean up
the structures immediately, and it leaves the inferior intact
after detaching from it. This allows the user to go back and
run the inferior later. Note that follow_fork_inferior cleans up
the data structures for an unfollowed child, because from the user
standpoint it was never attached.
The bottom line is that we can delete this call, and the other
detach/reattach_breakpoints calls in this function. */
detach_breakpoints (ptid_build (pid, lwpid, 0));
/* END BLOCK 3. */
/* BLOCK 4: This output is replaced by code in follow_fork_inferior.
The only difference is that in follow_fork_inferior, the user must
enable the output via 'set verbose' or 'set debug infrun 1'. So
we delete this block. */
target_terminal_ours ();
fprintf_unfiltered (gdb_stdlog,
_("Attaching after fork to child process %ld.\n"),
(long)fpid);
/* END BLOCK 4. */
}
else
{
/* BLOCK 5: We don't need to do this, since we are guaranteed that
inferior_ptid already contained the parent's ptid on entry. So
we delete this. */
inferior_ptid = ptid_build (pid, lwpid, 0);
/* END BLOCK 5. */
/* BLOCK 6: As in BLOCK 3, removal and cleanup of breakpoints is
handled by follow_fork_inferior, and as in BLOCK 4, the output
is also handled by follow_fork_inferior. Delete this block. */
/* 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);
/* END BLOCK 6. */
}
if (tts.tts_event == TTEVT_VFORK)
{
gdb_assert (!tts.tts_u.tts_fork.tts_isparent);
if (follow_child)
{
/* BLOCK 7: This is the moral equivalent of struct
inferior::vfork_parent. When we report the vfork (child)
event to infrun.c, follow_fork_inferior will set up the
inferiors with all the info (vfork_parent, pending_detach,
vfork_child) required to know how and when to detach the
unfollowed vfork parent. In inf_ttrace_wait we will need
to report TARGET_WAITKIND_VFORK_DONE to infrun.c for
TTEVT_VFORK events reported for the parent. We can delete
this block and eliminate the variable entirely. */
/* We can't detach from the parent yet. */
inf_ttrace_vfork_ppid = pid;
/* END BLOCK 7. */
/* BLOCK 8: as before, let follow_fork_inferior manage the
breakpoints. Delete this. */
reattach_breakpoints (fpid);
/* END BLOCK 8. */
}
else
{
/* BLOCK 9: All of the target follow fork functions are
expected to perform the detach from an unfollowed fork
child. (Not just vfork.) We may be able to consolidate
with detach of regular fork child. */
if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1)
perror_with_name (("ttrace"));
/* END BLOCK 9. */
/* BLOCK 10: We want to let the generic event handling code
deal with this. Modify inf_ttrace_wait vfork event handler
to help with this. */
/* 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);
/* END BLOCK 10. */
/* BLOCK 11: as before, let follow_fork_inferior manage the
breakpoints. Delete this. */
reattach_breakpoints (pid);
/* END BLOCK 11. */
}
}
else
{
gdb_assert (tts.tts_u.tts_fork.tts_isparent);
if (follow_child)
{
/* BLOCK 12: follow_fork_inferior takes care of detaching the
parent when it calls target_detach. Delete this. */
if (ttrace (TT_PROC_DETACH, pid, 0, 0, 0, 0) == -1)
perror_with_name (("ttrace"));
/* END BLOCK 12. */
}
else
{
/* BLOCK 13: This is like BLOCK 9 - we need to detach all
unfollowed child processes. Keep this in some form. */
if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1)
perror_with_name (("ttrace"));
/* END BLOCK 13. */
}
}
if (follow_child)
{
struct thread_info *ti;
/* BLOCK 14: These variables are used throughout this file, so
we need to keep this. */
/* The child will start out single-threaded. */
inf_ttrace_num_lwps = 1;
inf_ttrace_num_lwps_in_syscall = 0;
/* END BLOCK 14. */
/* BLOCK 15: This is handled by follow_fork_inferior when it
calls target_detach on the parent, and we don't want to delete
the parent inferior, but leave it in the list for possible re-run
later on. */
/* 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);
/* END BLOCK 15. */
/* BLOCK 16: We need to keep this target-specific code. */
ti->private =
xmalloc (sizeof (struct inf_ttrace_private_thread_info));
memset (ti->private, 0,
sizeof (struct inf_ttrace_private_thread_info));
/* END BLOCK 16. */
}
return 0;
}
===============================================
Annotated version of inf_ptrace_follow_fork:
static int
inf_ptrace_follow_fork (struct target_ops *ops, int follow_child,
int detach_fork)
{
/* BLOCK 1: Since we know that on entry inferior_ptid will be the
ptid of the followed inferior, we can use that to derive the pid
information we need without any system calls. Delete this block,
use inferior_ptid for the child ptid when following the child, and
use inferior_thread ()->pending_follow to find the child ptid
when following the parent. */
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;
/* BLOCK 1. */
if (follow_child)
{
/* BLOCK 2: This is all done in follow_fork_inferior, so delete. */
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;
/* 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"));
/* END BLOCK 2 */
/* BLOCK 3: We must switch inferior_ptid to the child, so keep. */
/* Switch inferior_ptid out of the parent's way. */
inferior_ptid = pid_to_ptid (fpid);
/* END BLOCK 3. */
/* BLOCK 4: We don't detach the inferior, and the thread is added
by follow_fork_inferior. */
/* Delete the parent. */
detach_inferior (pid);
add_thread_silent (inferior_ptid);
/* END BLOCK 4. */
}
else
{
/* BLOCK 5: We are required to detach the child, so keep. */
/* Breakpoints have already been detached from the child by
infrun.c. */
if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1)
perror_with_name (("ptrace"));
/* END BLOCK 5. */
}
return 0;
}
=============================================
Thanks,
--Don
gdb/
2014-09-12 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.
* infrun.c (follow_fork): Call follow_fork_inferior.
(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.
---
gdb/inf-ptrace.c | 48 ++---------
gdb/inf-ttrace.c | 185 ++++++++--------------------------------
gdb/infrun.c | 250
+++++++++++++++++++++++++++++++++++++++++++++++++++++-
gdb/infrun.h | 2 -
gdb/linux-nat.c | 243
++++++----------------------------------------------
5 files changed, 314 insertions(+), 414 deletions(-)
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..3360ee7 100644
--- a/gdb/inf-ttrace.c
+++ b/gdb/inf-ttrace.c
@@ -403,128 +403,18 @@ inf_ttrace_stopped_by_watchpoint (struct
target_ops *ops)
}
\f
-/* 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,22 @@ 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 = find_thread_ptid (inferior_ptid);
+ gdb_assert (ti != NULL);
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 +556,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 +666,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,11 +715,12 @@ 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 (current_inferior ()->vfork_parent != NULL)
{
- if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
+ pid_t ppid = current_inferior ()->vfork_parent->pid;
+ if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1)
perror_with_name (("ttrace"));
- inf_ttrace_vfork_ppid = -1;
+ detach_inferior (ppid);
}
inf_ttrace_num_lwps = 0;
@@ -850,11 +744,12 @@ 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 (current_inferior ()->vfork_parent != NULL)
{
- if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
+ pid_t ppid = current_inferior ()->vfork_parent->pid;
+ if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1)
perror_with_name (("ttrace"));
- inf_ttrace_vfork_ppid = -1;
+ detach_inferior (ppid);
}
target_mourn_inferior ();
@@ -967,20 +862,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 +956,23 @@ 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);
+ if (tts.tts_u.tts_fork.tts_isparent)
+ {
+ if (current_inferior ()->waiting_fork_vfork_done)
+ 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);
- ourstatus->kind = TARGET_WAITKIND_VFORKED;
- ourstatus->value.related_pid = related_ptid;
+ ourstatus->kind = TARGET_WAITKIND_VFORKED;
+ ourstatus->value.related_pid = related_ptid;
- /* HACK: To avoid touching the parent during the vfork, switch
- away from it. */
- inferior_ptid = ptid;
+ /* HACK: To avoid touching the parent during the vfork, switch
+ away from it. */
+ inferior_ptid = ptid;
+ }
break;
case TTEVT_LWP_CREATE:
diff --git a/gdb/infrun.c b/gdb/infrun.c
index c18267f..af9cbf8 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);
@@ -486,9 +491,11 @@ 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_follow_fork (follow_child, detach_fork))
{
/* Target refused to follow, or there's some other reason
we shouldn't resume. */
@@ -560,7 +567,242 @@ follow_fork (void)
return should_resume;
}
-void
+/* 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. */
+
+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 (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 (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 0;
+}
+
+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 1e8991d..de4ccc2 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 ();
}
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-09-12 16:50 ` Breazeal, Don
@ 2014-09-22 15:53 ` Breazeal, Don
2014-09-26 18:13 ` Pedro Alves
1 sibling, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-09-22 15:53 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 9/12/2014 9:50 AM, Breazeal, Don wrote:
> On 9/9/2014 4:09 AM, Pedro Alves wrote:
>> On 09/09/2014 12:54 AM, Breazeal, Don wrote:
>>> Hi Pedro,
>>>
>>> I've consolidated my responses to the issues you raised in this email,
>>> and attached an updated patch containing the proposed solutions.
>>>
>>> On 9/5/2014 7:20 AM, Pedro Alves wrote:
>>>> linux_child_follow_fork ends up with:
>>>>
>>>> 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 (!follow_child)
>>>> {
>>>> ...
>>>> }
>>>> else
>>>> {
>>>> struct lwp_info *child_lp;
>>>>
>>>> child_lp = add_lwp (inferior_ptid);
>>>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>>>> child_lp->stopped = 1;
>>>> child_lp->last_resume_kind = resume_stop;
>>>>
>>>> /* Let the thread_db layer learn about this new process. */
>>>> check_for_thread_db ();
>>>> }
>>>> }
>>>>
>>>> Nothing appears to switch inferior_ptid to the child, so seems
>>>> like we're adding the child_lp with the wrong lwp (and calling
>>>> check_for_thread_db in the wrong context) ? Is this managing
>>>> to work by chance because follow_fork_inferior leaves inferior_ptid
>>>> pointing to the child?
>>>
>>> Yes, follow_fork_inferior always sets inferior_ptid to the followed
>>> inferior. Then on entry, linux_child_follow_fork expects inferior_ptid
>>> to be the followed inferior. So I think it is getting the expected
>>> inferior from inferior_ptid in these cases.
>>>
>>> In the original code, inferior_ptid was the parent on entry to
>>> target_follow_fork, and the followed inferior after return. I made
>>> follow_fork_inferior do the same thing (return the followed inferior),
>>> but it would be no problem to have it return the parent and have
>>> linux_child_follow_fork expect inferior_ptid to be the parent and return
>>> the followed inferior (like it did before) if that would be preferable.
>>> Let me know if you'd like me to make that change.
>>>
>>> Regarding check_for_thread_db, there is something unrelated that I don't
>>> understand. Maybe you can clear this up for me. If we have reached
>>> linux_child_follow_fork, then aren't we guaranteed that
>>> PTRACE_O_TRACECLONE is supported, and that we are using that instead of
>>> libthread_db for detecting thread events? If so, why do we need to call
>>> check_for_thread_db at all?
>>>
>>> Then this at the top uses the wrong
>>>> inferior_thread ():
>>>>
>>>> has_vforked = (inferior_thread ()->pending_follow.kind
>>>> == TARGET_WAITKIND_VFORKED);
>>>>
>>>>
>>>> and we're lucky that nothing end up using has_vforked in the
>>>> follow child path?
>>>
>>> You are right, this is incorrect and unnecessary in the case where we
>>> are following the child.
>>>
>>>>
>>>> I'd much rather we don't have these assumptions in place.
>>>
>>> Would an acceptable solution be to move the definitions and assignments
>>> of has_vforked, parent_pid, and child_pid into the follow-parent case,
>>> as below?
>
> This change was made per your previous comments, and comments added to
> specify how inferior_ptid is changed by follow_fork_inferior and the
> target follow_fork routines.
>
>>>
>>> static int
>>> linux_child_follow_fork (struct target_ops *ops, int follow_child,
>>> int detach_fork)
>>> {
>>> 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
>>>>
>>>> These files / targets also have to_follow_fork implementations:
>>>>
>>>> inf-ptrace.c: t->to_follow_fork = inf_ptrace_follow_fork;
>>>> inf-ttrace.c: t->to_follow_fork = inf_ttrace_follow_fork;
>>>>
>>>> which will break if we don't adjust them as well. Did you
>>>> check whether the refactored code (follow_fork_inferior)
>>>> makes sense for those?
>>>
>>> I completely missed these; sorry about that. In my initial response to
>>> your review, I thought I should be able to make similar changes to these
>>> functions that maintained the existing functionality. After digging into
>>> this, I still think it is possible, but that a more prudent approach
>>> would be to make follow_fork_inferior into a new target hook that just
>>> returns zero for the non-Linux cases.
>>
>> I'd rather not. That'll just add more technical debt. :-) E.g.,
>> if we can't model this on the few native targets we have, then that'd
>> indicate that remote debugging against one of those targets (when
>> we get to gdb/gdbserver parity), wouldn't be correctly modelled, as
>> you'd be installing those methods in remote.c too. Plus, if we have
>> target_follow_fork_inferior as an extra method in addition
>> to target_follow_fork_inferior, and both are always called in
>> succession, then we might as well _not_ introduce a new target hook,
>> and just call infrun_follow_fork_inferior from the
>> top of linux-nat.c:linux_child_follow_fork, and the top of whatever
>> other target's to_follow_fork method that wants to use it.
>
> I've made modifications to inf_ttrace_follow_fork, inf_ttrace_wait,
> and inf_ptrace_follow_fork in the included patch that I think should
> allow them to work with follow_fork_inferior. Some other adjustments
> were necessary in inf-ttrace.c. Some of the changes weren't strictly
> necessary to get things working with follow_fork_inferior, but it
> seemed appropriate to make all the implementations more consistent.
>
>>
>>> The changes to inf_ptrace_follow_fork to get it to work with
>>> follow_fork_inferior are straightforward. Making those changes to
>>> inf_ttrace_follow_fork is problematic, primarily because it handles the
>>> moral equivalent of the vfork-done event differently.
>>
>> Can you summarize the differences ?
>
> HP-UX apparently delivers a TTEVT_VFORK event for the parent inferior
> where Linux would report PTRACE_EVENT_VFORK_DONE -- when the child
> process has either execd or exited. inf_ttrace_follow_fork was handling
> the parent VFORK event in-line, where the Linux implementation expects
> a TARGET_WAITKIND_VFORK_DONE event to be reported so it can be handled
> in the generic event code.
>
>>
>>> The changes
>>> required to make it work with follow_fork_inferior are more significant
>>> than I'd like, given that I don't have any way to test how the OS
>>> reports the events.
>>
>> Don't worry so much about the testing. We can go best effort, and
>> if someone can help with testing, good. If not, I'd still push
>> forward and have interested parties fix things up when they
>> stumble on issues. I'm mostly interested in checking whether
>> the model we're committing to makes sense and is at the right level.
>
> I haven't tested the changes to either of inf-ptrace.c or inf-ttrace.c,
> or even built them, lacking a suitable environment. I did re-test on
> x64 Ubuntu.
>
> I've included annotated versions of the original implementations of
> inf_ttrace_follow_fork and inf-ptrace_follow_fork below for reference,
> to explain how I made decisions on the changes. Each annotation is
> a comment beginning with "BLOCK n". If you want to skip past all of
> that you can just search for "Don". :-)
>
>>
>> Thanks,
>> Pedro Alves
>>
>
> My assumptions about how HP-UX ttrace events work:
> - FORK:
> - TTEVT_FORK is reported for both the parent and child processes.
> The event can be reported for the parent or child in any order.
>
> - VFORK:
> - TTEVT_VFORK is reported for both the parent and child
> processes.
> - TTEVT_VFORK is reported for the child first. It is reported
> for the parent when the vfork is "done" as with the Linux
> PTRACE_EVENT_VFORK_DONE event, meaning that the parent has
> called exec or exited. See this comment in inf_ttrace_follow_fork:
>
> /* 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. */
>
> The online HP-UX ttrace documentation doesn't really make this
> ordering explicit, but it doesn't contradict the implementation.
>
> ===============================================
> Annotated version of inf_ttrace_follow_fork:
>
> /* 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;
>
> 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);
>
> /* BLOCK 1: Since we know that on entry inferior_ptid will be the
> ptid of the followed inferior, we can use that to derive the pid
> information we need without any system calls. Delete this block,
> use inferior_ptid for the child ptid when following the child, and
> use inferior_thread ()->pending_follow to find the child ptid
> when following the parent. */
> /* 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;
> }
> /* BLOCK 1 END. */
>
> if (follow_child)
> {
> /* BLOCK 2: this block is replaced almost verbatim by equivalent
> code in follow_fork_inferior. We delete this block, except
> for the line that switches inferior_ptid to the 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);
> /* END BLOCK 2. */
>
> /* BLOCK 3: inf_ttrace_follow_fork deals with breakpoints by
> using detach_breakpoints on the unfollowed inferior, then just
> before returning it calls detach_inferior to clean up all of the
> data structures related to the inferior, including the breakpoint
> structures associated withthe inferior. By contrast,
> follow_fork_inferior calls remove_breakpoints_pid to clean up
> the structures immediately, and it leaves the inferior intact
> after detaching from it. This allows the user to go back and
> run the inferior later. Note that follow_fork_inferior cleans up
> the data structures for an unfollowed child, because from the user
> standpoint it was never attached.
>
> The bottom line is that we can delete this call, and the other
> detach/reattach_breakpoints calls in this function. */
> detach_breakpoints (ptid_build (pid, lwpid, 0));
> /* END BLOCK 3. */
>
> /* BLOCK 4: This output is replaced by code in follow_fork_inferior.
> The only difference is that in follow_fork_inferior, the user must
> enable the output via 'set verbose' or 'set debug infrun 1'. So
> we delete this block. */
> target_terminal_ours ();
> fprintf_unfiltered (gdb_stdlog,
> _("Attaching after fork to child process %ld.\n"),
> (long)fpid);
> /* END BLOCK 4. */
> }
> else
> {
> /* BLOCK 5: We don't need to do this, since we are guaranteed that
> inferior_ptid already contained the parent's ptid on entry. So
> we delete this. */
> inferior_ptid = ptid_build (pid, lwpid, 0);
> /* END BLOCK 5. */
>
> /* BLOCK 6: As in BLOCK 3, removal and cleanup of breakpoints is
> handled by follow_fork_inferior, and as in BLOCK 4, the output
> is also handled by follow_fork_inferior. Delete this block. */
> /* 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);
> /* END BLOCK 6. */
> }
>
> if (tts.tts_event == TTEVT_VFORK)
> {
> gdb_assert (!tts.tts_u.tts_fork.tts_isparent);
>
> if (follow_child)
> {
> /* BLOCK 7: This is the moral equivalent of struct
> inferior::vfork_parent. When we report the vfork (child)
> event to infrun.c, follow_fork_inferior will set up the
> inferiors with all the info (vfork_parent, pending_detach,
> vfork_child) required to know how and when to detach the
> unfollowed vfork parent. In inf_ttrace_wait we will need
> to report TARGET_WAITKIND_VFORK_DONE to infrun.c for
> TTEVT_VFORK events reported for the parent. We can delete
> this block and eliminate the variable entirely. */
> /* We can't detach from the parent yet. */
> inf_ttrace_vfork_ppid = pid;
> /* END BLOCK 7. */
>
> /* BLOCK 8: as before, let follow_fork_inferior manage the
> breakpoints. Delete this. */
> reattach_breakpoints (fpid);
> /* END BLOCK 8. */
> }
> else
> {
> /* BLOCK 9: All of the target follow fork functions are
> expected to perform the detach from an unfollowed fork
> child. (Not just vfork.) We may be able to consolidate
> with detach of regular fork child. */
> if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1)
> perror_with_name (("ttrace"));
> /* END BLOCK 9. */
>
> /* BLOCK 10: We want to let the generic event handling code
> deal with this. Modify inf_ttrace_wait vfork event handler
> to help with this. */
> /* 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);
> /* END BLOCK 10. */
>
> /* BLOCK 11: as before, let follow_fork_inferior manage the
> breakpoints. Delete this. */
> reattach_breakpoints (pid);
> /* END BLOCK 11. */
> }
> }
> else
> {
> gdb_assert (tts.tts_u.tts_fork.tts_isparent);
>
> if (follow_child)
> {
> /* BLOCK 12: follow_fork_inferior takes care of detaching the
> parent when it calls target_detach. Delete this. */
> if (ttrace (TT_PROC_DETACH, pid, 0, 0, 0, 0) == -1)
> perror_with_name (("ttrace"));
> /* END BLOCK 12. */
> }
> else
> {
> /* BLOCK 13: This is like BLOCK 9 - we need to detach all
> unfollowed child processes. Keep this in some form. */
> if (ttrace (TT_PROC_DETACH, fpid, 0, 0, 0, 0) == -1)
> perror_with_name (("ttrace"));
> /* END BLOCK 13. */
> }
> }
>
> if (follow_child)
> {
> struct thread_info *ti;
>
> /* BLOCK 14: These variables are used throughout this file, so
> we need to keep this. */
> /* The child will start out single-threaded. */
> inf_ttrace_num_lwps = 1;
> inf_ttrace_num_lwps_in_syscall = 0;
> /* END BLOCK 14. */
>
> /* BLOCK 15: This is handled by follow_fork_inferior when it
> calls target_detach on the parent, and we don't want to delete
> the parent inferior, but leave it in the list for possible re-run
> later on. */
> /* 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);
> /* END BLOCK 15. */
>
> /* BLOCK 16: We need to keep this target-specific code. */
> ti->private =
> xmalloc (sizeof (struct inf_ttrace_private_thread_info));
> memset (ti->private, 0,
> sizeof (struct inf_ttrace_private_thread_info));
> /* END BLOCK 16. */
> }
>
> return 0;
> }
>
> ===============================================
> Annotated version of inf_ptrace_follow_fork:
>
> static int
> inf_ptrace_follow_fork (struct target_ops *ops, int follow_child,
> int detach_fork)
> {
> /* BLOCK 1: Since we know that on entry inferior_ptid will be the
> ptid of the followed inferior, we can use that to derive the pid
> information we need without any system calls. Delete this block,
> use inferior_ptid for the child ptid when following the child, and
> use inferior_thread ()->pending_follow to find the child ptid
> when following the parent. */
> 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;
> /* BLOCK 1. */
>
> if (follow_child)
> {
> /* BLOCK 2: This is all done in follow_fork_inferior, so delete. */
> 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;
>
> /* 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"));
> /* END BLOCK 2 */
>
> /* BLOCK 3: We must switch inferior_ptid to the child, so keep. */
> /* Switch inferior_ptid out of the parent's way. */
> inferior_ptid = pid_to_ptid (fpid);
> /* END BLOCK 3. */
>
> /* BLOCK 4: We don't detach the inferior, and the thread is added
> by follow_fork_inferior. */
> /* Delete the parent. */
> detach_inferior (pid);
>
> add_thread_silent (inferior_ptid);
> /* END BLOCK 4. */
> }
> else
> {
> /* BLOCK 5: We are required to detach the child, so keep. */
> /* Breakpoints have already been detached from the child by
> infrun.c. */
>
> if (ptrace (PT_DETACH, fpid, (PTRACE_TYPE_ARG3)1, 0) == -1)
> perror_with_name (("ptrace"));
> /* END BLOCK 5. */
> }
>
> return 0;
> }
>
> =============================================
>
> Thanks,
> --Don
>
> gdb/
> 2014-09-12 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.
> * infrun.c (follow_fork): Call follow_fork_inferior.
> (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.
>
> ---
> gdb/inf-ptrace.c | 48 ++---------
> gdb/inf-ttrace.c | 185 ++++++++--------------------------------
> gdb/infrun.c | 250
> +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> gdb/infrun.h | 2 -
> gdb/linux-nat.c | 243
> ++++++----------------------------------------------
> 5 files changed, 314 insertions(+), 414 deletions(-)
>
> 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..3360ee7 100644
> --- a/gdb/inf-ttrace.c
> +++ b/gdb/inf-ttrace.c
> @@ -403,128 +403,18 @@ inf_ttrace_stopped_by_watchpoint (struct
> target_ops *ops)
> }
> \f
>
> -/* 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,22 @@ 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 = find_thread_ptid (inferior_ptid);
> + gdb_assert (ti != NULL);
> 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 +556,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 +666,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,11 +715,12 @@ 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 (current_inferior ()->vfork_parent != NULL)
> {
> - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
> + pid_t ppid = current_inferior ()->vfork_parent->pid;
> + if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1)
> perror_with_name (("ttrace"));
> - inf_ttrace_vfork_ppid = -1;
> + detach_inferior (ppid);
> }
>
> inf_ttrace_num_lwps = 0;
> @@ -850,11 +744,12 @@ 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 (current_inferior ()->vfork_parent != NULL)
> {
> - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
> + pid_t ppid = current_inferior ()->vfork_parent->pid;
> + if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1)
> perror_with_name (("ttrace"));
> - inf_ttrace_vfork_ppid = -1;
> + detach_inferior (ppid);
> }
>
> target_mourn_inferior ();
> @@ -967,20 +862,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 +956,23 @@ 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);
> + if (tts.tts_u.tts_fork.tts_isparent)
> + {
> + if (current_inferior ()->waiting_fork_vfork_done)
> + 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);
>
> - ourstatus->kind = TARGET_WAITKIND_VFORKED;
> - ourstatus->value.related_pid = related_ptid;
> + ourstatus->kind = TARGET_WAITKIND_VFORKED;
> + ourstatus->value.related_pid = related_ptid;
>
> - /* HACK: To avoid touching the parent during the vfork, switch
> - away from it. */
> - inferior_ptid = ptid;
> + /* HACK: To avoid touching the parent during the vfork, switch
> + away from it. */
> + inferior_ptid = ptid;
> + }
> break;
>
> case TTEVT_LWP_CREATE:
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index c18267f..af9cbf8 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);
>
> @@ -486,9 +491,11 @@ 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_follow_fork (follow_child, detach_fork))
> {
> /* Target refused to follow, or there's some other reason
> we shouldn't resume. */
> @@ -560,7 +567,242 @@ follow_fork (void)
> return should_resume;
> }
>
> -void
> +/* 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. */
> +
> +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 (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 (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 0;
> +}
> +
> +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 1e8991d..de4ccc2 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 ();
> }
>
Ping!
Thanks,
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-09-12 16:50 ` Breazeal, Don
2014-09-22 15:53 ` Breazeal, Don
@ 2014-09-26 18:13 ` Pedro Alves
2014-09-29 18:08 ` Breazeal, Don
1 sibling, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-09-26 18:13 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
On 09/12/2014 05:50 PM, Breazeal, Don wrote:
> On 9/9/2014 4:09 AM, Pedro Alves wrote:
>> On 09/09/2014 12:54 AM, Breazeal, Don wrote:
>> I'd rather not. That'll just add more technical debt. :-) E.g.,
>> if we can't model this on the few native targets we have, then that'd
>> indicate that remote debugging against one of those targets (when
>> we get to gdb/gdbserver parity), wouldn't be correctly modelled, as
>> you'd be installing those methods in remote.c too. Plus, if we have
>> target_follow_fork_inferior as an extra method in addition
>> to target_follow_fork_inferior, and both are always called in
>> succession, then we might as well _not_ introduce a new target hook,
>> and just call infrun_follow_fork_inferior from the
>> top of linux-nat.c:linux_child_follow_fork, and the top of whatever
>> other target's to_follow_fork method that wants to use it.
>
> I've made modifications to inf_ttrace_follow_fork, inf_ttrace_wait,
> and inf_ptrace_follow_fork in the included patch that I think should
> allow them to work with follow_fork_inferior. Some other adjustments
> were necessary in inf-ttrace.c. Some of the changes weren't strictly
> necessary to get things working with follow_fork_inferior, but it
> seemed appropriate to make all the implementations more consistent.
Thanks.
>>> The changes to inf_ptrace_follow_fork to get it to work with
>>> follow_fork_inferior are straightforward. Making those changes to
>>> inf_ttrace_follow_fork is problematic, primarily because it handles the
>>> moral equivalent of the vfork-done event differently.
>>
>> Can you summarize the differences ?
>
> HP-UX apparently delivers a TTEVT_VFORK event for the parent inferior
> where Linux would report PTRACE_EVENT_VFORK_DONE -- when the child
> process has either execd or exited. inf_ttrace_follow_fork was handling
> the parent VFORK event in-line, where the Linux implementation expects
> a TARGET_WAITKIND_VFORK_DONE event to be reported so it can be handled
> in the generic event code.
>
Right. Linux also used to handle that in-line, before 6c95b8df.
> I've included annotated versions of the original implementations of
> inf_ttrace_follow_fork and inf-ptrace_follow_fork below for reference,
> to explain how I made decisions on the changes. Each annotation is
> a comment beginning with "BLOCK n". If you want to skip past all of
> that you can just search for "Don". :-)
Excellent. That sure made it easier.
>>
>
> My assumptions about how HP-UX ttrace events work:
> - FORK:
> - TTEVT_FORK is reported for both the parent and child processes.
> The event can be reported for the parent or child in any order.
>
> - VFORK:
> - TTEVT_VFORK is reported for both the parent and child
> processes.
> - TTEVT_VFORK is reported for the child first. It is reported
> for the parent when the vfork is "done" as with the Linux
> PTRACE_EVENT_VFORK_DONE event, meaning that the parent has
> called exec or exited. See this comment in inf_ttrace_follow_fork:
>
> /* 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. */
>
> The online HP-UX ttrace documentation doesn't really make this
> ordering explicit, but it doesn't contradict the implementation.
Yeah, I can't imagine any way a TTEVT_VFORK could ever be reported to
the parent first. When the parent vforks, it blocks until the child
execs or exits. And the child won't do that until GDB resumes
the child after handling the TTEVT_VFORK that is reported for the
child. So the kernel must always report the child's TTEVT_VFORK first.
> if (follow_child)
> {
> struct thread_info *ti;
> @@ -533,17 +423,22 @@ 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 = find_thread_ptid (inferior_ptid);
> + gdb_assert (ti != NULL);
Replace these with:
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 +556,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 +666,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,11 +715,12 @@ 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 (current_inferior ()->vfork_parent != NULL)
> {
> - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
> + pid_t ppid = current_inferior ()->vfork_parent->pid;
> + if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1)
> perror_with_name (("ttrace"));
> - inf_ttrace_vfork_ppid = -1;
> + detach_inferior (ppid);
> }
I think we should just delete this block instead.
We shouldn't be blindly detaching from the parent here.
The user may well still want to debug it. The case of the
user doing "detach" on the child when the parent is waiting
for the the vfork-done should be handled by common code.
(and we should be going through the whole target_detach).
>
> inf_ttrace_num_lwps = 0;
> @@ -850,11 +744,12 @@ 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 (current_inferior ()->vfork_parent != NULL)
> {
> - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
> + pid_t ppid = current_inferior ()->vfork_parent->pid;
> + if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1)
> perror_with_name (("ttrace"));
> - inf_ttrace_vfork_ppid = -1;
> + detach_inferior (ppid);
> }
This too.
>
> target_mourn_inferior ();
> @@ -967,20 +862,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 +956,23 @@ 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);
> + if (tts.tts_u.tts_fork.tts_isparent)
> + {
> + if (current_inferior ()->waiting_fork_vfork_done)
I don't think we should have this waiting_fork_vfork_done check here.
This should always be a TARGET_WAITKIND_VFORK_DONE?
> + 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);
>
> - ourstatus->kind = TARGET_WAITKIND_VFORKED;
> - ourstatus->value.related_pid = related_ptid;
> + ourstatus->kind = TARGET_WAITKIND_VFORKED;
> + ourstatus->value.related_pid = related_ptid;
>
> - /* HACK: To avoid touching the parent during the vfork, switch
> - away from it. */
> - inferior_ptid = ptid;
> + /* HACK: To avoid touching the parent during the vfork, switch
> + away from it. */
> + inferior_ptid = ptid;
The core is now aware of the vfork parent inferior. Just delete
this hack.
> + }
> break;
>
> case TTEVT_LWP_CREATE:
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index c18267f..af9cbf8 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);
>
> @@ -486,9 +491,11 @@ 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_follow_fork (follow_child, detach_fork))
I don't think we should ever call follow_fork_inferior without
calling target_follow_fork, right? I think it'd be clearer if
we tail called target_follow_fork inside follow_fork_inferior.
> {
> /* Target refused to follow, or there's some other reason
> we shouldn't resume. */
> @@ -560,7 +567,242 @@ follow_fork (void)
> return should_resume;
> }
>
> -void
> +/* 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. */
> +
> +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;
> + }
> +
Moving this to common code isn't strictly correct, as we'd probably
be able to interrupt the vfork parent in this case when remote
debugging, or not sharing the terminal with the inferior. But let's
put this here for now.
> + 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
It's not always solib-svr4, so make that e.g., "e.g., solib-svr4" now.
> + 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
Likewise.
Otherwise this looks good to me.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-09-26 18:13 ` Pedro Alves
@ 2014-09-29 18:08 ` Breazeal, Don
2014-09-30 10:56 ` Pedro Alves
0 siblings, 1 reply; 110+ messages in thread
From: Breazeal, Don @ 2014-09-29 18:08 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 9/26/2014 11:13 AM, Pedro Alves wrote:
> On 09/12/2014 05:50 PM, Breazeal, Don wrote:
>> On 9/9/2014 4:09 AM, Pedro Alves wrote:
>>> On 09/09/2014 12:54 AM, Breazeal, Don wrote:
>
>>> I'd rather not. That'll just add more technical debt. :-) E.g.,
>>> if we can't model this on the few native targets we have, then that'd
>>> indicate that remote debugging against one of those targets (when
>>> we get to gdb/gdbserver parity), wouldn't be correctly modelled, as
>>> you'd be installing those methods in remote.c too. Plus, if we have
>>> target_follow_fork_inferior as an extra method in addition
>>> to target_follow_fork_inferior, and both are always called in
>>> succession, then we might as well _not_ introduce a new target hook,
>>> and just call infrun_follow_fork_inferior from the
>>> top of linux-nat.c:linux_child_follow_fork, and the top of whatever
>>> other target's to_follow_fork method that wants to use it.
>>
>> I've made modifications to inf_ttrace_follow_fork, inf_ttrace_wait,
>> and inf_ptrace_follow_fork in the included patch that I think should
>> allow them to work with follow_fork_inferior. Some other adjustments
>> were necessary in inf-ttrace.c. Some of the changes weren't strictly
>> necessary to get things working with follow_fork_inferior, but it
>> seemed appropriate to make all the implementations more consistent.
>
> Thanks.
>
>>>> The changes to inf_ptrace_follow_fork to get it to work with
>>>> follow_fork_inferior are straightforward. Making those changes to
>>>> inf_ttrace_follow_fork is problematic, primarily because it handles the
>>>> moral equivalent of the vfork-done event differently.
>>>
>>> Can you summarize the differences ?
>>
>> HP-UX apparently delivers a TTEVT_VFORK event for the parent inferior
>> where Linux would report PTRACE_EVENT_VFORK_DONE -- when the child
>> process has either execd or exited. inf_ttrace_follow_fork was handling
>> the parent VFORK event in-line, where the Linux implementation expects
>> a TARGET_WAITKIND_VFORK_DONE event to be reported so it can be handled
>> in the generic event code.
>>
>
> Right. Linux also used to handle that in-line, before 6c95b8df.
>
>> I've included annotated versions of the original implementations of
>> inf_ttrace_follow_fork and inf-ptrace_follow_fork below for reference,
>> to explain how I made decisions on the changes. Each annotation is
>> a comment beginning with "BLOCK n". If you want to skip past all of
>> that you can just search for "Don". :-)
>
> Excellent. That sure made it easier.
>
>
>>>
>>
>> My assumptions about how HP-UX ttrace events work:
>> - FORK:
>> - TTEVT_FORK is reported for both the parent and child processes.
>> The event can be reported for the parent or child in any order.
>>
>> - VFORK:
>> - TTEVT_VFORK is reported for both the parent and child
>> processes.
>> - TTEVT_VFORK is reported for the child first. It is reported
>> for the parent when the vfork is "done" as with the Linux
>> PTRACE_EVENT_VFORK_DONE event, meaning that the parent has
>> called exec or exited. See this comment in inf_ttrace_follow_fork:
>>
>> /* 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. */
>>
>> The online HP-UX ttrace documentation doesn't really make this
>> ordering explicit, but it doesn't contradict the implementation.
>
> Yeah, I can't imagine any way a TTEVT_VFORK could ever be reported to
> the parent first. When the parent vforks, it blocks until the child
> execs or exits. And the child won't do that until GDB resumes
> the child after handling the TTEVT_VFORK that is reported for the
> child. So the kernel must always report the child's TTEVT_VFORK first.
>
>
>> if (follow_child)
>> {
>> struct thread_info *ti;
>> @@ -533,17 +423,22 @@ 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 = find_thread_ptid (inferior_ptid);
>> + gdb_assert (ti != NULL);
>
> Replace these with:
>
> ti = inferior_thread ();
Done.
>
>> 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 +556,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 +666,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,11 +715,12 @@ 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 (current_inferior ()->vfork_parent != NULL)
>> {
>> - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
>> + pid_t ppid = current_inferior ()->vfork_parent->pid;
>> + if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1)
>> perror_with_name (("ttrace"));
>> - inf_ttrace_vfork_ppid = -1;
>> + detach_inferior (ppid);
>> }
>
> I think we should just delete this block instead.
> We shouldn't be blindly detaching from the parent here.
> The user may well still want to debug it. The case of the
> user doing "detach" on the child when the parent is waiting
> for the the vfork-done should be handled by common code.
> (and we should be going through the whole target_detach).
>
Done.
>>
>> inf_ttrace_num_lwps = 0;
>> @@ -850,11 +744,12 @@ 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 (current_inferior ()->vfork_parent != NULL)
>> {
>> - if (ttrace (TT_PROC_DETACH, inf_ttrace_vfork_ppid, 0, 0, 0, 0) == -1)
>> + pid_t ppid = current_inferior ()->vfork_parent->pid;
>> + if (ttrace (TT_PROC_DETACH, ppid, 0, 0, 0, 0) == -1)
>> perror_with_name (("ttrace"));
>> - inf_ttrace_vfork_ppid = -1;
>> + detach_inferior (ppid);
>> }
>
> This too.
>
Done.
>>
>> target_mourn_inferior ();
>> @@ -967,20 +862,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 +956,23 @@ 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);
>> + if (tts.tts_u.tts_fork.tts_isparent)
>> + {
>> + if (current_inferior ()->waiting_fork_vfork_done)
>
> I don't think we should have this waiting_fork_vfork_done check here.
> This should always be a TARGET_WAITKIND_VFORK_DONE?
You are right, at best this check was redundant. Removed it.
>
>> + 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);
>>
>> - ourstatus->kind = TARGET_WAITKIND_VFORKED;
>> - ourstatus->value.related_pid = related_ptid;
>> + ourstatus->kind = TARGET_WAITKIND_VFORKED;
>> + ourstatus->value.related_pid = related_ptid;
>>
>> - /* HACK: To avoid touching the parent during the vfork, switch
>> - away from it. */
>> - inferior_ptid = ptid;
>> + /* HACK: To avoid touching the parent during the vfork, switch
>> + away from it. */
>> + inferior_ptid = ptid;
>
> The core is now aware of the vfork parent inferior. Just delete
> this hack.
Done.
>
>> + }
>> break;
>>
>> case TTEVT_LWP_CREATE:
>> diff --git a/gdb/infrun.c b/gdb/infrun.c
>> index c18267f..af9cbf8 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);
>>
>> @@ -486,9 +491,11 @@ 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_follow_fork (follow_child, detach_fork))
>
> I don't think we should ever call follow_fork_inferior without
> calling target_follow_fork, right? I think it'd be clearer if
> we tail called target_follow_fork inside follow_fork_inferior.
Done.
>
>> {
>> /* Target refused to follow, or there's some other reason
>> we shouldn't resume. */
>> @@ -560,7 +567,242 @@ follow_fork (void)
>> return should_resume;
>> }
>>
>> -void
>> +/* 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. */
>> +
>> +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;
>> + }
>> +
>
> Moving this to common code isn't strictly correct, as we'd probably
> be able to interrupt the vfork parent in this case when remote
> debugging, or not sharing the terminal with the inferior. But let's
> put this here for now.
>
>
>> + 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
>
> It's not always solib-svr4, so make that e.g., "e.g., solib-svr4" now.
Done.
>
>> + 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
>
> Likewise.
Done.
>
> Otherwise this looks good to me.
Pedro, thanks for this review. The updated patch is included below.
I have one remaining question: I took this (above) to mean that the patch
was approved with the requested changes, but since this is part of a series
I'm not sure if I have the go-ahead to push it. The patch is standalone
and I believe it has value independent of the rest of the series.
OK to push?
Thanks
--Don
gdb/
2014-09-26 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.
---
gdb/inf-ptrace.c | 48 ++---------
gdb/inf-ttrace.c | 179 +++++----------------------------------
gdb/infrun.c | 249
+++++++++++++++++++++++++++++++++++++++++++++++++++++-
gdb/infrun.h | 2 -
gdb/linux-nat.c | 243
++++++-----------------------------------------------
5 files changed, 298 insertions(+), 423 deletions(-)
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)
}
\f
-/* 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..e8b1f01 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);
@@ -486,9 +491,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 +566,242 @@ follow_fork (void)
return should_resume;
}
-void
+/* 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. */
+
+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);
+}
+
+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 0fe4b0b..df830ae 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 ();
}
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-09-29 18:08 ` Breazeal, Don
@ 2014-09-30 10:56 ` Pedro Alves
2014-09-30 18:43 ` Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-09-30 10:56 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
On 09/29/2014 07:07 PM, Breazeal, Don wrote:
> The patch is standalone and I believe it has value independent
> of the rest of the series.
Yes, agreed.
> OK to push?
Yes, please push.
> +/* 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. */
> +
> +int
'static int' here too.
Consider putting the function above its caller thus avoiding
the need for the other declaration at the top.
> +follow_fork_inferior (int follow_child, int detach_fork)
> +{
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 01/16 v2] Refactor native follow-fork
2014-09-30 10:56 ` Pedro Alves
@ 2014-09-30 18:43 ` Breazeal, Don
0 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-09-30 18:43 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 9/30/2014 3:56 AM, Pedro Alves wrote:
> On 09/29/2014 07:07 PM, Breazeal, Don wrote:
>
>> The patch is standalone and I believe it has value independent
>> of the rest of the series.
>
> Yes, agreed.
>
>> OK to push?
>
> Yes, please push.
>
>> +/* 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. */
>> +
>> +int
>
> 'static int' here too.
>
> Consider putting the function above its caller thus avoiding
> the need for the other declaration at the top.
>
>> +follow_fork_inferior (int follow_child, int detach_fork)
>> +{
>
> Thanks,
> Pedro Alves
>
Patch is pushed with changes.
Thanks,
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 03/16 v2] Refactor ptrace extended event status
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (11 preceding siblings ...)
2014-08-21 0:29 ` [PATCH 01/16 v2] Refactor native follow-fork Don Breazeal
@ 2014-08-21 0:30 ` Don Breazeal
2014-09-09 11:31 ` Pedro Alves
2014-08-21 0:30 ` [PATCH 04/16 v2] Determine supported extended-remote features Don Breazeal
` (13 subsequent siblings)
26 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:30 UTC (permalink / raw)
To: gdb-patches
This patch implements functions for identifying and extracting extended
ptrace event information from a Linux wait status. These are just
convenience functions intended to hide the ">> 16" used to extract the
event from the wait status word, replacing the hard-coded shift with a more
descriptive function call. This is preparatory work for implementation of
follow-fork and detach-on-fork for extended-remote linux targets.
The functions linux_is_extended_waitstatus and
linux_ptrace_get_extended_event are defined in nat/linux-ptrace.c, and
called in linux-nat.c and gdbserver/linux-low.c.
My initial approach was to implement predicates for every extended event,
e.g. linux_is_traced_clone (status), linux_is_traced_fork (status), but
that didn't fit the current implementation as well, bloated the code a bit,
and didn't add anything to readability, so I went with just extracting the
event bits from the status instead.
Tested on x64 Ubuntu Lucid, native only.
Thanks
--Don
gdb/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* linux-nat.c (linux_handle_extended_wait): Call
linux_ptrace_get_extended_event.
(wait_lwp): Call linux_is_extended_waitstatus.
(linux_nat_filter_event): Call linux_ptrace_get_extended_event
and linux_is_extended_waitstatus.
* nat/linux-ptrace.c (linux_test_for_tracefork): Call
linux_ptrace_get_extended_event.
(linux_ptrace_get_extended_event): New function.
(linux_is_extended_waitstatus): New function.
* nat/linux-ptrace.h: Declare new functions.
gdbserver/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* linux-low.c (handle_extended_wait): Call
linux_ptrace_get_extended_event.
(get_stop_pc, get_detach_signal, linux_low_filter_event): Call
linux_is_extended_waitstatus.
---
gdb/gdbserver/linux-low.c | 8 ++++----
gdb/linux-nat.c | 11 +++++++----
gdb/nat/linux-ptrace.c | 18 +++++++++++++++++-
gdb/nat/linux-ptrace.h | 2 ++
4 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 543987a..dd73c24 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -370,7 +370,7 @@ linux_add_process (int pid, int attached)
static void
handle_extended_wait (struct lwp_info *event_child, int wstat)
{
- int event = wstat >> 16;
+ int event = linux_ptrace_get_extended_event (wstat);
struct thread_info *event_thr = get_lwp_thread (event_child);
struct lwp_info *new_lwp;
@@ -512,7 +512,7 @@ get_stop_pc (struct lwp_info *lwp)
if (WSTOPSIG (lwp->last_status) == SIGTRAP
&& !lwp->stepping
&& !lwp->stopped_by_watchpoint
- && lwp->last_status >> 16 == 0)
+ && !linux_is_extended_waitstatus (lwp->last_status))
stop_pc -= the_low_target.decr_pc_after_break;
if (debug_threads)
@@ -1056,7 +1056,7 @@ get_detach_signal (struct thread_info *thread)
}
/* Extended wait statuses aren't real SIGTRAPs. */
- if (WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ if (WSTOPSIG (status) == SIGTRAP && linux_is_extended_waitstatus (status))
{
if (debug_threads)
debug_printf ("GPS: lwp %s had stopped with extended "
@@ -1869,7 +1869,7 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
}
if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
- && wstat >> 16 != 0)
+ && linux_is_extended_waitstatus (wstat))
{
handle_extended_wait (child, wstat);
return NULL;
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index ab287bf..1798977 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1800,7 +1800,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
{
int pid = ptid_get_lwp (lp->ptid);
struct target_waitstatus *ourstatus = &lp->waitstatus;
- int event = status >> 16;
+ int event = linux_ptrace_get_extended_event (status);
if (event == PTRACE_EVENT_FORK || event == PTRACE_EVENT_VFORK
|| event == PTRACE_EVENT_CLONE)
@@ -2164,7 +2164,8 @@ wait_lwp (struct lwp_info *lp)
}
/* Handle GNU/Linux's extended waitstatus for trace events. */
- if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP
+ && linux_is_extended_waitstatus (status))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
@@ -2723,6 +2724,7 @@ static struct lwp_info *
linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
{
struct lwp_info *lp;
+ int event = linux_ptrace_get_extended_event (status);
*new_pending_p = 0;
@@ -2742,7 +2744,7 @@ linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
thread changes its tid to the tgid. */
if (WIFSTOPPED (status) && lp == NULL
- && (WSTOPSIG (status) == SIGTRAP && status >> 16 == PTRACE_EVENT_EXEC))
+ && (WSTOPSIG (status) == SIGTRAP && event == PTRACE_EVENT_EXEC))
{
/* A multi-thread exec after we had seen the leader exiting. */
if (debug_linux_nat)
@@ -2786,7 +2788,8 @@ linux_nat_filter_event (int lwpid, int status, int *new_pending_p)
}
/* Handle GNU/Linux's extended waitstatus for trace events. */
- if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP && status >> 16 != 0)
+ if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGTRAP
+ && linux_is_extended_waitstatus (status))
{
if (debug_linux_nat)
fprintf_unfiltered (gdb_stdlog,
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index b4db862..88d29f2 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -421,7 +421,7 @@ linux_test_for_tracefork (int child_pid)
/* Check if we received a fork event notification. */
if (ret == child_pid && WIFSTOPPED (status)
- && status >> 16 == PTRACE_EVENT_FORK)
+ && linux_ptrace_get_extended_event (status) == PTRACE_EVENT_FORK)
{
/* We did receive a fork event notification. Make sure its PID
is reported. */
@@ -555,3 +555,19 @@ linux_ptrace_set_additional_flags (int flags)
{
additional_flags = flags;
}
+
+/* Extract extended ptrace event from wait status. */
+
+int
+linux_ptrace_get_extended_event (int wstat)
+{
+ return (wstat >> 16);
+}
+
+/* Determine whether wait status denotes an extended event. */
+
+int
+linux_is_extended_waitstatus (int wstat)
+{
+ return (linux_ptrace_get_extended_event (wstat) != 0);
+}
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 41b3198..31a77cd 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -92,5 +92,7 @@ extern int linux_supports_traceclone (void);
extern int linux_supports_tracevforkdone (void);
extern int linux_supports_tracesysgood (void);
extern void linux_ptrace_set_additional_flags (int);
+extern int linux_ptrace_get_extended_event (int wstat);
+extern int linux_is_extended_waitstatus (int wstat);
#endif /* COMMON_LINUX_PTRACE_H */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 03/16 v2] Refactor ptrace extended event status
2014-08-21 0:30 ` [PATCH 03/16 v2] Refactor ptrace extended event status Don Breazeal
@ 2014-09-09 11:31 ` Pedro Alves
2014-09-19 18:14 ` [pushed] " Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-09-09 11:31 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
Hi Don,
Let's get this one out of the way.
On 08/21/2014 01:29 AM, Don Breazeal wrote:
> This patch implements functions for identifying and extracting extended
> ptrace event information from a Linux wait status. These are just
> convenience functions intended to hide the ">> 16" used to extract the
> event from the wait status word, replacing the hard-coded shift with a more
> descriptive function call. This is preparatory work for implementation of
> follow-fork and detach-on-fork for extended-remote linux targets.
>
> The functions linux_is_extended_waitstatus and
> linux_ptrace_get_extended_event are defined in nat/linux-ptrace.c, and
> called in linux-nat.c and gdbserver/linux-low.c.
>
> My initial approach was to implement predicates for every extended event,
> e.g. linux_is_traced_clone (status), linux_is_traced_fork (status), but
> that didn't fit the current implementation as well, bloated the code a bit,
> and didn't add anything to readability, so I went with just extracting the
> event bits from the status instead.
>
> Tested on x64 Ubuntu Lucid, native only.
This is OK. Please push.
Though I wonder why not push the SIGTRAP check to
linux_is_extended_waitstatus too.
> gdb/
> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>
> * linux-nat.c (linux_handle_extended_wait): Call
> linux_ptrace_get_extended_event.
> (wait_lwp): Call linux_is_extended_waitstatus.
> (linux_nat_filter_event): Call linux_ptrace_get_extended_event
> and linux_is_extended_waitstatus.
> * nat/linux-ptrace.c (linux_test_for_tracefork): Call
> linux_ptrace_get_extended_event.
> (linux_ptrace_get_extended_event): New function.
> (linux_is_extended_waitstatus): New function.
> * nat/linux-ptrace.h: Declare new functions.
Please spell out the new declarations. Like:
* nat/linux-ptrace.h (linux_ptrace_get_extended_event)
(linux_is_extended_waitstatus): New declarations.
>
> gdbserver/
> 2014-08-20 Don Breazeal <donb@codesourcery.com>
> * linux-low.c (handle_extended_wait): Call
Missing empty line above.
> linux_ptrace_get_extended_event.
> (get_stop_pc, get_detach_signal, linux_low_filter_event): Call
> linux_is_extended_waitstatus.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* [pushed] Re: [PATCH 03/16 v2] Refactor ptrace extended event status
2014-09-09 11:31 ` Pedro Alves
@ 2014-09-19 18:14 ` Breazeal, Don
0 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-09-19 18:14 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 9/9/2014 4:31 AM, Pedro Alves wrote:
> Hi Don,
>
> Let's get this one out of the way.
>
> On 08/21/2014 01:29 AM, Don Breazeal wrote:
>> This patch implements functions for identifying and extracting extended
>> ptrace event information from a Linux wait status. These are just
>> convenience functions intended to hide the ">> 16" used to extract the
>> event from the wait status word, replacing the hard-coded shift with a more
>> descriptive function call. This is preparatory work for implementation of
>> follow-fork and detach-on-fork for extended-remote linux targets.
>>
>> The functions linux_is_extended_waitstatus and
>> linux_ptrace_get_extended_event are defined in nat/linux-ptrace.c, and
>> called in linux-nat.c and gdbserver/linux-low.c.
>>
>> My initial approach was to implement predicates for every extended event,
>> e.g. linux_is_traced_clone (status), linux_is_traced_fork (status), but
>> that didn't fit the current implementation as well, bloated the code a bit,
>> and didn't add anything to readability, so I went with just extracting the
>> event bits from the status instead.
>>
>> Tested on x64 Ubuntu Lucid, native only.
>
> This is OK. Please push.
Thanks for the review. This is pushed in now, with fixes detailed
below.
>
> Though I wonder why not push the SIGTRAP check to
> linux_is_extended_waitstatus too.
My thinking was to implement something at the same level as WIFSTOPPED
et al.
>
>> gdb/
>> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>>
>> * linux-nat.c (linux_handle_extended_wait): Call
>> linux_ptrace_get_extended_event.
>> (wait_lwp): Call linux_is_extended_waitstatus.
>> (linux_nat_filter_event): Call linux_ptrace_get_extended_event
>> and linux_is_extended_waitstatus.
>> * nat/linux-ptrace.c (linux_test_for_tracefork): Call
>> linux_ptrace_get_extended_event.
>> (linux_ptrace_get_extended_event): New function.
>> (linux_is_extended_waitstatus): New function.
>> * nat/linux-ptrace.h: Declare new functions.
>
> Please spell out the new declarations. Like:
>
> * nat/linux-ptrace.h (linux_ptrace_get_extended_event)
> (linux_is_extended_waitstatus): New declarations.
Fixed.
>
>>
>> gdbserver/
>> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>> * linux-low.c (handle_extended_wait): Call
>
> Missing empty line above.
Fixed.
>
>> linux_ptrace_get_extended_event.
>> (get_stop_pc, get_detach_signal, linux_low_filter_event): Call
>> linux_is_extended_waitstatus.
>
> Thanks,
> Pedro Alves
>
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 04/16 v2] Determine supported extended-remote features
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (12 preceding siblings ...)
2014-08-21 0:30 ` [PATCH 03/16 v2] Refactor ptrace extended event status Don Breazeal
@ 2014-08-21 0:30 ` Don Breazeal
2014-10-15 16:17 ` Pedro Alves
2014-08-21 0:30 ` [PATCH 02/16 v2] Refactor follow-fork message printing Don Breazeal
` (12 subsequent siblings)
26 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:30 UTC (permalink / raw)
To: gdb-patches
This patch implements a mechanism for GDB to ask gdbserver what
extended-mode features are enabled.
The problems that are solved include:
1) A mechanism other than the qSupported RSP packet is needed for
gdbserver to inform GDB about the list of extended mode features that
are supported.
In the existing implementation most of the information about
supported gdbserver features is sent to GDB in response to a
qSupported RSP message. This exchange occurs prior to GDB
sending the "!" packet to enable extended mode. So as-is this
message doesn't work for features that are dependent on extended-
mode.
Also, the qSupported exchange is intended to inform GDB about which
packets are supported, and most of the extended mode features do not
have associated packets. There are some features included in the
qSupported response that do not have associated RSP messages, but the
code comments make it clear that this practice is not acceptable for new
packets.
2) A mechanism is needed to enable extended mode features when GDB
tells gdbserver to use extended mode.
The existing implementation checks for ptrace extended events
(just PTRACE_O_TRACELONE) and enables them immediately after checking.
This is done when the first event occurs as the program is loaded, which
can occur before GDB has connected to gdbserver and told it whether or
not to use extended mode.
The solution implemented in this patch is as follows:
1) Implement a new RSP packet, qExtendedFeatures, that returns a list of
the _currently enabled_ extended mode features, along with a host-side
function that tells the caller whether a specified extended-mode
feature is supported. The first time the host-side function is
called it sends the inquiry to gdbserver to populate its list of
features. It is called from every extended-remote target function
that uses one of the features.
2) Split linux_enable_event_reporting, which both checked for extended
ptrace events and enabled them, into linux_ptrace_check_options and
linux_ptrace_enable options. In gdbserver, linux_ptrace_check_options
is called during initialization, prior to accepting a connection from
GDB. linux_ptrace_enable_options is called from the same place that
linux_enable_event_reporting was called, when the first event after
program load occurs. It is also called when the "!" packet is received,
via a new gdbserver target routine, enable_extended_features. This
way both load scenarios are handled:
(a) when the program is specified on the gdbserver command line and
loaded prior to accepting a GDB connection, and
(b) when the program is loaded after GDB connects, using GDB commands.
There are some changes to linux-ptrace.c that build on the patch that Gary
Benson implemented to reduce the use of #ifdef GDBSERVER in linux-ptrace.c
here:
https://sourceware.org/ml/gdb-patches/2014-07/msg00633.html.
That patch allows the ptrace client (GDB or gdbserver) to request support
for certain ptrace options by maintaining static variables
current_ptrace_options (enabled options) and additional_options
(non-default options that the ptrace client has requested).
This patch modifies that to maintain three static variables:
current_ptrace_options (enabled options), requested_ptrace_options
(non-default options that the ptrace client has requested), and
available_ptrace_options (options from requested_ptrace_options that are
supported by the OS). available_ptrace_options is used in (2) above in
linux_ptrace_enable_options when extended-mode is enabled.
Tested on x64 Ubuntu Lucid, native only, and as part of testing of
subsequent patches.
gdb/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* linux-nat.c (linux_init_ptrace): Replace call to
linux_enable_event_reporting with calls to new functions.
(linux_child_follow_fork): Replace call to
linux_disable_event_reporting with call to renamed function.
* nat/linux-ptrace.c (linux_ptrace_check_options): Use
available_ptrace_options and requested_ptrace_options in place of
additional_flags.
(linux_test_for_tracesysgood): Ditto.
(linux_test_for_tracefork): Ditto.
(linux_ptrace_enable_options): New function from splitting
linux_enable_event_reporting.
(linux_ptrace_disable_options): Renamed function.
(linux_supports_traceexit): New function.
(linux_ptrace_set_requested_options): New function replaces
linux_ptrace_set_additional_flags.
* nat/linux-ptrace.h (linux_ptrace_check_options,
linux_ptrace_enable_options, linux_ptrace_disable_options,
linux_supports_traceexit, linux_ptrace_set_requested_options):
Declare new and renamed functions.
* remote.c (enum extended_feature_type): New enum.
(struct extended_feature): New structure type.
(enum) <PACKET_qExtendedFeatures>, <PACKET_vFollowFork>: Define
new RSP packets.
(remote_protocol_features): Initialize vFollowFork element.
(extract_response_item): New function extracted from
remote_query_supported.
(remote_query_supported): Call extract_response_item.
(extended_remote_follow_fork): New function.
(extended_remote_query_supported): New function.
(extended_remote_feature_supported): New function.
(_initialize_remote): Call add_packet_config_cmd for new RSP packets.
gdb/gdbserver/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* linux-low.c (linux_low_filter_event): Replace call to
linux_enable_event_reporting with call to linux_ptrace_enable_options.
(linux_supports_follow_fork): New function.
(linux_supports_follow_exec): New function.
(linux_enable_extended_features): New function.
(linux_target_ops): Initialize new struct members.
(initialize_low): Call linux_ptrace_set_requested_options and
linux_ptrace_check_options.
* lynx-low.c (lynx_target_ops): Initialize new struct members.
* nto-low.c (nto_target_ops): Initialize new struct members.
* server.c (handle_query): Support qExtendedFeatures packet.
(process_serial_event): Call target routine enable_extended_features.
(using_extended_protocol): New function.
* server.h (using_extended_protocol): Declare new function.
* spu-low.c (spu_target_ops): Initialize new struct members.
* target.h (struct target_ops) <supports_follow_fork>: New member.
<supports_follow_exec>: New member.
<enable_extended_features>: New member.
* win32-low.c (win32_target_ops: Initialize new struct members.
---
gdb/gdbserver/linux-low.c | 45 ++++++++++-
gdb/gdbserver/lynx-low.c | 3 +
gdb/gdbserver/nto-low.c | 3 +
gdb/gdbserver/server.c | 33 ++++++++
gdb/gdbserver/server.h | 1 +
gdb/gdbserver/spu-low.c | 3 +
gdb/gdbserver/target.h | 20 +++++
gdb/gdbserver/win32-low.c | 3 +
gdb/linux-nat.c | 15 ++--
gdb/nat/linux-ptrace.c | 77 +++++++++++++-------
gdb/nat/linux-ptrace.h | 8 +-
gdb/remote.c | 179 ++++++++++++++++++++++++++++++++++++++++-----
12 files changed, 330 insertions(+), 60 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index dd73c24..f501c05 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -205,6 +205,7 @@ int using_threads = 1;
jump pads). */
static int stabilizing_threads;
+static void async_file_mark (void);
static void linux_resume_one_lwp (struct lwp_info *lwp,
int step, int signal, siginfo_t *info);
static void linux_resume (struct thread_resume *resume_info, size_t n);
@@ -213,6 +214,9 @@ static void unstop_all_lwps (int unsuspend, struct lwp_info *except);
static int linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
int *wstat, int options);
static int linux_wait_for_event (ptid_t ptid, int *wstat, int options);
+static ptid_t linux_wait_1 (ptid_t ptid,
+ struct target_waitstatus *ourstatus,
+ int target_options);
static struct lwp_info *add_lwp (ptid_t ptid);
static int linux_stopped_by_watchpoint (void);
static void mark_lwp_dead (struct lwp_info *lwp, int wstat);
@@ -1864,7 +1868,7 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
{
- linux_enable_event_reporting (lwpid);
+ linux_ptrace_enable_options (lwpid, using_extended_protocol ());
child->must_set_ptrace_flags = 0;
}
@@ -2367,9 +2371,6 @@ static void move_out_of_jump_pad_callback (struct inferior_list_entry *entry);
static int stuck_in_jump_pad_callback (struct inferior_list_entry *entry,
void *data);
static int lwp_running (struct inferior_list_entry *entry, void *data);
-static ptid_t linux_wait_1 (ptid_t ptid,
- struct target_waitstatus *ourstatus,
- int target_options);
/* Stabilize threads (move out of jump pads).
@@ -6009,11 +6010,43 @@ linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
}
#endif /* HAVE_LINUX_BTRACE */
+/* Check if fork events are supported. */
+
+static int
+linux_supports_follow_fork (void)
+{
+ return linux_supports_tracefork ();
+}
+
+/* Check if exec events are supported. */
+
+static int
+linux_supports_follow_exec (void)
+{
+ /* Check for PTRACE_O_TRACEEXIT, since our implementation of follow
+ exec depends on this option, and it was implemented in later
+ kernel versions than fork, exec, et al. */
+ return linux_supports_traceexit ();
+}
+
+/* Enable any available extended-mode-only options. */
+
+static void
+linux_enable_extended_features (void)
+{
+ if (current_inferior != NULL)
+ linux_ptrace_enable_options (ptid_get_lwp (current_inferior->entry.id),
+ using_extended_protocol ());
+}
+
static struct target_ops linux_target_ops = {
linux_create_inferior,
linux_attach,
linux_kill,
linux_detach,
+ linux_supports_follow_fork,
+ linux_supports_follow_exec,
+ linux_enable_extended_features,
linux_mourn,
linux_join,
linux_thread_alive,
@@ -6128,4 +6161,8 @@ initialize_low (void)
sigaction (SIGCHLD, &sigchld_action, NULL);
initialize_low_arch ();
+
+ /* Placeholder to enable extended events. */
+ linux_ptrace_set_requested_options (0);
+ linux_ptrace_check_options ();
}
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 0b0ff47..0b27432 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -722,6 +722,9 @@ static struct target_ops lynx_target_ops = {
lynx_attach,
lynx_kill,
lynx_detach,
+ NULL, /* supports_follow_fork */
+ NULL, /* supports_follow_exec */
+ NULL, /* enable_extended_features */
lynx_mourn,
lynx_join,
lynx_thread_alive,
diff --git a/gdb/gdbserver/nto-low.c b/gdb/gdbserver/nto-low.c
index 0afaec7..a48c6dd 100644
--- a/gdb/gdbserver/nto-low.c
+++ b/gdb/gdbserver/nto-low.c
@@ -928,6 +928,9 @@ static struct target_ops nto_target_ops = {
nto_attach,
nto_kill,
nto_detach,
+ NULL, /* supports_follow_fork */
+ NULL, /* supports_follow_exec */
+ NULL, /* enable_extended_features */
nto_mourn,
NULL, /* nto_join */
nto_thread_alive,
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index cf1dffe..b4fd206 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2132,6 +2132,29 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
return;
}
+ if (strncmp ("qExtendedFeatures", own_buf, 15) == 0)
+ {
+ int fork_supported = 0;
+
+ /* Assume if fork events are supported then vfork and
+ vfork-done events are supported. If a target emerges
+ where this is not true, then each of these will need
+ to be checked separately. */
+ if (target_supports_follow_fork ())
+ {
+ strcat (own_buf, ":fork_event;vfork_event;vfork_done_event");
+ fork_supported = 1;
+ }
+
+ if (target_supports_follow_exec ())
+ {
+ strcat (own_buf, (fork_supported ? ";" : ":"));
+ strcat (own_buf, "exec_event");
+ }
+
+ return;
+ }
+
if (handle_qxfer (own_buf, packet_len, new_packet_len_p))
return;
@@ -3542,6 +3565,10 @@ process_serial_event (void)
break;
case '!':
extended_protocol = 1;
+
+ if (the_target->enable_extended_features != NULL)
+ (*the_target->enable_extended_features) ();
+
write_ok (own_buf);
break;
case '?':
@@ -3973,3 +4000,9 @@ handle_target_event (int err, gdb_client_data client_data)
return 0;
}
+
+int
+using_extended_protocol (void)
+{
+ return extended_protocol;
+}
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index e6b2277..2c5377b 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -114,6 +114,7 @@ typedef int gdb_fildes_t;
/* Functions from server.c. */
extern int handle_serial_event (int err, gdb_client_data client_data);
extern int handle_target_event (int err, gdb_client_data client_data);
+extern int using_extended_protocol (void);
#include "remote-utils.h"
diff --git a/gdb/gdbserver/spu-low.c b/gdb/gdbserver/spu-low.c
index 867f97b..d4c5429 100644
--- a/gdb/gdbserver/spu-low.c
+++ b/gdb/gdbserver/spu-low.c
@@ -641,6 +641,9 @@ static struct target_ops spu_target_ops = {
spu_attach,
spu_kill,
spu_detach,
+ NULL, /* supports_follow_fork */
+ NULL, /* supports_follow_exec */
+ NULL, /* enable_extended_features */
spu_mourn,
spu_join,
spu_thread_alive,
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index f5eda8a..fc1ec73 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -92,6 +92,18 @@ struct target_ops
int (*detach) (int pid);
+ /* Returns true if follow_fork is supported. */
+
+ int (*supports_follow_fork) (void);
+
+ /* Returns true if follow_exec is supported. */
+
+ int (*supports_follow_exec) (void);
+
+ /* Enable features that are only available in extended mode. */
+
+ void (*enable_extended_features) (void);
+
/* The inferior process has died. Do what is right. */
void (*mourn) (struct process_info *proc);
@@ -389,6 +401,14 @@ void set_target_ops (struct target_ops *);
int kill_inferior (int);
+#define target_supports_follow_fork() \
+ (the_target->supports_follow_fork ? \
+ (*the_target->supports_follow_fork) () : 0)
+
+#define target_supports_follow_exec() \
+ (the_target->supports_follow_exec ? \
+ (*the_target->supports_follow_exec) () : 0)
+
#define detach_inferior(pid) \
(*the_target->detach) (pid)
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index 2242d5c..7337034 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1767,6 +1767,9 @@ static struct target_ops win32_target_ops = {
win32_attach,
win32_kill,
win32_detach,
+ NULL, /* supports_follow_fork */
+ NULL, /* supports_follow_exec */
+ NULL, /* enable_extended_features */
win32_mourn,
win32_join,
win32_thread_alive,
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 1798977..b7bb978 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -327,7 +327,8 @@ pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
static void
linux_init_ptrace (pid_t pid)
{
- linux_enable_event_reporting (pid);
+ linux_ptrace_check_options ();
+ linux_ptrace_enable_options (pid, 1);
linux_ptrace_init_warnings ();
}
@@ -415,7 +416,7 @@ linux_child_follow_fork (struct target_ops *ops, int follow_child,
if (!gdbarch_software_single_step_p (target_thread_architecture
(child_lp->ptid)))
{
- linux_disable_event_reporting (child_pid);
+ 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)
@@ -4848,11 +4849,11 @@ Enables printf debugging output."),
/* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
support read-only process state. */
- linux_ptrace_set_additional_flags (PTRACE_O_TRACESYSGOOD
- | PTRACE_O_TRACEVFORKDONE
- | PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEEXEC);
+ linux_ptrace_set_requested_options (PTRACE_O_TRACESYSGOOD
+ | PTRACE_O_TRACEVFORKDONE
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEEXEC);
}
\f
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 88d29f2..fc9e83a 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -30,14 +30,20 @@
#include <stdint.h>
-/* Stores the currently supported ptrace options. A value of
- -1 means we did not check for features yet. A value of 0 means
- there are no supported features. */
-static int current_ptrace_options = -1;
+/* Stores the ptrace options that have been requested by the
+ ptrace client beyond the default options that we attempt
+ to enable for all ptrace clients. */
+static int requested_ptrace_options;
-/* Additional flags to test. */
+/* Stores the ptrace options from REQUESTED_PTRACE_OPTIONS
+ that are supported by the OS. A value of -1 means we did
+ not check for features yet. A value of 0 means that none
+ of the requested options are supported. */
+static int available_ptrace_options = -1;
-static int additional_flags;
+/* Stores the currently enabled ptrace options, or the default
+ option(s) that will be enabled once a process is loaded. */
+static int current_ptrace_options;
/* Find all possible reasons we could fail to attach PID and append
these as strings to the already initialized BUFFER. '\0'
@@ -315,13 +321,19 @@ static void linux_test_for_tracefork (int child_pid);
/* Determine ptrace features available on this target. */
-static void
-linux_check_ptrace_features (void)
+void
+linux_ptrace_check_options (void)
{
int child_pid, ret, status;
+ /* Check if we have initialized the ptrace features for this
+ target. If not, proceed. */
+ if (available_ptrace_options != -1)
+ return;
+
/* Initialize the options. */
current_ptrace_options = 0;
+ available_ptrace_options = 0;
/* Fork a child so we can do some testing. The child will call
linux_child_function and will get traced. The child will
@@ -363,14 +375,14 @@ linux_test_for_tracesysgood (int child_pid)
{
int ret;
- if ((additional_flags & PTRACE_O_TRACESYSGOOD) == 0)
+ if ((requested_ptrace_options & PTRACE_O_TRACESYSGOOD) == 0)
return;
ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
(PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
if (ret == 0)
- current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
+ available_ptrace_options |= PTRACE_O_TRACESYSGOOD;
}
/* Determine if PTRACE_O_TRACEFORK can be used to follow fork
@@ -390,14 +402,14 @@ linux_test_for_tracefork (int child_pid)
if (ret != 0)
return;
- if ((additional_flags & PTRACE_O_TRACEVFORKDONE) != 0)
+ if ((requested_ptrace_options & PTRACE_O_TRACEVFORKDONE) != 0)
{
/* Check if the target supports PTRACE_O_TRACEVFORKDONE. */
ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
(PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORKDONE));
if (ret == 0)
- current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
+ available_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
}
/* Setting PTRACE_O_TRACEFORK did not cause an error, however we
@@ -433,11 +445,14 @@ linux_test_for_tracefork (int child_pid)
int second_status;
/* We got the PID from the grandchild, which means fork
- tracing is supported. */
+ tracing is supported. Include default options in
+ current_ptrace_options and save the rest as
+ available options. */
current_ptrace_options |= PTRACE_O_TRACECLONE;
- current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
- | PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEEXEC));
+ available_ptrace_options |= (requested_ptrace_options
+ & (PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEEXEC));
/* Do some cleanup and kill the grandchild. */
my_waitpid (second_pid, &second_status, 0);
@@ -454,15 +469,15 @@ linux_test_for_tracefork (int child_pid)
"(%d, status 0x%x)"), ret, status);
}
-/* Enable reporting of all currently supported ptrace events. */
+/* Enable reporting of supported ptrace events. If
+ USE_AVAILABLE_OPTIONS is false, then exclude the events
+ specified in requested_ptrace_options. */
void
-linux_enable_event_reporting (pid_t pid)
+linux_ptrace_enable_options (pid_t pid, int use_available_options)
{
- /* Check if we have initialized the ptrace features for this
- target. If not, do it now. */
- if (current_ptrace_options == -1)
- linux_check_ptrace_features ();
+ if (use_available_options)
+ current_ptrace_options |= available_ptrace_options;
/* Set the options. */
ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
@@ -472,7 +487,7 @@ linux_enable_event_reporting (pid_t pid)
/* Disable reporting of all currently supported ptrace events. */
void
-linux_disable_event_reporting (pid_t pid)
+linux_ptrace_disable_options (pid_t pid)
{
/* Set the options. */
ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0, 0);
@@ -521,6 +536,15 @@ linux_supports_tracevforkdone (void)
return ptrace_supports_feature (PTRACE_O_TRACEVFORKDONE);
}
+/* Returns non-zero if PTRACE_O_TRACEEXIT is supported by ptrace,
+ 0 otherwise. */
+
+int
+linux_supports_traceexit (void)
+{
+ return ptrace_supports_feature (PTRACE_O_TRACEEXIT);
+}
+
/* Returns non-zero if PTRACE_O_TRACESYSGOOD is supported by ptrace,
0 otherwise. */
@@ -545,15 +569,16 @@ linux_ptrace_init_warnings (void)
linux_ptrace_test_ret_to_nx ();
}
-/* Set additional ptrace flags to use. Some such flags may be checked
+/* Set additional ptrace flags to use, beyond the default options
+ checked for every ptrace client. Some such flags may be checked
by the implementation above. This function must be called before
any other function in this file; otherwise the flags may not take
effect appropriately. */
void
-linux_ptrace_set_additional_flags (int flags)
+linux_ptrace_set_requested_options (int flags)
{
- additional_flags = flags;
+ requested_ptrace_options = flags;
}
/* Extract extended ptrace event from wait status. */
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 31a77cd..03d27e1 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -85,13 +85,15 @@ struct buffer;
extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
extern void linux_ptrace_init_warnings (void);
-extern void linux_enable_event_reporting (pid_t pid);
-extern void linux_disable_event_reporting (pid_t pid);
+extern void linux_ptrace_check_options (void);
+extern void linux_ptrace_enable_options (pid_t pid, int use_available_options);
+extern void linux_ptrace_disable_options (pid_t pid);
extern int linux_supports_tracefork (void);
extern int linux_supports_traceclone (void);
extern int linux_supports_tracevforkdone (void);
+extern int linux_supports_traceexit (void);
extern int linux_supports_tracesysgood (void);
-extern void linux_ptrace_set_additional_flags (int);
+extern void linux_ptrace_set_requested_options (int);
extern int linux_ptrace_get_extended_event (int wstat);
extern int linux_is_extended_waitstatus (int wstat);
diff --git a/gdb/remote.c b/gdb/remote.c
index 357e9f2..f3076b3 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -86,6 +86,17 @@ static long target_buf_size;
important here, not the possibly larger cache line size. */
enum { REMOTE_ALIGN_WRITES = 16 };
+/* Denotes features that may be supported in extended mode. */
+enum extended_feature_type
+{
+ EXTENDED_FEATURES_START,
+ FORK_EVENT = EXTENDED_FEATURES_START,
+ VFORK_EVENT,
+ VFORK_DONE_EVENT,
+ EXEC_EVENT,
+ EXTENDED_FEATURES_COUNT
+};
+
/* Prototypes for local functions. */
static void async_cleanup_sigint_signal_handler (void *dummy);
static int getpkt_sane (char **buf, long *sizeof_buf, int forever);
@@ -224,6 +235,8 @@ static int remote_supports_cond_breakpoints (struct target_ops *self);
static int remote_can_run_breakpoint_commands (struct target_ops *self);
+static int extended_remote_feature_supported (enum extended_feature_type);
+
/* For "remote". */
static struct cmd_list_element *remote_cmdlist;
@@ -383,6 +396,19 @@ struct private_thread_info
int core;
};
+/* Describes a feature that may be supported in extended mode. */
+struct extended_feature
+{
+ const char *name;
+ int supported;
+};
+
+/* Used to denote whether an extended-mode feature is supported. */
+static struct extended_feature extended_features[] = {
+{ "fork_event", 0}, { "vfork_event", 0}, { "vfork_done_event", 0},
+{ "exec_event", 0}
+};
+
static void
free_private_thread_info (struct private_thread_info *info)
{
@@ -1330,6 +1356,12 @@ enum {
/* Support for qXfer:libraries-svr4:read with a non-empty annex. */
PACKET_augmented_libraries_svr4_read_feature,
+ /* Query for available extended-mode features. */
+ PACKET_qExtendedFeatures,
+
+ /* Support for follow fork. */
+ PACKET_vFollowFork,
+
PACKET_MAX
};
@@ -3926,7 +3958,9 @@ static const struct protocol_feature remote_protocol_features[] = {
{ "Qbtrace:off", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_off },
{ "Qbtrace:bts", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_bts },
{ "qXfer:btrace:read", PACKET_DISABLE, remote_supported_packet,
- PACKET_qXfer_btrace }
+ PACKET_qXfer_btrace },
+ { "vFollowFork", PACKET_DISABLE, remote_supported_packet,
+ PACKET_vFollowFork }
};
static char *remote_support_xml;
@@ -3972,6 +4006,35 @@ remote_query_supported_append (char *msg, const char *append)
return xstrdup (append);
}
+/* Separate a response item (delimited by ';') from the rest of
+ the packet. If there's another item after this, we overwrite
+ the separator (terminated strings are much easier to work with). */
+static char *
+extract_response_item (char ** nextp, char **endp)
+{
+ char *p;
+ char *next = *nextp;
+ char *end;
+
+ p = next;
+ end = strchr (p, ';');
+
+ if (end == NULL)
+ {
+ end = p + strlen (p);
+ next = end;
+ }
+ else
+ {
+ *end = '\0';
+ next = end + 1;
+ }
+
+ *nextp = next;
+ *endp = end;
+ return p;
+}
+
static void
remote_query_supported (void)
{
@@ -4025,26 +4088,12 @@ remote_query_supported (void)
enum packet_support is_supported;
char *p, *end, *name_end, *value;
- /* First separate out this item from the rest of the packet. If
- there's another item after this, we overwrite the separator
- (terminated strings are much easier to work with). */
- p = next;
- end = strchr (p, ';');
- if (end == NULL)
- {
- end = p + strlen (p);
- next = end;
- }
- else
+ /* First separate out this item from the rest of the packet. */
+ p = extract_response_item (&next, &end);
+ if (end == p)
{
- *end = '\0';
- next = end + 1;
-
- if (end == p)
- {
- warning (_("empty item in \"qSupported\" response"));
- continue;
- }
+ warning (_("empty item in \"qSupported\" response"));
+ continue;
}
name_end = strchr (p, '=');
@@ -7787,6 +7836,20 @@ 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)
{
@@ -11386,6 +11449,75 @@ remote_augmented_libraries_svr4_read (struct target_ops *self)
== PACKET_ENABLE);
}
+/* Ask the target for a list of extended-remote features that are
+ supported and enabled. */
+
+static void
+extended_remote_query_supported (void)
+{
+ if (packet_support (PACKET_qExtendedFeatures) != PACKET_DISABLE)
+ {
+ struct remote_state *rs = get_remote_state ();
+ char *next;
+
+ xsnprintf (rs->buf, get_remote_packet_size (), "qExtendedFeatures");
+ putpkt (rs->buf);
+ getpkt (&rs->buf, &rs->buf_size, 0);
+
+ /* Locate the list of features. */
+ next = rs->buf;
+ next = strchr (next, ':') + 1;
+ if ((next - rs->buf) >= rs->buf_size)
+ return;
+
+ while (*next)
+ {
+ char *p;
+ char *end;
+ int i;
+
+ /* First separate out this item from the rest of the packet. */
+ p = extract_response_item (&next, &end);
+ if (end == p)
+ {
+ warning (_("empty item in \"qExtendedFeatures\" response"));
+ continue;
+ }
+
+ for (i = EXTENDED_FEATURES_START; i < EXTENDED_FEATURES_COUNT; i++)
+ {
+ if (strncmp (p, extended_features[i].name, strlen (p)) == 0)
+ {
+ extended_features[i].supported = 1;
+ if (strcmp (p, "fork_event") == 0)
+ {
+ remote_protocol_packets[PACKET_vFollowFork].support
+ = PACKET_ENABLE;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Predicate for whether the specified feature is supported. */
+
+static int
+extended_remote_feature_supported (enum extended_feature_type feature)
+{
+ static int has_queried = 0;
+
+ /* If the extended features list has not been initialized,
+ make it so. */
+ if (!has_queried)
+ {
+ extended_remote_query_supported ();
+ has_queried = 1;
+ }
+
+ return extended_features[feature].supported;
+}
+
/* Implementation of to_load. */
static void
@@ -11535,6 +11667,7 @@ init_extended_remote_ops (void)
Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_open = extended_remote_open;
extended_remote_ops.to_create_inferior = extended_remote_create_inferior;
+ extended_remote_ops.to_follow_fork = extended_remote_follow_fork;
extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
extended_remote_ops.to_detach = extended_remote_detach;
extended_remote_ops.to_attach = extended_remote_attach;
@@ -12105,6 +12238,12 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace],
"qXfer:btrace", "read-btrace", 0);
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_qExtendedFeatures],
+ "qExtendedFeatures", "extended_features", 0);
+
+ add_packet_config_cmd (&remote_protocol_packets[PACKET_vFollowFork],
+ "vFollowFork", "follow-fork", 0);
+
/* Assert that we've registered commands for all packet configs. */
{
int i;
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v2] Determine supported extended-remote features
2014-08-21 0:30 ` [PATCH 04/16 v2] Determine supported extended-remote features Don Breazeal
@ 2014-10-15 16:17 ` Pedro Alves
2014-10-21 23:23 ` Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-10-15 16:17 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 08/21/2014 01:29 AM, Don Breazeal wrote:
> This patch implements a mechanism for GDB to ask gdbserver what
> extended-mode features are enabled.
>
> The problems that are solved include:
>
> 1) A mechanism other than the qSupported RSP packet is needed for
> gdbserver to inform GDB about the list of extended mode features that
> are supported.
Sorry, this doesn't really make sense to me.
>
> In the existing implementation most of the information about
> supported gdbserver features is sent to GDB in response to a
> qSupported RSP message. This exchange occurs prior to GDB
> sending the "!" packet to enable extended mode. So as-is this
> message doesn't work for features that are dependent on extended-
> mode.
The differences between extended and non-extended modes are very
few, and I'd rather have fewer -- eventually merge them -- not
more.
I don't understand what motivated this. What's wrong with
just reporting the features as supported in qSupported, and
then have GDB decide whether to make use the features or not?
> Also, the qSupported exchange is intended to inform GDB about which
> packets are supported, and most of the extended mode features do not
> have associated packets. There are some features included in the
> qSupported response that do not have associated RSP messages, but the
> code comments make it clear that this practice is not acceptable for new
> packets.
Which comments?
>
> 2) A mechanism is needed to enable extended mode features when GDB
> tells gdbserver to use extended mode.
>
> The existing implementation checks for ptrace extended events
> (just PTRACE_O_TRACELONE) and enables them immediately after checking.
> This is done when the first event occurs as the program is loaded, which
> can occur before GDB has connected to gdbserver and told it whether or
> not to use extended mode.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v2] Determine supported extended-remote features
2014-10-15 16:17 ` Pedro Alves
@ 2014-10-21 23:23 ` Breazeal, Don
2014-10-22 21:48 ` Pedro Alves
0 siblings, 1 reply; 110+ messages in thread
From: Breazeal, Don @ 2014-10-21 23:23 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 10/15/2014 9:17 AM, Pedro Alves wrote:
> On 08/21/2014 01:29 AM, Don Breazeal wrote:
>> This patch implements a mechanism for GDB to ask gdbserver what
>> extended-mode features are enabled.
>>
>> The problems that are solved include:
>>
>> 1) A mechanism other than the qSupported RSP packet is needed for
>> gdbserver to inform GDB about the list of extended mode features that
>> are supported.
>
> Sorry, this doesn't really make sense to me.
>
>>
>> In the existing implementation most of the information about
>> supported gdbserver features is sent to GDB in response to a
>> qSupported RSP message. This exchange occurs prior to GDB
>> sending the "!" packet to enable extended mode. So as-is this
>> message doesn't work for features that are dependent on extended-
>> mode.
>
> The differences between extended and non-extended modes are very
> few, and I'd rather have fewer -- eventually merge them -- not
> more.
>
> I don't understand what motivated this. What's wrong with
> just reporting the features as supported in qSupported, and
> then have GDB decide whether to make use the features or not?
>
>> Also, the qSupported exchange is intended to inform GDB about which
>> packets are supported, and most of the extended mode features do not
>> have associated packets. There are some features included in the
>> qSupported response that do not have associated RSP messages, but the
>> code comments make it clear that this practice is not acceptable for new
>> packets.
>
> Which comments?
>
>>
>> 2) A mechanism is needed to enable extended mode features when GDB
>> tells gdbserver to use extended mode.
>>
>> The existing implementation checks for ptrace extended events
>> (just PTRACE_O_TRACELONE) and enables them immediately after checking.
>> This is done when the first event occurs as the program is loaded, which
>> can occur before GDB has connected to gdbserver and told it whether or
>> not to use extended mode.
>
>
> Thanks,
> Pedro Alves
>
Hi Pedro,
Thanks for looking at this. I should have made the rationale for
the new qExtendedFeatures packet more clear in my original
submission. I'll try to answer all of your questions in the
explanation below.
I had started out trying to report the features as
supported/unsupported using qSupported, following the
implementation of PACKET_multiprocess_feature as a model.
I abandoned that approach when I saw these comments in
remote.c_initialize_remote:
/* Ideally all configs would have a command associated. Some
still don't though. */
[---snip---]
case PACKET_multiprocess_feature:
[---snip---]
/* Additions to this list need to be well justified:
pre-existing packets are OK; new packets are not. */
I interpreted this to mean that the qSupported query was intended
to be used only to enable/disable packets that mapped to commands,
and that defining new packets that represented something else was
discouraged.
In the end, the qExtendedFeatures approach seemed better to me because
- it didn't violate the directive in the comment above
- it was less disruptive to existing code, and didn't affect native
- it could potentially be used to eliminate some special-case code
that used packets that had no associated command, e.g. the
multiprocess feature could be implemented using qExtendedFeatures
instead of PACKET_multiprocess_feature.
Implementing the qSupported approach required:
1) sending "!" before sending qSupported so that gdbserver knew it
was in extended mode when responding to qSupported
2) splitting nat/linux-ptrace.c:linux_enable_event_reporting into two
functions, one to check if extended events were supported at
startup time, and one to enable any supported extended events
where linux_enable_event_reporting is called today. This allowed
gdbserver to know what extended events were supported when
responding to qSupported. Note that this affects the native
implementation.
3) defining a new packet for each of the extended-mode events
In refreshing my memory about this I wrote up some detailed notes on
it. Let me know if you want to see the gruesome details.
Do you think the qSupported approach is preferable despite the
comment? Did I misinterpret it?
Thanks
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 04/16 v2] Determine supported extended-remote features
2014-10-21 23:23 ` Breazeal, Don
@ 2014-10-22 21:48 ` Pedro Alves
2014-10-31 23:38 ` Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-10-22 21:48 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
On 10/22/2014 12:23 AM, Breazeal, Don wrote:
> Hi Pedro,
> Thanks for looking at this. I should have made the rationale for
> the new qExtendedFeatures packet more clear in my original
> submission. I'll try to answer all of your questions in the
> explanation below.
>
> I had started out trying to report the features as
> supported/unsupported using qSupported, following the
> implementation of PACKET_multiprocess_feature as a model.
> I abandoned that approach when I saw these comments in
> remote.c_initialize_remote:
>
> /* Ideally all configs would have a command associated. Some
> still don't though. */
> [---snip---]
> case PACKET_multiprocess_feature:
> [---snip---]
> /* Additions to this list need to be well justified:
> pre-existing packets are OK; new packets are not. */
>
> I interpreted this to mean that the qSupported query was intended
> to be used only to enable/disable packets that mapped to commands,
> and that defining new packets that represented something else was
> discouraged.
Oh, you're reading this backwards --- this block is making sure that
we don't forget to call add_packet_config_cmd whenever we add a
new feature/packet. The commands that is talking about are the
"set remote foo-packet" commands that add_packet_config_cmd registers.
Those are commands that allow force- enable/disabling a given
RSP packet/feature. That loop is allowing some exceptions for
some packets/features that already didn't have the corresponding
"set remote foo-packet" commands when that assertion was
first added:
/* Assert that we've registered commands for all packet configs. */
{
int i;
for (i = 0; i < PACKET_MAX; i++)
{
/* Ideally all configs would have a command associated. Some
still don't though. */
int excepted;
See the log of ca4f7f8be, the commit that added this.
We can certainly add new qSupported features that don't map
to regular user commands.
Guess this would make it clearer:
- /* Assert that we've registered commands for all packet configs. */
+ /* Assert that we've registered "set remote foo-packet" commands for all packet configs. */
> In refreshing my memory about this I wrote up some detailed notes on
> it. Let me know if you want to see the gruesome details.
>
> Do you think the qSupported approach is preferable despite the
> comment? Did I misinterpret it?
Yes, qSupported is preferable. I'm sorry about not foreseeing
this potential confusion when I wrote that comment.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 02/16 v2] Refactor follow-fork message printing
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (13 preceding siblings ...)
2014-08-21 0:30 ` [PATCH 04/16 v2] Determine supported extended-remote features Don Breazeal
@ 2014-08-21 0:30 ` Don Breazeal
2014-09-26 19:52 ` Pedro Alves
2014-08-21 0:31 ` [PATCH 06/16 v2] Extended-remote Linux follow fork Don Breazeal
` (11 subsequent siblings)
26 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:30 UTC (permalink / raw)
To: gdb-patches
This patch refactors the code that prints messages related to follow-fork
into functions, and adds a call so that a message is now printed when the
parent process is detached. Previously in this case the only message was
notification of attaching to the child. We still do not print any messages
when following the parent and detaching the child (the default). My
rationale for this is that from the user's perspective the new child was
never attached.
The messages now distinguish between fork and vfork.
Note that all of these messages are only printed when 'verbose' is set or
when debugging is turned on.
This is preparatory work for follow-fork and detach-on-fork on
extended-remote linux targets.
The test gdb.base/foll-fork.exp was modified to check for the new message.
Tested on x64 Ubuntu Lucid, native only.
Thanks,
--Don
gdb/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* gdb/infrun.c (print_fork_attach): New function.
(print_fork_detach): New function.
(follow_fork_inferior): Call print_fork_attach and print_fork_detach.
(handle_vfork_child_exec_or_exit): Ditto.
gdb/testsuite/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* gdb.base/foll-fork.exp (test_follow_fork): Add check for new
detach message.
(catch_fork_child_follow): Ditto.
* gdb.base/foll-vfork.exp (vfork_parent_follow_through_step):
Modify to check for "vfork" instead of "fork".
(vfork_parent_follow_to_bp): Ditto.
(vfork_and_exec_child_follow_through_step): Ditto.
(vfork_and_exec_child_follow_to_main_bp): Ditto, plus add check
for new detach message.
---
gdb/infrun.c | 94 +++++++++++++++++++-------------
gdb/testsuite/gdb.base/foll-fork.exp | 12 +++--
gdb/testsuite/gdb.base/foll-vfork.exp | 8 ++--
3 files changed, 68 insertions(+), 46 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index a51c759..34e9295 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -567,6 +567,49 @@ follow_fork (void)
return should_resume;
}
+/* Print details about attaching to a process after a fork call. */
+
+static void
+print_fork_attach (pid_t child_pid, pid_t parent_pid, int is_vfork)
+{
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours ();
+ fprintf_filtered (gdb_stdlog,
+ _("Attaching after process %d "
+ "%s to child process %d.\n"),
+ parent_pid, is_vfork?"vfork":"fork", child_pid);
+ }
+}
+
+/* Print details about detaching from a process after a fork call. */
+
+static void
+print_fork_detach (pid_t pid, int is_parent, int is_vfork, char *vfork_action)
+{
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours ();
+
+ if (is_parent && is_vfork)
+ {
+ /* Detaching a vfork parent, so print what the child did
+ that allows the parent to resume. */
+ gdb_assert (vfork_action != NULL && strlen (vfork_action) > 0);
+ fprintf_filtered (gdb_stdlog,
+ "Detaching vfork parent process %d after"
+ " child %s.\n", pid, vfork_action);
+ }
+ else
+ {
+ fprintf_filtered (gdb_stdlog,
+ _("Detaching after %s from %s process %d.\n"),
+ is_vfork?"vfork":"fork",
+ is_parent?"parent":"child", pid);
+ }
+ }
+}
+
/* 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. */
@@ -623,14 +666,7 @@ holding the child stopped. Try \"set detach-on-fork\" or \
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);
- }
+ print_fork_detach (child_pid, follow_child, has_vforked, "");
}
else
{
@@ -711,20 +747,7 @@ holding the child stopped. Try \"set detach-on-fork\" or \
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);
- }
+ print_fork_attach (child_pid, parent_pid, has_vforked);
/* Add the new inferior first, so that the target_detach below
doesn't unpush the target. */
@@ -760,7 +783,10 @@ holding the child stopped. Try \"set detach-on-fork\" or \
parent_inf->waiting_for_vfork_done = 0;
}
else if (detach_fork)
- target_detach (NULL, 0);
+ {
+ print_fork_detach (parent_pid, follow_child, has_vforked, "");
+ target_detach (NULL, 0);
+ }
/* Note that the detach above makes PARENT_INF dangling. */
@@ -927,21 +953,13 @@ handle_vfork_child_exec_or_exit (int exec)
inf->aspace = NULL;
inf->pspace = NULL;
- if (debug_infrun || info_verbose)
- {
- target_terminal_ours ();
-
- if (exec)
- fprintf_filtered (gdb_stdlog,
- "Detaching vfork parent process "
- "%d after child exec.\n",
- inf->vfork_parent->pid);
- else
- fprintf_filtered (gdb_stdlog,
- "Detaching vfork parent process "
- "%d after child exit.\n",
- inf->vfork_parent->pid);
- }
+ /* Print verbose/debug info message. Hardcoded 1's designate
+ that we are detaching a parent and that it is after a vfork,
+ respectively. */
+ if (exec)
+ print_fork_detach (inf->vfork_parent->pid, 1, 1, "exec");
+ else
+ print_fork_detach (inf->vfork_parent->pid, 1, 1, "exit");
target_detach (NULL, 0);
diff --git a/gdb/testsuite/gdb.base/foll-fork.exp b/gdb/testsuite/gdb.base/foll-fork.exp
index ad8b750..06ba1b5 100644
--- a/gdb/testsuite/gdb.base/foll-fork.exp
+++ b/gdb/testsuite/gdb.base/foll-fork.exp
@@ -115,7 +115,11 @@ proc test_follow_fork { who detach cmd } {
# Set up the output we expect to see after we run.
set expected_re ""
if {$who == "child"} {
- set expected_re "Attaching after.* fork to.*set breakpoint here.*"
+ set expected_re "Attaching after.* fork to.*"
+ if {$detach == "on"} {
+ append expected_re "Detaching after fork from .*"
+ }
+ append expected_re "set breakpoint here.*"
} elseif {$who == "parent" && $detach == "on"} {
set expected_re "Detaching after fork from .*set breakpoint here.*"
} else {
@@ -218,9 +222,9 @@ proc catch_fork_child_follow {} {
"Temporary breakpoint.*, line $bp_after_fork.*" \
"set follow-fork child, tbreak"
- gdb_test "continue" \
- "Attaching after.* fork to.* at .*$bp_after_fork.*" \
- "set follow-fork child, hit tbreak"
+ set expected_re "Attaching after.* fork to.*Detaching after fork from"
+ append expected_re " .* at .*$bp_after_fork.*"
+ gdb_test "continue" $expected_re "set follow-fork child, hit tbreak"
# The parent has been detached; allow time for any output it might
# generate to arrive, so that output doesn't get confused with
diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp b/gdb/testsuite/gdb.base/foll-vfork.exp
index fe3663c..968db13 100644
--- a/gdb/testsuite/gdb.base/foll-vfork.exp
+++ b/gdb/testsuite/gdb.base/foll-vfork.exp
@@ -121,7 +121,7 @@ proc vfork_parent_follow_through_step {} {
set test "step"
gdb_test_multiple "next" $test {
- -re "Detaching after fork from.*if \\(pid == 0\\).*$gdb_prompt " {
+ -re "Detaching after vfork from.*if \\(pid == 0\\).*$gdb_prompt " {
pass $test
}
}
@@ -146,7 +146,7 @@ proc vfork_parent_follow_to_bp {} {
set test "continue to bp"
gdb_test_multiple "continue" $test {
- -re ".*Detaching after fork from child process.*Breakpoint.*${bp_location}.*$gdb_prompt " {
+ -re ".*Detaching after vfork from child process.*Breakpoint.*${bp_location}.*$gdb_prompt " {
pass $test
}
}
@@ -195,7 +195,7 @@ proc vfork_and_exec_child_follow_to_main_bp {} {
set test "continue to bp"
gdb_test_multiple "continue" $test {
- -re "Attaching after.* vfork to.*xecuting new program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
+ -re "Attaching after.* vfork to.*Detaching vfork parent.*xecuting new program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
pass $test
}
}
@@ -239,7 +239,7 @@ proc vfork_and_exec_child_follow_through_step {} {
#
set linenum [gdb_get_line_number "printf(\"Hello from vforked-prog" ${srcfile2}]
gdb_test_multiple "next" $test {
- -re "Attaching after fork to.*Executing new program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
+ -re "Attaching after vfork to.*Executing new program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
pass "$test"
}
}
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 02/16 v2] Refactor follow-fork message printing
2014-08-21 0:30 ` [PATCH 02/16 v2] Refactor follow-fork message printing Don Breazeal
@ 2014-09-26 19:52 ` Pedro Alves
2014-09-26 20:14 ` Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-09-26 19:52 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 08/21/2014 01:29 AM, Don Breazeal wrote:
> This patch refactors the code that prints messages related to follow-fork
> into functions, and adds a call so that a message is now printed when the
> parent process is detached. Previously in this case the only message was
> notification of attaching to the child. We still do not print any messages
> when following the parent and detaching the child (the default). My
> rationale for this is that from the user's perspective the new child was
> never attached.
>
> The messages now distinguish between fork and vfork.
>
> Note that all of these messages are only printed when 'verbose' is set or
> when debugging is turned on.
>
> This is preparatory work for follow-fork and detach-on-fork on
> extended-remote linux targets.
>
> The test gdb.base/foll-fork.exp was modified to check for the new message.
>
> Tested on x64 Ubuntu Lucid, native only.
>
> Thanks,
> --Don
>
> gdb/
> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>
> * gdb/infrun.c (print_fork_attach): New function.
> (print_fork_detach): New function.
> (follow_fork_inferior): Call print_fork_attach and print_fork_detach.
> (handle_vfork_child_exec_or_exit): Ditto.
>
> gdb/testsuite/
> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>
> * gdb.base/foll-fork.exp (test_follow_fork): Add check for new
> detach message.
> (catch_fork_child_follow): Ditto.
> * gdb.base/foll-vfork.exp (vfork_parent_follow_through_step):
> Modify to check for "vfork" instead of "fork".
> (vfork_parent_follow_to_bp): Ditto.
> (vfork_and_exec_child_follow_through_step): Ditto.
> (vfork_and_exec_child_follow_to_main_bp): Ditto, plus add check
> for new detach message.
>
> ---
> gdb/infrun.c | 94 +++++++++++++++++++-------------
> gdb/testsuite/gdb.base/foll-fork.exp | 12 +++--
> gdb/testsuite/gdb.base/foll-vfork.exp | 8 ++--
> 3 files changed, 68 insertions(+), 46 deletions(-)
>
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index a51c759..34e9295 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -567,6 +567,49 @@ follow_fork (void)
> return should_resume;
> }
>
> +/* Print details about attaching to a process after a fork call. */
> +
> +static void
> +print_fork_attach (pid_t child_pid, pid_t parent_pid, int is_vfork)
As this is called for the child only, I think it'd be good to make
that explicit in the name. E.g., print_attach_fork_child.
> +{
> + if (info_verbose || debug_infrun)
> + {
> + target_terminal_ours ();
We should really be using target_terminal_ours_for_output for
output instead.
> + fprintf_filtered (gdb_stdlog,
> + _("Attaching after process %d "
> + "%s to child process %d.\n"),
> + parent_pid, is_vfork?"vfork":"fork", child_pid);
Spaces around "?" and ":": 'is_vfork ? "vfork" : "fork"'
> + }
> +}
> +
> +/* Print details about detaching from a process after a fork call. */
> +
> +static void
> +print_fork_detach (pid_t pid, int is_parent, int is_vfork, char *vfork_action)
> +{
> + if (info_verbose || debug_infrun)
> + {
> + target_terminal_ours ();
> +
> + if (is_parent && is_vfork)
> + {
> + /* Detaching a vfork parent, so print what the child did
> + that allows the parent to resume. */
> + gdb_assert (vfork_action != NULL && strlen (vfork_action) > 0);
Write: '*vfork_action != '\0' instead of that strlen.
> + fprintf_filtered (gdb_stdlog,
> + "Detaching vfork parent process %d after"
> + " child %s.\n", pid, vfork_action);
This handling of vfork_action is bad for i18n. While at it, this is
missing _(). More below.
> + }
> + else
> + {
> + fprintf_filtered (gdb_stdlog,
> + _("Detaching after %s from %s process %d.\n"),
> + is_vfork?"vfork":"fork",
> + is_parent?"parent":"child", pid);
Spaces around operators. "parent" and "child" really shouldn't
be passed as %s, as this will be awkward when translated. We should
split those out instead, like:
if (is_parent)
{
fprintf_filtered (gdb_stdlog,
_("Detaching after %s from parent process %d.\n"),
is_vfork ? "vfork" : "fork", pid);
}
else
{
fprintf_filtered (gdb_stdlog,
_("Detaching after %s from child process %d.\n"),
is_vfork ? "vfork" : "fork", pid);
}
But after unrolling this, is there really any benefit to
print_fork_detach? It doesn't seem that it'll ever end
up called twice with the same arguments... Seems like
we may be obfuscating more than clarifying with the patch.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 02/16 v2] Refactor follow-fork message printing
2014-09-26 19:52 ` Pedro Alves
@ 2014-09-26 20:14 ` Breazeal, Don
2014-10-03 23:51 ` Breazeal, Don
2014-10-15 16:08 ` Pedro Alves
0 siblings, 2 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-09-26 20:14 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 9/26/2014 12:52 PM, Pedro Alves wrote:
> On 08/21/2014 01:29 AM, Don Breazeal wrote:
>> This patch refactors the code that prints messages related to follow-fork
>> into functions, and adds a call so that a message is now printed when the
>> parent process is detached. Previously in this case the only message was
>> notification of attaching to the child. We still do not print any messages
>> when following the parent and detaching the child (the default). My
>> rationale for this is that from the user's perspective the new child was
>> never attached.
>>
>> The messages now distinguish between fork and vfork.
>>
>> Note that all of these messages are only printed when 'verbose' is set or
>> when debugging is turned on.
>>
>> This is preparatory work for follow-fork and detach-on-fork on
>> extended-remote linux targets.
>>
>> The test gdb.base/foll-fork.exp was modified to check for the new message.
>>
>> Tested on x64 Ubuntu Lucid, native only.
>>
>> Thanks,
>> --Don
>>
>> gdb/
>> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>>
>> * gdb/infrun.c (print_fork_attach): New function.
>> (print_fork_detach): New function.
>> (follow_fork_inferior): Call print_fork_attach and print_fork_detach.
>> (handle_vfork_child_exec_or_exit): Ditto.
>>
>> gdb/testsuite/
>> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>>
>> * gdb.base/foll-fork.exp (test_follow_fork): Add check for new
>> detach message.
>> (catch_fork_child_follow): Ditto.
>> * gdb.base/foll-vfork.exp (vfork_parent_follow_through_step):
>> Modify to check for "vfork" instead of "fork".
>> (vfork_parent_follow_to_bp): Ditto.
>> (vfork_and_exec_child_follow_through_step): Ditto.
>> (vfork_and_exec_child_follow_to_main_bp): Ditto, plus add check
>> for new detach message.
>>
>> ---
>> gdb/infrun.c | 94 +++++++++++++++++++-------------
>> gdb/testsuite/gdb.base/foll-fork.exp | 12 +++--
>> gdb/testsuite/gdb.base/foll-vfork.exp | 8 ++--
>> 3 files changed, 68 insertions(+), 46 deletions(-)
>>
>> diff --git a/gdb/infrun.c b/gdb/infrun.c
>> index a51c759..34e9295 100644
>> --- a/gdb/infrun.c
>> +++ b/gdb/infrun.c
>> @@ -567,6 +567,49 @@ follow_fork (void)
>> return should_resume;
>> }
>>
>> +/* Print details about attaching to a process after a fork call. */
>> +
>> +static void
>> +print_fork_attach (pid_t child_pid, pid_t parent_pid, int is_vfork)
>
> As this is called for the child only, I think it'd be good to make
> that explicit in the name. E.g., print_attach_fork_child.
>
>> +{
>> + if (info_verbose || debug_infrun)
>> + {
>> + target_terminal_ours ();
>
> We should really be using target_terminal_ours_for_output for
> output instead.
>
>> + fprintf_filtered (gdb_stdlog,
>> + _("Attaching after process %d "
>> + "%s to child process %d.\n"),
>> + parent_pid, is_vfork?"vfork":"fork", child_pid);
>
> Spaces around "?" and ":": 'is_vfork ? "vfork" : "fork"'
>
>
>> + }
>> +}
>> +
>> +/* Print details about detaching from a process after a fork call. */
>> +
>> +static void
>> +print_fork_detach (pid_t pid, int is_parent, int is_vfork, char *vfork_action)
>> +{
>> + if (info_verbose || debug_infrun)
>> + {
>> + target_terminal_ours ();
>> +
>> + if (is_parent && is_vfork)
>> + {
>> + /* Detaching a vfork parent, so print what the child did
>> + that allows the parent to resume. */
>> + gdb_assert (vfork_action != NULL && strlen (vfork_action) > 0);
>
> Write: '*vfork_action != '\0' instead of that strlen.
>
>> + fprintf_filtered (gdb_stdlog,
>> + "Detaching vfork parent process %d after"
>> + " child %s.\n", pid, vfork_action);
>
> This handling of vfork_action is bad for i18n. While at it, this is
> missing _(). More below.
>
>> + }
>> + else
>> + {
>> + fprintf_filtered (gdb_stdlog,
>> + _("Detaching after %s from %s process %d.\n"),
>> + is_vfork?"vfork":"fork",
>> + is_parent?"parent":"child", pid);
>
> Spaces around operators. "parent" and "child" really shouldn't
> be passed as %s, as this will be awkward when translated. We should
> split those out instead, like:
>
> if (is_parent)
> {
> fprintf_filtered (gdb_stdlog,
> _("Detaching after %s from parent process %d.\n"),
> is_vfork ? "vfork" : "fork", pid);
> }
> else
> {
> fprintf_filtered (gdb_stdlog,
> _("Detaching after %s from child process %d.\n"),
> is_vfork ? "vfork" : "fork", pid);
> }
Just so I understand (and don't repeat the error), is the problem here
that "parent" and "child" are (a) strings that would need to be
translated, and (b) not in the printf format string?
>
> But after unrolling this, is there really any benefit to
> print_fork_detach? It doesn't seem that it'll ever end
> up called twice with the same arguments... Seems like
> we may be obfuscating more than clarifying with the patch.
My experience of reading and understanding the code was improved by
moving the blocks of printing code out of follow-fork. So for me, it
would be desirable even with a print function for each permutation of
the messages. But it's just a personal preference, so if you'd rather
just drop the whole patch, that's OK with me. Let me know and I'll
either make the requested changes above, or re-work my local branch to
drop this patch.
Thanks for the review,
--Don
>
> Thanks,
> Pedro Alves
>
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 02/16 v2] Refactor follow-fork message printing
2014-09-26 20:14 ` Breazeal, Don
@ 2014-10-03 23:51 ` Breazeal, Don
2014-10-15 16:08 ` Pedro Alves
1 sibling, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-10-03 23:51 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 9/26/2014 1:14 PM, Breazeal, Don wrote:
> On 9/26/2014 12:52 PM, Pedro Alves wrote:
>> On 08/21/2014 01:29 AM, Don Breazeal wrote:
>>> This patch refactors the code that prints messages related to follow-fork
>>> into functions, and adds a call so that a message is now printed when the
>>> parent process is detached. Previously in this case the only message was
>>> notification of attaching to the child. We still do not print any messages
>>> when following the parent and detaching the child (the default). My
>>> rationale for this is that from the user's perspective the new child was
>>> never attached.
>>>
>>> The messages now distinguish between fork and vfork.
>>>
>>> Note that all of these messages are only printed when 'verbose' is set or
>>> when debugging is turned on.
>>>
>>> This is preparatory work for follow-fork and detach-on-fork on
>>> extended-remote linux targets.
>>>
>>> The test gdb.base/foll-fork.exp was modified to check for the new message.
>>>
>>> Tested on x64 Ubuntu Lucid, native only.
>>>
>>> Thanks,
>>> --Don
>>>
>>> gdb/
>>> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>>>
>>> * gdb/infrun.c (print_fork_attach): New function.
>>> (print_fork_detach): New function.
>>> (follow_fork_inferior): Call print_fork_attach and print_fork_detach.
>>> (handle_vfork_child_exec_or_exit): Ditto.
>>>
>>> gdb/testsuite/
>>> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>>>
>>> * gdb.base/foll-fork.exp (test_follow_fork): Add check for new
>>> detach message.
>>> (catch_fork_child_follow): Ditto.
>>> * gdb.base/foll-vfork.exp (vfork_parent_follow_through_step):
>>> Modify to check for "vfork" instead of "fork".
>>> (vfork_parent_follow_to_bp): Ditto.
>>> (vfork_and_exec_child_follow_through_step): Ditto.
>>> (vfork_and_exec_child_follow_to_main_bp): Ditto, plus add check
>>> for new detach message.
>>>
>>> ---
>>> gdb/infrun.c | 94 +++++++++++++++++++-------------
>>> gdb/testsuite/gdb.base/foll-fork.exp | 12 +++--
>>> gdb/testsuite/gdb.base/foll-vfork.exp | 8 ++--
>>> 3 files changed, 68 insertions(+), 46 deletions(-)
>>>
>>> diff --git a/gdb/infrun.c b/gdb/infrun.c
>>> index a51c759..34e9295 100644
>>> --- a/gdb/infrun.c
>>> +++ b/gdb/infrun.c
>>> @@ -567,6 +567,49 @@ follow_fork (void)
>>> return should_resume;
>>> }
>>>
>>> +/* Print details about attaching to a process after a fork call. */
>>> +
>>> +static void
>>> +print_fork_attach (pid_t child_pid, pid_t parent_pid, int is_vfork)
>>
>> As this is called for the child only, I think it'd be good to make
>> that explicit in the name. E.g., print_attach_fork_child.
Done.
>>
>>> +{
>>> + if (info_verbose || debug_infrun)
>>> + {
>>> + target_terminal_ours ();
>>
>> We should really be using target_terminal_ours_for_output for
>> output instead.
Done in both print routines.
>>
>>> + fprintf_filtered (gdb_stdlog,
>>> + _("Attaching after process %d "
>>> + "%s to child process %d.\n"),
>>> + parent_pid, is_vfork?"vfork":"fork", child_pid);
>>
>> Spaces around "?" and ":": 'is_vfork ? "vfork" : "fork"'
Done.
>>
>>
>>> + }
>>> +}
>>> +
>>> +/* Print details about detaching from a process after a fork call. */
>>> +
>>> +static void
>>> +print_fork_detach (pid_t pid, int is_parent, int is_vfork, char *vfork_action)
>>> +{
>>> + if (info_verbose || debug_infrun)
>>> + {
>>> + target_terminal_ours ();
>>> +
>>> + if (is_parent && is_vfork)
>>> + {
>>> + /* Detaching a vfork parent, so print what the child did
>>> + that allows the parent to resume. */
>>> + gdb_assert (vfork_action != NULL && strlen (vfork_action) > 0);
>>
>> Write: '*vfork_action != '\0' instead of that strlen.
Changed the type to enum target_waitkind.
>>
>>> + fprintf_filtered (gdb_stdlog,
>>> + "Detaching vfork parent process %d after"
>>> + " child %s.\n", pid, vfork_action);
>>
>> This handling of vfork_action is bad for i18n. While at it, this is
>> missing _(). More below.
Changed vfork_action to be of type enum target_waitkind, requiring
TARGET_WAITKIND_EXECD or TARGET_WAITKIND_EXITED for the vfork parent
detach messages. It's now handled it much like the handling of the
"fork" vs. "vfork" strings.
>>
>>> + }
>>> + else
>>> + {
>>> + fprintf_filtered (gdb_stdlog,
>>> + _("Detaching after %s from %s process %d.\n"),
>>> + is_vfork?"vfork":"fork",
>>> + is_parent?"parent":"child", pid);
>>
>> Spaces around operators. "parent" and "child" really shouldn't
>> be passed as %s, as this will be awkward when translated. We should
>> split those out instead, like:
>>
>> if (is_parent)
>> {
>> fprintf_filtered (gdb_stdlog,
>> _("Detaching after %s from parent process %d.\n"),
>> is_vfork ? "vfork" : "fork", pid);
>> }
>> else
>> {
>> fprintf_filtered (gdb_stdlog,
>> _("Detaching after %s from child process %d.\n"),
>> is_vfork ? "vfork" : "fork", pid);
>> }
>
> Just so I understand (and don't repeat the error), is the problem here
> that "parent" and "child" are (a) strings that would need to be
> translated, and (b) not in the printf format string?
I changed this more-or-less as you suggested. It would help me to have
the translation problem explained.
>
>>
>> But after unrolling this, is there really any benefit to
>> print_fork_detach? It doesn't seem that it'll ever end
>> up called twice with the same arguments... Seems like
>> we may be obfuscating more than clarifying with the patch.
>
> My experience of reading and understanding the code was improved by
> moving the blocks of printing code out of follow-fork. So for me, it
> would be desirable even with a print function for each permutation of
> the messages. But it's just a personal preference, so if you'd rather
> just drop the whole patch, that's OK with me. Let me know and I'll
> either make the requested changes above, or re-work my local branch to
> drop this patch.
>
I've updated this patch based on your comments, and included it
below. It may not address your "obfuscating more than clarifying"
concern. My take on that issue, stated in a different way, is
that moving the message printing code into separate functions
makes the code in follow_fork_inferior and
handle_vfork_child_exec_or_exit cleaner, especially given that there
is now an additional detach message. I guess the question is
whether that benefit offsets the complexity of print_fork_detach.
Given the updated patch, WDYT?
Tested this by running gdb.base/foll-fork.exp and
gdb.base/foll-vfork.exp on x64 Ubuntu.
Thanks
--Don
gdb/
2014-10-03 Don Breazeal <donb@codesourcery.com>
* gdb/infrun.c (print_fork_attach_child): New function.
(print_fork_detach): New function.
(follow_fork_inferior): Call print_fork_attach_child and
print_fork_detach.
(handle_vfork_child_exec_or_exit): Call print_fork_detach.
gdb/testsuite/
2014-10-03 Don Breazeal <donb@codesourcery.com>
* gdb.base/foll-fork.exp (test_follow_fork): Add check for new
detach message.
(catch_fork_child_follow): Ditto.
* gdb.base/foll-vfork.exp (vfork_parent_follow_through_step):
Modify to check for "vfork" instead of "fork".
(vfork_parent_follow_to_bp): Ditto.
(vfork_and_exec_child_follow_through_step): Ditto.
(vfork_and_exec_child_follow_to_main_bp): Ditto, plus add check
for new detach message.
---
gdb/infrun.c | 115
+++++++++++++++++++++++-----------
gdb/testsuite/gdb.base/foll-fork.exp | 12 ++--
gdb/testsuite/gdb.base/foll-vfork.exp | 10 +--
3 files changed, 92 insertions(+), 45 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 728c160..703ebfe 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -401,6 +401,66 @@ show_follow_fork_mode_string (struct ui_file *file,
int from_tty,
}
\f
+/* Print details about attaching to a child process after a fork call. */
+
+static void
+print_attach_fork_child (pid_t child_pid, pid_t parent_pid, int is_vfork)
+{
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours_for_output ();
+ fprintf_filtered (gdb_stdlog,
+ _("Attaching after process %d %s to child "
+ "process %d.\n"),
+ parent_pid, is_vfork ? "vfork" : "fork", child_pid);
+ }
+}
+
+/* Print details about detaching from a process after a fork call.
+ VFORK_ACTION must be one of TARGET_WAITKIND_EXECD,
TARGET_WAITKIND_EXITED,
+ or TARGET_WAITKIND_IGNORE. Here TARGET_WAITKIND_IGNORE is a placeholder
+ for cases where we are detaching a vfork child and the argument is
+ unused. */
+
+static void
+print_fork_detach (pid_t pid, int is_parent, int is_vfork,
+ enum target_waitkind vfork_action)
+{
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours_for_output ();
+
+ if (is_parent)
+ {
+ if (is_vfork)
+ {
+ /* Detaching a vfork parent, so print what the child did
+ that allows the parent to resume. */
+ gdb_assert (vfork_action == TARGET_WAITKIND_EXECD
+ || vfork_action == TARGET_WAITKIND_EXITED);
+ fprintf_filtered (gdb_stdlog,
+ _("Detaching from vfork parent process %d"
+ " after child %s.\n"),
+ pid, (vfork_action == TARGET_WAITKIND_EXECD
+ ? "exec" : "exit"));
+ }
+ else
+ {
+ /* Detaching fork parent. */
+ fprintf_filtered (gdb_stdlog, _("Detaching after fork from "
+ "parent process %d.\n"), pid);
+ }
+ }
+ else
+ {
+ /* Detaching fork or vfork child. */
+ fprintf_filtered (gdb_stdlog,
+ _("Detaching after %s from child process %d.\n"),
+ is_vfork ? "vfork" : "fork", pid);
+ }
+ }
+}
+
/* 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
@@ -459,14 +519,8 @@ holding the child stopped. Try \"set
detach-on-fork\" or \
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);
- }
+ print_fork_detach (child_pid, follow_child, has_vforked,
+ TARGET_WAITKIND_IGNORE);
}
else
{
@@ -547,20 +601,7 @@ holding the child stopped. Try \"set
detach-on-fork\" or \
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);
- }
+ print_attach_fork_child (child_pid, parent_pid, has_vforked);
/* Add the new inferior first, so that the target_detach below
doesn't unpush the target. */
@@ -596,7 +637,11 @@ holding the child stopped. Try \"set
detach-on-fork\" or \
parent_inf->waiting_for_vfork_done = 0;
}
else if (detach_fork)
- target_detach (NULL, 0);
+ {
+ print_fork_detach (parent_pid, follow_child, has_vforked,
+ TARGET_WAITKIND_IGNORE);
+ target_detach (NULL, 0);
+ }
/* Note that the detach above makes PARENT_INF dangling. */
@@ -928,20 +973,18 @@ handle_vfork_child_exec_or_exit (int exec)
inf->aspace = NULL;
inf->pspace = NULL;
- if (debug_infrun || info_verbose)
+ /* Print verbose/debug info message. Hardcoded 1's designate
+ that we are detaching a parent and that it is after a vfork,
+ respectively. */
+ if (exec)
{
- target_terminal_ours ();
-
- if (exec)
- fprintf_filtered (gdb_stdlog,
- "Detaching vfork parent process "
- "%d after child exec.\n",
- inf->vfork_parent->pid);
- else
- fprintf_filtered (gdb_stdlog,
- "Detaching vfork parent process "
- "%d after child exit.\n",
- inf->vfork_parent->pid);
+ print_fork_detach (inf->vfork_parent->pid, 1, 1,
+ TARGET_WAITKIND_EXECD);
+ }
+ else
+ {
+ print_fork_detach (inf->vfork_parent->pid, 1, 1,
+ TARGET_WAITKIND_EXITED);
}
target_detach (NULL, 0);
diff --git a/gdb/testsuite/gdb.base/foll-fork.exp
b/gdb/testsuite/gdb.base/foll-fork.exp
index ad8b750..06ba1b5 100644
--- a/gdb/testsuite/gdb.base/foll-fork.exp
+++ b/gdb/testsuite/gdb.base/foll-fork.exp
@@ -115,7 +115,11 @@ proc test_follow_fork { who detach cmd } {
# Set up the output we expect to see after we run.
set expected_re ""
if {$who == "child"} {
- set expected_re "Attaching after.* fork to.*set breakpoint here.*"
+ set expected_re "Attaching after.* fork to.*"
+ if {$detach == "on"} {
+ append expected_re "Detaching after fork from .*"
+ }
+ append expected_re "set breakpoint here.*"
} elseif {$who == "parent" && $detach == "on"} {
set expected_re "Detaching after fork from .*set breakpoint here.*"
} else {
@@ -218,9 +222,9 @@ proc catch_fork_child_follow {} {
"Temporary breakpoint.*, line $bp_after_fork.*" \
"set follow-fork child, tbreak"
- gdb_test "continue" \
- "Attaching after.* fork to.* at .*$bp_after_fork.*" \
- "set follow-fork child, hit tbreak"
+ set expected_re "Attaching after.* fork to.*Detaching after fork from"
+ append expected_re " .* at .*$bp_after_fork.*"
+ gdb_test "continue" $expected_re "set follow-fork child, hit tbreak"
# The parent has been detached; allow time for any output it might
# generate to arrive, so that output doesn't get confused with
diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp
b/gdb/testsuite/gdb.base/foll-vfork.exp
index fe3663c..27ecdbc 100644
--- a/gdb/testsuite/gdb.base/foll-vfork.exp
+++ b/gdb/testsuite/gdb.base/foll-vfork.exp
@@ -121,7 +121,7 @@ proc vfork_parent_follow_through_step {} {
set test "step"
gdb_test_multiple "next" $test {
- -re "Detaching after fork from.*if \\(pid == 0\\).*$gdb_prompt " {
+ -re "Detaching after vfork from.*if \\(pid == 0\\).*$gdb_prompt " {
pass $test
}
}
@@ -146,7 +146,7 @@ proc vfork_parent_follow_to_bp {} {
set test "continue to bp"
gdb_test_multiple "continue" $test {
- -re ".*Detaching after fork from child
process.*Breakpoint.*${bp_location}.*$gdb_prompt " {
+ -re ".*Detaching after vfork from child
process.*Breakpoint.*${bp_location}.*$gdb_prompt " {
pass $test
}
}
@@ -171,7 +171,7 @@ proc vfork_child_follow_to_exit {} {
# PR gdb/14766
fail "$test"
}
- -re "Attaching after.* vfork to.*Detaching vfork parent .* after
child exit.*$gdb_prompt " {
+ -re "Attaching after.* vfork to.*Detaching from vfork parent .*
after child exit.*$gdb_prompt " {
pass $test
}
}
@@ -195,7 +195,7 @@ proc vfork_and_exec_child_follow_to_main_bp {} {
set test "continue to bp"
gdb_test_multiple "continue" $test {
- -re "Attaching after.* vfork to.*xecuting new
program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
+ -re "Attaching after.* vfork to.*Detaching from vfork
parent.*xecuting new
program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
pass $test
}
}
@@ -239,7 +239,7 @@ proc vfork_and_exec_child_follow_through_step {} {
#
set linenum [gdb_get_line_number "printf(\"Hello from
vforked-prog" ${srcfile2}]
gdb_test_multiple "next" $test {
- -re "Attaching after fork to.*Executing new
program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
+ -re "Attaching after vfork to.*Executing new
program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
pass "$test"
}
}
--
1.8.1.1
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 02/16 v2] Refactor follow-fork message printing
2014-09-26 20:14 ` Breazeal, Don
2014-10-03 23:51 ` Breazeal, Don
@ 2014-10-15 16:08 ` Pedro Alves
2014-10-22 23:47 ` Breazeal, Don
1 sibling, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-10-15 16:08 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
On 09/26/2014 09:14 PM, Breazeal, Don wrote:
> On 9/26/2014 12:52 PM, Pedro Alves wrote:
>> On 08/21/2014 01:29 AM, Don Breazeal wrote:
>>> This patch refactors the code that prints messages related to follow-fork
>>> into functions, and adds a call so that a message is now printed when the
>>> parent process is detached. Previously in this case the only message was
>>> notification of attaching to the child. We still do not print any messages
>>> when following the parent and detaching the child (the default). My
>>> rationale for this is that from the user's perspective the new child was
>>> never attached.
>>>
>>> The messages now distinguish between fork and vfork.
>>>
>>> Note that all of these messages are only printed when 'verbose' is set or
>>> when debugging is turned on.
>>>
>>> This is preparatory work for follow-fork and detach-on-fork on
>>> extended-remote linux targets.
>>>
>>> The test gdb.base/foll-fork.exp was modified to check for the new message.
>>>
>>> Tested on x64 Ubuntu Lucid, native only.
>>>
>>> Thanks,
>>> --Don
>>>
>>> gdb/
>>> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>>>
>>> * gdb/infrun.c (print_fork_attach): New function.
>>> (print_fork_detach): New function.
>>> (follow_fork_inferior): Call print_fork_attach and print_fork_detach.
>>> (handle_vfork_child_exec_or_exit): Ditto.
>>>
>>> gdb/testsuite/
>>> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>>>
>>> * gdb.base/foll-fork.exp (test_follow_fork): Add check for new
>>> detach message.
>>> (catch_fork_child_follow): Ditto.
>>> * gdb.base/foll-vfork.exp (vfork_parent_follow_through_step):
>>> Modify to check for "vfork" instead of "fork".
>>> (vfork_parent_follow_to_bp): Ditto.
>>> (vfork_and_exec_child_follow_through_step): Ditto.
>>> (vfork_and_exec_child_follow_to_main_bp): Ditto, plus add check
>>> for new detach message.
>>>
>>> ---
>>> gdb/infrun.c | 94 +++++++++++++++++++-------------
>>> gdb/testsuite/gdb.base/foll-fork.exp | 12 +++--
>>> gdb/testsuite/gdb.base/foll-vfork.exp | 8 ++--
>>> 3 files changed, 68 insertions(+), 46 deletions(-)
>>>
>>> diff --git a/gdb/infrun.c b/gdb/infrun.c
>>> index a51c759..34e9295 100644
>>> --- a/gdb/infrun.c
>>> +++ b/gdb/infrun.c
>>> @@ -567,6 +567,49 @@ follow_fork (void)
>>> return should_resume;
>>> }
>>>
>>> +/* Print details about attaching to a process after a fork call. */
>>> +
>>> +static void
>>> +print_fork_attach (pid_t child_pid, pid_t parent_pid, int is_vfork)
>>
>> As this is called for the child only, I think it'd be good to make
>> that explicit in the name. E.g., print_attach_fork_child.
>>
>>> +{
>>> + if (info_verbose || debug_infrun)
>>> + {
>>> + target_terminal_ours ();
>>
>> We should really be using target_terminal_ours_for_output for
>> output instead.
>>
>>> + fprintf_filtered (gdb_stdlog,
>>> + _("Attaching after process %d "
>>> + "%s to child process %d.\n"),
>>> + parent_pid, is_vfork?"vfork":"fork", child_pid);
>>
>> Spaces around "?" and ":": 'is_vfork ? "vfork" : "fork"'
>>
>>
>>> + }
>>> +}
>>> +
>>> +/* Print details about detaching from a process after a fork call. */
>>> +
>>> +static void
>>> +print_fork_detach (pid_t pid, int is_parent, int is_vfork, char *vfork_action)
>>> +{
>>> + if (info_verbose || debug_infrun)
>>> + {
>>> + target_terminal_ours ();
>>> +
>>> + if (is_parent && is_vfork)
>>> + {
>>> + /* Detaching a vfork parent, so print what the child did
>>> + that allows the parent to resume. */
>>> + gdb_assert (vfork_action != NULL && strlen (vfork_action) > 0);
>>
>> Write: '*vfork_action != '\0' instead of that strlen.
>>
>>> + fprintf_filtered (gdb_stdlog,
>>> + "Detaching vfork parent process %d after"
>>> + " child %s.\n", pid, vfork_action);
>>
>> This handling of vfork_action is bad for i18n. While at it, this is
>> missing _(). More below.
>>
>>> + }
>>> + else
>>> + {
>>> + fprintf_filtered (gdb_stdlog,
>>> + _("Detaching after %s from %s process %d.\n"),
>>> + is_vfork?"vfork":"fork",
>>> + is_parent?"parent":"child", pid);
>>
>> Spaces around operators. "parent" and "child" really shouldn't
>> be passed as %s, as this will be awkward when translated. We should
>> split those out instead, like:
>>
>> if (is_parent)
>> {
>> fprintf_filtered (gdb_stdlog,
>> _("Detaching after %s from parent process %d.\n"),
>> is_vfork ? "vfork" : "fork", pid);
>> }
>> else
>> {
>> fprintf_filtered (gdb_stdlog,
>> _("Detaching after %s from child process %d.\n"),
>> is_vfork ? "vfork" : "fork", pid);
>> }
>
> Just so I understand (and don't repeat the error), is the problem here
> that "parent" and "child" are (a) strings that would need to be
> translated, and (b) not in the printf format string?
Sorry, I saw wrongness in more places, and then picked the worst
example. Still, there's no reason to assume that:
1 - the best translation would put the child/parent word in the
same position.
2 - the translation for "parent" and "child" would be the same
in all occurrences.
But a worse example is here:
+ fprintf_filtered (gdb_stdlog,
+ "Detaching vfork parent process %d after"
+ " child %s.\n", pid, vfork_action);
vfork_action is either "exec" or "exit". Here, when I try to figure
out how I'd translate this to Portuguese, I'd probably want
to translate the two sentences differently: I'd probably want to
translate "child exit" naturally (maybe "depois de o processo
filho terminar"), while "child exec" can't be translated so
directly: it'd probably need an auxiliary verb, something
like "depois do o processo filho chamar exec".
Where ".. chamar exec" means "... _called/calling_ exec".
> My experience of reading and understanding the code was improved by
> moving the blocks of printing code out of follow-fork. So for me, it
> would be desirable even with a print function for each permutation of
> the messages. But it's just a personal preference, so if you'd rather
> just drop the whole patch, that's OK with me. Let me know and I'll
> either make the requested changes above, or re-work my local branch to
> drop this patch.
Sorry, I still don't think you're new patch (sent as follow up) is
an improvement... Having to explain the "Hardcoded 1's" in a
comment is a red sign to me. :-/
Could you do a patch that just adds the missing output, and fixes
fork/vfork without moving the printing code to a separate function?
For the fork vs vfork issue, doing ' is_vfork ? "vfork" : "fork" ' is
fine.
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 02/16 v2] Refactor follow-fork message printing
2014-10-15 16:08 ` Pedro Alves
@ 2014-10-22 23:47 ` Breazeal, Don
2014-10-24 12:35 ` Pedro Alves
0 siblings, 1 reply; 110+ messages in thread
From: Breazeal, Don @ 2014-10-22 23:47 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 10/15/2014 9:08 AM, Pedro Alves wrote:
> On 09/26/2014 09:14 PM, Breazeal, Don wrote:
>> On 9/26/2014 12:52 PM, Pedro Alves wrote:
>>> On 08/21/2014 01:29 AM, Don Breazeal wrote:
>>>> This patch refactors the code that prints messages related to follow-fork
---snip---
> Sorry, I still don't think you're new patch (sent as follow up) is
> an improvement... Having to explain the "Hardcoded 1's" in a
> comment is a red sign to me. :-/
Fair enough.
>
> Could you do a patch that just adds the missing output, and fixes
> fork/vfork without moving the printing code to a separate function?
> For the fork vs vfork issue, doing ' is_vfork ? "vfork" : "fork" ' is
> fine.
Thanks for clarifying the i18n issues for me. The revised patch is
included below, with an updated commit message as well. Is this version OK?
thanks,
--Don
This patch modifies the code that prints attach and detach messages
related to following fork and vfork. The changes include using
target_terminal_ours_for_output instead of target_terminal_ours,
printing "vfork" instead of "fork" for all vfork-related messages,
and using _() for the format strings of all of the messages.
We also add a "detach" message for when a fork parent is detached.
Previously in this case the only message was notification of attaching
to the child. We still do not print any messages when following the
parent and detaching the child (the default). The rationale for this
is that from the user's perspective the new child was never attached.
Note that all of these messages are only printed when 'verbose' is set
or when debugging is turned on.
This is preparatory work for follow-fork and detach-on-fork on
extended-remote Linux targets.
The tests gdb.base/foll-fork.exp and gdb.base/foll-vfork.exp were
modified to check for the new message.
Tested gdb.base/foll-fork.exp and gdb.base/foll-vfork.exp on x64 Ubuntu
Lucid, native only.
gdb/
2014-10-22 Don Breazeal <donb@codesourcery.com>
* infrun.c (follow_fork_inferior): Update fork message printing
to use target_terminal_ours_for_output instead of
target_terminal_ours, to use _() for all format strings, to print
"vfork" instead of "fork" for vforks, and to add a detach message.
(handle_vfork_child_exec_or_exit): Update message printing to use
target_terminal_ours_for_output instead of target_terminal_ours, to
use _() for all format strings, and to fix some formatting.
gdb/testsuite/
2014-10-22 Don Breazeal <donb@codesourcery.com>
* gdb.base/foll-fork.exp (test_follow_fork,
catch_fork_child_follow): Check for updated fork messages emitted
from infrun.c.
* gdb.base/foll-vfork.exp (vfork_parent_follow_through_step,
vfork_parent_follow_to_bp, vfork_and_exec_child_follow_to_main_bp,
vfork_and_exec_child_follow_through_step): Check for updated vfork
messages emitted from infrun.c.
---
gdb/infrun.c | 60
+++++++++++++++++++-------------
gdb/testsuite/gdb.base/foll-fork.exp | 12 ++++--
gdb/testsuite/gdb.base/foll-vfork.exp | 8 ++--
3 files changed, 48 insertions(+), 32 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index 728c160..672795c 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -461,10 +461,11 @@ holding the child stopped. Try \"set
detach-on-fork\" or \
if (info_verbose || debug_infrun)
{
- target_terminal_ours ();
+ target_terminal_ours_for_output ();
fprintf_filtered (gdb_stdlog,
- "Detaching after fork from "
- "child process %d.\n",
+ _("Detaching after %s from "
+ "child process %d.\n"),
+ has_vforked ? "vfork" : "fork",
child_pid);
}
}
@@ -549,17 +550,13 @@ holding the child stopped. Try \"set
detach-on-fork\" or \
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);
+ target_terminal_ours_for_output ();
+ fprintf_filtered (gdb_stdlog,
+ _("Attaching after process %d "
+ "%s to child process %d.\n"),
+ parent_pid,
+ has_vforked ? "vfork" : "fork",
+ child_pid);
}
/* Add the new inferior first, so that the target_detach below
@@ -596,7 +593,18 @@ holding the child stopped. Try \"set
detach-on-fork\" or \
parent_inf->waiting_for_vfork_done = 0;
}
else if (detach_fork)
- target_detach (NULL, 0);
+ {
+ if (info_verbose || debug_infrun)
+ {
+ target_terminal_ours_for_output ();
+ fprintf_filtered (gdb_stdlog,
+ _("Detaching after fork from "
+ "child process %d.\n"),
+ child_pid);
+ }
+
+ target_detach (NULL, 0);
+ }
/* Note that the detach above makes PARENT_INF dangling. */
@@ -930,18 +938,22 @@ handle_vfork_child_exec_or_exit (int exec)
if (debug_infrun || info_verbose)
{
- target_terminal_ours ();
+ target_terminal_ours_for_output ();
if (exec)
- fprintf_filtered (gdb_stdlog,
- "Detaching vfork parent process "
- "%d after child exec.\n",
- inf->vfork_parent->pid);
+ {
+ fprintf_filtered (gdb_stdlog,
+ _("Detaching vfork parent process "
+ "%d after child exec.\n"),
+ inf->vfork_parent->pid);
+ }
else
- fprintf_filtered (gdb_stdlog,
- "Detaching vfork parent process "
- "%d after child exit.\n",
- inf->vfork_parent->pid);
+ {
+ fprintf_filtered (gdb_stdlog,
+ _("Detaching vfork parent process "
+ "%d after child exit.\n"),
+ inf->vfork_parent->pid);
+ }
}
target_detach (NULL, 0);
diff --git a/gdb/testsuite/gdb.base/foll-fork.exp
b/gdb/testsuite/gdb.base/foll-fork.exp
index ad8b750..b2e2979 100644
--- a/gdb/testsuite/gdb.base/foll-fork.exp
+++ b/gdb/testsuite/gdb.base/foll-fork.exp
@@ -115,7 +115,11 @@ proc test_follow_fork { who detach cmd } {
# Set up the output we expect to see after we run.
set expected_re ""
if {$who == "child"} {
- set expected_re "Attaching after.* fork to.*set breakpoint here.*"
+ set expected_re "Attaching after.* fork to.*"
+ if {$detach == "on"} {
+ append expected_re "Detaching after fork from .*"
+ }
+ append expected_re "set breakpoint here.*"
} elseif {$who == "parent" && $detach == "on"} {
set expected_re "Detaching after fork from .*set breakpoint here.*"
} else {
@@ -218,9 +222,9 @@ proc catch_fork_child_follow {} {
"Temporary breakpoint.*, line $bp_after_fork.*" \
"set follow-fork child, tbreak"
- gdb_test "continue" \
- "Attaching after.* fork to.* at .*$bp_after_fork.*" \
- "set follow-fork child, hit tbreak"
+ set expected_re "Attaching after.* fork to.*Detaching after fork from"
+ append expected_re ".* at .*$bp_after_fork.*"
+ gdb_test "continue" $expected_re "set follow-fork child, hit tbreak"
# The parent has been detached; allow time for any output it might
# generate to arrive, so that output doesn't get confused with
diff --git a/gdb/testsuite/gdb.base/foll-vfork.exp
b/gdb/testsuite/gdb.base/foll-vfork.exp
index fe3663c..968db13 100644
--- a/gdb/testsuite/gdb.base/foll-vfork.exp
+++ b/gdb/testsuite/gdb.base/foll-vfork.exp
@@ -121,7 +121,7 @@ proc vfork_parent_follow_through_step {} {
set test "step"
gdb_test_multiple "next" $test {
- -re "Detaching after fork from.*if \\(pid == 0\\).*$gdb_prompt " {
+ -re "Detaching after vfork from.*if \\(pid == 0\\).*$gdb_prompt " {
pass $test
}
}
@@ -146,7 +146,7 @@ proc vfork_parent_follow_to_bp {} {
set test "continue to bp"
gdb_test_multiple "continue" $test {
- -re ".*Detaching after fork from child
process.*Breakpoint.*${bp_location}.*$gdb_prompt " {
+ -re ".*Detaching after vfork from child
process.*Breakpoint.*${bp_location}.*$gdb_prompt " {
pass $test
}
}
@@ -195,7 +195,7 @@ proc vfork_and_exec_child_follow_to_main_bp {} {
set test "continue to bp"
gdb_test_multiple "continue" $test {
- -re "Attaching after.* vfork to.*xecuting new
program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
+ -re "Attaching after.* vfork to.*Detaching vfork parent.*xecuting
new program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
pass $test
}
}
@@ -239,7 +239,7 @@ proc vfork_and_exec_child_follow_through_step {} {
#
set linenum [gdb_get_line_number "printf(\"Hello from
vforked-prog" ${srcfile2}]
gdb_test_multiple "next" $test {
- -re "Attaching after fork to.*Executing new
program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
+ -re "Attaching after vfork to.*Executing new
program.*Breakpoint.*vforked-prog.c:${linenum}.*$gdb_prompt " {
pass "$test"
}
}
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 02/16 v2] Refactor follow-fork message printing
2014-10-22 23:47 ` Breazeal, Don
@ 2014-10-24 12:35 ` Pedro Alves
2014-10-24 18:45 ` Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-10-24 12:35 UTC (permalink / raw)
To: Breazeal, Don, gdb-patches
On 10/23/2014 12:46 AM, Breazeal, Don wrote:
> On 10/15/2014 9:08 AM, Pedro Alves wrote:
>> > On 09/26/2014 09:14 PM, Breazeal, Don wrote:
>>> >> On 9/26/2014 12:52 PM, Pedro Alves wrote:
>>>> >>> On 08/21/2014 01:29 AM, Don Breazeal wrote:
>>>>> >>>> This patch refactors the code that prints messages related to follow-fork
> ---snip---
>
>> > Sorry, I still don't think you're new patch (sent as follow up) is
>> > an improvement... Having to explain the "Hardcoded 1's" in a
>> > comment is a red sign to me. :-/
> Fair enough.
>
>> >
>> > Could you do a patch that just adds the missing output, and fixes
>> > fork/vfork without moving the printing code to a separate function?
>> > For the fork vs vfork issue, doing ' is_vfork ? "vfork" : "fork" ' is
>> > fine.
> Thanks for clarifying the i18n issues for me. The revised patch is
> included below, with an updated commit message as well. Is this version OK?
Yes, thanks!
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 02/16 v2] Refactor follow-fork message printing
2014-10-24 12:35 ` Pedro Alves
@ 2014-10-24 18:45 ` Breazeal, Don
0 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-10-24 18:45 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 10/24/2014 5:35 AM, Pedro Alves wrote:
> On 10/23/2014 12:46 AM, Breazeal, Don wrote:
>> On 10/15/2014 9:08 AM, Pedro Alves wrote:
>>>> On 09/26/2014 09:14 PM, Breazeal, Don wrote:
>>>>>> On 9/26/2014 12:52 PM, Pedro Alves wrote:
>>>>>>>> On 08/21/2014 01:29 AM, Don Breazeal wrote:
>>>>>>>>>> This patch refactors the code that prints messages related to follow-fork
>> ---snip---
>>
>>>> Sorry, I still don't think you're new patch (sent as follow up) is
>>>> an improvement... Having to explain the "Hardcoded 1's" in a
>>>> comment is a red sign to me. :-/
>> Fair enough.
>>
>>>>
>>>> Could you do a patch that just adds the missing output, and fixes
>>>> fork/vfork without moving the printing code to a separate function?
>>>> For the fork vs vfork issue, doing ' is_vfork ? "vfork" : "fork" ' is
>>>> fine.
>> Thanks for clarifying the i18n issues for me. The revised patch is
>> included below, with an updated commit message as well. Is this version OK?
>
> Yes, thanks!
>
> Pedro Alves
>
Thanks, this is pushed in.
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 06/16 v2] Extended-remote Linux follow fork
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (14 preceding siblings ...)
2014-08-21 0:30 ` [PATCH 02/16 v2] Refactor follow-fork message printing Don Breazeal
@ 2014-08-21 0:31 ` Don Breazeal
2014-09-19 20:57 ` Breazeal, Don
2014-08-21 0:31 ` [PATCH 07/16 v2] Extended-remote arch-specific " Don Breazeal
` (10 subsequent siblings)
26 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:31 UTC (permalink / raw)
To: gdb-patches
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 <donb@codesourcery.com>
* 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 <donb@codesourcery.com>
* gdbthread.h (struct thread_info) <pending_follow>: 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) <follow_fork>: Initialize new member.
(initialize_low): Add PTRACE_O_TRACEFORK option.
* linux-low.h (struct lwp_info) <waitstatus>: New member.
* lynx-low.c (lynx_target_ops) <follow_fork>: Initialize new member.
* nto-low.c (nto_target_ops) <follow_fork>: 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) <follow_fork>: Initialize new member.
* target.h (struct target_ops) <follow_fork>: New member.
(target_follow_fork): Define macro.
* win32-low.c (win32_target_ops) <follow_fork>: 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(-)
diff --git a/gdb/gdbserver/gdbthread.h b/gdb/gdbserver/gdbthread.h
index fe0a75e..3691772 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 f501c05..3fb45b1 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;
+ 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
@@ -2473,6 +2622,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
@@ -2836,7 +2998,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);
@@ -2858,6 +3021,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
@@ -2956,7 +3126,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_inferior->last_resume_kind == resume_stop
&& WSTOPSIG (w) == SIGSTOP)
@@ -2973,7 +3153,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));
}
@@ -4762,8 +4942,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. */
@@ -6047,6 +6227,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,
@@ -6162,7 +6343,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 0b27432..a5e8a97 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 a48c6dd..91feb3f 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 327677a..5152a14 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_inferior;
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_inferior = current_inferior;
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index b4fd206..4dbdeb8 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2563,6 +2563,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)
@@ -2628,6 +2651,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 fc1ec73..6c12e84 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -104,6 +104,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);
@@ -437,6 +442,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 7337034..2d18aa1 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 <fcntl.h>
#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,
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 <main>");
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);
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 06/16 v2] Extended-remote Linux follow fork
2014-08-21 0:31 ` [PATCH 06/16 v2] Extended-remote Linux follow fork Don Breazeal
@ 2014-09-19 20:57 ` Breazeal, Don
0 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-09-19 20:57 UTC (permalink / raw)
To: gdb-patches
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 <donb@codesourcery.com>
>
> * 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 <donb@codesourcery.com>
>
> * gdbthread.h (struct thread_info) <pending_follow>: 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) <follow_fork>: Initialize new member.
> (initialize_low): Add PTRACE_O_TRACEFORK option.
> * linux-low.h (struct lwp_info) <waitstatus>: New member.
> * lynx-low.c (lynx_target_ops) <follow_fork>: Initialize new member.
> * nto-low.c (nto_target_ops) <follow_fork>: 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) <follow_fork>: Initialize new member.
> * target.h (struct target_ops) <follow_fork>: New member.
> (target_follow_fork): Define macro.
> * win32-low.c (win32_target_ops) <follow_fork>: 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(-)
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 <fcntl.h>
#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,
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 <main>");
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);
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 07/16 v2] Extended-remote arch-specific follow fork
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (15 preceding siblings ...)
2014-08-21 0:31 ` [PATCH 06/16 v2] Extended-remote Linux follow fork Don Breazeal
@ 2014-08-21 0:31 ` Don Breazeal
2014-09-19 21:26 ` Breazeal, Don
2014-08-21 0:31 ` [PATCH 05/16 v2] GDBserver clone breakpoint list Don Breazeal
` (9 subsequent siblings)
26 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:31 UTC (permalink / raw)
To: gdb-patches
This patch implements the architecture-specific pieces of follow-fork,
which in the current implementation copyies the parent's debug register
state into the new child's data structures. This is required for x86,
arm, aarch64, and mips.
I followed the native implementation as closely as I could by
implementing a new linux_target_ops function 'new_fork', which is
analogous to 'linux_nat_new_fork' in linux-nat.c. In gdbserver, the debug
registers are stored in the process list, instead of an
architecture-specific list, so the function arguments are process_info
pointers instead of an lwp_info and a pid as in the native implementation.
In the MIPS implementation the debug register mirror is stored differently
from x86, ARM, and aarch64, so instead of doing a simple structure assignment
I had to clone the list of watchpoint structures.
Tested using gdb.threads/watchpoint-fork.exp on x86, and ran manual tests
on a MIPS board.
I ran manual tests on an ARM board, but on the boards I had access to at
the time, hardware watchpoints did not work, even in the unmodified version
of the debugger. The kernel on one of the boards should have been new
enough to provide the support. I didn't debug it further at the time. If
someone is able to test this easily, please do. Otherwise I plan to
come back to this.
I don't currently have access to an aarch64 board, so again if someone is
able to test this easily, please do.
Thanks
--Don
gdb/gdbserver/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* linux-aarch64-low.c (aarch64_linux_new_fork): New function.
(the_low_target) <new_fork>: Initialize new member.
* linux-arm-low.c (arm_new_fork): New function.
(the_low_target) <new_fork>: Initialize new member.
* linux-low.c (handle_extended_wait): Call new target function
new_fork.
* linux-low.h (struct linux_target_ops) <new_fork>: New member.
* linux-mips-low.c (mips_add_watchpoint): New function
extracted from mips_insert_point.
(the_low_target) <new_fork>: Initialize new member.
(mips_linux_new_fork): New function.
(mips_insert_point): Call mips_add_watchpoint.
* linux-x86-low.c (x86_linux_new_fork): New function.
(the_low_target) <new_fork>: Initialize new member.
---
gdb/gdbserver/linux-aarch64-low.c | 28 +++++++++++++
gdb/gdbserver/linux-arm-low.c | 26 +++++++++++++
gdb/gdbserver/linux-low.c | 4 ++
gdb/gdbserver/linux-low.h | 3 +
gdb/gdbserver/linux-mips-low.c | 76 +++++++++++++++++++++++++++++++------
gdb/gdbserver/linux-x86-low.c | 29 ++++++++++++++
6 files changed, 154 insertions(+), 12 deletions(-)
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index 6066e15..915bc21 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -1132,6 +1132,33 @@ aarch64_linux_new_thread (void)
return info;
}
+static void
+aarch64_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
@@ -1296,6 +1323,7 @@ struct linux_target_ops the_low_target =
NULL,
aarch64_linux_new_process,
aarch64_linux_new_thread,
+ aarch64_linux_new_fork,
aarch64_linux_prepare_to_resume,
};
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index c4cfbd4..bfc9c24 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -717,6 +717,31 @@ arm_new_thread (void)
return info;
}
+static void
+arm_new_fork (struct process_info *parent, struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
static void
@@ -920,6 +945,7 @@ struct linux_target_ops the_low_target = {
NULL, /* siginfo_fixup */
arm_new_process,
arm_new_thread,
+ arm_new_fork,
arm_prepare_to_resume,
};
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 3fb45b1..852d02e 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -442,6 +442,10 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
child_proc->tdesc = tdesc;
child_lwp->must_set_ptrace_flags = 1;
+ /* 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 target processing. */
current_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED;
current_inferior->pending_follow.value.related_pid = ptid;
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index a903430..53d1c24 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -185,6 +185,9 @@ struct linux_target_ops
allocate it here. */
struct arch_lwp_info * (*new_thread) (void);
+ /* Hook to call, if any, when a new fork is attached. */
+ void (*new_fork) (struct process_info *parent, struct process_info *child);
+
/* Hook to call prior to resuming a thread. */
void (*prepare_to_resume) (struct lwp_info *);
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index 1b2160b..693a653 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -343,6 +343,68 @@ mips_linux_new_thread (void)
return info;
}
+/* Create a new mips_watchpoint and add it to the list. */
+
+static void
+mips_add_watchpoint (struct arch_process_info *private, CORE_ADDR addr,
+ int len, int watch_type)
+{
+ struct mips_watchpoint *new_watch;
+ struct mips_watchpoint **pw;
+
+ new_watch = xmalloc (sizeof (struct mips_watchpoint));
+ new_watch->addr = addr;
+ new_watch->len = len;
+ new_watch->type = watch_type;
+ new_watch->next = NULL;
+
+ pw = &private->current_watches;
+ while (*pw != NULL)
+ pw = &(*pw)->next;
+ *pw = new_watch;
+}
+
+/* Hook to call when a new fork is attached. */
+
+static void
+mips_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ struct arch_process_info *parent_private;
+ struct arch_process_info *child_private;
+ struct mips_watchpoint *wp;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ parent_private = parent->private->arch_private;
+ child_private = child->private->arch_private;
+
+ child_private->watch_readback_valid = parent_private->watch_readback_valid;
+ child_private->watch_readback = parent_private->watch_readback;
+
+ for (wp = parent_private->current_watches; wp != NULL; wp = wp->next)
+ mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type);
+
+ child_private->watch_mirror = parent_private->watch_mirror;
+}
/* This is the implementation of linux_target_ops method
prepare_to_resume. If the watch regs have changed, update the
thread's copies. */
@@ -396,8 +458,6 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
struct process_info *proc = current_process ();
struct arch_process_info *private = proc->private->arch_private;
struct pt_watch_regs regs;
- struct mips_watchpoint *new_watch;
- struct mips_watchpoint **pw;
int pid;
long lwpid;
enum target_hw_bp_type watch_type;
@@ -424,16 +484,7 @@ mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
return -1;
/* It fit. Stick it on the end of the list. */
- new_watch = xmalloc (sizeof (struct mips_watchpoint));
- new_watch->addr = addr;
- new_watch->len = len;
- new_watch->type = watch_type;
- new_watch->next = NULL;
-
- pw = &private->current_watches;
- while (*pw != NULL)
- pw = &(*pw)->next;
- *pw = new_watch;
+ mips_add_watchpoint (private, addr, len, watch_type);
private->watch_mirror = regs;
@@ -844,6 +895,7 @@ struct linux_target_ops the_low_target = {
NULL, /* siginfo_fixup */
mips_linux_new_process,
mips_linux_new_thread,
+ mips_linux_new_fork,
mips_linux_prepare_to_resume
};
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 7a8a473..48466ac 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -770,6 +770,34 @@ x86_linux_new_thread (void)
return info;
}
+/* Target routine for linux_new_fork. */
+
+static void
+x86_linux_new_fork (struct process_info *parent, struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
@@ -3432,6 +3460,7 @@ struct linux_target_ops the_low_target =
x86_siginfo_fixup,
x86_linux_new_process,
x86_linux_new_thread,
+ x86_linux_new_fork,
x86_linux_prepare_to_resume,
x86_linux_process_qsupported,
x86_supports_tracepoints,
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 07/16 v2] Extended-remote arch-specific follow fork
2014-08-21 0:31 ` [PATCH 07/16 v2] Extended-remote arch-specific " Don Breazeal
@ 2014-09-19 21:26 ` Breazeal, Don
0 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-09-19 21:26 UTC (permalink / raw)
To: gdb-patches
This is an update to this patch that makes inheritance of hardware
watchpoints across a fork work on ARM targets. I had noted previously
that I had not tested inheritance of hardware watchpoints across a fork
on ARM. I have not completed that testing, which exposed a couple of
problems.
The difference between the previous patch and this one is that the
function gdbserver/linux-arm-low.c:arm_new_fork, primarily so that the
arrays of flags used to mark hardware breakpoints and watchpoints as
"changed" are set for the new forked process.
Note that I still have no way to test the aarch64 target, but I did
examine the code and determined that it doesn't require the changes made
for the ARM target.
Thanks,
--Don
On 8/20/2014 5:29 PM, Don Breazeal wrote:
> This patch implements the architecture-specific pieces of follow-fork,
> which in the current implementation copyies the parent's debug register
> state into the new child's data structures. This is required for x86,
> arm, aarch64, and mips.
>
> I followed the native implementation as closely as I could by
> implementing a new linux_target_ops function 'new_fork', which is
> analogous to 'linux_nat_new_fork' in linux-nat.c. In gdbserver, the debug
> registers are stored in the process list, instead of an
> architecture-specific list, so the function arguments are process_info
> pointers instead of an lwp_info and a pid as in the native implementation.
>
> In the MIPS implementation the debug register mirror is stored differently
> from x86, ARM, and aarch64, so instead of doing a simple structure assignment
> I had to clone the list of watchpoint structures.
>
> Tested using gdb.threads/watchpoint-fork.exp on x86, and ran manual tests
> on a MIPS board.
>
> I ran manual tests on an ARM board, but on the boards I had access to at
> the time, hardware watchpoints did not work, even in the unmodified version
> of the debugger. The kernel on one of the boards should have been new
> enough to provide the support. I didn't debug it further at the time. If
> someone is able to test this easily, please do. Otherwise I plan to
> come back to this.
>
> I don't currently have access to an aarch64 board, so again if someone is
> able to test this easily, please do.
>
> Thanks
> --Don
>
> gdb/gdbserver/
> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>
> * linux-aarch64-low.c (aarch64_linux_new_fork): New function.
> (the_low_target) <new_fork>: Initialize new member.
> * linux-arm-low.c (arm_new_fork): New function.
> (the_low_target) <new_fork>: Initialize new member.
> * linux-low.c (handle_extended_wait): Call new target function
> new_fork.
> * linux-low.h (struct linux_target_ops) <new_fork>: New member.
> * linux-mips-low.c (mips_add_watchpoint): New function
> extracted from mips_insert_point.
> (the_low_target) <new_fork>: Initialize new member.
> (mips_linux_new_fork): New function.
> (mips_insert_point): Call mips_add_watchpoint.
> * linux-x86-low.c (x86_linux_new_fork): New function.
> (the_low_target) <new_fork>: Initialize new member.
>
---
gdb/gdbserver/linux-aarch64-low.c | 28 +++++++++++++
gdb/gdbserver/linux-arm-low.c | 42 ++++++++++++++++++++
gdb/gdbserver/linux-low.c | 4 ++
gdb/gdbserver/linux-low.h | 3 +
gdb/gdbserver/linux-mips-low.c | 76
+++++++++++++++++++++++++++++++------
gdb/gdbserver/linux-x86-low.c | 29 ++++++++++++++
6 files changed, 170 insertions(+), 12 deletions(-)
diff --git a/gdb/gdbserver/linux-aarch64-low.c
b/gdb/gdbserver/linux-aarch64-low.c
index 654b319..31ec0d7 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -1129,6 +1129,33 @@ aarch64_linux_new_thread (void)
return info;
}
+static void
+aarch64_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
@@ -1293,6 +1320,7 @@ struct linux_target_ops the_low_target =
NULL,
aarch64_linux_new_process,
aarch64_linux_new_thread,
+ aarch64_linux_new_fork,
aarch64_linux_prepare_to_resume,
};
diff --git a/gdb/gdbserver/linux-arm-low.c b/gdb/gdbserver/linux-arm-low.c
index 8b72523..fe9c34e 100644
--- a/gdb/gdbserver/linux-arm-low.c
+++ b/gdb/gdbserver/linux-arm-low.c
@@ -717,6 +717,47 @@ arm_new_thread (void)
return info;
}
+static void
+arm_new_fork (struct process_info *parent, struct process_info *child)
+{
+ struct arch_process_info *parent_proc_info =
parent->private->arch_private;
+ struct arch_process_info *child_proc_info = child->private->arch_private;
+ struct lwp_info *child_lwp;
+ struct arch_lwp_info *child_lwp_info;
+ int i;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child_proc_info = *parent_proc_info;
+
+ /* Mark all the hardware breakpoints and watchpoints as changed to
+ make sure that the registers will be updated. */
+ child_lwp = find_lwp_pid (ptid_of (child));
+ child_lwp_info = child_lwp->arch_private;
+ for (i = 0; i < MAX_BPTS; i++)
+ child_lwp_info->bpts_changed[i] = 1;
+ for (i = 0; i < MAX_WPTS; i++)
+ child_lwp_info->wpts_changed[i] = 1;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
static void
@@ -920,6 +961,7 @@ struct linux_target_ops the_low_target = {
NULL, /* siginfo_fixup */
arm_new_process,
arm_new_thread,
+ arm_new_fork,
arm_prepare_to_resume,
};
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 56993bd..1fc9be9 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -442,6 +442,10 @@ handle_extended_wait (struct lwp_info *event_child,
int wstat)
child_proc->tdesc = tdesc;
child_lwp->must_set_ptrace_flags = 1;
+ /* 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 target processing. */
current_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED;
current_inferior->pending_follow.value.related_pid = ptid;
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index a903430..53d1c24 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -185,6 +185,9 @@ struct linux_target_ops
allocate it here. */
struct arch_lwp_info * (*new_thread) (void);
+ /* Hook to call, if any, when a new fork is attached. */
+ void (*new_fork) (struct process_info *parent, struct process_info
*child);
+
/* Hook to call prior to resuming a thread. */
void (*prepare_to_resume) (struct lwp_info *);
diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c
index 0fc8cb4..d5db79d 100644
--- a/gdb/gdbserver/linux-mips-low.c
+++ b/gdb/gdbserver/linux-mips-low.c
@@ -344,6 +344,68 @@ mips_linux_new_thread (void)
return info;
}
+/* Create a new mips_watchpoint and add it to the list. */
+
+static void
+mips_add_watchpoint (struct arch_process_info *private, CORE_ADDR addr,
+ int len, int watch_type)
+{
+ struct mips_watchpoint *new_watch;
+ struct mips_watchpoint **pw;
+
+ new_watch = xmalloc (sizeof (struct mips_watchpoint));
+ new_watch->addr = addr;
+ new_watch->len = len;
+ new_watch->type = watch_type;
+ new_watch->next = NULL;
+
+ pw = &private->current_watches;
+ while (*pw != NULL)
+ pw = &(*pw)->next;
+ *pw = new_watch;
+}
+
+/* Hook to call when a new fork is attached. */
+
+static void
+mips_linux_new_fork (struct process_info *parent,
+ struct process_info *child)
+{
+ struct arch_process_info *parent_private;
+ struct arch_process_info *child_private;
+ struct mips_watchpoint *wp;
+
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ parent_private = parent->private->arch_private;
+ child_private = child->private->arch_private;
+
+ child_private->watch_readback_valid =
parent_private->watch_readback_valid;
+ child_private->watch_readback = parent_private->watch_readback;
+
+ for (wp = parent_private->current_watches; wp != NULL; wp = wp->next)
+ mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type);
+
+ child_private->watch_mirror = parent_private->watch_mirror;
+}
/* This is the implementation of linux_target_ops method
prepare_to_resume. If the watch regs have changed, update the
thread's copies. */
@@ -397,8 +459,6 @@ mips_insert_point (enum raw_bkpt_type type,
CORE_ADDR addr,
struct process_info *proc = current_process ();
struct arch_process_info *private = proc->private->arch_private;
struct pt_watch_regs regs;
- struct mips_watchpoint *new_watch;
- struct mips_watchpoint **pw;
int pid;
long lwpid;
enum target_hw_bp_type watch_type;
@@ -425,16 +485,7 @@ mips_insert_point (enum raw_bkpt_type type,
CORE_ADDR addr,
return -1;
/* It fit. Stick it on the end of the list. */
- new_watch = xmalloc (sizeof (struct mips_watchpoint));
- new_watch->addr = addr;
- new_watch->len = len;
- new_watch->type = watch_type;
- new_watch->next = NULL;
-
- pw = &private->current_watches;
- while (*pw != NULL)
- pw = &(*pw)->next;
- *pw = new_watch;
+ mips_add_watchpoint (private, addr, len, watch_type);
private->watch_mirror = regs;
@@ -845,6 +896,7 @@ struct linux_target_ops the_low_target = {
NULL, /* siginfo_fixup */
mips_linux_new_process,
mips_linux_new_thread,
+ mips_linux_new_fork,
mips_linux_prepare_to_resume
};
diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c
index 838e7c9..10b8a56 100644
--- a/gdb/gdbserver/linux-x86-low.c
+++ b/gdb/gdbserver/linux-x86-low.c
@@ -768,6 +768,34 @@ x86_linux_new_thread (void)
return info;
}
+/* Target routine for linux_new_fork. */
+
+static void
+x86_linux_new_fork (struct process_info *parent, struct process_info
*child)
+{
+ /* These are allocated by linux_add_process. */
+ gdb_assert (parent->private != NULL
+ && parent->private->arch_private != NULL);
+ gdb_assert (child->private != NULL
+ && child->private->arch_private != NULL);
+
+ /* Linux kernel before 2.6.33 commit
+ 72f674d203cd230426437cdcf7dd6f681dad8b0d
+ will inherit hardware debug registers from parent
+ on fork/vfork/clone. Newer Linux kernels create such tasks with
+ zeroed debug registers.
+
+ GDB core assumes the child inherits the watchpoints/hw
+ breakpoints of the parent, and will remove them all from the
+ forked off process. Copy the debug registers mirrors into the
+ new process so that all breakpoints and watchpoints can be
+ removed together. The debug registers mirror will become zeroed
+ in the end before detaching the forked off process, thus making
+ this compatible with older Linux kernels too. */
+
+ *child->private->arch_private = *parent->private->arch_private;
+}
+
/* Called when resuming a thread.
If the debug regs have changed, update the thread's copies. */
@@ -3428,6 +3456,7 @@ struct linux_target_ops the_low_target =
x86_siginfo_fixup,
x86_linux_new_process,
x86_linux_new_thread,
+ x86_linux_new_fork,
x86_linux_prepare_to_resume,
x86_linux_process_qsupported,
x86_supports_tracepoints,
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 05/16 v2] GDBserver clone breakpoint list
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (16 preceding siblings ...)
2014-08-21 0:31 ` [PATCH 07/16 v2] Extended-remote arch-specific " Don Breazeal
@ 2014-08-21 0:31 ` Don Breazeal
2014-10-15 17:40 ` Pedro Alves
2014-08-21 0:32 ` [PATCH 08/16 v2] Extended-remote follow vfork Don Breazeal
` (8 subsequent siblings)
26 siblings, 1 reply; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:31 UTC (permalink / raw)
To: gdb-patches
This patch implements gdbserver routines to clone the breakpoint lists of a
process, duplicating them for another process. In gdbserver, each process
maintains its own independent breakpoint list. When a fork call creates a
child, all of the breakpoints currently inserted in the parent process are
also inserted in the child process, but there is nothing to describe them
in the data structures related to the child. The child must have a
breakpoint list describing them so that they can be removed (if detaching)
or recognized (if following). Implementation is a mechanical process of
just cloning the lists in several new functions in gdbserver/mem-break.c.
Tested by building, since none of the new functions are called yet. This
was tested with the subsequent patch 6, the follow-fork patch.
Thanks
--Don
gdb/gdbserver/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* mem-break.c (APPEND_TO_LIST): Define macro.
(clone_agent_expr): New function.
(clone_one_breakpoint): New function.
(clone_all_breakpoints): New function.
* mem-break.h: Declare new functions.
---
gdb/gdbserver/mem-break.c | 104 +++++++++++++++++++++++++++++++++++++++++++++
gdb/gdbserver/mem-break.h | 6 +++
2 files changed, 110 insertions(+), 0 deletions(-)
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index 2ce3ab2..7a6062c 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -28,6 +28,22 @@ int breakpoint_len;
#define MAX_BREAKPOINT_LEN 8
+/* Helper macro used in loops that append multiple items to a singly-linked
+ list instead of inserting items at the head of the list, as, say, in the
+ breakpoint lists. LISTPP is a pointer to the pointer that is the head of
+ the new list. ITEMP is a pointer to the item to be added to the list.
+ TAILP must be defined to be the same type as ITEMP, and initialized to
+ NULL. */
+
+#define APPEND_TO_LIST(listpp, itemp, tailp) \
+ { \
+ if (tailp == NULL) \
+ *(listpp) = itemp; \
+ else \
+ tailp->next = itemp; \
+ tailp = itemp; \
+ }
+
/* GDB will never try to install multiple breakpoints at the same
address. However, we can see GDB requesting to insert a breakpoint
at an address is had already inserted one previously in a few
@@ -1878,3 +1894,91 @@ free_all_breakpoints (struct process_info *proc)
while (proc->breakpoints)
delete_breakpoint_1 (proc, proc->breakpoints);
}
+
+/* Clone an agent expression. */
+
+static void
+clone_agent_expr (struct agent_expr **new_ax, struct agent_expr *src_ax)
+{
+ struct agent_expr *ax;
+
+ ax = xcalloc (1, sizeof (*ax));
+ ax->length = src_ax->length;
+ ax->bytes = xcalloc (ax->length, 1);
+ memcpy (ax->bytes, src_ax->bytes, ax->length);
+ *new_ax = ax;
+}
+
+/* Deep-copy the contents of one breakpoint to another. */
+
+static void
+clone_one_breakpoint (struct breakpoint **new_bkpt,
+ struct raw_breakpoint **new_raw, struct breakpoint *src)
+{
+ struct breakpoint *dest;
+ struct raw_breakpoint *dest_raw;
+ struct point_cond_list *current_cond;
+ struct point_cond_list *new_cond;
+ struct point_cond_list *cond_tail = NULL;
+ struct point_command_list *current_cmd;
+ struct point_command_list *new_cmd;
+ struct point_command_list *cmd_tail = NULL;
+
+ /* Clone the raw breakpoint. */
+ dest_raw = xcalloc (1, sizeof (*dest_raw));
+ dest_raw->raw_type = src->raw->raw_type;
+ dest_raw->refcount = src->raw->refcount;
+ dest_raw->pc = src->raw->pc;
+ dest_raw->size = src->raw->size;
+ memcpy (dest_raw->old_data, src->raw->old_data, MAX_BREAKPOINT_LEN);
+ dest_raw->inserted = src->raw->inserted;
+
+ /* Clone the high-level breakpoint. */
+ dest = xcalloc (1, sizeof (*dest));
+ dest->type = src->type;
+ dest->raw = dest_raw;
+ dest->handler = src->handler;
+
+ /* Clone the condition list. */
+ for (current_cond = src->cond_list; current_cond != NULL;
+ current_cond = current_cond->next)
+ {
+ new_cond = xcalloc (1, sizeof (*new_cond));
+ clone_agent_expr (&new_cond->cond, current_cond->cond);
+ APPEND_TO_LIST (&dest->cond_list, new_cond, cond_tail);
+ }
+
+ /* Clone the command list. */
+ for (current_cmd = src->command_list; current_cmd != NULL;
+ current_cmd = current_cmd->next)
+ {
+ new_cmd = xcalloc (1, sizeof (*new_cmd));
+ clone_agent_expr (&new_cmd->cmd, current_cmd->cmd);
+ new_cmd->persistence = current_cmd->persistence;
+ APPEND_TO_LIST (&dest->command_list, new_cmd, cmd_tail);
+ }
+ *new_bkpt = dest;
+ *new_raw = dest_raw;
+}
+
+/* Create a new breakpoint list NEW_LIST that is a copy of SRC. Create
+ the corresponding new raw_breakpoint list NEW_RAW_LIST as well. */
+
+void
+clone_all_breakpoints (struct breakpoint **new_list,
+ struct raw_breakpoint **new_raw_list,
+ struct breakpoint *src)
+{
+ struct breakpoint *bp;
+ struct breakpoint *new_bkpt;
+ struct raw_breakpoint *new_raw_bkpt;
+ struct breakpoint *bkpt_tail = NULL;
+ struct raw_breakpoint *raw_bkpt_tail = NULL;
+
+ for (bp = src; bp != NULL; bp = bp->next)
+ {
+ clone_one_breakpoint (&new_bkpt, &new_raw_bkpt, bp);
+ APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);
+ APPEND_TO_LIST (new_raw_list, new_raw_bkpt, raw_bkpt_tail);
+ }
+}
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index c84c688..439792f 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -243,4 +243,10 @@ int insert_memory_breakpoint (struct raw_breakpoint *bp);
int remove_memory_breakpoint (struct raw_breakpoint *bp);
+/* Create a new breakpoint list NEW_BKPT_LIST that is a copy of SRC. */
+
+void clone_all_breakpoints (struct breakpoint **new_bkpt_list,
+ struct raw_breakpoint **new_raw_bkpt_list,
+ struct breakpoint *src);
+
#endif /* MEM_BREAK_H */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 05/16 v2] GDBserver clone breakpoint list
2014-08-21 0:31 ` [PATCH 05/16 v2] GDBserver clone breakpoint list Don Breazeal
@ 2014-10-15 17:40 ` Pedro Alves
2014-10-31 23:44 ` Breazeal, Don
0 siblings, 1 reply; 110+ messages in thread
From: Pedro Alves @ 2014-10-15 17:40 UTC (permalink / raw)
To: Don Breazeal, gdb-patches
On 08/21/2014 01:29 AM, Don Breazeal wrote:
> This patch implements gdbserver routines to clone the breakpoint lists of a
> process, duplicating them for another process. In gdbserver, each process
> maintains its own independent breakpoint list. When a fork call creates a
> child, all of the breakpoints currently inserted in the parent process are
> also inserted in the child process, but there is nothing to describe them
> in the data structures related to the child. The child must have a
> breakpoint list describing them so that they can be removed (if detaching)
> or recognized (if following). Implementation is a mechanical process of
> just cloning the lists in several new functions in gdbserver/mem-break.c.
>
> Tested by building, since none of the new functions are called yet. This
> was tested with the subsequent patch 6, the follow-fork patch.
>
Generally looks good. A few nits below.
>
> gdb/gdbserver/
> 2014-08-20 Don Breazeal <donb@codesourcery.com>
>
> * mem-break.c (APPEND_TO_LIST): Define macro.
> (clone_agent_expr): New function.
> (clone_one_breakpoint): New function.
> (clone_all_breakpoints): New function.
> * mem-break.h: Declare new functions.
>
> ---
> gdb/gdbserver/mem-break.c | 104 +++++++++++++++++++++++++++++++++++++++++++++
> gdb/gdbserver/mem-break.h | 6 +++
> 2 files changed, 110 insertions(+), 0 deletions(-)
>
> diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
> index 2ce3ab2..7a6062c 100644
> --- a/gdb/gdbserver/mem-break.c
> +++ b/gdb/gdbserver/mem-break.c
> @@ -28,6 +28,22 @@ int breakpoint_len;
>
> #define MAX_BREAKPOINT_LEN 8
>
> +/* Helper macro used in loops that append multiple items to a singly-linked
> + list instead of inserting items at the head of the list, as, say, in the
> + breakpoint lists. LISTPP is a pointer to the pointer that is the head of
> + the new list. ITEMP is a pointer to the item to be added to the list.
> + TAILP must be defined to be the same type as ITEMP, and initialized to
> + NULL. */
> +
> +#define APPEND_TO_LIST(listpp, itemp, tailp) \
> + { \
> + if (tailp == NULL) \
> + *(listpp) = itemp; \
> + else \
> + tailp->next = itemp; \
> + tailp = itemp; \
> + }
Please put ()'s around uses of the parameters.
Also, use 'do { ... } while (0)' instead of '{ ... }'.
> +
> /* GDB will never try to install multiple breakpoints at the same
> address. However, we can see GDB requesting to insert a breakpoint
> at an address is had already inserted one previously in a few
> @@ -1878,3 +1894,91 @@ free_all_breakpoints (struct process_info *proc)
> while (proc->breakpoints)
> delete_breakpoint_1 (proc, proc->breakpoints);
> }
> +
> +/* Clone an agent expression. */
> +
> +static void
> +clone_agent_expr (struct agent_expr **new_ax, struct agent_expr *src_ax)
> +{
> + struct agent_expr *ax;
> +
> + ax = xcalloc (1, sizeof (*ax));
> + ax->length = src_ax->length;
> + ax->bytes = xcalloc (ax->length, 1);
> + memcpy (ax->bytes, src_ax->bytes, ax->length);
> + *new_ax = ax;
> +}
Like memcpy, please make the src argument of these functions be
const.
Is there a reason this doesn't have the more natural prototype that
returns the new clone?
static struct agent_expr *
clone_agent_expr (const struct agent_expr *src_ax)
{
struct agent_expr *ax;
ax = xcalloc (1, sizeof (*ax));
ax->length = src_ax->length;
ax->bytes = xcalloc (ax->length, 1);
memcpy (ax->bytes, src_ax->bytes, ax->length);
return ax;
}
> +
> +/* Create a new breakpoint list NEW_LIST that is a copy of SRC. Create
> + the corresponding new raw_breakpoint list NEW_RAW_LIST as well. */
> +
> +void
> +clone_all_breakpoints (struct breakpoint **new_list,
> + struct raw_breakpoint **new_raw_list,
> + struct breakpoint *src)
> +{
> + struct breakpoint *bp;
> + struct breakpoint *new_bkpt;
> + struct raw_breakpoint *new_raw_bkpt;
> + struct breakpoint *bkpt_tail = NULL;
> + struct raw_breakpoint *raw_bkpt_tail = NULL;
> +
> + for (bp = src; bp != NULL; bp = bp->next)
> + {
> + clone_one_breakpoint (&new_bkpt, &new_raw_bkpt, bp);
> + APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);
> + APPEND_TO_LIST (new_raw_list, new_raw_bkpt, raw_bkpt_tail);
Here this could then be:
new_bkpt = clone_one_breakpoint (bp);
APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);
APPEND_TO_LIST (new_raw_list, new_bkpt->raw, raw_bkpt_tail);
> +/* Create a new breakpoint list NEW_BKPT_LIST that is a copy of SRC. */
> +
> +void clone_all_breakpoints (struct breakpoint **new_bkpt_list,
> + struct raw_breakpoint **new_raw_bkpt_list,
> + struct breakpoint *src);
It took me a second to realize that SRC is a list here rather than
a single breakpoint. Could you make that more explicit, like e.g.,:
/* Create a new breakpoint list NEW_BKPT_LIST that is a copy of the
list that starts at SRC. */
Thanks,
Pedro Alves
^ permalink raw reply [flat|nested] 110+ messages in thread
* Re: [PATCH 05/16 v2] GDBserver clone breakpoint list
2014-10-15 17:40 ` Pedro Alves
@ 2014-10-31 23:44 ` Breazeal, Don
0 siblings, 0 replies; 110+ messages in thread
From: Breazeal, Don @ 2014-10-31 23:44 UTC (permalink / raw)
To: Pedro Alves, gdb-patches
On 10/15/2014 10:40 AM, Pedro Alves wrote:
> On 08/21/2014 01:29 AM, Don Breazeal wrote:
>> This patch implements gdbserver routines to clone the breakpoint lists of a
>> process, duplicating them for another process.
>> +#define APPEND_TO_LIST(listpp, itemp, tailp) \
>> + { \
>> + if (tailp == NULL) \
>> + *(listpp) = itemp; \
>> + else \
>> + tailp->next = itemp; \
>> + tailp = itemp; \
>> + }
>
> Please put ()'s around uses of the parameters.
> Also, use 'do { ... } while (0)' instead of '{ ... }'.
Done.
>> +static void
>> +clone_agent_expr (struct agent_expr **new_ax, struct agent_expr *src_ax)
>> +{
>
> Like memcpy, please make the src argument of these functions be
> const.
Done.
>
> Is there a reason this doesn't have the more natural prototype that
> returns the new clone?
No reason. I've made this change in the new version.
> Here this could then be:
>
> new_bkpt = clone_one_breakpoint (bp);
> APPEND_TO_LIST (new_list, new_bkpt, bkpt_tail);
> APPEND_TO_LIST (new_raw_list, new_bkpt->raw, raw_bkpt_tail);
>
>> +/* Create a new breakpoint list NEW_BKPT_LIST that is a copy of SRC. */
>> +
>> +void clone_all_breakpoints (struct breakpoint **new_bkpt_list,
>> + struct raw_breakpoint **new_raw_bkpt_list,
>> + struct breakpoint *src);
And this change is made as well.
>
> It took me a second to realize that SRC is a list here rather than
> a single breakpoint. Could you make that more explicit, like e.g.,:
>
> /* Create a new breakpoint list NEW_BKPT_LIST that is a copy of the
> list that starts at SRC. */
I made this change, and also changed the name of SRC to SRC_LIST.
Thanks for the review. I've posted an updated version of this patch
here: https://sourceware.org/ml/gdb-patches/2014-10/msg00870.html.
It's part of a new version of the whole patch series, updated to work
with more recent changes.
Thanks,
--Don
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 08/16 v2] Extended-remote follow vfork
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (17 preceding siblings ...)
2014-08-21 0:31 ` [PATCH 05/16 v2] GDBserver clone breakpoint list Don Breazeal
@ 2014-08-21 0:32 ` Don Breazeal
2014-08-21 0:33 ` [PATCH 11/16 v2] Extended-remote Linux exit events Don Breazeal
` (7 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:32 UTC (permalink / raw)
To: gdb-patches
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/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* remote.c (remote_parse_stop_reply): New stop reasons "vfork"
and "vforkdone" for RSP 'T' Stop Reply Packet.
gdb/gdbserver/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* 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/gdbserver/linux-low.c | 107 +++++++++++++++++++++++++++++++++++------
gdb/gdbserver/remote-utils.c | 16 ++++++-
gdb/remote.c | 15 ++++++
3 files changed, 120 insertions(+), 18 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 852d02e..5617f5e 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -379,7 +379,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;
@@ -404,7 +405,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;
@@ -414,12 +415,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
@@ -446,12 +445,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_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED;
- current_inferior->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_inferior->pending_follow.kind = TARGET_WAITKIND_FORKED;
+ event_child->waitstatus.kind = TARGET_WAITKIND_FORKED;
+ }
+ else if (event == PTRACE_EVENT_VFORK)
+ {
+ current_inferior->pending_follow.kind = TARGET_WAITKIND_VFORKED;
+ event_child->waitstatus.kind = TARGET_WAITKIND_VFORKED;
+ }
+ current_inferior->pending_follow.value.related_pid = ptid;
event_child->waitstatus.value.related_pid = ptid;
/* Report the event. */
@@ -511,6 +520,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);
}
@@ -1262,6 +1276,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);
@@ -1271,7 +1286,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)
{
@@ -1323,8 +1339,63 @@ linux_follow_fork (int follow_child, int detach_fork)
current_inferior = 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;
}
@@ -2636,7 +2707,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. */
@@ -6348,6 +6421,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 5152a14..e816e5e 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_inferior;
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 19f3efb..6939eb5 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5553,6 +5553,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. */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 11/16 v2] Extended-remote Linux exit events
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (18 preceding siblings ...)
2014-08-21 0:32 ` [PATCH 08/16 v2] Extended-remote follow vfork Don Breazeal
@ 2014-08-21 0:33 ` Don Breazeal
2014-08-21 0:33 ` [PATCH 09/16 v2] Extended-remote fork catchpoints Don Breazeal
` (6 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:33 UTC (permalink / raw)
To: gdb-patches
This patch implements support for the extended ptrace event
PTRACE_EVENT_EXIT on Linux. This is a preparatory patch for exec event
support.
The use of this event is entirely internal to gdbserver; these events are
not reported to GDB or the user. When this event occurs, if the reporting
thread is not the last thread in a process, its lwp entry is simply
deleted, since this is what happens in the absence of exit events. If it
is the last thread of a process, the wait status is set to the actual wait
status of the thread, retrieved by PTRACE_O_GETMESSAGE, instead of the
status that indicates the extended event, and the existing mechanisms for
handling thread exit proceed as usual.
The only purpose in using the exit events instead of the existing wait
mechanisms is to ensure that the exit of a thread group leader is detected
reliably when a non-leader thread calls exec.
This was tested on x64 Ubuntu using extended-remote.
Rationale for Using Exit Events
--------------------------------
In brief, there is a race condition in the current implementation that can
leave a dangling entry in the lwp list (an entry that doesn't have a
corresponding actual lwp). In this case gdbserver will hang waiting for
the non-existent lwp to stop. Using the exit events eliminates this race
condition.
The same race may exist in the native implementation, since the two
implementations are similar, but I haven't verified that. It may be
difficult to concoct a test case that demonstrates the race since the
window is so small.
Now for the long answer: in my testing I ran into a race condition in
check_zombie_leaders, which detects when a thread group leader has exited
and other threads still exist. On the Linux kernel, ptrace/waitpid don't
allow reaping the leader thread until all other threads in the group are
reaped. When the leader exits, it goes zombie, but waitpid will not return
an exit status until the other threads are gone. When a non-leader thread
calls exec, all other non-leader threads are destroyed, the leader becomes
a zombie, and once the "other" threads have been reaped, the execing thread
takes over the leader's pid (tgid) and appears to vanish. In order to
handle this situation in the current implementation, check_zombie_leaders
polls the process state in /proc and deletes thread group leaders that are
in a zombie state. The replacement is added to the lwp list when the exec
event is reported.
See https://sourceware.org/ml/gdb-patches/2011-10/msg00704.html for a more
detailed explanation of how this works.
Here is the relevant part of check_zombie_leaders:
if (leader_lp != NULL
/* Check if there are other threads in the group, as we may
have raced with the inferior simply exiting. */
&& !last_thread_of_process_p (leader_pid)
&& linux_proc_pid_is_zombie (leader_pid))
{
/* ...large informative comment block... */
delete_lwp (leader_lp);
The race occurred when there were two threads in the program, and the
non-leader thread called exec. In this case the leader thread passed
through a very brief zombie state before being replaced by the exec'ing
thread as the thread group leader. This state transition was asynchronous,
with no dependency on anything gdbserver did. Because there were no other
threads, there were no thread exit events, and thus there was no
synchronization with the leader passing through the zombie state and the
exec completing. If there had been more threads, the leader would remain
in the zombie state until they were waited for. In the two-thread case,
sometimes the leader exit was detected and sometimes it wasn't. (Recall
that check_zombie_leaders is polling the state, via
linux_proc_pid_is_zombie. The race is between the leader thread passing
through the zombie state and check_zombie_leaders testing for zombie
state.) If leader exit wasn't detected, gdbserver would end up with a
dangling lwp entry that didn't correspond to any real lwp, and would hang
waiting for that lwp to stop. Using PTRACE_EVENT_EXIT guarantees that the
leader exit will be detected.
Note that check_zombie_leaders works just fine for the scenarios where the
leader thread exits and the other threads continue to run, with no exec
calls. It is required for systems that don't support the extended ptrace
events.
The sequence of events resulting in the race condition was this:
1) In the program, a CLONE event for a new thread occurs.
2) In the program, both threads are resumed once gdbserver has
completed the new thread processing.
3) In gdbserver, the function linux_wait_for_event_filtered loops until
waitpid returns "no more events" for the SIGCHLD generated by the
CLONE event. Then linux_wait_for_event_filtered calls
check_zombie_leaders.
4) In the program, the new thread is doing the exec. During the exec
the leader thread will pass through a transitory zombie state. If
there were more than two threads, the leader thread would remain a
zombie until all the non-leader, non-exec'ing threads were reaped by
gdbserver. Since there are no such threads to reap, the leader just
becomes a zombie and is replaced by the exec'ing thread on-the-fly.
(Note that it appears that the leader thread is a zombie just for a
very brief instant.)
5) In gdbserver, check_zombie_leaders checks whether an lwp entry
corresponds to a zombie leader thread, and if so, deletes it. Here
is the race: in (4) above, the leader may or may not be in the
transitory zombie state. In the case where a zombie isn't detected,
delete_lwp is not called.
6) In gdbserver, an EXEC event is detected and processed. When it gets
ready to report the event to GDB, it calls stop_all_lwps, which sends
a SIGSTOP to each lwp in the list and the waits until all the lwps in
the list have reported a stop event. If the zombie leader wasn't
detected and processed in step (5), gdbserver blocks forever in
linux_wait_for_event_filtered, waiting for the undeleted lwp to be
stopped, which will never happen.
Thanks
--Don
gdb/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* nat/linux-ptrace.c (linux_ptrace_check_options): Call
linux_test_for_traceexit.
(linux_test_for_traceexit): New function.
gdb/gdbserver/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* linux-low.c (handle_extended_wait): Support PTRACE_EVENT_EXIT.
(linux_low_filter_event): Change wstat argument to a pointer,
making it an input/output argument.
(linux_wait_for_event_filtered): Handle wstat point argument in
call to linux_low_filter_event.
(initialize_low): Add PTRACE_O_TRACEEXIT.
---
gdb/gdbserver/linux-low.c | 68 +++++++++++++++++++++++++++++++++++++-------
gdb/nat/linux-ptrace.c | 50 +++++++++++++++++++++++++++++++++
2 files changed, 107 insertions(+), 11 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 2d9bd4a..aa9a51e 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -225,6 +225,7 @@ 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 int kill_lwp (unsigned long lwpid, int signo);
+static int num_lwps (int pid);
/* True if the low target can hardware single-step. Such targets
don't need a BREAKPOINT_REINSERT_ADDR callback. */
@@ -369,12 +370,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 return 0 so as
- not to report the trap to higher layers). */
+ event, we need to add the new lwp EVENT_CHILD to our list (and not
+ report the trap to higher layers). This function returns non-zero
+ if the event should be ignored and we should wait again. If an
+ exit event occurred in the last thread of a process, the wait
+ status in WSTATP will be updated with the exit status of the
+ process. */
static int
-handle_extended_wait (struct lwp_info *event_child, int wstat)
+handle_extended_wait (struct lwp_info *event_child, int *wstatp)
{
+ int wstat = *wstatp;
int event = linux_ptrace_get_extended_event (wstat);
struct thread_info *event_thr = get_lwp_thread (event_child);
struct lwp_info *new_lwp;
@@ -527,6 +533,38 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
event_child->waitstatus.kind = TARGET_WAITKIND_VFORK_DONE;
return 0;
}
+ else if (event == PTRACE_EVENT_EXIT)
+ {
+ unsigned long exit_status;
+ unsigned long lwpid = lwpid_of (event_thr);
+ int ret;
+
+ if (debug_threads)
+ debug_printf ("HEW: Got exit event from LWP %ld\n", lwpid );
+
+ ptrace (PTRACE_GETEVENTMSG, lwpid, (PTRACE_TYPE_ARG3) 0, &exit_status);
+
+ if (num_lwps (pid_of (event_thr)) > 1)
+ {
+ /* If there is at least one more LWP, then the program still
+ exists and the exit should not be reported to GDB. */
+ delete_lwp (event_child);
+ ret = 1;
+ }
+ else
+ {
+ /* Set the exit status to the actual exit status, so normal
+ WIFEXITED/WIFSIGNALLED processing and reporting for the
+ last lwp in the process can proceed from here. */
+ *wstatp = exit_status;
+ ret = 0;
+ }
+
+ /* Resume the thread so that it actually exits. Subsequent exit
+ events for LWPs that were deleted above will be ignored. */
+ ptrace (PTRACE_CONT, lwpid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
+ return ret;
+ }
internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
}
@@ -1990,8 +2028,9 @@ cancel_breakpoint (struct lwp_info *lwp)
NULL otherwise. */
static struct lwp_info *
-linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
+linux_low_filter_event (ptid_t filter_ptid, int lwpid, int *wstatp)
{
+ int wstat = *wstatp;
struct lwp_info *child;
struct thread_info *thread;
@@ -2043,7 +2082,8 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
architecture being defined already (so that CHILD has a valid
regcache), and on LAST_STATUS being set (to check for SIGTRAP or
not). */
- if (WIFSTOPPED (wstat))
+ if (WIFSTOPPED (wstat)
+ && (linux_ptrace_get_extended_event (wstat) != PTRACE_EVENT_EXIT))
{
if (debug_threads
&& the_low_target.get_pc != NULL)
@@ -2079,7 +2119,8 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
changes the debug registers meanwhile, we have the cached data we
can rely on. */
- if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP)
+ if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
+ && !linux_is_extended_waitstatus (wstat))
{
if (the_low_target.stopped_by_watchpoint == NULL)
{
@@ -2117,10 +2158,15 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
&& linux_is_extended_waitstatus (wstat))
{
- if (handle_extended_wait (child, wstat))
+ if (handle_extended_wait (child, &wstat))
return NULL;
else
- return child;
+ {
+ /* Update caller's wstat in case handle_extended_wait
+ changed it. */
+ *wstatp = wstat;
+ return child;
+ }
}
if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGSTOP
@@ -2339,8 +2385,7 @@ linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
(long) ret, status_to_str (*wstatp));
}
- event_child = linux_low_filter_event (filter_ptid,
- ret, *wstatp);
+ event_child = linux_low_filter_event (filter_ptid, ret, wstatp);
if (event_child != NULL)
{
/* We got an event to report to the core. */
@@ -6443,6 +6488,7 @@ initialize_low (void)
/* Enable extended events. */
linux_ptrace_set_requested_options (PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORK
- | PTRACE_O_TRACEVFORKDONE);
+ | PTRACE_O_TRACEVFORKDONE
+ | PTRACE_O_TRACEEXIT);
linux_ptrace_check_options ();
}
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index fc9e83a..e63bcd8 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -318,6 +318,7 @@ linux_child_function (gdb_byte *child_stack)
static void linux_test_for_tracesysgood (int child_pid);
static void linux_test_for_tracefork (int child_pid);
+static void linux_test_for_traceexit (int child_pid);
/* Determine ptrace features available on this target. */
@@ -355,6 +356,8 @@ linux_ptrace_check_options (void)
linux_test_for_tracefork (child_pid);
+ linux_test_for_traceexit (child_pid);
+
/* Clean things up and kill any pending children. */
do
{
@@ -469,6 +472,53 @@ linux_test_for_tracefork (int child_pid)
"(%d, status 0x%x)"), ret, status);
}
+/* Determine if PTRACE_O_TRACEEXIT can be used to follow exit
+ events. */
+
+static void
+linux_test_for_traceexit (int child_pid)
+{
+ int ret;
+ int status;
+ int i;
+
+ /* Check if the caller wants PTRACE_O_TRACEEXIT. */
+ if ((requested_ptrace_options & PTRACE_O_TRACEEXIT) == 0)
+ return;
+
+ /* Test for PTRACE_O_TRACEEXIT. First try to set the option.
+ If this fails, we know for sure that it is not supported. */
+ ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
+ (PTRACE_TYPE_ARG4) PTRACE_O_TRACEEXIT);
+ if (ret != 0)
+ return;
+
+ /* We don't know for sure that the feature is available; old
+ versions of PTRACE_SETOPTIONS ignored unknown options. So
+ see if the process exit will generate a PTRACE_EVENT_EXIT.
+
+ We try twice (at most) to handle the case where the grandchild
+ process exits, causing a SIGCHLD to be delivered to the child
+ process, stopping it before exit. */
+ for (i = 0; i < 2; i++)
+ {
+ ret = ptrace (PTRACE_CONT, child_pid, (PTRACE_TYPE_ARG3) 0,
+ (PTRACE_TYPE_ARG4) 0);
+ if (ret != 0)
+ warning (_("linux_test_for_traceexit: failed to resume child"));
+
+ ret = my_waitpid (child_pid, &status, 0);
+
+ /* Check if we received an exit event notification. */
+ if (ret == child_pid && WIFSTOPPED (status)
+ && linux_ptrace_get_extended_event (status) == PTRACE_EVENT_EXIT)
+ {
+ /* PTRACE_O_TRACEEXIT is supported. */
+ available_ptrace_options |= PTRACE_O_TRACEEXIT;
+ }
+ }
+}
+
/* Enable reporting of supported ptrace events. If
USE_AVAILABLE_OPTIONS is false, then exclude the events
specified in requested_ptrace_options. */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 09/16 v2] Extended-remote fork catchpoints
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (19 preceding siblings ...)
2014-08-21 0:33 ` [PATCH 11/16 v2] Extended-remote Linux exit events Don Breazeal
@ 2014-08-21 0:33 ` Don Breazeal
2014-08-21 0:33 ` [PATCH 10/16 v2] Extended-remote fork event documentation Don Breazeal
` (5 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:33 UTC (permalink / raw)
To: gdb-patches
This patch implements catchpoints for fork events on extended-remote linux
targets.
Implementation appeared to be straightforward, requiring four new functions
in remote.c to implement insert/remove of fork/vfork catchpoints. These
functions are essentially stubs that just return 0 ('success'). If the
fork events were being reported, then catchpoints were set and hit.
However, there are some extra issues that arise with catchpoints.
1) Thread creation reporting -- fork catchpoints are hit before the
follow_fork has been completed. In the native implementation, the new
process is not 'reported' until after the follow is done. It doesn't
show up in the inferiors list or the threads list. However, in
gdbserver, an 'info threads' will retrieve the new thread info from the
target and add it to GDB's data structures. Because of this premature
report, things on the GDB side eventually get very confused.
So in gdbserver, in server.c:handle_qxfer_threads_worker, we check
'last_status' and if it shows a FORK event, we know that we are in an
unfollowed fork and we do not report the new (forked) thread to GDB.
2) Kill process before fork is followed -- on the native side in
linux-nat.c:linux_nat_kill, there is some code to handle the case where
a fork has occurred but follow_fork hasn't been called yet. It does
this by using the last status to determine if a follow is pending, and
if it is, to kill the child task. I implemented similar code in
linux-low.c:linux_kill.
3) One of the tests related to fork catchpoints,
gdb.threads/fork-thread-pending.exp, depended on the threads being
reported in a specific order. GDBserver reported the threads in a
different order, that is, 'info threads' showed the same threads, but in
a different order. The test used a hard-coded thread number to find a
threads that (a) was not the main thread (blocked in pthread_join), and
(b) was not the forking thread (stopped in fork).
I implemented a new proc, find_unforked_thread, that uses a pretty
brute-force method of finding a thread. I considered just hard-coding
another number (the native case used thread 2, which was the forking
thread in the remote case), but that didn't seem future-proof.
Suggestions on how to do this better would be welcome.
Tested on x64 Ubuntu Lucid, native, remote, extended-remote. Tested the
case of killing the forking process before the fork has been followed
manually. It wasn't clear to me how to check that a process had actually
been killed from a dejagnu test.
Thanks
--Don
gdb/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* remote.c (extended_remote_insert_fork_catchpoint): New function.
(extended_remote_remove_fork_catchpoint): New function.
(extended_remote_insert_vfork_catchpoint): New function.
(extended_remote_remove_vfork_catchpoint): New function.
(init_extended_remote_ops): Initialize target vector with
new fork catchpoint functions.
gdb/gdbserver/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* linux-low.c (handle_extended_wait): Fix braces in multi-line if.
(linux_kill): Kill forked child when between fork and follow_fork.
* server.c (handle_qxfer_threads_worker): Skip forked child thread
when between fork and follow_fork.
(get_last_target_status): New function.
* server.h (get_last_target_status): Declare new function.
gdb/testsuite/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* gdb.threads/fork-thread-pending.exp (find_unforked_thread):
New proc.
---
gdb/gdbserver/linux-low.c | 28 +++++++++++--
gdb/gdbserver/server.c | 18 ++++++++
gdb/gdbserver/server.h | 2 +
gdb/remote.c | 44 +++++++++++++++++++++
gdb/testsuite/gdb.threads/fork-thread-pending.exp | 23 ++++++++++-
5 files changed, 110 insertions(+), 5 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 5617f5e..2d9bd4a 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -415,10 +415,12 @@ 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
@@ -1066,6 +1068,24 @@ linux_kill (int pid)
{
struct process_info *process;
struct lwp_info *lwp;
+ struct target_waitstatus last;
+ ptid_t last_ptid;
+
+ /* If we're stopped while forking and we haven't followed yet,
+ kill the child task. We need to do this first because the
+ parent will be sleeping if this is a vfork. */
+
+ get_last_target_status (&last_ptid, &last);
+
+ if (last.kind == TARGET_WAITKIND_FORKED
+ || last.kind == TARGET_WAITKIND_VFORKED)
+ {
+ lwp = find_lwp_pid (last.value.related_pid);
+ gdb_assert (lwp != NULL);
+ kill_wait_lwp (lwp);
+ process = find_process_pid (ptid_get_pid (last.value.related_pid));
+ the_target->mourn (process);
+ }
process = find_process_pid (pid);
if (process == NULL)
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 4dbdeb8..888160d 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1316,6 +1316,15 @@ handle_qxfer_threads_worker (struct inferior_list_entry *inf, void *arg)
int core = target_core_of_thread (ptid);
char core_s[21];
+ /* Skip new threads created as the result of a fork if we are not done
+ handling that fork event. We won't know whether to tell GDB about
+ the new thread until we are done following the fork. */
+ if ((last_status.kind == TARGET_WAITKIND_FORKED
+ || last_status.kind == TARGET_WAITKIND_VFORKED)
+ && (ptid_get_pid (last_status.value.related_pid)
+ == ptid_get_pid (ptid)))
+ return;
+
write_ptid (ptid_s, ptid);
if (core != -1)
@@ -4047,3 +4056,12 @@ using_extended_protocol (void)
{
return extended_protocol;
}
+
+/* Retrieve the last waitstatus reported to GDB. */
+
+void
+get_last_target_status (ptid_t *ptid, struct target_waitstatus *last)
+{
+ *ptid = last_ptid;
+ *last = last_status;
+}
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index 2c5377b..17491bc 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -115,6 +115,8 @@ typedef int gdb_fildes_t;
extern int handle_serial_event (int err, gdb_client_data client_data);
extern int handle_target_event (int err, gdb_client_data client_data);
extern int using_extended_protocol (void);
+extern void get_last_target_status (ptid_t *ptid,
+ struct target_waitstatus *last);
#include "remote-utils.h"
diff --git a/gdb/remote.c b/gdb/remote.c
index 6939eb5..6694ca2 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -7855,6 +7855,42 @@ extended_remote_kill (struct target_ops *ops)
target_mourn_inferior ();
}
+/* Insert fork catchpoint target routine. If fork events are enabled
+ then return success, nothing more to do. */
+
+static int
+extended_remote_insert_fork_catchpoint (struct target_ops *ops, int pid)
+{
+ return !extended_remote_feature_supported (FORK_EVENT);
+}
+
+/* Remove fork catchpoint target routine. Nothing to do, just
+ return success. */
+
+static int
+extended_remote_remove_fork_catchpoint (struct target_ops *ops, int pid)
+{
+ return 0;
+}
+
+/* Insert vfork catchpoint target routine. If vfork events are enabled
+ then return success, nothing more to do. */
+
+static int
+extended_remote_insert_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+ return !extended_remote_feature_supported (VFORK_EVENT);
+}
+
+/* Remove vfork catchpoint target routine. Nothing to do, just
+ return success. */
+
+static int
+extended_remote_remove_vfork_catchpoint (struct target_ops *ops, int pid)
+{
+ return 0;
+}
+
/* Target routine for follow-fork. */
static int
@@ -11717,6 +11753,14 @@ init_extended_remote_ops (void)
Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_open = extended_remote_open;
extended_remote_ops.to_create_inferior = extended_remote_create_inferior;
+ extended_remote_ops.to_insert_fork_catchpoint
+ = extended_remote_insert_fork_catchpoint;
+ extended_remote_ops.to_remove_fork_catchpoint
+ = extended_remote_remove_fork_catchpoint;
+ extended_remote_ops.to_insert_vfork_catchpoint
+ = extended_remote_insert_vfork_catchpoint;
+ extended_remote_ops.to_remove_vfork_catchpoint
+ = extended_remote_remove_vfork_catchpoint;
extended_remote_ops.to_follow_fork = extended_remote_follow_fork;
extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
extended_remote_ops.to_detach = extended_remote_detach;
diff --git a/gdb/testsuite/gdb.threads/fork-thread-pending.exp b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
index 57e45c9..e8c4830 100644
--- a/gdb/testsuite/gdb.threads/fork-thread-pending.exp
+++ b/gdb/testsuite/gdb.threads/fork-thread-pending.exp
@@ -31,6 +31,26 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
+# Find a thread that did not fork and is not the main thread and
+# return its thread number. We can't just hard-code the thread
+# number since we have no guarantee as to the ordering of the threads
+# in gdb. We know that the main thread is in pthread_join and the
+# forking thread is in fork, so we use this rather ungainly regexp
+# to capture an entry from 'info threads' that doesn't show one of
+# those routines, then extract the thread number.
+
+proc find_unforked_thread { } {
+ gdb_test_multiple "info threads" "find unforked thread" {
+ -re "(\[^\r]*Thread\[^\r]* in \[^fp]\[^ot]\[^rh]\[^kr]\[^e]\[^a]\[^d]\[^_]\[^j]\[^\r]*\r\n)" {
+ regexp "(\[ ]*)(\[0-9]*)(\[ ]*Thread\[^\r]*\r\n)" $expect_out(0,string) ignore lead_spc threadnum rest
+ }
+ timeout {
+ set threadnum -1
+ }
+ }
+ return $threadnum
+}
+
clean_restart ${binfile}
if ![runto_main] then {
@@ -46,7 +66,8 @@ gdb_test "continue" "Catchpoint.*" "1, get to the fork event"
gdb_test "info threads" " Thread .* Thread .* Thread .* Thread .*" "1, multiple threads found"
-gdb_test "thread 2" ".*" "1, switched away from event thread"
+set threadnum [find_unforked_thread]
+gdb_test "thread $threadnum" ".*" "1, switched away from event thread to thread $threadnum"
gdb_test "continue" "Not resuming.*" "1, refused to resume"
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 10/16 v2] Extended-remote fork event documentation
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (20 preceding siblings ...)
2014-08-21 0:33 ` [PATCH 09/16 v2] Extended-remote fork catchpoints Don Breazeal
@ 2014-08-21 0:33 ` Don Breazeal
2014-08-21 0:34 ` [PATCH 12/16 v2] Extended-remote follow exec Don Breazeal
` (4 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:33 UTC (permalink / raw)
To: gdb-patches
This patch adds documentation for fork events on extended-remote linux
targets in the gdb manual and the NEWS file.
[This patch was approved by Eli:
https://sourceware.org/ml/gdb-patches/2014-08/msg00157.html]
Thanks
--Don
gdb/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* gdb/NEWS: Describe new gdbserver support for fork events and new
fork event support in RSP.
gdb/doc
2014-08-20 Don Breazeal <donb@codesourcery.com>
* gdb/doc/gdb.texinfo (Forks): Describe fork debugging support in
gdbserver.
(Set Catchpoints): Describe fork catchpoint support in gdbserver.
(Stop Reply Packets): Describe fork event support in RSP.
---
gdb/NEWS | 15 +++++++++++++++
gdb/doc/gdb.texinfo | 33 +++++++++++++++++++++++++++++----
2 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index d603cf7..c3b1d23 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -14,6 +14,21 @@
confirmation if the program had stopped for a signal and the user
switched threads meanwhile.
+* Remote fork events
+
+ GDBserver extended-remote Linux targets now support fork events.
+ This enables follow-fork-mode, detach-on-fork, catch fork, and
+ catch vfork for those targets with Linux kernels 2.5.60 and later.
+
+* New remote packets
+
+T Stop Reply Packet's reason
+ The T stop reply packet supports new stop reasons 'fork', 'vfork'
+ and 'vforkdone'. The 'fork' and 'vfork' reasons signify that the
+ specified inferior has executed a fork or vfork. The 'vforkdone'
+ reason signifies that a vforked child process has executed either
+ an exec or an exit.
+
*** Changes in GDB 7.8
* New command line options
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 8d9148c..07c151b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3107,6 +3107,9 @@ create additional processes using the @code{fork} or @code{vfork} functions.
Currently, the only platforms with this feature are HP-UX (11.x and later
only?) and @sc{gnu}/Linux (kernel version 2.5.60 and later).
+The fork debugging commands are supported in both native mode and when
+connected to @code{gdbserver} using @kbd{target extended-remote}.
+
By default, when a program forks, @value{GDBN} will continue to debug
the parent process and the child process will run unimpeded.
@@ -4395,13 +4398,15 @@ Again, in this case @value{GDBN} would not be able to display syscall's names.
@item fork
@kindex catch fork
-A call to @code{fork}. This is currently only available for HP-UX
-and @sc{gnu}/Linux.
+A call to @code{fork}. This is currently only available for native
+HP-UX and @sc{gnu}/Linux, and when connected to @code{gdbserver} running
+on @sc{gnu}/Linux with @kbd{target extended-remote}.
@item vfork
@kindex catch vfork
-A call to @code{vfork}. This is currently only available for HP-UX
-and @sc{gnu}/Linux.
+A call to @code{vfork}. This is currently only available for native
+HP-UX and @sc{gnu}/Linux, and when connected to @code{gdbserver} running
+on @sc{gnu}/Linux with @kbd{target extended-remote}.
@item load @r{[}regexp@r{]}
@itemx unload @r{[}regexp@r{]}
@@ -34721,6 +34726,26 @@ The packet indicates that the loaded libraries have changed.
@value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new
list of loaded libraries. The @var{r} part is ignored.
+@cindex fork events, remote reply
+@item fork
+The packet indicates that @code{fork} was called, and @var{r}
+is the ptid of the new child process. This packet is only
+applicable to targets that support fork events.
+
+@cindex vfork events, remote reply
+@item vfork
+The packet indicates that @code{vfork} was called, and @var{r}
+is the ptid of the new child process. This packet is only
+applicable to targets that support vfork events.
+
+@cindex vforkdone events, remote reply
+@item vforkdone
+The packet indicates that a child process created by a vfork
+has either called @code{exec} or terminated, so that the
+address spaces of the parent and child process are no longer
+shared. The @var{r} part is ignored. This packet is only
+applicable to targets that support vforkdone events.
+
@cindex replay log events, remote reply
@item replaylog
The packet indicates that the target cannot continue replaying
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 12/16 v2] Extended-remote follow exec
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (21 preceding siblings ...)
2014-08-21 0:33 ` [PATCH 10/16 v2] Extended-remote fork event documentation Don Breazeal
@ 2014-08-21 0:34 ` Don Breazeal
2014-08-21 0:34 ` [PATCH 13/16 v2] Extended-remote exec catchpoints Don Breazeal
` (3 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:34 UTC (permalink / raw)
To: gdb-patches
This patch implements support for exec events in gdbserver on linux, in multiprocess mode (target extended-remote). Follow-exec-mode and rerun behave as expected. Catchpoints for exec are not yet implemented since it will be easier to implement catchpoints for fork, vfork, and exec all at the same time.
TESTING
---------
The patch was tested on GNU/Linux x86_64 for native, remote, and
extended-remote targets. The test results for native-gdbserver were
unchanged. Thirteen tests that used to fail for native-extended-gdbserver
on Linux pass with this patch, and the non-ldr-exc-*.exp tests all pass in
all-stop mode and extended-remote.
One caveat: when an exec is detected, gdbserver emits a couple of warnings:
gdbserver: unexpected r_debug version 0
gdbserver: unexpected r_debug version 0
However, debugging of shared libraries that are loaded by the exec'd
program works just fine. These messages are caused by gdbserver making
an attempt to initialize the solib hook before the r_debug structure has
been initialized. A fix is provided in patch 14 of this patch series.
IMPLEMENTATION
----------------
Support for exec events in single-threaded programs was a fairly
straightforward replication of the implementation in native GDB:
1) Enable exec events via ptrace options.
2) Add support for handling the exec events to the handle_extended_wait and
linux_wait_for_event_filtered. Detect the exec event, then find and save
the pathname of the executable file being exec'd.
3) Implement an additional "stop reason", "exec", in the RSP stop reply
packet "T".
Existing GDB code takes care of handling the exec event on the host side
without modification.
Support for exec events in multi-threaded programs required some additional
work that required a couple of significant changes to existing code. In a
nutshell, the changes are to:
4) Use the PTRACE_EVENT_EXIT extended event to handle thread exit, while
not exposing any change in exit handling to the user.
5) Recognize when the exec'ing thread has vanished (become the thread group
leader) in send_sigstop. Native GDB does this differently.
The rationale for #4 is discussed in the previous patch of this series.
Rationale for item 5: when a non-leader thread exec's, all the other
threads are terminated and the exec'ing thread changes its thread id to
that of the old leader (the process id) as part of the exec. There is no
event reported for the "exit" of the exec'ing thread; it appears to have
vanished.
Determining that the exec'ing thread has "vanished" in native GDB is done
by calling waitpid(PID), and if it returns ECHILD it means that the thread
is gone. We don't want to use waitpid(PID) in gdbserver, based on the
discussion in
https://www.sourceware.org/ml/gdb-patches/2014-02/msg00828.html.
An alternative is to send a signal to each thread and look for an ESRCH (No
such process) error. In all-stop mode this can be done in the normal
course of events, since when gdbserver reports an exec event it stops all
the other threads with a SIGSTOP. In non-stop mode, when an exec event has
been detected, we can call stop_all_lwps/unstop_all_lwps to accomplish the
same thing.
Thanks
--Don
gdb/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* linux-nat.c (linux_handle_extended_wait): Change call to
linux_child_pid_to_exec_file to call to linux_proc_pid_to_exec_file.
(linux_child_pid_to_exec_file): Deleted function, moved to
common code.
(linux_target_install_ops): Change linux_child_pid_to_exec_file to
linux_proc_pid_to_exec_file.
* nat/linux-procfs.c (linux_proc_pid_to_exec_file): New function.
* nat/linux-procfs.h (linux_proc_pid_to_exec_file): Declare.
* nat/linux-ptrace.c (linux_test_for_traceexit): Add
PTRACE_O_TRACEEXEC.
* remote.c (remote_parse_stop_reply): New stop reason "exec" for
'T' Stop Reply Packet.
gdb/gdbserver/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* linux-low.c (handle_extended_wait): Handle PTRACE_EVENT_EXEC.
(check_zombie_leaders): Update comment.
(linux_low_filter_event): Handle exec event.
(linux_wait_for_event_filtered): Update comment.
(extended_event_reported): Add exec event to the list of extended
events.
(send_sigstop): Check return value from kill_lwp and delete lwp
on 'No such process' error.
(initialize_low): Add PTRACE_O_TRACEEXIT.
* remote-utils.c (prepare_resume_reply): New stop reason "exec" for
'T' Stop Reply Packet.
---
gdb/gdbserver/linux-low.c | 91 +++++++++++++++++++++++++++++++++++++++--
gdb/gdbserver/remote-utils.c | 19 +++++++++
gdb/linux-nat.c | 22 +---------
gdb/nat/linux-procfs.c | 18 ++++++++
gdb/nat/linux-procfs.h | 4 ++
gdb/nat/linux-ptrace.c | 9 +++-
gdb/remote.c | 25 +++++++++++-
7 files changed, 159 insertions(+), 29 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index aa9a51e..f8bab6c 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -565,6 +565,21 @@ handle_extended_wait (struct lwp_info *event_child, int *wstatp)
ptrace (PTRACE_CONT, lwpid, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
return ret;
}
+ else if (event == PTRACE_EVENT_EXEC)
+ {
+ if (debug_threads)
+ {
+ debug_printf ("HEW: Got exec event from LWP %ld\n",
+ lwpid_of (event_thr));
+ }
+
+ event_child->waitstatus.kind = TARGET_WAITKIND_EXECD;
+ event_child->waitstatus.value.execd_pathname
+ = xstrdup (linux_proc_pid_to_exec_file (NULL, lwpid_of (event_thr)));
+
+ /* Report the event. */
+ return 0;
+ }
internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
}
@@ -2036,6 +2051,56 @@ linux_low_filter_event (ptid_t filter_ptid, int lwpid, int *wstatp)
child = find_lwp_pid (pid_to_ptid (lwpid));
+ /* Check for stop events reported by a process we didn't already
+ know about - anything not already in our LWP list.
+
+ If we're expecting to receive stopped processes after
+ fork, vfork, and clone events, then we'll just add the
+ new one to our list and go back to waiting for the event
+ to be reported - the stopped process might be returned
+ from waitpid before or after the event is.
+
+ But note the case of a non-leader thread exec'ing after the
+ leader having exited, and gone from our lists. On an exec,
+ the Linux kernel destroys all other threads (except the execing
+ one) in the thread group, and resets the execing thread's tid
+ to the tgid. No exit notification is sent for the execing
+ thread -- from the ptracer's perspective, it appears as though
+ the execing thread just vanishes. When they are available, we
+ use exit events (PTRACE_EVENT_EXIT) to detect thread exit
+ reliably. As soon as all other threads (if any) are reaped or
+ have reported their PTRACE_EVENT_EXIT events, the execing
+ thread changes it's tid to the tgid, and the previous (zombie)
+ leader vanishes, giving place to the "new" leader. The lwp
+ entry for the previous leader is deleted when we handle its
+ exit event, and we re-add the new one here. */
+
+ if (WIFSTOPPED (wstat) && (child == NULL) && (WSTOPSIG (wstat) == SIGTRAP)
+ && (linux_ptrace_get_extended_event (wstat) == PTRACE_EVENT_EXEC))
+ {
+ ptid_t child_ptid;
+
+ /* A multi-thread exec after we had seen the leader exiting. */
+ if (debug_threads)
+ debug_printf ("LLW: Re-adding thread group leader LWP %d.\n", lwpid);
+
+ child_ptid = ptid_build (lwpid, lwpid, 0);
+ child = add_lwp (child_ptid);
+ child->stopped = 1;
+ current_inferior = child->thread;
+
+ if (non_stop && stopping_threads == NOT_STOPPING_THREADS)
+ {
+ /* Make sure we delete the lwp entry for the exec'ing thread,
+ which will have vanished. We do this by sending a signal
+ to all the other threads in the lwp list, deleting any
+ that are not found. Note that in all-stop mode this will
+ happen before reporting the event. */
+ stop_all_lwps (0, child);
+ unstop_all_lwps (0, child);
+ }
+ }
+
/* If we didn't find a process, one of two things presumably happened:
- A process we started and then detached from has exited. Ignore it.
- A process we are controlling has forked and the new child's stop
@@ -2368,8 +2433,7 @@ linux_wait_for_event_filtered (ptid_t wait_ptid, ptid_t filter_ptid,
- When a non-leader thread execs, that thread just vanishes
without reporting an exit (so we'd hang if we waited for it
explicitly in that case). The exec event is reported to
- the TGID pid (although we don't currently enable exec
- events). */
+ the TGID pid. */
errno = 0;
ret = my_waitpid (-1, wstatp, options | WNOHANG);
@@ -2774,7 +2838,8 @@ extended_event_reported (const struct target_waitstatus *waitstatus)
return (waitstatus->kind == TARGET_WAITKIND_FORKED
|| waitstatus->kind == TARGET_WAITKIND_VFORKED
- || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE);
+ || waitstatus->kind == TARGET_WAITKIND_VFORK_DONE
+ || waitstatus->kind == TARGET_WAITKIND_EXECD);
}
/* Wait for process, returns status. */
@@ -3401,6 +3466,7 @@ static void
send_sigstop (struct lwp_info *lwp)
{
int pid;
+ int ret;
pid = lwpid_of (get_lwp_thread (lwp));
@@ -3418,7 +3484,21 @@ send_sigstop (struct lwp_info *lwp)
debug_printf ("Sending sigstop to lwp %d\n", pid);
lwp->stop_expected = 1;
- kill_lwp (pid, SIGSTOP);
+ errno = 0;
+ ret = kill_lwp (pid, SIGSTOP);
+ if (ret == -1 && errno == ESRCH)
+ {
+ /* If the kill fails with "No such process", on GNU/Linux we know
+ that the LWP has vanished - it is not a zombie, it is gone.
+ This is due to a thread other than the thread group leader
+ calling exec. See comments in linux_low_filter_event regarding
+ PTRACE_EVENT_EXEC. */
+ delete_lwp (lwp);
+ set_desired_inferior (0);
+
+ if (debug_threads)
+ debug_printf ("send_sigstop: lwp %d has vanished\n", pid);
+ }
}
static int
@@ -6489,6 +6569,7 @@ initialize_low (void)
linux_ptrace_set_requested_options (PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORK
| PTRACE_O_TRACEVFORKDONE
- | PTRACE_O_TRACEEXIT);
+ | PTRACE_O_TRACEEXIT
+ | PTRACE_O_TRACEEXEC);
linux_ptrace_check_options ();
}
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index e816e5e..2b3e638 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1107,6 +1107,7 @@ prepare_resume_reply (char *buf, ptid_t ptid,
case TARGET_WAITKIND_STOPPED:
case TARGET_WAITKIND_FORKED:
case TARGET_WAITKIND_VFORKED:
+ case TARGET_WAITKIND_EXECD:
{
struct thread_info *saved_inferior;
const char **regp;
@@ -1123,6 +1124,24 @@ prepare_resume_reply (char *buf, ptid_t ptid,
ptid_get_pid (status->value.related_pid),
ptid_get_lwp (status->value.related_pid));
}
+ else if ((status->kind == TARGET_WAITKIND_EXECD) && multi_process)
+ {
+ enum gdb_signal signal = GDB_SIGNAL_TRAP;
+ const char *event = "exec";
+ char hexified_pathname[PATH_MAX];
+
+ sprintf (buf, "T%02x%s:", signal, event);
+ buf += strlen (buf);
+
+ /* Encode pathname to hexified format. */
+ bin2hex ((const gdb_byte *) status->value.execd_pathname,
+ hexified_pathname, strlen(status->value.execd_pathname));
+
+ sprintf (buf, "%s;", hexified_pathname);
+ xfree (status->value.execd_pathname);
+ status->value.execd_pathname = NULL;
+ buf += strlen (buf);
+ }
else
sprintf (buf, "T%02x", status->value.sig);
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index b7bb978..0003640 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -682,7 +682,6 @@ linux_nat_pass_signals (struct target_ops *self,
/* Prototypes for local functions. */
static int stop_wait_callback (struct lwp_info *lp, void *data);
static int linux_thread_alive (ptid_t ptid);
-static char *linux_child_pid_to_exec_file (struct target_ops *self, int pid);
\f
@@ -2011,7 +2010,7 @@ linux_handle_extended_wait (struct lwp_info *lp, int status,
ourstatus->kind = TARGET_WAITKIND_EXECD;
ourstatus->value.execd_pathname
- = xstrdup (linux_child_pid_to_exec_file (NULL, pid));
+ = xstrdup (linux_proc_pid_to_exec_file (NULL, pid));
return 0;
}
@@ -3850,23 +3849,6 @@ linux_nat_thread_name (struct target_ops *self, struct thread_info *thr)
return result;
}
-/* Accepts an integer PID; Returns a string representing a file that
- can be opened to get the symbols for the child process. */
-
-static char *
-linux_child_pid_to_exec_file (struct target_ops *self, int pid)
-{
- static char buf[PATH_MAX];
- char name[PATH_MAX];
-
- xsnprintf (name, PATH_MAX, "/proc/%d/exe", pid);
- memset (buf, 0, PATH_MAX);
- if (readlink (name, buf, PATH_MAX - 1) <= 0)
- strcpy (buf, name);
-
- return buf;
-}
-
/* Records the thread's register state for the corefile note
section. */
@@ -4315,7 +4297,7 @@ linux_target_install_ops (struct target_ops *t)
t->to_insert_exec_catchpoint = linux_child_insert_exec_catchpoint;
t->to_remove_exec_catchpoint = linux_child_remove_exec_catchpoint;
t->to_set_syscall_catchpoint = linux_child_set_syscall_catchpoint;
- t->to_pid_to_exec_file = linux_child_pid_to_exec_file;
+ t->to_pid_to_exec_file = linux_proc_pid_to_exec_file;
t->to_post_startup_inferior = linux_child_post_startup_inferior;
t->to_post_attach = linux_child_post_attach;
t->to_follow_fork = linux_child_follow_fork;
diff --git a/gdb/nat/linux-procfs.c b/gdb/nat/linux-procfs.c
index 84fc890..fa664f1 100644
--- a/gdb/nat/linux-procfs.c
+++ b/gdb/nat/linux-procfs.c
@@ -24,6 +24,7 @@
#include "linux-procfs.h"
#include "filestuff.h"
+#include "target.h"
/* Return the TGID of LWPID from /proc/pid/status. Returns -1 if not
found. */
@@ -118,3 +119,20 @@ linux_proc_pid_is_zombie (pid_t pid)
{
return linux_proc_pid_has_state (pid, "Z (zombie)");
}
+
+/* Accepts an integer PID; Returns a string representing a file that
+ can be opened to get the symbols for the child process. */
+
+char *
+linux_proc_pid_to_exec_file (struct target_ops *self, int pid)
+{
+ static char buf[PATH_MAX];
+ char name[PATH_MAX];
+
+ xsnprintf (name, PATH_MAX, "/proc/%d/exe", pid);
+ memset (buf, 0, PATH_MAX);
+ if (readlink (name, buf, PATH_MAX - 1) <= 0)
+ strcpy (buf, name);
+
+ return buf;
+}
diff --git a/gdb/nat/linux-procfs.h b/gdb/nat/linux-procfs.h
index d13fff7..d3581de 100644
--- a/gdb/nat/linux-procfs.h
+++ b/gdb/nat/linux-procfs.h
@@ -40,4 +40,8 @@ extern int linux_proc_pid_is_stopped (pid_t pid);
extern int linux_proc_pid_is_zombie (pid_t pid);
+/* Return pathname of exec file for process with PID. */
+
+extern char *linux_proc_pid_to_exec_file (struct target_ops *self, int pid);
+
#endif /* COMMON_LINUX_PROCFS_H */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index e63bcd8..1c02b71 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -493,6 +493,7 @@ linux_test_for_traceexit (int child_pid)
if (ret != 0)
return;
+
/* We don't know for sure that the feature is available; old
versions of PTRACE_SETOPTIONS ignored unknown options. So
see if the process exit will generate a PTRACE_EVENT_EXIT.
@@ -513,8 +514,12 @@ linux_test_for_traceexit (int child_pid)
if (ret == child_pid && WIFSTOPPED (status)
&& linux_ptrace_get_extended_event (status) == PTRACE_EVENT_EXIT)
{
- /* PTRACE_O_TRACEEXIT is supported. */
- available_ptrace_options |= PTRACE_O_TRACEEXIT;
+ /* PTRACE_O_TRACEEXIT is supported.
+
+ We use exit events to implement support for exec events.
+ Because exit events are supported, we can assume exec events
+ are also supported, so we add them as well. */
+ available_ptrace_options |= PTRACE_O_TRACEEXIT | PTRACE_O_TRACEEXEC;
}
}
}
diff --git a/gdb/remote.c b/gdb/remote.c
index 6694ca2..d2256fd 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5491,11 +5491,13 @@ remote_parse_stop_reply (char *buf, struct stop_reply *event)
pnum and set p1 to point to the character following it.
Otherwise p1 points to p. */
- /* If this packet is an awatch packet, don't parse the 'a'
- as a register number. */
+ /* If this packet has a stop reason string that starts
+ with a character that could be a hex digit, don't parse
+ it as a register number. */
if (strncmp (p, "awatch", strlen("awatch")) != 0
&& strncmp (p, "core", strlen ("core") != 0)
+ && strncmp (p, "exec", strlen ("exec") != 0)
&& strncmp (p, "fork", strlen ("fork") != 0))
{
/* Read the ``P'' register number. */
@@ -5568,6 +5570,25 @@ Packet: '%s'\n"),
event->ws.kind = TARGET_WAITKIND_VFORK_DONE;
p = p_temp;
}
+ else if (strncmp (p, "exec", p1 - p) == 0)
+ {
+ ULONGEST pid;
+ char pathname[PATH_MAX];
+
+ p = unpack_varlen_hex (++p1, &pid);
+
+ /* Save the pathname for event reporting and for
+ the next run command. */
+ hex2bin (p1, (gdb_byte *) pathname, (p - p1)/2);
+ /* Add the null terminator. */
+ pathname[(p - p1)/2] = '\0';
+ /* This is freed during event handling. */
+ event->ws.value.execd_pathname = xstrdup (pathname);
+ event->ws.kind = TARGET_WAITKIND_EXECD;
+ /* Save the pathname for the next run command. */
+ xfree (remote_exec_file);
+ remote_exec_file = xstrdup (pathname);
+ }
else
{
/* Silently skip unknown optional info. */
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 13/16 v2] Extended-remote exec catchpoints
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (22 preceding siblings ...)
2014-08-21 0:34 ` [PATCH 12/16 v2] Extended-remote follow exec Don Breazeal
@ 2014-08-21 0:34 ` Don Breazeal
2014-08-21 0:35 ` [PATCH 14/16 v2] Suppress spurious warnings with extended-remote follow exec Don Breazeal
` (2 subsequent siblings)
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:34 UTC (permalink / raw)
To: gdb-patches
This patch implements exec catchpoints for extended-remote Linux targets.
The implementation follows the same appraoch used in patch 9 of this
series for fork catchpoints, implementing extended_remote target
routines for insert/remove. Existing host-side code and the support
for exec events introduced in patch 12 of this series takes care of
the rest.
Tested native, remote, extended-remote on x64 Ubuntu.
Thanks
--Don
gdb/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* remote.c (extended_remote_insert_exec_catchpoint): New function.
(extended_remote_remove_exec_catchpoint): New function.
(_initialize_remote): Initialize target vector with new exec
catchpoint functions.
---
gdb/remote.c | 22 ++++++++++++++++++++++
1 files changed, 22 insertions(+), 0 deletions(-)
diff --git a/gdb/remote.c b/gdb/remote.c
index d2256fd..60297ca 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -7938,6 +7938,24 @@ extended_remote_follow_fork (struct target_ops *ops, int follow_child,
return 0;
}
+/* Insert exec catchpoint target routine. If exec events are
+ enabled, just return success. */
+
+static int
+extended_remote_insert_exec_catchpoint (struct target_ops *ops, int pid)
+{
+ return !extended_remote_feature_supported (EXEC_EVENT);
+}
+
+/* Remove exec catchpoint target routine. Nothing to do, just
+ return success. */
+
+static int
+extended_remote_remove_exec_catchpoint (struct target_ops *ops, int pid)
+{
+ return 0;
+}
+
static void
remote_mourn (struct target_ops *ops)
{
@@ -11783,6 +11801,10 @@ Specify the serial device it is connected to (e.g. /dev/ttya).";
extended_remote_ops.to_remove_vfork_catchpoint
= extended_remote_remove_vfork_catchpoint;
extended_remote_ops.to_follow_fork = extended_remote_follow_fork;
+ extended_remote_ops.to_insert_exec_catchpoint
+ = extended_remote_insert_exec_catchpoint;
+ extended_remote_ops.to_remove_exec_catchpoint
+ = extended_remote_remove_exec_catchpoint;
extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
extended_remote_ops.to_detach = extended_remote_detach;
extended_remote_ops.to_attach = extended_remote_attach;
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 14/16 v2] Suppress spurious warnings with extended-remote follow exec
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (23 preceding siblings ...)
2014-08-21 0:34 ` [PATCH 13/16 v2] Extended-remote exec catchpoints Don Breazeal
@ 2014-08-21 0:35 ` Don Breazeal
2014-08-21 0:36 ` [PATCH 15/16 v2] Extended-remote exec event documentation Don Breazeal
2014-08-21 0:36 ` [PATCH 16/16 v2] Non-stop follow exec tests Don Breazeal
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:35 UTC (permalink / raw)
To: gdb-patches
This patch eliminates some spurious gdbserver warnings that occur when
following an exec on an extended-remote Linux target.
When gdbserver on Linux sets up the hook for shared library load
detection, an initial step is to read the version number from the
r_debug structure in memory. In the current implementation, if the
version number is not equal to one, a warning was printed by gdbserver.
However, the number can be zero if the structure has not been
initialized yet.
To suppress the warnings the error check was changed so that if
the version number is not equal to one the function silently returns
-1. Subsequent calls to the routine find an initialized r_debug
structure.
Tested on x64 Ubuntu, both GDB tests and manual testing of following
an exec, then debugging a shared library loaded by the exec'd program
to ensure that there were no warnings and that debugging shared libs
was not adversely affected.
Thanks
--Don
gdb/gdbserver/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* linux-low.c (linux_qxfer_libraries_svr4): Change
handling of r_debug version mismatch.
---
gdb/gdbserver/linux-low.c | 11 ++++++++---
1 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index f8bab6c..6be78a7 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -6226,10 +6226,15 @@ linux_qxfer_libraries_svr4 (const char *annex, unsigned char *readbuf,
{
if (linux_read_memory (priv->r_debug + lmo->r_version_offset,
(unsigned char *) &r_version,
- sizeof (r_version)) != 0
- || r_version != 1)
+ sizeof (r_version)) != 0)
+ warning ("error reading r_debug version from memory");
+ else if (r_version != 1)
{
- warning ("unexpected r_debug version %d", r_version);
+ /* If the version is incorrect, it probably means that
+ r_debug hasn't been initialized yet. Just silently
+ return an error. We will read it in a subsequent pass
+ through here. */
+ return -1;
}
else if (read_one_ptr (priv->r_debug + lmo->r_map_offset,
&lm_addr, ptr_size) != 0)
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 15/16 v2] Extended-remote exec event documentation
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (24 preceding siblings ...)
2014-08-21 0:35 ` [PATCH 14/16 v2] Suppress spurious warnings with extended-remote follow exec Don Breazeal
@ 2014-08-21 0:36 ` Don Breazeal
2014-08-21 0:36 ` [PATCH 16/16 v2] Non-stop follow exec tests Don Breazeal
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:36 UTC (permalink / raw)
To: gdb-patches
This patch adds documentation of the new RSP support for exec events.
[This was previously approved by Eli:
https://sourceware.org/ml/gdb-patches/2014-05/msg00690.html]
Thanks
--Don
gdb/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* NEWS: Mention RSP Stop Reply Packet, new stop reason 'exec'.
Mention gdbserver support for exec events on Linux.
gdb/doc/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* gdb.texinfo (Stop Reply Packets): Document RSP support
for exec events.
---
gdb/NEWS | 21 ++++++++++++---------
gdb/doc/gdb.texinfo | 9 ++++++++-
2 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index c3b1d23..3faeb05 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -14,20 +14,23 @@
confirmation if the program had stopped for a signal and the user
switched threads meanwhile.
-* Remote fork events
+* Remote fork and exec events
- GDBserver extended-remote Linux targets now support fork events.
- This enables follow-fork-mode, detach-on-fork, catch fork, and
- catch vfork for those targets with Linux kernels 2.5.60 and later.
+ GDBserver extended-remote Linux targets now support fork and exec
+ events. This enables follow-fork-mode, detach-on-fork, follow-exec-mode,
+ catch fork, catch vfork, and catch exec for those targets with Linux
+ kernels that support these events. Linux kernel versions 2.5.60 and
+ later support all of these events.
* New remote packets
T Stop Reply Packet's reason
- The T stop reply packet supports new stop reasons 'fork', 'vfork'
- and 'vforkdone'. The 'fork' and 'vfork' reasons signify that the
- specified inferior has executed a fork or vfork. The 'vforkdone'
- reason signifies that a vforked child process has executed either
- an exec or an exit.
+ The T stop reply packet supports new stop reasons 'fork', 'vfork',
+ 'vforkdone', and 'exec'. The 'fork' and 'vfork' reasons signify
+ that the specified inferior has executed a fork or vfork. The
+ 'vforkdone' reason signifies that a vforked child process has
+ executed either an exec or an exit. The 'exec' reason signifies
+ that the specified inferior executed a call to execve.
*** Changes in GDB 7.8
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 07c151b..5c333d5 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3196,7 +3196,8 @@ process, use the @code{file} command with the parent executable name
as its argument. By default, after an @code{exec} call executes,
@value{GDBN} discards the symbols of the previous executable image.
You can change this behaviour with the @w{@code{set follow-exec-mode}}
-command.
+command. This command is supported when connected to @code{gdbserver}
++using @kbd{target extended-remote} as well as in native mode.
@table @code
@kindex set follow-exec-mode
@@ -34746,6 +34747,12 @@ address spaces of the parent and child process are no longer
shared. The @var{r} part is ignored. This packet is only
applicable to targets that support vforkdone events.
+@cindex exec events, remote reply
+@item exec
+The packet indicates that @code{execve} was called, and @var{r} is the
+absolute pathname of the file that was executed, in hex. This packet
+is only applicable to targets that support exec events.
+
@cindex replay log events, remote reply
@item replaylog
The packet indicates that the target cannot continue replaying
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread
* [PATCH 16/16 v2] Non-stop follow exec tests
2014-08-07 18:00 [PATCH 00/10] Linux extended-remote fork events Don Breazeal
` (25 preceding siblings ...)
2014-08-21 0:36 ` [PATCH 15/16 v2] Extended-remote exec event documentation Don Breazeal
@ 2014-08-21 0:36 ` Don Breazeal
26 siblings, 0 replies; 110+ messages in thread
From: Don Breazeal @ 2014-08-21 0:36 UTC (permalink / raw)
To: gdb-patches
This patch modifies some follow-exec tests, getting one to work with
extended-remote targets and extending another for more coverage.
First, gdb.base/foll-exec.exp is changed to use clean_restart for
the initial test startup and to replace the proc zap_session
everywhere in the test. This provides for extended-remote targets
to be able to do runto_main.
The functionality is changed slightly by this, in that zap_session
did not exit and restart the debugger as clean_restart does.
However, none of the tests are checking for the interaction of
follow-exec with re-running the program, so the functionality is
tested in the same way.
The other change extends the non-ldr-exc-*.exp tests so that they run all
their cases in non-stop mode as well as all-stop mode. These tests cover
handling of exec events when non-leader threads call exec.
The tests now report 'untested when 'runto_main' fails. In non-stop mode
with 'target extended-remote', runto_main always fails with something like:
(gdb) run
Starting program: /home/me/gdb/testsuite/gdb.threads/non-ldr-exc-4
Unexpected vCont reply in non-stop mode: T0506:10e0ffffff7f0000;07:c8deffffff7f0000;10:c1a6abaaaa2a0000;thread:p5ee.5ee;core:0;
This happens in other tests as well (e.g. gdb.threads/thread_events.exp),
so I copied the error handling from that test so that the non-stop tests
report 'untested' for target extended-remote. I couldn't find anything
related to this in the gdb bug database, but I assume it is a known problem
since the other tests handle it.
Thanks
--Don
gdb/testsuite/
2014-08-20 Don Breazeal <donb@codesourcery.com>
* gdb.base/foll-exec.exp (zap_session): Delete proc.
(do_exec_tests): Replace zap_session with clean_restart.
(main): Use clean_restart for initial GDB startup.
* gdb.threads/non-ldr-exc-1.exp: Add non-stop cases.
* gdb.threads/non-ldr-exc-2.exp: Ditto.
* gdb.threads/non-ldr-exc-3.exp: Ditto.
* gdb.threads/non-ldr-exc-4.exp: Ditto.
---
gdb/testsuite/gdb.base/foll-exec.exp | 44 ++++-----------------------
gdb/testsuite/gdb.threads/non-ldr-exc-1.exp | 20 ++++++++++--
gdb/testsuite/gdb.threads/non-ldr-exc-2.exp | 36 +++++++++++++++++++---
gdb/testsuite/gdb.threads/non-ldr-exc-3.exp | 36 +++++++++++++++++++---
gdb/testsuite/gdb.threads/non-ldr-exc-4.exp | 20 ++++++++++--
5 files changed, 100 insertions(+), 56 deletions(-)
diff --git a/gdb/testsuite/gdb.base/foll-exec.exp b/gdb/testsuite/gdb.base/foll-exec.exp
index c1b1354..6051d29 100644
--- a/gdb/testsuite/gdb.base/foll-exec.exp
+++ b/gdb/testsuite/gdb.base/foll-exec.exp
@@ -44,32 +44,6 @@ if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable $com
return -1
}
-proc zap_session {} {
- global gdb_prompt
- global binfile
-
- send_gdb "kill\n"
- gdb_expect {
- -re ".*Kill the program being debugged.*y or n. $" {
- gdb_test_no_output "y" ""
- send_gdb "file $binfile\n"
- gdb_expect {
- -re ".*Load new symbol table from.*y or n. $" {
- send_gdb "y\n"
- gdb_expect {
- -re "Reading symbols from.*$gdb_prompt $" {}
- timeout { fail "loading symbols (timeout)"; return }
- }
- }
- -re ".*gdb_prompt $" {}
- timeout { fail "loading symbols (timeout)"; return }
- }
- }
- -re ".*$gdb_prompt $" {}
- timeout { fail "killing inferior (timeout)" ; return }
- }
-}
-
proc do_exec_tests {} {
global gdb_prompt
global binfile
@@ -103,7 +77,7 @@ proc do_exec_tests {} {
return
}
- zap_session
+ clean_restart ${binfile}
# Start the program running, and stop at main.
#
@@ -191,7 +165,7 @@ proc do_exec_tests {} {
# Explicitly kill this program, or a subsequent rerun actually runs
# the exec'd program, not the original program...
- zap_session
+ clean_restart ${binfile}
# Start the program running, and stop at main.
#
@@ -264,7 +238,7 @@ proc do_exec_tests {} {
# Explicitly kill this program, or a subsequent rerun actually runs
# the exec'd program, not the original program...
- zap_session
+ clean_restart ${binfile}
# Start the program running, and stop at main.
#
@@ -324,7 +298,7 @@ proc do_exec_tests {} {
# Explicitly kill this program, or a subsequent rerun actually runs
# the exec'd program, not the original program...
- zap_session
+ clean_restart ${binfile}
# Start the program running, and stop at main.
#
@@ -376,9 +350,7 @@ proc do_exec_tests {} {
timeout {fail "(timeout) print execd-program/local_j (after execv)"}
}
- # Explicitly kill this program, or a subsequent rerun actually runs
- # the exec'd program, not the original program...
- zap_session
+ clean_restart ${binfile}
# Start the program running, and stop at main.
#
@@ -402,11 +374,7 @@ proc do_exec_tests {} {
# Start with a fresh gdb
-gdb_exit
-gdb_start
-gdb_reinitialize_dir $srcdir/$subdir
-gdb_load ${binfile}
-
+clean_restart $binfile
# This is a test of gdb's ability to follow a process through a
# Unix exec() system call.
diff --git a/gdb/testsuite/gdb.threads/non-ldr-exc-1.exp b/gdb/testsuite/gdb.threads/non-ldr-exc-1.exp
index 8123a99..e35236a 100644
--- a/gdb/testsuite/gdb.threads/non-ldr-exc-1.exp
+++ b/gdb/testsuite/gdb.threads/non-ldr-exc-1.exp
@@ -28,13 +28,19 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
-proc do_test { lock_sched } {
- with_test_prefix "lock-sched$lock_sched" {
+proc do_test { lock_sched mode } {
+ with_test_prefix "lock-sched$lock_sched,$mode" {
global executable
clean_restart ${executable}
+ if { $mode == "non-stop" } {
+ gdb_test_no_output "set target-async on" "set async mode"
+ gdb_test_no_output "set non-stop on" "set non-stop mode"
+ }
+
if ![runto_main] {
+ untested "could not run to main"
return -1
}
@@ -48,11 +54,17 @@ proc do_test { lock_sched } {
gdb_test_no_output "set scheduler-locking on"
}
+ if { $mode == "non-stop" } {
+ gdb_test "thread 2" "Switching.*"
+ }
+
gdb_test "continue" \
".*is executing new program.*Breakpoint 1, main.* at .*" \
"continue over exec"
}
}
-do_test 0
-do_test 1
+do_test 0 "all-stop"
+do_test 1 "all-stop"
+do_test 0 "non-stop"
+do_test 1 "non-stop"
diff --git a/gdb/testsuite/gdb.threads/non-ldr-exc-2.exp b/gdb/testsuite/gdb.threads/non-ldr-exc-2.exp
index 857e7bc..a0281d6 100644
--- a/gdb/testsuite/gdb.threads/non-ldr-exc-2.exp
+++ b/gdb/testsuite/gdb.threads/non-ldr-exc-2.exp
@@ -29,18 +29,42 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
-proc do_test { lock_sched } {
- with_test_prefix "lock-sched$lock_sched" {
+# Test for breakpoint event among async thread events.
+# gdb_continue_to_breakpoint requires "$gdb_prompt $", but
+# here we may get a thread event message instead of EOL.
+proc gdb_continue_to_breakpoint_async { name pattern } {
+ global gdb_prompt
+
+ gdb_test_multiple "continue" $name {
+ -re ".*Breakpoint .* (at|in) ($pattern)$gdb_prompt " {
+ pass $name
+ }
+ }
+}
+
+proc do_test { lock_sched mode } {
+ with_test_prefix "lock-sched$lock_sched,$mode" {
global executable
clean_restart ${executable}
+ if { $mode == "non-stop" } {
+ gdb_test_no_output "set target-async on" "set async mode"
+ gdb_test_no_output "set non-stop on" "set non-stop mode"
+ }
+
if ![runto_main] {
+ untested "could not run to main"
return -1
}
gdb_breakpoint [gdb_get_line_number "break-here"]
- gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+ if { $mode == "non-stop" } {
+ gdb_continue_to_breakpoint_async "break-here" ".* break-here .*"
+ gdb_test "thread 2" "Switching.*"
+ } else {
+ gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+ }
gdb_test "info threads" \
"\r\n\[ \t\]*Id\[ \t\]+Target\[ \t\]+Id\[ \t\]+Frame\[ \t\]*\r\n\\* 2 *Thread \[^\r\n\]* at \[^\r\n\]*" \
@@ -59,5 +83,7 @@ proc do_test { lock_sched } {
}
}
-do_test 0
-do_test 1
+do_test 0 "all-stop"
+do_test 1 "all-stop"
+do_test 0 "non-stop"
+do_test 1 "non-stop"
diff --git a/gdb/testsuite/gdb.threads/non-ldr-exc-3.exp b/gdb/testsuite/gdb.threads/non-ldr-exc-3.exp
index 7f33d39..69c27d8 100644
--- a/gdb/testsuite/gdb.threads/non-ldr-exc-3.exp
+++ b/gdb/testsuite/gdb.threads/non-ldr-exc-3.exp
@@ -31,18 +31,42 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
-proc do_test { lock_sched } {
- with_test_prefix "lock-sched$lock_sched" {
+# Test for breakpoint event among async thread events.
+# gdb_continue_to_breakpoint requires "$gdb_prompt $", but
+# here we may get a thread event message instead of EOL.
+proc gdb_continue_to_breakpoint_async { name pattern } {
+ global gdb_prompt
+
+ gdb_test_multiple "continue" $name {
+ -re ".*Breakpoint .* (at|in) ($pattern)$gdb_prompt " {
+ pass $name
+ }
+ }
+}
+
+proc do_test { lock_sched mode } {
+ with_test_prefix "lock-sched$lock_sched,$mode" {
global executable
clean_restart ${executable}
+ if { $mode == "non-stop" } {
+ gdb_test_no_output "set target-async on" "set async mode"
+ gdb_test_no_output "set non-stop on" "set non-stop mode"
+ }
+
if ![runto_main] {
+ untested "could not run to main"
return -1
}
gdb_breakpoint [gdb_get_line_number "break-here"]
- gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+ if { $mode == "non-stop" } {
+ gdb_continue_to_breakpoint_async "break-here" ".* break-here .*"
+ gdb_test "thread 2" "Switching.*"
+ } else {
+ gdb_continue_to_breakpoint "break-here" ".* break-here .*"
+ }
# Also test with sched-lock to make sure we can follow the
# non-leader thread execing even though the main thread wasn't
@@ -57,5 +81,7 @@ proc do_test { lock_sched } {
}
}
-do_test 0
-do_test 1
+do_test 0 "all-stop"
+do_test 1 "all-stop"
+do_test 0 "non-stop"
+do_test 1 "non-stop"
diff --git a/gdb/testsuite/gdb.threads/non-ldr-exc-4.exp b/gdb/testsuite/gdb.threads/non-ldr-exc-4.exp
index a5e88bb..f83577d 100644
--- a/gdb/testsuite/gdb.threads/non-ldr-exc-4.exp
+++ b/gdb/testsuite/gdb.threads/non-ldr-exc-4.exp
@@ -30,13 +30,19 @@ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executab
return -1
}
-proc do_test { lock_sched } {
- with_test_prefix "lock-sched$lock_sched" {
+proc do_test { lock_sched mode } {
+ with_test_prefix "lock-sched$lock_sched,$mode" {
global executable
clean_restart ${executable}
+ if { $mode == "non-stop" } {
+ gdb_test_no_output "set target-async on" "set async mode"
+ gdb_test_no_output "set non-stop on" "set non-stop mode"
+ }
+
if ![runto_main] {
+ untested "could not run to main"
return -1
}
@@ -50,11 +56,17 @@ proc do_test { lock_sched } {
gdb_test_no_output "set scheduler-locking on"
}
+ if { $mode == "non-stop" } {
+ gdb_test "thread 2" "Switching.*"
+ }
+
gdb_test "continue" \
".*is executing new program.*Breakpoint 1, main.* at .*" \
"continue over exec"
}
}
-do_test 0
-do_test 1
+do_test 0 "all-stop"
+do_test 1 "all-stop"
+do_test 0 "non-stop"
+do_test 1 "non-stop"
--
1.7.0.4
^ permalink raw reply [flat|nested] 110+ messages in thread