summaryrefslogtreecommitdiff
path: root/lib/libpthread_dbg
diff options
context:
space:
mode:
authorthorpej <thorpej@NetBSD.org>2003-01-18 10:32:11 +0000
committerthorpej <thorpej@NetBSD.org>2003-01-18 10:32:11 +0000
commitc62a74e6d5cfb2fb5034e98b983da7e2fc87709d (patch)
treef1281fd7111ddb57951e4472b342b53a2f56244a /lib/libpthread_dbg
parentaf919042eceb8c19f42acd688efa8cf58168af5d (diff)
Merge the nathanw_sa branch.
Diffstat (limited to 'lib/libpthread_dbg')
-rw-r--r--lib/libpthread_dbg/Makefile44
-rw-r--r--lib/libpthread_dbg/pthread_dbg.c895
-rw-r--r--lib/libpthread_dbg/pthread_dbg.h180
-rw-r--r--lib/libpthread_dbg/pthread_dbg_int.h33
-rw-r--r--lib/libpthread_dbg/shlib_version5
5 files changed, 1157 insertions, 0 deletions
diff --git a/lib/libpthread_dbg/Makefile b/lib/libpthread_dbg/Makefile
new file mode 100644
index 00000000000..2989fc55001
--- /dev/null
+++ b/lib/libpthread_dbg/Makefile
@@ -0,0 +1,44 @@
+# $NetBSD: Makefile,v 1.2 2003/01/18 10:34:23 thorpej Exp $
+#
+
+WARNS= 2
+
+.include <bsd.own.mk>
+
+LIBPDIR= ${.CURDIR}/../libpthread
+
+.if exists(${LIBPDIR}/arch/${MACHINE_ARCH})
+ARCHSUBDIR= ${MACHINE_ARCH}
+.elif exists(${LIBPDIR}/arch/${MACHINE_CPU})
+ARCHSUBDIR= ${MACHINE_CPU}
+.else
+.BEGIN:
+ @echo "no ARCHSUBDIR for ${MACHINE_ARCH}/${MACHINE_CPU}; skipping..."
+.endif
+
+.if defined(ARCHSUBDIR)
+
+LIB= pthread_dbg
+
+ARCHDIR= ${LIBPDIR}/arch/${ARCHSUBDIR}
+.PATH: ${ARCHDIR}
+
+CPPFLAGS+= -I${ARCHDIR} -I${LIBPDIR} -I${.CURDIR}
+
+assym.h: genassym.sh ${ARCHDIR}/genassym.cf
+ sh ${.CURDIR}/genassym.sh ${CC} ${CFLAGS} ${CPPFLAGS} ${PROF} \
+ < ${ARCHDIR}/genassym.cf > assym.h.tmp && \
+ mv -f assym.h.tmp assym.h
+
+SRCS= pthread_dbg.c
+
+INCS= pthread_dbg.h
+INCSDIR=/usr/include
+
+.include <bsd.lib.mk>
+
+.else
+
+.include <bsd.man.mk>
+
+.endif
diff --git a/lib/libpthread_dbg/pthread_dbg.c b/lib/libpthread_dbg/pthread_dbg.c
new file mode 100644
index 00000000000..1352e35a3d4
--- /dev/null
+++ b/lib/libpthread_dbg/pthread_dbg.c
@@ -0,0 +1,895 @@
+/* $NetBSD: pthread_dbg.c,v 1.2 2003/01/18 10:34:23 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2002 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Nathan J. Williams for Wasabi Systems, Inc.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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 <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <pthread.h>
+#include <pthread_int.h>
+#include <pthread_dbg.h>
+#include <pthread_dbg_int.h>
+#include <machine/reg.h>
+
+static int td__getthread(td_proc_t *proc, caddr_t addr, td_thread_t **threadp);
+static int td__getsync(td_proc_t *proc, caddr_t addr, td_sync_t **syncp);
+
+int
+td_open(struct td_proc_callbacks_t *cb, void *arg, td_proc_t **procp)
+{
+ td_proc_t *proc;
+ caddr_t dbgaddr;
+ int dbg;
+ int val;
+
+ proc = malloc(sizeof(*proc));
+ if (proc == NULL)
+ return TD_ERR_NOMEM;
+
+ proc->cb = cb;
+ proc->arg = arg;
+
+ val = LOOKUP(proc, "pthread__dbg", &dbgaddr);
+ if (val != 0) {
+ if (val == TD_ERR_NOSYM)
+ val = TD_ERR_NOLIB;
+ goto error;
+ }
+
+ val = READ(proc, dbgaddr, &dbg, sizeof(int));
+ if (val != 0)
+ goto error;
+
+ if (dbg != 0) {
+ /* Another instance of libpthread_dbg is already attached. */
+ val = TD_ERR_INUSE;
+ goto error;
+ }
+
+ dbg = getpid();
+ /*
+ * If this fails it probably means we're debugging a core file and
+ * can't write to it.
+ * If it's something else we'll lose the next time we hit WRITE,
+ * but not before, and that's OK.
+ */
+ WRITE(proc, dbgaddr, &dbg, sizeof(int));
+
+ proc->allqueue = 0;
+ PTQ_INIT(&proc->threads);
+ PTQ_INIT(&proc->syncs);
+
+ *procp = proc;
+
+ return 0;
+
+ error:
+ free(proc);
+ return val;
+}
+
+int
+td_close(td_proc_t *proc)
+{
+ caddr_t dbgaddr;
+ int dbg;
+ int val;
+ td_thread_t *t, *next;
+ td_sync_t *s, *nexts;
+
+ val = LOOKUP(proc, "pthread__dbg", &dbgaddr);
+ if (val != 0)
+ return val;
+
+ dbg = 0;
+ /*
+ * Error returns from this write are mot really a problem;
+ * the process doesn't exist any more.
+ */
+ WRITE(proc, dbgaddr, &dbg, sizeof(int));
+
+ /* Deallocate the list of thread structures */
+ for (t = PTQ_FIRST(&proc->threads); t; t = next) {
+ next = PTQ_NEXT(t, list);
+ PTQ_REMOVE(&proc->threads, t, list);
+ free(t);
+ }
+ /* Deallocate the list of sync objects */
+ for (s = PTQ_FIRST(&proc->syncs); s; s = nexts) {
+ nexts = PTQ_NEXT(s, list);
+ PTQ_REMOVE(&proc->syncs, s, list);
+ free(s);
+ }
+ free(proc);
+ return 0;
+}
+
+
+int
+td_thr_iter(td_proc_t *proc, int (*call)(td_thread_t *, void *), void *callarg)
+{
+ int val;
+ caddr_t allqaddr, next;
+ struct pthread_queue_t allq;
+ td_thread_t *thread;
+
+ if (proc->allqueue == 0) {
+ val = LOOKUP(proc, "pthread__allqueue", &allqaddr);
+ if (val != 0)
+ return val;
+ proc->allqueue = allqaddr;
+ } else {
+ allqaddr = proc->allqueue;
+ }
+
+ val = READ(proc, allqaddr, &allq, sizeof(allq));
+ if (val != 0)
+ return val;
+
+ next = (caddr_t) allq.ptqh_first;
+ while (next != 0) {
+ val = td__getthread(proc, next, &thread);
+ if (val != 0)
+ return val;
+ val = (*call)(thread, callarg);
+ if (val != 0)
+ return 0;
+
+ val = READ(proc,
+ next + offsetof(struct pthread_st, pt_allq.ptqe_next),
+ &next, sizeof(next));
+ if (val != 0)
+ return val;
+ }
+ return 0;
+}
+
+int
+td_thr_info(td_thread_t *thread, td_thread_info_t *info)
+{
+ int val, tmp;
+ struct pthread_queue_t queue;
+
+ val = READ(thread->proc, thread->addr, &tmp, sizeof(tmp));
+ if (val != 0)
+ return val;
+
+ if (tmp != PT_MAGIC)
+ return TD_ERR_BADTHREAD;
+
+ info->thread_addr = thread->addr;
+ if ((val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_state),
+ &tmp, sizeof(int))) != 0)
+ return val;
+ switch (tmp) {
+ case PT_STATE_RUNNING:
+ info->thread_state = TD_STATE_RUNNING;
+ break;
+ case PT_STATE_RUNNABLE:
+ info->thread_state = TD_STATE_RUNNABLE;
+ break;
+ case PT_STATE_BLOCKED_SYS:
+ info->thread_state = TD_STATE_BLOCKED;
+ break;
+ case PT_STATE_BLOCKED_QUEUE:
+ info->thread_state = TD_STATE_SLEEPING;
+ break;
+ case PT_STATE_ZOMBIE:
+ info->thread_state = TD_STATE_ZOMBIE;
+ break;
+ default:
+ info->thread_state = TD_STATE_UNKNOWN;
+ }
+
+ if ((val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_type),
+ &tmp, sizeof(int))) != 0)
+ return val;
+ switch (tmp) {
+ case PT_THREAD_NORMAL:
+ info->thread_type = TD_TYPE_USER;
+ break;
+ case PT_THREAD_UPCALL:
+ case PT_THREAD_IDLE:
+ info->thread_type = TD_TYPE_SYSTEM;
+ break;
+ default:
+ info->thread_type = TD_TYPE_UNKNOWN;
+ }
+
+ if ((val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_stack),
+ &info->thread_stack, sizeof(stack_t))) != 0)
+ return val;
+
+ if ((val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_joiners),
+ &queue, sizeof(struct pthread_queue_t))) != 0)
+ return val;
+
+ if (PTQ_EMPTY(&queue))
+ info->thread_hasjoiners = 0;
+ else
+ info->thread_hasjoiners = 1;
+
+ if ((val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_errno),
+ &info->thread_errno, sizeof(info->thread_errno))) != 0)
+ return val;
+
+ if ((val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_num),
+ &info->thread_id, sizeof(info->thread_errno))) != 0)
+ return val;
+
+ if ((val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_sigmask),
+ &info->thread_sigmask, sizeof(info->thread_sigmask))) != 0)
+ return val;
+
+ if ((val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_siglist),
+ &info->thread_sigpending, sizeof(info->thread_sigpending))) != 0)
+ return val;
+
+ return 0;
+}
+
+int
+td_thr_getregs(td_thread_t *thread, int regset, void *buf)
+{
+ int tmp, val;
+ caddr_t addr;
+ ucontext_t uc;
+
+ val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_state),
+ &tmp, sizeof(int));
+ if (val != 0)
+ return val;
+
+ switch (tmp) {
+ case PT_STATE_RUNNING:
+ /*
+ * The register state of the thread is live in the
+ * inferior process's register state.
+ */
+ val = GETREGS(thread->proc, regset, thread->lwp, buf);
+ if (val != 0)
+ return val;
+ break;
+ case PT_STATE_RUNNABLE:
+ case PT_STATE_BLOCKED_SYS:
+ case PT_STATE_BLOCKED_QUEUE:
+ /*
+ * The register state of the thread is in the ucontext_t
+ * of the thread structure.
+ */
+ val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_uc),
+ &addr, sizeof(addr));
+ if (val != 0)
+ return val;
+ val = READ(thread->proc,
+ addr, &uc, sizeof(uc));
+ if (val != 0)
+ return val;
+
+ switch (regset) {
+ case 0:
+ PTHREAD_UCONTEXT_TO_REG((struct reg *)buf, &uc);
+ break;
+ case 1:
+ PTHREAD_UCONTEXT_TO_FPREG((struct fpreg *)buf, &uc);
+ break;
+ case 2:
+ return TD_ERR_INVAL;
+ }
+ break;
+ case PT_STATE_ZOMBIE:
+ default:
+ return TD_ERR_BADTHREAD;
+ }
+
+ return 0;
+}
+
+int
+td_thr_setregs(td_thread_t *thread, int regset, void *buf)
+{
+
+ int tmp, val;
+ caddr_t addr;
+ ucontext_t uc;
+
+ val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_state),
+ &tmp, sizeof(int));
+ if (val != 0)
+ return val;
+
+ switch (tmp) {
+ case PT_STATE_RUNNING:
+ /*
+ * The register state of the thread is live in the
+ * inferior process's register state.
+ */
+ val = SETREGS(thread->proc, regset, thread->lwp, buf);
+ if (val != 0)
+ return val;
+ break;
+ case PT_STATE_RUNNABLE:
+ case PT_STATE_BLOCKED_SYS:
+ case PT_STATE_BLOCKED_QUEUE:
+ /*
+ * The register state of the thread is in the ucontext_t
+ * of the thread structure.
+ *
+ * Fetch the uc first, since there is state in it
+ * besides the registers that should be preserved.
+ */
+ val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_uc),
+ &addr, sizeof(addr));
+ if (val != 0)
+ return val;
+ val = READ(thread->proc,
+ addr, &uc, sizeof(uc));
+ if (val != 0)
+ return val;
+
+ switch (regset) {
+ case 0:
+ PTHREAD_REG_TO_UCONTEXT(&uc, (struct reg *)buf);
+ break;
+ case 1:
+ PTHREAD_FPREG_TO_UCONTEXT(&uc, (struct fpreg *)buf);
+ break;
+ case 2:
+ return TD_ERR_INVAL;
+ }
+
+ val = WRITE(thread->proc,
+ addr, &uc, sizeof(uc));
+ if (val != 0)
+ return val;
+
+ break;
+ case PT_STATE_ZOMBIE:
+ default:
+ return TD_ERR_BADTHREAD;
+ }
+
+ return 0;
+}
+
+int
+td_thr_join_iter(td_thread_t *thread, int (*call)(td_thread_t *, void *),
+ void *arg)
+{
+ int val;
+ caddr_t next;
+ td_thread_t *thread2;
+ struct pthread_queue_t queue;
+
+ if ((val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_joiners),
+ &queue, sizeof(struct pthread_queue_t))) != 0)
+ return val;
+
+ next = (caddr_t) queue.ptqh_first;
+ while (next != 0) {
+ val = td__getthread(thread->proc, next, &thread2);
+ if (val != 0)
+ return val;
+ val = (*call)(thread, arg);
+ if (val != 0)
+ return 0;
+
+ val = READ(thread->proc,
+ next + offsetof(struct pthread_st, pt_sleep.ptqe_next),
+ &next, sizeof(next));
+ if (val != 0)
+ return val;
+ }
+
+ return 0;
+}
+
+int
+td_sync_info(td_sync_t *s, td_sync_info_t *info)
+{
+ int val, magic, n;
+ struct pthread_queue_t queue;
+ pthread_spin_t slock;
+ pthread_t taddr;
+
+ val = READ(s->proc, s->addr, &magic, sizeof(magic));
+ if (val != 0)
+ return val;
+
+ info->sync_type = TD_SYNC_UNKNOWN;
+ info->sync_size = 0;
+ info->sync_haswaiters = 0;
+ switch (magic) {
+ case _PT_MUTEX_MAGIC:
+ info->sync_type = TD_SYNC_MUTEX;
+ info->sync_size = sizeof(struct pthread_mutex_st);
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_mutex_st, ptm_blocked),
+ &queue, sizeof(struct pthread_queue_t))) != 0)
+ return val;
+
+ if (!PTQ_EMPTY(&queue))
+ info->sync_haswaiters = 1;
+ /*
+ * The cast to (void *) is to explicitly throw away the
+ * volatile qualifier on pthread_spin_t,
+ * from __cpu_simple_lock_t.
+ */
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_mutex_st, ptm_lock),
+ (void *)&slock, sizeof(struct pthread_spinlock_st))) != 0)
+ return val;
+ if (slock == __SIMPLELOCK_LOCKED) {
+ info->sync_data.mutex.locked = 1;
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_mutex_st,
+ ptm_owner),
+ &taddr, sizeof(pthread_t))) != 0)
+ return val;
+ taddr = pthread__id(taddr);
+ td__getthread(s->proc, (caddr_t)taddr,
+ &info->sync_data.mutex.owner);
+ } else
+ info->sync_data.mutex.locked = 0;
+ break;
+ case _PT_COND_MAGIC:
+ info->sync_type = TD_SYNC_COND;
+ info->sync_size = sizeof(struct pthread_cond_st);
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_cond_st, ptc_waiters),
+ &queue, sizeof(struct pthread_queue_t))) != 0)
+ return val;
+ if (!PTQ_EMPTY(&queue))
+ info->sync_haswaiters = 1;
+ break;
+ case _PT_SPINLOCK_MAGIC:
+ info->sync_type = TD_SYNC_SPIN;
+ info->sync_size = sizeof(struct pthread_spinlock_st);
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_spinlock_st, pts_spin),
+ (void *)&slock, sizeof(struct pthread_spinlock_st))) != 0)
+ return val;
+ if (slock == __SIMPLELOCK_LOCKED)
+ info->sync_data.spin.locked = 1;
+ break;
+ case PT_MAGIC:
+ info->sync_type = TD_SYNC_JOIN;
+ info->sync_size = sizeof(struct pthread_st);
+ td__getthread(s->proc, s->addr,
+ &info->sync_data.join.thread);
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_st, pt_joiners),
+ &queue, sizeof(struct pthread_queue_t))) != 0)
+ return val;
+
+ if (!PTQ_EMPTY(&queue))
+ info->sync_haswaiters = 1;
+ break;
+ case _PT_RWLOCK_MAGIC:
+ info->sync_type = TD_SYNC_RWLOCK;
+ info->sync_size = sizeof(struct pthread_rwlock_st);
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_rwlock_st, ptr_rblocked),
+ &queue, sizeof(struct pthread_queue_t))) != 0)
+ return val;
+ if (!PTQ_EMPTY(&queue))
+ info->sync_haswaiters = 1;
+
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_rwlock_st, ptr_wblocked),
+ &queue, sizeof(struct pthread_queue_t))) != 0)
+ return val;
+ if (!PTQ_EMPTY(&queue))
+ info->sync_haswaiters = 1;
+
+
+ info->sync_data.rwlock.locked = 0;
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_rwlock_st, ptr_nreaders),
+ &n, sizeof(int))) != 0)
+ return val;
+ info->sync_data.rwlock.readlocks = n;
+ if (n > 0)
+ info->sync_data.rwlock.locked = 1;
+
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_rwlock_st, ptr_writer),
+ &taddr, sizeof(pthread_t))) != 0)
+ return val;
+ if (taddr != 0) {
+ info->sync_data.rwlock.locked = 1;
+ td__getthread(s->proc, (caddr_t)taddr,
+ &info->sync_data.rwlock.writeowner);
+ }
+ default:
+ return (0);
+ }
+
+ info->sync_addr = s->addr;
+
+ return 0;
+}
+
+
+int
+td_sync_waiters_iter(td_sync_t *s, int (*call)(td_thread_t *, void *),
+ void *arg)
+{
+ int val, magic;
+ caddr_t next;
+ struct pthread_queue_t queue;
+ td_thread_t *thread;
+
+ val = READ(s->proc, s->addr, &magic, sizeof(magic));
+ if (val != 0)
+ return val;
+
+ switch (magic) {
+ case _PT_MUTEX_MAGIC:
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_mutex_st, ptm_blocked),
+ &queue, sizeof(struct pthread_queue_t))) != 0)
+ return val;
+ break;
+ case _PT_COND_MAGIC:
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_cond_st, ptc_waiters),
+ &queue, sizeof(struct pthread_queue_t))) != 0)
+ return val;
+ break;
+ case PT_MAGIC:
+ /* Redundant with join_iter, but what the hell... */
+ if ((val = READ(s->proc,
+ s->addr + offsetof(struct pthread_st, pt_joiners),
+ &queue, sizeof(struct pthread_queue_t))) != 0)
+ return val;
+ break;
+ default:
+ return (0);
+ }
+
+ next = (caddr_t) queue.ptqh_first;
+ while (next != 0) {
+ val = td__getthread(s->proc, next, &thread);
+ if (val != 0)
+ return val;
+ val = (*call)(thread, arg);
+ if (val != 0)
+ return 0;
+
+ val = READ(s->proc,
+ next + offsetof(struct pthread_st, pt_sleep.ptqe_next),
+ &next, sizeof(next));
+ if (val != 0)
+ return val;
+ }
+ return 0;
+}
+
+
+int
+td_map_addr2sync(td_proc_t *proc, caddr_t addr, td_sync_t **syncp)
+{
+ int magic, val;
+
+ val = READ(proc, addr, &magic, sizeof(magic));
+ if (val != 0)
+ return val;
+
+ if ((magic != _PT_MUTEX_MAGIC) &&
+ (magic != _PT_COND_MAGIC) &&
+ (magic != _PT_SPINLOCK_MAGIC))
+ return TD_ERR_NOOBJ;
+
+ val = td__getsync(proc, addr, syncp);
+ if (val != 0)
+ return val;
+
+ return 0;
+}
+
+
+int
+td_map_pth2thr(td_proc_t *proc, pthread_t thread, td_thread_t **threadp)
+{
+ int magic, val;
+
+ val = READ(proc, (caddr_t)thread, &magic, sizeof(magic));
+ if (val != 0)
+ return val;
+
+ if (magic != PT_MAGIC)
+ return TD_ERR_NOOBJ;
+
+ val = td__getthread(proc, (caddr_t)thread, threadp);
+ if (val != 0)
+ return val;
+
+ return 0;
+}
+
+int
+td_map_id2thr(td_proc_t *proc, int threadid, td_thread_t **threadp)
+{
+ int val, num;
+ caddr_t allqaddr, next;
+ struct pthread_queue_t allq;
+ td_thread_t *thread;
+
+
+ if (proc->allqueue == 0) {
+ val = LOOKUP(proc, "pthread__allqueue", &allqaddr);
+ if (val != 0)
+ return val;
+ proc->allqueue = allqaddr;
+ } else {
+ allqaddr = proc->allqueue;
+ }
+
+ val = READ(proc, allqaddr, &allq, sizeof(allq));
+ if (val != 0)
+ return val;
+
+ next = (caddr_t) allq.ptqh_first;
+ while (next != 0) {
+ val = READ(proc,
+ next + offsetof(struct pthread_st, pt_num),
+ &num, sizeof(num));
+
+ if (num == threadid)
+ break;
+
+ val = READ(proc,
+ next + offsetof(struct pthread_st, pt_allq.ptqe_next),
+ &next, sizeof(next));
+ if (val != 0)
+ return val;
+ }
+
+ if (next == 0) {
+ /* A matching thread was not found. */
+ return TD_ERR_NOOBJ;
+ }
+
+ val = td__getthread(proc, next, &thread);
+ if (val != 0)
+ return val;
+ *threadp = thread;
+
+ return 0;
+}
+
+/* Return the thread handle of the thread running on the given LWP */
+int
+td_map_lwp2thr(td_proc_t *proc, int lwp, td_thread_t **threadp)
+{
+ int val, magic;
+ struct reg gregs;
+ ucontext_t uc;
+ caddr_t th;
+
+ val = GETREGS(proc, 0, lwp, &gregs);
+ if (val != 0)
+ return val;
+
+ PTHREAD_REG_TO_UCONTEXT(&uc, &gregs);
+
+ th = (caddr_t) pthread__id(pthread__uc_sp(&uc));
+
+ val = READ(proc, th, &magic, sizeof(magic));
+ if (val != 0)
+ return val;
+
+ if (magic != PT_MAGIC)
+ return TD_ERR_NOOBJ;
+
+ val = td__getthread(proc, th, threadp);
+ if (val != 0)
+ return val;
+
+ (*threadp)->lwp = lwp;
+
+ return 0;
+}
+
+int
+td_map_lwps(td_proc_t *proc)
+{
+ int i, val, nlwps;
+ caddr_t addr;
+ td_thread_t *thread;
+
+ val = LOOKUP(proc, "pthread__maxlwps", &addr);
+ if (val != 0)
+ return val;
+
+ val = READ(proc, addr, &nlwps, sizeof(int));
+ if (val != 0)
+ return val;
+
+ for (i = 1; i <= nlwps; i++) {
+ /*
+ * Errors are deliberately ignored for the call to
+ * td_map_lwp2thr(); it is entirely likely that not
+ * all LWPs in the range 1..nlwps exist, and that's
+ * not a problem.
+ */
+ td_map_lwp2thr(proc, i, &thread);
+ }
+ return 0;
+}
+
+int
+td_tsd_iter(td_proc_t *proc,
+ int (*call)(pthread_key_t, void (*)(void *), void *), void *arg)
+{
+ caddr_t desaddr, allocaddr;
+ int val;
+ int i, allocated;
+ void (*destructor)(void *);
+
+ val = LOOKUP(proc, "pthread__tsd_alloc", &allocaddr);
+ if (val != 0)
+ return val;
+ val = LOOKUP(proc, "pthread__tsd_destructors", &desaddr);
+ if (val != 0)
+ return val;
+
+ for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
+ val = READ(proc, allocaddr + i * sizeof(int),
+ &allocated, sizeof(int));
+ if (val != 0)
+ return val;
+
+ if (allocated) {
+ val = READ(proc, desaddr + i * sizeof(destructor),
+ &destructor, sizeof(destructor));
+ if (val != 0)
+ return val;
+
+ val = (call)(i, destructor, arg);
+ if (val != 0)
+ return val;
+ }
+ }
+
+ return 0;
+}
+
+/* Get the synchronization object that the thread is sleeping on */
+int
+td_thr_sleepinfo(td_thread_t *thread, td_sync_t **s)
+{
+ int val;
+ caddr_t addr;
+
+ if ((val = READ(thread->proc,
+ thread->addr + offsetof(struct pthread_st, pt_sleepobj),
+ &addr, sizeof(caddr_t))) != 0)
+ return val;
+
+ td__getsync(thread->proc, addr, s);
+
+ return 0;
+
+}
+
+
+
+static int
+td__getthread(td_proc_t *proc, caddr_t addr, td_thread_t **threadp)
+{
+ td_thread_t *thread;
+
+ /*
+ * Check if we've allocated a descriptor for this thread.
+ * Sadly, this makes iterating over a set of threads O(N^2)
+ * in the number of threads. More sophisticated data structures
+ * can wait.
+ */
+ PTQ_FOREACH(thread, &proc->threads, list) {
+ if (thread->addr == addr)
+ break;
+ }
+ if (thread == NULL) {
+ thread = malloc(sizeof(*thread));
+ if (thread == NULL)
+ return TD_ERR_NOMEM;
+ thread->proc = proc;
+ thread->addr = addr;
+ thread->lwp = 0;
+ PTQ_INSERT_HEAD(&proc->threads, thread, list);
+ }
+
+ *threadp = thread;
+ return 0;
+}
+
+
+static int
+td__getsync(td_proc_t *proc, caddr_t addr, td_sync_t **syncp)
+{
+ td_sync_t *s;
+
+ /* Check if we've allocated a descriptor for this object. */
+ PTQ_FOREACH(s, &proc->syncs, list) {
+ if (s->addr == addr)
+ break;
+ }
+ /* Allocate a fresh one */
+ if (s == NULL) {
+ s = malloc(sizeof(*s));
+ if (s == NULL)
+ return TD_ERR_NOMEM;
+ s->proc = proc;
+ s->addr = addr;
+ PTQ_INSERT_HEAD(&proc->syncs, s, list);
+ }
+
+ *syncp = s;
+ return 0;
+}
+
+
+int
+td_thr_tsd(td_thread_t *thread, pthread_key_t key, void **value)
+{
+ int val;
+
+ val = READ(thread->proc, thread->addr +
+ offsetof(struct pthread_st, pt_specific) +
+ key * sizeof(void *), &value, sizeof(void *));
+
+ return val;
+}
+
diff --git a/lib/libpthread_dbg/pthread_dbg.h b/lib/libpthread_dbg/pthread_dbg.h
new file mode 100644
index 00000000000..2b920581a60
--- /dev/null
+++ b/lib/libpthread_dbg/pthread_dbg.h
@@ -0,0 +1,180 @@
+/* $NetBSD: pthread_dbg.h,v 1.2 2003/01/18 10:34:23 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2002 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Nathan J. Williams for Wasabi Systems, Inc.
+ *
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the NetBSD Project by
+ * Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ * or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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/types.h>
+#include <signal.h>
+
+struct td_proc_st;
+struct td_thread_st;
+struct td_sync_st;
+
+typedef struct td_proc_st td_proc_t;
+typedef struct td_thread_st td_thread_t;
+typedef struct td_sync_st td_sync_t;
+
+struct td_proc_callbacks_t {
+ int (*proc_read)(void *arg, caddr_t addr, void *buf, size_t size);
+ int (*proc_write)(void *arg, caddr_t addr, void *buf, size_t size);
+ int (*proc_lookup)(void *arg, char *sym, caddr_t *addr);
+ int (*proc_regsize)(void *arg, int regset, size_t *size);
+ int (*proc_getregs)(void *arg, int regset, int lwp, void *buf);
+ int (*proc_setregs)(void *arg, int regset, int lwp, void *buf);
+};
+
+
+typedef struct td_thread_info_st {
+ caddr_t thread_addr; /* Address of data structure */
+ int thread_state; /* TD_STATE_*; see below */
+ int thread_type; /* TD_TYPE_*; see below */
+ int thread_id;
+ stack_t thread_stack;
+ int thread_hasjoiners; /* 1 if threads are waiting */
+ caddr_t thread_tls;
+ int thread_errno;
+ sigset_t thread_sigmask;
+ sigset_t thread_sigpending;
+ long pad[32];
+} td_thread_info_t;
+
+#define TD_STATE_UNKNOWN 0
+#define TD_STATE_RUNNING 1 /* On a processor */
+#define TD_STATE_RUNNABLE 2 /* On a run queue */
+#define TD_STATE_BLOCKED 3 /* Blocked in the kernel */
+#define TD_STATE_SLEEPING 4 /* Blocked on a sync object */
+#define TD_STATE_ZOMBIE 5
+
+#define TD_TYPE_UNKNOWN 0
+#define TD_TYPE_USER 1
+#define TD_TYPE_SYSTEM 2
+
+typedef struct {
+ caddr_t sync_addr; /* Address within the process */
+ int sync_type; /* TD_SYNC_*; see below */
+ size_t sync_size;
+ int sync_haswaiters; /* 1 if threads are known to be waiting */
+ union {
+ struct {
+ int locked;
+ td_thread_t *owner;
+ } mutex;
+ struct {
+ int locked;
+ } spin;
+ struct {
+ td_thread_t *thread;
+ } join;
+ struct {
+ int locked;
+ int readlocks;
+ td_thread_t *writeowner;
+ } rwlock;
+ long pad[8];
+ } sync_data;
+} td_sync_info_t;
+
+#define TD_SYNC_UNKNOWN 0
+#define TD_SYNC_MUTEX 1 /* pthread_mutex_t */
+#define TD_SYNC_COND 2 /* pthread_cond_t */
+#define TD_SYNC_SPIN 3 /* pthread_spinlock_t */
+#define TD_SYNC_JOIN 4 /* thread being joined */
+#define TD_SYNC_RWLOCK 5 /* pthread_rwlock_t */
+
+/* Error return codes */
+#define TD_ERR_OK 0
+#define TD_ERR_ERR 1 /* Generic error */
+#define TD_ERR_NOSYM 2 /* Symbol not found (proc_lookup) */
+#define TD_ERR_NOOBJ 3 /* No object matched the request */
+#define TD_ERR_BADTHREAD 4 /* Request is not meaningful for that thread */
+#define TD_ERR_INUSE 5 /* The process is already being debugged */
+#define TD_ERR_NOLIB 6 /* The process is not using libpthread */
+#define TD_ERR_NOMEM 7 /* malloc() failed */
+#define TD_ERR_IO 8 /* A callback failed to read or write */
+#define TD_ERR_INVAL 9 /* Invalid parameter */
+
+/* Make a connection to a threaded process */
+int td_open(struct td_proc_callbacks_t *, void *arg, td_proc_t **);
+int td_close(td_proc_t *);
+
+/* Iterate over the threads in the process */
+int td_thr_iter(td_proc_t *, int (*)(td_thread_t *, void *), void *);
+
+/* Get information on a thread */
+int td_thr_info(td_thread_t *, td_thread_info_t *);
+
+/* Get register state of a thread */
+int td_thr_getregs(td_thread_t *, int, void *);
+
+/* Set register state of a thread */
+int td_thr_setregs(td_thread_t *, int, void *);
+
+/* Iterate over the set of threads that are joining with the given thread */
+int td_thr_join_iter(td_thread_t *, int (*)(td_thread_t *, void *), void *);
+
+/* Get the synchronization object that the thread is sleeping on */
+int td_thr_sleepinfo(td_thread_t *, td_sync_t **);
+
+/* Get information on a synchronization object */
+int td_sync_info(td_sync_t *, td_sync_info_t *);
+
+/* Iterate over the set of threads waiting on a synchronization object */
+int td_sync_waiters_iter(td_sync_t *, int (*)(td_thread_t *, void *), void *);
+
+/* Convert the process address to a synchronization handle, if possible */
+int td_map_addr2sync(td_proc_t *, caddr_t addr, td_sync_t **);
+
+/* Convert the pthread_t to a thread handle, if possible */
+int td_map_pth2thr(td_proc_t *, pthread_t, td_thread_t **);
+
+/* Convert the thread ID to a thread handle, if possible */
+int td_map_id2thr(td_proc_t *, int, td_thread_t **);
+
+/* Return the thread handle of the thread running on the given LWP */
+int td_map_lwp2thr(td_proc_t *, int, td_thread_t **);
+
+/*
+ * Establish a mapping between threads and LWPs. Must be called
+ * every time a live process runs before calling td_thr_getregs() or
+ * td_thr_setregs().
+ */
+int td_map_lwps(td_proc_t *);
+
+/* Iterate over the set of TSD keys in the process */
+int td_tsd_iter(td_proc_t *, int (*)(pthread_key_t, void (*)(void *), void *),
+ void *);
+
+/* Get a TSD value from a thread */
+int td_thr_tsd(td_thread_t *, pthread_key_t, void **);
diff --git a/lib/libpthread_dbg/pthread_dbg_int.h b/lib/libpthread_dbg/pthread_dbg_int.h
new file mode 100644
index 00000000000..0279e88b77e
--- /dev/null
+++ b/lib/libpthread_dbg/pthread_dbg_int.h
@@ -0,0 +1,33 @@
+
+PTQ_HEAD(thread_queue_t, td_thread_st);
+PTQ_HEAD(sync_queue_t, td_sync_st);
+
+struct td_proc_st {
+ struct td_proc_callbacks_t *cb;
+ void *arg;
+
+ caddr_t allqueue;
+ struct thread_queue_t threads;
+ struct sync_queue_t syncs;
+};
+
+
+struct td_thread_st {
+ td_proc_t *proc;
+ caddr_t addr;
+ lwpid_t lwp;
+ PTQ_ENTRY(td_thread_st) list;
+};
+
+
+struct td_sync_st {
+ td_proc_t *proc;
+ caddr_t addr;
+ PTQ_ENTRY(td_sync_st) list;
+};
+
+#define READ(proc, addr, buf, size) ((proc)->cb->proc_read((proc)->arg, (addr), (buf), (size)))
+#define WRITE(proc, addr, buf, size) ((proc)->cb->proc_write((proc)->arg, (addr), (buf), (size)))
+#define LOOKUP(proc, sym, addr) ((proc)->cb->proc_lookup((proc)->arg, (sym), (addr)))
+#define GETREGS(proc, regset, lwp, buf) ((proc)->cb->proc_getregs((proc)->arg, (regset), (lwp), (buf)))
+#define SETREGS(proc, regset, lwp, buf) ((proc)->cb->proc_setregs((proc)->arg, (regset), (lwp), (buf)))
diff --git a/lib/libpthread_dbg/shlib_version b/lib/libpthread_dbg/shlib_version
new file mode 100644
index 00000000000..95cebc03ef9
--- /dev/null
+++ b/lib/libpthread_dbg/shlib_version
@@ -0,0 +1,5 @@
+# $NetBSD: shlib_version,v 1.2 2003/01/18 10:34:23 thorpej Exp $
+# Remember to update distrib/sets/lists/base/shl.* when changing
+#
+major=0
+minor=0