diff mbox

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

Message ID 1414798134-11536-2-git-send-email-donb@codesourcery.com
State New
Headers show

Commit Message

Don Breazeal Oct. 31, 2014, 11:28 p.m. UTC
This patch implements a mechanism for GDB to determine what
extended-mode features are enabled in gdbserver.  This is
a preparatory patch for extended-remote fork and exec event
support.

Several new features are included in the potential response to
the qSupported packet, denoting fork, vfork, and exec events.
and exec events.  Note that vfork_done events are not represented
here, because if vfork events are supported, gdbserver will fake
up a vfork_done event for GDB even if the target doesn't provide
direct support for vfork_done.

A number of changes were required to make this work:

1) Sending the "!" (use extended protocol) packet before sending the
   qSupported packet so that gdbserver knows it is in extended mode
   when responding to qSupported.  Previously qSupported was the
   first packet sent.

2) Splitting nat/linux-ptrace.c:linux_enable_event_reporting into two
   functions.  Previously that function did two things: it checked
   which extended ptrace events were supported, and then enabled any
   of those events that were supported by the OS.  The new functions,
   linux_ptrace_check_options and linux_ptrace_enable options, each do
   one of those operations.  This allows gdbserver to call
   linux_ptrace_check_options during initialization, prior to
   accepting a connection from GDB.  It saves the information about
   which events are available, so that if and when gdbserver goes into
   extended mode, those events can be enabled, and GDB and gdbserver
   will agree about which events are enabled.
   
   There are some changes to linux-ptrace.c that build on the changes
   in commit 8009206ae2dec541b55edc488103c6c1ccb1416a.  That commit
   reduced the use of #ifdef GDBSERVER in linux-ptrace.c by allowing
   the ptrace client (GDB or gdbserver) to request support for
   certain ptrace options by maintaining static variables
   current_ptrace_options (enabled options) and additional_options
   (non-default options that the ptrace client has requested).

   This patch modifies that to maintain three static variables:
   current_ptrace_options (enabled options), requested_ptrace_options
   (non-default options that the ptrace client has requested), and
   available_ptrace_options (options from requested_ptrace_options
   that are supported by the OS).  available_ptrace_options is used
   in linux_ptrace_enable_options when extended-mode is enabled.

3) Defining a new packet for each of the extended-mode events along
   with corresponding "supports" predicates for each of them.

Some support for ptrace exit events is introduced in this patch,
although they can't be enabled until linux_ptrace_test_for_traceexit
is implemented in a subsequent patch.  Exit events are used in the
implementation of remote follow exec later in the patch series, so
they are included in the infrastructure here with the other events.

Tested on x64 Ubuntu Lucid, native only, and as part of testing of
subsequent patches.

gdb/gdbserver/ChangeLog:
2014-10-31  Don Breazeal  <donb@codesourcery.com>

	* linux-low.c (linux_low_filter_event): Replace call to
	linux_enable_event_reporting with call to
	linux_ptrace_enable_options.
	(linux_supports_fork_events): New function.
	(linux_supports_vfork_events): New function.
	(linux_supports_exec_events): New function.
	(linux_enable_extended_features): New function.
	(linux_target_ops): Add entries for the four new functions in
	the target vector.
	(initialize_low): Add calls to linux_ptrace_set_requested_options
	and linux_ptrace_check_options.
	* lynx-low.c (lynx_target_ops): Add NULL entries for the four
	new functions in the target vector.
	* server.c (handle_query): Add fork_events, vfork_events, and
	exec_events to features that can be listed in qSupported response.
	(process_serial_event): Call target function to enable extended
	features when "!" extended mode packet is received.
	(using_extended_protocol): New function.
	* server.h (using_extended_protocol): Declare new function.
	* target.h (struct target_ops) <supports_fork_events>: New member.
	<supports_vfork_events>: New member.
	<supports_exec_events>: New member.
	<enable_extended_features>: New member.
	* win32-low.c (win32_target_ops): Add NULL entries for the four
	new functions in the target vector.

gdb/ChangeLog:
2014-10-31  Don Breazeal  <donb@codesourcery.com>

	* linux-nat.c (linux_init_ptrace): Replace call to
	linux_enable_event_reporting with calls to
	linux_ptrace_check_options and linux_ptrace_enable_options.
	(linux_child_follow_fork): Replace call to
	linux_disable_event_reporting with a call to
	linux_ptrace_disable_options.
	* nat/linux-ptrace.c (linux_ptrace_check_options): New function,
	derived from linux_enable_event_reporting.
	(linux_test_for_tracesysgood): Replace additional_flags with
	requested_ptrace_options.
	(linux_ptrace_check_options): Rename from linux_check_ptrace_features,
	make it extern, use requested_ptrace_options and
	available_ptrace_options in place of additional_flags.
	(linux_ptrace_enable_options): New function, derived from
	linux_enable_event_reporting.
	(linux_ptrace_disable_options): Rename from
	linux_disable_event_reporting.
	(linux_supports_traceexit): New function.
	(linux_ptrace_set_requested_options): Rename from
	linux_ptrace_set_additional_flags, use requested_ptrace_options
	instead of additional_flags.
	* nat/linux-ptrace.h (linux_enable_event_reporting,
	linux_disable_event_reporting): Remove renamed functions.
	(linux_ptrace_check_options, linux_ptrace_enable_options,
	linux_ptrace_disable_options, linux_supports_traceexit): Declare
	new functions.
	(linux_ptrace_set_additional_flags): Remove renamed function.
	(linux_ptrace_set_requested_options): Declare new function.
	* remote.c (anonymous enum) <PACKET_vFollowFork>: New constant.
	<PACKET_fork_event_feature>: New constant.
	<PACKET_vfork_event_feature>: New constant.
	<PACKET_exec_event_feature>: New constant.
	(extended_remote_fork_event_p): New function.
	(extended_remote_vfork_event_p): New function.
	(extended_remote_exec_event_p): New function.
	(extended_remote_follow_fork): New function.
	(remote_protocol_features): Add entries for the four new packets.
	(remote_start_remote): Send "!" extended packet before qSupported.
	(_initialize_remote): Add three packets to the list of packets 
	excepted from having config commands.

---
 gdb/gdbserver/linux-low.c |   53 +++++++++++++++++++++++-
 gdb/gdbserver/lynx-low.c  |    4 ++
 gdb/gdbserver/server.c    |   19 +++++++++
 gdb/gdbserver/server.h    |    1 +
 gdb/gdbserver/target.h    |   32 ++++++++++++++
 gdb/gdbserver/win32-low.c |    4 ++
 gdb/linux-nat.c           |   15 ++++---
 gdb/nat/linux-ptrace.c    |   87 ++++++++++++++++++++++++++-------------
 gdb/nat/linux-ptrace.h    |    8 ++-
 gdb/remote.c              |  100 +++++++++++++++++++++++++++++++++++++++-----
 10 files changed, 271 insertions(+), 52 deletions(-)

Comments

Pedro Alves Nov. 13, 2014, 12:59 p.m. UTC | #1
On 10/31/2014 11:28 PM, Don Breazeal wrote:
> This patch implements a mechanism for GDB to determine what
> extended-mode features are enabled in gdbserver.  This is
> a preparatory patch for extended-remote fork and exec event
> support.
> 
> Several new features are included in the potential response to
> the qSupported packet, denoting fork, vfork, and exec events.
> and exec events.  Note that vfork_done events are not represented
> here, because if vfork events are supported, gdbserver will fake
> up a vfork_done event for GDB even if the target doesn't provide
> direct support for vfork_done.
> 
> A number of changes were required to make this work:
> 
> 1) Sending the "!" (use extended protocol) packet before sending the
>    qSupported packet so that gdbserver knows it is in extended mode
>    when responding to qSupported.  Previously qSupported was the
>    first packet sent.

I don't think we should do this.  What's wrong with always
reporting support for the feature ?

If GDB doesn't want them with "target remote", then it won't use
them.

If the server needs to know whether GDB supports or wants
the feature before activating it, then GDB's qSupported query
should indicate it.   See the "multiprocess+" feature.

As I mentioned before, I'd rather have fewer differences between
"remote" and "extended-remote", not more.

Thanks,
Pedro Alves
Don Breazeal Nov. 13, 2014, 6:28 p.m. UTC | #2
On 11/13/2014 4:59 AM, Pedro Alves wrote:
> On 10/31/2014 11:28 PM, Don Breazeal wrote:
>> This patch implements a mechanism for GDB to determine what
>> extended-mode features are enabled in gdbserver.  This is
>> a preparatory patch for extended-remote fork and exec event
>> support.
>>
>> Several new features are included in the potential response to
>> the qSupported packet, denoting fork, vfork, and exec events.
>> and exec events.  Note that vfork_done events are not represented
>> here, because if vfork events are supported, gdbserver will fake
>> up a vfork_done event for GDB even if the target doesn't provide
>> direct support for vfork_done.
>>
>> A number of changes were required to make this work:
>>
>> 1) Sending the "!" (use extended protocol) packet before sending the
>>    qSupported packet so that gdbserver knows it is in extended mode
>>    when responding to qSupported.  Previously qSupported was the
>>    first packet sent.
> 
> I don't think we should do this.  What's wrong with always
> reporting support for the feature ?
> 
> If GDB doesn't want them with "target remote", then it won't use
> them.
> 
> If the server needs to know whether GDB supports or wants
> the feature before activating it, then GDB's qSupported query
> should indicate it.   See the "multiprocess+" feature.
> 
> As I mentioned before, I'd rather have fewer differences between
> "remote" and "extended-remote", not more.
> 
> Thanks,
> Pedro Alves
> 

Given your comment on patch 6 saying that there isn't any
reason not to support follow fork in remote as well as
extended-remote, the problem I was solving goes away.

The problem I thought I had was that if there was no
agreement between GDB and gdbserver on whether the
features were supported, then gdbserver would send
events to GDB that it wasn't expecting in remote mode,
and handling those looked problematic.

Those bad assumptions get me every time.

Thanks,
--Don
Pedro Alves Nov. 13, 2014, 6:33 p.m. UTC | #3
On 11/13/2014 06:28 PM, Breazeal, Don wrote:
> On 11/13/2014 4:59 AM, Pedro Alves wrote:
>> On 10/31/2014 11:28 PM, Don Breazeal wrote:
>>> This patch implements a mechanism for GDB to determine what
>>> extended-mode features are enabled in gdbserver.  This is
>>> a preparatory patch for extended-remote fork and exec event
>>> support.
>>>
>>> Several new features are included in the potential response to
>>> the qSupported packet, denoting fork, vfork, and exec events.
>>> and exec events.  Note that vfork_done events are not represented
>>> here, because if vfork events are supported, gdbserver will fake
>>> up a vfork_done event for GDB even if the target doesn't provide
>>> direct support for vfork_done.
>>>
>>> A number of changes were required to make this work:
>>>
>>> 1) Sending the "!" (use extended protocol) packet before sending the
>>>    qSupported packet so that gdbserver knows it is in extended mode
>>>    when responding to qSupported.  Previously qSupported was the
>>>    first packet sent.
>>
>> I don't think we should do this.  What's wrong with always
>> reporting support for the feature ?
>>
>> If GDB doesn't want them with "target remote", then it won't use
>> them.
>>
>> If the server needs to know whether GDB supports or wants
>> the feature before activating it, then GDB's qSupported query
>> should indicate it.   See the "multiprocess+" feature.
>>
>> As I mentioned before, I'd rather have fewer differences between
>> "remote" and "extended-remote", not more.

> Given your comment on patch 6 saying that there isn't any
> reason not to support follow fork in remote as well as
> extended-remote, the problem I was solving goes away.
> 
> The problem I thought I had was that if there was no
> agreement between GDB and gdbserver on whether the
> features were supported, then gdbserver would send
> events to GDB that it wasn't expecting in remote mode,
> and handling those looked problematic.

Yeah, but that's a problem that has to be handled
when new gdbserver is connected to old gdb anyway, even
in extended-remote mode.

> Those bad assumptions get me every time.

Sorry about that.  :-/

I'm testing a fix for the stale-threads issue, that I think
we can just go ahead and install, and I have a few other
fixes to send you.

Thanks,
Pedro Alves
Don Breazeal Nov. 13, 2014, 6:37 p.m. UTC | #4
On 11/13/2014 4:59 AM, Pedro Alves wrote:
> On 10/31/2014 11:28 PM, Don Breazeal wrote:
>> This patch implements a mechanism for GDB to determine what
>> extended-mode features are enabled in gdbserver.  This is
>> a preparatory patch for extended-remote fork and exec event
>> support.
>>
>> Several new features are included in the potential response to
>> the qSupported packet, denoting fork, vfork, and exec events.
>> and exec events.  Note that vfork_done events are not represented
>> here, because if vfork events are supported, gdbserver will fake
>> up a vfork_done event for GDB even if the target doesn't provide
>> direct support for vfork_done.
>>
>> A number of changes were required to make this work:
>>
>> 1) Sending the "!" (use extended protocol) packet before sending the
>>    qSupported packet so that gdbserver knows it is in extended mode
>>    when responding to qSupported.  Previously qSupported was the
>>    first packet sent.
> 
> I don't think we should do this.  What's wrong with always
> reporting support for the feature ?
> 
> If GDB doesn't want them with "target remote", then it won't use
> them.
> 
> If the server needs to know whether GDB supports or wants
> the feature before activating it, then GDB's qSupported query
> should indicate it.   See the "multiprocess+" feature.
> 
> As I mentioned before, I'd rather have fewer differences between
> "remote" and "extended-remote", not more.
> 
> Thanks,
> Pedro Alves
> 

Given your comment on patch 6 saying that there isn't any
reason not to support follow fork in remote as well as
extended-remote, the problem I was solving goes away.

The problem I thought I had was that if there was no
agreement between GDB and gdbserver on whether the
features were supported, then gdbserver would send
events to GDB that it wasn't expecting in remote mode,
and handling those looked problematic.

Those bad assumptions get me every time.  This change
should make things simpler as long as I don't run into
troubles with "remote".

Thanks,
--Don
Pedro Alves Nov. 13, 2014, 6:48 p.m. UTC | #5
On 11/13/2014 06:37 PM, Breazeal, Don wrote:

> Given your comment on patch 6 saying that there isn't any
> reason not to support follow fork in remote as well as
> extended-remote, the problem I was solving goes away.
> 
> The problem I thought I had was that if there was no
> agreement between GDB and gdbserver on whether the
> features were supported, then gdbserver would send
> events to GDB that it wasn't expecting in remote mode,
> and handling those looked problematic.
> 
> Those bad assumptions get me every time.  This change
> should make things simpler as long as I don't run into
> troubles with "remote".

The only thing I'm imagining at the moment, is that
in "remote" mode, gdbserver always disconnects after
a "D" / detach packet, even if there are other processes
to debug.  But that seems silly anyway and we should
probably fix it.

As I mentioned, you'll need to make old GDB against new
GDBserver work correctly, that is, make gdbserver not send these new
events if debugging with old GDB, which is not expecting them.
That can be handled by making GDB send a "fork-events+" in its
own qSupported packet, so that GDBserver knows its talking to a
new GDB.  See how the "multiprocess+" feature is handled in both
GDB and GDBserver.  For a while, GDB didn't
broadcast "multiprocess+" when connecting with "target remote".  See:

  https://sourceware.org/ml/gdb-patches/2012-01/msg00490.html

One of the reasons I wanted that on "remote" was exactly to
make it possible to have fork events without caring for
remote vs extended-remote.  :-)

If there are bigger problems, we can make GDB not broadcast
the support with plain remote for starters.  But I'd be curious
to hear what the problems are.

Thanks,
Pedro Alves
Pedro Alves Nov. 13, 2014, 7:08 p.m. UTC | #6
On 11/13/2014 06:33 PM, Pedro Alves wrote:
> 
> I'm testing a fix for the stale-threads issue, that I think
> we can just go ahead and install.

I just sent the email, but it looks like it got stuck in RHs
mail servers...  Happens sometimes.  It should show up in a while.

Thanks,
Pedro Alves
diff mbox

Patch

diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 8776670..8334578 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -1878,7 +1878,7 @@  linux_low_filter_event (ptid_t filter_ptid, int lwpid, int wstat)
 
   if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
     {
-      linux_enable_event_reporting (lwpid);
+      linux_ptrace_enable_options (lwpid, using_extended_protocol ());
       child->must_set_ptrace_flags = 0;
     }
 
@@ -5168,6 +5168,33 @@  linux_supports_multi_process (void)
   return 1;
 }
 
+/* Check if fork events are supported.  */
+
+static int
+linux_supports_fork_events (void)
+{
+  return linux_supports_tracefork ();
+}
+
+/* Check if vfork events are supported.  */
+
+static int
+linux_supports_vfork_events (void)
+{
+  return linux_supports_tracefork ();
+}
+
+/* Check if exec events are supported.  */
+
+static int
+linux_supports_exec_events (void)
+{
+  /* Check for PTRACE_O_TRACEEXIT, since our implementation of follow
+     exec depends on this option, which was implemented in a later
+     kernel version than PTRACE_O_TRACEFORK et al.  */
+  return linux_supports_traceexit ();
+}
+
 static int
 linux_supports_disable_randomization (void)
 {
@@ -6035,6 +6062,22 @@  linux_low_read_btrace (struct btrace_target_info *tinfo, struct buffer *buffer,
 }
 #endif /* HAVE_LINUX_BTRACE */
 
+/* Enable any available extended-mode-only options.  */
+
+static void
+linux_enable_extended_features (void)
+{
+  long lwp = 0;
+
+  if (current_thread != NULL)
+    {
+      /* There is an inferior, so set the lwp argument.  */
+      lwp = ptid_get_lwp (current_thread->entry.id);
+    }
+
+  linux_ptrace_enable_options (lwp, using_extended_protocol ());
+}
+
 static struct target_ops linux_target_ops = {
   linux_create_inferior,
   linux_attach,
@@ -6079,6 +6122,10 @@  static struct target_ops linux_target_ops = {
   linux_async,
   linux_start_non_stop,
   linux_supports_multi_process,
+  linux_supports_fork_events,
+  linux_supports_vfork_events,
+  linux_supports_exec_events,
+  linux_enable_extended_features,
 #ifdef USE_THREAD_DB
   thread_db_handle_monitor_command,
 #else
@@ -6154,4 +6201,8 @@  initialize_low (void)
   sigaction (SIGCHLD, &sigchld_action, NULL);
 
   initialize_low_arch ();
+
+  /* Placeholder to enable extended events.  */
+  linux_ptrace_set_requested_options (0);
+  linux_ptrace_check_options ();
 }
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 96dea03..3ccc032 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -751,6 +751,10 @@  static struct target_ops lynx_target_ops = {
   NULL,  /* async */
   NULL,  /* start_non_stop */
   NULL,  /* supports_multi_process */
+  NULL,  /* supports_fork_events */
+  NULL,  /* supports_vfork_events */
+  NULL,  /* supports_exec_events */
+  NULL,  /* enable_extended_features */
   NULL,  /* handle_monitor_command */
 };
 
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 522d6f6..d297403 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -1903,6 +1903,15 @@  handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
       if (target_supports_multi_process ())
 	strcat (own_buf, ";multiprocess+");
 
+      if (target_supports_fork_events ())
+	strcat (own_buf, ";fork_events+");
+
+      if (target_supports_vfork_events ())
+	strcat (own_buf, ";vfork_events+");
+
+      if (target_supports_exec_events ())
+	strcat (own_buf, ";exec_events+");
+
       if (target_supports_non_stop ())
 	strcat (own_buf, ";QNonStop+");
 
@@ -3559,6 +3568,10 @@  process_serial_event (void)
       break;
     case '!':
       extended_protocol = 1;
+
+      if (the_target->enable_extended_features != NULL)
+	(*the_target->enable_extended_features) ();
+
       write_ok (own_buf);
       break;
     case '?':
@@ -3990,3 +4003,9 @@  handle_target_event (int err, gdb_client_data client_data)
 
   return 0;
 }
+
+int
+using_extended_protocol (void)
+{
+  return extended_protocol;
+}
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index cac73e9..f4f192c 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -109,6 +109,7 @@  typedef int gdb_fildes_t;
 /* Functions from server.c.  */
 extern int handle_serial_event (int err, gdb_client_data client_data);
 extern int handle_target_event (int err, gdb_client_data client_data);
+extern int using_extended_protocol (void);
 
 #include "remote-utils.h"
 
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 5e29b7f..570f57d 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -262,6 +262,18 @@  struct target_ops
   /* Returns true if the target supports multi-process debugging.  */
   int (*supports_multi_process) (void);
 
+  /* Returns true if fork events are supported.  */
+  int (*supports_fork_events) (void);
+
+  /* Returns true if vfork events are supported.  */
+  int (*supports_vfork_events) (void);
+
+  /* Returns true if exec events are supported.  */
+  int (*supports_exec_events) (void);
+
+  /* Enable features that are only available in extended mode.  */
+  void (*enable_extended_features) (void);
+
   /* If not NULL, target-specific routine to process monitor command.
      Returns 1 if handled, or 0 to perform default processing.  */
   int (*handle_monitor_command) (char *);
@@ -390,6 +402,26 @@  void set_target_ops (struct target_ops *);
 
 int kill_inferior (int);
 
+#define target_supports_follow_fork() \
+  (the_target->supports_follow_fork ? \
+   (*the_target->supports_follow_fork) () : 0)
+
+#define target_supports_follow_exec() \
+  (the_target->supports_follow_exec ? \
+   (*the_target->supports_follow_exec) () : 0)
+
+#define target_supports_fork_events() \
+  (the_target->supports_fork_events ? \
+   (*the_target->supports_fork_events) () : 0)
+
+#define target_supports_vfork_events() \
+  (the_target->supports_vfork_events ? \
+   (*the_target->supports_vfork_events) () : 0)
+
+#define target_supports_exec_events() \
+  (the_target->supports_exec_events ? \
+   (*the_target->supports_exec_events) () : 0)
+
 #define detach_inferior(pid) \
   (*the_target->detach) (pid)
 
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index e714933..9ae2f94 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1823,6 +1823,10 @@  static struct target_ops win32_target_ops = {
   NULL, /* async */
   NULL, /* start_non_stop */
   NULL, /* supports_multi_process */
+  NULL, /* supports_fork_events */
+  NULL, /* supports_vfork_events */
+  NULL, /* supports_exec_events */
+  NULL, /* enable_extended_features */
   NULL, /* handle_monitor_command */
   NULL, /* core_of_thread */
   NULL, /* read_loadmap */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 21797c1..f0487e9 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -326,7 +326,8 @@  pull_pid_from_list (struct simple_pid_list **listp, int pid, int *statusp)
 static void
 linux_init_ptrace (pid_t pid)
 {
-  linux_enable_event_reporting (pid);
+  linux_ptrace_check_options ();
+  linux_ptrace_enable_options (pid, 1);
   linux_ptrace_init_warnings ();
 }
 
@@ -418,7 +419,7 @@  linux_child_follow_fork (struct target_ops *ops, int follow_child,
 	  if (!gdbarch_software_single_step_p (target_thread_architecture
 						   (child_lp->ptid)))
 	    {
-	      linux_disable_event_reporting (child_pid);
+	      linux_ptrace_disable_options (child_pid);
 	      if (ptrace (PTRACE_SINGLESTEP, child_pid, 0, 0) < 0)
 		perror_with_name (_("Couldn't do single step"));
 	      if (my_waitpid (child_pid, &status, 0) < 0)
@@ -4803,11 +4804,11 @@  Enables printf debugging output."),
 
   /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
      support read-only process state.  */
-  linux_ptrace_set_additional_flags (PTRACE_O_TRACESYSGOOD
-				     | PTRACE_O_TRACEVFORKDONE
-				     | PTRACE_O_TRACEVFORK
-				     | PTRACE_O_TRACEFORK
-				     | PTRACE_O_TRACEEXEC);
+  linux_ptrace_set_requested_options (PTRACE_O_TRACESYSGOOD
+				      | PTRACE_O_TRACEVFORKDONE
+				      | PTRACE_O_TRACEVFORK
+				      | PTRACE_O_TRACEFORK
+				      | PTRACE_O_TRACEEXEC);
 }
 
 
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 8bc3f16..7871d95 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -25,14 +25,20 @@ 
 
 #include <stdint.h>
 
-/* Stores the currently supported ptrace options.  A value of
-   -1 means we did not check for features yet.  A value of 0 means
-   there are no supported features.  */
-static int current_ptrace_options = -1;
+/* Stores the ptrace options that have been requested by the
+   ptrace client beyond the default options that we attempt
+   to enable for all ptrace clients.  */
+static int requested_ptrace_options;
 
-/* Additional flags to test.  */
+/* Stores the ptrace options from REQUESTED_PTRACE_OPTIONS
+   that are supported by the OS.  A value of -1 means we did
+   not check for features yet.  A value of 0 means that none
+   of the requested options are supported.  */
+static int available_ptrace_options = -1;
 
-static int additional_flags;
+/* Stores the currently enabled ptrace options, or the default
+   option(s) that will be enabled once a process is loaded.  */
+static int current_ptrace_options;
 
 /* Find all possible reasons we could fail to attach PID and append
    these as strings to the already initialized BUFFER.  '\0'
@@ -310,13 +316,19 @@  static void linux_test_for_tracefork (int child_pid);
 
 /* Determine ptrace features available on this target.  */
 
-static void
-linux_check_ptrace_features (void)
+void
+linux_ptrace_check_options (void)
 {
   int child_pid, ret, status;
 
+  /* Check if we have initialized the ptrace features for this
+     target.  If not, proceed.  */
+  if (available_ptrace_options != -1)
+    return;
+
   /* Initialize the options.  */
   current_ptrace_options = 0;
+  available_ptrace_options = 0;
 
   /* Fork a child so we can do some testing.  The child will call
      linux_child_function and will get traced.  The child will
@@ -358,14 +370,14 @@  linux_test_for_tracesysgood (int child_pid)
 {
   int ret;
 
-  if ((additional_flags & PTRACE_O_TRACESYSGOOD) == 0)
+  if ((requested_ptrace_options & PTRACE_O_TRACESYSGOOD) == 0)
     return;
 
   ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
 		(PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
 
   if (ret == 0)
-    current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
+    available_ptrace_options |= PTRACE_O_TRACESYSGOOD;
 }
 
 /* Determine if PTRACE_O_TRACEFORK can be used to follow fork
@@ -385,14 +397,14 @@  linux_test_for_tracefork (int child_pid)
   if (ret != 0)
     return;
 
-  if ((additional_flags & PTRACE_O_TRACEVFORKDONE) != 0)
+  if ((requested_ptrace_options & PTRACE_O_TRACEVFORKDONE) != 0)
     {
       /* Check if the target supports PTRACE_O_TRACEVFORKDONE.  */
       ret = ptrace (PTRACE_SETOPTIONS, child_pid, (PTRACE_TYPE_ARG3) 0,
 		    (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
 					| PTRACE_O_TRACEVFORKDONE));
       if (ret == 0)
-	current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
+	available_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
     }
 
   /* Setting PTRACE_O_TRACEFORK did not cause an error, however we
@@ -428,11 +440,14 @@  linux_test_for_tracefork (int child_pid)
 	  int second_status;
 
 	  /* We got the PID from the grandchild, which means fork
-	     tracing is supported.  */
+	     tracing is supported.  Include default options in
+	     current_ptrace_options and save the rest as
+	     available options.  */
 	  current_ptrace_options |= PTRACE_O_TRACECLONE;
-	  current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
-                                                         | PTRACE_O_TRACEVFORK
-                                                         | PTRACE_O_TRACEEXEC));
+	  available_ptrace_options |= (requested_ptrace_options
+				       & (PTRACE_O_TRACEFORK
+					  | PTRACE_O_TRACEVFORK
+					  | PTRACE_O_TRACEEXEC));
 
 	  /* Do some cleanup and kill the grandchild.  */
 	  my_waitpid (second_pid, &second_status, 0);
@@ -449,25 +464,29 @@  linux_test_for_tracefork (int child_pid)
 	     "(%d, status 0x%x)"), ret, status);
 }
 
-/* Enable reporting of all currently supported ptrace events.  */
+/* Enable reporting of supported ptrace events.  If
+   USE_AVAILABLE_OPTIONS is false, then exclude the events
+   specified in available_ptrace_options.  If PID is non-zero,
+   set the ptrace options using that process.  */
 
 void
-linux_enable_event_reporting (pid_t pid)
+linux_ptrace_enable_options (pid_t pid, int use_available_options)
 {
-  /* Check if we have initialized the ptrace features for this
-     target.  If not, do it now.  */
-  if (current_ptrace_options == -1)
-    linux_check_ptrace_features ();
+  if (use_available_options)
+    current_ptrace_options |= available_ptrace_options;
 
-  /* Set the options.  */
-  ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
-	  (PTRACE_TYPE_ARG4) (uintptr_t) current_ptrace_options);
+  if (pid != 0)
+    {
+      /* Set the options.  */
+      ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0,
+	      (PTRACE_TYPE_ARG4) (uintptr_t) current_ptrace_options);
+    }
 }
 
 /* Disable reporting of all currently supported ptrace events.  */
 
 void
-linux_disable_event_reporting (pid_t pid)
+linux_ptrace_disable_options (pid_t pid)
 {
   /* Set the options.  */
   ptrace (PTRACE_SETOPTIONS, pid, (PTRACE_TYPE_ARG3) 0, 0);
@@ -516,6 +535,15 @@  linux_supports_tracevforkdone (void)
   return ptrace_supports_feature (PTRACE_O_TRACEVFORKDONE);
 }
 
+/* Returns non-zero if PTRACE_O_TRACEEXIT is supported by ptrace,
+   0 otherwise.  */
+
+int
+linux_supports_traceexit (void)
+{
+  return ptrace_supports_feature (PTRACE_O_TRACEEXIT);
+}
+
 /* Returns non-zero if PTRACE_O_TRACESYSGOOD is supported by ptrace,
    0 otherwise.  */
 
@@ -540,15 +568,16 @@  linux_ptrace_init_warnings (void)
   linux_ptrace_test_ret_to_nx ();
 }
 
-/* Set additional ptrace flags to use.  Some such flags may be checked
+/* Set additional ptrace flags to use, beyond the default options
+   checked for every ptrace client.  Some such flags may be checked
    by the implementation above.  This function must be called before
    any other function in this file; otherwise the flags may not take
    effect appropriately.  */
 
 void
-linux_ptrace_set_additional_flags (int flags)
+linux_ptrace_set_requested_options (int flags)
 {
-  additional_flags = flags;
+  requested_ptrace_options = flags;
 }
 
 /* Extract extended ptrace event from wait status.  */
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 31a77cd..03d27e1 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -85,13 +85,15 @@  struct buffer;
 
 extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
 extern void linux_ptrace_init_warnings (void);
-extern void linux_enable_event_reporting (pid_t pid);
-extern void linux_disable_event_reporting (pid_t pid);
+extern void linux_ptrace_check_options (void);
+extern void linux_ptrace_enable_options (pid_t pid, int use_available_options);
+extern void linux_ptrace_disable_options (pid_t pid);
 extern int linux_supports_tracefork (void);
 extern int linux_supports_traceclone (void);
 extern int linux_supports_tracevforkdone (void);
+extern int linux_supports_traceexit (void);
 extern int linux_supports_tracesysgood (void);
-extern void linux_ptrace_set_additional_flags (int);
+extern void linux_ptrace_set_requested_options (int);
 extern int linux_ptrace_get_extended_event (int wstat);
 extern int linux_is_extended_waitstatus (int wstat);
 
diff --git a/gdb/remote.c b/gdb/remote.c
index 20f2988..77c68d8 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1327,6 +1327,18 @@  enum {
   /* Support for qXfer:libraries-svr4:read with a non-empty annex.  */
   PACKET_augmented_libraries_svr4_read_feature,
 
+  /* Support for follow fork.  */
+  PACKET_vFollowFork,
+
+  /* Support for fork events.  */
+  PACKET_fork_event_feature,
+
+  /* Support for vfork events.  */
+  PACKET_vfork_event_feature,
+
+  /* Support for exec events.  */
+  PACKET_exec_event_feature,
+
   PACKET_MAX
 };
 
@@ -1432,6 +1444,52 @@  remote_multi_process_p (struct remote_state *rs)
   return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
 }
 
+/* Returns true if fork events are supported.  */
+
+static int
+extended_remote_fork_event_p (struct remote_state *rs)
+{
+  return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
+}
+
+/* Ifdef out the two functions below until they are needed for vfork
+   and exec catchpoints.  */
+#if 0
+/* Returns true if vfork events are supported.  */
+
+static int
+extended_remote_vfork_event_p (struct remote_state *rs)
+{
+  return packet_support (PACKET_vfork_event_feature) == PACKET_ENABLE;
+}
+
+/* Returns true if exec events are supported.  */
+
+static int
+extended_remote_exec_event_p (struct remote_state *rs)
+{
+  return packet_support (PACKET_exec_event_feature) == PACKET_ENABLE;
+}
+#endif
+
+/* Target follow-fork function for extended-remote targets.  */
+
+static int
+extended_remote_follow_fork (struct target_ops *target, int follow_child,
+			     int detach_fork)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  /* There is no need to check for anything other than the fork event.
+     We will also follow vforks if they are supported.  */
+  if (extended_remote_fork_event_p (rs))
+    {
+      /* FIXME: Implement follow-fork here.  */
+      return -1;
+    }
+  return 0;
+}
+
 /* Tokens for use by the asynchronous signal handlers for SIGINT.  */
 static struct async_signal_handler *async_sigint_remote_twice_token;
 static struct async_signal_handler *async_sigint_remote_token;
@@ -3386,9 +3444,18 @@  remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
      and so things may not be stable yet.  */
   rs->starting_up = 1;
 
-  /* The first packet we send to the target is the optional "supported
-     packets" request.  If the target can answer this, it will tell us
-     which later probes to skip.  */
+  if (extended_p)
+    {
+      /* Tell the remote that we are using the extended protocol
+	 before making any queries about supported features, since
+	 some features may only be supported in extended mode.  */
+      putpkt ("!");
+      getpkt (&rs->buf, &rs->buf_size, 0);
+    }
+
+  /* The first query packet we send to the target is the optional
+     "supported packets" request.  If the target can answer this, it
+     will tell us which later probes to skip.  */
   remote_query_supported ();
 
   /* If the stub wants to get a QAllow, compose one and send it.  */
@@ -3417,13 +3484,6 @@  remote_start_remote (int from_tty, struct target_ops *target, int extended_p)
 	rs->noack_mode = 1;
     }
 
-  if (extended_p)
-    {
-      /* Tell the remote that we are using the extended protocol.  */
-      putpkt ("!");
-      getpkt (&rs->buf, &rs->buf_size, 0);
-    }
-
   /* Let the target know which signals it is allowed to pass down to
      the program.  */
   update_signals_program_target ();
@@ -3981,7 +4041,15 @@  static const struct protocol_feature remote_protocol_features[] = {
   { "Qbtrace:off", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_off },
   { "Qbtrace:bts", PACKET_DISABLE, remote_supported_packet, PACKET_Qbtrace_bts },
   { "qXfer:btrace:read", PACKET_DISABLE, remote_supported_packet,
-    PACKET_qXfer_btrace }
+    PACKET_qXfer_btrace },
+  { "vFollowFork", PACKET_DISABLE, remote_supported_packet,
+    PACKET_vFollowFork },
+  { "fork_events", PACKET_DISABLE, remote_supported_packet,
+    PACKET_fork_event_feature },
+  { "vfork_events", PACKET_DISABLE, remote_supported_packet,
+    PACKET_vfork_event_feature },
+  { "exec_events", PACKET_DISABLE, remote_supported_packet,
+    PACKET_exec_event_feature }
 };
 
 static char *remote_support_xml;
@@ -11592,6 +11660,7 @@  init_extended_remote_ops (void)
 Specify the serial device it is connected to (e.g. /dev/ttya).";
   extended_remote_ops.to_open = extended_remote_open;
   extended_remote_ops.to_create_inferior = extended_remote_create_inferior;
+  extended_remote_ops.to_follow_fork = extended_remote_follow_fork;
   extended_remote_ops.to_mourn_inferior = extended_remote_mourn;
   extended_remote_ops.to_detach = extended_remote_detach;
   extended_remote_ops.to_attach = extended_remote_attach;
@@ -12162,7 +12231,11 @@  Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_btrace],
        "qXfer:btrace", "read-btrace", 0);
 
-  /* Assert that we've registered commands for all packet configs.  */
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_vFollowFork],
+       "vFollowFork", "follow-fork", 0);
+
+  /* Assert that we've registered "set remote foo-packet" commands
+     for all packet configs.  */
   {
     int i;
 
@@ -12181,6 +12254,9 @@  Show the maximum size of the address (in bits) in a memory packet."), NULL,
 	  case PACKET_DisconnectedTracing_feature:
 	  case PACKET_augmented_libraries_svr4_read_feature:
 	  case PACKET_qCRC:
+	  case PACKET_fork_event_feature:
+	  case PACKET_vfork_event_feature:
+	  case PACKET_exec_event_feature:
 	    /* Additions to this list need to be well justified:
 	       pre-existing packets are OK; new packets are not.  */
 	    excepted = 1;