diff options
| author | thorpej <thorpej@NetBSD.org> | 2003-01-18 10:32:11 +0000 |
|---|---|---|
| committer | thorpej <thorpej@NetBSD.org> | 2003-01-18 10:32:11 +0000 |
| commit | c62a74e6d5cfb2fb5034e98b983da7e2fc87709d (patch) | |
| tree | f1281fd7111ddb57951e4472b342b53a2f56244a /lib/libpthread_dbg | |
| parent | af919042eceb8c19f42acd688efa8cf58168af5d (diff) | |
Merge the nathanw_sa branch.
Diffstat (limited to 'lib/libpthread_dbg')
| -rw-r--r-- | lib/libpthread_dbg/Makefile | 44 | ||||
| -rw-r--r-- | lib/libpthread_dbg/pthread_dbg.c | 895 | ||||
| -rw-r--r-- | lib/libpthread_dbg/pthread_dbg.h | 180 | ||||
| -rw-r--r-- | lib/libpthread_dbg/pthread_dbg_int.h | 33 | ||||
| -rw-r--r-- | lib/libpthread_dbg/shlib_version | 5 |
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 |
