summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Xu <davidxu@FreeBSD.org>2004-07-15 04:51:04 +0000
committerDavid Xu <davidxu@FreeBSD.org>2004-07-15 04:51:04 +0000
commit213683571222a0b4d4b007a18200b57c088f69e6 (patch)
treed9ff39e241f7b386ae3289d22dcb805628742e75
parent7bd73b069cd7960c9352342e6728c094f740b63f (diff)
downloadsrc-test2-213683571222a0b4d4b007a18200b57c088f69e6.tar.gz
src-test2-213683571222a0b4d4b007a18200b57c088f69e6.zip
Notes
-rw-r--r--gnu/usr.bin/gdb/libgdb/fbsd-threads.c1095
1 files changed, 1095 insertions, 0 deletions
diff --git a/gnu/usr.bin/gdb/libgdb/fbsd-threads.c b/gnu/usr.bin/gdb/libgdb/fbsd-threads.c
new file mode 100644
index 000000000000..d0bee7d75c1e
--- /dev/null
+++ b/gnu/usr.bin/gdb/libgdb/fbsd-threads.c
@@ -0,0 +1,1095 @@
+/* $FreeBSD$ */
+/* FreeBSD libthread_db assisted debugging support.
+ Copyright 1999, 2000, 2001 Free Software Foundation, 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 "gdb_assert.h"
+#include <dlfcn.h>
+#include "proc_service.h"
+#include "thread_db.h"
+
+#include "bfd.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "symfile.h"
+#include "symtab.h"
+#include "objfiles.h"
+#include "target.h"
+#include "regcache.h"
+#include "gdbcmd.h"
+
+#include <sys/ptrace.h>
+
+#define LIBTHREAD_DB_SO "libthread_db.so"
+
+struct ps_prochandle
+{
+ pid_t pid;
+};
+
+extern struct target_ops child_ops;
+
+/* This module's target vector. */
+static struct target_ops thread_db_ops;
+
+static struct target_ops base_ops;
+
+/* Pointer to the next function on the objfile event chain. */
+static void (*target_new_objfile_chain) (struct objfile *objfile);
+
+/* Non-zero if we're using this module's target vector. */
+static int using_thread_db;
+
+/* Non-zero if we have to keep this module's target vector active
+ across re-runs. */
+static int keep_thread_db;
+
+/* Structure that identifies the child process for the
+ <proc_service.h> interface. */
+static struct ps_prochandle proc_handle;
+
+/* Connection to the libthread_db library. */
+static td_thragent_t *thread_agent;
+
+/* The last thread we are single stepping */
+static ptid_t last_single_step_thread;
+
+/* Pointers to the libthread_db functions. */
+
+static td_err_e (*td_init_p) (void);
+
+static td_err_e (*td_ta_new_p) (struct ps_prochandle *ps, td_thragent_t **ta);
+static td_err_e (*td_ta_map_id2thr_p) (const td_thragent_t *ta, thread_t pt,
+ td_thrhandle_t *__th);
+static td_err_e (*td_ta_map_lwp2thr_p) (const td_thragent_t *ta, lwpid_t lwpid,
+ td_thrhandle_t *th);
+static td_err_e (*td_ta_thr_iter_p) (const td_thragent_t *ta,
+ td_thr_iter_f *callback,
+ void *cbdata_p, td_thr_state_e state,
+ int ti_pri, sigset_t *ti_sigmask_p,
+ unsigned int ti_user_flags);
+static td_err_e (*td_ta_event_addr_p) (const td_thragent_t *ta,
+ td_event_e event, td_notify_t *ptr);
+static td_err_e (*td_ta_set_event_p) (const td_thragent_t *ta,
+ td_thr_events_t *event);
+static td_err_e (*td_ta_event_getmsg_p) (const td_thragent_t *ta,
+ td_event_msg_t *msg);
+static td_err_e (*td_thr_validate_p) (const td_thrhandle_t *th);
+static td_err_e (*td_thr_get_info_p) (const td_thrhandle_t *th,
+ td_thrinfo_t *infop);
+static td_err_e (*td_thr_getfpregs_p) (const td_thrhandle_t *th,
+ prfpregset_t *regset);
+static td_err_e (*td_thr_getgregs_p) (const td_thrhandle_t *th,
+ prgregset_t gregs);
+static td_err_e (*td_thr_setfpregs_p) (const td_thrhandle_t *th,
+ const prfpregset_t *fpregs);
+static td_err_e (*td_thr_setgregs_p) (const td_thrhandle_t *th,
+ prgregset_t gregs);
+static td_err_e (*td_thr_event_enable_p) (const td_thrhandle_t *th, int event);
+
+static td_err_e (*td_thr_sstep_p) (td_thrhandle_t *th, int step);
+
+static td_err_e (*td_ta_tsd_iter_p) (const td_thragent_t *ta,
+ td_key_iter_f *func, void *data);
+
+static td_err_e (*td_thr_dbsuspend_p) (const td_thrhandle_t *);
+static td_err_e (*td_thr_dbresume_p) (const td_thrhandle_t *);
+
+/* Prototypes for local functions. */
+static void fbsd_thread_find_new_threads (void);
+
+/* Building process ids. */
+
+#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, pid) ptid_build (pid, lwp, 0)
+#define BUILD_THREAD(tid, pid) ptid_build (pid, 0, tid)
+
+static char *
+thread_db_err_str (td_err_e err)
+{
+ static char buf[64];
+
+ switch (err)
+ {
+ case TD_OK:
+ return "generic 'call succeeded'";
+ case TD_ERR:
+ return "generic error";
+ case TD_NOTHR:
+ return "no thread to satisfy query";
+ case TD_NOSV:
+ return "no sync handle to satisfy query";
+ case TD_NOLWP:
+ return "no LWP to satisfy query";
+ case TD_BADPH:
+ return "invalid process handle";
+ case TD_BADTH:
+ return "invalid thread handle";
+ case TD_BADSH:
+ return "invalid synchronization handle";
+ case TD_BADTA:
+ return "invalid thread agent";
+ case TD_BADKEY:
+ return "invalid key";
+ case TD_NOMSG:
+ return "no event message for getmsg";
+ case TD_NOFPREGS:
+ return "FPU register set not available";
+ case TD_NOLIBTHREAD:
+ return "application not linked with libthread";
+ case TD_NOEVENT:
+ return "requested event is not supported";
+ case TD_NOCAPAB:
+ return "capability not available";
+ case TD_DBERR:
+ return "debugger service failed";
+ case TD_NOAPLIC:
+ return "operation not applicable to";
+ case TD_NOTSD:
+ return "no thread-specific data for this thread";
+ case TD_MALLOC:
+ return "malloc failed";
+ case TD_PARTIALREG:
+ return "only part of register set was written/read";
+ case TD_NOXREGS:
+ return "X register set not available for this thread";
+ default:
+ snprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err);
+ return buf;
+ }
+}
+
+static char *
+thread_db_state_str (td_thr_state_e state)
+{
+ static char buf[64];
+
+ switch (state)
+ {
+ case TD_THR_STOPPED:
+ return "stopped by debugger";
+ case TD_THR_RUN:
+ return "runnable";
+ case TD_THR_ACTIVE:
+ return "active";
+ case TD_THR_ZOMBIE:
+ return "zombie";
+ case TD_THR_SLEEP:
+ return "sleeping";
+ case TD_THR_STOPPED_ASLEEP:
+ return "stopped by debugger AND blocked";
+ default:
+ snprintf (buf, sizeof (buf), "unknown thread_db state %d", state);
+ return buf;
+ }
+}
+
+/* Convert LWP to user-level thread id. */
+static ptid_t
+thread_from_lwp (ptid_t ptid)
+{
+ td_thrinfo_t ti;
+ td_thrhandle_t th;
+ td_err_e err;
+
+ gdb_assert (IS_LWP (ptid));
+
+ err = td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), &th);
+ if (err == TD_OK)
+ {
+ err = td_thr_get_info_p (&th, &ti);
+ if (err != TD_OK)
+ error ("Cannot get thread info: %s", thread_db_err_str (err));
+ return BUILD_THREAD (ti.ti_tid, GET_PID (ptid));
+ }
+
+ /* the LWP is not mapped to user thread */
+ return BUILD_LWP (GET_LWP (ptid), GET_PID (ptid));
+}
+
+static long
+get_current_lwp (int pid)
+{
+ struct ptrace_lwpinfo pl;
+
+ if (ptrace (PT_LWPINFO, pid, (caddr_t)&pl, sizeof(pl)))
+ perror_with_name("PT_LWPINFO");
+
+ return (long)pl.pl_lwpid;
+}
+
+static void
+get_current_thread ()
+{
+ long lwp;
+ ptid_t tmp, ptid;
+
+ lwp = get_current_lwp (proc_handle.pid);
+ tmp = BUILD_LWP (lwp, proc_handle.pid);
+ ptid = thread_from_lwp (tmp);
+ if (!in_thread_list (ptid))
+ {
+ add_thread (ptid);
+ inferior_ptid = ptid;
+ }
+}
+
+static void
+fbsd_thread_new_objfile (struct objfile *objfile)
+{
+ td_err_e err;
+
+ /* Don't attempt to use thread_db on targets which can not run
+ (core files). */
+ if (objfile == NULL || !target_has_execution)
+ {
+ /* All symbols have been discarded. If the thread_db target is
+ active, deactivate it now. */
+ if (using_thread_db)
+ {
+ gdb_assert (proc_handle.pid == 0);
+ unpush_target (&thread_db_ops);
+ using_thread_db = 0;
+ }
+
+ keep_thread_db = 0;
+
+ goto quit;
+ }
+
+ if (using_thread_db)
+ /* Nothing to do. The thread library was already detected and the
+ target vector was already activated. */
+ goto quit;
+
+ /* Initialize the structure that identifies the child process. Note
+ that at this point there is no guarantee that we actually have a
+ child process. */
+ proc_handle.pid = GET_PID (inferior_ptid);
+
+ /* Now attempt to open a connection to the thread library. */
+ err = td_ta_new_p (&proc_handle, &thread_agent);
+ switch (err)
+ {
+ case TD_NOLIBTHREAD:
+ /* No thread library was detected. */
+ break;
+
+ case TD_OK:
+ /* The thread library was detected. Activate the thread_db target. */
+ base_ops = current_target;
+ push_target (&thread_db_ops);
+ using_thread_db = 1;
+
+ /* If the thread library was detected in the main symbol file
+ itself, we assume that the program was statically linked
+ against the thread library and well have to keep this
+ module's target vector activated until forever... Well, at
+ least until all symbols have been discarded anyway (see
+ above). */
+ if (objfile == symfile_objfile)
+ {
+ gdb_assert (proc_handle.pid == 0);
+ keep_thread_db = 1;
+ }
+
+ /* We can only poke around if there actually is a child process.
+ If there is no child process alive, postpone the steps below
+ until one has been created. */
+ if (proc_handle.pid != 0)
+ {
+ fbsd_thread_find_new_threads ();
+ get_current_thread ();
+ }
+ else
+ printf_filtered("%s postpone processing\n", __func__);
+ break;
+
+ default:
+ warning ("Cannot initialize thread debugging library: %s",
+ thread_db_err_str (err));
+ break;
+ }
+
+ quit:
+ if (target_new_objfile_chain)
+ target_new_objfile_chain (objfile);
+}
+
+static void
+fbsd_thread_attach (char *args, int from_tty)
+{
+ child_ops.to_attach (args, from_tty);
+
+ /* Destroy thread info; it's no longer valid. */
+ init_thread_list ();
+
+ /* The child process is now the actual multi-threaded
+ program. Snatch its process ID... */
+ proc_handle.pid = GET_PID (inferior_ptid);
+
+ /* ...and perform the remaining initialization steps. */
+ fbsd_thread_find_new_threads();
+ get_current_thread ();
+}
+
+static void
+fbsd_thread_detach (char *args, int from_tty)
+{
+ proc_handle.pid = 0;
+ child_ops.to_detach (args, from_tty);
+}
+
+static int
+suspend_thread_callback (const td_thrhandle_t *th_p, void *data)
+{
+ return td_thr_dbsuspend_p (th_p);
+}
+
+static int
+resume_thread_callback (const td_thrhandle_t *th_p, void *data)
+{
+ return td_thr_dbresume_p (th_p);
+}
+
+static void
+fbsd_thread_resume (ptid_t ptid, int step, enum target_signal signo)
+{
+ td_thrhandle_t th;
+ td_thrinfo_t ti;
+ ptid_t work_ptid;
+ int resume_all, ret;
+ long lwp, thvalid = 0;
+
+#if 0
+ printf_filtered("%s ptid=%ld.%ld.%ld step=%d\n", __func__,
+ GET_PID(ptid), GET_LWP(ptid), GET_THREAD(ptid), step);
+ printf_filtered("%s inferior_ptid=%ld.%ld.%ld\n", __func__,
+ GET_PID(inferior_ptid), GET_LWP(inferior_ptid),
+ GET_THREAD(inferior_ptid));
+#endif
+
+ if (proc_handle.pid == 0)
+ {
+ base_ops.to_resume (ptid, step, signo);
+ return;
+ }
+
+ if (GET_PID(ptid) != -1 && step != 0)
+ {
+ resume_all = 0;
+ work_ptid = ptid;
+ }
+ else
+ {
+ resume_all = 1;
+ work_ptid = inferior_ptid;
+ }
+
+ lwp = GET_LWP (work_ptid);
+ if (lwp == 0)
+ {
+ /* check user thread */
+ ret = td_ta_map_id2thr_p (thread_agent, GET_THREAD(work_ptid), &th);
+ if (ret)
+ error (thread_db_err_str (ret));
+
+ /*
+ * For M:N thread, we need to tell UTS to set/unset single step
+ * flag at context switch time, the flag will be written into
+ * thread mailbox. This becauses some architecture may not have
+ * machine single step flag in ucontext, so we put the flag in mailbox,
+ * when the thread switches back, kse_switchin restores the single step
+ * state.
+ */
+ ret = td_thr_sstep_p (&th, step);
+ if (ret)
+ error (thread_db_err_str (ret));
+ ret = td_thr_get_info_p (&th, &ti);
+ if (ret)
+ error (thread_db_err_str (ret));
+ thvalid = 1;
+ lwp = ti.ti_lid;
+ }
+
+ if (lwp)
+ {
+ int req = step ? PT_SETSTEP : PT_CLEARSTEP;
+ if (ptrace (req, (pid_t) lwp, (caddr_t) 1, target_signal_to_host(signo)))
+ perror_with_name ("PT_SETSTEP/PT_CLEARSTEP");
+ }
+
+ if (!ptid_equal (last_single_step_thread, null_ptid))
+ {
+ ret = td_ta_thr_iter_p (thread_agent, resume_thread_callback, NULL,
+ TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+ TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+ if (ret != TD_OK)
+ error ("resume error: %s", thread_db_err_str (ret));
+ }
+
+ if (!resume_all)
+ {
+ ret = td_ta_thr_iter_p (thread_agent, suspend_thread_callback, NULL,
+ TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+ TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+ if (ret != TD_OK)
+ error ("suspend error: %s", thread_db_err_str (ret));
+ last_single_step_thread = work_ptid;
+ }
+ else
+ last_single_step_thread = null_ptid;
+
+ if (thvalid)
+ {
+ ret = td_thr_dbresume_p (&th);
+ if (ret != TD_OK)
+ error ("resume error: %s", thread_db_err_str (ret));
+ }
+ else
+ {
+ /* it is not necessary, put it here for completness */
+ ret = ptrace(PT_RESUME, lwp, 0, 0);
+ }
+
+ /* now continue the process, suspended thread wont run */
+ if (ptrace (PT_CONTINUE, proc_handle.pid , (caddr_t)1,
+ target_signal_to_host(signo)))
+ perror_with_name ("PT_CONTINUE");
+}
+
+static ptid_t
+fbsd_thread_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
+{
+ ptid_t ret;
+ long lwp;
+ CORE_ADDR stop_pc;
+
+ ret = child_ops.to_wait (ptid, ourstatus);
+ if (GET_PID(ret) >= 0 && ourstatus->kind == TARGET_WAITKIND_STOPPED)
+ {
+ lwp = get_current_lwp (proc_handle.pid);
+ ret = thread_from_lwp (BUILD_LWP (lwp, GET_PID (ret)));
+ if (!in_thread_list (ret))
+ add_thread (ret);
+ }
+
+ return (ret);
+}
+
+static int
+fbsd_thread_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write,
+ struct mem_attrib *attrib, struct target_ops *target)
+{
+ return base_ops.to_xfer_memory (memaddr, myaddr, len, write,
+ attrib, target);
+}
+
+static void
+fbsd_lwp_fetch_registers (int regno)
+{
+ gregset_t gregs;
+ fpregset_t fpregs;
+ lwpid_t lwp;
+
+ /* FIXME, use base_ops to fetch lwp registers! */
+ lwp = GET_LWP (inferior_ptid);
+
+ if (ptrace (PT_GETREGS, lwp, (caddr_t) &gregs, 0) == -1)
+ error ("Cannot get lwp %d registers: %s\n", lwp, safe_strerror (errno));
+ supply_gregset (&gregs);
+
+ if (ptrace (PT_GETFPREGS, lwp, (caddr_t) &fpregs, 0) == -1)
+ error ("Cannot get lwp %d registers: %s\n ", lwp, safe_strerror (errno));
+
+ supply_fpregset (&fpregs);
+}
+
+static void
+fbsd_thread_fetch_registers (int regno)
+{
+ prgregset_t gregset;
+ prfpregset_t fpregset;
+ td_thrhandle_t th;
+ td_err_e err;
+
+ if (!IS_THREAD (inferior_ptid))
+ {
+ fbsd_lwp_fetch_registers (regno);
+ return;
+ }
+
+ err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (inferior_ptid), &th);
+ if (err != TD_OK)
+ error ("Cannot find thread %d: Thread ID=%ld, %s",
+ pid_to_thread_id (inferior_ptid),
+ GET_THREAD (inferior_ptid), thread_db_err_str (err));
+
+ err = td_thr_getgregs_p (&th, gregset);
+ if (err != TD_OK)
+ error ("Cannot fetch general-purpose registers for thread %d: Thread ID=%ld, %s",
+ pid_to_thread_id (inferior_ptid),
+ GET_THREAD (inferior_ptid), thread_db_err_str (err));
+
+ err = td_thr_getfpregs_p (&th, &fpregset);
+ if (err != TD_OK)
+ error ("Cannot get floating-point registers for thread %d: Thread ID=%ld, %s",
+ pid_to_thread_id (inferior_ptid),
+ GET_THREAD (inferior_ptid), thread_db_err_str (err));
+
+ supply_gregset (gregset);
+ supply_fpregset (&fpregset);
+}
+
+static void
+fbsd_lwp_store_registers (int regno)
+{
+ gregset_t gregs;
+ fpregset_t fpregs;
+ lwpid_t lwp;
+
+ /* FIXME, is it possible ? */
+ if (!IS_LWP (inferior_ptid))
+ {
+ child_ops.to_store_registers (regno);
+ return ;
+ }
+
+ lwp = GET_LWP (inferior_ptid);
+ if (regno != -1)
+ if (ptrace (PT_GETREGS, lwp, (caddr_t) &gregs, 0) == -1)
+ error ("Cannot get lwp %d registers: %s\n", lwp, safe_strerror (errno));
+
+ fill_gregset (&gregs, regno);
+ if (ptrace (PT_SETREGS, lwp, (caddr_t) &gregs, 0) == -1)
+ error ("Cannot set lwp %d registers: %s\n", lwp, safe_strerror (errno));
+
+ if (regno != -1)
+ if (ptrace (PT_GETFPREGS, lwp, (caddr_t) &fpregs, 0) == -1)
+ error ("Cannot get lwp %d float registers: %s\n", lwp,
+ safe_strerror (errno));
+
+ fill_fpregset (&fpregs, regno);
+ if (ptrace (PT_SETFPREGS, lwp, (caddr_t) &fpregs, 0) == -1)
+ error ("Cannot set lwp %d float registers: %s\n", lwp,
+ safe_strerror (errno));
+}
+
+static void
+fbsd_thread_store_registers (int regno)
+{
+ prgregset_t gregset;
+ prfpregset_t fpregset;
+ td_thrhandle_t th;
+ td_err_e err;
+
+ if (!IS_THREAD (inferior_ptid))
+ {
+ fbsd_lwp_store_registers (regno);
+ return;
+ }
+
+ err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (inferior_ptid), &th);
+ if (err != TD_OK)
+ error ("Cannot find thread %d: Thread ID=%ld, %s",
+ pid_to_thread_id (inferior_ptid),
+ GET_THREAD (inferior_ptid),
+ thread_db_err_str (err));
+
+ if (regno != -1)
+ {
+ char old_value[MAX_REGISTER_SIZE];
+
+ regcache_collect (regno, old_value);
+ err = td_thr_getgregs_p (&th, gregset);
+ if (err != TD_OK)
+ error ("%s: td_thr_getgregs %s", __func__, thread_db_err_str (err));
+ err = td_thr_getfpregs_p (&th, &fpregset);
+ if (err != TD_OK)
+ error ("%s: td_thr_getfpgregs %s", __func__, thread_db_err_str (err));
+ supply_register (regno, old_value);
+ }
+
+ fill_gregset (gregset, regno);
+ fill_fpregset (&fpregset, regno);
+
+ err = td_thr_setgregs_p (&th, gregset);
+ if (err != TD_OK)
+ error ("Cannot store general-purpose registers for thread %d: Thread ID=%d, %s",
+ pid_to_thread_id (inferior_ptid), GET_THREAD (inferior_ptid),
+ thread_db_err_str (err));
+ err = td_thr_setfpregs_p (&th, &fpregset);
+ if (err != TD_OK)
+ error ("Cannot store floating-point registers for thread %d: Thread ID=%d, %s",
+ pid_to_thread_id (inferior_ptid), GET_THREAD (inferior_ptid),
+ thread_db_err_str (err));
+}
+
+static void
+fbsd_thread_kill (void)
+{
+ child_ops.to_kill();
+}
+
+static void
+fbsd_thread_create_inferior (char *exec_file, char *allargs, char **env)
+{
+ if (!keep_thread_db)
+ {
+ unpush_target (&thread_db_ops);
+ using_thread_db = 0;
+ }
+
+ child_ops.to_create_inferior (exec_file, allargs, env);
+}
+
+static void
+fbsd_thread_post_startup_inferior (ptid_t ptid)
+{
+ if (proc_handle.pid == 0)
+ {
+ /*
+ * The child process is now the actual multi-threaded
+ * program. Snatch its process ID...
+ */
+ proc_handle.pid = GET_PID (ptid);
+
+ fbsd_thread_find_new_threads ();
+ get_current_thread ();
+ }
+}
+
+static void
+fbsd_thread_mourn_inferior (void)
+{
+ /*
+ * Forget about the child's process ID. We shouldn't need it
+ * anymore.
+ */
+ proc_handle.pid = 0;
+
+ child_ops.to_mourn_inferior ();
+}
+
+static int
+fbsd_thread_alive (ptid_t ptid)
+{
+ td_thrhandle_t th;
+ td_thrinfo_t ti;
+ td_err_e err;
+ gregset_t gregs;
+
+ if (IS_THREAD (ptid))
+ {
+ err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (ptid), &th);
+ if (err != TD_OK)
+ return 0;
+
+ err = td_thr_get_info_p (&th, &ti);
+ if (err != TD_OK)
+ return 0;
+
+ /* A zombie thread. */
+ if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
+ return 0;
+
+ return 1;
+ }
+ else if (GET_LWP (ptid) == 0)
+ {
+ /* we sometimes are called with lwp == 0 */
+ return 1;
+ }
+
+ err = td_ta_map_lwp2thr_p (thread_agent, GET_LWP (ptid), &th);
+
+ /*
+ * if the lwp was already mapped to user thread, don't use it
+ * directly, please use user thread id instead.
+ */
+ if (err == TD_OK)
+ return 0;
+
+ /* check lwp in kernel */
+ return ptrace (PT_GETREGS, GET_LWP (ptid), (caddr_t)&gregs, 0) == 0;
+}
+
+static int
+find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
+{
+ td_thrinfo_t ti;
+ td_err_e err;
+ ptid_t ptid;
+
+ err = td_thr_get_info_p (th_p, &ti);
+ if (err != TD_OK)
+ error ("Cannot get thread info: %s", thread_db_err_str (err));
+
+ /* Ignore zombie */
+ if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
+ return 0;
+
+ ptid = BUILD_THREAD (ti.ti_tid, GET_PID (inferior_ptid));
+
+ if (!in_thread_list (ptid))
+ add_thread (ptid);
+
+ return 0;
+}
+
+static void
+fbsd_thread_find_new_threads (void)
+{
+ td_err_e err;
+
+ /* Iterate over all user-space threads to discover new threads. */
+ err = td_ta_thr_iter_p (thread_agent, find_new_threads_callback, NULL,
+ TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+ TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+ if (err != TD_OK)
+ error ("Cannot find new threads: %s", thread_db_err_str (err));
+}
+
+static char *
+fbsd_thread_pid_to_str (ptid_t ptid)
+{
+ static char buf[64];
+
+ if (IS_THREAD (ptid))
+ {
+ td_thrhandle_t th;
+ td_thrinfo_t ti;
+ td_err_e err;
+
+ err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (ptid), &th);
+ if (err != TD_OK)
+ error ("Cannot find thread, Thread ID=%ld, %s",
+ GET_THREAD (ptid), thread_db_err_str (err));
+
+ err = td_thr_get_info_p (&th, &ti);
+ if (err != TD_OK)
+ error ("Cannot get thread info, Thread ID=%ld, %s",
+ GET_THREAD (ptid), thread_db_err_str (err));
+
+ if (ti.ti_lid != 0)
+ {
+ snprintf (buf, sizeof (buf), "Thread %ld (LWP %d)",
+ GET_THREAD (ptid), ti.ti_lid);
+ }
+ else
+ {
+ snprintf (buf, sizeof (buf), "Thread %ld (%s)",
+ GET_THREAD (ptid), thread_db_state_str (ti.ti_state));
+ }
+
+ return buf;
+ }
+ else if (IS_LWP (ptid))
+ {
+ snprintf (buf, sizeof (buf), "LWP %d", (int) GET_LWP (ptid));
+ return buf;
+ }
+ return normal_pid_to_str (ptid);
+}
+
+static int
+tsd_cb (thread_key_t key, void (*destructor)(void *), void *ignore)
+{
+ struct minimal_symbol *ms;
+ char *name;
+
+ ms = lookup_minimal_symbol_by_pc ((CORE_ADDR)destructor);
+ if (!ms)
+ name = "???";
+ else
+ name = DEPRECATED_SYMBOL_NAME (ms);
+
+ printf_filtered ("Destructor %p <%s>\n", destructor, name);
+ return 0;
+}
+
+static void
+fbsd_thread_tsd_cmd (char *exp, int from_tty)
+{
+ td_ta_tsd_iter_p (thread_agent, tsd_cb, NULL);
+}
+
+static void
+init_thread_db_ops (void)
+{
+ thread_db_ops.to_shortname = "multi-thread";
+ thread_db_ops.to_longname = "multi-threaded child process.";
+ thread_db_ops.to_doc = "Threads and pthreads support.";
+ thread_db_ops.to_attach = fbsd_thread_attach;
+ thread_db_ops.to_detach = fbsd_thread_detach;
+ thread_db_ops.to_resume = fbsd_thread_resume;
+ thread_db_ops.to_wait = fbsd_thread_wait;
+ thread_db_ops.to_fetch_registers = fbsd_thread_fetch_registers;
+ thread_db_ops.to_store_registers = fbsd_thread_store_registers;
+ thread_db_ops.to_xfer_memory = fbsd_thread_xfer_memory;
+ thread_db_ops.to_kill = fbsd_thread_kill;
+ thread_db_ops.to_create_inferior = fbsd_thread_create_inferior;
+ thread_db_ops.to_post_startup_inferior = fbsd_thread_post_startup_inferior;
+ thread_db_ops.to_mourn_inferior = fbsd_thread_mourn_inferior;
+ thread_db_ops.to_thread_alive = fbsd_thread_alive;
+ thread_db_ops.to_find_new_threads = fbsd_thread_find_new_threads;
+ thread_db_ops.to_pid_to_str = fbsd_thread_pid_to_str;
+ thread_db_ops.to_stratum = thread_stratum;
+ thread_db_ops.to_has_thread_control = tc_none;
+ thread_db_ops.to_insert_breakpoint = memory_insert_breakpoint;
+ thread_db_ops.to_remove_breakpoint = memory_remove_breakpoint;
+ thread_db_ops.to_magic = OPS_MAGIC;
+}
+
+static int
+thread_db_load (void)
+{
+ void *handle;
+ td_err_e err;
+
+ handle = dlopen (LIBTHREAD_DB_SO, RTLD_NOW);
+ if (handle == NULL)
+ return 0;
+
+ td_init_p = dlsym (handle, "td_init");
+ if (td_init_p == NULL)
+ return 0;
+
+ td_ta_new_p = dlsym (handle, "td_ta_new");
+ if (td_ta_new_p == NULL)
+ return 0;
+
+ td_ta_map_id2thr_p = dlsym (handle, "td_ta_map_id2thr");
+ if (td_ta_map_id2thr_p == NULL)
+ return 0;
+
+ td_ta_map_lwp2thr_p = dlsym (handle, "td_ta_map_lwp2thr");
+ if (td_ta_map_lwp2thr_p == NULL)
+ return 0;
+
+ td_ta_thr_iter_p = dlsym (handle, "td_ta_thr_iter");
+ if (td_ta_thr_iter_p == NULL)
+ return 0;
+
+ td_thr_validate_p = dlsym (handle, "td_thr_validate");
+ if (td_thr_validate_p == NULL)
+ return 0;
+
+ td_thr_get_info_p = dlsym (handle, "td_thr_get_info");
+ if (td_thr_get_info_p == NULL)
+ return 0;
+
+ td_thr_getfpregs_p = dlsym (handle, "td_thr_getfpregs");
+ if (td_thr_getfpregs_p == NULL)
+ return 0;
+
+ td_thr_getgregs_p = dlsym (handle, "td_thr_getgregs");
+ if (td_thr_getgregs_p == NULL)
+ return 0;
+
+ td_thr_setfpregs_p = dlsym (handle, "td_thr_setfpregs");
+ if (td_thr_setfpregs_p == NULL)
+ return 0;
+
+ td_thr_setgregs_p = dlsym (handle, "td_thr_setgregs");
+ if (td_thr_setgregs_p == NULL)
+ return 0;
+
+ td_thr_sstep_p = dlsym(handle, "td_thr_sstep");
+ if (td_thr_sstep_p == NULL)
+ return 0;
+
+ td_ta_tsd_iter_p = dlsym (handle, "td_ta_tsd_iter");
+ if (td_ta_tsd_iter_p == NULL)
+ return 0;
+
+ td_thr_dbsuspend_p = dlsym (handle, "td_thr_dbsuspend");
+ if (td_thr_dbsuspend_p == NULL)
+ return 0;
+
+ td_thr_dbresume_p = dlsym (handle, "td_thr_dbresume");
+ if (td_thr_dbresume_p == NULL)
+ return 0;
+
+ /* Initialize the library. */
+ err = td_init_p ();
+ if (err != TD_OK)
+ {
+ warning ("Cannot initialize libthread_db: %s", thread_db_err_str (err));
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+_initialize_thread_db (void)
+{
+ /* Only initialize the module if we can load libthread_db. */
+ if (thread_db_load ())
+ {
+ init_thread_db_ops ();
+ add_target (&thread_db_ops);
+
+ /* "thread tsd" command */
+ add_cmd ("tsd", class_run, fbsd_thread_tsd_cmd,
+ "Show the thread-specific data keys and destructors "
+ "for the process.\n",
+ &thread_cmd_list);
+
+ /* Add ourselves to objfile event chain. */
+ target_new_objfile_chain = target_new_objfile_hook;
+ target_new_objfile_hook = fbsd_thread_new_objfile;
+ }
+ else
+ {
+ printf_filtered("%s: can not load %s.\n", __func__, LIBTHREAD_DB_SO);
+ }
+}
+
+/* proc service functions */
+void
+ps_plog (const char *fmt, ...)
+{
+ va_list args;
+
+ va_start (args, fmt);
+ vfprintf_filtered (gdb_stderr, fmt, args);
+ va_end (args);
+}
+
+ps_err_e
+ps_pglobal_lookup (struct ps_prochandle *ph, const char *obj,
+ const char *name, psaddr_t *sym_addr)
+{
+ struct minimal_symbol *ms;
+
+ ms = lookup_minimal_symbol (name, NULL, NULL);
+ if (ms == NULL)
+ return PS_NOSYM;
+
+ *sym_addr = (psaddr_t) SYMBOL_VALUE_ADDRESS (ms);
+ return PS_OK;
+}
+
+ps_err_e
+ps_pdread (struct ps_prochandle *ph, psaddr_t addr, void *buf, size_t len)
+{
+ return target_read_memory ((CORE_ADDR) addr, buf, len);
+}
+
+ps_err_e
+ps_pdwrite (struct ps_prochandle *ph, psaddr_t addr, const void *buf,
+ size_t len)
+{
+ return target_write_memory ((CORE_ADDR) addr, (void *)buf, len);
+}
+
+ps_err_e
+ps_ptread (struct ps_prochandle *ph, psaddr_t addr, void *buf, size_t len)
+{
+ return target_read_memory ((CORE_ADDR) addr, buf, len);
+}
+
+ps_err_e
+ps_ptwrite (struct ps_prochandle *ph, psaddr_t addr, const void *buf,
+ size_t len)
+{
+ return target_write_memory ((CORE_ADDR) addr, (void *)buf, len);
+}
+
+ps_err_e
+ps_lgetregs (struct ps_prochandle *ph, lwpid_t lwpid, prgregset_t gregset)
+{
+ struct cleanup *old_chain;
+
+ old_chain = save_inferior_ptid ();
+ inferior_ptid = BUILD_LWP (lwpid, PIDGET (inferior_ptid));
+ target_fetch_registers (-1);
+ fill_gregset (gregset, -1);
+ do_cleanups (old_chain);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lsetregs (struct ps_prochandle *ph, lwpid_t lwpid, const prgregset_t gregset)
+{
+ struct cleanup *old_chain;
+
+ old_chain = save_inferior_ptid ();
+ inferior_ptid = BUILD_LWP (lwpid, PIDGET (inferior_ptid));
+ supply_gregset (gregset);
+ target_store_registers (-1);
+ do_cleanups (old_chain);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lgetfpregs (struct ps_prochandle *ph, lwpid_t lwpid, prfpregset_t *fpregset)
+{
+ struct cleanup *old_chain;
+
+ old_chain = save_inferior_ptid ();
+ inferior_ptid = BUILD_LWP (lwpid, PIDGET (inferior_ptid));
+ target_fetch_registers (-1);
+ fill_fpregset (fpregset, -1);
+ do_cleanups (old_chain);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lsetfpregs (struct ps_prochandle *ph, lwpid_t lwpid,
+ const prfpregset_t *fpregset)
+{
+ struct cleanup *old_chain;
+
+ old_chain = save_inferior_ptid ();
+ inferior_ptid = BUILD_LWP (lwpid, PIDGET (inferior_ptid));
+ supply_fpregset (fpregset);
+ target_store_registers (-1);
+ do_cleanups (old_chain);
+ return PS_OK;
+}
+
+ps_err_e
+ps_lstop(struct ps_prochandle *ph, lwpid_t lwpid)
+{
+ return (ptrace (lwpid, PT_SUSPEND, 0, 0) == 0);
+}
+
+ps_err_e
+ps_lcontinue(struct ps_prochandle *ph, lwpid_t lwpid)
+{
+ return (ptrace (lwpid, PT_RESUME, 0, 0) == 0);
+}
+
+pid_t
+ps_getpid (struct ps_prochandle *ph)
+{
+ return ph->pid;
+}
+