summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authordavidxu <davidxu@FreeBSD.org>2004-07-15 03:36:35 +0000
committerdavidxu <davidxu@FreeBSD.org>2004-07-15 03:36:35 +0000
commitf64cd4bec9b12cafda2f341065400c877cd8c4f5 (patch)
tree5391cf6197c761aa0db16e260592bd92535e5ba9 /lib
parent61f27b8eb8fc6a29ff1468552ebbdc11a509ce7d (diff)
downloadFreeBSD-src-f64cd4bec9b12cafda2f341065400c877cd8c4f5.zip
FreeBSD-src-f64cd4bec9b12cafda2f341065400c877cd8c4f5.tar.gz
Add my initial work of libthread_db. The library is used by gdb to debug
threaded process. Current, only libpthread is supported, but macrel will work on it to support libthr and libc_r.
Diffstat (limited to 'lib')
-rw-r--r--lib/libthread_db/Makefile5
-rw-r--r--lib/libthread_db/include/thread_db.h287
-rw-r--r--lib/libthread_db/include/thread_db_int.h93
-rw-r--r--lib/libthread_db/pthread/Makefile19
-rw-r--r--lib/libthread_db/pthread/pthread_db.c1087
-rw-r--r--lib/libthread_db/pthread/pthread_db.h60
-rw-r--r--lib/libthread_db/pthread/pthread_db_i386.c105
-rw-r--r--lib/libthread_db/src/Makefile15
-rw-r--r--lib/libthread_db/src/thread_db.c359
9 files changed, 2030 insertions, 0 deletions
diff --git a/lib/libthread_db/Makefile b/lib/libthread_db/Makefile
new file mode 100644
index 0000000..e629598
--- /dev/null
+++ b/lib/libthread_db/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR=pthread src
+
+.include <bsd.subdir.mk>
diff --git a/lib/libthread_db/include/thread_db.h b/lib/libthread_db/include/thread_db.h
new file mode 100644
index 0000000..e5f0c32
--- /dev/null
+++ b/lib/libthread_db/include/thread_db.h
@@ -0,0 +1,287 @@
+/*
+ * 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 REGENTS 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 REGENTS 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _THREAD_DB_H_
+#define _THREAD_DB_H_
+
+#include <sys/types.h>
+#include <pthread.h>
+
+typedef enum
+{
+ TD_OK,
+ TD_ERR,
+ TD_NOTHR,
+ TD_NOSV,
+ TD_NOLWP,
+ TD_BADPH,
+ TD_BADTH,
+ TD_BADSH,
+ TD_BADTA,
+ TD_BADKEY,
+ TD_NOMSG,
+ TD_NOFPREGS,
+ TD_NOLIBTHREAD,
+ TD_NOEVENT,
+ TD_NOCAPAB,
+ TD_DBERR,
+ TD_NOAPLIC,
+ TD_NOTSD,
+ TD_MALLOC,
+ TD_PARTIALREG,
+ TD_NOXREGS
+} td_err_e;
+
+typedef enum
+{
+ TD_THR_ANY_STATE,
+ TD_THR_UNKNOWN,
+ TD_THR_STOPPED,
+ TD_THR_RUN,
+ TD_THR_ACTIVE,
+ TD_THR_ZOMBIE,
+ TD_THR_SLEEP,
+ TD_THR_STOPPED_ASLEEP
+} td_thr_state_e;
+
+typedef enum
+{
+ TD_THR_ANY_TYPE,
+ TD_THR_USER,
+ TD_THR_SYSTEM
+} td_thr_type_e;
+
+typedef long thread_t;
+typedef pthread_key_t thread_key_t;
+typedef struct td_thragent td_thragent_t;
+
+typedef struct td_thrhandle
+{
+ td_thragent_t *th_ta_p;
+ thread_t th_unique;
+ int th_ta_data;
+} td_thrhandle_t;
+
+/* Flags for `td_ta_thr_iter'. */
+#define TD_THR_ANY_USER_FLAGS 0xffffffff
+#define TD_THR_LOWEST_PRIORITY 0
+#define TD_SIGNO_MASK NULL
+
+typedef uint32_t td_thr_events_t;
+
+typedef enum
+{
+ TD_ALL_EVENTS,
+ TD_EVENT_NONE = TD_ALL_EVENTS,
+ TD_CREATE,
+ TD_DEATH,
+ TD_REAP,
+ TD_READY,
+ TD_SLEEP,
+ TD_SWITCHTO,
+ TD_SWITCHFROM,
+ TD_LOCK_TRY,
+ TD_CATCHSIG,
+ TD_IDLE,
+ TD_PREEMPT,
+ TD_PRI_INHERIT,
+ TD_CONCURRENCY,
+ TD_TIMEOUT,
+ TD_MIN_EVENT_NUM = TD_READY,
+ TD_MAX_EVENT_NUM = TD_TIMEOUT,
+ TD_EVENTS_ENABLE = 31
+} td_event_e;
+
+typedef enum
+{
+ NOTIFY_BPT,
+ NOTIFY_AUTOBPT,
+ NOTIFY_SYSCALL
+} td_notify_e;
+
+typedef struct td_notify
+{
+ td_notify_e type;
+ union {
+ psaddr_t bptaddr;
+ int syscallno;
+ } u;
+} td_notify_t;
+
+typedef struct td_event_msg
+{
+ td_event_e event;
+ const td_thrhandle_t *th_p;
+ union {
+#if 0
+ td_synchandle_t *sh;
+#endif
+ uintptr_t data;
+ } msg;
+} td_event_msg_t;
+
+/* Structure containing event data available in each thread structure. */
+typedef struct
+{
+ td_thr_events_t eventmask; /* Mask of enabled events. */
+ td_event_e eventnum; /* Number of last event. */
+ void *eventdata; /* Data associated with event. */
+} td_eventbuf_t;
+
+/* Gathered statistics about the process. */
+typedef struct td_ta_stats
+{
+ int nthreads; /* Total number of threads in use. */
+ int r_concurrency; /* Concurrency level requested by user. */
+ int nrunnable_num; /* Average runnable threads, numerator. */
+ int nrunnable_den; /* Average runnable threads, denominator. */
+ int a_concurrency_num; /* Achieved concurrency level, numerator. */
+ int a_concurrency_den; /* Achieved concurrency level, denominator. */
+ int nlwps_num; /* Average number of processes in use,
+ numerator. */
+ int nlwps_den; /* Average number of processes in use,
+ denominator. */
+ int nidle_num; /* Average number of idling processes,
+ numerator. */
+ int nidle_den; /* Average number of idling processes,
+ denominator. */
+} td_ta_stats_t;
+
+static inline void
+td_event_emptyset(td_thr_events_t *setp)
+{
+ *setp = 0;
+}
+
+static inline void
+td_event_fillset(td_thr_events_t *setp)
+{
+ *setp = 0xFFFFFFFF;
+}
+
+static inline void
+td_event_addset(td_thr_events_t *setp, int n)
+{
+ *setp |= (1 << (n-1));
+}
+
+static inline void
+td_event_delset(td_thr_events_t *setp, int n)
+{
+ *setp &= ~(1 << (n-1));
+}
+
+static inline int
+td_eventismember(td_thr_events_t *setp, int n)
+{
+ return (*setp & (1 << (n-1)) ? 1 : 0);
+}
+
+static inline int
+td_eventisempty(td_thr_events_t *setp)
+{
+ return (*setp == 0);
+}
+
+typedef int td_thr_iter_f(const td_thrhandle_t *, void *);
+typedef int td_key_iter_f(thread_key_t, void (*) (void *), void *);
+
+struct ps_prochandle;
+
+typedef struct td_thrinfo
+{
+ td_thragent_t *ti_ta_p;
+ unsigned int ti_user_flags;
+ thread_t ti_tid;
+ char *ti_tls;
+ psaddr_t ti_startfunc;
+ psaddr_t ti_stkbase;
+ long int ti_stksize;
+ psaddr_t ti_ro_area;
+ int ti_ro_size;
+ td_thr_state_e ti_state;
+ unsigned char ti_db_suspended;
+ td_thr_type_e ti_type;
+ intptr_t ti_pc;
+ intptr_t ti_sp;
+ short int ti_flags;
+ int ti_pri;
+ lwpid_t ti_lid;
+ sigset_t ti_sigmask;
+ unsigned char ti_traceme;
+ unsigned char ti_preemptflag;
+ unsigned char ti_pirecflag;
+ sigset_t ti_pending;
+ td_thr_events_t ti_events;
+} td_thrinfo_t;
+
+td_err_e td_init(void);
+td_err_e td_log(void);
+td_err_e td_ta_new(struct ps_prochandle *, td_thragent_t **);
+td_err_e td_ta_delete(td_thragent_t *);
+td_err_e td_ta_get_nthreads(const td_thragent_t *, int *);
+td_err_e td_ta_get_ph(const td_thragent_t *, struct ps_prochandle **);
+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 lwpid,
+ td_thrhandle_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_ta_event_addr(const td_thragent_t *, td_event_e , td_notify_t *);
+td_err_e td_ta_set_event(const td_thragent_t *, td_thr_events_t *);
+td_err_e td_ta_clear_event(const td_thragent_t *, td_thr_events_t *);
+td_err_e td_ta_event_getmsg(const td_thragent_t *, td_event_msg_t *);
+td_err_e td_ta_setconcurrency(const td_thragent_t *, int);
+td_err_e td_ta_enable_stats(const td_thragent_t *, int);
+td_err_e td_ta_reset_stats(const td_thragent_t *);
+td_err_e td_ta_get_stats(const td_thragent_t *, td_ta_stats_t *);
+td_err_e td_thr_validate(const td_thrhandle_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_getxregs(const td_thrhandle_t *, void *);
+td_err_e td_thr_getxregsize(const td_thrhandle_t *, int *);
+td_err_e td_thr_setfpregs(const td_thrhandle_t *, const prfpregset_t *);
+td_err_e td_thr_setgregs(const td_thrhandle_t *, prgregset_t);
+td_err_e td_thr_setxregs(const td_thrhandle_t *, const void *);
+td_err_e td_thr_event_enable(const td_thrhandle_t *, int);
+td_err_e td_thr_set_event(const td_thrhandle_t *, td_thr_events_t *);
+td_err_e td_thr_clear_event(const td_thrhandle_t *, td_thr_events_t *);
+td_err_e td_thr_event_getmsg(const td_thrhandle_t *, td_event_msg_t *);
+td_err_e td_thr_setprio(const td_thrhandle_t *, int);
+td_err_e td_thr_setsigpending(const td_thrhandle_t *, unsigned char,
+ const sigset_t *);
+td_err_e td_thr_sigsetmask(const td_thrhandle_t *, const sigset_t *);
+td_err_e td_thr_tsd(const td_thrhandle_t *, const thread_key_t, void **);
+td_err_e td_thr_dbsuspend(const td_thrhandle_t *);
+td_err_e td_thr_dbresume(const td_thrhandle_t *);
+td_err_e td_get_ta(int pid, td_thragent_t **);
+td_err_e td_ta_activated(td_thragent_t *, int *);
+td_err_e td_thr_sstep(td_thrhandle_t *, int);
+
+#endif
diff --git a/lib/libthread_db/include/thread_db_int.h b/lib/libthread_db/include/thread_db_int.h
new file mode 100644
index 0000000..177cb4a
--- /dev/null
+++ b/lib/libthread_db/include/thread_db_int.h
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _THREAD_DB_INT_H
+#define _THREAD_DB_INT_H
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+struct td_thragent {
+ struct ta_ops *ta_ops;
+ TAILQ_ENTRY(td_thragent) ta_next;
+};
+
+struct ta_ops {
+ td_err_e (*to_init)(void);
+ td_err_e (*to_ta_new)(struct ps_prochandle *, td_thragent_t **);
+ td_err_e (*to_ta_delete)(td_thragent_t *);
+ td_err_e (*to_ta_get_nthreads)(const td_thragent_t *, int *);
+ td_err_e (*to_ta_get_ph)(const td_thragent_t *, struct ps_prochandle **);
+ 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 lwpid,
+ td_thrhandle_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_ta_event_addr)(const td_thragent_t *, td_event_e , td_notify_t *);
+ td_err_e (*to_ta_set_event)(const td_thragent_t *, td_thr_events_t *);
+ td_err_e (*to_ta_clear_event)(const td_thragent_t *, td_thr_events_t *);
+ td_err_e (*to_ta_event_getmsg)(const td_thragent_t *, td_event_msg_t *);
+ td_err_e (*to_ta_setconcurrency)(const td_thragent_t *, int);
+ td_err_e (*to_ta_enable_stats)(const td_thragent_t *, int);
+ td_err_e (*to_ta_reset_stats)(const td_thragent_t *);
+ td_err_e (*to_ta_get_stats)(const td_thragent_t *, td_ta_stats_t *);
+ td_err_e (*to_thr_validate)(const td_thrhandle_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_getxregs)(const td_thrhandle_t *, void *);
+ td_err_e (*to_thr_getxregsize)(const td_thrhandle_t *, int *);
+ td_err_e (*to_thr_setfpregs)(const td_thrhandle_t *, const prfpregset_t *);
+ td_err_e (*to_thr_setgregs)(const td_thrhandle_t *, prgregset_t);
+ td_err_e (*to_thr_setxregs)(const td_thrhandle_t *, const void *);
+ td_err_e (*to_thr_event_enable)(const td_thrhandle_t *, int);
+ td_err_e (*to_thr_set_event)(const td_thrhandle_t *, td_thr_events_t *);
+ td_err_e (*to_thr_clear_event)(const td_thrhandle_t *, td_thr_events_t *);
+ td_err_e (*to_thr_event_getmsg)(const td_thrhandle_t *, td_event_msg_t *);
+ td_err_e (*to_thr_setprio)(const td_thrhandle_t *, int);
+ td_err_e (*to_thr_setsigpending)(const td_thrhandle_t *, unsigned char,
+ const sigset_t *);
+ td_err_e (*to_thr_sigsetmask)(const td_thrhandle_t *, const sigset_t *);
+ td_err_e (*to_thr_tsd)(const td_thrhandle_t *, const thread_key_t, void **);
+ td_err_e (*to_thr_dbsuspend)(const td_thrhandle_t *);
+ td_err_e (*to_thr_dbresume)(const td_thrhandle_t *);
+ td_err_e (*to_get_ta)(int pid, td_thragent_t **);
+ td_err_e (*to_ta_activated)(td_thragent_t *, int *);
+ td_err_e (*to_thr_sstep)(td_thrhandle_t *, int step);
+};
+
+#ifdef TD_DEBUG
+#define TDBG(...) ps_plog(__VA_ARGS__)
+#define TDBG_FUNC() ps_plog(__func__); ps_plog("\n")
+#else
+#define TDBG(...)
+#define TDBG_FUNC()
+#endif
+
+#endif
diff --git a/lib/libthread_db/pthread/Makefile b/lib/libthread_db/pthread/Makefile
new file mode 100644
index 0000000..cbd245d
--- /dev/null
+++ b/lib/libthread_db/pthread/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+LIB=pthread_db
+INTERNALLIB=yes
+NOPROFILE=yes
+NOPIC=yes
+CFLAGS+=-DPTHREAD_KERNEL -fpic -DPIC
+CFLAGS+=-I${.CURDIR}/../include \
+ -I${.CURDIR}/../../libc/include -I${.CURDIR}/../../libpthread/thread \
+ -I${.CURDIR}/../../../include
+CFLAGS+=-I${.CURDIR}/../../libpthread/arch/${MACHINE_ARCH}/include
+CFLAGS+=-I${.CURDIR}/../../libpthread/sys
+CFLAGS+=-Wall
+
+.PATH: ${.CURDIR}
+
+SRCS+=pthread_db.c pthread_db_${MACHINE_ARCH}.c
+
+.include <bsd.lib.mk>
diff --git a/lib/libthread_db/pthread/pthread_db.c b/lib/libthread_db/pthread/pthread_db.c
new file mode 100644
index 0000000..e4f74a6
--- /dev/null
+++ b/lib/libthread_db/pthread/pthread_db.c
@@ -0,0 +1,1087 @@
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <thr_private.h>
+#include <sys/types.h>
+#include <sys/kse.h>
+#include <sys/ptrace.h>
+#include <proc_service.h>
+#include <thread_db.h>
+
+#include "pthread_db.h"
+
+struct pt_map {
+ enum {
+ PT_NONE,
+ PT_USER,
+ PT_LWP
+ } type;
+
+ union {
+ lwpid_t lwp;
+ psaddr_t thr;
+ };
+};
+
+#define P2T(c) ps2td(c)
+
+static td_err_e pt_ta_activated(pt_thragent_t *ta, int *a);
+static long pt_map_thread(const pt_thragent_t *ta, psaddr_t pt);
+static long pt_map_lwp(const pt_thragent_t *ta, lwpid_t lwp);
+static void pt_unmap_lwp(const pt_thragent_t *ta, lwpid_t lwp);
+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)
+{
+ pt_md_init();
+ return (0);
+}
+
+static td_err_e
+pt_ta_new(struct ps_prochandle *ph, pt_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; \
+ }
+
+ pt_thragent_t *ta;
+ int dbg;
+ int ret;
+
+ TDBG_FUNC();
+
+ ta = malloc(sizeof(pt_thragent_t));
+ if (ta == NULL)
+ return (TD_MALLOC);
+
+ ta->ph = ph;
+ ta->thread_activated = 0;
+ ta->map = NULL;
+ ta->map_len = 0;
+
+ LOOKUP_SYM(ph, "_libkse_debug", &ta->libkse_debug_addr);
+ LOOKUP_SYM(ph, "_thread_list", &ta->thread_list_addr);
+ LOOKUP_SYM(ph, "_thread_activated", &ta->thread_activated_addr);
+ LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
+ LOOKUP_SYM(ph, "_thread_keytable", &ta->thread_keytable_addr);
+
+ dbg = getpid();
+ /*
+ * If this fails it probably means we're debugging a core file and
+ * can't write to it.
+ */
+ ps_pdwrite(ph, ta->libkse_debug_addr, &dbg, sizeof(int));
+ *pta = ta;
+ return (0);
+
+error:
+ free(ta);
+ return (ret);
+}
+
+static td_err_e
+pt_ta_delete(pt_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_pdwrite(ta->ph, ta->libkse_debug_addr, &dbg, sizeof(int));
+ if (ta->map)
+ free(ta->map);
+ free(ta);
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_get_nthreads (const pt_thragent_t *ta, int *np)
+{
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = ps_pdread(ta->ph, ta->thread_active_threads_addr, np,
+ sizeof(int));
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_ta_get_ph(const pt_thragent_t *ta, struct ps_prochandle **ph)
+{
+ TDBG_FUNC();
+
+ *ph = ta->ph;
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_map_id2thr(const pt_thragent_t *ta, thread_t id, td_thrhandle_t *th)
+{
+ prgregset_t gregs;
+ TAILQ_HEAD(, pthread) thread_list;
+ psaddr_t pt, tcb_addr;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ if (id < 0 || id >= ta->map_len || ta->map[id].type == PT_NONE)
+ return (TD_NOTHR);
+ ret = ps_pdread(ta->ph, ta->thread_list_addr, &thread_list,
+ sizeof(thread_list));
+ if (ret != 0)
+ return (P2T(ret));
+ pt = (psaddr_t)thread_list.tqh_first;
+ if (ta->map[id].type == PT_LWP) {
+ /*
+ * if we are referencing a lwp, make sure it was not already
+ * mapped to user thread.
+ */
+ while (pt != 0) {
+ ret = ps_pdread(ta->ph,
+ pt + offsetof(struct pthread, tcb),
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ ret = ps_pdread(ta->ph,
+ tcb_addr + offsetof(struct tcb,
+ tcb_tmbx.tm_lwp),
+ &lwp, sizeof(lwp));
+ if (ret != 0)
+ return (P2T(ret));
+ /*
+ * If the lwp was already mapped to userland thread,
+ * we shouldn't reference it directly in future.
+ */
+ if (lwp == ta->map[id].lwp) {
+ ta->map[id].type = PT_NONE;
+ return (TD_NOTHR);
+ }
+ /* get next thread */
+ ret = ps_pdread(ta->ph,
+ pt + offsetof(struct pthread, tle.tqe_next),
+ &pt, sizeof(pt));
+ if (ret != 0)
+ return (P2T(ret));
+ }
+ /* check lwp */
+ ret = ptrace(PT_GETREGS, ta->map[id].lwp, (caddr_t)&gregs, 0);
+ if (ret != 0) {
+ /* no longer exists */
+ ta->map[id].type = PT_NONE;
+ return (TD_NOTHR);
+ }
+ } else {
+ while (pt != 0 && ta->map[id].thr != pt) {
+ ret = ps_pdread(ta->ph,
+ pt + offsetof(struct pthread, tcb),
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ /* get next thread */
+ ret = ps_pdread(ta->ph,
+ pt + offsetof(struct pthread, tle.tqe_next),
+ &pt, sizeof(pt));
+ if (ret != 0)
+ return (P2T(ret));
+ }
+
+ if (pt == 0) {
+ /* no longer exists */
+ ta->map[id].type = PT_NONE;
+ return (TD_NOTHR);
+ }
+ }
+ th->th_ta_p = (td_thragent_t *)ta;
+ th->th_unique = id;
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_map_lwp2thr(const pt_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
+{
+ TAILQ_HEAD(, pthread) thread_list;
+ psaddr_t pt, ptr;
+ lwpid_t tmp_lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = ps_pdread(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_pdread(ta->ph, pt + offsetof(struct pthread, tcb),
+ &ptr, sizeof(ptr));
+ if (ret != 0)
+ return (P2T(ret));
+ ptr += offsetof(struct tcb, tcb_tmbx.tm_lwp);
+ ret = ps_pdread(ta->ph, ptr, &tmp_lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (tmp_lwp == lwp) {
+ th->th_ta_p = (td_thragent_t *)ta;
+ th->th_unique = pt_map_thread(ta, pt);
+ if (th->th_unique == -1)
+ return (TD_MALLOC);
+ pt_unmap_lwp(ta, lwp);
+ return (TD_OK);
+ }
+
+ /* get next thread */
+ ret = ps_pdread(ta->ph,
+ pt + offsetof(struct pthread, tle.tqe_next),
+ &pt, sizeof(pt));
+ if (ret != 0)
+ return (P2T(ret));
+ }
+
+ return (TD_NOTHR);
+}
+
+static td_err_e
+pt_ta_thr_iter(const pt_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;
+ int ret, activated;
+
+ TDBG_FUNC();
+
+ ret = pt_ta_activated((pt_thragent_t *)ta, &activated);
+ if (ret != 0)
+ return (P2T(ret));
+ if (!activated)
+ return (0);
+ ret = ps_pdread(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) {
+ th.th_ta_p = (td_thragent_t *)ta;
+ th.th_unique = pt_map_thread(ta, pt);
+ /* should we unmap lwp here ? */
+ if (th.th_unique == -1)
+ return (TD_MALLOC);
+ if ((*callback)(&th, cbdata_p))
+ return (TD_DBERR);
+ /* get next thread */
+ ret = ps_pdread(ta->ph,
+ pt + offsetof(struct pthread, tle.tqe_next),
+ &pt, sizeof(pt));
+ if (ret != 0)
+ return (P2T(ret));
+ }
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_tsd_iter(const pt_thragent_t *ta, td_key_iter_f *ki, void *arg)
+{
+ struct pthread_key keytable[PTHREAD_KEYS_MAX];
+ int i, ret;
+
+ TDBG_FUNC();
+
+ ret = ps_pdread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
+ sizeof(keytable));
+ if (ret != 0)
+ return (P2T(ret));
+
+ for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
+ if (keytable[i].allocated) {
+ ret = (ki)(i, keytable[i].destructor, arg);
+ if (ret != 0)
+ return (TD_DBERR);
+ }
+ }
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_event_addr(const pt_thragent_t *ta, td_event_e event, td_notify_t *ptr)
+{
+ TDBG_FUNC();
+ return (TD_NOEVENT);
+}
+
+static td_err_e
+pt_ta_set_event(const pt_thragent_t *ta, td_thr_events_t *events)
+{
+ TDBG_FUNC();
+ return (TD_ERR);
+}
+
+static td_err_e
+pt_ta_clear_event(const pt_thragent_t *ta, td_thr_events_t *events)
+{
+ TDBG_FUNC();
+ return (TD_ERR);
+}
+
+static td_err_e
+pt_ta_event_getmsg(const pt_thragent_t *ta, td_event_msg_t *msg)
+{
+ TDBG_FUNC();
+ return (TD_NOMSG);
+}
+
+static td_err_e
+pt_ta_setconcurrency(const pt_thragent_t *ta, int level)
+{
+ TDBG_FUNC();
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_enable_stats(const pt_thragent_t *ta, int enable)
+{
+ TDBG_FUNC();
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_reset_stats(const pt_thragent_t *ta)
+{
+ TDBG_FUNC();
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_get_stats(const pt_thragent_t *ta, td_ta_stats_t *statsp)
+{
+ TDBG_FUNC();
+ return (TD_OK);
+}
+
+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((pt_thragent_t *)th->th_ta_p, th->th_unique,
+ &temp);
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
+{
+ struct pthread pt;
+ pt_thragent_t *ta = (pt_thragent_t *)th->th_ta_p;
+ psaddr_t tcb_addr;
+ uint32_t dflags;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ memset(info, 0, sizeof(*info));
+ if (ta->map[th->th_unique].type == PT_LWP) {
+ info->ti_type = TD_THR_SYSTEM;
+ info->ti_lid = ta->map[th->th_unique].lwp;
+ info->ti_tid = th->th_unique;
+ info->ti_state = TD_THR_RUN;
+ info->ti_type = TD_THR_SYSTEM;
+ return (TD_OK);
+ }
+
+ ret = ps_pdread(ta->ph, (psaddr_t)(ta->map[th->th_unique].thr),
+ &pt, sizeof(pt));
+ if (ret != 0)
+ return (P2T(ret));
+ if (pt.magic != THR_MAGIC)
+ return (TD_BADTH);
+ tcb_addr = (psaddr_t) pt.tcb;
+ ret = ps_pdread(ta->ph,
+ tcb_addr + offsetof(struct tcb, tcb_tmbx.tm_lwp),
+ &info->ti_lid, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ ret = ps_pdread(ta->ph,
+ tcb_addr + offsetof(struct tcb, tcb_tmbx.tm_dflags),
+ &dflags, sizeof(dflags));
+ info->ti_ta_p = th->th_ta_p;
+ info->ti_tid = th->th_unique;
+ info->ti_tls = (char *)pt.specific;
+ info->ti_startfunc = (psaddr_t)pt.start_routine;
+ info->ti_stkbase = (psaddr_t) pt.attr.stackaddr_attr;
+ info->ti_stksize = pt.attr.stacksize_attr;
+ switch (pt.state) {
+ case PS_RUNNING:
+ info->ti_state = TD_THR_RUN;
+ break;
+ case PS_LOCKWAIT:
+ case PS_MUTEX_WAIT:
+ case PS_COND_WAIT:
+ case PS_SIGSUSPEND:
+ case PS_SIGWAIT:
+ case PS_JOIN:
+ case PS_SUSPENDED:
+ case PS_DEADLOCK:
+ case PS_SLEEP_WAIT:
+ info->ti_state = TD_THR_SLEEP;
+ break;
+ case PS_DEAD:
+ info->ti_state = TD_THR_ZOMBIE;
+ break;
+ default:
+ info->ti_state = TD_THR_UNKNOWN;
+ break;
+ }
+
+ info->ti_db_suspended = ((dflags & TMDF_DONOTRUNUSER) != 0);
+ info->ti_type = TD_THR_USER;
+ info->ti_pri = pt.active_priority;
+ info->ti_sigmask = pt.sigmask;
+ info->ti_traceme = 0;
+ info->ti_pending = pt.sigpend;
+ info->ti_events = 0;
+ return (0);
+}
+
+static td_err_e
+pt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
+{
+ struct kse_thr_mailbox tmbx;
+ pt_thragent_t *ta = (pt_thragent_t *)th->th_ta_p;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_unique].type == PT_LWP) {
+ ret = ps_lgetfpregs(ta->ph, ta->map[th->th_unique].lwp, fpregs);
+ return (P2T(ret));
+ }
+
+ ret = ps_pdread(ta->ph, ta->map[th->th_unique].thr +
+ offsetof(struct pthread, tcb),
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pdread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0) {
+ ret = ps_lgetfpregs(ta->ph, lwp, fpregs);
+ return (P2T(ret));
+ }
+
+ ret = ps_pdread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (P2T(ret));
+ pt_ucontext_to_fpreg(&tmbx.tm_context, fpregs);
+ return (0);
+}
+
+static td_err_e
+pt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
+{
+ struct kse_thr_mailbox tmbx;
+ pt_thragent_t *ta = (pt_thragent_t *)th->th_ta_p;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_unique].type == PT_LWP) {
+ ret = ps_lgetregs(ta->ph,
+ ta->map[th->th_unique].lwp, gregs);
+ return (P2T(ret));
+ }
+
+ ret = ps_pdread(ta->ph, ta->map[th->th_unique].thr +
+ offsetof(struct pthread, tcb),
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pdread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0) {
+ ret = ps_lgetregs(ta->ph, lwp, gregs);
+ return (P2T(ret));
+ }
+ ret = ps_pdread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (P2T(ret));
+ pt_ucontext_to_reg(&tmbx.tm_context, gregs);
+ return (0);
+}
+
+static td_err_e
+pt_thr_getxregs (const td_thrhandle_t *th, void *xregs)
+{
+ return (TD_NOXREGS);
+}
+
+static td_err_e
+pt_thr_getxregsize (const td_thrhandle_t *th, int *sizep)
+{
+ return (TD_NOXREGS);
+}
+
+static td_err_e
+pt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
+{
+ struct kse_thr_mailbox tmbx;
+ pt_thragent_t *ta = (pt_thragent_t *)th->th_ta_p;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_unique].type == PT_LWP) {
+ ret = ps_lsetfpregs(ta->ph, ta->map[th->th_unique].lwp, fpregs);
+ return (P2T(ret));
+ }
+
+ ret = ps_pdread(ta->ph, ta->map[th->th_unique].thr +
+ offsetof(struct pthread, tcb),
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pdread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0) {
+ ret = ps_lsetfpregs(ta->ph, lwp, fpregs);
+ return (P2T(ret));
+ }
+ /*
+ * Read a copy of context, this makes sure that registers
+ * not covered by structure reg won't be clobbered
+ */
+ ret = ps_pdread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (P2T(ret));
+
+ pt_fpreg_to_ucontext(fpregs, &tmbx.tm_context);
+ ret = ps_pdwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_setgregs(const td_thrhandle_t *th, prgregset_t gregs)
+{
+ struct kse_thr_mailbox tmbx;
+ pt_thragent_t *ta = (pt_thragent_t *)th->th_ta_p;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_unique].type == PT_LWP) {
+ ret = ps_lsetregs(ta->ph, ta->map[th->th_unique].lwp, gregs);
+ return (P2T(ret));
+ }
+
+ ret = ps_pdread(ta->ph, ta->map[th->th_unique].thr +
+ offsetof(struct pthread, tcb),
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pdread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0) {
+ ret = ps_lsetregs(ta->ph, lwp, gregs);
+ return (P2T(ret));
+ }
+
+ /*
+ * Read a copy of context, make sure that registers
+ * not covered by structure reg won't be clobbered
+ */
+ ret = ps_pdread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (P2T(ret));
+ pt_reg_to_ucontext(gregs, &tmbx.tm_context);
+ ret = ps_pdwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_setxregs(const td_thrhandle_t *th, const void *addr)
+{
+ TDBG_FUNC();
+ return (TD_NOXREGS);
+}
+
+static td_err_e
+pt_thr_event_enable(const td_thrhandle_t *th, int en)
+{
+ TDBG_FUNC();
+ return (TD_ERR);
+}
+
+static td_err_e
+pt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
+{
+ TDBG_FUNC();
+ return (TD_ERR);
+}
+
+static td_err_e
+pt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
+{
+ TDBG_FUNC();
+ return (TD_ERR);
+}
+
+static td_err_e
+pt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
+{
+ TDBG_FUNC();
+ return (TD_NOMSG);
+}
+
+static td_err_e
+pt_thr_setprio(const td_thrhandle_t *th, int pri)
+{
+ TDBG_FUNC();
+ return (TD_OK);
+}
+
+static td_err_e
+pt_thr_setsigpending(const td_thrhandle_t *th, unsigned char n,
+ const sigset_t *set)
+{
+ TDBG_FUNC();
+ return (TD_OK);
+}
+
+static td_err_e
+pt_thr_sigsetmask(const td_thrhandle_t *th, const sigset_t *set)
+{
+ TDBG_FUNC();
+ return (TD_OK);
+}
+
+static td_err_e
+pt_thr_tsd(const td_thrhandle_t *th, const thread_key_t key, void **data)
+{
+ pt_thragent_t *ta = (pt_thragent_t *)th->th_ta_p;
+ struct pthread_specific_elem *spec, elem;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (key < 0 || key >= PTHREAD_KEYS_MAX)
+ return (TD_BADKEY);
+
+ if (ta->map[th->th_unique].type == PT_LWP) {
+ *data = NULL;
+ return (TD_OK);
+ }
+
+ ret = ps_pdread(ta->ph,
+ ta->map[th->th_unique].thr +
+ offsetof(struct pthread, specific),
+ &spec, sizeof(spec));
+ if (ret == 0) {
+ if (spec == NULL) {
+ *data = NULL;
+ return (0);
+ }
+ ret = ps_pdread(ta->ph, (psaddr_t)&spec[key],
+ &elem, sizeof(elem));
+ if (ret == 0)
+ *data = (void *)elem.data;
+ }
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_dbsuspend(const td_thrhandle_t *th, int suspend)
+{
+ pt_thragent_t *ta = (pt_thragent_t *)th->th_ta_p;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ uint32_t dflags;
+ int attrflags;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_unique].type == PT_LWP) {
+ if (suspend)
+ ret = ps_lstop(ta->ph, ta->map[th->th_unique].lwp);
+ else
+ ret = ps_lcontinue(ta->ph, ta->map[th->th_unique].lwp);
+ return (P2T(ret));
+ }
+
+ ret = ps_pdread(ta->ph, ta->map[th->th_unique].thr +
+ offsetof(struct pthread, attr.flags),
+ &attrflags, sizeof(attrflags));
+ if (ret != 0)
+ return (P2T(ret));
+ ret = ps_pdread(ta->ph, ta->map[th->th_unique].thr +
+ offsetof(struct pthread, tcb),
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pdread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ /*
+ * don't stop lwp assigned to a M:N thread, it belongs
+ * to UTS, UTS shouldn't be stopped.
+ */
+ if (lwp != 0 && (attrflags & PTHREAD_SCOPE_SYSTEM)) {
+ /* dont' suspend signal thread */
+ if (attrflags & THR_SIGNAL_THREAD)
+ return 0;
+ if (suspend)
+ ret = ps_lstop(ta->ph, lwp);
+ else
+ ret = ps_lcontinue(ta->ph, lwp);
+ return (P2T(ret));
+ }
+
+ ret = ps_pdread(ta->ph,
+ tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
+ &dflags, sizeof(dflags));
+ if (ret != 0)
+ return (P2T(ret));
+
+ if (suspend)
+ dflags |= TMDF_DONOTRUNUSER;
+ else
+ dflags &= ~TMDF_DONOTRUNUSER;
+ ret = ps_pdwrite(ta->ph,
+ tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
+ &dflags, sizeof(dflags));
+ return (P2T(ret));
+}
+
+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_dbresume(const td_thrhandle_t *th)
+{
+ TDBG_FUNC();
+ return pt_dbsuspend(th, 0);
+}
+
+static td_err_e
+pt_ta_activated(pt_thragent_t *ta, int *a)
+{
+ int ret;
+
+ TDBG_FUNC();
+
+ if (ta->thread_activated) {
+ *a = ta->thread_activated;
+ return (TD_OK);
+ }
+ ret = ps_pdread(ta->ph, ta->thread_activated_addr,
+ &ta->thread_activated, sizeof(int));
+ if (ret == 0)
+ *a = ta->thread_activated;
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_sstep(td_thrhandle_t *th, int step)
+{
+ struct kse_thr_mailbox tmbx;
+ struct reg regs;
+ pt_thragent_t *ta = (pt_thragent_t *)th->th_ta_p;
+ psaddr_t tcb_addr, tmbx_addr;
+ uint32_t tmp;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_unique].type == PT_LWP) {
+ /* Let debugger deal with single step flag in in kernel */
+ return (0);
+ }
+
+ ret = ps_pdread(ta->ph, ta->map[th->th_unique].thr +
+ offsetof(struct pthread, tcb),
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+
+ /* Clear or set single step flag in thread mailbox */
+ tmp = step ? TMDF_SSTEP : 0;
+ ret = ps_pdwrite(ta->ph, tcb_addr + offsetof(struct tcb,
+ tcb_tmbx.tm_dflags), &tmp, sizeof(tmp));
+ if (ret != 0)
+ return (P2T(ret));
+ /* Get lwp */
+ ret = ps_pdread(ta->ph, tcb_addr + offsetof(struct tcb,
+ tcb_tmbx.tm_lwp), &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0) /* Let debugger deal with single step flag in in kernel */
+ return (0);
+
+ tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
+ /*
+ * context is in userland, some architectures store
+ * single step status in registers, we should change
+ * these registers.
+ */
+ ret = ps_pdread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret == 0) {
+ pt_ucontext_to_reg(&tmbx.tm_context, &regs);
+ /* only write out if it is really changed. */
+ if (pt_reg_sstep(&regs, step) != 0) {
+ pt_reg_to_ucontext(&regs, &tmbx.tm_context);
+ ret = ps_pdwrite(ta->ph, tmbx_addr, &tmbx,
+ sizeof(tmbx));
+ }
+ }
+ return (P2T(ret));
+}
+
+static long
+_map_thread(pt_thragent_t *ta, psaddr_t pt, int type)
+{
+ struct pt_map *new;
+ int i, first = -1;
+
+ /* leave zero out */
+ for (i = 1; i < ta->map_len; ++i) {
+ if (ta->map[i].type == PT_NONE) {
+ if (first == -1)
+ first = i;
+ } else if (ta->map[i].type == type && ta->map[i].thr == pt) {
+ return (i);
+ }
+ }
+
+ if (first == -1) {
+ if (ta->map_len == 0) {
+ ta->map = calloc(20, sizeof(struct pt_map));
+ if (ta->map == NULL)
+ return (-1);
+ ta->map_len = 20;
+ first = 1;
+ } else {
+ new = realloc(ta->map,
+ sizeof(struct pt_map) * ta->map_len * 2);
+ if (new == NULL)
+ return (-1);
+ memset(new + ta->map_len, '\0', sizeof(struct pt_map) *
+ ta->map_len);
+ first = ta->map_len;
+ ta->map = new;
+ ta->map_len *= 2;
+ }
+ }
+
+ ta->map[first].type = type;
+ ta->map[first].thr = pt;
+ return (first);
+}
+
+static long
+pt_map_thread(const pt_thragent_t *ta, psaddr_t pt)
+{
+ return _map_thread((pt_thragent_t *)ta, pt, PT_USER);
+}
+
+#if 0
+static long
+pt_map_lwp(const pt_thragent_t *ta, lwpid_t lwp)
+{
+ return _map_thread((pt_thragent_t *)ta, (psaddr_t)lwp, PT_LWP);
+}
+#endif
+
+static void
+pt_unmap_lwp(const pt_thragent_t *ta, lwpid_t lwp)
+{
+ int i;
+
+ for (i = 0; i < ta->map_len; ++i) {
+ if (ta->map[i].type == PT_LWP && ta->map[i].lwp == lwp) {
+ ta->map[i].type = PT_NONE;
+ return;
+ }
+ }
+}
+
+static int
+pt_validate(const td_thrhandle_t *th)
+{
+ pt_thragent_t *ta = (pt_thragent_t *)th->th_ta_p;
+
+ if (th->th_unique < 0 || th->th_unique >= ta->map_len ||
+ ta->map[th->th_unique].type == PT_NONE)
+ return (TD_NOTHR);
+ return (TD_OK);
+}
+
+struct ta_ops pthread_ops = {
+ .to_init = pt_init,
+ .to_ta_new = (void *)pt_ta_new,
+ .to_ta_delete = (void *)pt_ta_delete,
+ .to_ta_get_nthreads = (void *)pt_ta_get_nthreads,
+ .to_ta_get_ph = (void *)pt_ta_get_ph,
+ .to_ta_map_id2thr = (void *)pt_ta_map_id2thr,
+ .to_ta_map_lwp2thr = (void *)pt_ta_map_lwp2thr,
+ .to_ta_thr_iter = (void *)pt_ta_thr_iter,
+ .to_ta_tsd_iter = (void *)pt_ta_tsd_iter,
+ .to_ta_event_addr = (void *)pt_ta_event_addr,
+ .to_ta_set_event = (void *)pt_ta_set_event,
+ .to_ta_clear_event = (void *)pt_ta_clear_event,
+ .to_ta_event_getmsg = (void *)pt_ta_event_getmsg,
+ .to_ta_setconcurrency = (void *)pt_ta_setconcurrency,
+ .to_ta_enable_stats = (void *)pt_ta_enable_stats,
+ .to_ta_reset_stats = (void *)pt_ta_reset_stats,
+ .to_ta_get_stats = (void *)pt_ta_get_stats,
+ .to_thr_validate = pt_thr_validate,
+ .to_thr_get_info = pt_thr_get_info,
+ .to_thr_getfpregs = (void *)pt_thr_getfpregs,
+ .to_thr_getgregs = pt_thr_getgregs,
+ .to_thr_getxregs = pt_thr_getxregs,
+ .to_thr_getxregsize = pt_thr_getxregsize,
+ .to_thr_setfpregs = pt_thr_setfpregs,
+ .to_thr_setgregs = pt_thr_setgregs,
+ .to_thr_setxregs = pt_thr_setxregs,
+ .to_thr_event_enable = pt_thr_event_enable,
+ .to_thr_set_event = pt_thr_set_event,
+ .to_thr_clear_event = pt_thr_clear_event,
+ .to_thr_event_getmsg = pt_thr_event_getmsg,
+ .to_thr_setprio = pt_thr_setprio,
+ .to_thr_setsigpending = pt_thr_setsigpending,
+ .to_thr_sigsetmask = pt_thr_sigsetmask,
+ .to_thr_tsd = pt_thr_tsd,
+ .to_thr_dbsuspend = pt_thr_dbsuspend,
+ .to_thr_dbresume = pt_thr_dbresume,
+ .to_ta_activated = (void *)pt_ta_activated,
+ .to_thr_sstep = pt_thr_sstep
+};
diff --git a/lib/libthread_db/pthread/pthread_db.h b/lib/libthread_db/pthread/pthread_db.h
new file mode 100644
index 0000000..49dcdd7
--- /dev/null
+++ b/lib/libthread_db/pthread/pthread_db.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PTHREAD_DB_H
+#define _PTHREAD_DB_H
+
+#include <sys/ucontext.h>
+#include <machine/reg.h>
+
+#include "thread_db_int.h"
+
+struct pt_thragent {
+ struct td_thragent base;
+ struct ps_prochandle *ph;
+ psaddr_t libkse_debug_addr;
+ psaddr_t thread_list_addr;
+ psaddr_t thread_listgen_addr;
+ psaddr_t thread_activated_addr;
+ psaddr_t thread_active_threads_addr;
+ psaddr_t thread_keytable_addr;
+ int thread_activated;
+ struct pt_map *map;
+ int map_len;
+};
+
+typedef struct pt_thragent pt_thragent_t;
+
+void pt_md_init(void);
+void pt_reg_to_ucontext(const struct reg *, ucontext_t *);
+void pt_ucontext_to_reg(const ucontext_t *, struct reg *);
+void pt_fpreg_to_ucontext(const struct fpreg *, ucontext_t *);
+void pt_ucontext_to_fpreg(const ucontext_t *, struct fpreg *);
+int pt_reg_sstep(struct reg *reg, int step);
+
+#endif
diff --git a/lib/libthread_db/pthread/pthread_db_i386.c b/lib/libthread_db/pthread/pthread_db_i386.c
new file mode 100644
index 0000000..4875b74
--- /dev/null
+++ b/lib/libthread_db/pthread/pthread_db_i386.c
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <string.h>
+#include <sys/types.h>
+#include <proc_service.h>
+#include <thread_db.h>
+#include <machine/npx.h>
+
+#include "pthread_db.h"
+
+static int has_xmm_regs;
+
+void
+pt_reg_to_ucontext(const struct reg *r, ucontext_t *uc)
+{
+ memcpy(&uc->uc_mcontext.mc_fs, &r->r_fs, 18*4);
+ uc->uc_mcontext.mc_gs = r->r_gs;
+}
+
+void
+pt_ucontext_to_reg(const ucontext_t *uc, struct reg *r)
+{
+ memcpy(&r->r_fs, &uc->uc_mcontext.mc_fs, 18*4);
+ r->r_gs = uc->uc_mcontext.mc_gs;
+}
+
+void
+pt_fpreg_to_ucontext(const struct fpreg* r, ucontext_t *uc)
+{
+ if (!has_xmm_regs)
+ memcpy(&uc->uc_mcontext.mc_fpstate, r,
+ sizeof(struct save87));
+ else {
+ int i;
+ struct savexmm *sx = (struct savexmm *)&uc->uc_mcontext.mc_fpstate;
+ memcpy(&sx->sv_env, &r->fpr_env, sizeof(r->fpr_env));
+ for (i = 0; i < 8; ++i)
+ memcpy(&sx->sv_fp[i].fp_acc, &r->fpr_acc[i], 10);
+ }
+}
+
+void
+pt_ucontext_to_fpreg(const ucontext_t *uc, struct fpreg *r)
+{
+ if (!has_xmm_regs)
+ memcpy(r, &uc->uc_mcontext.mc_fpstate, sizeof(struct save87));
+ else {
+ int i;
+ struct savexmm *sx = (struct savexmm *)&uc->uc_mcontext.mc_fpstate;
+ memcpy(&r->fpr_env, &sx->sv_env, sizeof(r->fpr_env));
+ for (i = 0; i < 8; ++i)
+ memcpy(&r->fpr_acc[i], &sx->sv_fp[i].fp_acc, 10);
+ }
+}
+
+void
+pt_md_init(void)
+{
+ ucontext_t uc;
+
+ getcontext(&uc);
+ if (uc.uc_mcontext.mc_fpformat == _MC_FPFMT_XMM)
+ has_xmm_regs = 1;
+}
+
+int
+pt_reg_sstep(struct reg *reg, int step)
+{
+ unsigned int old;
+
+ old = reg->r_eflags;
+ if (step)
+ reg->r_eflags |= 0x0100;
+ else
+ reg->r_eflags &= ~0x0100;
+ return (old != reg->r_eflags); /* changed ? */
+}
+
diff --git a/lib/libthread_db/src/Makefile b/lib/libthread_db/src/Makefile
new file mode 100644
index 0000000..236e7bc
--- /dev/null
+++ b/lib/libthread_db/src/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+SHLIB_NAME=libthread_db.so.1
+NOPROFILE=yes
+CFLAGS+=-Wall -I${.CURDIR}/../../../include -I${.CURDIR}/../include
+LDADD=../pthread/libpthread_db.a
+
+PRECIOUSLIB=yes
+
+.PATH: ${.CURDIR}
+
+SRCS+=thread_db.c
+INCS=../include/thread_db.h
+
+.include <bsd.lib.mk>
diff --git a/lib/libthread_db/src/thread_db.c b/lib/libthread_db/src/thread_db.c
new file mode 100644
index 0000000..24d5e7c
--- /dev/null
+++ b/lib/libthread_db/src/thread_db.c
@@ -0,0 +1,359 @@
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stddef.h>
+#include <unistd.h>
+
+#include <proc_service.h>
+#include <thread_db.h>
+#include "thread_db_int.h"
+
+static TAILQ_HEAD(, td_thragent) proclist = TAILQ_HEAD_INITIALIZER(proclist);
+
+extern struct ta_ops pthread_ops;
+#if 0
+extern struct ta_ops thr_ops;
+extern struct ta_ops c_r_ops;
+#endif
+
+static struct ta_ops *ops[] = {
+ &pthread_ops,
+#if 0
+ &thr_ops,
+ &c_r_ops
+#endif
+};
+
+td_err_e
+td_init(void)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < sizeof(ops)/sizeof(ops[0]); ++i) {
+ int tmp;
+ if ((tmp = ops[i]->to_init()) != 0)
+ ret = tmp;
+ }
+ return (ret);
+}
+
+td_err_e
+td_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
+{
+ int i;
+
+ for (i = 0; i < sizeof(ops)/sizeof(ops[0]); ++i) {
+ if (ops[i]->to_ta_new(ph, pta) == 0) {
+ (*pta)->ta_ops = ops[i];
+ TAILQ_INSERT_HEAD(&proclist, *pta, ta_next);
+ return (0);
+ }
+ }
+ return (TD_NOLIBTHREAD);
+
+}
+
+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_get_nthreads(const td_thragent_t *ta, int *np)
+{
+ return ta->ta_ops->to_ta_get_nthreads(ta, np);
+}
+
+td_err_e
+td_ta_get_ph(const td_thragent_t *ta, struct ps_prochandle **ph)
+{
+ return ta->ta_ops->to_ta_get_ph(ta, ph);
+}
+
+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_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 *ki, void *arg)
+{
+ return ta->ta_ops->to_ta_tsd_iter(ta, ki, arg);
+}
+
+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_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_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_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_setconcurrency(const td_thragent_t *ta, int level)
+{
+ return ta->ta_ops->to_ta_setconcurrency(ta, level);
+}
+
+td_err_e
+td_ta_enable_stats(const td_thragent_t *ta, int enable)
+{
+ return ta->ta_ops->to_ta_enable_stats(ta, enable);
+}
+
+td_err_e
+td_ta_reset_stats(const td_thragent_t *ta)
+{
+ return ta->ta_ops->to_ta_reset_stats(ta);
+}
+
+td_err_e
+td_ta_get_stats(const td_thragent_t *ta, td_ta_stats_t *statsp)
+{
+ return ta->ta_ops->to_ta_get_stats(ta, statsp);
+}
+
+td_err_e
+td_thr_validate(const td_thrhandle_t *th)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_validate(th);
+}
+
+td_err_e
+td_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_get_info(th, info);
+}
+
+td_err_e
+td_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregset)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_getfpregs(th, fpregset);
+}
+
+td_err_e
+td_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_getgregs(th, gregs);
+}
+
+td_err_e
+td_thr_getxregs(const td_thrhandle_t *th, void *xregs)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_getxregs(th, xregs);
+}
+
+td_err_e
+td_thr_getxregsize(const td_thrhandle_t *th, int *sizep)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_getxregsize(th, sizep);
+}
+
+td_err_e
+td_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_setfpregs(th, fpregs);
+}
+
+td_err_e
+td_thr_setgregs(const td_thrhandle_t *th, prgregset_t gregs)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_setgregs(th, gregs);
+}
+
+td_err_e
+td_thr_setxregs(const td_thrhandle_t *th, const void *addr)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_setxregs(th, addr);
+}
+
+td_err_e
+td_thr_event_enable(const td_thrhandle_t *th, int en)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_event_enable(th, en);
+}
+
+td_err_e
+td_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_set_event(th, setp);
+}
+
+td_err_e
+td_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_clear_event(th, setp);
+}
+
+td_err_e
+td_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_event_getmsg(th, msg);
+}
+
+td_err_e
+td_thr_setprio(const td_thrhandle_t *th, int pri)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_setprio(th, pri);
+}
+
+td_err_e
+td_thr_setsigpending(const td_thrhandle_t *th, unsigned char n,
+ const sigset_t *set)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_setsigpending(th, n, set);
+}
+
+td_err_e
+td_thr_sigsetmask(const td_thrhandle_t *th, const sigset_t *set)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_sigsetmask(th, set);
+}
+
+td_err_e
+td_thr_tsd(const td_thrhandle_t *th, const thread_key_t key, void **data)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_tsd(th, key, data);
+}
+
+td_err_e
+td_thr_dbsuspend(const td_thrhandle_t *th)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_dbsuspend(th);
+}
+
+td_err_e
+td_thr_dbresume(const td_thrhandle_t *th)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_dbresume(th);
+}
+
+td_err_e
+td_get_ta(int pid, td_thragent_t **ta_p)
+{
+ td_thragent_t *ta;
+ struct ps_prochandle *ph;
+
+ TAILQ_FOREACH(ta, &proclist, ta_next) {
+ td_ta_get_ph(ta, &ph);
+ if (ps_getpid(ph) == pid) {
+ *ta_p = ta;
+ return (TD_OK);
+ }
+ }
+ return (TD_ERR);
+}
+
+td_err_e
+td_ta_activated(td_thragent_t *ta, int *a)
+{
+ return ta->ta_ops->to_ta_activated(ta, a);
+}
+
+td_err_e
+td_thr_sstep(td_thrhandle_t *th, int step)
+{
+ td_thragent_t *ta = th->th_ta_p;
+
+ return ta->ta_ops->to_thr_sstep(th, step);
+}
OpenPOWER on IntegriCloud