Add libthread_db, a thread debugging support library.
authorDavid Xu <davidxu@dragonflybsd.org>
Sat, 7 May 2005 09:25:44 +0000 (09:25 +0000)
committerDavid Xu <davidxu@dragonflybsd.org>
Sat, 7 May 2005 09:25:44 +0000 (09:25 +0000)
lib/libthread_db/Makefile [new file with mode: 0644]
lib/libthread_db/libthread_xu.c [new file with mode: 0644]
lib/libthread_db/thread_db.c [new file with mode: 0644]
lib/libthread_db/thread_db.h [new file with mode: 0644]
lib/libthread_db/thread_db_int.h [new file with mode: 0644]

diff --git a/lib/libthread_db/Makefile b/lib/libthread_db/Makefile
new file mode 100644 (file)
index 0000000..1d58ab8
--- /dev/null
@@ -0,0 +1,15 @@
+# $DragonFly: src/lib/libthread_db/Makefile,v 1.1 2005/05/07 09:25:44 davidxu Exp $
+
+.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}
+
+LIB=   thread_db
+SHLIB_MAJOR= 1
+SRCS=  thread_db.c
+SRCS+= libthread_xu.c
+INCS=  thread_db.h
+WARNS?= 1
+
+CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/../../libexec/rtld-elf \
+       -I${.CURDIR}/../../libexec/rtld-elf/${MACHINE_ARCH}
+
+.include <bsd.lib.mk>
diff --git a/lib/libthread_db/libthread_xu.c b/lib/libthread_db/libthread_xu.c
new file mode 100644 (file)
index 0000000..361182e
--- /dev/null
@@ -0,0 +1,748 @@
+/*
+ * Copyright (c) 2004 Marcel Moolenaar
+ * Copyright (c) 2005 David Xu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $DragonFly: src/lib/libthread_db/libthread_xu.c,v 1.1 2005/05/07 09:25:44 davidxu Exp $
+ */
+
+#include <sys/cdefs.h>
+
+#include <proc_service.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <machine/tls.h>
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <rtld.h>
+#include <thread_db.h>
+#include <unistd.h>
+
+#include "thread_db_int.h"
+
+#define        TERMINATED      1
+
+struct td_thragent {
+       TD_THRAGENT_FIELDS;
+       psaddr_t        libthread_xu_debug_addr;
+       psaddr_t        thread_active_threads_addr;
+       psaddr_t        thread_bp_create_addr;
+       psaddr_t        thread_bp_death_addr;
+       psaddr_t        thread_event_mask_addr;
+       psaddr_t        thread_keytable_addr;
+       psaddr_t        thread_last_event_addr;
+       psaddr_t        thread_list_addr;
+       int             thread_max_keys;
+       int             thread_off_attr_flags;
+       int             thread_off_dtv;
+       int             thread_off_event_buf;
+       int             thread_off_event_mask;
+       int             thread_off_key_allocated;
+       int             thread_off_key_destructor;
+       int             thread_off_linkmap;
+       int             thread_off_next;
+       int             thread_off_report_events;
+       int             thread_off_state;
+       int             thread_off_tcb;
+       int             thread_off_tid;
+       int             thread_off_tlsindex;
+       int             thread_size_key;
+       int             thread_state_running;
+       int             thread_state_zoombie;
+};
+
+#define P2T(c) ps2td(c)
+
+static int pt_validate(const td_thrhandle_t *th);
+
+static int
+ps2td(int c)
+{
+       switch (c) {
+       case PS_OK:
+               return TD_OK;
+       case PS_ERR:
+               return TD_ERR;
+       case PS_BADPID:
+               return TD_BADPH;
+       case PS_BADLID:
+               return TD_NOLWP;
+       case PS_BADADDR:
+               return TD_ERR;
+       case PS_NOSYM:
+               return TD_NOLIBTHREAD;
+       case PS_NOFREGS:
+               return TD_NOFPREGS;
+       default:
+               return TD_ERR;
+       }
+}
+
+static td_err_e
+pt_init(void)
+{
+       return (0);
+}
+
+static td_err_e
+pt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
+{
+#define LOOKUP_SYM(proc, sym, addr)                    \
+       ret = ps_pglobal_lookup(proc, NULL, sym, addr); \
+       if (ret != 0) {                                 \
+               TDBG("can not find symbol: %s\n", sym); \
+               ret = TD_NOLIBTHREAD;                   \
+               goto error;                             \
+       }
+
+#define        LOOKUP_VAL(proc, sym, val)                      \
+       ret = ps_pglobal_lookup(proc, NULL, sym, &vaddr);\
+       if (ret != 0) {                                 \
+               TDBG("can not find symbol: %s\n", sym); \
+               ret = TD_NOLIBTHREAD;                   \
+               goto error;                             \
+       }                                               \
+       ret = ps_pread(proc, vaddr, val, sizeof(int));  \
+       if (ret != 0) {                                 \
+               TDBG("can not read value of %s\n", sym);\
+               ret = TD_NOLIBTHREAD;                   \
+               goto error;                             \
+       }
+
+       td_thragent_t *ta;
+       psaddr_t vaddr;
+       int dbg;
+       int ret;
+
+       TDBG_FUNC();
+
+       ta = malloc(sizeof(td_thragent_t));
+       if (ta == NULL)
+               return (TD_MALLOC);
+
+       ta->ph = ph;
+
+       LOOKUP_SYM(ph, "_libthr_debug",         &ta->libthread_xu_debug_addr);
+       LOOKUP_SYM(ph, "_thread_list",          &ta->thread_list_addr);
+       LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
+       LOOKUP_SYM(ph, "_thread_keytable",      &ta->thread_keytable_addr);
+       LOOKUP_SYM(ph, "_thread_last_event",    &ta->thread_last_event_addr);
+       LOOKUP_SYM(ph, "_thread_event_mask",    &ta->thread_event_mask_addr);
+       LOOKUP_SYM(ph, "_thread_bp_create",     &ta->thread_bp_create_addr);
+       LOOKUP_SYM(ph, "_thread_bp_death",      &ta->thread_bp_death_addr);
+       LOOKUP_VAL(ph, "_thread_off_attr_flags",        &ta->thread_off_attr_flags);
+       LOOKUP_VAL(ph, "_thread_size_key",      &ta->thread_size_key);
+       LOOKUP_VAL(ph, "_thread_off_tcb",       &ta->thread_off_tcb);
+       LOOKUP_VAL(ph, "_thread_off_tid",       &ta->thread_off_tid);
+       LOOKUP_VAL(ph, "_thread_off_next",      &ta->thread_off_next);
+       LOOKUP_VAL(ph, "_thread_off_state",     &ta->thread_off_state);
+       LOOKUP_VAL(ph, "_thread_max_keys",      &ta->thread_max_keys);
+       LOOKUP_VAL(ph, "_thread_off_key_allocated", &ta->thread_off_key_allocated);
+       LOOKUP_VAL(ph, "_thread_off_key_destructor", &ta->thread_off_key_destructor);
+       LOOKUP_VAL(ph, "_thread_state_running", &ta->thread_state_running);
+       LOOKUP_VAL(ph, "_thread_state_zoombie", &ta->thread_state_zoombie);
+       LOOKUP_VAL(ph, "_thread_off_report_events", &ta->thread_off_report_events);
+       LOOKUP_VAL(ph, "_thread_off_event_mask", &ta->thread_off_event_mask);
+       LOOKUP_VAL(ph, "_thread_off_event_buf", &ta->thread_off_event_buf);
+
+       ta->thread_off_linkmap = offsetof(Obj_Entry, linkmap);
+       ta->thread_off_tlsindex = offsetof(Obj_Entry, tlsindex);
+       ta->thread_off_dtv = offsetof(struct tls_tcb, tcb_dtv);
+
+       dbg = getpid();
+       /*
+        * If this fails it probably means we're debugging a core file and
+        * can't write to it.
+        */
+       ps_pwrite(ph, ta->libthread_xu_debug_addr, &dbg, sizeof(int));
+       *pta = ta;
+       return (0);
+
+error:
+       free(ta);
+       return (ret);
+}
+
+static td_err_e
+pt_ta_delete(td_thragent_t *ta)
+{
+       int dbg;
+
+       TDBG_FUNC();
+
+       dbg = 0;
+       /*
+        * Error returns from this write are not really a problem;
+        * the process doesn't exist any more.
+        */
+       ps_pwrite(ta->ph, ta->libthread_xu_debug_addr, &dbg, sizeof(int));
+       free(ta);
+       return (TD_OK);
+}
+
+static td_err_e
+pt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
+{
+       prgregset_t gregs;
+       TAILQ_HEAD(, pthread) thread_list;
+       psaddr_t pt;
+       long lwp;
+       int ret;
+
+       TDBG_FUNC();
+
+       if (id == 0)
+               return (TD_NOTHR);
+       ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
+               sizeof(thread_list));
+       if (ret != 0)
+               return (P2T(ret));
+       /* Iterate through thread list to find pthread */
+       pt = (psaddr_t)thread_list.tqh_first;
+       while (pt != NULL) {
+               ret = ps_pread(ta->ph, pt + ta->thread_off_tid,
+                              &lwp, sizeof(lwp));
+               if (ret != 0)
+                       return (P2T(ret));
+               if (lwp == id)
+                       break;
+               /* get next thread */
+               ret = ps_pread(ta->ph,
+                               pt + ta->thread_off_next,
+                               &pt, sizeof(pt));
+               if (ret != 0)
+                       return (P2T(ret));
+       }
+       if (pt == NULL)
+               return (TD_NOTHR);
+       th->th_ta = ta;
+       th->th_tid = id;
+       th->th_thread = pt;
+       return (TD_OK);
+}
+
+static td_err_e
+pt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
+{
+       return (pt_ta_map_id2thr(ta, lwp, th));
+}
+
+static td_err_e
+pt_ta_thr_iter(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)
+{
+       TAILQ_HEAD(, pthread) thread_list;
+       td_thrhandle_t th;
+       psaddr_t pt;
+       long lwp;
+       int ret;
+
+       TDBG_FUNC();
+
+       ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
+                      sizeof(thread_list));
+       if (ret != 0)
+               return (P2T(ret));
+       pt = (psaddr_t)thread_list.tqh_first;
+       while (pt != 0) {
+               ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &lwp,
+                             sizeof(lwp));
+               if (ret != 0)
+                       return (P2T(ret));
+               if (lwp != 0 && lwp != TERMINATED) {
+                       th.th_ta = ta;
+                       th.th_tid = (thread_t)lwp;
+                       th.th_thread = pt;
+                       if ((*callback)(&th, cbdata_p))
+                               return (TD_DBERR);
+               }
+               /* get next thread */
+               ret = ps_pread(ta->ph, pt + ta->thread_off_next, &pt,
+                              sizeof(pt));
+               if (ret != 0)
+                       return (P2T(ret));
+       }
+       return (TD_OK);
+}
+
+static td_err_e
+pt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
+{
+       char *keytable;
+       void *destructor;
+       int i, ret, allocated;
+
+       TDBG_FUNC();
+
+       keytable = malloc(ta->thread_max_keys * ta->thread_size_key);
+       if (keytable == NULL)
+               return (TD_MALLOC);
+       ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
+                      ta->thread_max_keys * ta->thread_size_key);
+       if (ret != 0) {
+               free(keytable);
+               return (P2T(ret));
+       }
+       for (i = 0; i < ta->thread_max_keys; i++) {
+               allocated = *(int *)(keytable + i * ta->thread_size_key +
+                       ta->thread_off_key_allocated);
+               destructor = *(void **)(keytable + i * ta->thread_size_key +
+                       ta->thread_off_key_destructor);
+               if (allocated) {
+                       ret = (ki)(i, destructor, arg);
+                       if (ret != 0) {
+                               free(keytable);
+                               return (TD_DBERR);
+                       }
+               }
+       }
+       free(keytable);
+       return (TD_OK);
+}
+
+static td_err_e
+pt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
+{
+
+       TDBG_FUNC();
+
+       switch (event) {
+       case TD_CREATE:
+               ptr->type = NOTIFY_BPT;
+               ptr->u.bptaddr = ta->thread_bp_create_addr;
+               return (0);
+       case TD_DEATH:
+               ptr->type = NOTIFY_BPT;
+               ptr->u.bptaddr = ta->thread_bp_death_addr;
+               return (0);
+       default:
+               return (TD_ERR);
+       }
+}
+
+static td_err_e
+pt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
+{
+       td_thr_events_t mask;
+       int ret;
+
+       TDBG_FUNC();
+       ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
+               sizeof(mask));
+       if (ret != 0)
+               return (P2T(ret));
+       mask |= *events;
+       ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
+               sizeof(mask));
+       return (P2T(ret));
+}
+
+static td_err_e
+pt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
+{
+       td_thr_events_t mask;
+       int ret;
+
+       TDBG_FUNC();
+       ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
+               sizeof(mask));
+       if (ret != 0)
+               return (P2T(ret));
+       mask &= ~*events;
+       ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
+               sizeof(mask));
+       return (P2T(ret));
+}
+
+static td_err_e
+pt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
+{
+       static td_thrhandle_t handle;
+
+       psaddr_t pt, pt_temp;
+       td_thr_events_e tmp;
+       long lwp;
+       int ret;
+
+       TDBG_FUNC();
+
+       ret = ps_pread(ta->ph, ta->thread_last_event_addr, &pt, sizeof(pt));
+       if (ret != 0)
+               return (P2T(ret));
+       if (pt == NULL)
+               return (TD_NOMSG);
+       /*
+        * Take the event pointer, at the time, libthr only reports event
+        * once a time, so it is not a link list.
+        */
+       pt_temp = NULL;
+       ps_pwrite(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp));
+
+       /* Read event info */
+       ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
+       if (ret != 0)
+               return (P2T(ret));
+       if (msg->event == 0)
+               return (TD_NOMSG);
+       /* Clear event */
+       tmp = 0;
+       ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
+       /* Convert event */
+       pt = (psaddr_t)msg->th_p;
+       ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &lwp, sizeof(lwp));
+       if (ret != 0)
+               return (P2T(ret));
+       handle.th_ta = ta;
+       handle.th_tid = lwp;
+       handle.th_thread = pt;
+       msg->th_p = &handle;
+       return (0);
+}
+
+static td_err_e
+pt_dbsuspend(const td_thrhandle_t *th, int suspend)
+{
+       td_thragent_t *ta = (td_thragent_t *)th->th_ta;
+       int ret;
+
+       TDBG_FUNC();
+
+       ret = pt_validate(th);
+       if (ret)
+               return (ret);
+
+       if (suspend)
+               ret = ps_lstop(ta->ph, th->th_tid);
+       else
+               ret = ps_lcontinue(ta->ph, th->th_tid);
+       return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_dbresume(const td_thrhandle_t *th)
+{
+       TDBG_FUNC();
+
+       return pt_dbsuspend(th, 0);
+}
+
+static td_err_e
+pt_thr_dbsuspend(const td_thrhandle_t *th)
+{
+       TDBG_FUNC();
+
+       return pt_dbsuspend(th, 1);
+}
+
+static td_err_e
+pt_thr_validate(const td_thrhandle_t *th)
+{
+       td_thrhandle_t temp;
+       int ret;
+
+       TDBG_FUNC();
+
+       ret = pt_ta_map_id2thr(th->th_ta, th->th_tid, &temp);
+       return (ret);
+}
+
+static td_err_e
+pt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
+{
+       const td_thragent_t *ta = th->th_ta;
+       int state;
+       int ret;
+
+       TDBG_FUNC();
+
+       ret = pt_validate(th);
+       if (ret)
+               return (ret);
+       ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_state,
+                      &state, sizeof(state));
+       if (ret != 0)
+               return (P2T(ret));
+       ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_report_events,
+               &info->ti_traceme, sizeof(int));
+       if (ret != 0)
+               return (P2T(ret));
+       ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
+               &info->ti_events, sizeof(td_thr_events_t));
+       if (ret != 0)
+               return (P2T(ret));
+       ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_tcb,
+               &info->ti_tls, sizeof(void *));
+       info->ti_lid = th->th_tid;
+       info->ti_tid = th->th_tid;
+       info->ti_thread = th->th_thread;
+       info->ti_ta_p = th->th_ta;
+       if (state == ta->thread_state_running)
+               info->ti_state = TD_THR_RUN;
+       else if (state == ta->thread_state_zoombie)
+               info->ti_state = TD_THR_ZOMBIE;
+       else
+               info->ti_state = TD_THR_SLEEP;
+       info->ti_type = TD_THR_USER;
+       return (0);
+}
+
+static td_err_e
+pt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
+{
+       const td_thragent_t *ta = th->th_ta;
+       int ret;
+
+       TDBG_FUNC();
+
+       ret = pt_validate(th);
+       if (ret)
+               return (ret);
+
+       ret = ps_lgetfpregs(ta->ph, th->th_tid, fpregs);
+       return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
+{
+       const td_thragent_t *ta = th->th_ta;
+       int ret;
+
+       TDBG_FUNC();
+
+       ret = pt_validate(th);
+       if (ret)
+               return (ret);
+
+       ret = ps_lgetregs(ta->ph, th->th_tid, gregs);
+       return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
+{
+       const td_thragent_t *ta = th->th_ta;
+       int ret;
+
+       TDBG_FUNC();
+
+       ret = pt_validate(th);
+       if (ret)
+               return (ret);
+
+       ret = ps_lsetfpregs(ta->ph, th->th_tid, fpregs);
+       return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
+{
+       const td_thragent_t *ta = th->th_ta;
+       int ret;
+
+       TDBG_FUNC();
+
+       ret = pt_validate(th);
+       if (ret)
+               return (ret);
+
+       ret = ps_lsetregs(ta->ph, th->th_tid, gregs);
+       return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_event_enable(const td_thrhandle_t *th, int en)
+{
+       const td_thragent_t *ta = th->th_ta;
+       int ret;
+
+       TDBG_FUNC();
+       ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_report_events,
+               &en, sizeof(int));
+       return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
+{
+       const td_thragent_t *ta = th->th_ta;
+       td_thr_events_t mask;
+       int ret;
+
+       TDBG_FUNC();
+       ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
+                       &mask, sizeof(mask));
+       mask |= *setp;
+       ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
+                       &mask, sizeof(mask));
+       return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
+{
+       const td_thragent_t *ta = th->th_ta;
+       td_thr_events_t mask;
+       int ret;
+
+       TDBG_FUNC();
+       ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
+                       &mask, sizeof(mask));
+       mask &= ~*setp;
+       ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
+                       &mask, sizeof(mask));
+       return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
+{
+       static td_thrhandle_t handle;
+       td_thragent_t *ta = (td_thragent_t *)th->th_ta;
+       psaddr_t pt, pt_temp;
+       long lwp;
+       int ret;
+       td_thr_events_e tmp;
+
+       TDBG_FUNC();
+       pt = th->th_thread;
+       ret = ps_pread(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp));
+       if (ret != 0)
+               return (P2T(ret));
+       /* Get event */
+       ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
+       if (ret != 0)
+               return (P2T(ret));
+       if (msg->event == 0)
+               return (TD_NOMSG);
+       /*
+        * Take the event pointer, at the time, libthr only reports event
+        * once a time, so it is not a link list.
+        */
+       if (pt == pt_temp) {
+               pt_temp = NULL;
+               ps_pwrite(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp));
+       }
+       /* Clear event */
+       tmp = 0;
+       ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
+       /* Convert event */
+       pt = (psaddr_t)msg->th_p;
+       ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &lwp, sizeof(lwp));
+       if (ret != 0)
+               return (P2T(ret));
+       handle.th_ta = ta;
+       handle.th_tid = lwp;
+       handle.th_thread = pt;
+       msg->th_p = &handle;
+       return (0);
+}
+
+static td_err_e
+pt_thr_sstep(const td_thrhandle_t *th, int step)
+{
+       TDBG_FUNC();
+
+       return pt_validate(th);
+}
+
+static int
+pt_validate(const td_thrhandle_t *th)
+{
+
+       if (th->th_tid == 0 || th->th_thread == NULL)
+               return (TD_ERR);
+       return (TD_OK);
+}
+
+static td_err_e
+pt_thr_tls_get_addr(const td_thrhandle_t *th, void *_linkmap, size_t offset,
+                   void **address)
+{
+       char *obj_entry;
+       const td_thragent_t *ta = th->th_ta;
+       psaddr_t tcb_addr, *dtv_addr, tcb_tp;
+       int tls_index, ret;
+
+       /* linkmap is a member of Obj_Entry */
+       obj_entry = (char *)_linkmap - ta->thread_off_linkmap;
+
+       /* get tlsindex of the object file */
+       ret = ps_pread(ta->ph,
+               obj_entry + ta->thread_off_tlsindex,
+               &tls_index, sizeof(tls_index));
+       if (ret != 0)
+               return (P2T(ret));
+
+       /* get thread tcb */
+       ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_tcb,
+               &tcb_addr, sizeof(tcb_addr));
+       if (ret != 0)
+               return (P2T(ret));
+
+       /* get dtv array address */
+       ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv,
+               &dtv_addr, sizeof(dtv_addr));
+       if (ret != 0)
+               return (P2T(ret));
+       /* now get the object's tls block base address */
+       ret = ps_pread(ta->ph, &dtv_addr[tls_index+1], address,
+               sizeof(*address));
+       if (ret != 0)
+               return (P2T(ret));
+
+       *address += offset;
+       return (TD_OK);
+}
+
+struct ta_ops libthread_xu_ops = {
+       .to_init                = pt_init,
+       .to_ta_clear_event      = pt_ta_clear_event,
+       .to_ta_delete           = pt_ta_delete,
+       .to_ta_event_addr       = pt_ta_event_addr,
+       .to_ta_event_getmsg     = pt_ta_event_getmsg,
+       .to_ta_map_id2thr       = pt_ta_map_id2thr,
+       .to_ta_map_lwp2thr      = pt_ta_map_lwp2thr,
+       .to_ta_new              = pt_ta_new,
+       .to_ta_set_event        = pt_ta_set_event,
+       .to_ta_thr_iter         = pt_ta_thr_iter,
+       .to_ta_tsd_iter         = pt_ta_tsd_iter,
+       .to_thr_clear_event     = pt_thr_clear_event,
+       .to_thr_dbresume        = pt_thr_dbresume,
+       .to_thr_dbsuspend       = pt_thr_dbsuspend,
+       .to_thr_event_enable    = pt_thr_event_enable,
+       .to_thr_event_getmsg    = pt_thr_event_getmsg,
+       .to_thr_get_info        = pt_thr_get_info,
+       .to_thr_getfpregs       = pt_thr_getfpregs,
+       .to_thr_getgregs        = pt_thr_getgregs,
+       .to_thr_set_event       = pt_thr_set_event,
+       .to_thr_setfpregs       = pt_thr_setfpregs,
+       .to_thr_setgregs        = pt_thr_setgregs,
+       .to_thr_validate        = pt_thr_validate,
+       .to_thr_tls_get_addr    = pt_thr_tls_get_addr,
+};
diff --git a/lib/libthread_db/thread_db.c b/lib/libthread_db/thread_db.c
new file mode 100644 (file)
index 0000000..13b6b4a
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly: src/lib/libthread_db/thread_db.c,v 1.1 2005/05/07 09:25:44 davidxu Exp $
+ */
+
+#include <sys/cdefs.h>
+#include <proc_service.h>
+#include <stddef.h>
+#include <thread_db.h>
+#include <unistd.h>
+
+#include "thread_db_int.h"
+
+struct td_thragent 
+{
+       TD_THRAGENT_FIELDS;
+};
+
+static TAILQ_HEAD(, td_thragent) proclist = TAILQ_HEAD_INITIALIZER(proclist);
+
+extern struct ta_ops libthread_xu_ops;
+
+static struct ta_ops *ops[] = {
+       &libthread_xu_ops,
+};
+
+td_err_e
+td_init(void)
+{
+       td_err_e ret, tmp;
+       size_t i;
+
+       ret = 0;
+       for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
+               if (ops[i]->to_init != NULL) {
+                       tmp = ops[i]->to_init();
+                       if (tmp != TD_OK)
+                               ret = tmp;
+               }
+       }
+       return (ret);
+}
+
+td_err_e
+td_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
+{
+       return (ta->ta_ops->to_ta_clear_event(ta, events));
+}
+
+td_err_e
+td_ta_delete(td_thragent_t *ta)
+{
+       TAILQ_REMOVE(&proclist, ta, ta_next);
+       return (ta->ta_ops->to_ta_delete(ta));
+}
+
+td_err_e
+td_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
+{
+       return (ta->ta_ops->to_ta_event_addr(ta, event, ptr));
+}
+
+td_err_e
+td_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
+{
+       return (ta->ta_ops->to_ta_event_getmsg(ta, msg));
+}
+
+td_err_e
+td_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
+{
+       return (ta->ta_ops->to_ta_map_id2thr(ta, id, th));
+}
+
+td_err_e
+td_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwpid, td_thrhandle_t *th)
+{
+       return (ta->ta_ops->to_ta_map_lwp2thr(ta, lwpid, th));
+}
+
+td_err_e
+td_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
+{
+       size_t i;
+
+       for (i = 0; i < sizeof(ops)/sizeof(ops[0]); ++i) {
+               if (ops[i]->to_ta_new(ph, pta) == TD_OK) {
+                       TAILQ_INSERT_HEAD(&proclist, *pta, ta_next);
+                       (*pta)->ta_ops = ops[i];
+                       return (TD_OK);
+               }
+       }
+       return (TD_NOLIBTHREAD);
+}
+
+td_err_e
+td_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
+{
+       return (ta->ta_ops->to_ta_set_event(ta, events));
+}
+
+td_err_e
+td_ta_thr_iter(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)
+{
+       return (ta->ta_ops->to_ta_thr_iter(ta, callback, cbdata_p, state,
+                   ti_pri, ti_sigmask_p, ti_user_flags));
+}
+
+td_err_e
+td_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *callback,
+    void *cbdata_p)
+{
+       return (ta->ta_ops->to_ta_tsd_iter(ta, callback, cbdata_p));
+}
+
+td_err_e
+td_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *events)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_clear_event(th, events));
+}
+
+td_err_e
+td_thr_dbresume(const td_thrhandle_t *th)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_dbresume(th));
+}
+
+td_err_e
+td_thr_dbsuspend(const td_thrhandle_t *th)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_dbsuspend(th));
+}
+
+td_err_e
+td_thr_event_enable(const td_thrhandle_t *th, int en)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_event_enable(th, en));
+}
+
+td_err_e
+td_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_event_getmsg(th, msg));
+}
+
+td_err_e
+td_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_get_info(th, info));
+}
+
+td_err_e
+td_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregset)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_getfpregs(th, fpregset));
+}
+
+td_err_e
+td_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_getgregs(th, gregs));
+}
+
+td_err_e
+td_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *events)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_set_event(th, events));
+}
+
+td_err_e
+td_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_setfpregs(th, fpregs));
+}
+
+td_err_e
+td_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_setgregs(th, gregs));
+}
+
+td_err_e
+td_thr_validate(const td_thrhandle_t *th)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_validate(th));
+}
+
+td_err_e
+td_thr_tls_get_addr(const td_thrhandle_t *th, void *linkmap, size_t offset,
+                   void **address)
+{
+       const td_thragent_t *ta = th->th_ta;
+       return (ta->ta_ops->to_thr_tls_get_addr(th, linkmap, offset, address));
+}
diff --git a/lib/libthread_db/thread_db.h b/lib/libthread_db/thread_db.h
new file mode 100644 (file)
index 0000000..32d87db
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
+ * Copyright (c) 2004 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $DragonFly: src/lib/libthread_db/thread_db.h,v 1.1 2005/05/07 09:25:44 davidxu Exp $
+ */
+
+#ifndef _THREAD_DB_H_
+#define        _THREAD_DB_H_
+
+#include <sys/procfs.h>
+#include <pthread.h>
+
+typedef enum {
+       TD_ERR = -1,            /* Unspecified error. */
+       TD_OK = 0,              /* No error. */
+       TD_BADKEY,
+       TD_BADPH,
+       TD_BADSH,
+       TD_BADTA,
+       TD_BADTH,
+       TD_DBERR,
+       TD_MALLOC,
+       TD_NOAPLIC,
+       TD_NOCAPAB,
+       TD_NOEVENT,
+       TD_NOFPREGS,
+       TD_NOLIBTHREAD,
+       TD_NOLWP,
+       TD_NOMSG,
+       TD_NOSV,
+       TD_NOTHR,
+       TD_NOTSD,
+       TD_NOXREGS,
+       TD_PARTIALREG
+} td_err_e;
+
+struct ps_prochandle;
+typedef struct td_thragent td_thragent_t;
+typedef long thread_t;                 /* Must be an integral type. */
+
+typedef struct {
+       const td_thragent_t *th_ta;
+       psaddr_t        th_thread;
+       thread_t        th_tid;
+} td_thrhandle_t;                      /* Used non-opaguely. */
+
+/*
+ * Events.
+ */
+
+typedef enum {
+       TD_EVENT_NONE = 0,
+       TD_CATCHSIG =   0x0001,
+       TD_CONCURRENCY= 0x0002,
+       TD_CREATE =     0x0004,
+       TD_DEATH =      0x0008,
+       TD_IDLE =       0x0010,
+       TD_LOCK_TRY =   0x0020,
+       TD_PREEMPT =    0x0040,
+       TD_PRI_INHERIT= 0x0080,
+       TD_READY =      0x0100,
+       TD_REAP =       0x0200,
+       TD_SLEEP =      0x0400,
+       TD_SWITCHFROM = 0x0800,
+       TD_SWITCHTO =   0x1000,
+       TD_TIMEOUT =    0x2000,
+       TD_ALL_EVENTS = ~0
+} td_thr_events_e;
+
+/* Compatibility with Linux. */
+#define        td_event_e      td_thr_events_e
+
+typedef struct {
+       td_thr_events_e event;
+       const td_thrhandle_t *th_p;
+       uintptr_t       data;
+} td_event_msg_t;
+
+typedef unsigned int td_thr_events_t;
+
+typedef enum {
+       NOTIFY_BPT,             /* User inserted breakpoint. */
+       NOTIFY_AUTOBPT,         /* Automatic breakpoint. */
+       NOTIFY_SYSCALL          /* Invocation of system call. */
+} td_notify_e;
+
+typedef struct {
+       td_notify_e     type;
+       union {
+               psaddr_t bptaddr;
+               int syscallno;
+       } u;
+} td_notify_t;
+
+static __inline void
+td_event_addset(td_thr_events_t *es, td_thr_events_e e)
+{
+       *es |= e;
+}
+
+static __inline void
+td_event_delset(td_thr_events_t *es, td_thr_events_e e)
+{
+       *es &= ~e;
+}
+
+static __inline void
+td_event_emptyset(td_thr_events_t *es)
+{
+       *es = TD_EVENT_NONE;
+}
+
+static __inline void
+td_event_fillset(td_thr_events_t *es)
+{
+       *es = TD_ALL_EVENTS;
+}
+
+static __inline int
+td_eventisempty(td_thr_events_t *es)
+{
+       return ((*es == TD_EVENT_NONE) ? 1 : 0);
+}
+
+static __inline int
+td_eventismember(td_thr_events_t *es, td_thr_events_e e)
+{
+       return ((*es & e) ? 1 : 0);
+}
+
+/*
+ * Thread info.
+ */
+
+typedef enum {
+       TD_THR_UNKNOWN = -1,
+       TD_THR_ANY_STATE = 0,
+       TD_THR_ACTIVE,
+       TD_THR_RUN,
+       TD_THR_SLEEP,
+       TD_THR_STOPPED,
+       TD_THR_STOPPED_ASLEEP,
+       TD_THR_ZOMBIE
+} td_thr_state_e;
+
+typedef enum
+{
+       TD_THR_SYSTEM = 1,
+       TD_THR_USER
+} td_thr_type_e;
+
+typedef pthread_key_t thread_key_t;
+
+typedef struct {
+       const td_thragent_t *ti_ta_p;
+       thread_t        ti_tid;
+       psaddr_t        ti_thread;
+       td_thr_state_e  ti_state;
+       td_thr_type_e   ti_type;
+       td_thr_events_t ti_events;
+       int             ti_pri;
+       lwpid_t         ti_lid;
+       char            ti_db_suspended;
+       char            ti_traceme;
+       sigset_t        ti_sigmask;
+       sigset_t        ti_pending;
+       psaddr_t        ti_tls;
+       psaddr_t        ti_startfunc;
+       psaddr_t        ti_stkbase;
+       size_t          ti_stksize;
+} td_thrinfo_t;
+
+/*
+ * Prototypes.
+ */
+
+typedef int td_key_iter_f(thread_key_t, void (*)(void *), void *);
+typedef int td_thr_iter_f(const td_thrhandle_t *, void *);
+
+/* Flags for `td_ta_thr_iter'. */
+#define        TD_THR_ANY_USER_FLAGS   0xffffffff
+#define        TD_THR_LOWEST_PRIORITY  -20
+#define        TD_SIGNO_MASK           NULL
+
+__BEGIN_DECLS
+td_err_e td_init(void);
+td_err_e td_ta_clear_event(const td_thragent_t *, td_thr_events_t *);
+td_err_e td_ta_delete(td_thragent_t *);
+td_err_e td_ta_event_addr(const td_thragent_t *, td_thr_events_e,
+    td_notify_t *);
+td_err_e td_ta_event_getmsg(const td_thragent_t *, td_event_msg_t *);
+td_err_e td_ta_map_id2thr(const td_thragent_t *, thread_t, td_thrhandle_t *);
+td_err_e td_ta_map_lwp2thr(const td_thragent_t *, lwpid_t, td_thrhandle_t *);
+td_err_e td_ta_new(struct ps_prochandle *, td_thragent_t **);
+td_err_e td_ta_set_event(const td_thragent_t *, td_thr_events_t *);
+td_err_e td_ta_thr_iter(const td_thragent_t *, td_thr_iter_f *, void *,
+    td_thr_state_e, int, sigset_t *, unsigned int);
+td_err_e td_ta_tsd_iter(const td_thragent_t *, td_key_iter_f *, void *);
+td_err_e td_thr_clear_event(const td_thrhandle_t *, td_thr_events_t *);
+td_err_e td_thr_dbresume(const td_thrhandle_t *);
+td_err_e td_thr_dbsuspend(const td_thrhandle_t *);
+td_err_e td_thr_event_enable(const td_thrhandle_t *, int);
+td_err_e td_thr_event_getmsg(const td_thrhandle_t *, td_event_msg_t *);
+td_err_e td_thr_get_info(const td_thrhandle_t *, td_thrinfo_t *);
+td_err_e td_thr_getfpregs(const td_thrhandle_t *, prfpregset_t *);
+td_err_e td_thr_getgregs(const td_thrhandle_t *, prgregset_t);
+td_err_e td_thr_set_event(const td_thrhandle_t *, td_thr_events_t *);
+td_err_e td_thr_setfpregs(const td_thrhandle_t *, const prfpregset_t *);
+td_err_e td_thr_setgregs(const td_thrhandle_t *, const prgregset_t);
+td_err_e td_thr_validate(const td_thrhandle_t *);
+td_err_e td_thr_tls_get_addr(const td_thrhandle_t *, void *, size_t, void **);
+
+__END_DECLS
+
+#endif /* _THREAD_DB_H_ */
diff --git a/lib/libthread_db/thread_db_int.h b/lib/libthread_db/thread_db_int.h
new file mode 100644 (file)
index 0000000..5bbfecc
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $DragonFly: src/lib/libthread_db/thread_db_int.h,v 1.1 2005/05/07 09:25:44 davidxu Exp $
+ */
+
+#ifndef _THREAD_DB_INT_H_
+#define        _THREAD_DB_INT_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#define        TD_THRAGENT_FIELDS                      \
+       struct ta_ops           *ta_ops;        \
+       TAILQ_ENTRY(td_thragent) ta_next;       \
+       struct ps_prochandle    *ph
+
+struct ta_ops {
+       td_err_e (*to_init)(void);
+
+       td_err_e (*to_ta_clear_event)(const td_thragent_t *,
+           td_thr_events_t *);
+       td_err_e (*to_ta_delete)(td_thragent_t *);
+       td_err_e (*to_ta_event_addr)(const td_thragent_t *, td_thr_events_e,
+           td_notify_t *);
+       td_err_e (*to_ta_event_getmsg)(const td_thragent_t *,
+           td_event_msg_t *);
+       td_err_e (*to_ta_map_id2thr)(const td_thragent_t *, thread_t,
+           td_thrhandle_t *);
+       td_err_e (*to_ta_map_lwp2thr)(const td_thragent_t *, lwpid_t,
+           td_thrhandle_t *);
+       td_err_e (*to_ta_new)(struct ps_prochandle *, td_thragent_t **);
+       td_err_e (*to_ta_set_event)(const td_thragent_t *, td_thr_events_t *);
+       td_err_e (*to_ta_thr_iter)(const td_thragent_t *, td_thr_iter_f *,
+           void *, td_thr_state_e, int, sigset_t *, unsigned int);
+       td_err_e (*to_ta_tsd_iter)(const td_thragent_t *, td_key_iter_f *,
+           void *);
+
+       td_err_e (*to_thr_clear_event)(const td_thrhandle_t *,
+           td_thr_events_t *);
+       td_err_e (*to_thr_dbresume)(const td_thrhandle_t *);
+       td_err_e (*to_thr_dbsuspend)(const td_thrhandle_t *);
+       td_err_e (*to_thr_event_enable)(const td_thrhandle_t *, int);
+       td_err_e (*to_thr_event_getmsg)(const td_thrhandle_t *,
+           td_event_msg_t *);
+       td_err_e (*to_thr_get_info)(const td_thrhandle_t *, td_thrinfo_t *);
+       td_err_e (*to_thr_getfpregs)(const td_thrhandle_t *, prfpregset_t *);
+       td_err_e (*to_thr_getgregs)(const td_thrhandle_t *, prgregset_t);
+       td_err_e (*to_thr_set_event)(const td_thrhandle_t *,
+           td_thr_events_t *);
+       td_err_e (*to_thr_setfpregs)(const td_thrhandle_t *,
+           const prfpregset_t *);
+       td_err_e (*to_thr_setgregs)(const td_thrhandle_t *, const prgregset_t);
+       td_err_e (*to_thr_validate)(const td_thrhandle_t *);
+       td_err_e (*to_thr_tls_get_addr)(const td_thrhandle_t *,
+                   void *, size_t, void **);
+};
+
+#ifdef TD_DEBUG
+#define TDBG(...) ps_plog(__VA_ARGS__)
+#define TDBG_FUNC() ps_plog("%s\n", __func__)
+#else
+#define TDBG(...)
+#define TDBG_FUNC()
+#endif
+
+#endif /* _THREAD_DB_INT_H_ */