[PATCH 04/16 v2] Determine supported extended-remote features

Don Breazeal
2014-08-21T00:30:57Z

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