[1/3] Target remote mode fork and exec events

Message ID 1446854188-496-2-git-send-email-donb@codesourcery.com
State New, archived
Headers

Commit Message

Don Breazeal Nov. 6, 2015, 11:56 p.m. UTC
  This patch implements support for fork and exec events with target remote
mode Linux targets.  For such targets with Linux kernels 2.5.46 and later,
this enables follow-fork-mode, detach-on-fork and fork and exec
catchpoints.  Note that follow-exec-mode is not supported, because target
remote mode does not support the 'run' command.

The changes required to implement this included:

 * Don't exit from gdbserver if there are still active inferiors.

 * Allow changing the active process in remote mode.

 * Enable fork and exec events in remote mode.

 * Print "Ending remote debugging" when detaching only if in remote
   mode and there is only one inferior left.

   (As I write this up I'm concluding that this is incorrect.  We
   don't care how many active inferiors there are, yes?  Perhaps we
   need a test that detaches when there are multiple inferiors. I've
   added that to my list.)

 * Combine remote_kill and extended_remote_kill into a single function
   that can handle the multiple inferior case for target remote.  Also,
   the same thing for remote_mourn and extended_remote_mourn.

 * Enable process-style ptids in target remote.

 * Remove restriction on multiprocess mode in target remote.

Thanks
--Don

gdb/gdbserver/
2015-11-06  Don Breazeal  <donb@sourceware.org>

	* server.c (process_serial_event): Don't exit from gdbserver
	in remote mode if there are still active inferiors.

gdb/
2015-11-06  Don Breazeal  <donb@sourceware.org>

	* remote.c (set_general_process): Remove restriction on target
	remote mode.
	(remote_query_supported): Likewise.
	(remote_detach_1): Change restriction to target remote mode to
	restriction to last inferior left.
	(remote_disconnect): Unpush the target directly instead of 
	calling remote_mourn.
	(remote_kill, extended_remote_kill): Combine functions into one,
	remote_kill, and enable extended functionality for target remote.
	(remote_mourn, extended_remote_mourn): Combine functions into
	one, remote_mourn, and enable extended functionality for target
	remote.
	(remote_pid_to_str): Enable "process" style ptid string for
	target remote.
	(remote_supports_multi_process): Remove restriction on target
	remote mode.

---
 gdb/gdbserver/server.c |   6 +-
 gdb/remote.c           | 166 ++++++++++++++++++++++++-------------------------
 2 files changed, 84 insertions(+), 88 deletions(-)
  

Comments

Pedro Alves Nov. 20, 2015, 1:04 p.m. UTC | #1
Hi Don,

Thanks for doing this.  Starting to look at the series.

On 11/06/2015 11:56 PM, Don Breazeal wrote:
> This patch implements support for fork and exec events with target remote
> mode Linux targets.  For such targets with Linux kernels 2.5.46 and later,
> this enables follow-fork-mode, detach-on-fork and fork and exec
> catchpoints.  

> Note that follow-exec-mode is not supported, because target
> remote mode does not support the 'run' command.

Not sure I don't understand this part/comment.

> 
> The changes required to implement this included:
> 
>  * Don't exit from gdbserver if there are still active inferiors.
> 
>  * Allow changing the active process in remote mode.
> 
>  * Enable fork and exec events in remote mode.
> 
>  * Print "Ending remote debugging" when detaching only if in remote
>    mode and there is only one inferior left.
> 
>    (As I write this up I'm concluding that this is incorrect.  We
>    don't care how many active inferiors there are, yes?

Not sure I understand the question.  But I'd say what matters is
whether we're disconnecting/closing the connection.

> Perhaps we
>    need a test that detaches when there are multiple inferiors. I've
>    added that to my list.)
> 
>  * Combine remote_kill and extended_remote_kill into a single function
>    that can handle the multiple inferior case for target remote.  Also,
>    the same thing for remote_mourn and extended_remote_mourn.
> 
>  * Enable process-style ptids in target remote.
> 
>  * Remove restriction on multiprocess mode in target remote.
> 
> Thanks
> --Don
> 
> gdb/gdbserver/
> 2015-11-06  Don Breazeal  <donb@sourceware.org>
> 
> 	* server.c (process_serial_event): Don't exit from gdbserver
> 	in remote mode if there are still active inferiors.
> 
> gdb/
> 2015-11-06  Don Breazeal  <donb@sourceware.org>
> 
> 	* remote.c (set_general_process): Remove restriction on target
> 	remote mode.
> 	(remote_query_supported): Likewise.
> 	(remote_detach_1): Change restriction to target remote mode to
> 	restriction to last inferior left.
> 	(remote_disconnect): Unpush the target directly instead of 
> 	calling remote_mourn.
> 	(remote_kill, extended_remote_kill): Combine functions into one,
> 	remote_kill, and enable extended functionality for target remote.
> 	(remote_mourn, extended_remote_mourn): Combine functions into
> 	one, remote_mourn, and enable extended functionality for target
> 	remote.
> 	(remote_pid_to_str): Enable "process" style ptid string for
> 	target remote.
> 	(remote_supports_multi_process): Remove restriction on target
> 	remote mode.
> 
> ---
>  gdb/gdbserver/server.c |   6 +-
>  gdb/remote.c           | 166 ++++++++++++++++++++++++-------------------------
>  2 files changed, 84 insertions(+), 88 deletions(-)
> 
> diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
> index fd2804b..0f57237 100644
> --- a/gdb/gdbserver/server.c
> +++ b/gdb/gdbserver/server.c
> @@ -3865,9 +3865,11 @@ process_serial_event (void)
>  	  discard_queued_stop_replies (pid_to_ptid (pid));
>  	  write_ok (own_buf);
>  
> -	  if (extended_protocol)
> +	  if (extended_protocol || get_first_inferior (&all_threads) != NULL)
>  	    {
> -	      /* Treat this like a normal program exit.  */
> +	      /* There is still at least one inferior remaining, so
> +		 don't terminate gdbserver and treat this like a
> +		 normal program exit.  */
>  	      last_status.kind = TARGET_WAITKIND_EXITED;
>  	      last_status.value.integer = 0;
>  	      last_ptid = pid_to_ptid (pid);
> diff --git a/gdb/remote.c b/gdb/remote.c
> index fed397a..60da26c 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -123,8 +123,6 @@ static void remote_mourn (struct target_ops *ops);
>  
>  static void extended_remote_restart (void);
>  
> -static void extended_remote_mourn (struct target_ops *);
> -
>  static void remote_send (char **buf, long *sizeof_buf_p);
>  
>  static int readchar (int timeout);
> @@ -2084,7 +2082,7 @@ set_general_process (void)
>    struct remote_state *rs = get_remote_state ();
>  
>    /* If the remote can't handle multiple processes, don't bother.  */
> -  if (!rs->extended || !remote_multi_process_p (rs))
> +  if (!remote_multi_process_p (rs))
>      return;
>  
>    /* We only need to change the remote current thread if it's pointing
> @@ -4472,18 +4470,15 @@ remote_query_supported (void)
>  
>        q = remote_query_supported_append (q, "qRelocInsn+");
>  
> -      if (rs->extended)
> -	{
> -	  if (packet_set_cmd_state (PACKET_fork_event_feature)
> -	      != AUTO_BOOLEAN_FALSE)
> -	    q = remote_query_supported_append (q, "fork-events+");
> -	  if (packet_set_cmd_state (PACKET_vfork_event_feature)
> -	      != AUTO_BOOLEAN_FALSE)
> -	    q = remote_query_supported_append (q, "vfork-events+");
> -	  if (packet_set_cmd_state (PACKET_exec_event_feature)
> -	      != AUTO_BOOLEAN_FALSE)
> -	    q = remote_query_supported_append (q, "exec-events+");
> -	}
> +      if (packet_set_cmd_state (PACKET_fork_event_feature)
> +	  != AUTO_BOOLEAN_FALSE)
> +	q = remote_query_supported_append (q, "fork-events+");
> +      if (packet_set_cmd_state (PACKET_vfork_event_feature)
> +	  != AUTO_BOOLEAN_FALSE)
> +	q = remote_query_supported_append (q, "vfork-events+");
> +      if (packet_set_cmd_state (PACKET_exec_event_feature)
> +	  != AUTO_BOOLEAN_FALSE)
> +	q = remote_query_supported_append (q, "exec-events+");
>  
>        if (packet_set_cmd_state (PACKET_vContSupported) != AUTO_BOOLEAN_FALSE)
>  	q = remote_query_supported_append (q, "vContSupported+");
> @@ -4827,7 +4822,8 @@ remote_detach_1 (const char *args, int from_tty)
>    /* Tell the remote target to detach.  */
>    remote_detach_pid (pid);
>  
> -  if (from_tty && !rs->extended)
> +  /* Exit only if this is the only active inferior.  */
> +  if (from_tty && !rs->extended && number_of_inferiors () == 1)
>      puts_filtered (_("Ending remote debugging.\n"));
>  
>    /* Check to see if we are detaching a fork parent.  Note that if we
> @@ -4923,10 +4919,11 @@ remote_disconnect (struct target_ops *target, const char *args, int from_tty)
>    if (args)
>      error (_("Argument given to \"disconnect\" when remotely debugging."));
>  
> -  /* Make sure we unpush even the extended remote targets; mourn
> -     won't do it.  So call remote_mourn directly instead of
> -     target_mourn_inferior.  */
> -  remote_mourn (target);
> +  /* Make sure we unpush even the extended remote targets.  Calling
> +     target_mourn_inferior won't unpush, and remote_mourn won't
> +     unpush if there is more than one inferior left.  */
> +  unpush_target (target);
> +  generic_mourn_inferior ();

Note: this generic_mourn_inferior call here looks wrong, because we may be
debugging more than one inferior.  But remote_mourn was already wrong
for the same reason, so, not a problem added by this patch.

>  
>    if (from_tty)
>      puts_filtered ("Ending remote debugging.\n");
> @@ -8562,42 +8559,6 @@ kill_new_fork_children (int pid, struct remote_state *rs)
>  }
>  
>  
> -static void
> -remote_kill (struct target_ops *ops)
> -{

Please rename this (making it a helper function) instead of inlining
it in the new remote_kill.  E.g., remote_kill_k.

> -
> -  /* Catch errors so the user can quit from gdb even when we
> -     aren't on speaking terms with the remote system.  */
> -  TRY
> -    {
> -      putpkt ("k");
> -    }
> -  CATCH (ex, RETURN_MASK_ERROR)
> -    {
> -      if (ex.error == TARGET_CLOSE_ERROR)
> -	{
> -	  /* If we got an (EOF) error that caused the target
> -	     to go away, then we're done, that's what we wanted.
> -	     "k" is susceptible to cause a premature EOF, given
> -	     that the remote server isn't actually required to
> -	     reply to "k", and it can happen that it doesn't
> -	     even get to reply ACK to the "k".  */
> -	  return;
> -	}
> -
> -	/* Otherwise, something went wrong.  We didn't actually kill
> -	   the target.  Just propagate the exception, and let the
> -	   user or higher layers decide what to do.  */
> -	throw_exception (ex);
> -    }
> -  END_CATCH
> -
> -  /* We've killed the remote end, we get to mourn it.  Since this is
> -     target remote, single-process, mourning the inferior also
> -     unpushes remote_ops.  */
> -  target_mourn_inferior ();

Maybe do still move this mourn out into new remote_kill.

> -}
> -
>  static int
>  remote_vkill (int pid, struct remote_state *rs)
>  {
> @@ -8624,19 +8585,58 @@ remote_vkill (int pid, struct remote_state *rs)
>  }
>  
>  static void
> -extended_remote_kill (struct target_ops *ops)
> +remote_kill (struct target_ops *ops)
>  {
>    int res;
>    int pid = ptid_get_pid (inferior_ptid);
>    struct remote_state *rs = get_remote_state ();
>  
> +  /* If we are in 'target remote' mode and we are killing the only
> +     inferior, then we will tell gdbserver to exit and unpush the
> +     target.  */
> +  if (!rs->extended && number_of_inferiors () <= 1)

It's number of inferiors, or number of _live_ inferiors that matters?
I'd think the latter.  Likewise all other new number_of_inferiors
calls should be audited.

> +    {
> +      /* Catch errors so the user can quit from gdb even when we
> +	 aren't on speaking terms with the remote system.  */
> +      TRY
> +	{
> +	  putpkt ("k");
> +	}
> +      CATCH (ex, RETURN_MASK_ERROR)
> +	{
> +	  if (ex.error == TARGET_CLOSE_ERROR)
> +	    {
> +	      /* If we got an (EOF) error that caused the target
> +		 to go away, then we're done, that's what we wanted.
> +		 "k" is susceptible to cause a premature EOF, given
> +		 that the remote server isn't actually required to
> +		 reply to "k", and it can happen that it doesn't
> +		 even get to reply ACK to the "k".  */
> +	      return;
> +	    }
> +
> +	  /* Otherwise, something went wrong.  We didn't actually kill
> +	     the target.  Just propagate the exception, and let the
> +	     user or higher layers decide what to do.  */
> +	  throw_exception (ex);
> +	}
> +      END_CATCH
> +
> +      /* We've killed the remote end, we get to mourn it.  Since this is
> +	 target remote, single-process, mourning the inferior also
> +	 unpushes remote_ops.  */

Mentioning "single-process," here no longer looks right to me.


> +      target_mourn_inferior ();
> +
> +      return;
> +    }
> +
>    /* If we're stopped while forking and we haven't followed yet, kill the
>       child task.  We need to do this before killing the parent task
>       because if this is a vfork then the parent will be sleeping.  */
>    kill_new_fork_children (pid, rs);
>  
>    res = remote_vkill (pid, rs);
> -  if (res == -1 && !(rs->extended && remote_multi_process_p (rs)))
> +  if (res == -1 && !(remote_multi_process_p (rs)))

Drop the now-unnecessary parens.

>      {
>        /* Don't try 'k' on a multi-process aware stub -- it has no way
>  	 to specify the pid.  */


I wonder about re-writing this function in top-down approach?

 #0 - if vkill is supported:

     - kill the new fork children first.

     - then kill the current process with remote_vkill.

 #1 - then if not multi-process, and there are still
      live processes, call the new remote_kill_k helper, to kill
      with "k".

 #2 - then, if !extended, and there are no live processes
      left, disconnect.


Note this also gets rid of the putpkt("k") call, which currently misses
handling the EOF issue already handled by the (new) remote_kill_k.

> @@ -8662,16 +8662,17 @@ extended_remote_kill (struct target_ops *ops)
>  static void
>  remote_mourn (struct target_ops *target)
>  {
> -  unpush_target (target);
> +  struct remote_state *rs = get_remote_state ();
>  
> -  /* remote_close takes care of doing most of the clean up.  */
> -  generic_mourn_inferior ();
> -}
> +  /* In 'target remote' mode with one inferior, we shut down gdbserver.  */

I'd rather say:

     /* In 'target remote' mode with one inferior, we close the connection.  */


> +  if (!rs->extended && number_of_inferiors () <= 1)
> +    {
> +      unpush_target (target);
>  
> -static void
> -extended_remote_mourn (struct target_ops *target)
> -{
> -  struct remote_state *rs = get_remote_state ();
> +      /* remote_close takes care of doing most of the clean up.  */
> +      generic_mourn_inferior ();
> +      return;
> +    }
>  
>    /* In case we got here due to an error, but we're going to stay
>       connected.  */
> @@ -8702,8 +8703,9 @@ extended_remote_mourn (struct target_ops *target)
>       current thread.  */
>    record_currthread (rs, minus_one_ptid);
>  
> -  /* Unlike "target remote", we do not want to unpush the target; then
> -     the next time the user says "run", we won't be connected.  */
> +  /* Unlike 'target remote' with no more inferiors, we do not want to
> +     unpush the target.  If we do then the next time the user says
> +     "run", we won't be connected.  */
>  
>    /* Call common code to mark the inferior as not running.	*/
>    generic_mourn_inferior ();
> @@ -10224,7 +10226,7 @@ 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))
> +      else if (remote_multi_process_p (rs))
>  	if (ptid_get_lwp (ptid) == 0)
>  	  return normal_pid_to_str (ptid);
>  	else
> @@ -11398,7 +11400,7 @@ remote_supports_multi_process (struct target_ops *self)
>       processes, even though plain remote can use the multi-process
>       thread id extensions, so that GDB knows the target process's
>       PID.  */

Comment above needs updating.

> -  return rs->extended && remote_multi_process_p (rs);
> +  return remote_multi_process_p (rs);
>  }
>  

Thanks,
Pedro Alves
  
Don Breazeal Nov. 20, 2015, 4:49 p.m. UTC | #2
On 11/20/2015 5:04 AM, Pedro Alves wrote:
> Hi Don,
> 
> Thanks for doing this.  Starting to look at the series.

Thanks!

> 
> On 11/06/2015 11:56 PM, Don Breazeal wrote:
>> This patch implements support for fork and exec events with target remote
>> mode Linux targets.  For such targets with Linux kernels 2.5.46 and later,
>> this enables follow-fork-mode, detach-on-fork and fork and exec
>> catchpoints.  
> 
>> Note that follow-exec-mode is not supported, because target
>> remote mode does not support the 'run' command.
> 
> Not sure I don't understand this part/comment.

I assume that we agree that the 'run' command is not supported in target
remote mode.

Maybe a better way to have stated this would be that follow-exec-mode is
of limited used in target remote mode, since the 'run' command is not
supported.  The point being that the main reason a user would want to
set follow-exec-mode would be to get control over which inferior is run
when a 'run' command is executed.

I have to admit that it is incorrect to say that it is unsupported,
since setting follow-exec-mode *will* control whether a new inferior or
the same inferior is used for the execed program.  But there isn't much
you can do differently without the 'run' command.  Using
follow-exec-mode 'new' there *is* a record of what programs have been
run, which could be useful.

Is that a more accurate description, or am I misunderstanding something?

I'll get to work on the rest of this, hopefully sometime next week.
thanks
--Don
  
Don Breazeal Dec. 7, 2015, 10:14 p.m. UTC | #3
This is an update of the patchset implementing target remote mode
for and exec events.  The primary changes from the previous version
are:
* reorder the patches so that the test patch is first
* implement changes requested by Pedro's review of the implementation
  patch, primarily rewriting remote_kill.
* minor updates to the documentation patch to eliminate incorrect
  characterization of follow-exec-mode as "unsupported" in remote mode.

Details of the changes are outlined in each patch.

Thanks!
--Don

This patchset implements support fork and exec events for target
remote mode Linux targets.  This is done primarily by enabling
functionality that had previously been restricted to extended-remote
mode, and by preventing gdbserver from exiting until there are no
more active inferiors left.

There were a number of tests that had to be modified to enable them in
target-remote mode.

The GDB manual sections on connecting to a remote target were modified
and reorganized to clarify the distinction between remote,
extended-remote, and multiprocess, and to collect related information
into subsections.

The patches include:

PATCH 1/3: Enables fork and exec tests for target remote mode.
PATCH 2/3: Implements target remote mode fork and exec event support.
PATCH 3/3: Updates documentation for fork and exec events with target
           remote.

Tested on x86_64 Linux with native, remote, and extended-remote.  Also
tried it with Pedro's patch that modified gdbserver exit criteria,
https://sourceware.org/ml/gdb-patches/2015-10/msg00222.html, and saw no
regressions.

Thanks, Don
  
Pedro Alves Dec. 8, 2015, 1:11 p.m. UTC | #4
On 12/07/2015 10:14 PM, Don Breazeal wrote:
> This is an update of the patchset implementing target remote mode
> for and exec events.  The primary changes from the previous version
> are:
> * reorder the patches so that the test patch is first
> * implement changes requested by Pedro's review of the implementation
>   patch, primarily rewriting remote_kill.
> * minor updates to the documentation patch to eliminate incorrect
>   characterization of follow-exec-mode as "unsupported" in remote mode.
> 
> Details of the changes are outlined in each patch.
> 

Thanks!

> (...) Also
> tried it with Pedro's patch that modified gdbserver exit criteria,
> https://sourceware.org/ml/gdb-patches/2015-10/msg00222.html, and saw no
> regressions.

(Note this is in master now.)

Thanks,
Pedro Alves
  

Patch

diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index fd2804b..0f57237 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -3865,9 +3865,11 @@  process_serial_event (void)
 	  discard_queued_stop_replies (pid_to_ptid (pid));
 	  write_ok (own_buf);
 
-	  if (extended_protocol)
+	  if (extended_protocol || get_first_inferior (&all_threads) != NULL)
 	    {
-	      /* Treat this like a normal program exit.  */
+	      /* There is still at least one inferior remaining, so
+		 don't terminate gdbserver and treat this like a
+		 normal program exit.  */
 	      last_status.kind = TARGET_WAITKIND_EXITED;
 	      last_status.value.integer = 0;
 	      last_ptid = pid_to_ptid (pid);
diff --git a/gdb/remote.c b/gdb/remote.c
index fed397a..60da26c 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -123,8 +123,6 @@  static void remote_mourn (struct target_ops *ops);
 
 static void extended_remote_restart (void);
 
-static void extended_remote_mourn (struct target_ops *);
-
 static void remote_send (char **buf, long *sizeof_buf_p);
 
 static int readchar (int timeout);
@@ -2084,7 +2082,7 @@  set_general_process (void)
   struct remote_state *rs = get_remote_state ();
 
   /* If the remote can't handle multiple processes, don't bother.  */
-  if (!rs->extended || !remote_multi_process_p (rs))
+  if (!remote_multi_process_p (rs))
     return;
 
   /* We only need to change the remote current thread if it's pointing
@@ -4472,18 +4470,15 @@  remote_query_supported (void)
 
       q = remote_query_supported_append (q, "qRelocInsn+");
 
-      if (rs->extended)
-	{
-	  if (packet_set_cmd_state (PACKET_fork_event_feature)
-	      != AUTO_BOOLEAN_FALSE)
-	    q = remote_query_supported_append (q, "fork-events+");
-	  if (packet_set_cmd_state (PACKET_vfork_event_feature)
-	      != AUTO_BOOLEAN_FALSE)
-	    q = remote_query_supported_append (q, "vfork-events+");
-	  if (packet_set_cmd_state (PACKET_exec_event_feature)
-	      != AUTO_BOOLEAN_FALSE)
-	    q = remote_query_supported_append (q, "exec-events+");
-	}
+      if (packet_set_cmd_state (PACKET_fork_event_feature)
+	  != AUTO_BOOLEAN_FALSE)
+	q = remote_query_supported_append (q, "fork-events+");
+      if (packet_set_cmd_state (PACKET_vfork_event_feature)
+	  != AUTO_BOOLEAN_FALSE)
+	q = remote_query_supported_append (q, "vfork-events+");
+      if (packet_set_cmd_state (PACKET_exec_event_feature)
+	  != AUTO_BOOLEAN_FALSE)
+	q = remote_query_supported_append (q, "exec-events+");
 
       if (packet_set_cmd_state (PACKET_vContSupported) != AUTO_BOOLEAN_FALSE)
 	q = remote_query_supported_append (q, "vContSupported+");
@@ -4827,7 +4822,8 @@  remote_detach_1 (const char *args, int from_tty)
   /* Tell the remote target to detach.  */
   remote_detach_pid (pid);
 
-  if (from_tty && !rs->extended)
+  /* Exit only if this is the only active inferior.  */
+  if (from_tty && !rs->extended && number_of_inferiors () == 1)
     puts_filtered (_("Ending remote debugging.\n"));
 
   /* Check to see if we are detaching a fork parent.  Note that if we
@@ -4923,10 +4919,11 @@  remote_disconnect (struct target_ops *target, const char *args, int from_tty)
   if (args)
     error (_("Argument given to \"disconnect\" when remotely debugging."));
 
-  /* Make sure we unpush even the extended remote targets; mourn
-     won't do it.  So call remote_mourn directly instead of
-     target_mourn_inferior.  */
-  remote_mourn (target);
+  /* Make sure we unpush even the extended remote targets.  Calling
+     target_mourn_inferior won't unpush, and remote_mourn won't
+     unpush if there is more than one inferior left.  */
+  unpush_target (target);
+  generic_mourn_inferior ();
 
   if (from_tty)
     puts_filtered ("Ending remote debugging.\n");
@@ -8562,42 +8559,6 @@  kill_new_fork_children (int pid, struct remote_state *rs)
 }
 
 
-static void
-remote_kill (struct target_ops *ops)
-{
-
-  /* Catch errors so the user can quit from gdb even when we
-     aren't on speaking terms with the remote system.  */
-  TRY
-    {
-      putpkt ("k");
-    }
-  CATCH (ex, RETURN_MASK_ERROR)
-    {
-      if (ex.error == TARGET_CLOSE_ERROR)
-	{
-	  /* If we got an (EOF) error that caused the target
-	     to go away, then we're done, that's what we wanted.
-	     "k" is susceptible to cause a premature EOF, given
-	     that the remote server isn't actually required to
-	     reply to "k", and it can happen that it doesn't
-	     even get to reply ACK to the "k".  */
-	  return;
-	}
-
-	/* Otherwise, something went wrong.  We didn't actually kill
-	   the target.  Just propagate the exception, and let the
-	   user or higher layers decide what to do.  */
-	throw_exception (ex);
-    }
-  END_CATCH
-
-  /* We've killed the remote end, we get to mourn it.  Since this is
-     target remote, single-process, mourning the inferior also
-     unpushes remote_ops.  */
-  target_mourn_inferior ();
-}
-
 static int
 remote_vkill (int pid, struct remote_state *rs)
 {
@@ -8624,19 +8585,58 @@  remote_vkill (int pid, struct remote_state *rs)
 }
 
 static void
-extended_remote_kill (struct target_ops *ops)
+remote_kill (struct target_ops *ops)
 {
   int res;
   int pid = ptid_get_pid (inferior_ptid);
   struct remote_state *rs = get_remote_state ();
 
+  /* If we are in 'target remote' mode and we are killing the only
+     inferior, then we will tell gdbserver to exit and unpush the
+     target.  */
+  if (!rs->extended && number_of_inferiors () <= 1)
+    {
+      /* Catch errors so the user can quit from gdb even when we
+	 aren't on speaking terms with the remote system.  */
+      TRY
+	{
+	  putpkt ("k");
+	}
+      CATCH (ex, RETURN_MASK_ERROR)
+	{
+	  if (ex.error == TARGET_CLOSE_ERROR)
+	    {
+	      /* If we got an (EOF) error that caused the target
+		 to go away, then we're done, that's what we wanted.
+		 "k" is susceptible to cause a premature EOF, given
+		 that the remote server isn't actually required to
+		 reply to "k", and it can happen that it doesn't
+		 even get to reply ACK to the "k".  */
+	      return;
+	    }
+
+	  /* Otherwise, something went wrong.  We didn't actually kill
+	     the target.  Just propagate the exception, and let the
+	     user or higher layers decide what to do.  */
+	  throw_exception (ex);
+	}
+      END_CATCH
+
+      /* We've killed the remote end, we get to mourn it.  Since this is
+	 target remote, single-process, mourning the inferior also
+	 unpushes remote_ops.  */
+      target_mourn_inferior ();
+
+      return;
+    }
+
   /* If we're stopped while forking and we haven't followed yet, kill the
      child task.  We need to do this before killing the parent task
      because if this is a vfork then the parent will be sleeping.  */
   kill_new_fork_children (pid, rs);
 
   res = remote_vkill (pid, rs);
-  if (res == -1 && !(rs->extended && remote_multi_process_p (rs)))
+  if (res == -1 && !(remote_multi_process_p (rs)))
     {
       /* Don't try 'k' on a multi-process aware stub -- it has no way
 	 to specify the pid.  */
@@ -8662,16 +8662,17 @@  extended_remote_kill (struct target_ops *ops)
 static void
 remote_mourn (struct target_ops *target)
 {
-  unpush_target (target);
+  struct remote_state *rs = get_remote_state ();
 
-  /* remote_close takes care of doing most of the clean up.  */
-  generic_mourn_inferior ();
-}
+  /* In 'target remote' mode with one inferior, we shut down gdbserver.  */
+  if (!rs->extended && number_of_inferiors () <= 1)
+    {
+      unpush_target (target);
 
-static void
-extended_remote_mourn (struct target_ops *target)
-{
-  struct remote_state *rs = get_remote_state ();
+      /* remote_close takes care of doing most of the clean up.  */
+      generic_mourn_inferior ();
+      return;
+    }
 
   /* In case we got here due to an error, but we're going to stay
      connected.  */
@@ -8702,8 +8703,9 @@  extended_remote_mourn (struct target_ops *target)
      current thread.  */
   record_currthread (rs, minus_one_ptid);
 
-  /* Unlike "target remote", we do not want to unpush the target; then
-     the next time the user says "run", we won't be connected.  */
+  /* Unlike 'target remote' with no more inferiors, we do not want to
+     unpush the target.  If we do then the next time the user says
+     "run", we won't be connected.  */
 
   /* Call common code to mark the inferior as not running.	*/
   generic_mourn_inferior ();
@@ -10224,7 +10226,7 @@  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))
+      else if (remote_multi_process_p (rs))
 	if (ptid_get_lwp (ptid) == 0)
 	  return normal_pid_to_str (ptid);
 	else
@@ -11398,7 +11400,7 @@  remote_supports_multi_process (struct target_ops *self)
      processes, even though plain remote can use the multi-process
      thread id extensions, so that GDB knows the target process's
      PID.  */
-  return rs->extended && remote_multi_process_p (rs);
+  return remote_multi_process_p (rs);
 }
 
 static int
@@ -12828,6 +12830,14 @@  Specify the serial device it is connected to\n\
   remote_ops.to_btrace_conf = remote_btrace_conf;
   remote_ops.to_augmented_libraries_svr4_read =
     remote_augmented_libraries_svr4_read;
+  remote_ops.to_follow_fork = remote_follow_fork;
+  remote_ops.to_follow_exec = remote_follow_exec;
+  remote_ops.to_insert_fork_catchpoint = remote_insert_fork_catchpoint;
+  remote_ops.to_remove_fork_catchpoint = remote_remove_fork_catchpoint;
+  remote_ops.to_insert_vfork_catchpoint = remote_insert_vfork_catchpoint;
+  remote_ops.to_remove_vfork_catchpoint = remote_remove_vfork_catchpoint;
+  remote_ops.to_insert_exec_catchpoint = remote_insert_exec_catchpoint;
+  remote_ops.to_remove_exec_catchpoint = remote_remove_exec_catchpoint;
 }
 
 /* Set up the extended remote vector by making a copy of the standard
@@ -12846,27 +12856,11 @@  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_mourn_inferior = extended_remote_mourn;
   extended_remote_ops.to_detach = extended_remote_detach;
   extended_remote_ops.to_attach = extended_remote_attach;
   extended_remote_ops.to_post_attach = extended_remote_post_attach;
-  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;
-  extended_remote_ops.to_follow_exec = remote_follow_exec;
-  extended_remote_ops.to_insert_fork_catchpoint
-    = remote_insert_fork_catchpoint;
-  extended_remote_ops.to_remove_fork_catchpoint
-    = remote_remove_fork_catchpoint;
-  extended_remote_ops.to_insert_vfork_catchpoint
-    = remote_insert_vfork_catchpoint;
-  extended_remote_ops.to_remove_vfork_catchpoint
-    = remote_remove_vfork_catchpoint;
-  extended_remote_ops.to_insert_exec_catchpoint
-    = remote_insert_exec_catchpoint;
-  extended_remote_ops.to_remove_exec_catchpoint
-    = remote_remove_exec_catchpoint;
 }
 
 static int