diff --git a/lib/libpthread_dbg/Makefile b/lib/libpthread_dbg/Makefile new file mode 100644 index 000000000000..6114eb9a43ce --- /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 diff --git a/lib/libpthread_dbg/arch/i386/Makefile.inc b/lib/libpthread_dbg/arch/i386/Makefile.inc new file mode 100644 index 000000000000..794a4b0d0552 --- /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 000000000000..593a784c742c --- /dev/null +++ b/lib/libpthread_dbg/arch/i386/i386/pthread_dbg_md.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2004 David Xu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#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 000000000000..426a38114120 --- /dev/null +++ b/lib/libpthread_dbg/pthread_dbg.c @@ -0,0 +1,562 @@ +/*- + * Copyright (c) 2004 David Xu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#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, ®); + if (ret == 0) { + /* only write out if it is really changed. */ + if (td_reg_sstep(®, step) != 0) + ret = td_thr_setregs(thread, 0, ®); + } + 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 000000000000..0f807fc630cb --- /dev/null +++ b/lib/libpthread_dbg/pthread_dbg.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2004 David Xu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 +#include + +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 000000000000..b6c4e3d4fb2b --- /dev/null +++ b/lib/libpthread_dbg/pthread_dbg_int.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004 David Xu + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 +#include + +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 +