This is the mail archive of the gdb-patches@sourceware.cygnus.com 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]

[RFA] Re: Linux threads improvements


   Date: Mon, 24 Apr 2000 09:17:06 -0700
   From: Michael Snyder <msnyder@cygnus.com>

   Mark, 

   As long as the patch applies cleanly, it's fine...
   I'll just look at the old and new side by side.
   If you can give me some little hints like "this
   function does what that one used to do" that would
   be great...

Ok.  Included here is a patch.  The ChangeLog entry should give you
enough hints to see how I moved things around.  I left out much of the
details for the functions that were largely rewritten.  I can make a
ChangeLog entry that's more precise, but I'm not sure if that would
serve any purpose.  I went over the code another time, adding some
more comments and reverting some of the gratitous changes that I
mentioned.  I'll be pestering you with those somewhere in the future
:-).

I've added some FIXME's to document issues that I don't want to deal
with right now.  There's one thing I'd like you to take a bit closer
look at.  I added a call to remove_thread_event_breakpoint() in
thread_db_mourn_inferior(), since I caught GDB trying to set those
breakpoints when restarting and complaining loudly about it.  The
other threads implementation seem to do this in that case, but I'm not
100% sure if that's exactly the right place, or if I'm still missing
places where this should be done.

We might consider adding this to the release branch too, since it
fixes a quite visible bug.

Mark


2000-04-25  Mark Kettenis  <kettenis@gnu.org>

	Make Linux libthread_db-assisted thread debuging module use GDB's
	internal thread list instead of rolling its own.  Take advantage
	of the thread event reporting facility, and make it handle exiting
	threads.  Improve documentation and do some minor cleanups.
	* lin-thread.c (struct private_thread_info): New definition.
	(supply_gregset, fill_gregset, supply_fpregset, fill_fpregset):
	Add missing prototypes.
	(struct ps_prochandle): Change type of pid member to `pid_t'.  Add
	member stopped.
	(THREADINFO, threadinfo): Removed struct and typedef.
	(threadlist, threadlist_max, threadlist_top): Removed variables.
	(THREADLIST_ALLOC): Removed define.
	(insert_thread, empty_threadlist, next_pending_event,
	threadlist_iter): Removed functions.
	(pid_to_thread, pid_to_lwp): New functions.
	(attach_pid): Removed.
	(enable_thread_event_reporting): Disable TD_DEATH event
	reporting.  Cast notify.ubptaddr to CORE_ADDR where necessary.
	(check_for_thread_event): Removed function.
	(thread_db_unpush_target): Add prototype for
	linuxthreads_discard_global_state.
	(thread_db_new_objfile): Adapt to use GDB's internal thread list.
	(thread_db_pid_to_str): Cast thread id to `long'.  Adapt printf
	format string accordingly.
	(handle_new_thread): Removed.  Moved functionality into ...
	(add_new_thread): ... new function.
	(find_new_threads_callback, new_resume_thread_callback): Removed.
	Moved functionality into ...
	(td_find_new_threads_callback): ... new function.
	(thread_db_find_new_threads): Rewrite to use GDB's internal thread
	list and td_find_new_threads_callback callback function.
	(thread_resume_callback): Moved functionality into ...
	(resume_callback): ... new function.
	(last_resume_pid, last_resume_step, last_resume_signo): Removed
	variables.
	(thread_db_resume): Rename to ...
	(lin_thread_resume): ... and rewrite to use GDB's internal thread
	list and resume_callback callback function.
	(stop_or_attach_thread_callback): Removed.  Moved part of the
	functionality into ...
	(stop_callback): ... new function.  Only stops a thread.
	(wait_for_stop): Change type of argument to `lwpid_t'.  Add code
	to deal with exiting threads.
	(wait_thread_callback): Removed.  Moved functionality into ...
	(wait_callback): ... new function.
	(delete_old_thread): New function.
	(check_event): New function.
	(child_wait): New function.
	(lin_thread_wait): Renamed from thread_db_wait.  Reimplemented to
	use GDB's internal thread list.  Adapt to deal with exiting
	kernel threads.
	(thread_db_mourn_inferior): Add call to
	remove_thread_event_breakpoints.
	(init_thread_db_ops): Use lin_thread_wait and lin_thread_resume
	instead of thread_db_wait and thread_db_resume.


Index: lin-thread.c
===================================================================
RCS file: /cvs/src/src/gdb/lin-thread.c,v
retrieving revision 1.4
diff -u -p -r1.4 lin-thread.c
--- lin-thread.c	2000/04/14 10:13:50	1.4
+++ lin-thread.c	2000/04/25 22:24:07
@@ -1,6 +1,5 @@
-/* Multi-threaded debugging support for the thread_db interface,
-   used on operating systems such as Solaris and Linux.
-   Copyright 1999 Free Software Foundation, Inc.
+/* Multi-threaded debugging support Linux (using the thread_db interface).
+   Copyright 1999, 2000 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -36,11 +35,9 @@
    the relationship between kernel LWP threads and user (eg. posix)
    threads.
 
-   Linux threads are likely to be different -- but the thread_db 
-   library API should make the difference largely transparent to GDB.
+   Linux threads are different -- but the thread_db library API should
+   make the difference largely transparent to GDB.  */
 
-   */
-
 /* The thread_db API provides a number of functions that give the caller
    access to the inner workings of the child process's thread library. 
    We will be using the following (others may be added):
@@ -125,6 +122,11 @@
 
 #include <dlfcn.h>		/* dynamic library interface */
 
+/* kettenis/2000-04-15: I'd like to keep the assertions in for my own
+   debugging purposes, but I disable them for everybody else :-).  */
+#define NDEBUG
+#include <assert.h>
+
 #ifndef TIDGET
 #define TIDGET(PID)		(((PID) & 0x7fffffff) >> 16)
 #define PIDGET(PID)		(((PID) & 0xffff))
@@ -140,6 +142,59 @@
 #define BUILD_LWP(TID, PID)	MERGEPID (PID, TID)
 #define BUILD_THREAD(TID, PID)	(MERGEPID (PID, TID) | THREAD_FLAG)
 
+/* On Linux, threads are implemented as multiple processes running
+   within the same VM space.  Each kernel thread has its own process
+   id.  This is quite distinct from Solaris where each process has a
+   single process id, that can consist of several light weight
+   processes.  There's also the concept of user-space threads, which
+   are in a way completely orthogonal to kernel threads.
+
+   In a way, GDB sees all these entities as processes.  Right now GDB
+   stores these an `int', but this will hopefully change in the near
+   future to something more sensible.  Here is a list:
+
+   * The POSIX process id.  Represented here by variables of type
+     `pid_t'.  This is the id of the initial kernel thread in a Linux
+     VM space.  When referring to the whole process, GDB simply uses
+     the POSIX process id.
+   
+   * A light weight process LWP.  Represented here by variables of
+     type `lwpid_t'.  When referring to a particular kernel thread,
+     GDB combines the POSIX process id and the LWP id (see the
+     definition of the BUILD_LWP macro above).  On Linux, we use the
+     Linux process id as LWP id.  This has some strange consequences
+     for the initial thread (left as an exercise to the reader).
+
+   * User-space threads.  Represented here by variables of `thread_t'.
+     When referring to a particular user-space thread, GDB combines
+     the POSIX process id with the user-space thread id, and sets a
+     special flag (see the definition of the BUILD_THREAD macro
+     above). On Linux, the only supported user-space threads library
+     is the LinuxThreads library (distributed together with glibc).
+     In this implementation user-space threads are always bound to a
+     single LWP.  But the code here tries to avoid making that
+     assumption.  */
+
+/* For every user-space thread (those "process" ids that have
+   THREAD_FLAG set), in GDB's thread list, we store the following
+   private data.  */
+
+struct private_thread_info
+{
+  /* Cached LWP id of the kernel thread this user-space thread is
+     bound to.  */
+  lwpid_t lwpid;
+
+  /* Non-zero if we sent SIGSTOP to this kernel thread. */
+  int signalled;
+
+  /* Non-zero if the thread is stopped.  */
+  int stopped;
+
+  /* Non-zero if this thread has exited.  */
+  int exited;
+};
+
 /*
  * target_beneath is a pointer to the target_ops underlying this one.
  */
@@ -180,6 +235,15 @@ typedef elf_fpregset_t gdb_prfpregset_t;
 typedef prfpregset_t gdb_prfpregset_t;
 #endif
 
+/* Note that these prototypes differ slightly from those used
+   elsewhere in GDB.  */
+
+extern void supply_gregset (const prgregset_t);
+extern void fill_gregset (prgregset_t, int);
+extern void supply_fpregset (const gdb_prfpregset_t *);
+extern void fill_fpregset (gdb_prfpregset_t *, int);
+
+
 /* 
  * proc_service callback functions, called by thread_db.
  */
@@ -317,13 +381,26 @@ static struct cleanup *save_inferior_pid
 static void            restore_inferior_pid (void *saved_pid);
 static char *thr_err_string   (td_err_e);
 static char *thr_state_string (td_thr_state_e);
+
 
-struct ps_prochandle {
-  int pid;
+/* We identify a process by its process id (which on Linux is the
+   process id of the initial kernel thread).  On Linux we need to keep
+   track if that thread is stopped.  In that case, store the
+   information here too.  */
+
+struct ps_prochandle
+{
+  pid_t pid;			/* The process id.  */
+  int stopped;			/* True if that process is stopped.  */
 };
 
+/* The external process handler ("main prochandle") that's used by the
+   threads debugging library to call our process service interfaces.  */
 struct ps_prochandle main_prochandle;
-td_thragent_t *      main_threadagent;
+
+/* The internal process handler associated with it.  */
+td_thragent_t * main_threadagent;
+
 
 /* 
  * Common proc_service routine for reading and writing memory.  
@@ -452,8 +529,7 @@ ps_lgetLDT (gdb_ps_prochandle_t ph, lwpi
   extern struct ssd *procfs_find_LDT_entry (int);
   struct ssd *ret;
 
-  ret = procfs_find_LDT_entry (BUILD_LWP (lwpid, 
-					  PIDGET (main_prochandle.pid)));
+  ret = procfs_find_LDT_entry (BUILD_LWP (lwpid, main_prochandle.pid));
   if (ret)
     {
       memcpy (pldt, ret, sizeof (struct ssd));
@@ -770,86 +846,91 @@ thr_state_string (statecode)
   }
 }
 
-/*
- * Local thread/event list.
- * This data structure will be used to hold a list of threads and 
- * pending/deliverable events.
- */
-
-typedef struct THREADINFO {
-  thread_t       tid;		/* thread ID */
-  pid_t          lid;		/* process/lwp ID */
-  td_thr_state_e state;		/* thread state (a la thread_db) */
-  td_thr_type_e  type;		/* thread type (a la thread_db) */
-  int            pending;	/* true if holding a pending event */
-  int            status;	/* wait status of any interesting event */
-} threadinfo;
-
-threadinfo * threadlist;
-int threadlist_max = 0;		/* current size of table */
-int threadlist_top = 0;		/* number of threads now in table */
-#define THREADLIST_ALLOC 100	/* chunk size by which to expand table */
-
-static threadinfo *
-insert_thread (tid, lid, state, type)
-     int            tid;
-     int            lid;
-     td_thr_state_e state;
-     td_thr_type_e  type;
-{
-  if (threadlist_top >= threadlist_max)
-    {
-      threadlist_max += THREADLIST_ALLOC;
-      threadlist      = realloc (threadlist, 
-				 threadlist_max * sizeof (threadinfo));
-      if (threadlist == NULL)
-	return NULL;
-    }
-  threadlist[threadlist_top].tid     = tid;
-  threadlist[threadlist_top].lid     = lid;
-  threadlist[threadlist_top].state   = state;
-  threadlist[threadlist_top].type    = type;
-  threadlist[threadlist_top].pending = 0;
-  threadlist[threadlist_top].status  = 0;
+/* Convert PID, which may be a bare LWP id or process id, or an LWP id
+   in GDB's multiplexed format, to a thread id in GDB's multiplexed
+   format.  If it isn't recognized by the threads layer, or if it
+   already is a thread id, simply return PID itself.  */
 
-  return &threadlist[threadlist_top++];
-}
-
-static void
-empty_threadlist ()
+static int
+pid_to_thread (int pid)
 {
-  threadlist_top = 0;
-}
+  td_thrinfo_t ti;
+  td_thrhandle_t th;
+  td_err_e err;
+  lwpid_t lwpid;
 
-static threadinfo *
-next_pending_event ()
-{
-  int i;
+  assert (pid != -1);
+
+  if (is_thread (pid))
+    return pid;			/* It's already a thread id.  */
 
-  for (i = 0; i < threadlist_top; i++)
-    if (threadlist[i].pending)
-      return &threadlist[i];
+  lwpid = GET_LWP (pid);
+  if (lwpid == 0)
+    lwpid = pid;		/* It's a process id.  */
 
-  return NULL;
+  err = p_td_ta_map_lwp2thr (main_threadagent, lwpid, &th);
+  if (err != TD_OK)
+    {
+      if (err == TD_NOTHR)
+	return pid;		/* It's unrelated to a user-level thread.  */
+      else
+	error ("pid_to_thread: td_ta_map_lwp2thr: %s", thr_err_string (err));
+    }
+
+  err = p_td_thr_get_info (&th, &ti);
+  if (err != TD_OK)
+    error ("pid_to_thread: td_thr_get_info: %s", thr_err_string (err));
+
+  return BUILD_THREAD (ti.ti_tid, main_prochandle.pid);
 }
 
-static void
-threadlist_iter (func, data, state, type)
-     int (*func) ();
-     void *data;
-     td_thr_state_e state;
-     td_thr_type_e  type;
+/* Convert PID, which may be a bare LWP id or a thread id in GDB's
+   multiplexed format, to a LWP id in GDB's multiplexed format.  If it
+   isn't recognized by the threads layer, or if it already is a LWP
+   id, simply return PID itself.  */
+
+static int
+pid_to_lwp (int pid)
 {
-  int i;
+  td_thrinfo_t ti;
+  td_thrhandle_t th;
+  td_err_e err;
 
-  for (i = 0; i < threadlist_top; i++)
-    if ((state == TD_THR_ANY_STATE || state == threadlist[i].state) &&
-	(type  == TD_THR_ANY_TYPE  || type  == threadlist[i].type))
-      if ((*func) (&threadlist[i], data) != 0)
-	break;
+  assert (pid != -1);
 
-  return;
-}     
+  if (is_thread (pid))
+    {
+      thread_t tid = GET_THREAD (pid);
+
+      err = p_td_ta_map_id2thr (main_threadagent, tid, &th);
+      if (err != TD_OK)
+	error ("pid_to_lwp: td_ta_map_id2thr: %s", thr_err_string (err));
+
+      err = p_td_thr_get_info (&th, &ti);
+      if (err != TD_OK)
+	error ("pid_to_lwp: td_thr_get_info: %s", thr_err_string (err));
+
+      return BUILD_LWP (ti.ti_lid, PIDGET (pid));
+    }
+
+  if (GET_LWP (pid))
+    return pid;			/* It's already a LWP id.  */
+
+  err = p_td_ta_map_lwp2thr (main_threadagent, pid, &th);
+  if (err != TD_OK)
+    {
+      if (err == TD_NOTHR)
+	return pid;		/* It's unrelated to a user-level thread.  */
+      else
+	error ("pid_to_lwp: td_thr_map_lwp2thr: %s", thr_err_string (err));
+    }
+
+  err = p_td_thr_get_info (&th, &ti);
+  if (err != TD_OK)
+    error ("pid_to_thread: td_thr_get_info: %s", thr_err_string (err));
+
+  return BUILD_LWP (ti.ti_lid, main_prochandle.pid);
+}
 
 /*
  * Global state
@@ -858,21 +939,17 @@ threadlist_iter (func, data, state, type
  */
 
 /* This flag is set when we activate, so that we don't do it twice. 
-   Defined in linux-thread.c and used for inter-target syncronization.  */
+   Defined in linux-thread.c and used for inter-target synchronization.  */
 extern int using_thread_db;
 
 /* The process id for which we've stopped.
- * This is only set when we actually stop all threads.
- * Otherwise it's zero.
- */
-static int event_pid;
-
-/*
- * The process id for a new thread to which we've just attached.
- * This process needs special handling at resume time.
- */
-static int attach_pid;
 
+   FIXME: kettenis/2000-04-24: I think we can get rid of this global
+   variable.  All places where we set this variable in the code below
+   are #ifdef'ed out.  If time passes and nobody complains, remove it
+   altogether.  */
+static int event_pid;
+
 
 /*
  * thread_db event handling:
@@ -923,7 +1000,12 @@ enable_thread_event_reporting (ta)
   /* set process wide mask saying which events we are interested in */
   td_event_emptyset (&events);
   td_event_addset (&events, TD_CREATE);
+#if 0
+  /* FIXME: kettenis/2000-04-23: The event reporting facility is
+     broken for TD_DEATH events in glibc 2.1.3, so don't enable it for
+     now.  */
   td_event_addset (&events, TD_DEATH);
+#endif
 
   if (p_td_ta_set_event (ta, &events) != TD_OK)
     {
@@ -944,10 +1026,10 @@ enable_thread_event_reporting (ta)
     }
 
   /* Set up the breakpoint. */
-  create_thread_event_breakpoint (notify.u.bptaddr);
+  create_thread_event_breakpoint ((CORE_ADDR) notify.u.bptaddr);
 
   /* Save it's location. */
-  thread_creation_bkpt_address = notify.u.bptaddr;
+  thread_creation_bkpt_address = (CORE_ADDR) notify.u.bptaddr;
 
   /* thread death */
   /* get breakpoint location */
@@ -957,10 +1039,10 @@ enable_thread_event_reporting (ta)
       return;
     }
   /* Set up the breakpoint. */
-  create_thread_event_breakpoint (notify.u.bptaddr);
+  create_thread_event_breakpoint ((CORE_ADDR) notify.u.bptaddr);
 
   /* Save it's location. */
-  thread_death_bkpt_address = notify.u.bptaddr;
+  thread_death_bkpt_address = (CORE_ADDR) notify.u.bptaddr;
 }
 
 /* This function handles the global parts of disabling thread events.
@@ -981,21 +1063,7 @@ disable_thread_event_reporting (ta)
   thread_creation_bkpt_address = 0;
   thread_death_bkpt_address = 0;
 }
-
-/* check_for_thread_event
-   
-   if it's a thread event we recognize (currently
-   we only recognize creation and destruction
-   events), return 1; else return 0.  */
-
-
-static int
-check_for_thread_event (struct target_waitstatus *tws, int event_pid)
-{
-  /* FIXME: to be more efficient, we should keep a static 
-     list of threads, and update it only here (with td_ta_thr_iter). */
-}
-
+
 static void
 thread_db_push_target (void)
 {
@@ -1013,13 +1081,13 @@ thread_db_push_target (void)
 static void
 thread_db_unpush_target (void)
 {
+  extern void linuxthreads_discard_global_state (void);
+
   /* Must be called whenever we remove ourself from the target stack! */
 
   using_thread_db = 0;
   target_beneath = NULL;
 
-  /* delete local list of threads */
-  empty_threadlist ();
   /* Turn off the thread_db API.  */
   p_td_ta_delete (main_threadagent);
   /* Unpush this target vector */
@@ -1051,63 +1119,71 @@ thread_db_unpush_target (void)
  * ourself onto the target stack.
  */
 
-static void (*target_new_objfile_chain)   (struct objfile *objfile);
-static int stop_or_attach_thread_callback (const td_thrhandle_t *th, 
-					   void *data);
-static int wait_thread_callback           (const td_thrhandle_t *th, 
-					   void *data);
+static int stop_callback (struct thread_info *, void *);
+static int wait_callback (struct thread_info *, void *);
+static int td_find_new_threads_callback (const td_thrhandle_t *, void *);
 
+static void (*target_new_objfile_chain)(struct objfile *objfile);
+
 static void
 thread_db_new_objfile (struct objfile *objfile)
 {
-  td_err_e   ret;
-  
-  if (using_thread_db)			/* libthread already detected, and */
-    goto quit;				/* thread target vector activated. */
+  td_err_e err;
+
+  if (using_thread_db)
+    /* Nothing to do.  The thread library was already detected and the
+       thread target vector was already activated.  */
+    goto quit;
 
   if (objfile == NULL)
-    goto quit;	/* un-interesting object file */
+    goto quit;			/* Un-interesting object file.  */
 
-  /* Initialize our "main prochandle" with the main inferior pid.  */
+  /* Initialize our "main prochandle" with the inferior process id.  */
   main_prochandle.pid = PIDGET (inferior_pid);
+  main_prochandle.stopped = 1;
 
-  /* Now attempt to open a thread_db connection to the 
-     thread library running in the child process.  */
-  ret = p_td_ta_new (&main_prochandle, &main_threadagent);
-  switch (ret) {
-  default:
-    warning ("Unexpected error initializing thread_db: %s", 
-	     thr_err_string (ret));
-    break;
-  case TD_NOLIBTHREAD:	/* expected: no libthread in child process (yet) */
-    break;	
-  case TD_OK:		/* libthread detected in child: we go live now! */
-    thread_db_push_target ();
-    event_pid = inferior_pid;	/* for resume */
-
-    /* Now stop everyone else, and attach any new threads you find.  */
-    p_td_ta_thr_iter (main_threadagent, 
-		      stop_or_attach_thread_callback,
-		      (void *) 0,
-		      TD_THR_ANY_STATE,
-		      TD_THR_LOWEST_PRIORITY,
-		      TD_SIGNO_MASK,
-		      TD_THR_ANY_USER_FLAGS);
-
-    /* Now go call wait on all the threads you've stopped:
-       This allows us to absorb the SIGKILL event, and to make sure
-       that the thread knows that it is stopped (Linux peculiarity).  */
-    p_td_ta_thr_iter (main_threadagent, 
-		      wait_thread_callback,
-		      (void *) 0,
-		      TD_THR_ANY_STATE,
-		      TD_THR_LOWEST_PRIORITY,
-		      TD_SIGNO_MASK,
-		      TD_THR_ANY_USER_FLAGS);
+  /* Now attempt to open a thread_db connection to the thread library
+     running in the child process.  */
+  err = p_td_ta_new (&main_prochandle, &main_threadagent);
+  switch (err)
+    {
+    case TD_NOLIBTHREAD:
+      /* No thread library found in the child process, probably
+         because the child process isn't running yet.  */
+      break;
+      
+    case TD_OK:
+      /* The thread library was detected in the child; we go live now!  */
+      thread_db_push_target ();
 
-    break;
+#if 0
+      /* Assign this event to the inferior process id.  */
+      event_pid = inferior_pid;
+#endif
+
+      /* Now stop all user-space threads ...  */
+      iterate_over_threads (stop_callback, NULL);
+
+      /* ... iterate over all user-space threads to discover new
+         threads and attach ...  */
+      err = p_td_ta_thr_iter (main_threadagent, td_find_new_threads_callback,
+			      NULL, TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+			      TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+      if (err != TD_OK)
+	error ("thread_db_find_new_threads: td_ta_thr_iter: %s",
+	       thr_err_string (err));
+
+      /* ... and wait until all of them have reported back that
+	 they're no longer running.  */
+      iterate_over_threads (wait_callback, NULL);
+      break;
+      
+    default:
+      warning ("thread_db_new_objfile: td_ta_new:  %s", thr_err_string (err));
+      break;
   }
-quit:
+  
+ quit:
   if (target_new_objfile_chain)
     target_new_objfile_chain (objfile);
 }
@@ -1213,9 +1289,9 @@ thread_db_pid_to_str (int pid)
 
       if (ti.ti_state == TD_THR_ACTIVE &&
 	  ti.ti_lid != 0)
-	sprintf (buf, "Thread %d (LWP %d)", ti.ti_tid, ti.ti_lid);
+	sprintf (buf, "Thread %ld (LWP %d)", (long) ti.ti_tid, ti.ti_lid);
       else
-	sprintf (buf, "Thread %d (%s)", ti.ti_tid,
+	sprintf (buf, "Thread %ld (%s)", (long) ti.ti_tid,
 		 thr_state_string (ti.ti_state));
     }
   else if (GET_LWP (pid))
@@ -1377,278 +1453,236 @@ thread_db_store_registers (regno)
       ret != TD_NOFPREGS)
     error ("store_registers: td_thr_setfpregs %s", thr_err_string (ret));
 }
+
 
+/* Add the thread with thread handle *TH_P and thread info *TI_P to
+   GDB's internal thread list, and attach to it.  If VERBOSE is
+   non-zero, print a message about the new thread.  */
+
 static void
-handle_new_thread (tid, lid, verbose)
-     int tid;	/* user thread id */
-     int lid;	/* kernel thread id */
-     int verbose;
+add_new_thread (const td_thrhandle_t *th_p,
+		const td_thrinfo_t *ti_p, int verbose)
 {
-  int gdb_pid = BUILD_THREAD (tid, main_prochandle.pid);
-  int wait_pid, wait_status;
+  struct thread_info *tp;
+  td_err_e err;
+  int pid;
+
+  pid = BUILD_THREAD (ti_p->ti_tid, main_prochandle.pid);
 
   if (verbose)
-    printf_filtered ("[New %s]\n", target_pid_to_str (gdb_pid));
-  add_thread (gdb_pid);
+    printf_filtered ("[New %s]\n", target_pid_to_str (pid));
+
+  tp = add_thread (pid);
+
+  tp->private = xmalloc (sizeof (struct private_thread_info));
+  tp->private->lwpid = ti_p->ti_lid;
+  tp->private->signalled = 0;
+  tp->private->stopped = 0;
+  tp->private->exited = 0;
 
-  if (lid != main_prochandle.pid)
+  if (ti_p->ti_lid != main_prochandle.pid)
     {
-      attach_thread (lid);
-      /* According to the Eric Paire model, we now have to send
-	 the restart signal to the new thread -- however, empirically,
-	 I do not find that to be necessary.  */
-      attach_pid = lid;
+      extern void attach_thread (int);
+
+      attach_thread (ti_p->ti_lid);
+
+      /* If we attach, a SIGSTOP is automatically sent.  */
+      tp->private->signalled = 1;
     }
+  else
+    {
+      /* The main thread is supposed to be stopped.  */
+      tp->private->stopped = 1;
+    }
+
+  /* Enable event reporting for this thread.  */
+  err = p_td_thr_event_enable (th_p, 1);
+  if (err != TD_OK)
+    error ("add_new_thread: td_thr_event_enable: %s", thr_err_string (err));
 }
 
+/* Remove the thread with thread handle *TH_P and thread info *TI_P
+   from GDB's internal thread list.  If VERBOSE is non-zero, print a
+   message about the thread having exited.
+   
+   FIXME: kettenis/2000-04-25: The thread isn't really removed from
+   the list but merely marked as having exited.  */
+
 static void
-test_for_new_thread (tid, lid, verbose)
-     int tid;
-     int lid;
-     int verbose;
+delete_old_thread (const td_thrhandle_t *th_p,
+		   const td_thrinfo_t *ti_p, int verbose)
 {
-  if (!in_thread_list (BUILD_THREAD (tid, main_prochandle.pid)))
-    handle_new_thread (tid, lid, verbose);
+  struct thread_info *tp;
+  int pid;
+
+  pid = BUILD_THREAD (ti_p->ti_tid, main_prochandle.pid);
+
+  if (verbose)
+    printf_filtered ("[Exited %s]\n", target_pid_to_str (pid));
+
+  /* FIMXME: kettenis/2000-04-23: Detaching the thread here doesn't
+     work.  We keep the thread around instead, but mark it as exited,
+     and never stop it again.  */
+  tp = find_thread_pid (pid);
+  tp->private->exited = 1;
 }
 
-/* 
- * Callback function that gets called once per USER thread 
- * (i.e., not kernel) thread by td_ta_thr_iter.
- */
+/* Check if TH corresponds to a new thread.  If it does, add it to
+   GDB's internal thread list and attach to it.  */
 
 static int
-find_new_threads_callback (th, ignored)
-     const td_thrhandle_t *th;
-     void *ignored;
+td_find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
 {
   td_thrinfo_t ti;
-  td_err_e     ret;
+  td_err_e err;
+  int pid;
 
-  if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
-    {
-      warning ("find_new_threads_callback: %s", thr_err_string (ret));
-      return -1;		/* bail out, get_info failed. */
-    }
+  err = p_td_thr_get_info (th_p, &ti);
+  if (err != TD_OK)
+    error ("td_find_new_threads_callback: td_thr_get_info: %s",
+	   thr_err_string (err));
 
-  /* FIXME: 
-     As things now stand, this should never detect a new thread.
-     But if it does, we could be in trouble because we aren't calling
-     wait_thread_callback for it.  */
-  test_for_new_thread (ti.ti_tid, ti.ti_lid, 0);
+  pid = BUILD_THREAD (ti.ti_tid, main_prochandle.pid);
+
+  if (! in_thread_list (pid))
+    add_new_thread (th_p, &ti, 1);
+
   return 0;
 }
 
-/* 
- * find_new_threads uses the thread_db iterator function to discover
- * user-space threads.  Then if the underlying process stratum has a
- * find_new_threads method, we call that too.
- */
+/* Query for new threads and add them to the thread list.  */
 
 static void
-thread_db_find_new_threads ()
+thread_db_find_new_threads (void)
 {
-  if (inferior_pid == -1)	/* FIXME: still necessary? */
-    {
-      printf_filtered ("No process.\n");
-      return;
-    }
-  p_td_ta_thr_iter (main_threadagent, 
-		    find_new_threads_callback, 
-		    (void *) 0, 
-		    TD_THR_ANY_STATE, 
-		    TD_THR_LOWEST_PRIORITY,
-		    TD_SIGNO_MASK, 
-		    TD_THR_ANY_USER_FLAGS);
+  td_err_e err;
+
+  /* Iterate over all user-space threads to discover new threads.
+     This will attach newly detected threads ...  */
+  err = p_td_ta_thr_iter (main_threadagent, td_find_new_threads_callback, NULL,
+			  TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+			  TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+  if (err != TD_OK)
+    error ("thread_db_find_new_threads: td_ta_thr_iter: %s",
+	   thr_err_string (err));
+
+  /* ... so we'll have to wait for them to report back.  */
+  iterate_over_threads (wait_callback, NULL);
+			
+  /* If the underlying process stratum has a find_new_threads method,
+     call that too.  */
   if (target_beneath->to_find_new_threads)
     target_beneath->to_find_new_threads ();
 }
-
-/*
- * Resume all threads, or resume a single thread.
- * If step is true, then single-step the appropriate thread
- * (or single-step inferior_pid, but continue everyone else).
- * If signo is true, then send that signal to at least one thread.
- */
 
-/*
- * This function is called once for each thread before resuming.
- * It sends continue (no step, and no signal) to each thread except
- *   the main thread, and
- *   the event thread (the one that stopped at a breakpoint etc.)
- *
- * The event thread is handled separately so that it can be sent
- * the stepping and signal args with which target_resume was called.
- *
- * The main thread is resumed last, so that the thread_db proc_service
- * callbacks will still work during the iterator function.
- */
+
+/* If TP corresponds to a user-space thread bound to a LWP, resume
+   that LWP.  */
 
 static int
-resume_thread_callback (th, data)
-     const td_thrhandle_t *th;
-     void *data;
+resume_callback (struct thread_info *tp, void *data)
 {
-  td_thrinfo_t ti;
-  td_err_e     ret;
-
-  if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
+  if (is_thread (tp->pid) && tp->private->stopped)
     {
-      warning ("resume_thread_callback: %s", thr_err_string (ret));
-      return -1;		/* bail out, get_info failed. */
-    }
-  /* FIXME: 
-     As things now stand, this should never detect a new thread.
-     But if it does, we could be in trouble because we aren't calling
-     wait_thread_callback for it.  */
-  test_for_new_thread (ti.ti_tid, ti.ti_lid, 1);
+      lwpid_t lwpid = tp->private->lwpid;
 
-  if (ti.ti_lid != main_prochandle.pid &&
-      ti.ti_lid != event_pid)
-    {
-      /* Unconditionally continue the thread with no signal.
-	 Only the event thread will get a signal of any kind.  */
-
-      target_beneath->to_resume (ti.ti_lid, 0, 0);
+      if (lwpid != 0 && lwpid != main_prochandle.pid)
+	{
+	  target_beneath->to_resume (lwpid, 0, 0);
+	  tp->private->stopped = 0;
+	  tp->private->lwpid = 0;
+	}
     }
+
   return 0;
 }
 
-static int
-new_resume_thread_callback (thread, data)
-     threadinfo *thread;
-     void *data;
+static void
+lin_thread_resume (int pid, int step, enum target_signal signo)
 {
-  if (thread->lid != event_pid &&
-      thread->lid != main_prochandle.pid)
-    {
-      /* Unconditionally continue the thread with no signal (for now).  */
+  int resume_all;
+  lwpid_t lwpid;
 
-      target_beneath->to_resume (thread->lid, 0, 0);
-    }
-  return 0;
-}
+  /* Apparently the interpretation of PID is dependent on STEP: If
+     STEP is non-zero, a specific PID means `step only this process
+     id'.  But if STEP is zero, then PID means `continue *all*
+     processes, but give the signal only to this one'.  */
 
-static int last_resume_pid;
-static int last_resume_step;
-static int last_resume_signo;
+  resume_all = (pid == -1) || !step;
 
-static void
-thread_db_resume (pid, step, signo)
-     int pid;
-     int step;
-     enum target_signal signo;
-{
-  last_resume_pid   = pid;
-  last_resume_step  = step;
-  last_resume_signo = signo;
-
-  /* resuming a specific pid? */
-  if (pid != -1)
-    {
-      if (is_thread (pid))
-	pid = get_lwp_from_thread_id (GET_THREAD (pid));
-      else if (GET_LWP (pid))
-	pid = GET_LWP (pid);
-    }
-
-  /* Apparently the interpretation of 'pid' is dependent on 'step':
-     If step is true, then a specific pid means 'step only this pid'.
-     But if step is not true, then pid means 'continue ALL pids, but
-     give the signal only to this one'.  */
-  if (pid != -1 && step)
+  /* If PID is -1, it's the current inferior that should be
+     handled special.  */
+  if (pid == -1)
+    pid = inferior_pid;
+
+  if (is_thread (pid))
     {
-      /* FIXME: is this gonna work in all circumstances? */
-      target_beneath->to_resume (pid, step, signo);
+      struct thread_info *tp = find_thread_pid (pid);
+
+      lwpid = tp->private->lwpid;
+      if (lwpid == 0)
+	error ("thread_db_resume: Cannot resume un-bound user-space thread.");
+
+      /* Marking this thread as not stopped will make sure that this
+         thread isn't resumed by resume_callback.  */
+      tp->private->stopped = 0;
+      tp->private->lwpid = 0;
     }
   else
     {
-      /* 1) Continue all threads except the event thread and the main thread.
-	 2) resume the event thread with step and signo.
-	 3) If event thread != main thread, continue the main thread.
-
-	 Note: order of 2 and 3 may need to be reversed.  */
-
-      threadlist_iter (new_resume_thread_callback, 
-			(void *) 0, 
-			TD_THR_ANY_STATE, 
-			TD_THR_ANY_TYPE);
-      /* now resume event thread, and if necessary also main thread. */
-      if (event_pid)
-	{
-	  target_beneath->to_resume (event_pid, step, signo);
-	}
-      if (event_pid != main_prochandle.pid)
-	{
-	  target_beneath->to_resume (main_prochandle.pid, 0, 0);
-	}
+      lwpid = GET_LWP (pid);
+      if (lwpid == 0)
+	lwpid = pid;
     }
-}
 
-/* All new threads will be attached.
-   All previously known threads will be stopped using kill (SIGKILL).  */
+  /* If we are supposed to continue all processes, esume all
+     user-space threads.  This doesn't cover the main process though.
+     That one is handled below.  */
+  if (resume_all)
+    iterate_over_threads (resume_callback, NULL);
 
-static int
-stop_or_attach_thread_callback (const td_thrhandle_t *th, void *data)
-{
-  td_thrinfo_t ti;
-  td_err_e     ret;
-  int          gdb_pid;
-  int on_off = 1;
+  /* Resume PID.  */
+  target_beneath->to_resume (lwpid, step, signo);
+  if (lwpid == main_prochandle.pid)
+    main_prochandle.stopped = 0;
 
-  if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
-    {
-      warning ("stop_or_attach_thread_callback: %s", thr_err_string (ret));
-      return -1;		/* bail out, get_info failed. */
-    }
+  /* If we are supposed to continue all processes, and the main
+     process is still stopped, resume it now.
 
-  /* First add it to our internal list.  
-     We build this list anew at every wait event.  */
-  insert_thread (ti.ti_tid, ti.ti_lid, ti.ti_state, ti.ti_type);
-  /* Now: if we've already seen it, stop it, else add it and attach it.  */
-  gdb_pid = BUILD_THREAD (ti.ti_tid, main_prochandle.pid);
-  if (!in_thread_list (gdb_pid))	/* new thread */
-    {
-      handle_new_thread (ti.ti_tid, ti.ti_lid, 1);
-      /* Enable thread events */
-      if (p_td_thr_event_enable)
-	if ((ret = p_td_thr_event_enable (th, on_off)) != TD_OK)
-	  warning ("stop_or_attach_thread: %s", thr_err_string (ret));
-    }
-  else if (ti.ti_lid != event_pid &&
-	   ti.ti_lid != main_prochandle.pid)
+     FIXME: kettenis/2000-04-23: LinuxThreads seems to require that
+     the main process is resumed last.  Unfortunately this does result
+     in some unfairness in the scheduling of the threads; the main
+     thread may never run if there are other threads running.  */
+  if (resume_all && main_prochandle.stopped)
     {
-      ret = (td_err_e) kill (ti.ti_lid, SIGSTOP);
+      target_beneath->to_resume (main_prochandle.pid, 0, 0);
+      main_prochandle.stopped = 0;
     }
-
-  return 0;
 }
-     
-/*
- * Wait for signal N from pid PID.
- * If wait returns any other signals, put them back before returning.
- */
+
+
+/* Wait for SIGSTOP from kernel thread PID.  */
 
 static void
-wait_for_stop (pid)
-     int pid;
+wait_for_stop (lwpid_t pid)
 {
-  int i;
-  int retpid;
+  lwpid_t retpid;
   int status;
+  int signo;
 
   /* Array of wait/signal status */
   /* FIXME: wrong data structure, we need a queue.
      Realtime signals may be delivered more than once.  
      And at that, we really can't handle them (see below).  */
 #if defined (NSIG)
-  static int   wstatus [NSIG];
+  static int wstatus [NSIG];
 #elif defined (_NSIG)
-  static int   wstatus [_NSIG];
+  static int wstatus [_NSIG];
 #else
 #error No definition for number of signals!
 #endif
 
-  /* clear wait/status list */
+  /* Clear wait/status list.  */
   memset (&wstatus, 0, sizeof (wstatus));
 
   /* Now look for SIGSTOP event on all threads except event thread.  */
@@ -1660,21 +1694,20 @@ wait_for_stop (pid)
       retpid = waitpid (pid, &status, __WCLONE);
 
     if (retpid > 0)
-      if (WSTOPSIG (status) == SIGSTOP)
+      if (WIFSTOPPED (status) && WSTOPSIG (status) == SIGSTOP)
 	{
 	  /* Got the SIGSTOP event we're looking for.
 	     Throw it away, and throw any other events back!  */
-	  for (i = 0; i < sizeof(wstatus) / sizeof (wstatus[0]); i++)
-	    if (wstatus[i])
-	      if (i != SIGSTOP)
-		{
-		  kill (retpid, i);
-		}
-	  break;	/* all done */
+	  for (signo = 0;
+	       signo < sizeof (wstatus) / sizeof (wstatus[0]);
+	       signo++)
+	    if (wstatus[signo])
+	      if (signo != SIGSTOP)
+		kill (retpid, signo);
+	  break;		/* All done.  */
 	}
       else
 	{
-	  int signo;
 	  /* Oops, got an event other than SIGSTOP.
 	     Save it, and throw it back after we find the SIGSTOP event.  */
 	  
@@ -1682,7 +1715,7 @@ wait_for_stop (pid)
 	     signals, which cannot be put back simply by using kill.  */
 
 	  if (WIFEXITED (status))
-	    error ("Ack!  Thread Exited event.  What do I do now???");
+	    goto exit;
 	  else if (WIFSTOPPED (status))
 	    signo = WSTOPSIG (status);
 	  else
@@ -1700,12 +1733,12 @@ wait_for_stop (pid)
 	     will delete or disable the breakpoint, but the thread will
 	     have already tripped on it.  */
 
-	  if (retpid != event_pid &&
-	      signo == SIGTRAP &&
-	      breakpoint_inserted_here_p (read_pc_pid (retpid) - 
-					  DECR_PC_AFTER_BREAK))
+	  if (retpid != event_pid && signo == SIGTRAP
+	      && breakpoint_inserted_here_p (read_pc_pid (retpid) - 
+					     DECR_PC_AFTER_BREAK))
 	    {
-	      /* Set the pc to before the trap and DO NOT re-send the signal */
+	      /* Set the pc to before the trap and *don't* re-send the
+                 signal. */
 	      if (DECR_PC_AFTER_BREAK)
 		write_pc_pid (read_pc_pid (retpid) - DECR_PC_AFTER_BREAK,
 			      retpid);
@@ -1715,7 +1748,8 @@ wait_for_stop (pid)
 	     (in the case where ^C is typed at the tty / console), 
 	     just ignore all SIGINTs from other than the event thread.  */
 	  else if (retpid != event_pid && signo == SIGINT)
-	    { /* do nothing.  Signal will disappear into oblivion!  */
+	    {
+	      /* Do nothing.  Signal will disappear into oblivion!  */
 	      ;
 	    }
 
@@ -1727,164 +1761,304 @@ wait_for_stop (pid)
 	  continue;
 	}
 
+    if (retpid < 0 && errno == ECHILD)
+      goto exit;
+
   } while (errno == 0 || errno == EINTR);
+
+  return;
+
+ exit:
+  /* FIXME: kettenis/2000-04-24: This bit of code (and the goto's
+     above) can go away, once we start using the thread termination
+     event reporting facility.  It is here to handle the case where
+     we're trying to stop a thread that has already exited.  */
+  {
+    struct thread_info *tp;
+
+    pid = pid_to_thread (pid);
+
+    tp = find_thread_pid (pid);
+    tp->private->stopped = 1;
+    tp->private->lwpid = 0;
+  }
 }
 
-/*
- * wait_thread_callback
- *
- * Calls waitpid for each thread, repeatedly if necessary, until
- * SIGSTOP is returned.  Afterward, if any other signals were returned
- * by waitpid, return them to the thread's pending queue by calling kill.
- */
+/* If TP corresponds to a user-space thread bound to a LWP, make that
+   LWP stop by sending SIGSTOP.  */
 
 static int
-wait_thread_callback (const td_thrhandle_t *th, void *data)
+stop_callback (struct thread_info *tp, void *data)
 {
-  td_thrinfo_t ti;
-  td_err_e     ret;
-
-  if ((ret = p_td_thr_get_info (th, &ti)) != TD_OK)
+  if (is_thread (tp->pid) && !tp->private->stopped && !tp->private->exited)
     {
-      warning ("wait_thread_callback: %s", thr_err_string (ret));
-      return -1;		/* bail out, get_info failed. */
-    }
+      /* Try to find the associated kernel thread id.  */
+      lwpid_t lwpid = GET_LWP (pid_to_lwp (tp->pid));
+
+      assert (! tp->private->signalled);
+      assert (tp->private->lwpid == 0);
 
-  /* This callback to act on all threads except the event thread: */
-  if (ti.ti_lid == event_pid || 	/* no need to wait (no sigstop) */
-      ti.ti_lid == main_prochandle.pid)	/* no need to wait (already waited) */
-    return 0;	/* don't wait on the event thread.  */
+      /* The main process should already have been stopped.  */
+      if (lwpid != 0 && lwpid != main_prochandle.pid)
+	{
+	  /* FIXME: kettenis/2000-04-16: What should we do if sending
+	     SIGSTOP fails?  */
+	  if (kill (lwpid, SIGSTOP) == 0)
+	    {
+	      tp->private->signalled = 1;
+
+	      /* Cache the kernel thread id for further use.  */
+	      tp->private->lwpid = lwpid;
+	    }
+	}
+    }
 
-  wait_for_stop (ti.ti_lid);
-  return 0;	/* finished: next thread. */
+  return 0;
 }
 
+/* If TP corresponds to a user-space thread, and we have previously
+   sent SIGSTOP to it's associated LWP, wait until SIGSTOP is returned
+   to us, such that we know for sure that the LWP isn't running
+   anymore.  */
+
 static int
-new_wait_thread_callback (thread, data)
-     threadinfo *thread;
-     void *data;
+wait_callback (struct thread_info *tp, void *data)
 {
-  /* don't wait on the event thread -- it's already stopped and waited.  
-     Ditto the main thread.  */
-  if (thread->lid != event_pid &&
-      thread->lid != main_prochandle.pid)
+  if (is_thread (tp->pid) && tp->private->signalled)
     {
-      wait_for_stop (thread->lid);
+      assert (! tp->private->stopped);
+      assert (tp->private->lwpid);
+
+      wait_for_stop (tp->private->lwpid);
+      tp->private->signalled = 0;
+      tp->private->stopped = 1;
     }
+
   return 0;
 }
+
 
-/* 
- * Wait for any thread to stop, by calling the underlying wait method.
- * The PID returned by the underlying target may be a kernel thread,
- * in which case we will want to convert it to the corresponding
- * user-space thread.  
- */
+/* Check if PID is currently stopped at the location of a thread event
+   breakpoint location.  If it is, read the event message and act upon
+   the event.  */
 
-static int
-thread_db_wait (int pid, struct target_waitstatus *ourstatus)
+static void
+check_event (int pid)
 {
-  td_thrhandle_t thandle;
+  td_event_msg_t msg;
   td_thrinfo_t ti;
-  td_err_e ret;
-  lwpid_t lwp;
-  int retpid;
-  int status;
-  int save_errno;
+  td_err_e err;
+  CORE_ADDR stop_pc;
+  int event_pid;
+
+  stop_pc = read_pc_pid (pid) - DECR_PC_AFTER_BREAK;
+  if (stop_pc != thread_creation_bkpt_address
+      && stop_pc != thread_death_bkpt_address)
+    return;
 
-  /* OK, we're about to wait for an event from the running inferior.
-     Make sure we're ignoring the right signals.  */
+  err = p_td_ta_event_getmsg (main_threadagent, &msg);
+  if (err != TD_OK)
+    {
+      if (err == TD_NOMSG)
+	return;
+      else
+	error ("check_event: td_ta_event_getmsg: %s", thr_err_string (err));
+    }
 
-  check_all_signal_numbers ();	/* see if magic signals changed. */
+  err = p_td_thr_get_info (msg.th_p, &ti);
+  if (err != TD_OK)
+    error ("check_event: td_thr_get_info: %s", thr_err_string (err));
 
-  event_pid = 0;
-  attach_pid = 0;
+  event_pid = BUILD_THREAD (ti.ti_tid, main_prochandle.pid);
 
-  /* FIXME: should I do the wait right here inline?  */
-#if 0
-  if (pid == -1)
-    lwp = -1;
-  else
-    lwp = get_lwp_from_thread_id (GET_THREAD (pid));
-#endif
+  switch (msg.event)
+    {
+    case TD_CREATE:
+      if (in_thread_list (event_pid))
+	{
+	  warning ("Spurious thread creation event.");
+	  return;
+	}
 
+      add_new_thread (msg.th_p, &ti, 1);
 
-  save_errno = linux_child_wait (-1, &retpid, &status);
-  store_waitstatus (ourstatus, status);
+      /* Since attaching to a kernel thread automatically sends a
+         SIGSTOP to that thread, wait until the thread is actually
+         halted.  */
+      wait_callback (find_thread_pid (event_pid), NULL);
+      return;
+      
+    case TD_DEATH:
+      if (! in_thread_list (event_pid))
+	{
+	  warning ("Spurious thread death event.");
+	  return;
+	}
 
-  /* Thread ID is irrelevant if the target process exited.
-     FIXME: do I have any killing to do?
-     Can I get this event mistakenly from a thread?  */
-  if (ourstatus->kind == TARGET_WAITKIND_EXITED)
-    return retpid;
+      delete_old_thread (msg.th_p, &ti, 1);
+      return;
 
-  /* OK, we got an event of interest.
-     Go stop all threads and look for new ones.
-     FIXME: maybe don't do this for the restart signal?  Optimization...  */
-  event_pid = retpid;
+    default:
+      warning ("Spurious thread event.");
+      break;
+    }
+  
+  return;
+}
 
-  /* If the last call to resume was for a specific thread, then we don't
-     need to stop everyone else: they should already be stopped.  */
-  if (last_resume_step == 0 || last_resume_pid == -1)
-    {
-      /* Main thread must be stopped before calling the iterator.  */
-      if (retpid != main_prochandle.pid)
-	{
-	  kill (main_prochandle.pid, SIGSTOP);
-	  wait_for_stop (main_prochandle.pid);
-	}
+/* This function is only there to have a standard interface for
+   linux_child_wait.  */
 
-      empty_threadlist ();
-      /* Now stop everyone else, and attach any new threads you find.  */
-      p_td_ta_thr_iter (main_threadagent, 
-			stop_or_attach_thread_callback,
-			(void *) 0,
-			TD_THR_ANY_STATE,
-			TD_THR_LOWEST_PRIORITY,
-			TD_SIGNO_MASK,
-			TD_THR_ANY_USER_FLAGS);
+static int
+child_wait (int pid, struct target_waitstatus *ourstatus)
+{
+  int status;
+  int retpid;
 
-      /* Now go call wait on all the threads we've stopped:
-	 This allows us to absorb the SIGKILL event, and to make sure
-	 that the thread knows that it is stopped (Linux peculiarity).  */
+  extern int linux_child_wait (int, int *, int *);
 
-      threadlist_iter (new_wait_thread_callback, 
-		       (void *) 0,
-		       TD_THR_ANY_STATE, 
-		       TD_THR_ANY_TYPE);
-    }
+  /* PID is currently ignored.  Otherwise we should probably do some
+     manipulation such that we find its associated kernel thread id.  */
+
+  linux_child_wait (-1, &retpid, &status);
+  store_waitstatus (ourstatus, status);
 
-  /* Convert the kernel thread id to the corresponding thread id.  */
+  return retpid;
+}
 
-  /* If the process layer does not furnish an lwp,
-     then perhaps the returned pid IS the lwp... */
-  if ((lwp = GET_LWP (retpid)) == 0)
-    lwp = retpid;
+/* Wait for process PID to do something.  */
 
-  if ((ret = p_td_ta_map_lwp2thr (main_threadagent, lwp, &thandle)) != TD_OK)
-    return retpid;	/* LWP is not mapped onto a user-space thread. */
+static int
+lin_thread_wait (int pid, struct target_waitstatus *ourstatus)
+{
+  struct thread_info *tp;
+  lwpid_t lwpid;
+  int retpid;
 
-  if ((ret = p_td_thr_validate (&thandle)) != TD_OK)
-    return retpid;	/* LWP is not mapped onto a valid thread. */
+  extern void check_all_signal_numbers (void);
+  
+  /* OK, we're about to wait for an event from the running inferior.
+     Make sure we're ignoring the right signals.  */
+  check_all_signal_numbers ();
 
-  if ((ret = p_td_thr_get_info (&thandle, &ti)) != TD_OK)
-    {
-      warning ("thread_db: thr_get_info failed ('%s')", thr_err_string (ret));
-      return retpid;
-    }
+#if 0
+  event_pid = 0;
+#endif
+
+  retpid = child_wait (pid, ourstatus);
 
-  retpid = BUILD_THREAD (ti.ti_tid, main_prochandle.pid);
-  /* If this is a new user thread, notify GDB about it.  */
-  if (!in_thread_list (retpid))
+  /* Get the associated kernel thread id.  */
+  lwpid = GET_LWP (retpid);
+  if (lwpid == 0)
+    lwpid = retpid;
+    
+  if (ourstatus->kind == TARGET_WAITKIND_EXITED)
     {
-      printf_filtered ("[New %s]\n", target_pid_to_str (retpid));
-      add_thread (retpid);
+      /* On Linux, we get this event for every kernel thread that
+         exits.  We only report it for the main process.  */
+      if (lwpid == main_prochandle.pid)
+	/* The thread id is irrelevant if the target process exited.  */
+	return retpid;
+
+      /* The underlying kernel thread is a zombie now.  This means
+         that we can no longer use ptrace on it.  This case is handled
+         below.  */
     }
 
+  /* OK, we got an event of interest.  */
 #if 0
-  /* Now detect if this is a thread creation/deletion event: */
-  check_for_thread_event (ourstatus, retpid);
+  event_pid = retpid;
 #endif
+
+  /* If the event was for the main process, it's stopped now.  */
+  if (lwpid == main_prochandle.pid)
+    main_prochandle.stopped = 1;
+
+  /* The main process (the initial kernel thread) must be stopped
+     before we try to read from the target.  Since most threads
+     debuging library functions try to do that, do it right now.  */
+  if (! main_prochandle.stopped)
+    {
+      int main_pid;
+
+      kill (main_prochandle.pid, SIGSTOP);
+      wait_for_stop (main_prochandle.pid);
+      main_prochandle.stopped = 1;
+
+      main_pid = pid_to_thread (main_prochandle.pid);
+      
+      /* Mark the user-space thread bound to the main process as stopped.  */
+      if (is_thread (main_pid))
+	{
+	  tp = find_thread_pid (main_pid);
+	  tp->private->lwpid = main_prochandle.pid;
+	  tp->private->stopped = 1;
+	}
+    }
+
+  /* Try to convert the process id to the corresponding user-space
+     thread id.  If RETPID isn't a LWP associated with a user-space
+     thread this is a no-op.  */
+  retpid = pid_to_thread (retpid);
+
+  /* Mark that thread as stopped if it corresponds to a user-space thread.  */
+  if (is_thread (retpid))
+    {
+      tp = find_thread_pid (retpid);
+      tp->private->lwpid = lwpid;
+      tp->private->stopped = 1;
+    }
+
+  /* We must now stop all kernel threads in the program.  The Linux
+     kernel doesn't provid a convenient way to do this.  Therefore we
+     iterate over all user-space threads, and stop the kernel threads
+     associated with them.
+     
+     FIXME: Maybe we can optimize thus by not doing this for the
+     restart signal.  */
+
+  /* Now stop all other user-space threads ...  */
+  iterate_over_threads (stop_callback, NULL);
+
+  /* ... and wait until all of them have reported back that
+     they're no longer running.  */
+  iterate_over_threads (wait_callback, NULL);
+
+  if (ourstatus->kind == TARGET_WAITKIND_STOPPED
+      && ourstatus->value.sig == TARGET_SIGNAL_TRAP)
+    /* Check for a thread event.  */
+    check_event (retpid);
+  
+  if (ourstatus->kind == TARGET_WAITKIND_EXITED)
+    {
+      /* A kernel thread has exited, but it wasn't the main process.
+         If it was bound to a user-space thread take appropriate
+         action.  Otherwise do nothing.  */
+
+      if (is_thread (retpid))
+	{
+	  /* Marking this thread as exited will make sure that we
+             never touch it again.  Also mark it as no longer being
+             stopped, to avoid resuming it, since that won't work (and
+             isn't necessary).  */
+	  tp->private->exited = 1;
+	  tp->private->stopped = 0;
+	  tp->private->lwpid = 0;
+	}
+
+      /* If this thread was GDB's idea of the current thread, make GDB
+         change its mind.  */
+      if (inferior_pid == retpid)
+	inferior_pid = main_prochandle.pid;
+
+      /* Ignore the event.  Even though we report this event as being
+         spurious, don't attribute it to the thread that just exited
+         since that confuses our caller.  Attribute it to the the
+         thread that GDB thinks of as the current thread instead.  */
+      ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+      return inferior_pid;
+    }
+
   return retpid;
 }
 
@@ -1975,6 +2149,7 @@ static void thread_db_kill (void)
 
 static void thread_db_mourn_inferior (void)
 {
+  remove_thread_event_breakpoints ();
   thread_db_unpush_target ();
   target_mourn_inferior ();	/* call the underlying mourn */
 }
@@ -2094,8 +2269,8 @@ init_thread_db_ops ()
   thread_db_ops.to_files_info       = thread_db_files_info;
   thread_db_ops.to_create_inferior  = thread_db_create_inferior;
   thread_db_ops.to_detach           = thread_db_detach;
-  thread_db_ops.to_wait             = thread_db_wait;
-  thread_db_ops.to_resume           = thread_db_resume;
+  thread_db_ops.to_wait             = lin_thread_wait;
+  thread_db_ops.to_resume           = lin_thread_resume;
   thread_db_ops.to_mourn_inferior   = thread_db_mourn_inferior;
   thread_db_ops.to_kill             = thread_db_kill;
   thread_db_ops.to_xfer_memory      = thread_db_xfer_memory;
@@ -2136,4 +2311,3 @@ _initialize_thread_db ()
     }
 #endif	/* HAVE_STDINT_H */
 }
-




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