@@ -76,6 +76,10 @@ hwbreak stop reason
vFile:fstat:
Return information about files on the remote system.
+T Stop Reply Packet's reason
+ The T stop reply packet supports a new stop reason 'xfork', which
+ signifies that the specified inferior has executed a fork.
+
* The info record command now shows the recording format and the
branch tracing configuration for the current thread when using
the btrace record target.
@@ -92,8 +96,15 @@ vFile:fstat:
HP/PA running HP-UX hppa*-*-hpux*
Itanium running HP-UX ia64-*-hpux*
-* The remote stub now reports support for fork and vfork events to GDB's
-qSupported query.
+* Remote fork events
+
+ The remote stub now reports support for fork and vfork events to
+ GDB's qSupported query.
+
+ GDBserver extended-remote Linux targets now provides basic support
+ for fork events. This enables follow-fork-mode and detach-on-fork
+ for those targets with Linux kernels 2.5.60 and later. Hardware
+ watchpoints are not inherited across a fork in this implementation.
*** Changes in GDB 7.9
@@ -3142,6 +3142,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.
@@ -35197,6 +35200,12 @@ 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 xfork
+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 replay log events, remote reply
@item replaylog
The packet indicates that the target cannot continue replaying
@@ -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"
@@ -371,22 +372,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
-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);
@@ -406,6 +408,57 @@ 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_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+ event_lwp->waitstatus.value.related_pid = ptid;
+ /* The status_pending field contains bits denoting the
+ extended event, so when the pending event is handled,
+ the handler will look at lwp->waitstatus. */
+ event_lwp->status_pending_p = 1;
+ event_lwp->status_pending = wstat;
+
+ /* Report the event. */
+ return 0;
+ }
+
if (debug_threads)
debug_printf ("HEW: Got clone event "
"from LWP %ld, new child is LWP %ld\n",
@@ -434,7 +487,12 @@ handle_extended_wait (struct lwp_info *event_child, int wstat)
new_lwp->status_pending_p = 1;
new_lwp->status_pending = status;
}
+
+ /* 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
@@ -1828,6 +1886,19 @@ check_stopped_by_watchpoint (struct lwp_info *child)
return child->stop_reason == TARGET_STOPPED_BY_WATCHPOINT;
}
+/* Wrapper for linux_enable_event_reporting that disables any
+ supported events that we have determined should not be
+ reported (e.g. GDB did not request them). */
+
+static void
+linux_low_enable_events (pid_t pid, int attached)
+{
+ if (!report_fork_events)
+ linux_ptrace_clear_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. */
@@ -1912,11 +1983,11 @@ linux_low_filter_event (int lwpid, int wstat)
}
}
- if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
+ if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags && gdb_connected ())
{
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;
}
@@ -1926,8 +1997,12 @@ 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))
+ {
+ /* The event has been handled, so just return without
+ reporting it. */
+ return NULL;
+ }
}
/* Check first whether this was a SW/HW breakpoint before checking
@@ -2502,6 +2577,18 @@ ignore_event (struct target_waitstatus *ourstatus)
return null_ptid;
}
+/* Return non-zero if WAITSTATUS reflects an extended linux
+ event. 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
@@ -2868,7 +2955,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 (&event_child->waitstatus));
run_breakpoint_commands (event_child->stop_pc);
@@ -2890,6 +2978,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 (&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
@@ -2999,7 +3094,17 @@ linux_wait_1 (ptid_t ptid,
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 lwp's waitstatus since we handled it already. */
+ event_child->waitstatus.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, and the client doesn't know we can
@@ -3032,7 +3137,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));
}
@@ -4830,8 +4935,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. */
@@ -5271,6 +5376,39 @@ linux_supports_vfork_events (void)
return linux_supports_tracefork ();
}
+/* Callback for 'find_inferior'. Set the (possibly changed) ptrace
+ options for the specified lwp. */
+
+static int
+reset_lwp_ptrace_options_callback (struct inferior_list_entry *entry,
+ void *args)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+ struct lwp_info *lwp = get_thread_lwp (thread);
+ struct process_info *proc = find_process_pid (pid_of (thread));
+
+ linux_low_enable_events (lwpid_of (thread), proc->attached);
+ lwp->must_set_ptrace_flags = 0;
+
+ return 0;
+}
+
+/* Target hook for 'handle_new_gdb_connection'. Causes a reset of the
+ ptrace flags for all inferiors. This is in case the new GDB connection
+ doesn't support the same set of events that the previous one did. */
+
+static void
+linux_handle_new_gdb_connection (void)
+{
+ pid_t pid;
+
+ /* Reset the ptrace options to enable on the inferior(s). */
+ linux_reset_ptrace_options ();
+
+ /* Request that all the lwps reset their ptrace options. */
+ find_inferior (&all_threads, reset_lwp_ptrace_options_callback , &pid);
+}
+
static int
linux_supports_disable_randomization (void)
{
@@ -6237,6 +6375,7 @@ static struct target_ops linux_target_ops = {
linux_supports_multi_process,
linux_supports_fork_events,
linux_supports_vfork_events,
+ linux_handle_new_gdb_connection,
#ifdef USE_THREAD_DB
thread_db_handle_monitor_command,
#else
@@ -6315,5 +6454,6 @@ initialize_low (void)
initialize_low_arch ();
/* Enable extended ptrace events. */
+ linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK);
linux_check_ptrace_features ();
}
@@ -270,6 +270,10 @@ struct lwp_info
/* When stopped is set, the last wait status recorded for this lwp. */
int last_status;
+ /* This is used to store extended ptrace event information until
+ it is reported to GDB. */
+ struct target_waitstatus waitstatus;
+
/* When stopped is set, this is where the lwp last stopped, with
decr_pc_after_break already accounted for. If the LWP is
running, this is the address at which the lwp was resumed. */
@@ -760,6 +760,7 @@ static struct target_ops lynx_target_ops = {
NULL, /* supports_multi_process */
NULL, /* supports_fork_events */
NULL, /* supports_vfork_events */
+ NULL, /* handle_new_gdb_connection */
NULL, /* handle_monitor_command */
};
@@ -1114,12 +1114,25 @@ 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 && report_fork_events)
+ {
+ enum gdb_signal signal = GDB_SIGNAL_TRAP;
+ const char *event = "xfork";
+
+ sprintf (buf, "T%02x%s:", signal, event);
+ buf += strlen (buf);
+ buf = write_ptid (buf, status->value.related_pid);
+ strcat (buf, ";");
+ }
+ else
+ sprintf (buf, "T%02x", status->value.sig);
+
buf += strlen (buf);
saved_thread = current_thread;
@@ -2104,6 +2104,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
strcat (own_buf, ";vFile:fstat+");
+ /* Reinitialize the target as needed for the new connection. */
+ target_handle_new_gdb_connection ();
+
return;
}
@@ -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;
/* True if the "swbreak+" feature is active. In that case, GDB wants
@@ -283,6 +283,9 @@ struct target_ops
/* Returns true if vfork events are supported. */
int (*supports_vfork_events) (void);
+ /* Allows target to re-initialize connection-specific settings. */
+ void (*handle_new_gdb_connection) (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 *);
@@ -422,6 +425,10 @@ int kill_inferior (int);
(the_target->supports_vfork_events ? \
(*the_target->supports_vfork_events) () : 0)
+#define target_handle_new_gdb_connection() \
+ (the_target->handle_new_gdb_connection ? \
+ (*the_target->handle_new_gdb_connection) () : 0)
+
#define detach_inferior(pid) \
(*the_target->detach) (pid)
@@ -1829,6 +1829,7 @@ static struct target_ops win32_target_ops = {
NULL, /* supports_multi_process */
NULL, /* supports_fork_events */
NULL, /* supports_vfork_events */
+ NULL, /* handle_new_gdb_connection */
NULL, /* handle_monitor_command */
NULL, /* core_of_thread */
NULL, /* read_loadmap */
@@ -4898,11 +4898,7 @@ 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_additional_flags (all_additional_flags);
}
@@ -25,13 +25,16 @@
#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. */
+/* Stores the ptrace options currently enabled by this gdbserver.
+ A value of -1 means we did not check for features yet. A value
+ of 0 means there are no enabled features. */
static int current_ptrace_options = -1;
-/* Additional flags to test. */
+/* Stores the fork and exec ptrace options supported by the operating
+ system. */
+static int supported_additional_flags = 0;
+/* Additional flags to test. */
static int additional_flags;
/* Find all possible reasons we could fail to attach PID and append
@@ -344,6 +347,7 @@ linux_check_ptrace_features (void)
/* Initialize the options. */
current_ptrace_options = 0;
+ supported_additional_flags = 0;
/* Fork a child so we can do some testing. The child will call
linux_child_function and will get traced. The child will
@@ -394,7 +398,7 @@ linux_test_for_tracesysgood (int child_pid)
(PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
if (ret == 0)
- current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
+ supported_additional_flags |= PTRACE_O_TRACESYSGOOD;
}
/* Determine if PTRACE_O_TRACEFORK can be used to follow fork
@@ -421,7 +425,7 @@ linux_test_for_tracefork (int child_pid)
(PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
| PTRACE_O_TRACEVFORKDONE));
if (ret == 0)
- current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
+ supported_additional_flags |= PTRACE_O_TRACEVFORKDONE;
}
/* Setting PTRACE_O_TRACEFORK did not cause an error, however we
@@ -459,9 +463,16 @@ 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));
+
+ /* Save the "extended" options in case we need to reset
+ the options later for a connect from a different GDB. */
+ supported_additional_flags |= (PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEEXEC);
+
+ /* Enable only those options requested by GDB. */
+ current_ptrace_options |= (supported_additional_flags
+ & additional_flags);
/* Do some cleanup and kill the grandchild. */
my_waitpid (second_pid, &second_status, 0);
@@ -518,6 +529,25 @@ linux_enable_event_reporting (pid_t pid, int attached)
(PTRACE_TYPE_ARG4) (uintptr_t) ptrace_options);
}
+/* Reset the ptrace options using potentially different
+ additional_flags. */
+
+void
+linux_reset_ptrace_options (void)
+{
+ /* 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 ();
+
+ /* Clear only the "extended" options. */
+ linux_ptrace_clear_flags (all_additional_flags);
+
+ /* Add the new requested flags back in. */
+ current_ptrace_options |= (supported_additional_flags
+ & additional_flags);
+}
+
/* Disable reporting of all currently supported ptrace events. */
void
@@ -603,9 +633,20 @@ linux_ptrace_init_warnings (void)
void
linux_ptrace_set_additional_flags (int flags)
{
+ int invalid_flags = (flags & ~all_additional_flags);
+
+ gdb_assert (invalid_flags == 0);
additional_flags = flags;
}
+/* Clear FLAGS in current_ptrace_options. */
+
+void
+linux_ptrace_clear_flags (int flags)
+{
+ current_ptrace_options &= ~flags;
+}
+
/* Extract extended ptrace event from wait status. */
int
@@ -139,6 +139,15 @@ struct buffer;
# define TRAP_HWBKPT 4
#endif
+/* All of the additional ptrace flags. The flags passed to
+ linux_ptrace_set_additional_flags must be in
+ this set of flags. */
+static const int all_additional_flags = (PTRACE_O_TRACEFORK
+ | PTRACE_O_TRACEVFORK
+ | PTRACE_O_TRACEVFORKDONE
+ | PTRACE_O_TRACEEXEC
+ | PTRACE_O_TRACESYSGOOD);
+
extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
/* Find all possible reasons we could have failed to attach to PTID
@@ -151,12 +160,14 @@ extern char *linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err);
extern void linux_ptrace_init_warnings (void);
extern void linux_check_ptrace_features (void);
extern void linux_enable_event_reporting (pid_t pid, int attached);
+extern void linux_reset_ptrace_options (void);
extern void linux_disable_event_reporting (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_tracesysgood (void);
extern void linux_ptrace_set_additional_flags (int);
+extern void linux_ptrace_clear_flags (int flags);
extern int linux_ptrace_get_extended_event (int wstat);
extern int linux_is_extended_waitstatus (int wstat);
extern int linux_wstatus_maybe_breakpoint (int wstat);
@@ -1470,6 +1470,14 @@ 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
+remote_fork_event_p (struct remote_state *rs)
+{
+ return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
+}
+
/* 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;
@@ -4425,16 +4433,22 @@ remote_open_1 (const char *name, int from_tty,
wait_forever_enabled_p = 1;
}
-/* This takes a program previously attached to and detaches it. After
- this is done, GDB can be used to debug some other program. We
- better not have left any breakpoints in the target program or it'll
- die when it hits one. */
+/* This detaches a program to which we previously attached, using
+ inferior_ptid to identify the process. After this is done, GDB
+ can be used to debug some other program. We better not have left
+ any breakpoints in the target program or it'll die when it hits
+ one. If IS_FORK_CHILD is true, then inferior_ptid is the child
+ of an unfollowed fork, and we need to avoid deleting breakpoints
+ still needed by the parent. */
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 is_fork_child)
{
int pid = ptid_get_pid (inferior_ptid);
struct remote_state *rs = get_remote_state ();
+ struct thread_info *tp = find_thread_ptid (inferior_ptid);
+ int is_fork_parent;
if (args)
error (_("Argument given to \"detach\" when remotely debugging."));
@@ -4468,22 +4482,75 @@ remote_detach_1 (const char *args, int from_tty, int extended)
else
error (_("Can't detach process."));
- if (from_tty && !extended)
+ if (from_tty && !rs->extended)
puts_filtered (_("Ending remote debugging.\n"));
- target_mourn_inferior ();
+ /* Check to see if we are detaching a fork parent. Note that if we
+ are detaching a fork child, tp == NULL. */
+ if (tp != NULL)
+ is_fork_parent = tp->pending_follow.kind == TARGET_WAITKIND_FORKED;
+
+ /* If doing detach-on-fork, we don't mourn, because that will delete
+ breakpoints that should be available for the followed inferior. */
+ if (!is_fork_child && !is_fork_parent)
+ target_mourn_inferior ();
+ else
+ {
+ inferior_ptid = null_ptid;
+ detach_inferior (pid);
+ }
}
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, 0);
+}
+
+/* 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 *ops, int follow_child,
+ int detach_fork)
+{
+ struct remote_state *rs = get_remote_state ();
+
+ if (remote_fork_event_p (rs))
+ {
+ if (detach_fork && !follow_child)
+ {
+ ptid_t parent_ptid;
+ ptid_t child_ptid;
+
+ gdb_assert (inferior_thread ()->pending_follow.kind
+ == TARGET_WAITKIND_FORKED);
+
+ /* remote_detach_1 detaches inferior_ptid, which is currently
+ the ptid of the parent. Switch inferior_ptid to the ptid
+ of the child for the duration of the call. */
+ parent_ptid = inferior_ptid;
+ child_ptid = inferior_thread ()->pending_follow.value.related_pid;
+ inferior_ptid = child_ptid;
+ gdb_assert (!ptid_equal (parent_ptid, child_ptid));
+
+ remote_detach_1 (ops, NULL, 0, 1);
+
+ /* Restore the parent ptid. */
+ inferior_ptid = parent_ptid;
+ }
+ }
+ return 0;
}
/* Same as remote_detach, but don't send the "D" packet; just disconnect. */
@@ -5652,6 +5719,11 @@ Packet: '%s'\n"),
p = unpack_varlen_hex (++p1, &c);
event->core = c;
}
+ else if (strncmp (p, "xfork", p1 - p) == 0)
+ {
+ event->ws.value.related_pid = read_ptid (++p1, &p);
+ event->ws.kind = TARGET_WAITKIND_FORKED;
+ }
else
{
ULONGEST pnum;
@@ -9514,8 +9586,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));
@@ -11938,6 +12013,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
@@ -62,6 +62,23 @@ proc continue_to_exit_bp_loc {} {
set seen_break 0
set seen_prompt 0
set seen_timeout 0
+
+ # If we are running with a native 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.
+ #
+ # If we are running with a truly remote gdbserver (gdb,noinferiorio),
+ # the output of the program will be inaccessible. In this case we do
+ # not check for the ($decimal done) output, but just look for the gdb
+ # prompt.
+ 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 +87,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)]} {
@@ -90,7 +108,7 @@ proc continue_to_exit_bp_loc {} {
fail "run to exit 2 (no prompt)"
} elseif { ! $seen_break } {
fail "run to exit 2 (no breakpoint hit)"
- } elseif { $seen_done != 16 } {
+ } elseif {![target_info exists gdb,noinferiorio] && $seen_done != 16 } {
fail "run to exit 2 (missing done messages)"
} else {
pass "run to exit 2"
@@ -29,6 +29,9 @@ load_lib libgloss.exp
load_lib cache.exp
load_lib gdb-utils.exp
+# Stores the expect spawn ID of gdbserver.
+global server_spawn_id
+
global GDB
if [info exists TOOL_EXECUTABLE] {
@@ -270,6 +270,8 @@ proc gdbserver_start { options arguments } {
append gdbserver_command " $arguments"
}
+ # Save the gdbserver spawn ID.
+ 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.