Module Name:    src
Committed By:   christos
Date:           Mon Sep 26 18:55:57 UTC 2011

Added Files:
        src/external/gpl3/gdb/dist/gdb: nbsd-thread.c

Log Message:
Resurrect old thread debugging code. Not hooked in, compiles.


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/external/gpl3/gdb/dist/gdb/nbsd-thread.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Added files:

Index: src/external/gpl3/gdb/dist/gdb/nbsd-thread.c
diff -u /dev/null src/external/gpl3/gdb/dist/gdb/nbsd-thread.c:1.1
--- /dev/null	Mon Sep 26 14:55:57 2011
+++ src/external/gpl3/gdb/dist/gdb/nbsd-thread.c	Mon Sep 26 14:55:56 2011
@@ -0,0 +1,1297 @@
+/* Thread debugging back-end code for NetBSD, for GDB.
+   Copyright 2002
+   Wasabi Systems, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   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.  */
+
+
+#include "defs.h"
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+
+#include <pthread.h>
+#include <pthread_dbg.h>
+
+#include "symtab.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "solib.h"
+#include "gdbthread.h"
+#include "bfd.h"
+#include "elf-bfd.h"
+#include "target.h"
+#include "inferior.h"
+#include "gdbcmd.h"
+#include "gdbcore.h"
+
+#include <machine/reg.h>
+
+#ifndef HAVE_GREGSET_T
+typedef struct reg gregset_t;
+#endif
+
+#ifndef HAVE_FPREGSET_T
+typedef struct fpreg fpregset_t;
+#endif
+
+#include "gregset.h"
+
+/* nbsd_thread_present indicates that new_objfile has spotted
+   libpthread and that post_attach() or create_inferior() should fire
+   up thread debugging if it isn't already active. */
+static int nbsd_thread_present = 0;
+
+/* nbsd_thread_active indicates that thread debugging is up and running, and
+   in particular that main_ta and main_ptid are valid. */
+static int nbsd_thread_active = 0;
+
+/* nbsd_thread_core indicates that we're working on a corefile, not a
+   live process. */ 
+static int nbsd_thread_core = 0;
+
+static ptid_t main_ptid;		/* Real process ID */
+
+static ptid_t cached_thread;
+
+struct target_ops nbsd_thread_ops;
+struct target_ops nbsd_core_ops;
+
+struct td_proc_callbacks_t nbsd_thread_callbacks;
+struct td_proc_callbacks_t nbsd_core_callbacks;
+
+/* place to store core_ops before we overwrite it */
+static struct target_ops orig_core_ops;
+
+extern struct target_ops child_ops;  /* target vector for inftarg.c */
+extern struct target_ops core_ops; /* target vector for corelow.c */
+
+extern int child_suppress_run;
+
+static ptid_t find_active_thread (void);
+static void nbsd_find_new_threads (struct target_ops *);
+
+#define GET_PID(ptid)		ptid_get_pid (ptid)
+#define GET_LWP(ptid)		ptid_get_lwp (ptid)
+#define GET_THREAD(ptid)	ptid_get_tid (ptid)
+
+#define IS_LWP(ptid)		(GET_LWP (ptid) != 0)
+#define IS_THREAD(ptid)		(GET_THREAD (ptid) != 0)
+
+#define BUILD_LWP(lwp, ptid)	ptid_build (GET_PID(ptid), lwp, 0)
+#define BUILD_THREAD(tid, ptid)	ptid_build (GET_PID(ptid), 0, tid)
+
+static td_proc_t *main_ta;
+
+struct nbsd_thread_proc_arg {
+  struct target_ops *ops;
+  struct regcache *cache;
+} main_arg;
+
+static const char *syncnames[] = {
+  "unknown", "mutex", "cond var", "spinlock", "thread"
+};
+
+struct string_map
+  {
+    int num;
+    char *str;
+  };
+
+static char *
+td_err_string (int errcode)
+{
+  static struct string_map
+    td_err_table[] =
+  {
+    {TD_ERR_OK, "generic \"call succeeded\""},
+    {TD_ERR_ERR, "generic error."},
+    {TD_ERR_NOSYM, "symbol not found"},
+    {TD_ERR_NOOBJ, "no object can be found to satisfy query"},
+    {TD_ERR_BADTHREAD, "thread can not answer request"},
+    {TD_ERR_INUSE, "debugging interface already in use for this process"},
+    {TD_ERR_NOLIB, "process is not using libpthread"},
+    {TD_ERR_NOMEM, "out of memory"},
+    {TD_ERR_IO, "process callback error"},
+    {TD_ERR_INVAL, "invalid argument"},
+  };
+  const int td_err_size = sizeof td_err_table / sizeof (struct string_map);
+  int i;
+  static char buf[90];
+
+  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;
+}
+
+static void
+nbsd_thread_activate (void)
+{
+  nbsd_thread_active = 1;
+  main_ptid = inferior_ptid;
+  cached_thread = minus_one_ptid;
+  nbsd_find_new_threads (NULL);
+  inferior_ptid = find_active_thread ();
+}
+
+static void
+nbsd_thread_deactivate (void)
+{
+  td_close (main_ta);
+
+  inferior_ptid = main_ptid;
+  main_ptid = minus_one_ptid;
+  cached_thread = main_ptid;
+  nbsd_thread_active = 0;
+  nbsd_thread_present = 0;
+  init_thread_list ();
+}
+
+static void
+nbsd_thread_attach (struct target_ops *ops, char *args, int from_tty)
+{
+  nbsd_thread_core = 0;
+
+  if (nbsd_thread_present && !nbsd_thread_active)
+    push_target(&nbsd_thread_ops);
+
+  child_ops.to_attach (ops, args, from_tty);
+
+  /* seems like a good place to activate, but isn't. Let it happen in
+     nbsd_thread_post_attach(), after a wait has occurred. */
+}
+
+static void
+nbsd_thread_post_attach (int pid)
+{
+  child_ops.to_post_attach (pid);
+
+  if (nbsd_thread_present && !nbsd_thread_active)
+    nbsd_thread_activate ();
+}
+
+
+/* Take a program previously attached to and detaches it.
+   The program resumes execution and will no longer stop
+   on signals, etc.  We'd better not have left any breakpoints
+   in the program or it'll die when it hits one.  For this
+   to work, it may be necessary for the process to have been
+   previously attached.  It *might* work if the program was
+   started via the normal ptrace (PTRACE_TRACEME).  */
+
+static void
+nbsd_thread_detach (struct target_ops *ops, char *args, int from_tty)
+{
+  struct target_ops *beneath = find_target_beneath (ops);
+  nbsd_thread_deactivate ();
+  unpush_target (ops);
+  /* Ordinairly, gdb caches solib information, but this means that it
+     won't call the new_obfile hook on a reattach. Clear the symbol file
+     cache so that attach -> detach -> attach works. */
+  clear_solib();
+  symbol_file_clear(0);
+  child_ops.to_detach (beneath, args, from_tty);
+}
+
+static int nsusp;
+static int nsuspalloc;
+static td_thread_t **susp;
+
+static int
+thread_resume_suspend_cb (td_thread_t *th, void *arg)
+{
+  int val;
+  ptid_t *pt = arg;
+  td_thread_info_t ti;
+
+  if (td_thr_info (th, &ti) != 0)
+      return -1;
+
+  if ((ti.thread_id != GET_THREAD (*pt)) &&
+      (ti.thread_type == TD_TYPE_USER) &&
+      (ti.thread_state != TD_STATE_SUSPENDED) &&
+      (ti.thread_state != TD_STATE_ZOMBIE))
+    {
+      val = td_thr_suspend(th);
+      if (val != 0)
+	error ("thread_resume_suspend_cb: td_thr_suspend(%p): %s", th,
+	       td_err_string (val));
+	
+      if (nsusp == nsuspalloc)
+	{
+	  if (nsuspalloc == 0)
+	    {
+	      nsuspalloc = 32;
+	      susp = malloc (nsuspalloc * sizeof(td_thread_t *));
+	      if (susp == NULL)
+		error ("thread_resume_suspend_cb: out of memory\n");
+	    }
+	  else
+	    {
+	      static td_thread_t **newsusp;
+	      nsuspalloc *= 2;
+	      newsusp = realloc (susp, nsuspalloc * sizeof(td_thread_t *));
+	      if (newsusp == NULL)
+		error ("thread_resume_suspend_cb: out of memory\n");
+	      susp = newsusp;
+	    }
+	}
+      susp[nsusp] = th;
+      nsusp++;
+    }
+  
+  return 0;
+}
+
+static void
+nbsd_thread_resume (struct target_ops *ops, ptid_t ptid, int step,
+    enum target_signal signo)
+{
+
+  /* If a particular thread is specified, then gdb wants to resume or
+     step just that thread. If it isn't on a processor, then it needs
+     to be put on one, and nothing else can be on the runnable list.
+     XXX If GDB asks us to step a LWP rather than a thread, there
+     isn't anything we can do but pass it down to the ptrace call;
+     given the flexibility of the LWP-to-thread mapping, this might or
+     might not accomplish what the user wanted. */
+  if (GET_PID (ptid) != -1 && IS_THREAD (ptid))
+    {
+      int val;
+
+      val = td_thr_iter (main_ta, thread_resume_suspend_cb, &ptid);
+      if (val != 0)
+	error ("nbsd_thread_resume td_thr_iter: %s", td_err_string (val));
+
+	child_ops.to_resume (ops, ptid, step, signo);
+
+      /* can't un-suspend just yet, child may not be stopped */
+    }
+  else
+    child_ops.to_resume (ops, ptid, step, signo);
+
+  cached_thread = minus_one_ptid;
+}
+
+
+static void
+nbsd_thread_unsuspend(void)
+{
+  int i, val;
+
+  for (i = 0; i < nsusp; i++)
+    {
+      val = td_thr_resume(susp[i]);
+      if (val != 0)
+	error ("nbsd_thread_unsuspend: td_thr_resume(%p): %s", susp[i],
+	       td_err_string (val));
+    }
+  nsusp = 0;
+}
+  
+static ptid_t
+find_active_thread (void)
+{
+  int val;
+  td_thread_t *thread;
+  td_thread_info_t ti;
+  struct ptrace_lwpinfo pl;
+
+  if (!ptid_equal (cached_thread, minus_one_ptid))
+    return cached_thread;
+
+  if (target_has_execution)
+    {
+      pl.pl_lwpid = 0;
+      val = ptrace (PT_LWPINFO, GET_PID(inferior_ptid), (void *)&pl, sizeof(pl));
+      while ((val != -1) && (pl.pl_lwpid != 0) &&
+	     (pl.pl_event != PL_EVENT_SIGNAL))
+	val = ptrace (PT_LWPINFO, GET_PID(inferior_ptid), (void *)&pl, sizeof(pl));
+    }
+
+  cached_thread = BUILD_LWP (pl.pl_lwpid, main_ptid);
+  return cached_thread;
+}
+
+
+/* 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 ptid_t
+nbsd_thread_wait (struct target_ops *ops, ptid_t ptid,
+  struct target_waitstatus *ourstatus, int options)
+{
+  ptid_t rtnval;
+
+  rtnval = child_ops.to_wait (ops, ptid, ourstatus, options);
+
+  nbsd_thread_unsuspend();
+
+  if (nbsd_thread_active && (ourstatus->kind != TARGET_WAITKIND_EXITED))
+    {
+      rtnval = find_active_thread ();
+      if (ptid_equal (rtnval, minus_one_ptid))
+	error ("No active thread!\n");
+      if (!in_thread_list (rtnval))
+	add_thread (rtnval);
+    }
+
+  return rtnval;
+}
+
+static void
+nbsd_thread_fetch_registers (struct target_ops *ops, struct regcache *cache,
+    int regno)
+{
+  td_thread_t *thread;
+  gregset_t gregs;
+  fpregset_t fpregs;
+  int val;
+  struct cleanup *old_chain;
+
+  old_chain = save_inferior_ptid ();
+
+  if (nbsd_thread_active && IS_THREAD (inferior_ptid))
+    {
+      if ((val = td_map_id2thr (main_ta, GET_THREAD (inferior_ptid), &thread)) != 0)
+	error ("nbsd_thread_fetch_registers: td_map_id2thr: %s\n",
+	       td_err_string (val));
+      if ((val = td_thr_getregs (thread, 0, &gregs)) != 0)
+	error ("nbsd_thread_fetch_registers: td_thr_getregs: %s\n",
+	       td_err_string (val));
+      supply_gregset (cache, &gregs);
+      if ((val = td_thr_getregs (thread, 1, &fpregs)) == 0)
+	      supply_fpregset (cache, &fpregs);
+    }
+  else
+    {
+      if (target_has_execution)
+	child_ops.to_fetch_registers (ops, cache, regno);
+      else
+	{
+	  inferior_ptid = pid_to_ptid ((GET_LWP (inferior_ptid) << 16) | 
+				        GET_PID (inferior_ptid));
+	  orig_core_ops.to_fetch_registers (ops, cache, regno);
+	}
+    }
+  
+  do_cleanups (old_chain);
+}
+
+static void
+nbsd_thread_store_registers (struct target_ops *ops, struct regcache *cache,
+    int regno)
+{
+  td_thread_t *thread;
+  gregset_t gregs;
+  fpregset_t fpregs;
+  int val;
+
+  if (nbsd_thread_active && IS_THREAD (inferior_ptid))
+    {
+      val = td_map_id2thr (main_ta, GET_THREAD (inferior_ptid), &thread);
+      if (val != 0)
+	error ("nbsd_thread_store_registers: td_map_id2thr: %s\n",
+	      td_err_string (val));
+
+      fill_gregset (cache, &gregs, -1);
+      fill_fpregset (cache, &fpregs, -1);
+
+      val = td_thr_setregs (thread, 0, &gregs);
+      if (val != 0)
+	error ("nbsd_thread_store_registers: td_thr_setregs: %s\n",
+	      td_err_string (val));
+      val = td_thr_setregs (thread, 1, &fpregs);
+      if (val != 0)
+	error ("nbsd_thread_store_registers: td_thr_setregs: %s\n",
+	      td_err_string (val));
+    }
+  else
+    {
+      if (target_has_execution)
+	child_ops.to_store_registers (ops, cache, regno);
+      else
+	orig_core_ops.to_store_registers (ops, cache, regno);
+    }
+
+}
+
+
+
+static LONGEST
+nbsd_thread_xfer_partial (struct target_ops *ops, enum target_object object,
+			  const char *annex, gdb_byte *readbuf,
+			  const gdb_byte *writebuf,  ULONGEST offset,
+			  LONGEST len)
+{
+  LONGEST val;
+
+  if (target_has_execution)
+    val = child_ops.to_xfer_partial (ops, object, annex, readbuf, writebuf,
+				     offset, len);
+  else
+    val = orig_core_ops.to_xfer_partial (ops, object, annex, readbuf, writebuf,
+					 offset, len);
+
+  return val;
+}
+
+/* Clean up after the inferior dies.  */
+
+static void
+nbsd_thread_mourn_inferior (struct target_ops *ops)
+{
+  struct target_ops *beneath = find_target_beneath (ops);
+
+  if (nbsd_thread_active)
+    nbsd_thread_deactivate ();
+
+  unpush_target (ops);
+  child_ops.to_mourn_inferior (beneath);
+}
+
+
+static void
+nbsd_thread_files_info (struct target_ops *ignore)
+{
+  child_ops.to_files_info (ignore);
+}
+
+static void
+nbsd_core_files_info (struct target_ops *ignore)
+{
+  orig_core_ops.to_files_info (ignore);
+}
+
+/* Convert a ptid to printable form. */
+
+char *
+nbsd_pid_to_str (struct target_ops *ops, ptid_t ptid)
+{
+  static char buf[100];
+  td_thread_t *th;
+  char name[32];
+
+  if ((GET_THREAD(ptid) == 0) &&
+      (GET_LWP(ptid) == 0) && 
+      (nbsd_thread_active == 0))
+    sprintf (buf, "process %d", GET_PID (ptid));
+  else if (IS_THREAD (ptid))
+    {
+      if ((td_map_id2thr (main_ta, GET_THREAD (ptid), &th) == 0) &&
+	  (td_thr_getname (th, name, 32) == 0))
+	sprintf (buf, "Thread %ld (%s)", GET_THREAD (ptid), name);
+      else
+	sprintf (buf, "Thread %ld", GET_THREAD (ptid));
+    }
+  else
+    sprintf (buf, "LWP %ld", GET_LWP (ptid));
+
+  return buf;
+}
+
+
+/* This routine is called whenever a new symbol table is read in, or when all
+   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.  */
+
+/* This new_objfile event is now managed by a chained function pointer.
+ * It is the callee's responsability to call the next client on the chain.
+ */
+
+void
+nbsd_thread_new_objfile (struct objfile *objfile)
+{
+  int val;
+
+  if (!objfile)
+    {
+      nbsd_thread_active = 0;
+      goto quit;
+    }
+
+  /* don't do anything if init failed */
+  if (!child_suppress_run)
+    goto quit;
+
+  /* Don't do anything if we've already fired up the debugging library */
+  if (nbsd_thread_active)
+    goto quit;
+
+  /* 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_open (&nbsd_thread_callbacks, &main_arg, &main_ta);
+  if (val == TD_ERR_NOLIB)
+    goto quit;
+  else if (val != 0)
+    {
+      warning ("nbsd_thread_new_objfile: td_open: %s", td_err_string (val));
+      goto quit;
+    }
+
+  nbsd_thread_present = 1;
+
+  if ((nbsd_thread_core == 0) && 
+      !ptid_equal (inferior_ptid, null_ptid))
+    {
+      push_target (&nbsd_thread_ops);
+      nbsd_thread_activate();
+    }
+quit:
+  return;
+}
+
+static int
+nbsd_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+  td_thread_t *th;
+  td_thread_info_t ti;
+  int val;
+
+  if (nbsd_thread_active)
+    {
+      if (IS_THREAD (ptid))
+	{
+	  val = 0;
+	  if (td_map_id2thr (main_ta, GET_THREAD (ptid), &th) == 0)
+	    {
+	      /* Thread found */
+	      if ((td_thr_info (th, &ti) == 0) &&
+		  (ti.thread_state != TD_STATE_ZOMBIE) &&
+		  (ti.thread_type != TD_TYPE_SYSTEM))
+		val = 1;
+	    }
+	}
+      else if (IS_LWP (ptid))
+	{
+	  struct ptrace_lwpinfo pl;
+	  pl.pl_lwpid = GET_LWP (ptid);
+	  val = ptrace (PT_LWPINFO, GET_PID (ptid), (void *)&pl, sizeof(pl));
+	  if (val == -1)
+	    val = 0;
+	  else
+	    val = 1;
+	}
+      else
+	val = child_ops.to_thread_alive (ops, ptid);
+    }
+  else
+    val = child_ops.to_thread_alive (ops, ptid);
+
+  return val;
+}
+
+static int
+nbsd_core_thread_alive (struct target_ops *ops, ptid_t ptid)
+{
+  return orig_core_ops.to_thread_alive (ops, ptid);
+}
+
+
+/* Worker bee for find_new_threads
+   Callback function that gets called once per USER thread (i.e., not
+   kernel) thread. */
+
+static int
+nbsd_find_new_threads_callback (td_thread_t *th, void *ignored)
+{
+  td_thread_info_t ti;
+  ptid_t ptid;
+
+  if (td_thr_info (th, &ti) != 0)
+      return -1;
+
+  ptid = BUILD_THREAD (ti.thread_id, main_ptid);
+  if (ti.thread_type == TD_TYPE_USER &&
+      ti.thread_state != TD_STATE_BLOCKED &&
+      ti.thread_state != TD_STATE_ZOMBIE &&
+      !in_thread_list (ptid))
+    add_thread (ptid);
+
+  return 0;
+}
+
+static void
+nbsd_find_new_threads (struct target_ops *ops)
+{
+  int retval;
+  ptid_t ptid;
+  td_thread_t *thread;
+
+  if (nbsd_thread_active == 0)
+	  return;
+
+  /* don't do anything if init failed to resolve the libthread_db library */
+  if (!child_suppress_run)
+    return;
+
+  if (ptid_equal (inferior_ptid, minus_one_ptid))
+    {
+      printf_filtered ("No process.\n");
+      return;
+    }
+
+  if (target_has_execution)
+    {
+      struct ptrace_lwpinfo pl;
+      pl.pl_lwpid = 0;
+      retval = ptrace (PT_LWPINFO, GET_PID(inferior_ptid), (void *)&pl, sizeof(pl));
+      while ((retval != -1) && pl.pl_lwpid != 0)
+	{
+	  ptid = BUILD_LWP (pl.pl_lwpid, main_ptid);
+	  td_map_lwp2thr (main_ta, pl.pl_lwpid, &thread);
+	  if (!in_thread_list (ptid))
+	    add_thread (ptid);
+	  retval = ptrace (PT_LWPINFO, GET_PID(inferior_ptid), (void *)&pl, sizeof(pl));
+	}
+    }
+
+  retval = td_thr_iter (main_ta, nbsd_find_new_threads_callback, (void *) 0);
+  if (retval != 0)
+    error ("nbsd_find_new_threads: td_thr_iter: %s",
+	   td_err_string (retval));
+}
+
+
+/* Mark our target-struct as eligible for stray "run" and "attach" commands.  */
+
+static int
+nbsd_thread_can_run (void)
+{
+  return child_suppress_run;
+}
+
+
+/* Fork an inferior process, and start debugging it with /proc.  */
+
+static void
+nbsd_thread_create_inferior (struct target_ops *ops, char *exec_file,
+			     char *allargs, char **env, int from_tty)
+{
+  nbsd_thread_core = 0;
+
+  if (nbsd_thread_present && !nbsd_thread_active)
+    push_target(&nbsd_thread_ops);
+
+  child_ops.to_create_inferior (ops, exec_file, allargs, env, from_tty);
+
+  if (nbsd_thread_present && !nbsd_thread_active)
+    nbsd_thread_activate();
+}
+
+
+static int
+waiter_cb (td_thread_t *th, void *s)
+{
+  int ret;
+  td_thread_info_t ti;
+
+  if ((ret = td_thr_info (th, &ti)) == 0)
+    {
+      wrap_here (NULL);
+      printf_filtered (" %d", ti.thread_id);
+    }
+
+  return 0;
+}
+
+/* Worker bee for thread state command.  This is a callback function that
+   gets called once for each user thread (ie. not kernel thread) in the
+   inferior.  Print anything interesting that we can think of.  */
+
+static int
+info_cb (td_thread_t *th, void *s)
+{
+  int ret;
+  td_thread_info_t ti, ti2;
+  td_sync_t *ts;
+  td_sync_info_t tsi;
+  char name[32];
+
+  if ((ret = td_thr_info (th, &ti)) == 0)
+    {
+      if (ti.thread_type != TD_TYPE_USER)
+	return 0;
+      td_thr_getname(th, name, 32);
+      printf_filtered ("%p: thread %4d ", ti.thread_addr, ti.thread_id);
+      if (name[0] != '\0')
+	      printf_filtered("(%s) ", name);
+
+      switch (ti.thread_state)
+	{
+	default:
+	case TD_STATE_UNKNOWN:
+	  printf_filtered ("<unknown state> ");
+	  break;
+	case TD_STATE_RUNNING:
+	  printf_filtered ("running  ");
+	  break;
+	case TD_STATE_RUNNABLE:
+	  printf_filtered ("active   ");
+	  break;
+	case TD_STATE_BLOCKED:
+	  printf_filtered ("in kernel");
+	  break;
+	case TD_STATE_SLEEPING:
+	  printf_filtered ("sleeping ");
+	  break;
+	case TD_STATE_ZOMBIE:
+	  printf_filtered ("zombie   ");
+	  break;
+	}
+
+      if (ti.thread_state == TD_STATE_SLEEPING)
+	{
+	  td_thr_sleepinfo (th, &ts);
+	  td_sync_info (ts, &tsi);
+	  if (tsi.sync_type == TD_SYNC_JOIN)
+	    {
+	      td_thr_info (tsi.sync_data.join.thread, &ti2);
+	      printf ("joining thread %d ", ti2.thread_id);
+	    }
+	  else
+	    {
+	      printf_filtered ("on %s at %p ",
+			       syncnames[tsi.sync_type],
+			       (void *)tsi.sync_addr);
+	    }
+	}
+
+      if (ti.thread_hasjoiners)
+	{
+	  printf_filtered ("(being joined by");
+	  td_thr_join_iter (th, waiter_cb, NULL);
+	  printf_filtered (")");
+	}
+      printf_filtered ("\n");
+    }
+  else
+    warning ("info nbsd-thread: failed to get info for thread.");
+
+  return 0;
+}
+
+
+/* List some state about each user thread in the inferior.  */
+
+static void
+nbsd_thread_examine_all_cmd (char *args, int from_tty)
+{
+  int val;
+
+  if (nbsd_thread_active)
+    {
+      val = td_thr_iter (main_ta, info_cb, args);
+      if (val != 0)
+	error ("nbsd_thread_examine_all_cmd: td_thr_iter: %s",
+	       td_err_string (val));
+    }
+  else
+    printf_filtered ("Thread debugging not active.\n");
+}
+
+static void
+nbsd_thread_examine_cmd (char *exp, int from_tty)
+{
+  CORE_ADDR addr;
+  td_thread_t *th;
+  int ret;
+
+  if (!nbsd_thread_active)
+    {
+      warning ("Thread debugging not active.\n");
+      return;
+    }
+
+  if (exp != NULL && *exp != '\000')
+    {
+      addr = parse_and_eval_address (exp);
+      if (from_tty)
+	*exp = 0;
+    }
+  else
+    return;
+
+  if ((ret = td_map_pth2thr (main_ta, (pthread_t) addr, &th)) != 0)
+    error ("nbsd_thread_examine_command: td_map_pth2thr: %s",
+	   td_err_string (ret));
+  
+  info_cb (th, NULL);
+}
+
+
+static void
+nbsd_thread_sync_cmd (char *exp, int from_tty)
+{
+  CORE_ADDR addr;
+  td_sync_t *ts;
+  td_sync_info_t tsi;
+  td_thread_info_t ti;
+  int ret;
+
+  if (!nbsd_thread_active)
+    {
+      warning ("Thread debugging not active.\n");
+      return;
+    }
+
+  if (exp != NULL && *exp != '\000')
+    {
+      addr = parse_and_eval_address (exp);
+      if (from_tty)
+	*exp = 0;
+    }
+  else
+    return;
+
+  if ((ret = td_map_addr2sync (main_ta, (caddr_t)addr, &ts)) != 0)
+    error ("nbsd_thread_sync_cmd: td_map_addr2sync: %s", td_err_string (ret));
+
+  if ((ret = td_sync_info (ts, &tsi)) != 0)
+    error ("nbsd_thread_sync_cmd: td_sync_info: %s", td_err_string (ret));
+
+  printf_filtered ("%p: %s", (void *)addr, syncnames[tsi.sync_type]);
+
+  if (tsi.sync_type == TD_SYNC_MUTEX)
+    {
+      if (!tsi.sync_data.mutex.locked)
+	printf_filtered (" unlocked");
+      else
+	{
+	  td_thr_info (tsi.sync_data.mutex.owner, &ti);
+	  printf_filtered (" locked by thread %d", ti.thread_id);
+	}
+    }
+  else if (tsi.sync_type == TD_SYNC_SPIN)
+    {
+      if (!tsi.sync_data.spin.locked)
+	printf_filtered (" unlocked");
+      else
+	printf_filtered (" locked (waiters not tracked)");
+    }
+  else if (tsi.sync_type == TD_SYNC_JOIN)
+    {
+      td_thr_info (tsi.sync_data.join.thread, &ti);
+      printf_filtered (" %d", ti.thread_id);
+    }
+  else if (tsi.sync_type == TD_SYNC_RWLOCK)
+    {
+      if (!tsi.sync_data.rwlock.locked)
+	printf_filtered (" unlocked");
+      else
+	{
+	  printf_filtered (" locked");
+	  if (tsi.sync_data.rwlock.readlocks > 0)
+	    printf_filtered (" by %d reader%s", 
+			     tsi.sync_data.rwlock.readlocks,
+			     (tsi.sync_data.rwlock.readlocks > 1) ? "s" : "");
+	  else
+	    {
+	      td_thr_info (tsi.sync_data.rwlock.writeowner, &ti);
+	      printf_filtered (" by writer %d", ti.thread_id);
+	    }
+	}
+    }
+  else
+    printf_filtered("Unknown sync object type %d", tsi.sync_type);
+
+  if (tsi.sync_haswaiters)
+    {
+      printf_filtered (" waiters:");
+      if ((ret = td_sync_waiters_iter (ts, waiter_cb, NULL)) != 0)
+	error ("nbsd_thread_sync_cmd: td_sync_info: %s",
+	       td_err_string (ret));
+    }
+
+  printf_filtered ("\n");
+}
+
+int
+tsd_cb (pthread_key_t key, void (*destructor)(void *), void *ignore)
+{
+  struct minimal_symbol *ms;
+  char *name;
+
+  printf_filtered ("Key %3d   ", key);
+
+  /* XXX What does GDB use to print a function? */
+  ms = lookup_minimal_symbol_by_pc ((CORE_ADDR)destructor);
+
+  if (!ms)
+    name = "???";
+  else
+    name = SYMBOL_PRINT_NAME (ms);
+
+  printf_filtered ("Destructor %p <%s>\n", destructor, name);
+
+  return 0;
+}
+
+static void
+nbsd_thread_tsd_cmd (char *exp, int from_tty)
+{
+  td_tsd_iter (main_ta, tsd_cb, NULL);
+}
+
+static void
+nbsd_add_to_thread_list (bfd *abfd, asection *asect, PTR reg_sect_arg)
+{
+  int regval;
+  td_thread_t *dummy;
+
+  if (strncmp (bfd_section_name (abfd, asect), ".reg/", 5) != 0)
+    return;
+
+  regval = atoi (bfd_section_name (abfd, asect) + 5);
+
+  td_map_lwp2thr (main_ta, regval >> 16, &dummy);
+
+  add_thread (BUILD_LWP(regval >> 16, main_ptid));
+}
+
+static void
+nbsd_core_open (char *filename, int from_tty)
+{
+  int val;
+
+  nbsd_thread_core = 1;
+
+  orig_core_ops.to_open (filename, from_tty);
+
+  if (nbsd_thread_present)
+    {
+      val = td_open (&nbsd_thread_callbacks, NULL, &main_ta);
+      if (val == 0)
+	{
+	  main_ptid = pid_to_ptid (elf_tdata (core_bfd)->core_pid);
+	  nbsd_thread_active = 1;
+	  init_thread_list ();
+	  bfd_map_over_sections (core_bfd, nbsd_add_to_thread_list, NULL);
+	  nbsd_find_new_threads (NULL);
+	}
+      else
+	error ("nbsd_core_open: td_open: %s", td_err_string (val));
+    }
+}
+
+static void
+nbsd_core_close (int quitting)
+{
+  /* XXX Setting these here is a gross hack. It needs to be set to
+   * XXX the "current thread ID" when a core file is loaded, but there's
+   * XXX no hook that happens then. However, core_open() in corelow is
+   * XXX pretty likely to call this.
+   */
+  inferior_ptid = minus_one_ptid;
+
+  orig_core_ops.to_close (quitting);
+}
+
+static void
+nbsd_core_detach (struct target_ops *ops, char *args, int from_tty)
+{
+  struct target_ops *beneath = find_target_beneath (ops);
+  if (nbsd_thread_active)
+    nbsd_thread_deactivate ();
+  unpush_target (ops);
+  orig_core_ops.to_detach (beneath, args, from_tty);
+}
+
+/*
+ * Process operation callbacks
+ */
+static int
+nbsd_thread_proc_read (void *arg, caddr_t addr, void *buf, size_t size)
+{
+  int val;
+
+  val = target_read_memory ((CORE_ADDR)addr, buf, size);
+
+  if (val == 0)
+    return 0;
+  else
+    return TD_ERR_IO;
+}
+
+
+static int
+nbsd_thread_proc_write (void *arg, caddr_t addr, void *buf, size_t size)
+{
+  int val;
+
+  val = target_write_memory ((CORE_ADDR)addr, buf, size);
+
+  if (val == 0)
+    return 0;
+  else
+    return TD_ERR_IO;
+}
+
+static int
+nbsd_thread_proc_lookup (void *arg, char *sym, caddr_t *addr)
+{
+  struct minimal_symbol *ms;
+
+  ms = lookup_minimal_symbol (sym, NULL, NULL);
+
+  if (!ms)
+    return TD_ERR_NOSYM;
+
+  *addr = (caddr_t) SYMBOL_VALUE_ADDRESS (ms);
+
+  return 0;
+
+}
+
+static int
+nbsd_thread_proc_regsize (void *arg, int regset, size_t *size)
+{
+  switch (regset)
+    {
+    case 0:
+      *size = sizeof (gregset_t);
+      break;
+    case 1:
+      *size = sizeof (fpregset_t);
+      break;
+    default:
+      return TD_ERR_INVAL;
+    }
+
+  return 0;
+}
+
+static int
+nbsd_thread_proc_getregs (void *arg, int regset, int lwp, void *buf)
+{
+  struct nbsd_thread_proc_arg *a = arg; 
+  struct regcache *cache = a->cache;
+  struct target_ops *ops = a->ops;
+  struct cleanup *old_chain;
+  int ret;
+
+  old_chain = save_inferior_ptid ();
+
+  if (target_has_execution)
+    {
+      /* Fetching registers from a live process requires that
+	 inferior_ptid is a LWP value rather than a thread value. */
+      inferior_ptid = BUILD_LWP (lwp, main_ptid);
+      child_ops.to_fetch_registers (ops, cache, -1);
+    }
+  else
+    {
+      /* Fetching registers from a core process requires that
+	 the PID value of inferior_ptid have the funky value that
+	 the kernel drops rather than the real PID. Gross. */
+      inferior_ptid = pid_to_ptid ((lwp << 16) | GET_PID (main_ptid));
+      orig_core_ops.to_fetch_registers (ops, cache, -1);
+    }
+
+  ret = 0;
+  switch (regset)
+    {
+    case 0:
+      fill_gregset (cache, (gregset_t *)buf, -1);
+      break;
+    case 1:
+      fill_fpregset (cache, (fpregset_t *)buf, -1);
+      break;
+    default: /* XXX need to handle other reg sets: SSE, AltiVec, etc. */
+      ret = TD_ERR_INVAL;
+    }
+
+  do_cleanups (old_chain);
+
+  return ret;
+}
+
+static int
+nbsd_thread_proc_setregs (void *arg, int regset, int lwp, void *buf)
+{
+  struct nbsd_thread_proc_arg *a = arg; 
+  struct regcache *cache = a->cache;
+  struct target_ops *ops = a->ops;
+  struct cleanup *old_chain;
+  int ret;
+
+  ret = 0;
+  old_chain = save_inferior_ptid ();
+
+  switch (regset)
+    {
+    case 0:
+      supply_gregset(cache, (gregset_t *)buf);
+      break;
+    case 1:
+      supply_fpregset(cache, (fpregset_t *)buf);
+      break;
+    default: /* XXX need to handle other reg sets: SSE, AltiVec, etc. */
+      ret = TD_ERR_INVAL;
+    }
+
+  /* Storing registers requires that inferior_ptid is a LWP value
+     rather than a thread value. */
+  inferior_ptid = BUILD_LWP (lwp, main_ptid);
+  child_ops.to_store_registers (ops, cache, -1);
+  do_cleanups (old_chain);
+
+  return ret;
+}
+
+
+static int
+ignore (CORE_ADDR addr, char *contents)
+{
+  return 0;
+}
+
+static void
+init_nbsd_proc_callbacks (void)
+{
+  nbsd_thread_callbacks.proc_read = nbsd_thread_proc_read;
+  nbsd_thread_callbacks.proc_write = nbsd_thread_proc_write;
+  nbsd_thread_callbacks.proc_lookup = nbsd_thread_proc_lookup;
+  nbsd_thread_callbacks.proc_regsize = nbsd_thread_proc_regsize;
+  nbsd_thread_callbacks.proc_getregs = nbsd_thread_proc_getregs;
+  nbsd_thread_callbacks.proc_setregs = nbsd_thread_proc_setregs;
+}
+
+static int
+one(struct target_ops *ops)
+{
+  return 1;
+}
+
+static int
+oneptid(struct target_ops *ops, ptid_t id)
+{
+  return 1;
+}
+
+static void
+init_nbsd_thread_ops (void)
+{
+  nbsd_thread_ops.to_shortname = "netbsd-threads";
+  nbsd_thread_ops.to_longname = "NetBSD pthread.";
+  nbsd_thread_ops.to_doc = "NetBSD pthread support.";
+  nbsd_thread_ops.to_open = 0;
+  nbsd_thread_ops.to_close = 0;
+  nbsd_thread_ops.to_attach = nbsd_thread_attach;
+  nbsd_thread_ops.to_post_attach = nbsd_thread_post_attach;
+  nbsd_thread_ops.to_detach = nbsd_thread_detach;
+  nbsd_thread_ops.to_resume = nbsd_thread_resume;
+  nbsd_thread_ops.to_wait = nbsd_thread_wait;
+  nbsd_thread_ops.to_fetch_registers = nbsd_thread_fetch_registers;
+  nbsd_thread_ops.to_store_registers = nbsd_thread_store_registers;
+  nbsd_thread_ops.to_prepare_to_store = 0;
+  nbsd_thread_ops.to_xfer_partial = nbsd_thread_xfer_partial;
+  nbsd_thread_ops.to_files_info = nbsd_thread_files_info;
+  nbsd_thread_ops.to_insert_breakpoint = memory_insert_breakpoint;
+  nbsd_thread_ops.to_remove_breakpoint = memory_remove_breakpoint;
+  nbsd_thread_ops.to_terminal_init = terminal_init_inferior;
+  nbsd_thread_ops.to_terminal_inferior = terminal_inferior;
+  nbsd_thread_ops.to_terminal_ours_for_output = terminal_ours_for_output;
+  nbsd_thread_ops.to_terminal_ours = terminal_ours;
+  nbsd_thread_ops.to_terminal_info = child_terminal_info;
+  nbsd_thread_ops.to_create_inferior = nbsd_thread_create_inferior;
+  nbsd_thread_ops.to_mourn_inferior = nbsd_thread_mourn_inferior;
+  nbsd_thread_ops.to_can_run = nbsd_thread_can_run;
+  nbsd_thread_ops.to_thread_alive = nbsd_thread_alive;
+  nbsd_thread_ops.to_pid_to_str = nbsd_pid_to_str;
+  nbsd_thread_ops.to_find_new_threads = nbsd_find_new_threads;
+  nbsd_thread_ops.to_stratum = thread_stratum;
+  nbsd_thread_ops.to_has_all_memory = one;
+  nbsd_thread_ops.to_has_memory = one;
+  nbsd_thread_ops.to_has_stack = one;
+  nbsd_thread_ops.to_has_registers = one;
+  nbsd_thread_ops.to_has_execution = oneptid;
+  nbsd_thread_ops.to_has_thread_control = tc_none;
+  nbsd_thread_ops.to_magic = OPS_MAGIC;
+}
+
+static void
+init_nbsd_core_ops (void)
+{
+  nbsd_core_ops.to_shortname = "netbsd-core";
+  nbsd_core_ops.to_longname = "NetBSD core pthread.";
+  nbsd_core_ops.to_doc = "NetBSD pthread support for core files.";
+  nbsd_core_ops.to_open = nbsd_core_open;
+  nbsd_core_ops.to_close = nbsd_core_close;
+  nbsd_core_ops.to_detach = nbsd_core_detach;
+  nbsd_core_ops.to_fetch_registers = nbsd_thread_fetch_registers;
+  nbsd_core_ops.to_xfer_partial = nbsd_thread_xfer_partial;
+  nbsd_core_ops.to_files_info = nbsd_core_files_info;
+  nbsd_core_ops.to_create_inferior = nbsd_thread_create_inferior;
+  nbsd_core_ops.to_has_memory = one;
+  nbsd_core_ops.to_has_stack = one;
+  nbsd_core_ops.to_has_registers = one;
+  nbsd_core_ops.to_has_thread_control = tc_schedlock;
+  nbsd_core_ops.to_thread_alive = nbsd_core_thread_alive;
+  nbsd_core_ops.to_pid_to_str = nbsd_pid_to_str;
+  nbsd_core_ops.to_find_new_threads = nbsd_find_new_threads;
+  nbsd_core_ops.to_magic = OPS_MAGIC;
+}
+
+/* we suppress the call to add_target of core_ops in corelow because
+   if there are two targets in the stratum core_stratum, find_core_target
+   won't know which one to return.  see corelow.c for an additonal
+   comment on coreops_suppress_target. */
+int coreops_suppress_target = 1;
+
+void
+_initialize_nbsd_thread (void)
+{
+  static struct cmd_list_element *thread_examine_list = NULL;
+
+  init_nbsd_thread_ops ();
+  init_nbsd_core_ops ();
+  init_nbsd_proc_callbacks ();
+
+  add_target (&nbsd_thread_ops);
+  add_cmd ("tsd", class_run, nbsd_thread_tsd_cmd,
+	   "Show the thread-specific data keys and destructors for the process.\n",
+	   &thread_cmd_list);
+
+  add_cmd ("sync", class_run, nbsd_thread_sync_cmd,
+	   "Show the synchronization object at the given address.\n",
+	   &thread_cmd_list);
+
+  add_prefix_cmd ("examine", class_run, nbsd_thread_examine_cmd,
+		  "Show the thread at the given address.\n",
+		  &thread_examine_list, "examine ", 1, &thread_cmd_list);
+
+  add_cmd ("all", class_run, nbsd_thread_examine_all_cmd,
+	   "Show all threads.",
+	   &thread_examine_list);
+
+
+  memcpy (&orig_core_ops, &core_ops, sizeof (struct target_ops));
+  memcpy (&core_ops, &nbsd_core_ops, sizeof (struct target_ops));
+  add_target (&core_ops);
+
+  child_suppress_run = 1;
+
+}

Reply via email to