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.
This commit is contained in:
parent
953792046b
commit
58d87c5c98
20
lib/libpthread_dbg/Makefile
Normal file
20
lib/libpthread_dbg/Makefile
Normal file
@ -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>
|
5
lib/libpthread_dbg/arch/i386/Makefile.inc
Normal file
5
lib/libpthread_dbg/arch/i386/Makefile.inc
Normal file
@ -0,0 +1,5 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}/${MACHINE_ARCH}
|
||||
|
||||
SRCS+= pthread_dbg_md.c
|
106
lib/libpthread_dbg/arch/i386/i386/pthread_dbg_md.c
Normal file
106
lib/libpthread_dbg/arch/i386/i386/pthread_dbg_md.c
Normal file
@ -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 ? */
|
||||
}
|
||||
|
562
lib/libpthread_dbg/pthread_dbg.c
Normal file
562
lib/libpthread_dbg/pthread_dbg.c
Normal file
@ -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, ®);
|
||||
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;
|
||||
}
|
139
lib/libpthread_dbg/pthread_dbg.h
Normal file
139
lib/libpthread_dbg/pthread_dbg.h
Normal file
@ -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
|
||||
|
68
lib/libpthread_dbg/pthread_dbg_int.h
Normal file
68
lib/libpthread_dbg/pthread_dbg_int.h
Normal file
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user