summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authordavidxu <davidxu@FreeBSD.org>2004-02-04 05:01:15 +0000
committerdavidxu <davidxu@FreeBSD.org>2004-02-04 05:01:15 +0000
commit58d87c5c98d9ad8390efa179ce5713e9b63ec640 (patch)
tree343df7daa011616b70a0268f75439e1a0159ee9c /lib
parent953792046bc4f1e47a60e299db88452c5c7d1ed0 (diff)
downloadFreeBSD-src-58d87c5c98d9ad8390efa179ce5713e9b63ec640.zip
FreeBSD-src-58d87c5c98d9ad8390efa179ce5713e9b63ec640.tar.gz
Import initial work of libpthread debugging. This is a debugger independent
friend library for libpthread, the library will be used by debugger to read/write libpthread's internal data structures.
Diffstat (limited to 'lib')
-rw-r--r--lib/libpthread_dbg/Makefile20
-rw-r--r--lib/libpthread_dbg/arch/i386/Makefile.inc5
-rw-r--r--lib/libpthread_dbg/arch/i386/i386/pthread_dbg_md.c106
-rw-r--r--lib/libpthread_dbg/pthread_dbg.c562
-rw-r--r--lib/libpthread_dbg/pthread_dbg.h139
-rw-r--r--lib/libpthread_dbg/pthread_dbg_int.h68
6 files changed, 900 insertions, 0 deletions
diff --git a/lib/libpthread_dbg/Makefile b/lib/libpthread_dbg/Makefile
new file mode 100644
index 0000000..6114eb9
--- /dev/null
+++ b/lib/libpthread_dbg/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+LIB=pthread_dbg
+SHLIB_MAJOR=1
+CFLAGS+=-DPTHREAD_KERNEL
+CFLAGS+=-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+=-I${.CURDIR} -I${.CURDIR}/arch/${MACHINE_ARCH}/include
+CFLAGS+=-Wall
+
+PRECIOUSLIB=yes
+
+.PATH: ${.CURDIR}
+
+SRCS+=pthread_dbg.c
+
+.include "${.CURDIR}/arch/${MACHINE_ARCH}/Makefile.inc"
+.include <bsd.lib.mk>
diff --git a/lib/libpthread_dbg/arch/i386/Makefile.inc b/lib/libpthread_dbg/arch/i386/Makefile.inc
new file mode 100644
index 0000000..794a4b0
--- /dev/null
+++ b/lib/libpthread_dbg/arch/i386/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/${MACHINE_ARCH}
+
+SRCS+= pthread_dbg_md.c
diff --git a/lib/libpthread_dbg/arch/i386/i386/pthread_dbg_md.c b/lib/libpthread_dbg/arch/i386/i386/pthread_dbg_md.c
new file mode 100644
index 0000000..593a784
--- /dev/null
+++ b/lib/libpthread_dbg/arch/i386/i386/pthread_dbg_md.c
@@ -0,0 +1,106 @@
+/*
+ * 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 <machine/npx.h>
+#include <machine/reg.h>
+
+#include "pthread_dbg.h"
+#include "pthread_dbg_int.h"
+#include "pthread_dbg_md.h"
+
+static int has_xmm_regs;
+
+void
+td_reg_to_ucontext(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
+td_ucontext_to_reg(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
+td_fpreg_to_ucontext(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
+td_ucontext_to_fpreg(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
+td_md_init(void)
+{
+ ucontext_t uc;
+
+ getcontext(&uc);
+ if (uc.uc_mcontext.mc_fpformat == _MC_FPFMT_XMM)
+ has_xmm_regs = 1;
+}
+
+int
+td_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/libpthread_dbg/pthread_dbg.c b/lib/libpthread_dbg/pthread_dbg.c
new file mode 100644
index 0000000..426a381
--- /dev/null
+++ b/lib/libpthread_dbg/pthread_dbg.c
@@ -0,0 +1,562 @@
+/*-
+ * 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 "pthread_dbg_md.h"
+#include "pthread_dbg.h"
+#include "pthread_dbg_int.h"
+
+static void td_empty_thread_list(td_proc_t *proc);
+static int td_refresh_thread_list(td_proc_t *proc);
+static int td_get_thread(td_proc_t *proc, caddr_t addr, int type,
+ td_thread_t **threadp);
+static void td_remove_map(td_proc_t *proc, int type);
+
+int
+td_open(td_proc_callbacks_t *cb, void *arg, td_proc_t **procp)
+{
+#define LOOKUP_SYM(proc, sym, addr) \
+ ret = LOOKUP(proc, sym, addr); \
+ if (ret != 0) { \
+ if (ret == TD_ERR_NOSYM) \
+ ret = TD_ERR_NOLIB; \
+ goto error; \
+ }
+
+ td_proc_t *proc;
+ int dbg;
+ int ret;
+
+ td_md_init();
+ proc = malloc(sizeof(*proc));
+ if (proc == NULL)
+ return (TD_ERR_NOMEM);
+
+ proc->cb = cb;
+ proc->arg = arg;
+ proc->thread_activated = 0;
+ proc->thread_listgen = -1;
+ TAILQ_INIT(&proc->threads);
+
+ LOOKUP_SYM(proc, "_libkse_debug", &proc->libkse_debug_addr);
+ LOOKUP_SYM(proc, "_thread_list", &proc->thread_list_addr);
+ LOOKUP_SYM(proc, "_thread_listgen", &proc->thread_listgen_addr);
+ LOOKUP_SYM(proc, "_thread_activated", &proc->thread_activated_addr);
+/* LOOKUP_SYM(proc, "_thread_active_kseq", &proc->thread_active_kseq_addr);*/
+ 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, proc->libkse_debug_addr, &dbg, sizeof(int));
+
+ *procp = proc;
+ return (0);
+
+error:
+ free(proc);
+ return (ret);
+}
+
+int
+td_close(td_proc_t *proc)
+{
+ int dbg;
+
+ td_empty_thread_list(proc);
+
+ dbg = 0;
+ /*
+ * Error returns from this write are not really a problem;
+ * the process doesn't exist any more.
+ */
+ WRITE(proc, proc->libkse_debug_addr, &dbg, sizeof(int));
+
+ free(proc);
+ return 0;
+}
+
+int
+td_thr_iter(td_proc_t *proc, int (*call)(td_thread_t *, void *),
+ void *callarg)
+{
+ int ret;
+ td_thread_t *thread;
+
+ td_refresh_thread_list(proc);
+
+ TAILQ_FOREACH(thread, &proc->threads, tle) {
+ ret = (*call)(thread, callarg);
+ if (ret != 0)
+ return (ret);
+ }
+ return (0);
+}
+
+int
+td_thr_info(td_thread_t *thread, td_thread_info_t *info)
+{
+ int ret = 0;
+ struct pthread pt;
+
+ memset(info, 0, sizeof(*info));
+ if (thread->type == TD_TYPE_UPCALL) {
+ info->thread_id = (long)thread->kthread;
+ info->thread_state = TD_STATE_RUNNING;
+ info->thread_type = TD_TYPE_UPCALL;
+ return (ret);
+ }
+
+ /* Handle normal thread */
+ ret = READ(thread->proc, thread->addr, &pt, sizeof(pt));
+ if (ret != 0)
+ return (ret);
+
+ if (pt.magic != THR_MAGIC)
+ return (TD_ERR_BADTHREAD);
+
+ info->thread_id = (long)thread->addr;
+ info->thread_addr = thread->addr;
+ info->thread_type = TD_TYPE_NORMAL;
+ switch (pt.state) {
+ case PS_RUNNING:
+ info->thread_state = TD_STATE_RUNNING;
+ break;
+ case PS_LOCKWAIT:
+ info->thread_state = TD_STATE_LOCKWAIT;
+ break;
+ case PS_MUTEX_WAIT:
+ info->thread_state = TD_STATE_MUTEXWAIT;
+ break;
+ case PS_COND_WAIT:
+ info->thread_state = TD_STATE_SLEEPING;
+ break;
+ case PS_SIGSUSPEND:
+ info->thread_state = TD_STATE_SIGSUSPEND;
+ break;
+ case PS_SIGWAIT:
+ info->thread_state = TD_STATE_SIGWAIT;
+ break;
+ case PS_JOIN:
+ info->thread_state = TD_STATE_JOIN;
+ break;
+ case PS_SUSPENDED:
+ info->thread_state = TD_STATE_SUSPENDED;
+ break;
+ case PS_DEAD:
+ info->thread_state = TD_STATE_DEAD;
+ break;
+ case PS_DEADLOCK:
+ info->thread_state = TD_STATE_DEADLOCK;
+ break;
+ default:
+ info->thread_state = TD_STATE_UNKNOWN;
+ break;
+ }
+
+ info->thread_scope = (pt.attr.flags & PTHREAD_SCOPE_SYSTEM) ?
+ TD_SCOPE_SYSTEM : TD_SCOPE_PROCESS;
+ info->thread_stack.ss_sp = pt.attr.stackaddr_attr;
+ info->thread_stack.ss_size = pt.attr.stacksize_attr;
+ info->thread_joiner = (caddr_t)pt.joiner;
+ info->thread_errno = pt.error;
+ info->thread_sigmask = pt.sigmask;
+ info->thread_sigpend = pt.sigpend;
+ info->thread_sigstk = pt.sigstk;
+ info->thread_base_priority = pt.base_priority;
+ info->thread_inherited_priority = pt.inherited_priority;
+ info->thread_active_priority = pt.active_priority;
+ info->thread_cancelflags = pt.cancelflags;
+ info->thread_retval = pt.join_status.ret;
+ info->thread_tls = (caddr_t)pt.specific;
+ info->thread_tlscount = pt.specific_data_count;
+ return (0);
+}
+
+int
+td_thr_getregs(td_thread_t *thread, int regset, void *buf)
+{
+ td_proc_t *proc = thread->proc;
+ caddr_t tmbx_addr, ptr;
+ struct kse_thr_mailbox tmbx;
+ int ret;
+
+ if (regset != 0 && regset != 1)
+ return (TD_ERR_INVAL);
+
+ if (thread->kthread != NULL)
+ return GETREGS(proc, regset, thread->kthread, buf);
+
+ tmbx_addr = thread->tcb_addr + offsetof(struct tcb, tcb_tmbx);
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_kthread);
+ ret = READ(proc, ptr, &ptr, sizeof(ptr));
+ if (ret != 0)
+ return (ret);
+ if (ptr != NULL)
+ return GETREGS(proc, regset, ptr, buf);
+ ret = READ(proc, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (ret);
+ if (regset == 0)
+ td_ucontext_to_reg(&tmbx.tm_context, (struct reg *)buf);
+ else
+ td_ucontext_to_fpreg(&tmbx.tm_context, (struct fpreg *)buf);
+ return (0);
+}
+
+int
+td_thr_setregs(td_thread_t *thread, int regset, void *buf)
+{
+ td_proc_t *proc = thread->proc;
+ caddr_t tmbx_addr, ptr;
+ struct kse_thr_mailbox tmbx;
+ int ret;
+
+ if (regset != 0 && regset != 1)
+ return (TD_ERR_INVAL);
+
+ if (thread->kthread != NULL)
+ return SETREGS(proc, regset, thread->kthread, buf);
+
+ tmbx_addr = thread->tcb_addr + offsetof(struct tcb, tcb_tmbx);
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_kthread);
+ ret = READ(proc, ptr, &ptr, sizeof(ptr));
+ if (ret != 0)
+ return (ret);
+ if (ptr != NULL)
+ return SETREGS(proc, regset, ptr, buf);
+
+ /*
+ * Read a copy of context, this makes sure that registers
+ * not covered by structure reg won't be clobbered
+ */
+ ret = READ(proc, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (ret);
+ if (regset == 0)
+ td_reg_to_ucontext((struct reg *)buf, &tmbx.tm_context);
+ else
+ td_fpreg_to_ucontext((struct fpreg *)buf, &tmbx.tm_context);
+ return (WRITE(proc, tmbx_addr, &tmbx, sizeof(tmbx)));
+}
+
+int
+td_thr_getname(td_thread_t *thread, char *name, int len)
+{
+ int ret;
+ caddr_t nameaddr;
+
+ if ((ret = READ(thread->proc,
+ thread->addr + offsetof(struct pthread, name),
+ &nameaddr, sizeof(nameaddr))) != 0)
+ return (ret);
+
+ if (nameaddr == 0)
+ name[0] = '\0';
+ else if ((ret = READ_STRING(thread->proc, nameaddr,
+ name, len)) != 0)
+ return (ret);
+
+ return (0);
+}
+
+int
+td_activated(td_proc_t *proc)
+{
+ if (proc->thread_activated)
+ return (1);
+ READ(proc, proc->thread_activated_addr, &proc->thread_activated,
+ sizeof(proc->thread_activated));
+ return (proc->thread_activated != 0);
+}
+
+int
+td_map_lwp2thr(td_proc_t *proc, void *lwp, td_thread_t **threadp)
+{
+ int ret;
+ caddr_t next, ptr;
+ td_thread_t *thread;
+ TAILQ_HEAD(, pthread) thread_list;
+
+ ret = READ(proc, proc->thread_list_addr, &thread_list,
+ sizeof(thread_list));
+ if (ret != 0)
+ return (ret);
+ /*
+ * We have to iterate through thread list to find which
+ * userland thread is running on the kernel thread.
+ */
+ next = (caddr_t)thread_list.tqh_first;
+ while (next != NULL) {
+ ret = READ(proc, next + offsetof(struct pthread, tcb),
+ &ptr, sizeof(ptr));
+ if (ret != 0)
+ return (ret);
+ ptr += offsetof(struct tcb, tcb_tmbx.tm_kthread);
+ ret = READ(proc, ptr, &ptr, sizeof(ptr));
+ if (ret != 0)
+ return (ret);
+ if (ptr == lwp) {
+ ret = td_get_thread(proc, next, TD_TYPE_NORMAL,
+ &thread);
+ if (ret != 0)
+ return (ret);
+ *threadp = thread;
+ return (0);
+ }
+
+ /* get next thread */
+ ret = READ(proc,
+ next + offsetof(struct pthread, tle.tqe_next),
+ &next, sizeof(next));
+ if (ret != 0)
+ return (ret);
+ }
+ return (TD_ERR_NOOBJ);
+}
+
+int
+td_map_id2thr(td_proc_t *proc, long threadid, td_thread_t **threadp)
+{
+ td_thread_t *thread;
+
+ td_refresh_thread_list(proc);
+
+ TAILQ_FOREACH(thread, &proc->threads, tle) {
+ if (thread->type == TD_TYPE_UPCALL) {
+ if ((long)thread->kthread == threadid)
+ break;
+ } else if ((long)thread->addr == threadid)
+ break;
+ }
+ if (thread) {
+ *threadp = thread;
+ return (0);
+ }
+ return (TD_ERR_NOOBJ);
+}
+
+int
+td_thr_sstep(td_thread_t *thread, int step)
+{
+ int ret;
+ uint32_t tmp;
+ struct reg reg;
+ td_proc_t *proc;
+ caddr_t kthread;
+
+ proc = thread->proc;
+ if ((kthread=thread->kthread) != NULL)
+ return SSTEP(proc, kthread, step);
+
+ if (thread->step == step)
+ return (0);
+
+ /* Clear or set single step flag in thread mailbox */
+ tmp = step ? TMDF_SSTEP : 0;
+ ret = WRITE(proc, thread->tcb_addr + offsetof(struct tcb,
+ tcb_tmbx.tm_dflags), &tmp, sizeof(tmp));
+
+ /* Get kthread */
+ ret = READ(proc, thread->tcb_addr + offsetof(struct tcb,
+ tcb_tmbx.tm_kthread), &kthread, sizeof(kthread));
+ if (ret != 0)
+ return (ret);
+ thread->step = step;
+ if (kthread != NULL)
+ return SSTEP(proc, kthread, step);
+
+ /*
+ * context is in userland, some architectures store
+ * single step status in registers, we should change
+ * these registers.
+ */
+ ret = td_thr_getregs(thread, 0, &reg);
+ if (ret == 0) {
+ /* only write out if it is really changed. */
+ if (td_reg_sstep(&reg, step) != 0)
+ ret = td_thr_setregs(thread, 0, &reg);
+ }
+ return (ret);
+}
+
+void
+td_remove_lwp_map(td_proc_t *proc)
+{
+ td_remove_map(proc, TD_TYPE_UPCALL);
+}
+
+static void
+td_remove_map(td_proc_t *proc, int type)
+{
+ td_thread_t *thread, *next;
+
+ for (thread = TAILQ_FIRST(&proc->threads); thread; thread = next) {
+ next = TAILQ_NEXT(thread, tle);
+ if (type == type)
+ TAILQ_REMOVE(&proc->threads, thread, tle);
+ }
+}
+
+static int
+td_refresh_thread_list(td_proc_t *proc)
+{
+ int ret, gen;
+ caddr_t next;
+ td_thread_t *thread;
+ TAILQ_HEAD(, pthread) thread_list;
+
+ ret = READ(proc, proc->thread_listgen_addr, &gen, sizeof(gen));
+ if (ret != 0)
+ return (ret);
+ if (gen == proc->thread_listgen)
+ return (TD_ERR_OK);
+ proc->thread_listgen = gen;
+
+ td_remove_map(proc, TD_TYPE_NORMAL);
+
+ ret = READ(proc, proc->thread_list_addr, &thread_list,
+ sizeof(thread_list));
+ if (ret != 0)
+ return (ret);
+ next = (caddr_t)thread_list.tqh_first;
+ while (next != NULL) {
+ ret = td_get_thread(proc, next, TD_TYPE_NORMAL, &thread);
+ if (ret != 0)
+ return (ret);
+ ret = READ(proc,
+ next + offsetof(struct pthread, tle.tqe_next),
+ &next, sizeof(next));
+ if (ret != 0)
+ return (ret);
+ }
+ return (0);
+}
+
+static void
+td_empty_thread_list(td_proc_t *proc)
+{
+ td_thread_t *thread;
+
+ while ((thread = TAILQ_FIRST(&proc->threads)) != NULL) {
+ TAILQ_REMOVE(&proc->threads, thread, tle);
+ free(thread);
+ }
+}
+
+static int
+td_get_thread(td_proc_t *proc, caddr_t addr, int type, td_thread_t **threadp)
+{
+ td_thread_t *thread;
+ caddr_t tcb_addr;
+ int ret;
+
+ TAILQ_FOREACH(thread, &proc->threads, tle) {
+ if (type == TD_TYPE_UPCALL) {
+ /* match upcall thread */
+ if (thread->kthread == addr)
+ break;
+ } else {
+ /* match normal thread */
+ if (thread->addr == addr)
+ break;
+ }
+ }
+
+ if (thread == NULL) {
+ tcb_addr = NULL;
+ if (type != TD_TYPE_UPCALL) {
+ ret = READ(proc, addr + offsetof(struct pthread, tcb),
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret)
+ return (ret);
+ }
+ thread = malloc(sizeof(*thread));
+ if (thread == NULL)
+ return (TD_ERR_NOMEM);
+ thread->proc = proc;
+ thread->step = -1;
+ thread->tcb_addr = tcb_addr;
+ if (type == TD_TYPE_NORMAL) {
+ thread->addr = addr;
+ thread->kthread = NULL;
+ } else {
+ thread->addr = NULL;
+ thread->kthread = addr;
+ }
+ thread->type = type;
+ TAILQ_INSERT_TAIL(&proc->threads, thread, tle);
+ }
+ *threadp = thread;
+ return (0);
+}
+
+struct string_map {
+ int num;
+ char *str;
+};
+
+char *
+td_err_string (int errcode)
+{
+ static struct string_map err_table[] = {
+ {TD_ERR_OK, "generic \"call succeeded\""},
+ {TD_ERR_ERR, "generic error."},
+ {TD_ERR_NOSYM, "symbol not found"},
+ {TD_ERR_NOOBJ, "no object can be found to satisfy query"},
+ {TD_ERR_BADTHREAD, "thread can not answer request"},
+ {TD_ERR_INUSE, "debugging interface already in use for this process"},
+ {TD_ERR_NOLIB, "process is not using libpthread"},
+ {TD_ERR_NOMEM, "out of memory"},
+ {TD_ERR_IO, "process callback error"},
+ {TD_ERR_INVAL, "invalid argument"},
+ };
+
+ const int err_size = sizeof(err_table) / sizeof (struct string_map);
+ int i;
+ static char buf[90];
+
+ for (i = 0; i < err_size; i++)
+ if (err_table[i].num == errcode)
+ return err_table[i].str;
+
+ sprintf (buf, "Unknown thread library debug error code: %d", errcode);
+
+ return buf;
+}
diff --git a/lib/libpthread_dbg/pthread_dbg.h b/lib/libpthread_dbg/pthread_dbg.h
new file mode 100644
index 0000000..0f807fc
--- /dev/null
+++ b/lib/libpthread_dbg/pthread_dbg.h
@@ -0,0 +1,139 @@
+/*
+ * 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_DBG_H
+#define _PTHREAD_DBG_H
+
+#include <sys/types.h>
+#include <signal.h>
+
+typedef struct td_proc td_proc_t;
+typedef struct td_thread td_thread_t;
+
+typedef struct td_proc_callbacks {
+ int (*proc_read)(void *arg, caddr_t addr, void *buf, size_t size);
+ int (*proc_readstring)(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, void *lwp, void *buf);
+ int (*proc_setregs)(void *arg, int regset, void *lwp, void *buf);
+ int (*proc_sstep)(void *arg, void *lwp, int onoff);
+} td_proc_callbacks_t;
+
+typedef struct td_thread_info {
+ caddr_t thread_addr; /* Address of data structure */
+ int thread_state; /* TD_STATE_*; see below */
+ int thread_type; /* TD_TYPE_*; see below */
+ int thread_scope; /* TD_SCOPE_*; see below */
+ long thread_id;
+ stack_t thread_stack;
+ caddr_t thread_joiner;
+ caddr_t thread_tls;
+ int thread_tlscount;
+ int thread_errno;
+ sigset_t thread_sigmask;
+ sigset_t thread_sigpend;
+ stack_t thread_sigstk;
+ char thread_base_priority;
+ char thread_inherited_priority;
+ char thread_active_priority;
+ int thread_cancelflags; /* TD_CANCEL_*; see below */
+ caddr_t thread_retval;
+ long pad[32];
+} td_thread_info_t;
+
+#define TD_STATE_UNKNOWN 0
+#define TD_STATE_RUNNING 1
+#define TD_STATE_LOCKWAIT 2
+#define TD_STATE_MUTEXWAIT 3
+#define TD_STATE_CONDWAIT 4
+#define TD_STATE_SLEEPING 5
+#define TD_STATE_SIGSUSPEND 6
+#define TD_STATE_SIGWAIT 7
+#define TD_STATE_JOIN 8
+#define TD_STATE_SUSPENDED 9
+#define TD_STATE_DEAD 10
+#define TD_STATE_DEADLOCK 11
+
+#define TD_TYPE_NORMAL 0
+#define TD_TYPE_UPCALL 1
+
+#define TD_SCOPE_PROCESS 0
+#define TD_SCOPE_SYSTEM 1
+
+#define TD_CANCEL_DISABLED 1
+#define TD_CANCEL_ASYNCHRONOUS 2
+#define TD_CANCEL_AT_POINT 4
+#define TD_CANCEL_CANCELLING 8
+#define TD_CANCEL_NEEDED 10
+
+/* 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 /* Thread structure damaged */
+#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(td_proc_callbacks_t *, void *arg, td_proc_t **);
+
+/* Release proc object */
+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 *);
+
+/* Check if threaded mode is activated */
+int td_activated(td_proc_t *);
+
+/* Get information on a thread */
+int td_thr_info(td_thread_t *, td_thread_info_t *);
+
+/* Get name of a thread */
+int td_thr_getname(td_thread_t *, char *, int);
+
+/* Get registers set of a thread */
+int td_thr_getregs(td_thread_t *, int, void *);
+
+/* Set registers set of a thread */
+int td_thr_setregs(td_thread_t *, int, void *);
+
+/* Set/clear single step status */
+int td_thr_sstep(td_thread_t *, int step);
+
+/* Map error code to error message */
+char *td_err_string (int errcode);
+
+#endif
+
diff --git a/lib/libpthread_dbg/pthread_dbg_int.h b/lib/libpthread_dbg/pthread_dbg_int.h
new file mode 100644
index 0000000..b6c4e3d
--- /dev/null
+++ b/lib/libpthread_dbg/pthread_dbg_int.h
@@ -0,0 +1,68 @@
+/*
+ * 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_DBG_INT_H
+#define _PTHREAD_DBG_INT_H
+
+#include <sys/queue.h>
+#include <sys/ucontext.h>
+
+struct td_proc {
+ td_proc_callbacks_t *cb;
+ void *arg;
+ pid_t pid;
+ caddr_t libkse_debug_addr;
+ caddr_t thread_list_addr;
+ caddr_t thread_listgen_addr;
+ caddr_t thread_activated_addr;
+ caddr_t thread_active_kseq_addr;
+ int thread_activated;
+ int thread_listgen;
+ TAILQ_HEAD(, td_thread) threads;
+};
+
+struct td_thread {
+ td_proc_t *proc;
+ caddr_t addr;
+ caddr_t tcb_addr;
+ void *kthread;
+ int type;
+ int step;
+ TAILQ_ENTRY(td_thread) tle;
+};
+
+#define READ(proc, addr, buf, size) ((proc)->cb->proc_read((proc)->arg, (addr), (buf), (size)))
+#define READ_STRING(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)))
+#define SSTEP(proc, lwp, on) ((proc)->cb->proc_sstep((proc)->arg, (lwp), (on)))
+
+#endif
+
OpenPOWER on IntegriCloud