This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

GDBserver disconnected tracing support


I've applied this to implement disconnected tracing support
in GDBserver.  This means, you can disconnect GDB from
GDBserver, leaving the trace running, and connect back
later to check what happened.

When a tracing is ongoing, and the disconnected tracing
setting is on, when GDB asks gdbserver to detach
from a process, gdbserver actually doesn't detach from
the process, but stays attached in a special mode that
still handles tracepoints, and, forwards all other
signals back to the inferior.

Implementation wise, this required a few changes:

 - In this mode, GDBserver needs to be able to wait for an
   incoming connection from GDB, and, handle target events
   simultaneously.  The current code blocks on `accept' waiting
   for a new connection.  Instead of blocking, accepting a new
   connection is handled by registering the accept socket in
   the event loop, as a waitable file descriptor, and handling
   the new connection with an event callback.  This way, everything
   goes through the event loop, new sockets, gdb traffic, and target
   events.  The target also needs to be forced into asynchronous
   even reporting mode.

 - I needed to be able to access the exiting process object after
   the target having reported to gdbserver common code (through the
   `wait' interface, TARGET_WAITKIND_EXITED|SIGNALLED) it had exited.
   This cried for adding a target_mourn_inferior equivalent to GDBserver.
   And so I did that.  This also cleaned up thread-db.c a bit, by
   splitting the thread_db_free function in two
   (thread_db_detach+disable_thread_event_reporting/thread_db_mourn).
   This now looks a  bit more like native GDB's linux-thread-db.c
   similar code, and saner, IMO.

 - I needed to have to able to stop all threads when a new GDB connection
   arrives in all-stop mode.  Hence, the new pause_all method, to expose
   stop-all-threads functionality to common gdbserver code.  This may one
   day evolve into always having the linux low target in non-stop/async mode.

 - I needed a way for a event-loop handler callback to tell the event-loop
   to stop processing immediately.  I've done that by adding the option
   of the callback returning -1 to signal just that.  This also allowed
   me to easily unbreak "(gdb) monitor exit", which wasn't really causing
   a gdbserver exit presently (seems like I broke it when I added the
   event-loop.c based event-loop a while back :-/)

 - I moved the `last_resume_kind' field from lwp_info to
   thread_info (that is, to common code), since common code
   needed to tweak it.  Since not all targets handle the flag
   yet, I've guarded the expectation that the flag contains
   anything useful behind the target having a thread_stopped
   callback installed as well.  This check will ideally be
   removed at some point, and the `last_resume_kind' flag
   would ideally be all handled by common code.

 - There's a possible race between GDB telling gdbserver to
   detach a process, and actually closing the socket.  In the
   race window, we could end up handling a target event, and
   trying to push it down to GDB.  To handle that, I've
   added a new `process_info->gdb_detached' flag.  Don't 
   push events to GDB if that flag is set.  This may be
   also used in the future when tracing is defined to
   work with multi-process, as then we may potentially
   be able to have GDB detach from a process for disconnected
   tracing while still debugging other processes.

No regressions on x86_64-linux.

-- 
Pedro Alves

2010-04-11  Pedro Alves  <pedro@codesourcery.com>

	GDBserver disconnected tracing support.

	gdb/gdbserver/

	* linux-low.c (linux_remove_process): Delete.
	(add_lwp): Don't set last_resume_kind here.
	(linux_kill): Use `mourn'.
	(linux_detach): Use `thread_db_detach', and `mourn'.
	(linux_mourn): New.
	(linux_attach_lwp_1): Adjust comment.
	(linux_attach): last_resume_kind moved the thread_info; adjust.
	(status_pending_p_callback): Adjust.
	(linux_wait_for_event_1): Adjust.
	(count_events_callback, select_singlestep_lwp_callback)
	(select_event_lwp_callback, cancel_breakpoints_callback)
	(db_wants_lwp_stopped, linux_wait_1, need_step_over_p)
	(proceed_one_lwp): Adjust.
	(linux_async): Add debug output.
	(linux_thread_stopped): New.
	(linux_pause_all): New.
	(linux_target_ops): Install linux_mourn, linux_thread_stopped and
	linux_pause_all.
	* linux-low.h (struct lwp_info): Delete last_resume_kind field.
	(thread_db_free): Delete declaration.
	(thread_db_detach, thread_db_mourn): Declare.
	* thread-db.c (thread_db_init): Use thread_db_mourn.
	(thread_db_free): Delete, split in two.
	(disable_thread_event_reporting): New.
	(thread_db_detach): New.
	(thread_db_mourn): New.

	* server.h (struct thread_info) <last_resume_kind>: New field.
	<attached>: Add comment.
	<gdb_detached>: New field.
	(handler_func): Change return type to int.
	(handle_serial_event, handle_target_event): Ditto.
	(gdb_connected): Declare.
	(tracing): Delete.
	(disconnected_tracing): Declare.
	(stop_tracing): Declare.

	* server.c (handle_query) <qSupported>: Report support for
	disconnected tracing.
	(queue_stop_reply_callback): Account for running threads.
	(gdb_wants_thread_stopped): New.
	(gdb_wants_all_threads_stopped): New.
	(gdb_reattached_process): New.
	(handle_status): Clear the `gdb_detached' flag of all processes.
	In all-stop, stop all threads.
	(main): Be sure to leave tfind mode.  Handle disconnected tracing.
	(process_serial_event): If the remote connection breaks, or if an
	exit was forced with "monitor exit", force an event loop exit.
	Handle disconnected tracing on detach.
	(handle_serial_event): Adjust.
	(handle_target_event): If GDB isn't connected, forward events back
	to the inferior, unless the last process exited, in which case,
	exit gdbserver.  Adjust interface.

	* remote-utils.c (remote_open): Don't block in accept.  Instead
	register an event loop source on the listen socket file
	descriptor.  Refactor bits into ...
	(listen_desc): ... this new global.
	(gdb_connected): ... this new function.
	(enable_async_notification): ... this new function.
	(handle_accept_event): ... this new function.
	(remote_close): Clear remote_desc.

	* inferiors.c (add_thread): Set the new thread's last_resume_kind.

	* target.h (struct target_ops) <mourn, thread_stopped, pause_all>:
	New fields.
	(mourn_inferior): Define.
	(target_process_qsupported): Avoid the dangling else problem.
	(thread_stopped): Define.
	(pause_all): Define.
	(target_waitstatus_to_string): Declare.
	* target.c (target_waitstatus_to_string): New.

	* tracepoint.c (tracing): Make extern.
	(disconnected_tracing): New.
	(stop_tracing): Make extern.  Handle tracing stops due to GDB
	disconnecting.
	(cmd_qtdisconnected): New.
	(cmd_qtstatus): Report disconnected tracing status in trace reply.
	(handle_tracepoint_general_set): Handle QTDisconnected.

	* event-loop.c (event_handler_func): Change return type to int.
	(process_event): Bail out if the event handler wants the event
	loop to stop.
	(handle_file_event): Ditto.
	(start_event_loop): Bail out if the event handler wants the event
	loop to stop.

	* nto-low.c (nto_target_ops): Adjust.
	* spu-low.c (spu_wait): Don't remove the process here.
	(spu_target_ops): Adjust.
	* win32-low.c (win32_wait): Don't remove the process here.
	(win32_target_ops): Adjust.

---
 gdb/gdbserver/event-loop.c   |   23 +++
 gdb/gdbserver/inferiors.c    |    1 
 gdb/gdbserver/linux-low.c    |  142 ++++++++++++++----------
 gdb/gdbserver/linux-low.h    |    6 -
 gdb/gdbserver/nto-low.c      |    1 
 gdb/gdbserver/remote-utils.c |  143 +++++++++++++++---------
 gdb/gdbserver/server.c       |  249 +++++++++++++++++++++++++++++++++++++------
 gdb/gdbserver/server.h       |   22 +++
 gdb/gdbserver/spu-low.c      |    3 
 gdb/gdbserver/target.c       |   42 +++++++
 gdb/gdbserver/target.h       |   34 +++++
 gdb/gdbserver/thread-db.c    |   54 ++++++---
 gdb/gdbserver/tracepoint.c   |   48 +++++++-
 gdb/gdbserver/win32-low.c    |    4 
 14 files changed, 587 insertions(+), 185 deletions(-)

Index: src/gdb/gdbserver/linux-low.c
===================================================================
--- src.orig/gdb/gdbserver/linux-low.c	2010-04-11 01:53:44.000000000 +0100
+++ src/gdb/gdbserver/linux-low.c	2010-04-11 16:43:09.000000000 +0100
@@ -287,19 +287,6 @@ linux_add_process (int pid, int attached
   return proc;
 }
 
-/* Remove a process from the common process list,
-   also freeing all private data.  */
-
-static void
-linux_remove_process (struct process_info *process)
-{
-  struct process_info_private *priv = process->private;
-
-  free (priv->arch_private);
-  free (priv);
-  remove_process (process);
-}
-
 /* Wrapper function for waitpid which handles EINTR, and emulates
    __WALL for systems where that is not available.  */
 
@@ -534,8 +521,6 @@ add_lwp (ptid_t ptid)
 
   lwp->head.id = ptid;
 
-  lwp->last_resume_kind = resume_continue;
-
   if (the_low_target.new_thread != NULL)
     lwp->arch_private = the_low_target.new_thread ();
 
@@ -644,7 +629,7 @@ linux_attach_lwp_1 (unsigned long lwpid,
 	of a new thread that is being created.
 	In this case we should ignore that SIGSTOP and resume the
 	process.  This is handled below by setting stop_expected = 1,
-	and the fact that add_lwp sets last_resume_kind ==
+	and the fact that add_thread sets last_resume_kind ==
 	resume_continue.
 
      2) This is the first thread (the process thread), and we're attaching
@@ -680,19 +665,17 @@ linux_attach_lwp (unsigned long lwpid)
 int
 linux_attach (unsigned long pid)
 {
-  struct lwp_info *lwp;
-
   linux_attach_lwp_1 (pid, 1);
-
   linux_add_process (pid, 1);
 
   if (!non_stop)
     {
-      /* Don't ignore the initial SIGSTOP if we just attached to this
-	 process.  It will be collected by wait shortly.  */
-      lwp = (struct lwp_info *) find_inferior_id (&all_lwps,
-						  ptid_build (pid, pid, 0));
-      lwp->last_resume_kind = resume_stop;
+      struct thread_info *thread;
+
+     /* Don't ignore the initial SIGSTOP if we just attached to this
+	process.  It will be collected by wait shortly.  */
+      thread = find_thread_ptid (ptid_build (pid, pid, 0));
+      thread->last_resume_kind = resume_stop;
     }
 
   return 0;
@@ -808,11 +791,9 @@ linux_kill (int pid)
       lwpid = linux_wait_for_event (lwp->head.id, &wstat, __WALL);
     } while (lwpid > 0 && WIFSTOPPED (wstat));
 
-#ifdef USE_THREAD_DB
-  thread_db_free (process, 0);
-#endif
   delete_lwp (lwp);
-  linux_remove_process (process);
+
+  the_target->mourn (process);
   return 0;
 }
 
@@ -893,7 +874,7 @@ linux_detach (int pid)
     return -1;
 
 #ifdef USE_THREAD_DB
-  thread_db_free (process, 1);
+  thread_db_detach (process);
 #endif
 
   current_inferior =
@@ -901,11 +882,28 @@ linux_detach (int pid)
 
   delete_all_breakpoints ();
   find_inferior (&all_threads, linux_detach_one_lwp, &pid);
-  linux_remove_process (process);
+
+  the_target->mourn (process);
   return 0;
 }
 
 static void
+linux_mourn (struct process_info *process)
+{
+  struct process_info_private *priv;
+
+#ifdef USE_THREAD_DB
+  thread_db_mourn (process);
+#endif
+
+  /* Freeing all private data.  */
+  priv = process->private;
+  free (priv->arch_private);
+  free (priv);
+  process->private = NULL;
+}
+
+static void
 linux_join (int pid)
 {
   int status, ret;
@@ -955,7 +953,7 @@ status_pending_p_callback (struct inferi
 
   /* If we got a `vCont;t', but we haven't reported a stop yet, do
      report any status pending the LWP may have.  */
-  if (lwp->last_resume_kind == resume_stop
+  if (thread->last_resume_kind == resume_stop
       && thread->last_status.kind == TARGET_WAITKIND_STOPPED)
     return 0;
 
@@ -1377,7 +1375,7 @@ linux_wait_for_event_1 (ptid_t ptid, int
 	    fprintf (stderr, "Expected stop.\n");
 	  event_child->stop_expected = 0;
 
-	  should_stop = (event_child->last_resume_kind == resume_stop
+	  should_stop = (current_inferior->last_resume_kind == resume_stop
 			 || stopping_threads);
 
 	  if (!should_stop)
@@ -1442,14 +1440,15 @@ static int
 count_events_callback (struct inferior_list_entry *entry, void *data)
 {
   struct lwp_info *lp = (struct lwp_info *) entry;
+  struct thread_info *thread = get_lwp_thread (lp);
   int *count = data;
 
   gdb_assert (count != NULL);
 
   /* Count only resumed LWPs that have a SIGTRAP event pending that
      should be reported to GDB.  */
-  if (get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
-      && lp->last_resume_kind != resume_stop
+  if (thread->last_status.kind == TARGET_WAITKIND_IGNORE
+      && thread->last_resume_kind != resume_stop
       && lp->status_pending_p
       && WIFSTOPPED (lp->status_pending)
       && WSTOPSIG (lp->status_pending) == SIGTRAP
@@ -1465,9 +1464,10 @@ static int
 select_singlestep_lwp_callback (struct inferior_list_entry *entry, void *data)
 {
   struct lwp_info *lp = (struct lwp_info *) entry;
+  struct thread_info *thread = get_lwp_thread (lp);
 
-  if (get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
-      && lp->last_resume_kind == resume_step
+  if (thread->last_status.kind == TARGET_WAITKIND_IGNORE
+      && thread->last_resume_kind == resume_step
       && lp->status_pending_p)
     return 1;
   else
@@ -1481,13 +1481,14 @@ static int
 select_event_lwp_callback (struct inferior_list_entry *entry, void *data)
 {
   struct lwp_info *lp = (struct lwp_info *) entry;
+  struct thread_info *thread = get_lwp_thread (lp);
   int *selector = data;
 
   gdb_assert (selector != NULL);
 
   /* Select only resumed LWPs that have a SIGTRAP event pending. */
-  if (lp->last_resume_kind != resume_stop
-      && get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
+  if (thread->last_resume_kind != resume_stop
+      && thread->last_status.kind == TARGET_WAITKIND_IGNORE
       && lp->status_pending_p
       && WIFSTOPPED (lp->status_pending)
       && WSTOPSIG (lp->status_pending) == SIGTRAP
@@ -1502,6 +1503,7 @@ static int
 cancel_breakpoints_callback (struct inferior_list_entry *entry, void *data)
 {
   struct lwp_info *lp = (struct lwp_info *) entry;
+  struct thread_info *thread = get_lwp_thread (lp);
   struct lwp_info *event_lp = data;
 
   /* Leave the LWP that has been elected to receive a SIGTRAP alone.  */
@@ -1519,8 +1521,8 @@ cancel_breakpoints_callback (struct infe
      delete or disable the breakpoint, but the LWP will have already
      tripped on it.  */
 
-  if (lp->last_resume_kind != resume_stop
-      && get_lwp_thread (lp)->last_status.kind == TARGET_WAITKIND_IGNORE
+  if (thread->last_resume_kind != resume_stop
+      && thread->last_status.kind == TARGET_WAITKIND_IGNORE
       && lp->status_pending_p
       && WIFSTOPPED (lp->status_pending)
       && WSTOPSIG (lp->status_pending) == SIGTRAP
@@ -1597,7 +1599,7 @@ gdb_wants_lwp_stopped (struct inferior_l
   thread->last_status.kind = TARGET_WAITKIND_STOPPED;
   thread->last_status.value.sig = TARGET_SIGNAL_0;
 
-  lwp->last_resume_kind = resume_stop;
+  thread->last_resume_kind = resume_stop;
 }
 
 /* Set all LWP's states as "want-stopped".  */
@@ -1691,14 +1693,7 @@ retry:
     {
       if (WIFEXITED (w) || WIFSIGNALED (w))
 	{
-	  int pid = pid_of (event_child);
-	  struct process_info *process = find_process_pid (pid);
-
-#ifdef USE_THREAD_DB
-	  thread_db_free (process, 0);
-#endif
 	  delete_lwp (event_child);
-	  linux_remove_process (process);
 
 	  current_inferior = NULL;
 
@@ -1800,7 +1795,7 @@ retry:
      breakpoint and still reporting the event to GDB.  If we don't,
      we're out of luck, GDB won't see the breakpoint hit.  */
   report_to_gdb = (!maybe_internal_trap
-		   || event_child->last_resume_kind == resume_step
+		   || current_inferior->last_resume_kind == resume_step
 		   || event_child->stopped_by_watchpoint
 		   || (!step_over_finished && !bp_explains_trap && !trace_event)
 		   || gdb_breakpoint_here (event_child->stop_pc));
@@ -1843,7 +1838,7 @@ retry:
 
   if (debug_threads)
     {
-      if (event_child->last_resume_kind == resume_step)
+      if (current_inferior->last_resume_kind == resume_step)
 	fprintf (stderr, "GDB wanted to single-step, reporting event.\n");
       if (event_child->stopped_by_watchpoint)
 	fprintf (stderr, "Stopped by watchpoint.\n");
@@ -1895,14 +1890,16 @@ retry:
 
   /* Do this before the gdb_wants_all_stopped calls below, since they
      always set last_resume_kind to resume_stop.  */
-  if (event_child->last_resume_kind == resume_stop && WSTOPSIG (w) == SIGSTOP)
+  if (current_inferior->last_resume_kind == resume_stop
+      && WSTOPSIG (w) == SIGSTOP)
     {
       /* A thread that has been requested to stop by GDB with vCont;t,
 	 and it stopped cleanly, so report as SIG0.  The use of
 	 SIGSTOP is an implementation detail.  */
       ourstatus->value.sig = TARGET_SIGNAL_0;
     }
-  else if (event_child->last_resume_kind == resume_stop && WSTOPSIG (w) != SIGSTOP)
+  else if (current_inferior->last_resume_kind == resume_stop
+	   && WSTOPSIG (w) != SIGSTOP)
     {
       /* A thread that has been requested to stop by GDB with vCont;t,
 	 but, it stopped for other reasons.  */
@@ -2361,7 +2358,7 @@ linux_set_resume_request (struct inferio
 	      && (ptid_get_pid (ptid) == pid_of (lwp))))
 	{
 	  if (r->resume[ndx].kind == resume_stop
-	      && lwp->last_resume_kind == resume_stop)
+	      && thread->last_resume_kind == resume_stop)
 	    {
 	      if (debug_threads)
 		fprintf (stderr, "already %s LWP %ld at GDB's request\n",
@@ -2374,7 +2371,7 @@ linux_set_resume_request (struct inferio
 	    }
 
 	  lwp->resume = &r->resume[ndx];
-	  lwp->last_resume_kind = lwp->resume->kind;
+	  thread->last_resume_kind = lwp->resume->kind;
 	  return 0;
 	}
     }
@@ -2412,6 +2409,7 @@ static int
 need_step_over_p (struct inferior_list_entry *entry, void *dummy)
 {
   struct lwp_info *lwp = (struct lwp_info *) entry;
+  struct thread_info *thread;
   struct thread_info *saved_inferior;
   CORE_ADDR pc;
 
@@ -2427,7 +2425,9 @@ need_step_over_p (struct inferior_list_e
       return 0;
     }
 
-  if (lwp->last_resume_kind == resume_stop)
+  thread = get_lwp_thread (lwp);
+
+  if (thread->last_resume_kind == resume_stop)
     {
       if (debug_threads)
 	fprintf (stderr,
@@ -2474,7 +2474,7 @@ need_step_over_p (struct inferior_list_e
     }
 
   saved_inferior = current_inferior;
-  current_inferior = get_lwp_thread (lwp);
+  current_inferior = thread;
 
   /* We can only step over breakpoints we know about.  */
   if (breakpoint_here (pc))
@@ -2802,6 +2802,7 @@ static void
 proceed_one_lwp (struct inferior_list_entry *entry)
 {
   struct lwp_info *lwp;
+  struct thread_info *thread;
   int step;
 
   lwp = (struct lwp_info *) entry;
@@ -2817,7 +2818,9 @@ proceed_one_lwp (struct inferior_list_en
       return;
     }
 
-  if (lwp->last_resume_kind == resume_stop)
+  thread = get_lwp_thread (lwp);
+
+  if (thread->last_resume_kind == resume_stop)
     {
       if (debug_threads)
 	fprintf (stderr, "   client wants LWP %ld stopped\n", lwpid_of (lwp));
@@ -2839,7 +2842,7 @@ proceed_one_lwp (struct inferior_list_en
       return;
     }
 
-  step = lwp->last_resume_kind == resume_step;
+  step = thread->last_resume_kind == resume_step;
   linux_resume_one_lwp (lwp, step, 0, NULL);
 }
 
@@ -4032,6 +4035,10 @@ linux_async (int enable)
 {
   int previous = (linux_event_pipe[0] != -1);
 
+  if (debug_threads)
+    fprintf (stderr, "linux_async (%d), previous=%d\n",
+	     enable, previous);
+
   if (previous != enable)
     {
       sigset_t mask;
@@ -4261,11 +4268,26 @@ linux_write_pc (struct regcache *regcach
   (*the_low_target.set_pc) (regcache, pc);
 }
 
+static int
+linux_thread_stopped (struct thread_info *thread)
+{
+  return get_thread_lwp (thread)->stopped;
+}
+
+/* This exposes stop-all-threads functionality to other modules.  */
+
+static void
+linux_pause_all (void)
+{
+  stop_all_lwps ();
+}
+
 static struct target_ops linux_target_ops = {
   linux_create_inferior,
   linux_attach,
   linux_kill,
   linux_detach,
+  linux_mourn,
   linux_join,
   linux_thread_alive,
   linux_resume,
@@ -4308,7 +4330,9 @@ static struct target_ops linux_target_op
   linux_process_qsupported,
   linux_supports_tracepoints,
   linux_read_pc,
-  linux_write_pc
+  linux_write_pc,
+  linux_thread_stopped,
+  linux_pause_all
 };
 
 static void
Index: src/gdb/gdbserver/linux-low.h
===================================================================
--- src.orig/gdb/gdbserver/linux-low.h	2010-04-11 01:39:32.000000000 +0100
+++ src/gdb/gdbserver/linux-low.h	2010-04-11 16:43:09.000000000 +0100
@@ -201,9 +201,6 @@ struct lwp_info
      and then processed and cleared in linux_resume_one_lwp.  */
   struct thread_resume *resume;
 
-  /* The last resume GDB requested on this thread.  */
-  enum resume_kind last_resume_kind;
-
   /* True if the LWP was seen stop at an internal breakpoint and needs
      stepping over later when it is resumed.  */
   int need_step_over;
@@ -229,7 +226,8 @@ struct lwp_info *find_lwp_pid (ptid_t pt
 
 /* From thread-db.c  */
 int thread_db_init (int use_events);
-void thread_db_free (struct process_info *, int detaching);
+void thread_db_detach (struct process_info *);
+void thread_db_mourn (struct process_info *);
 int thread_db_handle_monitor_command (char *);
 int thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
 			       CORE_ADDR load_module, CORE_ADDR *address);
Index: src/gdb/gdbserver/remote-utils.c
===================================================================
--- src.orig/gdb/gdbserver/remote-utils.c	2010-04-11 01:39:32.000000000 +0100
+++ src/gdb/gdbserver/remote-utils.c	2010-04-11 02:55:57.000000000 +0100
@@ -92,6 +92,7 @@ int remote_debug = 0;
 struct ui_file *gdb_stdlog;
 
 static int remote_desc = INVALID_DESCRIPTOR;
+static int listen_desc = INVALID_DESCRIPTOR;
 
 /* FIXME headerize? */
 extern int using_threads;
@@ -107,15 +108,88 @@ int transport_is_reliable = 0;
 # define write(fd, buf, len) send (fd, (char *) buf, len, 0)
 #endif
 
+int
+gdb_connected (void)
+{
+  return remote_desc != INVALID_DESCRIPTOR;
+}
+
+static void
+enable_async_notification (int fd)
+{
+#if defined(F_SETFL) && defined (FASYNC)
+  int save_fcntl_flags;
+
+  save_fcntl_flags = fcntl (fd, F_GETFL, 0);
+  fcntl (fd, F_SETFL, save_fcntl_flags | FASYNC);
+#if defined (F_SETOWN)
+  fcntl (fd, F_SETOWN, getpid ());
+#endif
+#endif
+}
+
+static int
+handle_accept_event (int err, gdb_client_data client_data)
+{
+  struct sockaddr_in sockaddr;
+  socklen_t tmp;
+
+  if (debug_threads)
+    fprintf (stderr, "handling possible accept event\n");
+
+  tmp = sizeof (sockaddr);
+  remote_desc = accept (listen_desc, (struct sockaddr *) &sockaddr, &tmp);
+  if (remote_desc == -1)
+    perror_with_name ("Accept failed");
+
+  /* Enable TCP keep alive process. */
+  tmp = 1;
+  setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
+	      (char *) &tmp, sizeof (tmp));
+
+  /* Tell TCP not to delay small packets.  This greatly speeds up
+     interactive response. */
+  tmp = 1;
+  setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
+	      (char *) &tmp, sizeof (tmp));
+
+#ifndef USE_WIN32API
+  close (listen_desc);		/* No longer need this */
+
+  signal (SIGPIPE, SIG_IGN);	/* If we don't do this, then gdbserver simply
+				   exits when the remote side dies.  */
+#else
+  closesocket (listen_desc);	/* No longer need this */
+#endif
+
+  delete_file_handler (listen_desc);
+
+  /* Convert IP address to string.  */
+  fprintf (stderr, "Remote debugging from host %s\n",
+	   inet_ntoa (sockaddr.sin_addr));
+
+  enable_async_notification (remote_desc);
+
+  /* Register the event loop handler.  */
+  add_file_handler (remote_desc, handle_serial_event, NULL);
+
+  /* We have a new GDB connection now.  If we were disconnected
+     tracing, there's a window where the target could report a stop
+     event to the event loop, and since we have a connection now, we'd
+     try to send vStopped notifications to GDB.  But, don't do that
+     until GDB as selected all-stop/non-stop, and has queried the
+     threads' status ('?').  */
+  target_async (0);
+
+  return 0;
+}
+
 /* Open a connection to a remote debugger.
    NAME is the filename used for communication.  */
 
 void
 remote_open (char *name)
 {
-#if defined(F_SETFL) && defined (FASYNC)
-  int save_fcntl_flags;
-#endif
   char *port_str;
 
   port_str = strchr (name, ':');
@@ -183,9 +257,14 @@ remote_open (char *name)
 #endif
 
       fprintf (stderr, "Remote debugging using %s\n", name);
-#endif /* USE_WIN32API */
 
       transport_is_reliable = 0;
+
+      enable_async_notification (remote_desc);
+
+      /* Register the event loop handler.  */
+      add_file_handler (remote_desc, handle_serial_event, NULL);
+#endif /* USE_WIN32API */
     }
   else
     {
@@ -195,7 +274,6 @@ remote_open (char *name)
       int port;
       struct sockaddr_in sockaddr;
       socklen_t tmp;
-      int tmp_desc;
       char *port_end;
 
       port = strtoul (port_str + 1, &port_end, 10);
@@ -212,21 +290,21 @@ remote_open (char *name)
 	}
 #endif
 
-      tmp_desc = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
-      if (tmp_desc < 0)
+      listen_desc = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+      if (listen_desc < 0)
 	perror_with_name ("Can't open socket");
 
       /* Allow rapid reuse of this port. */
       tmp = 1;
-      setsockopt (tmp_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
+      setsockopt (listen_desc, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp,
 		  sizeof (tmp));
 
       sockaddr.sin_family = PF_INET;
       sockaddr.sin_port = htons (port);
       sockaddr.sin_addr.s_addr = INADDR_ANY;
 
-      if (bind (tmp_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
-	  || listen (tmp_desc, 1))
+      if (bind (listen_desc, (struct sockaddr *) &sockaddr, sizeof (sockaddr))
+	  || listen (listen_desc, 1))
 	perror_with_name ("Can't bind address");
 
       /* If port is zero, a random port will be selected, and the
@@ -234,7 +312,7 @@ remote_open (char *name)
       if (port == 0)
 	{
 	  socklen_t len = sizeof (sockaddr);
-	  if (getsockname (tmp_desc, (struct sockaddr *) &sockaddr, &len) < 0
+	  if (getsockname (listen_desc, (struct sockaddr *) &sockaddr, &len) < 0
 	      || len < sizeof (sockaddr))
 	    perror_with_name ("Can't determine port");
 	  port = ntohs (sockaddr.sin_port);
@@ -243,49 +321,11 @@ remote_open (char *name)
       fprintf (stderr, "Listening on port %d\n", port);
       fflush (stderr);
 
-      tmp = sizeof (sockaddr);
-      remote_desc = accept (tmp_desc, (struct sockaddr *) &sockaddr, &tmp);
-      if (remote_desc == -1)
-	perror_with_name ("Accept failed");
-
-      /* Enable TCP keep alive process. */
-      tmp = 1;
-      setsockopt (remote_desc, SOL_SOCKET, SO_KEEPALIVE,
-		  (char *) &tmp, sizeof (tmp));
-
-      /* Tell TCP not to delay small packets.  This greatly speeds up
-	 interactive response. */
-      tmp = 1;
-      setsockopt (remote_desc, IPPROTO_TCP, TCP_NODELAY,
-		  (char *) &tmp, sizeof (tmp));
-
-
-#ifndef USE_WIN32API
-      close (tmp_desc);		/* No longer need this */
-
-      signal (SIGPIPE, SIG_IGN);	/* If we don't do this, then gdbserver simply
-					   exits when the remote side dies.  */
-#else
-      closesocket (tmp_desc);	/* No longer need this */
-#endif
-
-      /* Convert IP address to string.  */
-      fprintf (stderr, "Remote debugging from host %s\n",
-	       inet_ntoa (sockaddr.sin_addr));
+      /* Register the event loop handler.  */
+      add_file_handler (listen_desc, handle_accept_event, NULL);
 
       transport_is_reliable = 1;
     }
-
-#if defined(F_SETFL) && defined (FASYNC)
-  save_fcntl_flags = fcntl (remote_desc, F_GETFL, 0);
-  fcntl (remote_desc, F_SETFL, save_fcntl_flags | FASYNC);
-#if defined (F_SETOWN)
-  fcntl (remote_desc, F_SETOWN, getpid ());
-#endif
-#endif
-
-  /* Register the event loop handler.  */
-  add_file_handler (remote_desc, handle_serial_event, NULL);
 }
 
 void
@@ -298,6 +338,7 @@ remote_close (void)
 #else
   close (remote_desc);
 #endif
+  remote_desc = INVALID_DESCRIPTOR;
 }
 
 /* Convert hex digit A to a number.  */
Index: src/gdb/gdbserver/server.c
===================================================================
--- src.orig/gdb/gdbserver/server.c	2010-04-11 01:53:44.000000000 +0100
+++ src/gdb/gdbserver/server.c	2010-04-11 16:43:44.000000000 +0100
@@ -1392,6 +1392,7 @@ handle_query (char *own_buf, int packet_
 	  strcat (own_buf, ";ConditionalTracepoints+");
 	  strcat (own_buf, ";TraceStateVariables+");
 	  strcat (own_buf, ";TracepointSource+");
+	  strcat (own_buf, ";DisconnectedTracing+");
 	}
 
       return;
@@ -1976,31 +1977,81 @@ myresume (char *own_buf, int step, int s
 static int
 queue_stop_reply_callback (struct inferior_list_entry *entry, void *arg)
 {
-  int pid = * (int *) arg;
+  struct thread_info *thread = (struct thread_info *) entry;
 
-  if (pid == -1
-      || ptid_get_pid (entry->id) == pid)
+  /* For now, assume targets that don't have this callback also don't
+     manage the thread's last_status field.  */
+  if (the_target->thread_stopped == NULL)
     {
       struct target_waitstatus status;
 
       status.kind = TARGET_WAITKIND_STOPPED;
       status.value.sig = TARGET_SIGNAL_TRAP;
 
-      /* Pass the last stop reply back to GDB, but don't notify.  */
-      queue_stop_reply (entry->id, &status);
+      /* Pass the last stop reply back to GDB, but don't notify
+	 yet.  */
+      queue_stop_reply (entry->id, &thread->last_status);
+    }
+  else
+    {
+      if (thread_stopped (thread))
+	{
+	  if (debug_threads)
+	    fprintf (stderr, "Reporting thread %s as already stopped with %s\n",
+		     target_pid_to_str (entry->id),
+		     target_waitstatus_to_string (&thread->last_status));
+
+	  /* Pass the last stop reply back to GDB, but don't notify
+	     yet.  */
+	  queue_stop_reply (entry->id, &thread->last_status);
+	}
     }
 
   return 0;
 }
 
+/* Set this inferior LWP's state as "want-stopped".  We won't resume
+   this LWP until the client gives us another action for it.  */
+
+static void
+gdb_wants_thread_stopped (struct inferior_list_entry *entry)
+{
+  struct thread_info *thread = (struct thread_info *) entry;
+
+  thread->last_resume_kind = resume_stop;
+
+  if (thread->last_status.kind == TARGET_WAITKIND_IGNORE)
+    {
+      thread->last_status.kind = TARGET_WAITKIND_STOPPED;
+      thread->last_status.value.sig = TARGET_SIGNAL_0;
+    }
+}
+
+/* Set all threads' states as "want-stopped".  */
+
+static void
+gdb_wants_all_threads_stopped (void)
+{
+  for_each_inferior (&all_threads, gdb_wants_thread_stopped);
+}
+
+/* Clear the gdb_detached flag of every process.  */
+
+static void
+gdb_reattached_process (struct inferior_list_entry *entry)
+{
+  struct process_info *process = (struct process_info *) entry;
+
+  process->gdb_detached = 0;
+}
+
 /* Status handler for the '?' packet.  */
 
 static void
 handle_status (char *own_buf)
 {
-  struct target_waitstatus status;
-  status.kind = TARGET_WAITKIND_STOPPED;
-  status.value.sig = TARGET_SIGNAL_TRAP;
+  /* GDB is connected, don't forward events to the target anymore.  */
+  for_each_inferior (&all_processes, gdb_reattached_process);
 
   /* In non-stop mode, we must send a stop reply for each stopped
      thread.  In all-stop mode, just send one for the first stopped
@@ -2008,9 +2059,8 @@ handle_status (char *own_buf)
 
   if (non_stop)
     {
-      int pid = -1;
-      discard_queued_stop_replies (pid);
-      find_inferior (&all_threads, queue_stop_reply_callback, &pid);
+      discard_queued_stop_replies (-1);
+      find_inferior (&all_threads, queue_stop_reply_callback, NULL);
 
       /* The first is sent immediatly.  OK is sent if there is no
 	 stopped thread, which is the same handling of the vStopped
@@ -2019,9 +2069,18 @@ handle_status (char *own_buf)
     }
   else
     {
+      pause_all ();
+      gdb_wants_all_threads_stopped ();
+
       if (all_threads.head)
-	prepare_resume_reply (own_buf,
-			      all_threads.head->id, &status);
+	{
+	  struct target_waitstatus status;
+
+	  status.kind = TARGET_WAITKIND_STOPPED;
+	  status.value.sig = TARGET_SIGNAL_TRAP;
+	  prepare_resume_reply (own_buf,
+				all_threads.head->id, &status);
+	}
       else
 	strcpy (own_buf, "W00");
     }
@@ -2390,7 +2449,8 @@ main (int argc, char *argv[])
     {
       noack_mode = 0;
       multi_process = 0;
-      non_stop = 0;
+      /* Be sure we're out of tfind mode.  */
+      current_traceframe = -1;
 
       remote_open (port);
 
@@ -2405,7 +2465,7 @@ main (int argc, char *argv[])
 	}
 
       /* Wait for events.  This will return when all event sources are
-	 removed from the event loop. */
+	 removed from the event loop.  */
       start_event_loop ();
 
       /* If an exit was requested (using the "monitor exit" command),
@@ -2418,9 +2478,37 @@ main (int argc, char *argv[])
 	  detach_or_kill_for_exit ();
 	  exit (0);
 	}
-      else
-	fprintf (stderr, "Remote side has terminated connection.  "
-		 "GDBserver will reopen the connection.\n");
+
+      fprintf (stderr,
+	       "Remote side has terminated connection.  "
+	       "GDBserver will reopen the connection.\n");
+
+      if (tracing)
+	{
+	  if (disconnected_tracing)
+	    {
+	      /* Try to enable non-stop/async mode, so we we can both
+		 wait for an async socket accept, and handle async
+		 target events simultaneously.  There's also no point
+		 either in having the target always stop all threads,
+		 when we're going to pass signals down without
+		 informing GDB.  */
+	      if (!non_stop)
+		{
+		  if (start_non_stop (1))
+		    non_stop = 1;
+
+		  /* Detaching implicitly resumes all threads; simply
+		     disconnecting does not.  */
+		}
+	    }
+	  else
+	    {
+	      fprintf (stderr,
+		       "Disconnected tracing disabled; stopping trace run.\n");
+	      stop_tracing ();
+	    }
+	}
     }
 }
 
@@ -2429,7 +2517,7 @@ main (int argc, char *argv[])
    a brisk pace, so we read the rest of the packet with a blocking
    getpkt call.  */
 
-static void
+static int
 process_serial_event (void)
 {
   char ch;
@@ -2455,9 +2543,9 @@ process_serial_event (void)
   packet_len = getpkt (own_buf);
   if (packet_len <= 0)
     {
-      target_async (0);
       remote_close ();
-      return;
+      /* Force an event loop break.  */
+      return -1;
     }
   response_needed = 1;
 
@@ -2483,7 +2571,49 @@ process_serial_event (void)
 	pid =
 	  ptid_get_pid (((struct inferior_list_entry *) current_inferior)->id);
 
+      if (tracing && disconnected_tracing)
+	{
+	  struct thread_resume resume_info;
+	  struct process_info *process = find_process_pid (pid);
+
+	  if (process == NULL)
+	    {
+	      write_enn (own_buf);
+	      break;
+	    }
+
+	  fprintf (stderr,
+		   "Disconnected tracing in effect, "
+		   "leaving gdbserver attached to the process\n");
+
+	  /* Make sure we're in non-stop/async mode, so we we can both
+	     wait for an async socket accept, and handle async target
+	     events simultaneously.  There's also no point either in
+	     having the target stop all threads, when we're going to
+	     pass signals down without informing GDB.  */
+	  if (!non_stop)
+	    {
+	      if (debug_threads)
+		fprintf (stderr, "Forcing non-stop mode\n");
+
+	      non_stop = 1;
+	      start_non_stop (1);
+	    }
+
+	  process->gdb_detached = 1;
+
+	  /* Detaching implicitly resumes all threads.  */
+	  resume_info.thread = minus_one_ptid;
+	  resume_info.kind = resume_continue;
+	  resume_info.sig = 0;
+	  (*the_target->resume) (&resume_info, 1);
+
+	  write_ok (own_buf);
+	  break; /* from switch/case */
+	}
+
       fprintf (stderr, "Detaching from process %d\n", pid);
+      stop_tracing ();
       if (detach_inferior (pid) != 0)
 	write_enn (own_buf);
       else
@@ -2727,7 +2857,7 @@ process_serial_event (void)
       if (!target_running ())
 	/* The packet we received doesn't make sense - but we can't
 	   reply to it, either.  */
-	return;
+	return 0;
 
       fprintf (stderr, "Killing all inferiors\n");
       for_each_inferior (&all_processes, kill_inferior_callback);
@@ -2738,13 +2868,11 @@ process_serial_event (void)
 	{
 	  last_status.kind = TARGET_WAITKIND_EXITED;
 	  last_status.value.sig = TARGET_SIGNAL_KILL;
-	  return;
+	  return 0;
 	}
       else
-	{
-	  exit (0);
-	  break;
-	}
+	exit (0);
+
     case 'T':
       {
 	ptid_t gdb_id, thread_id;
@@ -2785,7 +2913,7 @@ process_serial_event (void)
 	      last_status.kind = TARGET_WAITKIND_EXITED;
 	      last_status.value.sig = TARGET_SIGNAL_KILL;
 	    }
-	  return;
+	  return 0;
 	}
       else
 	{
@@ -2826,27 +2954,35 @@ process_serial_event (void)
 	  exit (0);
 	}
     }
+
+  if (exit_requested)
+    return -1;
+
+  return 0;
 }
 
 /* Event-loop callback for serial events.  */
 
-void
+int
 handle_serial_event (int err, gdb_client_data client_data)
 {
   if (debug_threads)
     fprintf (stderr, "handling possible serial event\n");
 
   /* Really handle it.  */
-  process_serial_event ();
+  if (process_serial_event () < 0)
+    return -1;
 
   /* Be sure to not change the selected inferior behind GDB's back.
      Important in the non-stop mode asynchronous protocol.  */
   set_desired_inferior (1);
+
+  return 0;
 }
 
 /* Event-loop callback for target events.  */
 
-void
+int
 handle_target_event (int err, gdb_client_data client_data)
 {
   if (debug_threads)
@@ -2857,11 +2993,58 @@ handle_target_event (int err, gdb_client
 
   if (last_status.kind != TARGET_WAITKIND_IGNORE)
     {
-      /* Something interesting.  Tell GDB about it.  */
-      push_event (last_ptid, &last_status);
+      int pid = ptid_get_pid (last_ptid);
+      struct process_info *process = find_process_pid (pid);
+      int forward_event = !gdb_connected () || process->gdb_detached;
+
+      if (last_status.kind == TARGET_WAITKIND_EXITED
+	  || last_status.kind == TARGET_WAITKIND_SIGNALLED)
+	{
+	  mourn_inferior (process);
+	  remove_process (process);
+	}
+
+      if (forward_event)
+	{
+	  if (!target_running ())
+	    {
+	      /* The last process exited.  We're done.  */
+	      exit (0);
+	    }
+
+	  if (last_status.kind == TARGET_WAITKIND_STOPPED)
+	    {
+	      /* A thread stopped with a signal, but gdb isn't
+		 connected to handle it.  Pass it down to the
+		 inferior, as if it wasn't being traced.  */
+	      struct thread_resume resume_info;
+
+	      if (debug_threads)
+		fprintf (stderr,
+			 "GDB not connected; forwarding event %d for [%s]\n",
+			 (int) last_status.kind,
+			 target_pid_to_str (last_ptid));
+
+	      resume_info.thread = last_ptid;
+	      resume_info.kind = resume_continue;
+	      resume_info.sig = last_status.value.sig;
+	      (*the_target->resume) (&resume_info, 1);
+	    }
+	  else if (debug_threads)
+	    fprintf (stderr, "GDB not connected; ignoring event %d for [%s]\n",
+		     (int) last_status.kind,
+		     target_pid_to_str (last_ptid));
+	}
+      else
+	{
+	  /* Something interesting.  Tell GDB about it.  */
+	  push_event (last_ptid, &last_status);
+	}
     }
 
   /* Be sure to not change the selected inferior behind GDB's back.
      Important in the non-stop mode asynchronous protocol.  */
   set_desired_inferior (1);
+
+  return 0;
 }
Index: src/gdb/gdbserver/server.h
===================================================================
--- src.orig/gdb/gdbserver/server.h	2010-04-11 01:39:32.000000000 +0100
+++ src/gdb/gdbserver/server.h	2010-04-11 16:43:09.000000000 +0100
@@ -179,6 +179,9 @@ struct thread_info
   void *target_data;
   void *regcache_data;
 
+  /* The last resume GDB requested on this thread.  */
+  enum resume_kind last_resume_kind;
+
   /* The last wait status reported for this thread.  */
   struct target_waitstatus last_status;
 
@@ -224,8 +227,14 @@ struct process_info
 {
   struct inferior_list_entry head;
 
+  /* Nonzero if this child process was attached rather than
+     spawned.  */
   int attached;
 
+  /* True if GDB asked us to detach from this process, but we remained
+     attached anyway.  */
+  int gdb_detached;
+
   /* The symbol cache.  */
   struct sym_cache *symbol_cache;
 
@@ -327,7 +336,7 @@ extern int non_stop;
 
 /* Functions from event-loop.c.  */
 typedef void *gdb_client_data;
-typedef void (handler_func) (int, gdb_client_data);
+typedef int (handler_func) (int, gdb_client_data);
 
 extern void delete_file_handler (int fd);
 extern void add_file_handler (int fd, handler_func *proc,
@@ -336,8 +345,8 @@ extern void add_file_handler (int fd, ha
 extern void start_event_loop (void);
 
 /* Functions from server.c.  */
-extern void handle_serial_event (int err, gdb_client_data client_data);
-extern void handle_target_event (int err, gdb_client_data client_data);
+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 void push_event (ptid_t ptid, struct target_waitstatus *status);
 
@@ -354,6 +363,8 @@ extern int all_symbols_looked_up;
 extern int noack_mode;
 extern int transport_is_reliable;
 
+int gdb_connected (void);
+
 ptid_t read_ptid (char *buf, char **obuf);
 char *write_ptid (char *buf, ptid_t ptid);
 
@@ -499,6 +510,11 @@ char *phex_nz (ULONGEST l, int sizeof_l)
 
 void initialize_tracepoint (void);
 
+extern int tracing;
+extern int disconnected_tracing;
+
+void stop_tracing (void);
+
 int handle_tracepoint_general_set (char *own_buf);
 int handle_tracepoint_query (char *own_buf);
 
Index: src/gdb/gdbserver/target.c
===================================================================
--- src.orig/gdb/gdbserver/target.c	2010-04-11 01:39:32.000000000 +0100
+++ src/gdb/gdbserver/target.c	2010-04-11 02:55:57.000000000 +0100
@@ -154,3 +154,45 @@ target_pid_to_str (ptid_t ptid)
 
   return buf;
 }
+
+/* Return a pretty printed form of target_waitstatus.  */
+
+const char *
+target_waitstatus_to_string (const struct target_waitstatus *ws)
+{
+  static char buf[200];
+  const char *kind_str = "status->kind = ";
+
+  switch (ws->kind)
+    {
+    case TARGET_WAITKIND_EXITED:
+      sprintf (buf, "%sexited, status = %d",
+	       kind_str, ws->value.integer);
+      break;
+    case TARGET_WAITKIND_STOPPED:
+      sprintf (buf, "%sstopped, signal = %s",
+	       kind_str, target_signal_to_name (ws->value.sig));
+      break;
+    case TARGET_WAITKIND_SIGNALLED:
+      sprintf (buf, "%ssignalled, signal = %s",
+	       kind_str, target_signal_to_name (ws->value.sig));
+      break;
+    case TARGET_WAITKIND_LOADED:
+      sprintf (buf, "%sloaded", kind_str);
+      break;
+    case TARGET_WAITKIND_EXECD:
+      sprintf (buf, "%sexecd", kind_str);
+      break;
+    case TARGET_WAITKIND_SPURIOUS:
+      sprintf (buf, "%sspurious", kind_str);
+      break;
+    case TARGET_WAITKIND_IGNORE:
+      sprintf (buf, "%signore", kind_str);
+      break;
+    default:
+      sprintf (buf, "%sunknown???", kind_str);
+      break;
+    }
+
+  return buf;
+}
Index: src/gdb/gdbserver/target.h
===================================================================
--- src.orig/gdb/gdbserver/target.h	2010-04-11 01:39:32.000000000 +0100
+++ src/gdb/gdbserver/target.h	2010-04-11 16:43:44.000000000 +0100
@@ -138,6 +138,10 @@ struct target_ops
 
   int (*detach) (int pid);
 
+  /* The inferior process has died.  Do what is right.  */
+
+  void (*mourn) (struct process_info *proc);
+
   /* Wait for inferior PID to exit.  */
   void (*join) (int pid);
 
@@ -299,6 +303,12 @@ struct target_ops
 
   /* Write PC to REGCACHE.  */
   void (*write_pc) (struct regcache *regcache, CORE_ADDR pc);
+
+  /* Return true if THREAD is known to be stopped now.  */
+  int (*thread_stopped) (struct thread_info *thread);
+
+  /* Pause all threads.  */
+  void (*pause_all) (void);
 };
 
 extern struct target_ops *the_target;
@@ -317,6 +327,9 @@ void set_target_ops (struct target_ops *
 #define detach_inferior(pid) \
   (*the_target->detach) (pid)
 
+#define mourn_inferior(PROC) \
+  (*the_target->mourn) (PROC)
+
 #define mythread_alive(pid) \
   (*the_target->thread_alive) (pid)
 
@@ -339,14 +352,27 @@ void set_target_ops (struct target_ops *
   (the_target->supports_multi_process ? \
    (*the_target->supports_multi_process) () : 0)
 
-#define target_process_qsupported(query) \
-  if (the_target->process_qsupported) \
-    the_target->process_qsupported (query)
+#define target_process_qsupported(query)		\
+  do							\
+    {							\
+      if (the_target->process_qsupported)		\
+	the_target->process_qsupported (query);		\
+    } while (0)
 
 #define target_supports_tracepoints()			\
   (the_target->supports_tracepoints			\
    ? (*the_target->supports_tracepoints) () : 0)
 
+#define thread_stopped(thread) \
+  (*the_target->thread_stopped) (thread)
+
+#define pause_all()			\
+  do					\
+    {					\
+      if (the_target->pause_all)	\
+	(*the_target->pause_all) ();	\
+    } while (0)
+
 /* Start non-stop mode, returns 0 on success, -1 on failure.   */
 
 int start_non_stop (int nonstop);
@@ -363,4 +389,6 @@ void set_desired_inferior (int id);
 
 const char *target_pid_to_str (ptid_t);
 
+const char *target_waitstatus_to_string (const struct target_waitstatus *);
+
 #endif /* TARGET_H */
Index: src/gdb/gdbserver/tracepoint.c
===================================================================
--- src.orig/gdb/gdbserver/tracepoint.c	2010-04-11 01:39:32.000000000 +0100
+++ src/gdb/gdbserver/tracepoint.c	2010-04-11 02:55:57.000000000 +0100
@@ -558,7 +558,11 @@ static struct readonly_region *readonly_
 
 /* The global that controls tracing overall.  */
 
-static int tracing;
+int tracing;
+
+/* Controls whether tracing should continue after GDB disconnects.  */
+
+int disconnected_tracing;
 
 /* The reason for the last tracing run to have stopped.  We initialize
    to a distinct string so that GDB can distinguish between "stopped
@@ -1699,7 +1703,7 @@ cmd_qtstart (char *packet)
 /* End a tracing run, filling in a stop reason to report back to GDB,
    and removing the tracepoints from the code.  */
 
-static void
+void
 stop_tracing (void)
 {
   if (!tracing)
@@ -1736,6 +1740,11 @@ stop_tracing (void)
       tracing_stop_reason = eval_result_names[expr_eval_result];
       tracing_stop_tpnum = error_tracepoint->number;
     }
+  else if (!gdb_connected ())
+    {
+      trace_debug ("Stopping the trace because GDB disconnected");
+      tracing_stop_reason = "tdisconnected";
+    }
   else
     {
       trace_debug ("Stopping the trace because of a tstop command");
@@ -1757,6 +1766,21 @@ cmd_qtstop (char *packet)
 }
 
 static void
+cmd_qtdisconnected (char *own_buf)
+{
+  ULONGEST setting;
+  char *packet = own_buf;
+
+  packet += strlen ("QTDisconnected:");
+
+  unpack_varlen_hex (packet, &setting);
+
+  write_ok (own_buf);
+
+  disconnected_tracing = setting;
+}
+
+static void
 cmd_qtframe (char *own_buf)
 {
   ULONGEST frame, pc, lo, hi, num;
@@ -1853,13 +1877,19 @@ cmd_qtstatus (char *packet)
       convert_int_to_ascii ((gdb_byte *) result_name, p, strlen (result_name));
     }
 
-  sprintf (packet, "T%d;%s:%x;tframes:%x;tcreated:%x;tfree:%x;tsize:%s;circular:%d",
+  sprintf (packet,
+	   "T%d;"
+	   "%s:%x;"
+	   "tframes:%x;tcreated:%x;"
+	   "tfree:%x;tsize:%s;"
+	   "circular:%d;"
+	   "disconn:%d",
 	   tracing ? 1 : 0,
 	   stop_reason_rsp, tracing_stop_tpnum,
 	   traceframe_count, traceframes_created,
-	   free_space (),
-	   phex_nz (trace_buffer_hi - trace_buffer_lo, 0),
-	   circular_trace_buffer);
+	   free_space (), phex_nz (trace_buffer_hi - trace_buffer_lo, 0),
+	   circular_trace_buffer,
+	   disconnected_tracing);
 }
 
 /* State variables to help return all the tracepoint bits.  */
@@ -2168,6 +2198,12 @@ handle_tracepoint_general_set (char *pac
       cmd_qtstop (packet);
       return 1;
     }
+  else if (strncmp ("QTDisconnected:", packet,
+		    strlen ("QTDisconnected:")) == 0)
+    {
+      cmd_qtdisconnected (packet);
+      return 1;
+    }
   else if (strncmp ("QTFrame:", packet, strlen ("QTFrame:")) == 0)
     {
       cmd_qtframe (packet);
Index: src/gdb/gdbserver/inferiors.c
===================================================================
--- src.orig/gdb/gdbserver/inferiors.c	2010-04-11 01:39:32.000000000 +0100
+++ src/gdb/gdbserver/inferiors.c	2010-04-11 02:55:57.000000000 +0100
@@ -171,6 +171,7 @@ add_thread (ptid_t thread_id, void *targ
   memset (new_thread, 0, sizeof (*new_thread));
 
   new_thread->entry.id = thread_id;
+  new_thread->last_resume_kind = resume_continue;
   new_thread->last_status.kind = TARGET_WAITKIND_IGNORE;
 
   add_inferior_to_list (&all_threads, & new_thread->entry);
Index: src/gdb/gdbserver/event-loop.c
===================================================================
--- src.orig/gdb/gdbserver/event-loop.c	2010-04-11 01:39:32.000000000 +0100
+++ src/gdb/gdbserver/event-loop.c	2010-04-11 16:44:23.000000000 +0100
@@ -39,7 +39,7 @@
 #endif
 
 typedef struct gdb_event gdb_event;
-typedef void (event_handler_func) (int);
+typedef int (event_handler_func) (int);
 
 /* Tell create_file_handler what events we are interested in.  */
 
@@ -211,7 +211,8 @@ process_event (void)
       free (event_ptr);
 
       /* Now call the procedure associated with the event.  */
-      (*proc) (fd);
+      if ((*proc) (fd))
+	return -1;
       return 1;
     }
 
@@ -347,7 +348,7 @@ delete_file_handler (int fd)
    through event_ptr->proc.  EVENT_FILE_DESC is file descriptor of the
    event in the front of the event queue.  */
 
-static void
+static int
 handle_file_event (int event_file_desc)
 {
   file_handler *file_ptr;
@@ -378,10 +379,16 @@ handle_file_event (int event_file_desc)
 
 	  /* If there was a match, then call the handler.  */
 	  if (mask != 0)
-	    (*file_ptr->proc) (file_ptr->error, file_ptr->client_data);
+	    {
+	      if ((*file_ptr->proc) (file_ptr->error,
+				     file_ptr->client_data) < 0)
+		return -1;
+	    }
 	  break;
 	}
     }
+
+  return 0;
 }
 
 /* Create a file event, to be enqueued in the event queue for
@@ -491,7 +498,13 @@ start_event_loop (void)
   while (1)
     {
       /* Any events already waiting in the queue?  */
-      if (process_event ())
+      int res = process_event ();
+
+      /* Did the event handler want the event loop to stop?  */
+      if (res == -1)
+	return;
+
+      if (res)
 	continue;
 
       /* Wait for a new event.  If wait_for_event returns -1, we
Index: src/gdb/gdbserver/nto-low.c
===================================================================
--- src.orig/gdb/gdbserver/nto-low.c	2010-04-11 16:42:59.000000000 +0100
+++ src/gdb/gdbserver/nto-low.c	2010-04-11 16:43:09.000000000 +0100
@@ -900,6 +900,7 @@ static struct target_ops nto_target_ops 
   nto_attach,
   nto_kill,
   nto_detach,
+  NULL, /* nto_mourn */
   NULL, /* nto_join */
   nto_thread_alive,
   nto_resume,
Index: src/gdb/gdbserver/spu-low.c
===================================================================
--- src.orig/gdb/gdbserver/spu-low.c	2010-04-11 16:42:59.000000000 +0100
+++ src/gdb/gdbserver/spu-low.c	2010-04-11 16:43:09.000000000 +0100
@@ -448,7 +448,6 @@ spu_wait (ptid_t ptid, struct target_wai
       ourstatus->kind =  TARGET_WAITKIND_EXITED;
       ourstatus->value.integer = WEXITSTATUS (w);
       clear_inferiors ();
-      remove_process (find_process_pid (ret));
       return pid_to_ptid (ret);
     }
   else if (!WIFSTOPPED (w))
@@ -457,7 +456,6 @@ spu_wait (ptid_t ptid, struct target_wai
       ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
       ourstatus->value.sig = target_signal_from_host (WTERMSIG (w));
       clear_inferiors ();
-      remove_process (find_process_pid (ret));
       return pid_to_ptid (ret);
     }
 
@@ -608,6 +606,7 @@ static struct target_ops spu_target_ops 
   spu_attach,
   spu_kill,
   spu_detach,
+  NULL, /* mourn */
   spu_join,
   spu_thread_alive,
   spu_resume,
Index: src/gdb/gdbserver/thread-db.c
===================================================================
--- src.orig/gdb/gdbserver/thread-db.c	2010-04-11 16:42:59.000000000 +0100
+++ src/gdb/gdbserver/thread-db.c	2010-04-11 16:43:09.000000000 +0100
@@ -743,7 +743,7 @@ thread_db_init (int use_events)
       if (use_events && thread_db_enable_reporting () == 0)
 	{
 	  /* Keep trying; maybe event reporting will work later.  */
-	  thread_db_free (proc, 0);
+	  thread_db_mourn (proc);
 	  return 0;
 	}
       thread_db_find_new_threads ();
@@ -768,41 +768,64 @@ any_thread_of (struct inferior_list_entr
 
 /* Disconnect from libthread_db and free resources.  */
 
-void
-thread_db_free (struct process_info *proc, int detaching)
+static void
+disable_thread_event_reporting (struct process_info *proc)
 {
   struct thread_db *thread_db = proc->private->thread_db;
   if (thread_db)
     {
-      struct thread_info *saved_inferior;
-      int pid;
-      td_err_e (*td_ta_delete_p) (td_thragent_t *);
       td_err_e (*td_ta_clear_event_p) (const td_thragent_t *ta,
 				       td_thr_events_t *event);
 
 #ifndef USE_LIBTHREAD_DB_DIRECTLY
       td_ta_clear_event_p = dlsym (thread_db->handle, "td_ta_clear_event");
-      td_ta_delete_p = dlsym (thread_db->handle, "td_ta_delete");
 #else
-      td_ta_delete_p = &td_ta_delete;
       td_ta_clear_event_p = &td_ta_clear_event;
 #endif
 
-      pid = pid_of (proc);
-      saved_inferior = current_inferior;
-      current_inferior =
-	(struct thread_info *) find_inferior (&all_threads,
-					      any_thread_of, &pid);
-
-      if (detaching && td_ta_clear_event_p != NULL)
+      if (td_ta_clear_event_p != NULL)
 	{
+	  struct thread_info *saved_inferior;
 	  td_thr_events_t events;
+	  int pid;
+
+	  pid = pid_of (proc);
+	  saved_inferior = current_inferior;
+	  current_inferior =
+	    (struct thread_info *) find_inferior (&all_threads,
+						  any_thread_of, &pid);
 
 	  /* Set the process wide mask saying we aren't interested
 	     in any events anymore.  */
 	  td_event_fillset (&events);
 	  (*td_ta_clear_event_p) (thread_db->thread_agent, &events);
+
+	  current_inferior = saved_inferior;
 	}
+    }
+}
+
+void
+thread_db_detach (struct process_info *proc)
+{
+  disable_thread_event_reporting (proc);
+}
+
+/* Disconnect from libthread_db and free resources.  */
+
+void
+thread_db_mourn (struct process_info *proc)
+{
+  struct thread_db *thread_db = proc->private->thread_db;
+  if (thread_db)
+    {
+      td_err_e (*td_ta_delete_p) (td_thragent_t *);
+
+#ifndef USE_LIBTHREAD_DB_DIRECTLY
+      td_ta_delete_p = dlsym (thread_db->handle, "td_ta_delete");
+#else
+      td_ta_delete_p = &td_ta_delete;
+#endif
 
       if (td_ta_delete_p != NULL)
 	(*td_ta_delete_p) (thread_db->thread_agent);
@@ -813,7 +836,6 @@ thread_db_free (struct process_info *pro
 
       free (thread_db);
       proc->private->thread_db = NULL;
-      current_inferior = saved_inferior;
     }
 }
 
Index: src/gdb/gdbserver/win32-low.c
===================================================================
--- src.orig/gdb/gdbserver/win32-low.c	2010-04-11 16:42:59.000000000 +0100
+++ src/gdb/gdbserver/win32-low.c	2010-04-11 16:43:09.000000000 +0100
@@ -1581,9 +1581,6 @@ win32_wait (ptid_t ptid, struct target_w
 	case TARGET_WAITKIND_EXITED:
 	  OUTMSG2 (("Child exited with retcode = %x\n",
 		    ourstatus->value.integer));
-
-	  process = find_process_pid (current_process_id);
-	  remove_process (process);
 	  win32_clear_inferiors ();
 	  return pid_to_ptid (current_event.dwProcessId);
 	case TARGET_WAITKIND_STOPPED:
@@ -1755,6 +1752,7 @@ static struct target_ops win32_target_op
   win32_attach,
   win32_kill,
   win32_detach,
+  NULL,
   win32_join,
   win32_thread_alive,
   win32_resume,


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]