@@ -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.:
@@ -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 ();
}
@@ -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;
@@ -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 */
};
@@ -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;
@@ -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;
@@ -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 \
{ \
@@ -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 */
@@ -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
@@ -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);