GDBserver disconnected tracing support.
authorPedro Alves <palves@redhat.com>
Sun, 11 Apr 2010 16:33:56 +0000 (16:33 +0000)
committerPedro Alves <palves@redhat.com>
Sun, 11 Apr 2010 16:33:56 +0000 (16:33 +0000)
* 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.

15 files changed:
gdb/gdbserver/ChangeLog
gdb/gdbserver/event-loop.c
gdb/gdbserver/inferiors.c
gdb/gdbserver/linux-low.c
gdb/gdbserver/linux-low.h
gdb/gdbserver/nto-low.c
gdb/gdbserver/remote-utils.c
gdb/gdbserver/server.c
gdb/gdbserver/server.h
gdb/gdbserver/spu-low.c
gdb/gdbserver/target.c
gdb/gdbserver/target.h
gdb/gdbserver/thread-db.c
gdb/gdbserver/tracepoint.c
gdb/gdbserver/win32-low.c

index 6e29310ebbcbbb473b5a1c7a61e89b467fdef460..86caf1e7639b253686c485d2790b1ad6b6fbac75 100644 (file)
@@ -1,3 +1,102 @@
+2010-04-11  Pedro Alves  <pedro@codesourcery.com>
+
+       GDBserver disconnected tracing support.
+
+       * 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.
+
 2010-04-11  Pedro Alves  <pedro@codesourcery.com>
 
        * regcache.c (realloc_register_cache): Invalidate inferior's
index 7482e1cf5a7ef35b6cfcfba2cd8400cdc79644b8..6471772e16478d3a07ca661ff4933233481bff09 100644 (file)
@@ -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 8e3f1de6bb99c49ab62d315f11d53b0a3ae25c89..787b018f75c3c25b496e790b7d447bd8fd3e6c1c 100644 (file)
@@ -171,6 +171,7 @@ add_thread (ptid_t thread_id, void *target_data)
   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 3e37722e784ad6d5330948eca5c455f8de0e028f..fcdcc45fdb9cc64d8c42c04078a1bba6db24455b 100644 (file)
@@ -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, int initial)
        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,10 +882,27 @@ 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)
 {
@@ -955,7 +953,7 @@ status_pending_p_callback (struct inferior_list_entry *entry, void *arg)
 
   /* 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 *wstat, int options)
            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 inferior_list_entry *entry, void *data)
      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_list_entry *entry)
   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 inferior_list_entry *entry, void *arg)
              && (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 inferior_list_entry *entry, void *arg)
            }
 
          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_entry *entry, void *dummy)
       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_entry *entry, void *dummy)
     }
 
   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_entry *entry)
       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_entry *entry)
       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 *regcache, CORE_ADDR pc)
   (*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_ops = {
   linux_process_qsupported,
   linux_supports_tracepoints,
   linux_read_pc,
-  linux_write_pc
+  linux_write_pc,
+  linux_thread_stopped,
+  linux_pause_all
 };
 
 static void
index 4564f56d070b4953bb13b7e81fc70f2235542162..ec8f3f6aab6f810526c9d233b848b520037a42e2 100644 (file)
@@ -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 ptid);
 
 /* 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 89933b91d92e0a5cdbefc43ce9fd969283c938e5..854208291e919f0e98db4e2c7eb2d6b72963c983 100644 (file)
@@ -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 7cd1e2d7cf23c750904a8f81a4413a33d43ab473..6a93c3ae20c254aa1b167f8e429fae9c72c8cef1 100644 (file)
@@ -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 0fd82eebea64590d78461ea46ceb3ec969303d2d..8666bcd87899f37fae5d861841e3de3fdc1ab1fc 100644 (file)
@@ -1392,6 +1392,7 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
          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 sig)
 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_data client_data)
 
   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 dcff3599729ee3a16bf32af2bd95064fbf240698..e90e088b64357318b568f873a5a3433f4dfc40e2 100644 (file)
@@ -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, handler_func *proc,
 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 b2eb9130632d81445263abcf8ccfa74aef2b133a..d171abc3fc48d8d7fef48a0b4a42d695d71cacda 100644 (file)
@@ -448,7 +448,6 @@ spu_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
       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_waitstatus *ourstatus, int options)
       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 f50bc02c3da388d42819a06b4d2b3480e4ad6770..4fc8c07597f643398c94f1b10da95f00b3eb94d1 100644 (file)
@@ -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 6ee500982e679fc9ea4f98bdb33c0f50002727bc..4cccb29394a037d35971bc6f7ed7c97e8d5fb5bc 100644 (file)
@@ -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 55f953dfec0d17b2b58534a84d8d2d7f71380c9e..8e7d7a9361a4813b491f6c510e35141ade3ce868 100644 (file)
@@ -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_entry *entry, void *args)
 
 /* 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 *proc, int detaching)
 
       free (thread_db);
       proc->private->thread_db = NULL;
-      current_inferior = saved_inferior;
     }
 }
 
index df378ff4bb854d3442bfd3288d62062add968739..c7970e32ca8b581a8094223b68d1518ea39a3afe 100644 (file)
@@ -558,7 +558,11 @@ static struct readonly_region *readonly_regions;
 
 /* 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");
@@ -1756,6 +1765,21 @@ cmd_qtstop (char *packet)
   write_ok (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)
 {
@@ -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 *packet)
       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 904d6149eee9e25d079a818290f3492bb8cb923f..ea5de4cd9837af4c33c391df7c7f80e8980f98bb 100644 (file)
@@ -1581,9 +1581,6 @@ win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
        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_ops = {
   win32_attach,
   win32_kill,
   win32_detach,
+  NULL,
   win32_join,
   win32_thread_alive,
   win32_resume,