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;
+
+}