* symtab.c, dbxread.c, stabsread.c: Fix up ANSI-C isms. Fix
[deliverable/binutils-gdb.git] / gdb / sol-thread.c
index 074754668c88421dd3e8f467614d0c377f5f1156..cc61c6a0f4f3fb19d4501f9afcd7ca05057a5137 100644 (file)
@@ -17,9 +17,38 @@ You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
+/* This module implements a sort of half target that sits between the
+   machine-independent parts of GDB and the /proc interface (procfs.c) to
+   provide access to the Solaris user-mode thread implementation.
+
+   Solaris threads are true user-mode threads, which are invoked via the thr_*
+   and pthread_* (native and Posix respectivly) interfaces.  These are mostly
+   implemented in user-space, with all thread context kept in various
+   structures that live in the user's heap.  These should not be confused with
+   lightweight processes (LWPs), which are implemented by the kernel, and
+   scheduled without explicit intervention by the process.
+
+   Just to confuse things a little, Solaris threads (both native and Posix) are
+   actually implemented using LWPs.  In general, there are going to be more
+   threads than LWPs.  There is no fixed correspondence between a thread and an
+   LWP.  When a thread wants to run, it gets scheduled onto the first available
+   LWP and can therefore migrate from one LWP to another as time goes on.  A
+   sleeping thread may not be associated with an LWP at all!
+
+   To make it possible to mess with threads, Sun provides a library called
+   libthread_db.so.1 (not to be confused with libthread_db.so.0, which doesn't
+   have a published interface).  This interface has an upper part, which it
+   provides, and a lower part which I provide.  The upper part consists of the
+   td_* routines, which allow me to find all the threads, query their state,
+   etc...  The lower part consists of all of the ps_*, which are used by the
+   td_* routines to read/write memory, manipulate LWPs, lookup symbols, etc...
+   The ps_* routines actually do most of their work by calling functions in
+   procfs.c.  */
 
 #include "defs.h"
 
+/* Undefine gregset_t and fpregset_t to avoid conflict with defs in xm file. */
+
 #ifdef gregset_t
 #undef gregset_t
 #endif
@@ -28,7 +57,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #undef fpregset_t
 #endif
 
-#include "/usr/include/thread.h"
+#include <thread.h>
 #include <proc_service.h>
 #include <thread_db.h>
 #include "gdbthread.h"
@@ -37,31 +66,240 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 #include <fcntl.h>
 #include <unistd.h>
 #include <sys/stat.h>
-
-static void sol_thread_resume PARAMS ((int pid, int step,
-                                      enum target_signal signo));
+#include <dlfcn.h>
+#include "gdbcmd.h"
 
 extern struct target_ops sol_thread_ops; /* Forward declaration */
 
 extern int procfs_suppress_run;
+extern struct target_ops procfs_ops; /* target vector for procfs.c */
+extern char *procfs_pid_to_str PARAMS ((int pid));
+
+/* Note that these prototypes differ slightly from those used in procfs.c
+   for of two reasons.  One, we can't use gregset_t, as that's got a whole
+   different meaning under Solaris (also, see above).  Two, we can't use the
+   pointer form here as these are actually arrays of ints (for Sparc's at
+   least), and are automatically coerced into pointers to ints when used as
+   parameters.  That makes it impossible to avoid a compiler warning when
+   passing pr{g fp}regset_t's from a parameter to an argument of one of
+   these functions.  */
+
+extern void supply_gregset PARAMS ((const prgregset_t));
+extern void fill_gregset PARAMS ((prgregset_t, int));
+extern void supply_fpregset PARAMS ((const prfpregset_t));
+extern void fill_fpregset PARAMS ((prfpregset_t, int));
+
+/* This struct is defined by us, but mainly used for the proc_service interface.
+   We don't have much use for it, except as a handy place to get a real pid
+   for memory accesses.  */
 
 struct ps_prochandle
 {
   pid_t pid;
 };
 
+struct string_map
+{
+  int num;
+  char *str;
+};
+
 static struct ps_prochandle main_ph;
 static td_thragent_t *main_ta;
-
 static int sol_thread_active = 0;
 
+static struct cleanup * save_inferior_pid PARAMS ((void));
+static void restore_inferior_pid PARAMS ((int pid));
+static char *td_err_string PARAMS ((td_err_e errcode));
+static char *td_state_string PARAMS ((td_thr_state_e statecode));
+static int  thread_to_lwp PARAMS ((int thread_id, int default_lwp));
+static void sol_thread_resume PARAMS ((int pid, int step,
+                                      enum target_signal signo));
+static int lwp_to_thread PARAMS ((int lwp));
+static int sol_thread_alive PARAMS ((int pid));
+
+#define THREAD_FLAG 0x80000000
+#define is_thread(ARG) (((ARG) & THREAD_FLAG) != 0)
+#define is_lwp(ARG) (((ARG) & THREAD_FLAG) == 0)
+#define GET_LWP(LWP_ID) (TIDGET(LWP_ID))
+#define GET_THREAD(THREAD_ID) (((THREAD_ID) >> 16) & 0x7fff)
+#define BUILD_LWP(LWP_ID, PID) ((LWP_ID) << 16 | (PID))
+#define BUILD_THREAD(THREAD_ID, PID) (THREAD_FLAG | BUILD_LWP (THREAD_ID, PID))
+
+/* Pointers to routines from lithread_db resolved by dlopen() */
+
+static void
+  (*p_td_log) (const int on_off);
+static td_err_e
+  (*p_td_ta_new) (const struct ps_prochandle *ph_p, td_thragent_t **ta_pp);
+static td_err_e
+  (*p_td_ta_delete) (td_thragent_t *ta_p);
+static td_err_e
+  (*p_td_init) (void);
+static td_err_e
+  (*p_td_ta_get_ph) (const td_thragent_t *ta_p, struct ps_prochandle **ph_pp);
+static td_err_e
+  (*p_td_ta_get_nthreads) (const td_thragent_t *ta_p, int *nthread_p);
+static td_err_e
+  (*p_td_ta_tsd_iter) (const td_thragent_t *ta_p, td_key_iter_f *cb, void *cbdata_p);
+static td_err_e
+  (*p_td_ta_thr_iter) (const td_thragent_t *ta_p, td_thr_iter_f *cb, void *cbdata_p, td_thr_state_e state,
+                      int ti_pri, sigset_t *ti_sigmask_p, unsigned ti_user_flags);
+static td_err_e
+  (*p_td_thr_validate) (const td_thrhandle_t *th_p);
+static td_err_e
+  (*p_td_thr_tsd) (const td_thrhandle_t *th_p, const thread_key_t key, void **data_pp);
+static td_err_e
+  (*p_td_thr_get_info) (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p);
+static td_err_e
+  (*p_td_thr_getfpregs) (const td_thrhandle_t *th_p, prfpregset_t *fpregset);
+static td_err_e
+  (*p_td_thr_getxregsize) (const td_thrhandle_t *th_p, int *xregsize);
+static td_err_e
+  (*p_td_thr_getxregs) (const td_thrhandle_t *th_p, const caddr_t xregset);
+static td_err_e
+  (*p_td_thr_sigsetmask) (const td_thrhandle_t *th_p, const sigset_t ti_sigmask);
+static td_err_e
+  (*p_td_thr_setprio) (const td_thrhandle_t *th_p, const int ti_pri);
+static td_err_e
+  (*p_td_thr_setsigpending) (const td_thrhandle_t *th_p, const uchar_t ti_pending_flag, const sigset_t ti_pending);
+static td_err_e
+  (*p_td_thr_setfpregs) (const td_thrhandle_t *th_p, const prfpregset_t *fpregset);
+static td_err_e
+  (*p_td_thr_setxregs) (const td_thrhandle_t *th_p, const caddr_t xregset);
+static td_err_e
+  (*p_td_ta_map_id2thr) (const td_thragent_t *ta_p, thread_t tid, td_thrhandle_t *th_p);
+static td_err_e
+  (*p_td_ta_map_lwp2thr) (const td_thragent_t *ta_p, lwpid_t lwpid, td_thrhandle_t *th_p);
+static td_err_e
+  (*p_td_thr_getgregs) (const td_thrhandle_t *th_p, prgregset_t regset);
+static td_err_e
+  (*p_td_thr_setgregs) (const td_thrhandle_t *th_p, const prgregset_t regset);
+\f
+/*
+
+LOCAL FUNCTION
+
+       td_err_string - Convert a thread_db error code to a string
+
+SYNOPSIS
+
+       char * td_err_string (errcode)
+
+DESCRIPTION
+
+       Return the thread_db error string associated with errcode.  If errcode
+       is unknown, then return a message.
+
+ */
+
+static char *
+td_err_string (errcode)
+     td_err_e errcode;
+{
+  static struct string_map
+    td_err_table[] = {
+      {TD_OK,          "generic \"call succeeded\""},
+      {TD_ERR,         "generic error."},
+      {TD_NOTHR,       "no thread can be found to satisfy query"},
+      {TD_NOSV,                "no synch. variable can be found to satisfy query"},
+      {TD_NOLWP,       "no lwp can be found to satisfy query"},
+      {TD_BADPH,       "invalid process handle"},
+      {TD_BADTH,       "invalid thread handle"},
+      {TD_BADSH,       "invalid synchronization handle"},
+      {TD_BADTA,       "invalid thread agent"},
+      {TD_BADKEY,      "invalid key"},
+      {TD_NOMSG,       "td_thr_event_getmsg() called when there was no message"},
+      {TD_NOFPREGS,    "FPU register set not available for given thread"},
+      {TD_NOLIBTHREAD, "application not linked with libthread"},
+      {TD_NOEVENT,     "requested event is not supported"},
+      {TD_NOCAPAB,     "capability not available"},
+      {TD_DBERR,       "Debugger service failed"},
+      {TD_NOAPLIC,     "Operation not applicable to"},
+      {TD_NOTSD,       "No thread specific data for this thread"},
+      {TD_MALLOC,      "Malloc failed"},
+      {TD_PARTIALREG,  "Only part of register set was writen/read"},
+      {TD_NOXREGS,     "X register set not available for given thread"}
+    };
+  const int td_err_size = sizeof td_err_table / sizeof (struct string_map);
+  int i;
+  static char buf[50];
+
+  for (i = 0; i < td_err_size; i++)
+    if (td_err_table[i].num == errcode)
+      return td_err_table[i].str;
+                 
+  sprintf (buf, "Unknown thread_db error code: %d", errcode);
+
+  return buf;
+}
+\f
+/*
+
+LOCAL FUNCTION
+
+       td_state_string - Convert a thread_db state code to a string
+
+SYNOPSIS
+
+       char * td_state_string (statecode)
+
+DESCRIPTION
+
+       Return the thread_db state string associated with statecode.  If
+       statecode is unknown, then return a message.
+
+ */
+
+static char *
+td_state_string (statecode)
+     td_thr_state_e statecode;
+{
+  static struct string_map
+    td_thr_state_table[] = {
+      {TD_THR_ANY_STATE, "any state"},
+      {TD_THR_UNKNOWN, "unknown"},
+      {TD_THR_STOPPED, "stopped"},
+      {TD_THR_RUN,     "run"},
+      {TD_THR_ACTIVE,  "active"},
+      {TD_THR_ZOMBIE,  "zombie"},
+      {TD_THR_SLEEP,   "sleep"},
+      {TD_THR_STOPPED_ASLEEP, "stopped asleep"}
+    };
+  const int td_thr_state_table_size = sizeof td_thr_state_table / sizeof (struct string_map);
+  int i;
+  static char buf[50];
+
+  for (i = 0; i < td_thr_state_table_size; i++)
+    if (td_thr_state_table[i].num == statecode)
+      return td_thr_state_table[i].str;
+                 
+  sprintf (buf, "Unknown thread_db state code: %d", statecode);
+
+  return buf;
+}
 \f
+/*
+
+LOCAL FUNCTION
+
+       thread_to_lwp - Convert a Posix or Solaris thread id to a LWP id.
+
+SYNOPSIS
 
-extern struct target_ops procfs_ops;
+       int thread_to_lwp (thread_id, default_lwp)
 
-/* Convert thread_id to an LWP id.  */
+DESCRIPTION
 
-static int thread_to_lwp PARAMS ((int thread_id, int default_lwp));
+       This function converts a Posix or Solaris thread id to a lightweight
+       process id.  If thread_id is non-existent, that's an error.  If it's
+       an inactive thread, then we return default_lwp.
+
+NOTES
+
+       This function probably shouldn't call error()...
+
+ */
 
 static int
 thread_to_lwp (thread_id, default_lwp)
@@ -71,41 +309,55 @@ thread_to_lwp (thread_id, default_lwp)
   td_thrinfo_t ti;
   td_thrhandle_t th;
   td_err_e val;
-  int pid;
-  int lwp;
 
-  if (!(thread_id & 0x80000000))
+  if (is_lwp (thread_id))
     return thread_id;                  /* It's already an LWP id */
 
   /* It's a thread.  Convert to lwp */
 
-  pid = thread_id & 0xffff;
-  thread_id = (thread_id >> 16) & 0x7fff;
-
-  val = td_ta_map_id2thr (main_ta, thread_id, &th);
-  if (val != TD_OK)
-    error ("thread_to_lwp: td_ta_map_id2thr %d", val);
-
-  val = td_thr_get_info (&th, &ti);
+  val = p_td_ta_map_id2thr (main_ta, GET_THREAD (thread_id), &th);
+  if (val == TD_NOTHR)
+    return -1;         /* thread must have terminated */
+  else if (val != TD_OK)
+    error ("thread_to_lwp: td_ta_map_id2thr %s", td_err_string (val));
 
-  if (val != TD_OK)
-    error ("thread_to_lwp: td_thr_get_info: %d", val);
+  val = p_td_thr_get_info (&th, &ti);
+  if (val == TD_NOTHR)
+    return -1;         /* thread must have terminated */
+  else if (val != TD_OK)
+    error ("thread_to_lwp: td_thr_get_info: %s", td_err_string (val));
 
   if (ti.ti_state != TD_THR_ACTIVE)
     {
       if (default_lwp != -1)
        return default_lwp;
-      error ("thread_to_lwp: thread state not active: %d", ti.ti_state);
+      error ("thread_to_lwp: thread state not active: %s",
+            td_state_string (ti.ti_state));
     }
-  
-  lwp = (ti.ti_lid << 16) | pid;
 
-  return lwp;
+  return BUILD_LWP (ti.ti_lid, PIDGET (thread_id));
 }
+\f
+/*
 
-/* Convert an LWP id to a thread. */
+LOCAL FUNCTION
 
-static int lwp_to_thread PARAMS ((int lwp));
+       lwp_to_thread - Convert a LWP id to a Posix or Solaris thread id.
+
+SYNOPSIS
+
+       int lwp_to_thread (lwp_id)
+
+DESCRIPTION
+
+       This function converts a lightweight process id to a Posix or Solaris
+       thread id.  If thread_id is non-existent, that's an error.
+
+NOTES
+
+       This function probably shouldn't call error()...
+
+ */
 
 static int
 lwp_to_thread (lwp)
@@ -114,30 +366,80 @@ lwp_to_thread (lwp)
   td_thrinfo_t ti;
   td_thrhandle_t th;
   td_err_e val;
-  int pid;
-  int thread_id;
 
-  if (lwp & 0x80000000)
+  if (is_thread (lwp))
     return lwp;                        /* It's already a thread id */
 
   /* It's an lwp.  Convert it to a thread id.  */
 
-  pid = lwp & 0xffff;
-  lwp = (lwp >> 16) & 0xffff;
+  if (!sol_thread_alive (lwp))
+    return -1;                 /* defunct lwp */
 
-  val = td_ta_map_lwp2thr (main_ta, lwp, &th);
-  if (val != TD_OK)
-    error ("lwp_to_thread: td_thr_get_info: %d.", val);
+  val = p_td_ta_map_lwp2thr (main_ta, GET_LWP (lwp), &th);
+  if (val == TD_NOTHR)
+    return -1;         /* thread must have terminated */
+  else if (val != TD_OK)
+    error ("lwp_to_thread: td_thr_get_info: %s.", td_err_string (val));
 
-  val = td_thr_get_info (&th, &ti);
+  val = p_td_thr_validate (&th);
+  if (val == TD_NOTHR)
+    return lwp;                        /* libthread doesn't know about it, just return lwp */
+  else if (val != TD_OK)
+    error ("lwp_to_thread: td_thr_validate: %s.", td_err_string (val));
 
-  if (val != TD_OK)
-    error ("lwp_to_thread: td_thr_get_info: %d.", val);
+  val = p_td_thr_get_info (&th, &ti);
+  if (val == TD_NOTHR)
+    return -1;         /* thread must have terminated */
+  else if (val != TD_OK)
+    error ("lwp_to_thread: td_thr_get_info: %s.", td_err_string (val));
+
+  return BUILD_THREAD (ti.ti_tid, PIDGET (lwp));
+}
+\f
+/*
+
+LOCAL FUNCTION
+
+       save_inferior_pid - Save inferior_pid on the cleanup list
+       restore_inferior_pid - Restore inferior_pid from the cleanup list
+
+SYNOPSIS
+
+       struct cleanup *save_inferior_pid ()
+       void restore_inferior_pid (int pid)
+
+DESCRIPTION
+
+       These two functions act in unison to restore inferior_pid in
+       case of an error.
+
+NOTES
+
+       inferior_pid is a global variable that needs to be changed by many of
+       these routines before calling functions in procfs.c.  In order to
+       guarantee that inferior_pid gets restored (in case of errors), you
+       need to call save_inferior_pid before changing it.  At the end of the
+       function, you should invoke do_cleanups to restore it.
+
+ */
 
-  thread_id = (ti.ti_tid << 16) | pid | 0x80000000;
 
-  return thread_id;
+static struct cleanup *
+save_inferior_pid ()
+{
+  return make_cleanup (restore_inferior_pid, inferior_pid);
+}
+
+static void
+restore_inferior_pid (pid)
+     int pid;
+{
+  inferior_pid = pid;
 }
+\f
+
+/* Most target vector functions from here on actually just pass through to
+   procfs.c, as they don't need to do anything specific for threads.  */
 
 
 /* ARGSUSED */
@@ -158,7 +460,19 @@ sol_thread_attach (args, from_tty)
      int from_tty;
 {
   procfs_ops.to_attach (args, from_tty);
-
+  /* Must get symbols from solibs before libthread_db can run! */
+  SOLIB_ADD ((char *)0, from_tty, (struct target_ops *)0);
+  if (sol_thread_active)
+    {
+      printf_filtered ("sol-thread active.\n");
+      main_ph.pid = inferior_pid; /* Save for xfer_memory */
+      push_target (&sol_thread_ops);
+      inferior_pid = lwp_to_thread (inferior_pid);
+      if (inferior_pid == -1)
+       inferior_pid = main_ph.pid;
+      else
+       add_thread (inferior_pid);
+    }
   /* XXX - might want to iterate over all the threads and register them. */
 }
 
@@ -175,12 +489,14 @@ sol_thread_detach (args, from_tty)
      char *args;
      int from_tty;
 {
+  unpush_target (&sol_thread_ops);
   procfs_ops.to_detach (args, from_tty);
 }
 
 /* Resume execution of process PID.  If STEP is nozero, then
    just single step it.  If SIGNAL is nonzero, restart it with that
-   signal activated.  */
+   signal activated.  We may have to convert pid from a thread-id to an LWP id
+   for procfs.  */
 
 static void
 sol_thread_resume (pid, step, signo)
@@ -188,60 +504,86 @@ sol_thread_resume (pid, step, signo)
      int step;
      enum target_signal signo;
 {
-  int save_pid;
+  struct cleanup *old_chain;
 
-  save_pid = inferior_pid;
+  old_chain = save_inferior_pid ();
 
   inferior_pid = thread_to_lwp (inferior_pid, main_ph.pid);
+  if (inferior_pid == -1)
+    inferior_pid = procfs_first_available ();
 
   if (pid != -1)
-    pid = thread_to_lwp (pid, -1);
+    {
+      int save_pid = pid;
+
+      pid = thread_to_lwp (pid, -2);
+      if (pid == -2)           /* Inactive thread */
+       error ("This version of Solaris can't start inactive threads.");
+      if (info_verbose && pid == -1)
+       warning ("Specified thread %d seems to have terminated", 
+                GET_THREAD (save_pid));
+    }
 
   procfs_ops.to_resume (pid, step, signo);
 
-  inferior_pid = save_pid;
+  do_cleanups (old_chain);
 }
 
-/* Wait for any LWPs to stop */
+/* Wait for any threads to stop.  We may have to convert PID from a thread id
+   to a LWP id, and vice versa on the way out.  */
 
 static int
 sol_thread_wait (pid, ourstatus)
      int pid;
      struct target_waitstatus *ourstatus;
 {
-  int statval;
   int rtnval;
   int save_pid;
-
-  if (!sol_thread_active)
-    return procfs_ops.to_wait (pid, ourstatus);
+  struct cleanup *old_chain;
 
   save_pid = inferior_pid;
+  old_chain = save_inferior_pid ();
 
   inferior_pid = thread_to_lwp (inferior_pid, main_ph.pid);
+  if (inferior_pid == -1)
+    inferior_pid = procfs_first_available ();
 
   if (pid != -1)
-    pid = thread_to_lwp (pid, -1);
+    {
+      int save_pid = pid;
+
+      pid = thread_to_lwp (pid, -2);
+      if (pid == -2)           /* Inactive thread */
+       error ("This version of Solaris can't start inactive threads.");
+      if (info_verbose && pid == -1)
+       warning ("Specified thread %d seems to have terminated", 
+                GET_THREAD (save_pid));
+    }
 
   rtnval = procfs_ops.to_wait (pid, ourstatus);
 
-  if (rtnval != save_pid
-      && !in_thread_list (rtnval))
+  if (ourstatus->kind != TARGET_WAITKIND_EXITED)
     {
-      fprintf_unfiltered (gdb_stderr, "[New %s]\n",
-                         target_pid_to_str (rtnval));
-      add_thread (rtnval);
+      /* Map the LWP of interest back to the appropriate thread ID */
+      rtnval = lwp_to_thread (rtnval);
+      if (rtnval == -1)
+       rtnval = save_pid;
+
+      /* See if we have a new thread */
+      if (is_thread (rtnval)
+         && rtnval != save_pid
+         && !in_thread_list (rtnval))
+       {
+         printf_filtered ("[New %s]\n", target_pid_to_str (rtnval));
+         add_thread (rtnval);
+       }
     }
 
-  inferior_pid = save_pid;     /* XXX need to make a cleanup for this in case of error */
-
   /* During process initialization, we may get here without the thread package
      being initialized, since that can only happen after we've found the shared
      libs.  */
 
-  /* Map the LWP of interest back to the appropriate thread ID */
-
-  rtnval = lwp_to_thread (rtnval);
+  do_cleanups (old_chain);
 
   return rtnval;
 }
@@ -253,68 +595,71 @@ sol_thread_fetch_registers (regno)
   thread_t thread;
   td_thrhandle_t thandle;
   td_err_e val;
-  prgregset_t regset;
+  prgregset_t gregset;
   prfpregset_t fpregset;
+#if 0
   int xregsize;
   caddr_t xregset;
+#endif
 
-  if (!sol_thread_active
-      || !(inferior_pid & 0x80000000))
-    {
+  if (!is_thread (inferior_pid))
+    { /* LWP: pass the request on to procfs.c */
       procfs_ops.to_fetch_registers (regno);
       return;
     }
 
-  /* Convert inferior_pid into a td_thrhandle_t */
+  /* Solaris thread: convert inferior_pid into a td_thrhandle_t */
 
-  thread = (inferior_pid >> 16) & 0x7fff;
+  thread = GET_THREAD (inferior_pid);
 
   if (thread == 0)
     error ("sol_thread_fetch_registers:  thread == 0");
 
-  val = td_ta_map_id2thr (main_ta, thread, &thandle);
+  val = p_td_ta_map_id2thr (main_ta, thread, &thandle);
   if (val != TD_OK)
-    error ("sol_thread_fetch_registers: td_ta_map_id2thr: %d", val);
+    error ("sol_thread_fetch_registers: td_ta_map_id2thr: %s",
+          td_err_string (val));
 
   /* Get the integer regs */
 
-  val = td_thr_getgregs (&thandle, regset);
-  if (val == TD_OK)
-    supply_gregset (regset);
-  else if (val == TD_PARTIALREG)
-    {
-      /* For the sparc, only i0->i7, l0->l7, pc and sp are saved by a thread
-        context switch.  */
-
-      supply_gregset (regset); /* This is not entirely correct, as it sets
-                                  the valid bits for the o, g, ps, y, npc,
-                                  wim and tbr.  That should be harmless
-                                  though, as the context switch routine
-                                  doesn't need to save them.  */
-    }
-  else
-    error ("sol_thread_fetch_registers: td_thr_getgregs %d", val);
+  val = p_td_thr_getgregs (&thandle, gregset);
+  if (val != TD_OK
+      && val != TD_PARTIALREG)
+    error ("sol_thread_fetch_registers: td_thr_getgregs %s",
+          td_err_string (val));
+
+  /* For the sparc, TD_PARTIALREG means that only i0->i7, l0->l7, pc and sp
+     are saved (by a thread context switch).  */
 
   /* And, now the fp regs */
 
-  val = td_thr_getfpregs (&thandle, &fpregset);
-  if (val == TD_OK)
-    supply_fpregset (&fpregset);
-  else if (val != TD_NOFPREGS)
-    error ("sol_thread_fetch_registers: td_thr_getfpregs %d", val);
+  val = p_td_thr_getfpregs (&thandle, &fpregset);
+  if (val != TD_OK
+      && val != TD_NOFPREGS)
+    error ("sol_thread_fetch_registers: td_thr_getfpregs %s",
+          td_err_string (val));
+
+/* Note that we must call supply_{g fp}regset *after* calling the td routines
+   because the td routines call ps_lget* which affect the values stored in the
+   registers array.  */
+
+  supply_gregset (gregset);
+  supply_fpregset (fpregset);
 
 #if 0
 /* thread_db doesn't seem to handle this right */
   val = td_thr_getxregsize (&thandle, &xregsize);
   if (val != TD_OK && val != TD_NOXREGS)
-    error ("sol_thread_fetch_registers: td_thr_getxregsize %d", val);
+    error ("sol_thread_fetch_registers: td_thr_getxregsize %s",
+          td_err_string (val));
 
   if (val == TD_OK)
     {
       xregset = alloca (xregsize);
       val = td_thr_getxregs (&thandle, xregset);
       if (val != TD_OK)
-       error ("sol_thread_fetch_registers: td_thr_getxregs %d", val);
+       error ("sol_thread_fetch_registers: td_thr_getxregs %s",
+              td_err_string (val));
     }
 #endif
 }
@@ -328,64 +673,73 @@ sol_thread_store_registers (regno)
   td_err_e val;
   prgregset_t regset;
   prfpregset_t fpregset;
+#if 0
   int xregsize;
   caddr_t xregset;
+#endif
 
-  if (!sol_thread_active
-      || !(inferior_pid & 0x80000000))
-    {
+  if (!is_thread (inferior_pid))
+    { /* LWP: pass the request on to procfs.c */
       procfs_ops.to_store_registers (regno);
       return;
     }
 
-  /* Convert inferior_pid into a td_thrhandle_t */
+  /* Solaris thread: convert inferior_pid into a td_thrhandle_t */
 
-  thread = (inferior_pid >> 16) & 0x7fff;
+  thread = GET_THREAD (inferior_pid);
 
-  val = td_ta_map_id2thr (main_ta, thread, &thandle);
+  val = p_td_ta_map_id2thr (main_ta, thread, &thandle);
   if (val != TD_OK)
-    error ("sol_thread_store_registers: td_ta_map_id2thr %d", val);
+    error ("sol_thread_store_registers: td_ta_map_id2thr %s",
+          td_err_string (val));
 
   if (regno != -1)
     {                          /* Not writing all the regs */
-      val = td_thr_getgregs (&thandle, regset);
+      val = p_td_thr_getgregs (&thandle, regset);
       if (val != TD_OK)
-       error ("sol_thread_store_registers: td_thr_getgregs %d", val);
-      val = td_thr_getfpregs (&thandle, &fpregset);
+       error ("sol_thread_store_registers: td_thr_getgregs %s",
+              td_err_string (val));
+      val = p_td_thr_getfpregs (&thandle, &fpregset);
       if (val != TD_OK)
-       error ("sol_thread_store_registers: td_thr_getfpregs %d", val);
+       error ("sol_thread_store_registers: td_thr_getfpregs %s",
+              td_err_string (val));
 
 #if 0
 /* thread_db doesn't seem to handle this right */
       val = td_thr_getxregsize (&thandle, &xregsize);
       if (val != TD_OK && val != TD_NOXREGS)
-       error ("sol_thread_store_registers: td_thr_getxregsize %d", val);
+       error ("sol_thread_store_registers: td_thr_getxregsize %s",
+              td_err_string (val));
 
       if (val == TD_OK)
        {
          xregset = alloca (xregsize);
          val = td_thr_getxregs (&thandle, xregset);
          if (val != TD_OK)
-           error ("sol_thread_store_registers: td_thr_getxregs %d", val);
+           error ("sol_thread_store_registers: td_thr_getxregs %s",
+                  td_err_string (val));
        }
 #endif
     }
 
   fill_gregset (regset, regno);
-  fill_fpregset (&fpregset, regno);
+  fill_fpregset (fpregset, regno);
 
-  val = td_thr_setgregs (&thandle, regset);
+  val = p_td_thr_setgregs (&thandle, regset);
   if (val != TD_OK)
-    error ("sol_thread_store_registers: td_thr_setgregs %d", val);
-  val = td_thr_setfpregs (&thandle, &fpregset);
+    error ("sol_thread_store_registers: td_thr_setgregs %s",
+          td_err_string (val));
+  val = p_td_thr_setfpregs (&thandle, &fpregset);
   if (val != TD_OK)
-    error ("sol_thread_store_registers: td_thr_setfpregs %d", val);
+    error ("sol_thread_store_registers: td_thr_setfpregs %s",
+          td_err_string (val));
 
 #if 0
 /* thread_db doesn't seem to handle this right */
   val = td_thr_getxregsize (&thandle, &xregsize);
   if (val != TD_OK && val != TD_NOXREGS)
-    error ("sol_thread_store_registers: td_thr_getxregsize %d", val);
+    error ("sol_thread_store_registers: td_thr_getxregsize %s",
+          td_err_string (val));
 
   /* Should probably do something about writing the xregs here, but what are
      they? */
@@ -413,16 +767,18 @@ sol_thread_xfer_memory (memaddr, myaddr, len, dowrite, target)
      struct target_ops *target; /* ignored */
 {
   int retval;
-  int save_pid;
+  struct cleanup *old_chain;
 
-  save_pid = inferior_pid;
+  old_chain = save_inferior_pid ();
 
-  if (inferior_pid & 0x80000000)
-    inferior_pid = main_ph.pid;        /* It's a thread.  Convert to lwp */
+  if (is_thread (inferior_pid) ||              /* A thread */
+      !target_thread_alive (inferior_pid))     /* An lwp, but not alive */
+    inferior_pid = procfs_first_available ();  /* Find any live lwp.  */
+  /* Note: don't need to call switch_to_thread; we're just reading memory.  */
 
   retval = procfs_ops.to_xfer_memory (memaddr, myaddr, len, dowrite, target);
 
-  inferior_pid = save_pid;
+  do_cleanups (old_chain);
 
   return retval;
 }
@@ -449,8 +805,6 @@ sol_thread_notice_signals (pid)
   procfs_ops.to_notice_signals (pid);
 }
 
-void target_new_objfile PARAMS ((struct objfile *objfile));
-
 /* Fork an inferior process, and start debugging it with /proc.  */
 
 static void
@@ -461,24 +815,25 @@ sol_thread_create_inferior (exec_file, allargs, env)
 {
   procfs_ops.to_create_inferior (exec_file, allargs, env);
 
-  if (sol_thread_active)
+  if (sol_thread_active && inferior_pid != 0)
     {
-      td_thrhandle_t thandle;
-      td_err_e val;
-      td_thrinfo_t ti;
-
       main_ph.pid = inferior_pid; /* Save for xfer_memory */
 
       push_target (&sol_thread_ops);
 
       inferior_pid = lwp_to_thread (inferior_pid);
+      if (inferior_pid == -1)
+       inferior_pid = main_ph.pid;
 
       add_thread (inferior_pid);
     }
 }
 
 /* This routine is called whenever a new symbol table is read in, or when all
-   symbol tables are removed.  */
+   symbol tables are removed.  libthread_db can only be initialized when it
+   finds the right variables in libthread.so.  Since it's a shared library,
+   those variables don't show up until the library gets mapped and the symbol
+   table is read in.  */
 
 void
 sol_thread_new_objfile (objfile)
@@ -493,19 +848,23 @@ sol_thread_new_objfile (objfile)
       return;
     }
 
+  /* don't do anything if init failed to resolve the libthread_db library */
+  if (!procfs_suppress_run)
+    return;
+
   /* Now, initialize the thread debugging library.  This needs to be done after
      the shared libraries are located because it needs information from the
      user's thread library.  */
 
-  val = td_init ();
+  val = p_td_init ();
   if (val != TD_OK)
-    error ("target_new_objfile: td_init: %d", val);
+    error ("target_new_objfile: td_init: %s", td_err_string (val));
 
-  val = td_ta_new (&main_ph, &main_ta);
+  val = p_td_ta_new (&main_ph, &main_ta);
   if (val == TD_NOLIBTHREAD)
     return;
   else if (val != TD_OK)
-    error ("target_new_objfile: td_ta_new: %d", val);
+    error ("target_new_objfile: td_ta_new: %s", td_err_string (val));
 
   sol_thread_active = 1;
 }
@@ -515,142 +874,89 @@ sol_thread_new_objfile (objfile)
 static void
 sol_thread_mourn_inferior ()
 {
+  unpush_target (&sol_thread_ops);
   procfs_ops.to_mourn_inferior ();
 }
 
 /* Mark our target-struct as eligible for stray "run" and "attach" commands.  */
+
 static int
 sol_thread_can_run ()
 {
   return procfs_suppress_run;
 }
 
-int
-sol_thread_alive (pid)
-     int pid;
-{
-  return 1;
-}
-
-void
-sol_thread_stop ()
-{
-  procfs_ops.to_stop ();
-}
+/* 
 
-/* Service routines we must supply to libthread_db */
+LOCAL FUNCTION
 
-struct lwp_map
-{
-  struct lwp_map *next;
-  pid_t pid;
-  lwpid_t lwp;
-  int lwpfd;
-};
+       sol_thread_alive     - test thread for "aliveness"
 
-#if 0
-struct lwp_map *lwp_map;
+SYNOPSIS
 
-/* Create a /proc file descriptor for the given LWPID */
+       static bool sol_thread_alive (int pid);
 
-static ps_err_e
-get_lwp_fd (const struct ps_prochandle *ph, const lwpid_t lwpid, int *fd)
-{
-  struct lwp_map *lp;
+DESCRIPTION
 
-  for (lp = lwp_map; lp; lp = lp->next)
-    if (lp->pid = ph->pid
-       && lp->lwp == lwpid)
-      {
-       *fd = lp->lwpfd;
+       returns true if thread still active in inferior.
 
-       return PS_OK;
-      }
-       
-  lp = xmalloc (sizeof (struct lwp_map));
+ */
 
-  if ((lp->lwpfd = ioctl (ph->fd, PIOCOPENLWP, &lwpid)) < 0)
+static int
+sol_thread_alive (pid)
+     int pid;
+{
+  if (is_thread (pid))         /* non-kernel thread */
     {
-      print_sys_errmsg ("get_lwp_fd (): PIOCOPENLWP", errno);
-      return PS_BADLID;
+      td_err_e val;
+      td_thrhandle_t th;
+
+      pid = GET_THREAD (pid);
+      if ((val = p_td_ta_map_id2thr (main_ta, pid, &th)) != TD_OK)
+       return 0;       /* thread not found */
+      if ((val = p_td_thr_validate (&th)) != TD_OK)
+       return 0;       /* thread not valid */
+      return 1;                /* known thread: return true */
     }
+  else                 /* kernel thread (LWP): let procfs test it */
+    return procfs_ops.to_thread_alive (pid);
+}
 
-  lp->pid = ph->pid;
-  lp->lwp = lwpid;
-  lp->next = lwp_map;
-  lwp_map = lp;
-
-  *fd = lp->lwpfd;
-
-  return PS_OK;
+static void
+sol_thread_stop ()
+{
+  procfs_ops.to_stop ();
 }
-#endif
+\f
+/* These routines implement the lower half of the thread_db interface.  Ie: the
+   ps_* routines.  */
+
+/* The next four routines are called by thread_db to tell us to stop and stop
+   a particular process or lwp.  Since GDB ensures that these are all stopped
+   by the time we call anything in thread_db, these routines need to do
+   nothing.  */
 
 ps_err_e
 ps_pstop (const struct ps_prochandle *ph)
 {
-#if 0
-  if (ioctl (ph->fd, PIOCSTOP, NULL))
-    {
-      print_sys_errmsg ("ps_pstop (): PIOCSTOP", errno);
-      return PS_ERR;
-    }
-#endif
   return PS_OK;
 }
 
 ps_err_e
 ps_pcontinue (const struct ps_prochandle *ph)
 {
-#if 0
-  if (ioctl (ph->fd, PIOCRUN, NULL))
-    {
-      print_sys_errmsg ("ps_pcontinue (): PIOCRUN", errno);
-      return PS_ERR;
-    }
-#endif
   return PS_OK;
 }
 
 ps_err_e
 ps_lstop (const struct ps_prochandle *ph, lwpid_t lwpid)
 {
-  int lwp_fd;
-  ps_err_e val;
-
-#if 0
-  val = get_lwp_fd (ph, lwpid, &lwp_fd);
-  if (val != PS_OK)
-    return val;
-
-  if (ioctl (lwp_fd, PIOCSTOP, NULL))
-    {
-      print_sys_errmsg ("ps_lstop (): PIOCSTOP", errno);
-      return PS_ERR;
-    }
-#endif
-
   return PS_OK;
 }
 
 ps_err_e
 ps_lcontinue (const struct ps_prochandle *ph, lwpid_t lwpid)
 {
-  int lwp_fd;
-  ps_err_e val;
-
-#if 0
-  val = get_lwp_fd (ph, lwpid, &lwp_fd);
-  if (val != PS_OK)
-    return val;
-
-  if (ioctl (lwp_fd, PIOCRUN, NULL))
-    {
-      print_sys_errmsg ("ps_lcontinue (): PIOCRUN", errno);
-      return PS_ERR;
-    }
-#endif
-
   return PS_OK;
 }
 
@@ -670,16 +976,20 @@ ps_pglobal_lookup (const struct ps_prochandle *ph, const char *ld_object_name,
   return PS_OK;
 }
 
+/* Common routine for reading and writing memory.  */
+
 static ps_err_e
 rw_common (int dowrite, const struct ps_prochandle *ph, paddr_t addr,
           char *buf, int size)
 {
-  int save_pid;
+  struct cleanup *old_chain;
 
-  save_pid = inferior_pid;
+  old_chain = save_inferior_pid ();
 
-  if (inferior_pid & 0x80000000)
-    inferior_pid = main_ph.pid;        /* It's a thread.  Convert to lwp */
+  if (is_thread (inferior_pid) ||              /* A thread */
+      !target_thread_alive (inferior_pid))     /* An lwp, but not alive */
+    inferior_pid = procfs_first_available ();  /* Find any live lwp.  */
+  /* Note: don't need to call switch_to_thread; we're just reading memory.  */
 
   while (size > 0)
     {
@@ -690,11 +1000,11 @@ rw_common (int dowrite, const struct ps_prochandle *ph, paddr_t addr,
       if (cc < 0)
        {
          if (dowrite == 0)
-           print_sys_errmsg ("ps_pdread (): read", errno);
+           print_sys_errmsg ("rw_common (): read", errno);
          else
-           print_sys_errmsg ("ps_pdread (): write", errno);
+           print_sys_errmsg ("rw_common (): write", errno);
 
-         inferior_pid = save_pid;
+         do_cleanups (old_chain);
 
          return PS_ERR;
        }
@@ -702,7 +1012,7 @@ rw_common (int dowrite, const struct ps_prochandle *ph, paddr_t addr,
       buf += cc;
     }
 
-  inferior_pid = save_pid;
+  do_cleanups (old_chain);
 
   return PS_OK;
 }
@@ -731,38 +1041,42 @@ ps_ptwrite (const struct ps_prochandle *ph, paddr_t addr, char *buf, int size)
   return rw_common (1, ph, addr, buf, size);
 }
 
+/* Get integer regs */
+
 ps_err_e
 ps_lgetregs (const struct ps_prochandle *ph, lwpid_t lwpid,
             prgregset_t gregset)
 {
-  int save_pid;
+  struct cleanup *old_chain;
 
-  save_pid = inferior_pid;
+  old_chain = save_inferior_pid ();
 
-  inferior_pid = (lwpid << 16) | (inferior_pid & 0xffff);
+  inferior_pid = BUILD_LWP (lwpid, PIDGET (inferior_pid));
   
   procfs_ops.to_fetch_registers (-1);
   fill_gregset (gregset, -1);
 
-  inferior_pid = save_pid;
+  do_cleanups (old_chain);
 
   return PS_OK;
 }
 
+/* Set integer regs */
+
 ps_err_e
 ps_lsetregs (const struct ps_prochandle *ph, lwpid_t lwpid,
             const prgregset_t gregset)
 {
-  int save_pid;
+  struct cleanup *old_chain;
 
-  save_pid = inferior_pid;
+  old_chain = save_inferior_pid ();
 
-  inferior_pid = (lwpid << 16) | (inferior_pid & 0xffff);
+  inferior_pid = BUILD_LWP (lwpid, PIDGET (inferior_pid));
   
   supply_gregset (gregset);
   procfs_ops.to_store_registers (-1);
 
-  inferior_pid = save_pid;
+  do_cleanups (old_chain);
 
   return PS_OK;
 }
@@ -777,14 +1091,16 @@ ps_plog (const char *fmt, ...)
   vfprintf_filtered (gdb_stderr, fmt, args);
 }
 
+/* Get size of extra register set.  Currently a noop.  */
+
 ps_err_e
 ps_lgetxregsize (const struct ps_prochandle *ph, lwpid_t lwpid, int *xregsize)
 {
+#if 0
   int lwp_fd;
   int regsize;
   ps_err_e val;
 
-#if 0
   val = get_lwp_fd (ph, lwpid, &lwp_fd);
   if (val != PS_OK)
     return val;
@@ -803,13 +1119,15 @@ ps_lgetxregsize (const struct ps_prochandle *ph, lwpid_t lwpid, int *xregsize)
   return PS_OK;
 }
 
+/* Get extra register set.  Currently a noop.  */
+
 ps_err_e
 ps_lgetxregs (const struct ps_prochandle *ph, lwpid_t lwpid, caddr_t xregset)
 {
+#if 0
   int lwp_fd;
   ps_err_e val;
 
-#if 0
   val = get_lwp_fd (ph, lwpid, &lwp_fd);
   if (val != PS_OK)
     return val;
@@ -824,13 +1142,15 @@ ps_lgetxregs (const struct ps_prochandle *ph, lwpid_t lwpid, caddr_t xregset)
   return PS_OK;
 }
 
+/* Set extra register set.  Currently a noop.  */
+
 ps_err_e
 ps_lsetxregs (const struct ps_prochandle *ph, lwpid_t lwpid, caddr_t xregset)
 {
+#if 0
   int lwp_fd;
   ps_err_e val;
 
-#if 0
   val = get_lwp_fd (ph, lwpid, &lwp_fd);
   if (val != PS_OK)
     return val;
@@ -845,81 +1165,162 @@ ps_lsetxregs (const struct ps_prochandle *ph, lwpid_t lwpid, caddr_t xregset)
   return PS_OK;
 }
 
+/* Get floating-point regs.  */
+
 ps_err_e
 ps_lgetfpregs (const struct ps_prochandle *ph, lwpid_t lwpid,
               prfpregset_t *fpregset)
 {
-  int save_pid;
+  struct cleanup *old_chain;
 
-  save_pid = inferior_pid;
+  old_chain = save_inferior_pid ();
 
-  inferior_pid = (lwpid << 16) | (inferior_pid & 0xffff);
+  inferior_pid = BUILD_LWP (lwpid, PIDGET (inferior_pid));
 
   procfs_ops.to_fetch_registers (-1);
-  fill_fpregset (fpregset, -1);
+  fill_fpregset (*fpregset, -1);
 
-  inferior_pid = save_pid;
+  do_cleanups (old_chain);
 
   return PS_OK;
 }
 
+/* Set floating-point regs.  */
+
 ps_err_e
 ps_lsetfpregs (const struct ps_prochandle *ph, lwpid_t lwpid,
               const prfpregset_t *fpregset)
 {
-  int save_pid;
+  struct cleanup *old_chain;
 
-  save_pid = inferior_pid;
+  old_chain = save_inferior_pid ();
 
-  inferior_pid = (lwpid << 16) | (inferior_pid & 0xffff);
+  inferior_pid = BUILD_LWP (lwpid, PIDGET (inferior_pid));
   
-  supply_gregset (fpregset);
+  supply_fpregset (*fpregset);
   procfs_ops.to_store_registers (-1);
 
-  inferior_pid = save_pid;
+  do_cleanups (old_chain);
 
   return PS_OK;
 }
 \f
+/* Convert a pid to printable form. */
+
 char *
 solaris_pid_to_str (pid)
      int pid;
 {
   static char buf[100];
 
-  if (pid & 0x80000000)
+  /* in case init failed to resolve the libthread_db library */
+  if (!procfs_suppress_run)
+    return procfs_pid_to_str (pid);
+
+  if (is_thread (pid))
     {
       int lwp;
 
       lwp = thread_to_lwp (pid, -2);
 
-      if (lwp != -2)
-       sprintf (buf, "Thread %d (LWP %d)", (pid >> 16) & 0x7fff,
-                (lwp >> 16) & 0xffff);
+      if (lwp == -1)
+       sprintf (buf, "Thread %d (defunct)", GET_THREAD (pid));
+      else if (lwp != -2)
+       sprintf (buf, "Thread %d (LWP %d)", GET_THREAD (pid), GET_LWP (lwp));
       else
-       sprintf (buf, "Thread %d        ", (pid >> 16) & 0x7fff);
+       sprintf (buf, "Thread %d        ", GET_THREAD (pid));
     }
+  else if (GET_LWP (pid) != 0)
+    sprintf (buf, "LWP    %d        ", GET_LWP (pid));
   else
-    sprintf (buf, "LWP    %d        ", (pid >> 16) & 0xffff);
+    sprintf (buf, "process %d    ", PIDGET (pid));
 
   return buf;
 }
 \f
+
+#ifdef MAINTENANCE_CMDS
+/* Worker bee for info sol-thread command.  This is a callback function that
+   gets called once for each Solaris thread (ie. not kernel thread) in the 
+   inferior.  Print anything interesting that we can think of.  */
+
+static int 
+info_cb (th, s)
+     const td_thrhandle_t *th;
+     void *s;
+{
+  td_err_e ret;
+  td_thrinfo_t ti;
+  struct minimal_symbol *msym;
+
+  if ((ret = p_td_thr_get_info (th, &ti)) == TD_OK)
+    {
+      printf_filtered ("%s thread #%d, lwp %d, ", 
+                      ti.ti_type == TD_THR_SYSTEM ? "system" : "user  ", 
+                      ti.ti_tid, ti.ti_lid);
+      switch (ti.ti_state) {
+       default:
+       case TD_THR_UNKNOWN: printf_filtered ("<unknown state>");       break;
+       case TD_THR_STOPPED: printf_filtered ("(stopped)");     break;
+       case TD_THR_RUN:     printf_filtered ("(run)    ");     break;
+       case TD_THR_ACTIVE:  printf_filtered ("(active) ");     break;
+       case TD_THR_ZOMBIE:  printf_filtered ("(zombie) ");     break;
+       case TD_THR_SLEEP:   printf_filtered ("(asleep) ");     break;
+       case TD_THR_STOPPED_ASLEEP: 
+         printf_filtered ("(stopped asleep)");                 break;
+      }
+      /* Print thr_create start function: */
+      if (ti.ti_startfunc != 0)
+       if (msym = lookup_minimal_symbol_by_pc (ti.ti_startfunc))
+         printf_filtered ("   startfunc: %s\n", SYMBOL_NAME (msym));
+       else
+         printf_filtered ("   startfunc: 0x%08x\n", ti.ti_startfunc);
+
+      /* If thread is asleep, print function that went to sleep: */
+      if (ti.ti_state == TD_THR_SLEEP)
+       if (msym = lookup_minimal_symbol_by_pc (ti.ti_pc))
+         printf_filtered (" - Sleep func: %s\n", SYMBOL_NAME (msym));
+       else
+         printf_filtered (" - Sleep func: 0x%08x\n", ti.ti_startfunc);
+
+      /* Wrap up line, if necessary */
+      if (ti.ti_state != TD_THR_SLEEP && ti.ti_startfunc == 0)
+       printf_filtered ("\n"); /* don't you hate counting newlines? */
+    }
+  else
+    warning ("info sol-thread: failed to get info for thread.");
+
+  return 0;    
+}
+
+/* List some state about each Solaris user thread in the inferior.  */
+
+static void
+info_solthreads (args, from_tty)
+     char *args;
+     int from_tty;
+{
+  p_td_ta_thr_iter (main_ta, info_cb, args, 
+                   TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+                   TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+}
+#endif /* MAINTENANCE_CMDS */
+
 struct target_ops sol_thread_ops = {
   "solaris-threads",           /* to_shortname */
   "Solaris threads and pthread.", /* to_longname */
   "Solaris threads and pthread support.", /* to_doc */
   sol_thread_open,             /* to_open */
   0,                           /* to_close */
-  sol_thread_attach,                   /* to_attach */
+  sol_thread_attach,           /* to_attach */
   sol_thread_detach,           /* to_detach */
-  sol_thread_resume,                   /* to_resume */
-  sol_thread_wait,                     /* to_wait */
+  sol_thread_resume,           /* to_resume */
+  sol_thread_wait,             /* to_wait */
   sol_thread_fetch_registers,  /* to_fetch_registers */
   sol_thread_store_registers,  /* to_store_registers */
   sol_thread_prepare_to_store, /* to_prepare_to_store */
-  sol_thread_xfer_memory,              /* to_xfer_memory */
-  sol_thread_files_info,               /* to_files_info */
+  sol_thread_xfer_memory,      /* to_xfer_memory */
+  sol_thread_files_info,       /* to_files_info */
   memory_insert_breakpoint,    /* to_insert_breakpoint */
   memory_remove_breakpoint,    /* to_remove_breakpoint */
   terminal_init_inferior,      /* to_terminal_init */
@@ -951,7 +1352,57 @@ struct target_ops sol_thread_ops = {
 void
 _initialize_sol_thread ()
 {
+  void *dlhandle;
+
+  dlhandle = dlopen ("libthread_db.so.1", RTLD_NOW);
+  if (!dlhandle)
+    goto die;
+
+#define resolve(X) \
+  if (!(p_##X = dlsym (dlhandle, #X))) \
+    goto die;
+
+  resolve (td_log);
+  resolve (td_ta_new);
+  resolve (td_ta_delete);
+  resolve (td_init);
+  resolve (td_ta_get_ph);
+  resolve (td_ta_get_nthreads);
+  resolve (td_ta_tsd_iter);
+  resolve (td_ta_thr_iter);
+  resolve (td_thr_validate);
+  resolve (td_thr_tsd);
+  resolve (td_thr_get_info);
+  resolve (td_thr_getfpregs);
+  resolve (td_thr_getxregsize);
+  resolve (td_thr_getxregs);
+  resolve (td_thr_sigsetmask);
+  resolve (td_thr_setprio);
+  resolve (td_thr_setsigpending);
+  resolve (td_thr_setfpregs);
+  resolve (td_thr_setxregs);
+  resolve (td_ta_map_id2thr);
+  resolve (td_ta_map_lwp2thr);
+  resolve (td_thr_getgregs);
+  resolve (td_thr_setgregs);
+
   add_target (&sol_thread_ops);
 
   procfs_suppress_run = 1;
+
+#ifdef MAINTENANCE_CMDS
+  add_cmd ("sol-threads", class_maintenance, info_solthreads, 
+           "Show info on Solaris user threads.\n", &maintenanceinfolist);
+#endif /* MAINTENANCE_CMDS */
+
+  return;
+
+ die:
+
+  fprintf_unfiltered (gdb_stderr, "[GDB will not be able to debug user-mode threads: %s]\n", dlerror ());
+
+  if (dlhandle)
+    dlclose (dlhandle);
+
+  return;
 }
This page took 0.038208 seconds and 4 git commands to generate.