Implement attach/detach for multi-threaded programs on Linux.
authorMark Kettenis <kettenis@gnu.org>
Sun, 6 May 2001 17:00:44 +0000 (17:00 +0000)
committerMark Kettenis <kettenis@gnu.org>
Sun, 6 May 2001 17:00:44 +0000 (17:00 +0000)
* thread-db.c (keep_thread_db): Adjust comment.
(deactivate_target): Removed.
(thread_db_new_objfile): Don't call deactivate_target.  Implement
guts of deactivate_target inline instead.
(attach_thread): Call ATTACH_LWP unconditionally if defined.
(thread_db_attach): New function.
(thread_db_detach): Don't call deactivate_target.  Do necessary
cleanup inline instead.  Set inferior_ptid to LWP corresponding to
the current user-level thread.
(thread_db_kill): Set inferior_ptid to LWP corresponding to the
current user-level thread.
(thread_db_create_inferior): Deactivate target vector if
KEEP_THREAD_DB is zero.
(thread_db_mourn_inferior): Don't call deactivate_target.  Do
necessary cleanup inline instead.
(init_thread_db_ops): Initialize to_attach field to
thread_db_attach.
* lin-lwp.c (lin_lwp_mourn_inferior): Remove prototype.
(stop_wait_callback): Add prototype.
(init_lwp_list): Add comment about when to re-initialize the LWP
list.
(lin_lwp_attach_lwp): Only call ptrace for cloned processes.
Avoid adding publicates to the LWP list.  Only mark an LWP as
signalled if it doesn't correspond to a cloned process.
(lin_lwp_attach): Add initial process to the LWP list.  Make sure
it's stopped and fake a SIGSTOP.
(detach_callback): New function.
(lin_lwp_detach): Implement.
(lin_lwp_create_inferior): Don't re-initialize LWP list here.
Call child_ops.to_create_inferior directly instead of via
target_beneath local.
(lin_lwp_mourn_inferior): Call child_ops.to_mourn_inferior
directly instead of via target_beneath local.

gdb/ChangeLog
gdb/lin-lwp.c
gdb/thread-db.c

index fd5483ba33e2c38ca20794bc4a388b4a55f255e8..28555d9c16017cad26dd38f001393134193f6b5f 100644 (file)
@@ -1,3 +1,40 @@
+2001-05-06  Mark Kettenis  <kettenis@gnu.org>
+
+       Implement attach/detach for multi-threaded programs on Linux.
+       * thread-db.c (keep_thread_db): Adjust comment.
+       (deactivate_target): Removed.
+       (thread_db_new_objfile): Don't call deactivate_target.  Implement
+       guts of deactivate_target inline instead.
+       (attach_thread): Call ATTACH_LWP unconditionally if defined.
+       (thread_db_attach): New function.
+       (thread_db_detach): Don't call deactivate_target.  Do necessary
+       cleanup inline instead.  Set inferior_ptid to LWP corresponding to
+       the current user-level thread.
+       (thread_db_kill): Set inferior_ptid to LWP corresponding to the
+       current user-level thread.
+       (thread_db_create_inferior): Deactivate target vector if
+       KEEP_THREAD_DB is zero.
+       (thread_db_mourn_inferior): Don't call deactivate_target.  Do
+       necessary cleanup inline instead.
+       (init_thread_db_ops): Initialize to_attach field to
+       thread_db_attach.
+       * lin-lwp.c (lin_lwp_mourn_inferior): Remove prototype.
+       (stop_wait_callback): Add prototype.
+       (init_lwp_list): Add comment about when to re-initialize the LWP
+       list.
+       (lin_lwp_attach_lwp): Only call ptrace for cloned processes.
+       Avoid adding publicates to the LWP list.  Only mark an LWP as
+       signalled if it doesn't correspond to a cloned process.
+       (lin_lwp_attach): Add initial process to the LWP list.  Make sure
+       it's stopped and fake a SIGSTOP.
+       (detach_callback): New function.
+       (lin_lwp_detach): Implement.
+       (lin_lwp_create_inferior): Don't re-initialize LWP list here.
+       Call child_ops.to_create_inferior directly instead of via
+       target_beneath local.
+       (lin_lwp_mourn_inferior): Call child_ops.to_mourn_inferior
+       directly instead of via target_beneath local.
+
 2001-05-06  Eli Zaretskii  <eliz@is.elta.co.il>
 
        * symtab.c (lookup_symtab_1, lookup_partial_symtab): Use basename
index 6df4d0dab3e21cb5e0577c993b90a3f9372c6def..58ad3c5857ee52b267d5b422622eca45e6dbaf3c 100644 (file)
@@ -151,10 +151,13 @@ static sigset_t blocked_mask;
 \f
 
 /* Prototypes for local functions.  */
-static void lin_lwp_mourn_inferior (void);
+static int stop_wait_callback (struct lwp_info *lp, void *data);
 \f
 
-/* Initialize the list of LWPs.  */
+/* Initialize the list of LWPs.  Note that this module, contrary to
+   what GDB's generic threads layer does for its thread list,
+   re-initializes the LWP lists whenever we mourn or detach (which
+   doesn't involve mourning) the inferior.  */
 
 static void
 init_lwp_list (void)
@@ -344,26 +347,96 @@ lin_lwp_attach_lwp (ptid_t ptid, int verbose)
   if (verbose)
     printf_filtered ("[New %s]\n", target_pid_to_str (ptid));
 
-  if (ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
+  /* We assume that we're already tracing the initial process.  */
+  if (is_cloned (ptid) && ptrace (PTRACE_ATTACH, GET_LWP (ptid), 0, 0) < 0)
     error ("Can't attach %s: %s", target_pid_to_str (ptid), strerror (errno));
 
-  lp = add_lwp (ptid);
-  lp->signalled = 1;
+  lp = find_lwp_pid (ptid);
+  if (lp == NULL)
+    lp = add_lwp (ptid);
+
+  if (is_cloned (ptid))
+    lp->signalled = 1;
 }
 
 static void
 lin_lwp_attach (char *args, int from_tty)
 {
+  struct lwp_info *lp;
+
   /* FIXME: We should probably accept a list of process id's, and
      attach all of them.  */
-  error("Not implemented yet");
+  child_ops.to_attach (args, from_tty);
+
+  /* Add the initial process as the first LWP to the list.  */
+  lp = add_lwp (BUILD_LWP (inferior_ptid, inferior_ptid));
+
+  /* Make sure the initial process is stopped.  The user-level threads
+     layer might want to poke around in the inferior, and that won't
+     work if things haven't stabilized yet.  */
+  lp->signalled = 1;
+  stop_wait_callback (lp, NULL);
+  gdb_assert (lp->status == 0);
+
+  /* Fake the SIGSTOP that core GDB expects.  */
+  lp->status = W_STOPCODE (SIGSTOP);
+}
+
+static int
+detach_callback (struct lwp_info *lp, void *data)
+{
+  gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status));
+
+  if (debug_lin_lwp && lp->status)
+    fprintf_unfiltered (gdb_stdlog, "Pending %s for LWP %d on detach.\n",
+                       strsignal (WSTOPSIG (lp->status)), GET_LWP (lp->ptid));
+
+  while (lp->signalled && lp->stopped)
+    {
+      if (ptrace (PTRACE_CONT, GET_LWP (lp->ptid), 0,
+                 WSTOPSIG (lp->status)) < 0)
+       error ("Can't continue %s: %s", target_pid_to_str (lp->ptid),
+              strerror (errno));
+
+      lp->stopped = 0;
+      lp->status = 0;
+      stop_wait_callback (lp, NULL);
+
+      gdb_assert (lp->status == 0 || WIFSTOPPED (lp->status));
+    }
+
+  if (is_cloned (lp->ptid))
+    {
+      if (ptrace (PTRACE_DETACH, GET_LWP (lp->ptid), 0,
+                 WSTOPSIG (lp->status)) < 0)
+       error ("Can't detach %s: %s", target_pid_to_str (lp->ptid),
+              strerror (errno));
+
+      delete_lwp (lp->ptid);
+    }
+
+  return 0;
 }
 
 static void
 lin_lwp_detach (char *args, int from_tty)
 {
-  /* FIXME: Provide implementation when we implement lin_lwp_attach.  */
-  error ("Not implemented yet");
+  iterate_over_lwps (detach_callback, NULL);
+
+  /* Only the initial (uncloned) process should be left right now.  */
+  gdb_assert (num_lwps == 1);
+
+  trap_ptid = null_ptid;
+
+  /* Destroy LWP info; it's no longer valid.  */
+  init_lwp_list ();
+
+  /* Restore the original signal mask.  */
+  sigprocmask (SIG_SETMASK, &normal_mask, NULL);
+  sigemptyset (&blocked_mask);
+
+  inferior_ptid = GET_PID (inferior_ptid);
+  child_ops.to_detach (args, from_tty);
 }
 \f
 
@@ -899,37 +972,22 @@ lin_lwp_kill (void)
 static void
 lin_lwp_create_inferior (char *exec_file, char *allargs, char **env)
 {
-  struct target_ops *target_beneath;
-
-  init_lwp_list ();
-
-#if 0
-  target_beneath = find_target_beneath (&lin_lwp_ops);
-#else
-  target_beneath = &child_ops;
-#endif
-  target_beneath->to_create_inferior (exec_file, allargs, env);
+  child_ops.to_create_inferior (exec_file, allargs, env);
 }
 
 static void  
 lin_lwp_mourn_inferior (void)
 {
-  struct target_ops *target_beneath;
+  trap_ptid = null_ptid;
 
+  /* Destroy LWP info; it's no longer valid.  */
   init_lwp_list ();
 
-  trap_ptid = null_ptid;
-
   /* Restore the original signal mask.  */
   sigprocmask (SIG_SETMASK, &normal_mask, NULL);
   sigemptyset (&blocked_mask);
 
-#if 0
-  target_beneath = find_target_beneath (&lin_lwp_ops);
-#else
-  target_beneath = &child_ops;
-#endif
-  target_beneath->to_mourn_inferior ();
+  child_ops.to_mourn_inferior ();
 }
 
 static void
index 8c1bdba4411c70fc43b90cb46f9f262bed0fc6e7..ab94429bb8e37c2ac74885f32336aedb1dc55caa 100644 (file)
@@ -56,7 +56,8 @@ static void (*target_new_objfile_chain) (struct objfile *objfile);
 /* Non-zero if we're using this module's target vector.  */
 static int using_thread_db;
 
-/* Non-zero if we musn't deactivate this module's target vector.  */
+/* Non-zero if we have to keep this module's target vector active
+   across re-runs.  */
 static int keep_thread_db;
 
 /* Non-zero if we have determined the signals used by the threads
@@ -506,20 +507,6 @@ disable_thread_signals (void)
 #endif
 }
 
-static void
-deactivate_target (void)
-{
-  /* Forget about the child's process ID.  We shouldn't need it
-     anymore.  */
-  proc_handle.pid = 0;
-
-  if (! keep_thread_db)
-    {
-      using_thread_db = 0;
-      unpush_target (&thread_db_ops);
-    }
-}
-
 static void
 thread_db_new_objfile (struct objfile *objfile)
 {
@@ -528,11 +515,15 @@ thread_db_new_objfile (struct objfile *objfile)
   if (objfile == NULL)
     {
       /* All symbols have been discarded.  If the thread_db target is
-         active, deactivate it now, even if the application was linked
-         statically against the thread library.  */
-      keep_thread_db = 0;
+         active, deactivate it now.  */
       if (using_thread_db)
-       deactivate_target ();
+       {
+         gdb_assert (proc_handle.pid == 0);
+         unpush_target (&thread_db_ops);
+         using_thread_db = 0;
+       }
+
+      keep_thread_db = 0;
 
       goto quit;
     }
@@ -612,8 +603,7 @@ attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
 
   /* Under Linux, we have to attach to each and every thread.  */
 #ifdef ATTACH_LWP
-  if (ti_p->ti_lid != GET_PID (ptid))
-    ATTACH_LWP (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid)), 0);
+  ATTACH_LWP (BUILD_LWP (ti_p->ti_lid, GET_PID (ptid)), 0);
 #endif
 
   /* Enable thread event reporting for this thread.  */
@@ -623,6 +613,23 @@ attach_thread (ptid_t ptid, const td_thrhandle_t *th_p,
           target_pid_to_str (ptid), thread_db_err_str (err));
 }
 
+static void
+thread_db_attach (char *args, int from_tty)
+{
+  target_beneath->to_attach (args, from_tty);
+
+  /* Destroy thread info; it's no longer valid.  */
+  init_thread_list ();
+
+  /* The child process is now the actual multi-threaded
+     program.  Snatch its process ID...  */
+  proc_handle.pid = GET_PID (inferior_ptid);
+
+  /* ...and perform the remaining initialization steps.  */
+  enable_thread_event_reporting ();
+  thread_db_find_new_threads();
+}
+
 static void
 detach_thread (ptid_t ptid, int verbose)
 {
@@ -634,7 +641,14 @@ static void
 thread_db_detach (char *args, int from_tty)
 {
   disable_thread_event_reporting ();
-  deactivate_target ();
+
+  /* There's no need to save & restore inferior_ptid here, since the
+     inferior is supposed to be survive this function call.  */
+  inferior_ptid = lwp_from_thread (inferior_ptid);
+
+  /* Forget about the child's process ID.  We shouldn't need it
+     anymore.  */
+  proc_handle.pid = 0;
 
   target_beneath->to_detach (args, from_tty);
 }
@@ -860,12 +874,21 @@ thread_db_store_registers (int regno)
 static void
 thread_db_kill (void)
 {
+  /* There's no need to save & restore inferior_ptid here, since the
+     inferior isn't supposed to survive this function call.  */
+  inferior_ptid = lwp_from_thread (inferior_ptid);
   target_beneath->to_kill ();
 }
 
 static void
 thread_db_create_inferior (char *exec_file, char *allargs, char **env)
 {
+  if (! keep_thread_db)
+    {
+      unpush_target (&thread_db_ops);
+      using_thread_db = 0;
+    }
+
   target_beneath->to_create_inferior (exec_file, allargs, env);
 }
 
@@ -888,7 +911,10 @@ static void
 thread_db_mourn_inferior (void)
 {
   remove_thread_event_breakpoints ();
-  deactivate_target ();
+
+  /* Forget about the child's process ID.  We shouldn't need it
+     anymore.  */
+  proc_handle.pid = 0;
 
   target_beneath->to_mourn_inferior ();
 }
@@ -996,6 +1022,7 @@ init_thread_db_ops (void)
   thread_db_ops.to_shortname = "multi-thread";
   thread_db_ops.to_longname = "multi-threaded child process.";
   thread_db_ops.to_doc = "Threads and pthreads support.";
+  thread_db_ops.to_attach = thread_db_attach;
   thread_db_ops.to_detach = thread_db_detach;
   thread_db_ops.to_resume = thread_db_resume;
   thread_db_ops.to_wait = thread_db_wait;