From f9e39928dc25d4f8ec5cad87c6cea4cd5ef08f58 Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Sun, 2 May 2010 00:47:34 +0000
Subject: 	* linux-low.c (linux_kill_one_lwp): Assume the lwp is stopped.
 	(linux_kill): Stop all lwps here.  Don't delete the main lwp here. 
 (linux_detach_one_lwp): Assume the lwp is stopped. 	(any_thread_of):
 Delete. 	(linux_detach): Stop all lwps here.  Don't blindly delete all 
 breakpoints. 	(delete_lwp_callback): New. 	(linux_mourn): Delete all lwps
 of the process that is gone. 	(linux_wait_1): Don't delete the last lwp of
 the process here. 	* mem-break.h (mark_breakpoints_out): Declare. 	*
 mem-break.c (mark_breakpoints_out): New. 	(free_all_breakpoints): Use
 it. 	* server.c (handle_target_event): If the process is gone, mark 
 breakpoints out. 	* thread-db.c (struct thread_db) <create_bp>: New
 field. 	(thread_db_enable_reporting): Fix prototype.  Store a thread
 event 	breakpoint reference in the thread_db struct. 
 (thread_db_load_search): Clear the thread_db object. 
 (try_thread_db_load_1): Ditto. 	(switch_to_process): New. 
 (disable_thread_event_reporting): Use it. 
 (remove_thread_event_breakpoints): New. 	(thread_db_detach,
 thread_db_mourn): Use it.

---
 gdb/gdbserver/ChangeLog   | 26 ++++++++++++++
 gdb/gdbserver/linux-low.c | 90 ++++++++++++++++++-----------------------------
 gdb/gdbserver/mem-break.c | 14 ++++++--
 gdb/gdbserver/mem-break.h |  4 +++
 gdb/gdbserver/server.c    |  5 ++-
 gdb/gdbserver/thread-db.c | 66 ++++++++++++++++++++++++++++------
 6 files changed, 135 insertions(+), 70 deletions(-)

diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 15f2176df36..a1f479857b4 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,29 @@
+2010-05-02  Pedro Alves  <pedro@codesourcery.com>
+
+	* linux-low.c (linux_kill_one_lwp): Assume the lwp is stopped.
+	(linux_kill): Stop all lwps here.  Don't delete the main lwp here.
+	(linux_detach_one_lwp): Assume the lwp is stopped.
+	(any_thread_of): Delete.
+	(linux_detach): Stop all lwps here.  Don't blindly delete all
+	breakpoints.
+	(delete_lwp_callback): New.
+	(linux_mourn): Delete all lwps of the process that is gone.
+	(linux_wait_1): Don't delete the last lwp of the process here.
+	* mem-break.h (mark_breakpoints_out): Declare.
+	* mem-break.c (mark_breakpoints_out): New.
+	(free_all_breakpoints): Use it.
+	* server.c (handle_target_event): If the process is gone, mark
+	breakpoints out.
+	* thread-db.c (struct thread_db) <create_bp>: New field.
+	(thread_db_enable_reporting): Fix prototype.  Store a thread event
+	breakpoint reference in the thread_db struct.
+	(thread_db_load_search): Clear the thread_db object.
+	(try_thread_db_load_1): Ditto.
+	(switch_to_process): New.
+	(disable_thread_event_reporting): Use it.
+	(remove_thread_event_breakpoints): New.
+	(thread_db_detach, thread_db_mourn): Use it.
+
 2010-05-01  Pedro Alves  <pedro@codesourcery.com>
 
 	* linux-low.c (linux_enable_event_reporting): New.
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 302f7028578..65a87f68ed8 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -739,11 +739,6 @@ linux_kill_one_lwp (struct inferior_list_entry *entry, void *args)
       return 0;
     }
 
-  /* If we're killing a running inferior, make sure it is stopped
-     first, as PTRACE_KILL will not work otherwise.  */
-  if (!lwp->stopped)
-    send_sigstop (lwp);
-
   do
     {
       ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0);
@@ -768,6 +763,10 @@ linux_kill (int pid)
   if (process == NULL)
     return -1;
 
+  /* If we're killing a running inferior, make sure it is stopped
+     first, as PTRACE_KILL will not work otherwise.  */
+  stop_all_lwps ();
+
   find_inferior (&all_threads, linux_kill_one_lwp, &pid);
 
   /* See the comment in linux_kill_one_lwp.  We did not kill the first
@@ -779,11 +778,6 @@ linux_kill (int pid)
     fprintf (stderr, "lk_1: killing lwp %ld, for pid: %d\n",
 	     lwpid_of (lwp), pid);
 
-  /* If we're killing a running inferior, make sure it is stopped
-     first, as PTRACE_KILL will not work otherwise.  */
-  if (!lwp->stopped)
-    send_sigstop (lwp);
-
   do
     {
       ptrace (PTRACE_KILL, lwpid_of (lwp), 0, 0);
@@ -792,9 +786,11 @@ linux_kill (int pid)
       lwpid = linux_wait_for_event (lwp->head.id, &wstat, __WALL);
     } while (lwpid > 0 && WIFSTOPPED (wstat));
 
-  delete_lwp (lwp);
-
   the_target->mourn (process);
+
+  /* Since we presently can only stop all lwps of all processes, we
+     need to unstop lwps of other processes.  */
+  unstop_all_lwps (NULL);
   return 0;
 }
 
@@ -808,28 +804,6 @@ linux_detach_one_lwp (struct inferior_list_entry *entry, void *args)
   if (ptid_get_pid (entry->id) != pid)
     return 0;
 
-  /* If we're detaching from a running inferior, make sure it is
-     stopped first, as PTRACE_DETACH will not work otherwise.  */
-  if (!lwp->stopped)
-    {
-      int lwpid = lwpid_of (lwp);
-
-      stopping_threads = 1;
-      send_sigstop (lwp);
-
-      /* If this detects a new thread through a clone event, the new
-	 thread is appended to the end of the lwp list, so we'll
-	 eventually detach from it.  */
-      wait_for_sigstop (&lwp->head);
-      stopping_threads = 0;
-
-      /* If LWP exits while we're trying to stop it, there's nothing
-	 left to do.  */
-      lwp = find_lwp_pid (pid_to_ptid (lwpid));
-      if (lwp == NULL)
-	return 0;
-    }
-
   /* If this process is stopped but is expecting a SIGSTOP, then make
      sure we take care of that now.  This isn't absolutely guaranteed
      to collect the SIGSTOP, but is fairly likely to.  */
@@ -838,8 +812,7 @@ linux_detach_one_lwp (struct inferior_list_entry *entry, void *args)
       int wstat;
       /* Clear stop_expected, so that the SIGSTOP will be reported.  */
       lwp->stop_expected = 0;
-      if (lwp->stopped)
-	linux_resume_one_lwp (lwp, 0, 0, NULL);
+      linux_resume_one_lwp (lwp, 0, 0, NULL);
       linux_wait_for_event (lwp->head.id, &wstat, __WALL);
     }
 
@@ -854,17 +827,6 @@ linux_detach_one_lwp (struct inferior_list_entry *entry, void *args)
   return 0;
 }
 
-static int
-any_thread_of (struct inferior_list_entry *entry, void *args)
-{
-  int *pid_p = args;
-
-  if (ptid_get_pid (entry->id) == *pid_p)
-    return 1;
-
-  return 0;
-}
-
 static int
 linux_detach (int pid)
 {
@@ -874,17 +836,37 @@ linux_detach (int pid)
   if (process == NULL)
     return -1;
 
+  /* Stop all threads before detaching.  First, ptrace requires that
+     the thread is stopped to sucessfully detach.  Second, thread_db
+     may need to uninstall thread event breakpoints from memory, which
+     only works with a stopped process anyway.  */
+  stop_all_lwps ();
+
 #ifdef USE_THREAD_DB
   thread_db_detach (process);
 #endif
 
-  current_inferior =
-    (struct thread_info *) find_inferior (&all_threads, any_thread_of, &pid);
-
-  delete_all_breakpoints ();
   find_inferior (&all_threads, linux_detach_one_lwp, &pid);
 
   the_target->mourn (process);
+
+  /* Since we presently can only stop all lwps of all processes, we
+     need to unstop lwps of other processes.  */
+  unstop_all_lwps (NULL);
+  return 0;
+}
+
+/* Remove all LWPs that belong to process PROC from the lwp list.  */
+
+static int
+delete_lwp_callback (struct inferior_list_entry *entry, void *proc)
+{
+  struct lwp_info *lwp = (struct lwp_info *) entry;
+  struct process_info *process = proc;
+
+  if (pid_of (lwp) == pid_of (process))
+    delete_lwp (lwp);
+
   return 0;
 }
 
@@ -897,6 +879,8 @@ linux_mourn (struct process_info *process)
   thread_db_mourn (process);
 #endif
 
+  find_inferior (&all_lwps, delete_lwp_callback, process);
+
   /* Freeing all private data.  */
   priv = process->private;
   free (priv->arch_private);
@@ -1695,10 +1679,6 @@ retry:
     {
       if (WIFEXITED (w) || WIFSIGNALED (w))
 	{
-	  delete_lwp (event_child);
-
-	  current_inferior = NULL;
-
 	  if (WIFEXITED (w))
 	    {
 	      ourstatus->kind = TARGET_WAITKIND_EXITED;
diff --git a/gdb/gdbserver/mem-break.c b/gdb/gdbserver/mem-break.c
index c796948ec88..3d7382ecdea 100644
--- a/gdb/gdbserver/mem-break.c
+++ b/gdb/gdbserver/mem-break.c
@@ -716,16 +716,24 @@ delete_all_breakpoints (void)
     delete_breakpoint_1 (proc, proc->breakpoints);
 }
 
-/* Release all breakpoints, but do not try to un-insert them from the
-   inferior.  */
+/* Clear the "inserted" flag in all breakpoints.  */
 
 void
-free_all_breakpoints (struct process_info *proc)
+mark_breakpoints_out (struct process_info *proc)
 {
   struct raw_breakpoint *raw_bp;
 
   for (raw_bp = proc->raw_breakpoints; raw_bp != NULL; raw_bp = raw_bp->next)
     raw_bp->inserted = 0;
+}
+
+/* Release all breakpoints, but do not try to un-insert them from the
+   inferior.  */
+
+void
+free_all_breakpoints (struct process_info *proc)
+{
+  mark_breakpoints_out (proc);
 
   /* Note: use PROC explicitly instead of deferring to
      delete_all_breakpoints --- CURRENT_INFERIOR may already have been
diff --git a/gdb/gdbserver/mem-break.h b/gdb/gdbserver/mem-break.h
index b9fcf651c4c..0bd86b5d89b 100644
--- a/gdb/gdbserver/mem-break.h
+++ b/gdb/gdbserver/mem-break.h
@@ -103,6 +103,10 @@ void set_breakpoint_data (const unsigned char *bp_data, int bp_len);
 
 void delete_all_breakpoints (void);
 
+/* Clear the "inserted" flag in all breakpoints of PROC.  */
+
+void mark_breakpoints_out (struct process_info *proc);
+
 /* Delete all breakpoints, but do not try to un-insert them from the
    inferior.  */
 
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 460cb46beca..ffca3977f39 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -3031,7 +3031,10 @@ handle_target_event (int err, gdb_client_data client_data)
 
       if (last_status.kind == TARGET_WAITKIND_EXITED
 	  || last_status.kind == TARGET_WAITKIND_SIGNALLED)
-	mourn_inferior (process);
+	{
+	  mark_breakpoints_out (process);
+	  mourn_inferior (process);
+	}
 
       if (forward_event)
 	{
diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c
index 8e7d7a9361a..7dec30dafab 100644
--- a/gdb/gdbserver/thread-db.c
+++ b/gdb/gdbserver/thread-db.c
@@ -52,6 +52,16 @@ struct thread_db
   void *handle;
 #endif
 
+  /* Thread creation event breakpoint.  The code at this location in
+     the child process will be called by the pthread library whenever
+     a new thread is created.  By setting a special breakpoint at this
+     location, GDB can detect when a new thread is created.  We obtain
+     this location via the td_ta_event_addr call.  Note that if the
+     running kernel supports tracing clones, then we don't need to use
+     (and in fact don't use) this magic thread event breakpoint to
+     learn about threads.  */
+  struct breakpoint *td_create_bp;
+
   /* Addresses of libthread_db functions.  */
   td_err_e (*td_ta_new_p) (struct ps_prochandle * ps, td_thragent_t **ta);
   td_err_e (*td_ta_event_getmsg_p) (const td_thragent_t *ta,
@@ -205,7 +215,7 @@ thread_db_create_event (CORE_ADDR where)
 }
 
 static int
-thread_db_enable_reporting ()
+thread_db_enable_reporting (void)
 {
   td_thr_events_t events;
   td_notify_t notify;
@@ -239,8 +249,9 @@ thread_db_enable_reporting ()
 	       thread_db_err_str (err));
       return 0;
     }
-  set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr,
-		     thread_db_create_event);
+  thread_db->td_create_bp
+    = set_breakpoint_at ((CORE_ADDR) (unsigned long) notify.u.bptaddr,
+			 thread_db_create_event);
 
   return 1;
 }
@@ -501,6 +512,8 @@ thread_db_load_search (void)
   if (proc->private->thread_db != NULL)
     fatal ("unexpected: proc->private->thread_db != NULL");
 
+  memset (&tdb, 0, sizeof (tdb));
+
   tdb.td_ta_new_p = &td_ta_new;
 
   /* Attempt to open a connection to the thread library.  */
@@ -544,6 +557,8 @@ try_thread_db_load_1 (void *handle)
   if (proc->private->thread_db != NULL)
     fatal ("unexpected: proc->private->thread_db != NULL");
 
+  memset (&tdb, 0, sizeof (tdb));
+
   tdb.handle = handle;
 
   /* Initialize pointers to the dynamic library functions we will use.
@@ -766,6 +781,16 @@ any_thread_of (struct inferior_list_entry *entry, void *args)
   return 0;
 }
 
+static void
+switch_to_process (struct process_info *proc)
+{
+  int pid = pid_of (proc);
+
+  current_inferior =
+    (struct thread_info *) find_inferior (&all_threads,
+					  any_thread_of, &pid);
+}
+
 /* Disconnect from libthread_db and free resources.  */
 
 static void
@@ -785,15 +810,10 @@ disable_thread_event_reporting (struct process_info *proc)
 
       if (td_ta_clear_event_p != NULL)
 	{
-	  struct thread_info *saved_inferior;
+	  struct thread_info *saved_inferior = current_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);
+	  switch_to_process (proc);
 
 	  /* Set the process wide mask saying we aren't interested
 	     in any events anymore.  */
@@ -805,10 +825,34 @@ disable_thread_event_reporting (struct process_info *proc)
     }
 }
 
+static void
+remove_thread_event_breakpoints (struct process_info *proc)
+{
+  struct thread_db *thread_db = proc->private->thread_db;
+
+  if (thread_db->td_create_bp != NULL)
+    {
+      struct thread_info *saved_inferior = current_inferior;
+
+      switch_to_process (proc);
+
+      delete_breakpoint (thread_db->td_create_bp);
+      thread_db->td_create_bp = NULL;
+
+      current_inferior = saved_inferior;
+    }
+}
+
 void
 thread_db_detach (struct process_info *proc)
 {
-  disable_thread_event_reporting (proc);
+  struct thread_db *thread_db = proc->private->thread_db;
+
+  if (thread_db)
+    {
+      disable_thread_event_reporting (proc);
+      remove_thread_event_breakpoints (proc);
+    }
 }
 
 /* Disconnect from libthread_db and free resources.  */
-- 
cgit v1.2.1