diff mbox

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

Message ID 1408580964-27916-5-git-send-email-donb@codesourcery.com
State New
Headers show

Commit Message

Don Breazeal Aug. 21, 2014, 12:29 a.m. UTC
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(-)

Comments

Pedro Alves Oct. 15, 2014, 4:17 p.m. UTC | #1
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
Don Breazeal Oct. 21, 2014, 11:23 p.m. UTC | #2
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
Pedro Alves Oct. 22, 2014, 9:48 p.m. UTC | #3
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
Don Breazeal Oct. 31, 2014, 11:38 p.m. UTC | #4
On 10/22/2014 2:48 PM, Pedro Alves wrote:

> 
> Yes, qSupported is preferable.  I'm sorry about not foreseeing
> this potential confusion when I wrote that comment.
> 
> Thanks,
> Pedro Alves
> 
Hi Pedro,
I've posted a revised version of the patch that uses qSupported to
tell GDB which extended-mode features are supported as part of a
new version of the patch series here:

https://sourceware.org/ml/gdb-patches/2014-10/msg00869.html

I updated the entire patch series so that it would work with more recent
commits.  Sorry that it took me awhile to respond.
Thanks
--Don
diff mbox

Patch

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);
 }
 
 
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;