[v5,03/06] Extended-remote Linux follow fork

Message ID 1424997977-13316-4-git-send-email-donb@codesourcery.com
State New, archived
Headers

Commit Message

Don Breazeal Feb. 27, 2015, 12:46 a.m. UTC
  This is an updated version of the patch last reviewed here:
https://sourceware.org/ml/gdb-patches/2015-02/msg00261.html

Hi Pedro,
Here are responses to your review comments:

On 2/10/2015 8:36 AM, Pedro Alves wrote:
> On 01/25/2015 09:46 PM, Don Breazeal wrote:
...
>>> I'm not seeing what would be linux-specific?  On remote_follow_fork
>>> fork, we switch the current remote thread to gdb's current
>>> thread (either parent or child), by
>>> calling 'set_general_thread (inferior_ptid);'
>>> And then if we need to detach parent or child, we detach it with
>>> the D;PID packet.
>> 
>> There are two linux-specific issues, both of which are workarounds for
>> kernel issues.
>> 
>> 1) workaround for the hardware single-step kernel bug that causes the 
>>    hardware single-step flag to be inherited across a vfork.  In
>>    linux-nat.c:linux_child_follow_fork we manually step the inferior
>>    before detaching.
>> 
>> 2) workaround for kernels that support PTRACE_O_TRACEVFORK but not
>>    PTRACE_O_TRACEVFORKDONE.  In linux-nat.c:linux_child_follow_fork
>>    we run the vfork child for a little while, then create a fake
>>    vfork-done event.
>> 
>> I propose that we don't do either of these workarounds for remote 
>> follow fork.  For (1), the kernel fix that eliminates the need for
>> this looks like it is from 2009.  For (2), the workaround is for
>> kernels between versions 2.5.46 (04.Nov.2002) and 2.6.18 (20.Sep.2006).
>> Is it necessary to provide workarounds in a new feature for kernels
>> as old as these?  (1) in particular is difficult to implement, since
>> gdbserver has no way to know whether detach-on-fork has been set, or
>> whether a given detach has anything to do with a vfork that preceded
>> it.  OK to drop these?
>
> OK, let's do that, even if just to focus on the essence first.

Thanks.

>>    - handle_extended_wait is given a return value, denoting whether the
>>      handled event should be reported to GDB.  Previously it had only
>>      handled clone events, which were never reported.
>
> This may need to be adjusted a bit when rebased on mainline.
> handle_extended_wait now never resumes any LWP.

I think there were some mainline changes in this area that I hadn't
caught.  I've made some changes here that I hope will address this concern.
Primarily linux_low_filter_event doesn't return after the call to
handle_extended_wait in the case where we get an event that we want to
report to GDB.

> We never, ever, send "T05 fork" to a GDB that does not understand that, right?
> Because although GDB skips magic register "numbers", "fork" starts with
> a hex digit and a GDB that doesn't understand it would be very confused.
> We did that mistake with "core", and broke backwards compatibility then.

The only time we will send GDB a "T05 fork" is when it has previously 
included "fork-events+" in the qSupported packet.  I did change the
"stop reason" from "fork" to "xfork" anyway, just to eliminate any
possible hex digit confusion.  I see that you changed how this is
handled, so maybe that change is now unnecessary.


>> diff --git a/gdb/gdbserver/gdbthread.h b/gdb/gdbserver/gdbthread.h
>> index b3e3e9d..33643af 100644
>> --- a/gdb/gdbserver/gdbthread.h
>> +++ b/gdb/gdbserver/gdbthread.h
>> @@ -41,6 +41,10 @@ struct thread_info
>>    /* True if LAST_STATUS hasn't been reported to GDB yet.  */
>>    int status_pending_p;
>>  
>> +  /* This is used to store fork and exec event information until
>> +     it is reported to GDB.  */
>> +  struct target_waitstatus pending_follow;
>> +
>
> I'm not sure this interact correctly with status_pending_p.  On the
> GDB/linux-nat.c side, the equivalent would be lp->waitstatus, not
> thread->pending_follow, and the former is considered a pending status.
> See linux-nat.c:lwp_status_pending_p.
> 
> Consider the case of debugging more than one process simulatenously,
> and two forking at the same time.  GDB is only going to report one
> fork event at a time.  So the other event will need to stay pending.
> 
> But I don't see anything that would make status_pending_p_callback
> understand that the forked LWP has a pending event to report, but
> that it has already been processed by handle_extended_wait.
> We can't process the same extended wait twice.  GDBserver would
> get confused and likely hang (waiting for the wait status of the child).

There are a couple of issues here, I think.
1) putting this field in the thread_info structure and naming it
   pending_follow wasn't consistent with the native side's implementation,
   where thread_info.pending_follow is used for a different purpose.  To
   be consistent with the native side, this should be in the lwp_info
   structure, named waitstatus.

I have made this change.

2) This field should interact with status_pending_p.

In the previous version of the patch, I had cleverly hidden the
assignment of status_pending_p in linux_low_filter_event after the
return from handle_extended_wait. :-S

In this new version I've put the assignment right next to the assignment of
lp->waitstatus inside handle_extended_wait.  Thanks for pointing this out;
it makes more sense this way.


>> +static void
>> +linux_low_enable_events (pid_t pid, int attached)
>> +{
>> +  if (!report_fork_events)
>> +    linux_ptrace_clear_additional_flags (PTRACE_O_TRACEFORK);
>
> I'm not sure about this bit.  We should also consider cases like:
> 
>   1. A GDB that supports extended-remote and fork events starts
>     a program.
>   2. GDB disconnects
>   3. A new GDB connection comes along.  But this time, this GDB
>     does not understand fork events.
> 
> And the reverse too.
> 
> Also, if a non-stop or disconnected tracing connection
> disconnects, it'll leave the program running.  When a new
> GDB connects, we may need to change the ptrace options
> to enable/disable these events.

In this version of the patch I've implemented a new gdbserver
target function that is called when a qSupported packet is
processed.  The assumption is that receipt of a qSupported
packet is equivalent to the start of a new GDB connection.

I've made some changes to linux-ptrace.c that keep track
of the "additional flags" that the OS supports, as well as
the set of valid "additional flags".  Another new function
uses this information to reset the ptrace options based on
what GDB requests in the qSupported option.

> 
> Alternatively, always enable the events at the ptrace
> level, and then filter them out if the connected
> GDB doesn't support them.

I made an attempt at doing this back when I switched to using the
qSupported packet for the fork event features, and concluded that
the complexity of handling the events but not reporting them wasn't
worth the benefit.  By not enabling them if GDB doesn't want them
we can just use the existing (working) code.

> 
> Or even, report a T05 / SIGTRAP to GDBs that don't support
> the events.  That may actually be better than not reporting
> anything like today, because what we do today (ignore the
> fork child) is just a recipe for it crashing when it trips
> on a breakpoint that was set on the parent.  A random SIGTRAP
> at least gives the user a chance of doing something.

My approach has been to make the implementation behave like
the native implementation and to keep the default case the
same as how things work today.  So although this sounds like
it would be a nice feature, I propose that we leave reporting
a SIGTRAP in this case for another day.  This patch series
has taken a while, and I'm concerned about the impact of
adding new functionality.

> 
> Hmm.  Maybe we should rename "T05 fork" to something that
> doesn't start with "f" (and hex char), and then that'd be
> the simplest.  We'd always send the event over.  That may
> be best, as it gives us more leverage even if we decide to
> not report the events to old GDB, and then change our minds.

As noted previously, this is now "T05 xfork'.

> 
> (Even with that, I think it'd be good to keep GDB / GDBserver
> broadcasting the support in qSupported.)

Agreed.

>> +/* Return non-zero if WAITSTATUS reflects an extended linux
>> +   event that gdbserver supports.  Otherwise, return zero.  */
>
> I don't understand "gdbserver supports" here.  This is gdbserver
> code, so anything gdbserver doesn't support shouldn't even
> compile, right?  :-)  Can you clarify this comment please?

You are right, that didn't make sense.  Removed "gdbserver supports".
Sorry for the noise.

>> +++ b/gdb/gdbserver/remote-utils.c
>> @@ -1114,12 +1114,24 @@ prepare_resume_reply (char *buf, ptid_t ptid,
>>    switch (status->kind)
>>      {
>>      case TARGET_WAITKIND_STOPPED:
>> +    case TARGET_WAITKIND_FORKED:
>>        {
>>  	struct thread_info *saved_thread;
>>  	const char **regp;
>>  	struct regcache *regcache;
>>  
>> -	sprintf (buf, "T%02x", status->value.sig);
>> +	if (status->kind == TARGET_WAITKIND_FORKED && multi_process)
>
> I'm not sure this "multi_process" check here is correct.
> Shouldn't it be "report_fork_events" instead?

You are correct.  Fixed.

>> +	    sprintf (buf, "T%02x%s:p%x.%lx;", signal, event,
>> +		     ptid_get_pid (status->value.related_pid),
>> +		     ptid_get_lwp (status->value.related_pid));
>
> Can this use write_ptid?

Yes, done.

>> +#include "inf-child.h"
>
> That's a layering violation.  inf-child.h is the
> base/prototype target for default child (native) targets.

Got it.  Fixed.

>> +static int
>> +remote_follow_fork (struct target_ops *target, int follow_child,
>> +		    int detach_fork)
>> +{
>> +  struct remote_state *rs = get_remote_state ();
>> +
>> +  /* Checking the fork event is sufficient for both fork and vfork.  */
>
> Why?

Because the fork and vfork events were both introduced in the same version
of the Linux kernel (as I expect you knew already :-) ).  But, for this
patch we only need fork events anyway, so I just removed the comment from
this patch.  I put an appropriately explanatory comment in the follow vfork
patch.  I could go ahead and add remote_vfork_event_p in the vfork patch 
instead, if that would be preferable.

>> +  if (remote_fork_event_p (rs))
>> +    {
>> +      if (detach_fork && !follow_child)
>> +	{
>> +	  ptid_t child_ptid;
>> +	  pid_t child_pid;
>> +
>> +	  gdb_assert ((inferior_thread ()->pending_follow.kind
>> +		       == TARGET_WAITKIND_FORKED));
>> +	  child_ptid = inferior_thread ()->pending_follow.value.related_pid;
>> +	  child_pid = ptid_get_pid (child_ptid);
>> +
>> +	  /* Tell the remote target to detach.  */
>> +	  xsnprintf (rs->buf, get_remote_packet_size (), "D;%x", child_pid);
>> +
>> +	  putpkt (rs->buf);
>> +	  getpkt (&rs->buf, &rs->buf_size, 0);
>> +
>> +	  if (rs->buf[0] == 'O' && rs->buf[1] == 'K')
>> +	    ;
>> +	  else if (rs->buf[0] == '\0')
>> +	    error (_("Remote doesn't know how to detach"));
>> +	  else
>> +	    error (_("Can't detach process."));
>
> Instead of copying/inlining this part of remote_detach_1 here,
> can you please factor out this bit to a function that is used
> both here and in remote_detach_1 ?

I figured out that I could just call remote_detach_1 from
remote_follow_fork.  I just had to set inferior_ptid to the
child's ptid, call remote_detach_1, then restore inferior_ptid
with the parent's ptid.

>> +
>> +	  inferior_ptid = null_ptid;
>> +	  detach_inferior (child_pid);
>> +	  inf_child_maybe_unpush_target (target);
>
> Please just do:
> 
>   if (!have_inferiors ())
>     unpush_target (target);
> 
> inf_child_maybe_unpush_target checks the inf_child_explicitly_opened
> flag that is completely unrelated to "target remote".  That's for
> "target native".
> 
> But wait, why would you ever want to unpush the target here?

You are right, we wouldn't want to.  Even for 'target remote', there
will still be at least one inferior at this point (the parent), so
we will always want to keep the target.

>> +remote_detach_1 (struct target_ops *ops, const char *args,
>> +		 int from_tty, int extended)
>>  {
>>    int pid = ptid_get_pid (inferior_ptid);
>>    struct remote_state *rs = get_remote_state ();
>> +  struct thread_info *tp = first_thread_of_process (pid);
>>  
>>    if (args)
>>      error (_("Argument given to \"detach\" when remotely debugging."));
>> @@ -4442,19 +4492,28 @@ remote_detach_1 (const char *args, int from_tty, int extended)
>>    if (from_tty && !extended)
>>      puts_filtered (_("Ending remote debugging.\n"));
>>  
>> -  target_mourn_inferior ();
>> +  /* If doing detach-on-fork, we don't mourn, because that will delete
>> +     breakpoints that should be available for the child.  */
>> +  if (tp->pending_follow.kind != TARGET_WAITKIND_FORKED)
>> +    target_mourn_inferior ();
>> +  else
>> +    {
>> +      inferior_ptid = null_ptid;
>> +      detach_inferior (pid);
>> +      inf_child_maybe_unpush_target (ops);
>
> Likewise.  If this is the last process, and we're doing
> extended-remote, this will disconnect from the target, but
> extended-remote shouldn't disconnect.  This doesn't look like
> will do the right thing?

It seemed to work OK, I assume because to get to the call to
inf_child_maybe_unpush_target it had to have been dealing with
a fork.  But you are right, the call is unnecessary, aside from
being the wrong function to call.

I think this was an artifact of my work on 'target remote'.  I expect
that we may revisit this sort of thing when we get to that patch,
although thanks to this review I understand it all much better.

The updated version looks like this:

+  /* If doing detach-on-fork, we don't mourn, because that will delete
+     breakpoints that should be available for the followed inferior.  */
+  if (!is_fork_child && !is_fork_parent)
+    target_mourn_inferior ();
+  else
+    {
+      inferior_ptid = null_ptid;
+      detach_inferior (pid);
+    }


>> diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp
>> index e95cb4b..5682730 100644
>> --- a/gdb/testsuite/gdb.base/multi-forks.exp
>> +++ b/gdb/testsuite/gdb.base/multi-forks.exp
>> @@ -62,6 +62,18 @@ proc continue_to_exit_bp_loc {} {
>>      set seen_break 0
>>      set seen_prompt 0
>>      set seen_timeout 0
>> +
>> +    # If we are running with gdbserver, the output ($decimal done) will
>> +    # come via the spawn_id of gdbserver, not the spawn_id of gdb (the
>> +    # default).  So we grab the spawn_id of gdbserver, if it exists, and
>> +    # add it to the gdb_expect statement below using "-i", allowing us
>> +    # to apply the expect statement to the output of both spawn_ids.
>> +    global server_spawn_id
>> +    set current_spawn_id [board_info host fileid]
>> +    if {![info exists server_spawn_id]} {
>> +	set server_spawn_id ""
>> +    }
>> +
>
> Clever.  :-)  But it also means that the test will fail on
> true remote testing, right?
> 
> ISTM that if we don't have a server_spawn_id, the test should
> not be expecting the "done"s when gdb,noinferiorio is set.

I have changed the test so that if gdb,noinferiorio is set it
just ignores the test for the "done"s from the inferior.

I saw that you had implemented a more general version of the
spawn id changes.  I don't know if this will need to change
to work with that, but if it does presumably the changes will
be minor.

> +global server_spawn_id
>
> This should have a describing comment.
>
Done, but probably moot given your work on this.

>> +++ b/gdb/testsuite/lib/gdbserver-support.exp
>> @@ -39,7 +39,6 @@
>>  #	After GDB starts you should check global $gdbserver_gdbport for the
>>  #	real port used.  It is not useful if $gdbserver_reconnect_p was not set.
>>  #
>> -
>>  #
>
> Please don't remove that line.  It was splitting different sections.
> Above is baseboard config options.  Below is description of the
> following procedure.

Sorry about that.  I'll have to add "removed blank line" to the list of
common formatting errors I check for.

The updated patch is below.
Thanks!
--Don


This patch implements basic support for follow-fork and detach-on-fork on
extended-remote Linux targets.  Only 'fork' is supported in this patch;
'vfork' support is added n a subsequent patch.  This patch depends on 
the previous patches in the patch series.

Sufficient extended-remote functionality has been implemented here to pass
gdb.base/multi-forks.exp, as well as gdb.base/foll-fork.exp with the
catchpoint tests commented out.  Some other fork tests fail with this
patch because it doesn't provide the architecture support needed for
watchpoint inheritance or fork catchpoints.

The implementation follows the same general structure as for the native
implementation as much as possible.

This implementation included:
 * enabling fork events in linux-low.c in initialize_low and
   linux_enable_extended_features

 * handling fork events in gdbserver/linux-low.c:handle_extended_wait

   - when a fork event occurs in gdbserver, we must do the full creation
     of the new process, thread, lwp, and breakpoint lists.  This is
     required whether or not the new child is destined to be
     detached-on-fork, because GDB will make target calls that require all
     the structures.  In particular we need the breakpoint lists in order
     to remove the breakpoints from a detaching child.  If we are not
     detaching the child we will need all these structures anyway.

   - as part of this event handling we store the target_waitstatus in a new
     member of the parent thread_info structure, 'pending_follow'.  This
     is used to store extended event information for reporting to GDB.

   - handle_extended_wait is given a return value, denoting whether the
     handled event should be reported to GDB.  Previously it had only
     handled clone events, which were never reported.

 * using a new predicate in gdbserver to control handling of the fork event
   (and eventually all extended events) in linux_wait_1.  The predicate,
   extended_event_reported, checks a target_waitstatus.kind for an
   extended ptrace event.

 * implementing a new RSP 'T' Stop Reply Packet stop reason: "fork", in
   gdbserver/remote-utils.c and remote.c.

 * implementing new target and RSP support for target_follow_fork with
   target extended-remote.  (The RSP components were actually defined in
   patch 4, but they see their first use here).

   - remote target routine remote_follow_fork, which just sends the 'D;pid'
     detach packet to detach the new fork child cleanly.  We can't just
     call target_detach because the data structures for the forked child
     have not been allocated on the host side.

Tested on x64 Ubuntu Lucid, native, remote, extended-remote.

gdb/doc/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* gdb.texinfo (Forks): List targets that have fork debugging
	support.
	(Stop Reply Packets): List new stop reason "xfork".

gdb/gdbserver/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* linux-low.c (handle_extended_wait): Implement return value,
	handle PTRACE_EVENT_FORK.
	(linux_low_enable_events): New function.
	(linux_low_filter_event): Use return value from
	handle_extended_wait.
	(extended_event_reported): New function.
	(linux_wait_1): Call extended_event_reported.
	(linux_write_memory): Add pid to debug message.
	(reset_lwp_ptrace_options_callback): New function.
	(linux_handle_new_gdb_connection): New function.
	(linux_target_ops): Initialize new structure member.
	(initialize_low): Call linux_ptrace_set_additional_flags.
	* linux-low.h (struct lwp_info) <waitstatus>: New member.
	* lynx-low.c: Initialize new structure member.
	* remote-utils.c (prepare_resume_reply): Implement stop reason
	"xfork" for "T" stop message.
	* server.h (report_fork_events): Declare global flag.
	* target.h (struct target_ops) <handle_new_gdb_connection>:
	New member.
	* win32-low.c: Initialize new structure member.

gdb/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* NEWS: Announce follow fork support and associated RSP
	addition.
	* nat/linux-ptrace.c (supported_additional_flags): New
	static variable.
	(linux_check_ptrace_features): Use supported_additional_flags.
	(linux_test_for_tracesysgood): Likewise.
	(linux_test_for_tracefork): Likewise.
	(linux_reset_ptrace_options): New function.
	(linux_ptrace_clear_flags): New function.
	* nat/linux-ptrace.h: (linux_ptrace_clear_flags): Declare new
	function.
	* remote.c (remote_fork_event_p): New function.
	(remote_detach_1): Add target_ops argument, don't mourn inferior
	if doing detach-on-fork.
	(remote_detach): Add target_ops argument, pass to remote_detach_1.
	(extended_remote_detach): Add target_ops argument, pass to
	remote_detach_1.
	(remote_follow_fork): New function.
	(remote_parse_stop_reply): Handle new "T" stop reason "xfork".
	(remote_pid_to_str): Print "process" strings for pid/0/0 ptids.
	(init_extended_remote_ops): Initialize to_follow_fork.

gdb/testsuite/
2015-02-25  Don Breazeal  <donb@codesourcery.com>

	* gdb.base/multi-forks.exp (continue_to_exit_bp_loc):
	Use '-i' in expect statement to include input from forked child.
	Ignore inferior output if gdb,noinferiorio.
	* lib/gdb.exp (server_spawn_id): Declare new global
	variable.
	* lib/gdbserver-support.exp (server_spawn_id): Make
	variable global.


---
 gdb/NEWS                                |  15 ++-
 gdb/doc/gdb.texinfo                     |   9 ++
 gdb/gdbserver/linux-low.c               | 168 +++++++++++++++++++++++++++++---
 gdb/gdbserver/linux-low.h               |   4 +
 gdb/gdbserver/lynx-low.c                |   1 +
 gdb/gdbserver/remote-utils.c            |  15 ++-
 gdb/gdbserver/server.c                  |   3 +
 gdb/gdbserver/server.h                  |   1 +
 gdb/gdbserver/target.h                  |   7 ++
 gdb/gdbserver/win32-low.c               |   1 +
 gdb/linux-nat.c                         |   6 +-
 gdb/nat/linux-ptrace.c                  |  59 +++++++++--
 gdb/nat/linux-ptrace.h                  |  11 +++
 gdb/remote.c                            |  98 ++++++++++++++++---
 gdb/testsuite/gdb.base/multi-forks.exp  |  22 ++++-
 gdb/testsuite/lib/gdb.exp               |   3 +
 gdb/testsuite/lib/gdbserver-support.exp |   2 +
 17 files changed, 382 insertions(+), 43 deletions(-)
  

Patch

diff --git a/gdb/NEWS b/gdb/NEWS
index d7fe430..bd7fac9 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -63,6 +63,10 @@  qXfer:btrace-conf:read
 Qbtrace-conf:bts:size
   Set the requested ring buffer size for branch tracing in BTS format.
 
+T Stop Reply Packet's reason
+  The T stop reply packet supports a new stop reason 'xfork', which
+  signifies that the specified inferior has executed a fork.
+
 * The info record command now shows the recording format and the
   branch tracing configuration for the current thread when using
   the btrace record target.
@@ -71,8 +75,15 @@  Qbtrace-conf:bts:size
 * GDB now has support for DTrace USDT (Userland Static Defined
   Tracing) probes.  The supported targets are x86_64-*-linux-gnu.
 
-* The remote stub now reports support for fork and vfork events to GDB's
-qSupported query.
+* Remote fork events
+
+  The remote stub now reports support for fork and vfork events to
+  GDB's qSupported query.
+
+  GDBserver extended-remote Linux targets now provides basic support
+  for fork events.  This enables follow-fork-mode and detach-on-fork
+  for those targets with Linux kernels 2.5.60 and later.  Hardware
+  watchpoints are not inherited across a fork in this implementation.
 
 *** Changes in GDB 7.9
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 9834709..9b685bc 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -3142,6 +3142,9 @@  create additional processes using the @code{fork} or @code{vfork} functions.
 Currently, the only platforms with this feature are HP-UX (11.x and later
 only?) and @sc{gnu}/Linux (kernel version 2.5.60 and later).
 
+The fork debugging commands are supported in both native mode and when
+connected to @code{gdbserver} using @kbd{target extended-remote}.
+
 By default, when a program forks, @value{GDBN} will continue to debug
 the parent process and the child process will run unimpeded.
 
@@ -35181,6 +35184,12 @@  The packet indicates that the loaded libraries have changed.
 @value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new
 list of loaded libraries.  The @var{r} part is ignored.
 
+@cindex fork events, remote reply
+@item xfork
+The packet indicates that @code{fork} was called, and @var{r}
+is the ptid of the new child process.  This packet is only
+applicable to targets that support fork events.
+
 @cindex replay log events, remote reply
 @item replaylog
 The packet indicates that the target cannot continue replaying 
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 5eb7576..d26282b 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -20,6 +20,7 @@ 
 #include "linux-low.h"
 #include "nat/linux-osdata.h"
 #include "agent.h"
+#include "tdesc.h"
 
 #include "nat/linux-nat.h"
 #include "nat/linux-waitpid.h"
@@ -371,22 +372,23 @@  linux_add_process (int pid, int attached)
 static CORE_ADDR get_pc (struct lwp_info *lwp);
 
 /* Handle a GNU/Linux extended wait response.  If we see a clone
-   event, we need to add the new LWP to our list (and not report the
-   trap to higher layers).  */
+   event, we need to add the new LWP to our list (and return 0 so as
+   not to report the trap to higher layers).  */
 
-static void
+static int
 handle_extended_wait (struct lwp_info *event_child, int wstat)
 {
   int event = linux_ptrace_get_extended_event (wstat);
   struct thread_info *event_thr = get_lwp_thread (event_child);
   struct lwp_info *new_lwp;
 
-  if (event == PTRACE_EVENT_CLONE)
+  if ((event == PTRACE_EVENT_FORK) || (event == PTRACE_EVENT_CLONE))
     {
       ptid_t ptid;
       unsigned long new_pid;
       int ret, status;
 
+      /* Get the pid of the new lwp.  */
       ptrace (PTRACE_GETEVENTMSG, lwpid_of (event_thr), (PTRACE_TYPE_ARG3) 0,
 	      &new_pid);
 
@@ -406,6 +408,59 @@  handle_extended_wait (struct lwp_info *event_child, int wstat)
 	    warning ("wait returned unexpected status 0x%x", status);
 	}
 
+      if (event == PTRACE_EVENT_FORK)
+	{
+	  struct process_info *parent_proc;
+	  struct process_info *child_proc;
+	  struct lwp_info *child_lwp;
+	  struct lwp_info *parent_lwp;
+	  struct target_desc *tdesc;
+
+	  ptid = ptid_build (new_pid, new_pid, 0);
+
+	  if (debug_threads)
+	    {
+	      debug_printf ("HEW: Got fork event from LWP %ld, "
+			    "new child is %d\n",
+			    ptid_get_lwp (ptid_of (event_thr)),
+			    ptid_get_pid (ptid));
+	    }
+
+	  /* Add the new process to the tables and clone the breakpoint
+	     lists of the parent.  We need to do this even if the new process
+	     will be detached, since we will need the process object and the
+	     breakpoints to remove any breakpoints from memory when we
+	     detach, and the host side will access registers.  */
+	  child_proc = linux_add_process (new_pid, 0);
+	  gdb_assert (child_proc != NULL);
+	  child_lwp = add_lwp (ptid);
+	  gdb_assert (child_lwp != NULL);
+	  child_lwp->stopped = 1;
+	  parent_proc = get_thread_process (event_thr);
+	  child_proc->attached = parent_proc->attached;
+	  clone_all_breakpoints (&child_proc->breakpoints,
+				 &child_proc->raw_breakpoints,
+				 parent_proc->breakpoints);
+
+	  tdesc = xmalloc (sizeof (struct target_desc));
+	  copy_target_description (tdesc, parent_proc->tdesc);
+	  child_proc->tdesc = tdesc;
+	  child_lwp->must_set_ptrace_flags = 1;
+
+	  /* Save fork info in the parent thread.  */
+	  parent_lwp = get_thread_lwp (event_thr);
+	  parent_lwp->waitstatus.kind = TARGET_WAITKIND_FORKED;
+	  parent_lwp->waitstatus.value.related_pid = ptid;
+	  /* The status_pending field contains bits denoting the
+	     extended event, so when the pending event is handled,
+	     the handler will look at lwp->waitstatus.  */
+	  parent_lwp->status_pending_p = 1;
+	  parent_lwp->status_pending = wstat;
+
+	  /* Report the event.  */
+	  return 0;
+	}
+
       if (debug_threads)
 	debug_printf ("HEW: Got clone event "
 		      "from LWP %ld, new child is LWP %ld\n",
@@ -434,7 +489,12 @@  handle_extended_wait (struct lwp_info *event_child, int wstat)
 	  new_lwp->status_pending_p = 1;
 	  new_lwp->status_pending = status;
 	}
+
+      /* Don't report the event.  */
+      return 1;
     }
+
+  internal_error (__FILE__, __LINE__, _("unknown ptrace event %d"), event);
 }
 
 /* Return the PC as read from the regcache of LWP, without any
@@ -1773,6 +1833,19 @@  check_stopped_by_watchpoint (struct lwp_info *child)
   return child->stop_reason == LWP_STOPPED_BY_WATCHPOINT;
 }
 
+/* Wrapper for linux_enable_event_reporting that disables any
+   supported events that we have determined should not be
+   reported (e.g. GDB did not request them).  */
+
+static void
+linux_low_enable_events (pid_t pid, int attached)
+{
+  if (!report_fork_events)
+    linux_ptrace_clear_flags (PTRACE_O_TRACEFORK);
+
+  linux_enable_event_reporting (pid, attached);
+}
+
 /* Do low-level handling of the event, and check if we should go on
    and pass it to caller code.  Return the affected lwp if we are, or
    NULL otherwise.  */
@@ -1857,11 +1930,11 @@  linux_low_filter_event (int lwpid, int wstat)
 	}
     }
 
-  if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags)
+  if (WIFSTOPPED (wstat) && child->must_set_ptrace_flags && gdb_connected ())
     {
       struct process_info *proc = find_process_pid (pid_of (thread));
 
-      linux_enable_event_reporting (lwpid, proc->attached);
+      linux_low_enable_events (lwpid, proc->attached);
       child->must_set_ptrace_flags = 0;
     }
 
@@ -1871,8 +1944,12 @@  linux_low_filter_event (int lwpid, int wstat)
       && linux_is_extended_waitstatus (wstat))
     {
       child->stop_pc = get_pc (child);
-      handle_extended_wait (child, wstat);
-      return NULL;
+      if (handle_extended_wait (child, wstat))
+	{
+	  /* The event has been handled, so just return without
+	     reporting it.  */
+	  return NULL;
+	}
     }
 
   if (WIFSTOPPED (wstat) && WSTOPSIG (wstat) == SIGTRAP
@@ -2438,6 +2515,18 @@  ignore_event (struct target_waitstatus *ourstatus)
   return null_ptid;
 }
 
+/* Return non-zero if WAITSTATUS reflects an extended linux
+   event.  Otherwise, return zero.  */
+
+static int
+extended_event_reported (const struct target_waitstatus *waitstatus)
+{
+  if (waitstatus == NULL)
+    return 0;
+
+  return (waitstatus->kind == TARGET_WAITKIND_FORKED);
+}
+
 /* Wait for process, returns status.  */
 
 static ptid_t
@@ -2809,7 +2898,8 @@  linux_wait_1 (ptid_t ptid,
 		       && !bp_explains_trap && !trace_event)
 		   || (gdb_breakpoint_here (event_child->stop_pc)
 		       && gdb_condition_true_at_breakpoint (event_child->stop_pc)
-		       && gdb_no_commands_at_breakpoint (event_child->stop_pc)));
+		       && gdb_no_commands_at_breakpoint (event_child->stop_pc))
+		   || extended_event_reported (&event_child->waitstatus));
 
   run_breakpoint_commands (event_child->stop_pc);
 
@@ -2831,6 +2921,13 @@  linux_wait_1 (ptid_t ptid,
 			  paddress (event_child->stop_pc),
 			  paddress (event_child->step_range_start),
 			  paddress (event_child->step_range_end));
+	  if (extended_event_reported (&event_child->waitstatus))
+	    {
+	      char *str = target_waitstatus_to_string (ourstatus);
+	      debug_printf ("LWP %ld: extended event with waitstatus %s\n",
+			    lwpid_of (get_lwp_thread (event_child)), str);
+	      xfree (str);
+	    }
 	}
 
       /* We're not reporting this breakpoint to GDB, so apply the
@@ -2940,7 +3037,17 @@  linux_wait_1 (ptid_t ptid,
 	unstop_all_lwps (1, event_child);
     }
 
-  ourstatus->kind = TARGET_WAITKIND_STOPPED;
+  if (extended_event_reported (&event_child->waitstatus))
+    {
+      /* If the reported event is a fork, vfork or exec, let GDB know.  */
+      ourstatus->kind = event_child->waitstatus.kind;
+      ourstatus->value = event_child->waitstatus.value;
+
+      /* Reset the event lwp's waitstatus since we handled it already.  */
+      event_child->waitstatus.kind = TARGET_WAITKIND_SPURIOUS;
+    }
+  else
+    ourstatus->kind = TARGET_WAITKIND_STOPPED;
 
   /* Now that we've selected our final event LWP, un-adjust its PC if
      it was a software breakpoint.  */
@@ -2971,7 +3078,7 @@  linux_wait_1 (ptid_t ptid,
 	 but, it stopped for other reasons.  */
       ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
     }
-  else
+  else if (ourstatus->kind == TARGET_WAITKIND_STOPPED)
     {
       ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
     }
@@ -4769,8 +4876,8 @@  linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
 	val = val & 0xffff;
       else if (len == 3)
 	val = val & 0xffffff;
-      debug_printf ("Writing %0*x to 0x%08lx\n", 2 * ((len < 4) ? len : 4),
-		    val, (long)memaddr);
+      debug_printf ("Writing %0*x to 0x%08lx in process %d\n",
+		    2 * ((len < 4) ? len : 4), val, (long)memaddr, pid);
     }
 
   /* Fill start and end extra bytes of buffer with existing memory data.  */
@@ -5170,6 +5277,39 @@  linux_supports_vfork_events (void)
   return linux_supports_tracefork ();
 }
 
+/* Callback for 'find_inferior'.  Set the (possibly changed) ptrace
+   options for the specified lwp.  */
+
+static int
+reset_lwp_ptrace_options_callback (struct inferior_list_entry *entry,
+				   void *args)
+{
+  struct thread_info *thread = (struct thread_info *) entry;
+  struct lwp_info *lwp = get_thread_lwp (thread);
+  struct process_info *proc = find_process_pid (pid_of (thread));
+
+  linux_low_enable_events (lwpid_of (thread), proc->attached);
+  lwp->must_set_ptrace_flags = 0;
+
+  return 0;
+}
+
+/* Target hook for 'handle_new_gdb_connection'.  Causes a reset of the
+   ptrace flags for all inferiors.  This is in case the new GDB connection
+   doesn't support the same set of events that the previous one did.  */
+
+static void
+linux_handle_new_gdb_connection (void)
+{
+  pid_t pid;
+
+  /* Reset the ptrace options to enable on the inferior(s).  */
+  linux_reset_ptrace_options ();
+
+  /* Request that all the lwps reset their ptrace options.  */
+  find_inferior (&all_threads, reset_lwp_ptrace_options_callback , &pid);
+}
+
 static int
 linux_supports_disable_randomization (void)
 {
@@ -6132,6 +6272,7 @@  static struct target_ops linux_target_ops = {
   linux_supports_multi_process,
   linux_supports_fork_events,
   linux_supports_vfork_events,
+  linux_handle_new_gdb_connection,
 #ifdef USE_THREAD_DB
   thread_db_handle_monitor_command,
 #else
@@ -6210,5 +6351,6 @@  initialize_low (void)
   initialize_low_arch ();
 
   /* Enable extended ptrace events.  */
+  linux_ptrace_set_additional_flags (PTRACE_O_TRACEFORK);
   linux_check_ptrace_features ();
 }
diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h
index bea4a37..0a53eaf 100644
--- a/gdb/gdbserver/linux-low.h
+++ b/gdb/gdbserver/linux-low.h
@@ -287,6 +287,10 @@  struct lwp_info
   /* When stopped is set, the last wait status recorded for this lwp.  */
   int last_status;
 
+  /* This is used to store extended ptrace event information until
+     it is reported to GDB.  */
+  struct target_waitstatus waitstatus;
+
   /* When stopped is set, this is where the lwp last stopped, with
      decr_pc_after_break already accounted for.  If the LWP is
      running, this is the address at which the lwp was resumed.  */
diff --git a/gdb/gdbserver/lynx-low.c b/gdb/gdbserver/lynx-low.c
index 1126103..64a5bf3 100644
--- a/gdb/gdbserver/lynx-low.c
+++ b/gdb/gdbserver/lynx-low.c
@@ -756,6 +756,7 @@  static struct target_ops lynx_target_ops = {
   NULL,  /* supports_multi_process */
   NULL,  /* supports_fork_events */
   NULL,  /* supports_vfork_events */
+  NULL,  /* handle_new_gdb_connection */
   NULL,  /* handle_monitor_command */
 };
 
diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c
index 8854c4c..6191e58 100644
--- a/gdb/gdbserver/remote-utils.c
+++ b/gdb/gdbserver/remote-utils.c
@@ -1114,12 +1114,25 @@  prepare_resume_reply (char *buf, ptid_t ptid,
   switch (status->kind)
     {
     case TARGET_WAITKIND_STOPPED:
+    case TARGET_WAITKIND_FORKED:
       {
 	struct thread_info *saved_thread;
 	const char **regp;
 	struct regcache *regcache;
 
-	sprintf (buf, "T%02x", status->value.sig);
+	if (status->kind == TARGET_WAITKIND_FORKED && report_fork_events)
+	  {
+	    enum gdb_signal signal = GDB_SIGNAL_TRAP;
+	    const char *event = "xfork";
+
+	    sprintf (buf, "T%02x%s:", signal, event);
+	    buf += strlen (buf);
+	    buf = write_ptid (buf, status->value.related_pid);
+	    strcat (buf, ";");
+	  }
+	else
+	  sprintf (buf, "T%02x", status->value.sig);
+
 	buf += strlen (buf);
 
 	saved_thread = current_thread;
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 1efcbc0..19e3ba4 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2081,6 +2081,9 @@  handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 
       supported_btrace_packets (own_buf);
 
+      /* Reinitialize the target as needed for the new connection.  */
+      target_handle_new_gdb_connection ();
+
       return;
     }
 
diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h
index dbf31d5..ce6ffb9 100644
--- a/gdb/gdbserver/server.h
+++ b/gdb/gdbserver/server.h
@@ -84,6 +84,7 @@  extern int disable_packet_qfThreadInfo;
 
 extern int run_once;
 extern int multi_process;
+extern int report_fork_events;
 extern int non_stop;
 
 extern int disable_randomization;
diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h
index 74ed07c..1911584 100644
--- a/gdb/gdbserver/target.h
+++ b/gdb/gdbserver/target.h
@@ -268,6 +268,9 @@  struct target_ops
   /* Returns true if vfork events are supported.  */
   int (*supports_vfork_events) (void);
 
+  /* Allows target to re-initialize connection-specific settings.  */
+  void (*handle_new_gdb_connection) (void);
+
   /* If not NULL, target-specific routine to process monitor command.
      Returns 1 if handled, or 0 to perform default processing.  */
   int (*handle_monitor_command) (char *);
@@ -407,6 +410,10 @@  int kill_inferior (int);
   (the_target->supports_vfork_events ? \
    (*the_target->supports_vfork_events) () : 0)
 
+#define target_handle_new_gdb_connection() \
+  (the_target->handle_new_gdb_connection ? \
+   (*the_target->handle_new_gdb_connection) () : 0)
+
 #define detach_inferior(pid) \
   (*the_target->detach) (pid)
 
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index 210a747..fa6c455 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1825,6 +1825,7 @@  static struct target_ops win32_target_ops = {
   NULL, /* supports_multi_process */
   NULL, /* supports_fork_events */
   NULL, /* supports_vfork_events */
+  NULL, /* handle_new_gdb_connection */
   NULL, /* handle_monitor_command */
   NULL, /* core_of_thread */
   NULL, /* read_loadmap */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 2e1133d..2f93951 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -4804,11 +4804,7 @@  Enables printf debugging output."),
 
   /* Do not enable PTRACE_O_TRACEEXIT until GDB is more prepared to
      support read-only process state.  */
-  linux_ptrace_set_additional_flags (PTRACE_O_TRACESYSGOOD
-				     | PTRACE_O_TRACEVFORKDONE
-				     | PTRACE_O_TRACEVFORK
-				     | PTRACE_O_TRACEFORK
-				     | PTRACE_O_TRACEEXEC);
+  linux_ptrace_set_additional_flags (all_additional_flags);
 }
 
 
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index ba934af..55fd20d 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -25,13 +25,16 @@ 
 
 #include <stdint.h>
 
-/* Stores the currently supported ptrace options.  A value of
-   -1 means we did not check for features yet.  A value of 0 means
-   there are no supported features.  */
+/* Stores the ptrace options currently enabled by this gdbserver.
+   A value of -1 means we did not check for features yet.  A value
+   of 0 means there are no enabled features.  */
 static int current_ptrace_options = -1;
 
-/* Additional flags to test.  */
+/* Stores the fork and exec ptrace options supported by the operating
+   system.  */
+static int supported_additional_flags = 0;
 
+/* Additional flags to test.  */
 static int additional_flags;
 
 /* Find all possible reasons we could fail to attach PID and append
@@ -344,6 +347,7 @@  linux_check_ptrace_features (void)
 
   /* Initialize the options.  */
   current_ptrace_options = 0;
+  supported_additional_flags = 0;
 
   /* Fork a child so we can do some testing.  The child will call
      linux_child_function and will get traced.  The child will
@@ -394,7 +398,7 @@  linux_test_for_tracesysgood (int child_pid)
 		(PTRACE_TYPE_ARG4) PTRACE_O_TRACESYSGOOD);
 
   if (ret == 0)
-    current_ptrace_options |= PTRACE_O_TRACESYSGOOD;
+    supported_additional_flags |= PTRACE_O_TRACESYSGOOD;
 }
 
 /* Determine if PTRACE_O_TRACEFORK can be used to follow fork
@@ -421,7 +425,7 @@  linux_test_for_tracefork (int child_pid)
 		    (PTRACE_TYPE_ARG4) (PTRACE_O_TRACEFORK
 					| PTRACE_O_TRACEVFORKDONE));
       if (ret == 0)
-	current_ptrace_options |= PTRACE_O_TRACEVFORKDONE;
+	supported_additional_flags |= PTRACE_O_TRACEVFORKDONE;
     }
 
   /* Setting PTRACE_O_TRACEFORK did not cause an error, however we
@@ -459,9 +463,16 @@  linux_test_for_tracefork (int child_pid)
 	  /* We got the PID from the grandchild, which means fork
 	     tracing is supported.  */
 	  current_ptrace_options |= PTRACE_O_TRACECLONE;
-	  current_ptrace_options |= (additional_flags & (PTRACE_O_TRACEFORK
-                                                         | PTRACE_O_TRACEVFORK
-                                                         | PTRACE_O_TRACEEXEC));
+
+	  /* Save the "extended" options in case we need to reset
+	     the options later for a connect from a different GDB.  */
+	  supported_additional_flags |= (PTRACE_O_TRACEFORK
+					  | PTRACE_O_TRACEVFORK
+					  | PTRACE_O_TRACEEXEC);
+
+	  /* Enable only those options requested by GDB.  */
+	  current_ptrace_options |= (supported_additional_flags
+				     & additional_flags);
 
 	  /* Do some cleanup and kill the grandchild.  */
 	  my_waitpid (second_pid, &second_status, 0);
@@ -518,6 +529,25 @@  linux_enable_event_reporting (pid_t pid, int attached)
 	  (PTRACE_TYPE_ARG4) (uintptr_t) ptrace_options);
 }
 
+/* Reset the ptrace options using potentially different
+   additional_flags.  */
+
+void
+linux_reset_ptrace_options (void)
+{
+  /* Check if we have initialized the ptrace features for this
+     target.  If not, do it now.  */
+  if (current_ptrace_options == -1)
+    linux_check_ptrace_features ();
+
+  /* Clear only the "extended" options.  */
+  linux_ptrace_clear_flags (all_additional_flags);
+
+  /* Add the new requested flags back in.  */
+  current_ptrace_options |= (supported_additional_flags
+			     & additional_flags);
+}
+
 /* Disable reporting of all currently supported ptrace events.  */
 
 void
@@ -603,9 +633,20 @@  linux_ptrace_init_warnings (void)
 void
 linux_ptrace_set_additional_flags (int flags)
 {
+  int invalid_flags = (flags & ~all_additional_flags);
+
+  gdb_assert (invalid_flags == 0);
   additional_flags = flags;
 }
 
+/* Clear FLAGS in current_ptrace_options.  */
+
+void
+linux_ptrace_clear_flags (int flags)
+{
+  current_ptrace_options &= ~flags;
+}
+
 /* Extract extended ptrace event from wait status.  */
 
 int
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index c7bcfb0..4d772ee 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -88,6 +88,15 @@  struct buffer;
 #define __WALL          0x40000000 /* Wait for any child.  */
 #endif
 
+/* All of the additional ptrace flags.  The flags passed to
+   linux_ptrace_set_additional_flags must be in
+   this set of flags.  */
+static const int all_additional_flags = (PTRACE_O_TRACEFORK
+					 | PTRACE_O_TRACEVFORK
+					 | PTRACE_O_TRACEVFORKDONE
+					 | PTRACE_O_TRACEEXEC
+					 | PTRACE_O_TRACESYSGOOD);
+
 extern void linux_ptrace_attach_fail_reason (pid_t pid, struct buffer *buffer);
 
 /* Find all possible reasons we could have failed to attach to PTID
@@ -100,12 +109,14 @@  extern char *linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err);
 extern void linux_ptrace_init_warnings (void);
 extern void linux_check_ptrace_features (void);
 extern void linux_enable_event_reporting (pid_t pid, int attached);
+extern void linux_reset_ptrace_options (void);
 extern void linux_disable_event_reporting (pid_t pid);
 extern int linux_supports_tracefork (void);
 extern int linux_supports_traceclone (void);
 extern int linux_supports_tracevforkdone (void);
 extern int linux_supports_tracesysgood (void);
 extern void linux_ptrace_set_additional_flags (int);
+extern void linux_ptrace_clear_flags (int flags);
 extern int linux_ptrace_get_extended_event (int wstat);
 extern int linux_is_extended_waitstatus (int wstat);
 extern int linux_wstatus_maybe_breakpoint (int wstat);
diff --git a/gdb/remote.c b/gdb/remote.c
index 50b6d45..3a6c6cb 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1449,6 +1449,14 @@  remote_multi_process_p (struct remote_state *rs)
   return packet_support (PACKET_multiprocess_feature) == PACKET_ENABLE;
 }
 
+/* Returns true if fork events are supported.  */
+
+static int
+remote_fork_event_p (struct remote_state *rs)
+{
+  return packet_support (PACKET_fork_event_feature) == PACKET_ENABLE;
+}
+
 /* Tokens for use by the asynchronous signal handlers for SIGINT.  */
 static struct async_signal_handler *async_sigint_remote_twice_token;
 static struct async_signal_handler *async_sigint_remote_token;
@@ -4395,16 +4403,22 @@  remote_open_1 (const char *name, int from_tty,
     wait_forever_enabled_p = 1;
 }
 
-/* This takes a program previously attached to and detaches it.  After
-   this is done, GDB can be used to debug some other program.  We
-   better not have left any breakpoints in the target program or it'll
-   die when it hits one.  */
+/* This detaches a program to which we previously attached, using
+   inferior_ptid to identify the process.  After this is done, GDB
+   can be used to debug some other program.  We better not have left
+   any breakpoints in the target program or it'll die when it hits
+   one.  If IS_FORK_CHILD is true, then inferior_ptid is the child
+   of an unfollowed fork, and we need to avoid deleting breakpoints
+   still needed by the parent.  */
 
 static void
-remote_detach_1 (const char *args, int from_tty, int extended)
+remote_detach_1 (struct target_ops *ops, const char *args,
+		 int from_tty, int is_fork_child)
 {
   int pid = ptid_get_pid (inferior_ptid);
   struct remote_state *rs = get_remote_state ();
+  struct thread_info *tp = find_thread_ptid (inferior_ptid);
+  int is_fork_parent;
 
   if (args)
     error (_("Argument given to \"detach\" when remotely debugging."));
@@ -4438,22 +4452,75 @@  remote_detach_1 (const char *args, int from_tty, int extended)
   else
     error (_("Can't detach process."));
 
-  if (from_tty && !extended)
+  if (from_tty && !rs->extended)
     puts_filtered (_("Ending remote debugging.\n"));
 
-  target_mourn_inferior ();
+  /* Check to see if we are detaching a fork parent.  Note that if we
+     are detaching a fork child, tp == NULL.  */
+  if (tp != NULL)
+    is_fork_parent = tp->pending_follow.kind == TARGET_WAITKIND_FORKED;
+
+  /* If doing detach-on-fork, we don't mourn, because that will delete
+     breakpoints that should be available for the followed inferior.  */
+  if (!is_fork_child && !is_fork_parent)
+    target_mourn_inferior ();
+  else
+    {
+      inferior_ptid = null_ptid;
+      detach_inferior (pid);
+    }
 }
 
 static void
 remote_detach (struct target_ops *ops, const char *args, int from_tty)
 {
-  remote_detach_1 (args, from_tty, 0);
+  remote_detach_1 (ops, args, from_tty, 0);
 }
 
 static void
 extended_remote_detach (struct target_ops *ops, const char *args, int from_tty)
 {
-  remote_detach_1 (args, from_tty, 1);
+  remote_detach_1 (ops, args, from_tty, 0);
+}
+
+/* Target follow-fork function for remote targets.  On entry, and
+   at return, the current inferior is the fork parent.
+
+   Note that although this is currently only used for extended-remote,
+   it is named remote_follow_fork in anticipation of using it for the
+   remote target as well.  */
+
+static int
+remote_follow_fork (struct target_ops *ops, int follow_child,
+		    int detach_fork)
+{
+  struct remote_state *rs = get_remote_state ();
+
+  if (remote_fork_event_p (rs))
+    {
+      if (detach_fork && !follow_child)
+	{
+	  ptid_t parent_ptid;
+	  ptid_t child_ptid;
+
+	  gdb_assert (inferior_thread ()->pending_follow.kind
+		      == TARGET_WAITKIND_FORKED);
+
+	  /* remote_detach_1 detaches inferior_ptid, which is currently
+	     the ptid of the parent.  Switch inferior_ptid to the ptid
+	     of the child for the duration of the call.  */
+	  parent_ptid = inferior_ptid;
+	  child_ptid = inferior_thread ()->pending_follow.value.related_pid;
+	  inferior_ptid = child_ptid;
+	  gdb_assert (!ptid_equal (parent_ptid, child_ptid));
+
+	  remote_detach_1 (ops, NULL, 0, 1);
+
+	  /* Restore the parent ptid.  */
+	  inferior_ptid = parent_ptid;
+	}
+    }
+  return 0;
 }
 
 /* Same as remote_detach, but don't send the "D" packet; just disconnect.  */
@@ -5595,6 +5662,11 @@  Packet: '%s'\n"),
 	      p = unpack_varlen_hex (++p1, &c);
 	      event->core = c;
 	    }
+	  else if (strncmp (p, "xfork", p1 - p) == 0)
+	    {
+	      event->ws.value.related_pid = read_ptid (++p1, &p);
+	      event->ws.kind = TARGET_WAITKIND_FORKED;
+	    }
 	  else
 	    {
 	      ULONGEST pnum;
@@ -9413,8 +9485,11 @@  remote_pid_to_str (struct target_ops *ops, ptid_t ptid)
       if (ptid_equal (magic_null_ptid, ptid))
 	xsnprintf (buf, sizeof buf, "Thread <main>");
       else if (rs->extended && remote_multi_process_p (rs))
-	xsnprintf (buf, sizeof buf, "Thread %d.%ld",
-		   ptid_get_pid (ptid), ptid_get_lwp (ptid));
+	if (ptid_get_lwp (ptid) == 0)
+	  return normal_pid_to_str (ptid);
+	else
+	  xsnprintf (buf, sizeof buf, "Thread %d.%ld",
+		     ptid_get_pid (ptid), ptid_get_lwp (ptid));
       else
 	xsnprintf (buf, sizeof buf, "Thread %ld",
 		   ptid_get_lwp (ptid));
@@ -11758,6 +11833,7 @@  Specify the serial device it is connected to (e.g. /dev/ttya).";
   extended_remote_ops.to_kill = extended_remote_kill;
   extended_remote_ops.to_supports_disable_randomization
     = extended_remote_supports_disable_randomization;
+  extended_remote_ops.to_follow_fork = remote_follow_fork;
 }
 
 static int
diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp
index e95cb4b..2a71e63 100644
--- a/gdb/testsuite/gdb.base/multi-forks.exp
+++ b/gdb/testsuite/gdb.base/multi-forks.exp
@@ -62,6 +62,23 @@  proc continue_to_exit_bp_loc {} {
     set seen_break 0
     set seen_prompt 0
     set seen_timeout 0
+
+    # If we are running with a native gdbserver, the output ($decimal done)
+    # will come via the spawn_id of gdbserver, not the spawn_id of gdb (the
+    # default).  So we grab the spawn_id of gdbserver, if it exists, and
+    # add it to the gdb_expect statement below using "-i", allowing us to
+    # apply the expect statement to the output of both spawn_ids.
+    #
+    # If we are running with a truly remote gdbserver (gdb,noinferiorio),
+    # the output of the program will be inaccessible.  In this case we do
+    # not check for the ($decimal done) output, but just look for the gdb
+    # prompt.
+    global server_spawn_id
+    set current_spawn_id [board_info host fileid]
+    if {![info exists server_spawn_id]} {
+	set server_spawn_id ""
+    }
+
     while { ($seen_done < 16 || ! $seen_prompt) && ! $seen_timeout } {
 	# We don't know what order the interesting things will arrive in.
 	# Using a pattern of the form 'x|y|z' instead of -re x ... -re y
@@ -70,7 +87,8 @@  proc continue_to_exit_bp_loc {} {
 	# first in the script that occurs anywhere in the input, so that
 	# we don't skip anything.
 	gdb_expect {
-	    -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
+	    -i "$current_spawn_id $server_spawn_id" \
+	         -re "($decimal done)|(Breakpoint)|($gdb_prompt)" {
 		if {[info exists expect_out(1,string)]} {
 		    incr seen_done
 		} elseif {[info exists expect_out(2,string)]} {
@@ -90,7 +108,7 @@  proc continue_to_exit_bp_loc {} {
 	fail "run to exit 2 (no prompt)"
     } elseif { ! $seen_break } {
 	fail "run to exit 2 (no breakpoint hit)"
-    } elseif { $seen_done != 16 } {
+    } elseif {![target_info exists gdb,noinferiorio] && $seen_done != 16 } {
 	fail "run to exit 2 (missing done messages)"
     } else {
 	pass "run to exit 2"
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 08db806..cc29ad2 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -29,6 +29,9 @@  load_lib libgloss.exp
 load_lib cache.exp
 load_lib gdb-utils.exp
 
+# Stores the expect spawn ID of gdbserver.
+global server_spawn_id
+
 global GDB
 
 if [info exists TOOL_EXECUTABLE] {
diff --git a/gdb/testsuite/lib/gdbserver-support.exp b/gdb/testsuite/lib/gdbserver-support.exp
index 4a59154..86c6e35 100644
--- a/gdb/testsuite/lib/gdbserver-support.exp
+++ b/gdb/testsuite/lib/gdbserver-support.exp
@@ -270,6 +270,8 @@  proc gdbserver_start { options arguments } {
 	    append gdbserver_command " $arguments"
 	}
 
+	# Save the gdbserver spawn ID.
+	global server_spawn_id
 	set server_spawn_id [remote_spawn target $gdbserver_command]
 
 	# Wait for the server to open its TCP socket, so that GDB can connect.