PR/43128: Paul Koning: Threads support in ptrace() is insufficient for gdb to

debug threaded live apps: Add an optional lwpid in PT_STEP and PT_CONTINUE to
indicate which lwp to operate on, and implement the glue required to make it
work.
This commit is contained in:
christos 2010-04-06 13:50:22 +00:00
parent 1819dce19c
commit ca843a73b0
5 changed files with 163 additions and 22 deletions

View File

@ -1,7 +1,7 @@
.\" $NetBSD: ptrace.2,v 1.31 2010/03/22 19:30:55 joerg Exp $
.\" $NetBSD: ptrace.2,v 1.32 2010/04/06 13:50:22 christos Exp $
.\"
.\" This file is in the public domain.
.Dd March 12, 2007
.Dd April 6, 2010
.Dt PTRACE 2
.Os
.Sh NAME
@ -149,7 +149,9 @@ new value for the program counter), or
to indicate that execution is to pick up where it left off.
.Fa data
provides a signal number to be delivered to the traced process as it
resumes execution, or 0 if no signal is to be sent.
resumes execution, or 0 if no signal is to be sent. If a negative
value is supplied, that is the negative of the LWP ID of the thread to
be resumed, and only that thread executes.
.It Dv PT_KILL
The traced process terminates, as if
.Dv PT_CONTINUE
@ -256,8 +258,8 @@ This
call currently does not stop the child process so it can generate
inconsistent data.
.It Dv PT_LWPINFO
Returns information about the specific thread from the process specified
in the
Returns information about a thread from the list of threads for the
process specified in the
.Fa pid
argument.
The
@ -274,8 +276,15 @@ struct ptrace_lwpinfo {
.Pp
where
.Fa pl_lwpid
contains the thread for which to get info.
contains a thread LWP ID. Information is returned for the thread
following the one with the specified ID in the process thread list,
or for the first thread if
.Fa pl_lwpid
is 0.
Upon return
.Fa pl_lwpid
contains the LWP ID of the thread that was found, or 0 if there is
no thread after the one whose LWP ID was supplied in the call.
.Fa pl_event
contains the event that stopped the thread.
Possible
@ -303,6 +312,14 @@ lists which requests exist on a given machine.
Execution continues as in request PT_CONTINUE; however
as soon as possible after execution of at least one
instruction, execution stops again.
If the
.Fa data
argument is greater than 0, it contains the LWP ID of the thread to be
stepped, and any other threads are continued. If the
.Fa data
argument is less than zero, it contains the negative of the LWP ID of
the
thread to be stepped, and only that thread executes.
.It Dv PT_GETREGS
This request reads the traced process' machine registers into the
.Dq Li "struct reg"
@ -310,6 +327,10 @@ This request reads the traced process' machine registers into the
.In machine/reg.h )
pointed to by
.Fa addr .
The
.Fa data
argument contains the LWP ID of the thread whose registers are to
be read. If zero is supplied, the first thread of the process is read.
.It Dv PT_SETREGS
This request is the converse of
.Dv PT_GETREGS ;
@ -319,6 +340,10 @@ it loads the traced process' machine registers from the
.In machine/reg.h )
pointed to by
.Fa addr .
The
.Fa data
argument contains the LWP ID of the thread whose registers are to
be written. If zero is supplied, the first thread of the process is written.
.It Dv PT_GETFPREGS
This request reads the traced process' floating-point registers into
the
@ -327,6 +352,11 @@ the
.In machine/reg.h )
pointed to by
.Fa addr .
The
.Fa data
argument contains the LWP ID of the thread whose registers are to
be read. If zero is supplied, the first thread of the process is
read.
.It Dv PT_SETFPREGS
This request is the converse of
.Dv PT_GETFPREGS ;
@ -336,6 +366,11 @@ it loads the traced process' floating-point registers from the
.In machine/reg.h )
pointed to by
.Fa addr .
The
.Fa data
argument contains the LWP ID of the thread whose registers are to
be written. If zero is supplied, the first thread of the process is
written.
.\" .It Dv PT_SYSCALL
.\" This request is like
.\" .Dv PT_CONTINUE

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_lwp.c,v 1.141 2010/03/01 21:10:17 darran Exp $ */
/* $NetBSD: kern_lwp.c,v 1.142 2010/04/06 13:50:22 christos Exp $ */
/*-
* Copyright (c) 2001, 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc.
@ -209,7 +209,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.141 2010/03/01 21:10:17 darran Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.142 2010/04/06 13:50:22 christos Exp $");
#include "opt_ddb.h"
#include "opt_lockdebug.h"
@ -374,6 +374,44 @@ lwp_continue(struct lwp *l)
setrunnable(l);
}
/*
* Restart a stopped LWP.
*
* Must be called with p_lock held, and the LWP NOT locked. Will unlock the
* LWP before return.
*/
void
lwp_unstop(struct lwp *l)
{
struct proc *p = l->l_proc;
KASSERT(mutex_owned(proc_lock));
KASSERT(mutex_owned(p->p_lock));
lwp_lock(l);
/* If not stopped, then just bail out. */
if (l->l_stat != LSSTOP) {
lwp_unlock(l);
return;
}
p->p_stat = SACTIVE;
p->p_sflag &= ~PS_STOPPING;
if (!p->p_waited)
p->p_pptr->p_nstopchild--;
if (l->l_wchan == NULL) {
/* setrunnable() will release the lock. */
setrunnable(l);
} else {
l->l_stat = LSSLEEP;
p->p_nrlwps++;
lwp_unlock(l);
}
}
/*
* Wait for an LWP within the current process to exit. If 'lid' is
* non-zero, we are waiting for a specific LWP.
@ -1396,11 +1434,25 @@ lwp_delref(struct lwp *l)
struct proc *p = l->l_proc;
mutex_enter(p->p_lock);
lwp_delref2(l);
mutex_exit(p->p_lock);
}
/*
* Remove one reference to an LWP. If this is the last reference,
* then we must finalize the LWP's death. The proc mutex is held
* on entry.
*/
void
lwp_delref2(struct lwp *l)
{
struct proc *p = l->l_proc;
KASSERT(mutex_owned(p->p_lock));
KASSERT(l->l_stat != LSZOMB);
KASSERT(l->l_refcnt > 0);
if (--l->l_refcnt == 0)
cv_broadcast(&p->p_lwpcv);
mutex_exit(p->p_lock);
}
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_sig.c,v 1.304 2010/03/03 00:47:31 yamt Exp $ */
/* $NetBSD: kern_sig.c,v 1.305 2010/04/06 13:50:22 christos Exp $ */
/*-
* Copyright (c) 2006, 2007, 2008 The NetBSD Foundation, Inc.
@ -66,7 +66,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_sig.c,v 1.304 2010/03/03 00:47:31 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_sig.c,v 1.305 2010/04/06 13:50:22 christos Exp $");
#include "opt_ptrace.h"
#include "opt_compat_sunos.h"
@ -1725,9 +1725,10 @@ sigchecktrace(void)
/*
* If we are no longer being traced, or the parent didn't
* give us a signal, look for more signals.
* give us a signal, or we're stopping, look for more signals.
*/
if ((p->p_slflag & PSL_TRACED) == 0 || p->p_xstat == 0)
if ((p->p_slflag & PSL_TRACED) == 0 || p->p_xstat == 0 ||
(p->p_sflag & PS_STOPPING) != 0)
return 0;
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: sys_process.c,v 1.153 2009/12/17 01:25:10 rmind Exp $ */
/* $NetBSD: sys_process.c,v 1.154 2010/04/06 13:50:22 christos Exp $ */
/*-
* Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@ -118,7 +118,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sys_process.c,v 1.153 2009/12/17 01:25:10 rmind Exp $");
__KERNEL_RCSID(0, "$NetBSD: sys_process.c,v 1.154 2010/04/06 13:50:22 christos Exp $");
#include "opt_ptrace.h"
#include "opt_ktrace.h"
@ -227,7 +227,7 @@ sys_ptrace(struct lwp *l, const struct sys_ptrace_args *uap, register_t *retval)
syscallarg(int) data;
} */
struct proc *p = l->l_proc;
struct lwp *lt;
struct lwp *lt, *lt2;
struct proc *t; /* target process */
struct uio uio;
struct iovec iov;
@ -235,7 +235,8 @@ sys_ptrace(struct lwp *l, const struct sys_ptrace_args *uap, register_t *retval)
struct ptrace_lwpinfo pl;
struct vmspace *vm;
int error, write, tmp, req, pheld;
int signo;
int signo = 0;
int resume_all;
ksiginfo_t ksi;
char *path;
int len;
@ -463,6 +464,7 @@ sys_ptrace(struct lwp *l, const struct sys_ptrace_args *uap, register_t *retval)
write = 0;
*retval = 0;
tmp = 0;
resume_all = 1;
switch (req) {
case PT_TRACE_ME:
@ -596,6 +598,44 @@ sys_ptrace(struct lwp *l, const struct sys_ptrace_args *uap, register_t *retval)
}
p->p_trace_enabled = trace_is_enabled(p);
/*
* Pick up the LWPID, if supplied. There are two cases:
* data < 0 : step or continue single thread, lwp = -data
* data > 0 in PT_STEP : step this thread, continue others
* For operations other than PT_STEP, data > 0 means
* data is the signo to deliver to the process.
*/
tmp = SCARG(uap, data);
if (tmp >= 0) {
#ifdef PT_STEP
if (req == PT_STEP)
signo = 0;
else
#endif
{
signo = tmp;
tmp = 0; /* don't search for LWP */
}
}
else
tmp = -tmp;
if (tmp > 0) {
if (req == PT_DETACH) {
error = EINVAL;
break;
}
lwp_delref2 (lt);
lt = lwp_find(t, tmp);
if (lt == NULL) {
error = ESRCH;
break;
}
lwp_addref(lt);
resume_all = 0;
signo = 0;
}
/*
* From the 4.4BSD PRM:
* "The data argument is taken as a signal number and the
@ -609,7 +649,7 @@ sys_ptrace(struct lwp *l, const struct sys_ptrace_args *uap, register_t *retval)
*/
/* Check that the data is a valid signal number or zero. */
if (SCARG(uap, data) < 0 || SCARG(uap, data) >= NSIG) {
if (signo < 0 || signo >= NSIG) {
error = EINVAL;
break;
}
@ -623,7 +663,17 @@ sys_ptrace(struct lwp *l, const struct sys_ptrace_args *uap, register_t *retval)
#ifdef PT_STEP
/*
* Arrange for a single-step, if that's requested and possible.
* More precisely, set the single step status as requested for
* the requested thread, and clear it for other threads.
*/
LIST_FOREACH(lt2, &t->p_lwps, l_sibling) {
if (lt != lt2)
{
lwp_lock(lt2);
process_sstep(lt2, 0);
lwp_unlock(lt2);
}
}
error = process_sstep(lt, req == PT_STEP);
if (error)
break;
@ -640,8 +690,6 @@ sys_ptrace(struct lwp *l, const struct sys_ptrace_args *uap, register_t *retval)
/* not being traced any more */
t->p_opptr = NULL;
}
signo = SCARG(uap, data);
sendsig:
/* Finally, deliver the requested signal (or none). */
if (t->p_stat == SSTOP) {
@ -651,7 +699,10 @@ sys_ptrace(struct lwp *l, const struct sys_ptrace_args *uap, register_t *retval)
* an LWP runs to see it.
*/
t->p_xstat = signo;
proc_unstop(t);
if (resume_all)
proc_unstop(t);
else
lwp_unstop(lt);
} else if (signo != 0) {
KSI_INIT_EMPTY(&ksi);
ksi.ksi_signo = signo;

View File

@ -1,4 +1,4 @@
/* $NetBSD: lwp.h,v 1.128 2010/02/21 02:11:39 darran Exp $ */
/* $NetBSD: lwp.h,v 1.129 2010/04/06 13:50:22 christos Exp $ */
/*-
* Copyright (c) 2001, 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc.
@ -298,6 +298,7 @@ void lwp_relock(lwp_t *, kmutex_t *);
int lwp_trylock(lwp_t *);
void lwp_addref(lwp_t *);
void lwp_delref(lwp_t *);
void lwp_delref2(lwp_t *);
void lwp_drainrefs(lwp_t *);
bool lwp_alive(lwp_t *);
lwp_t *lwp_find_first(proc_t *);
@ -307,6 +308,7 @@ lwp_t *lwp_find_first(proc_t *);
void lwpinit(void);
int lwp_wait1(lwp_t *, lwpid_t, lwpid_t *, int);
void lwp_continue(lwp_t *);
void lwp_unstop(lwp_t *);
void cpu_setfunc(lwp_t *, void (*)(void *), void *);
void startlwp(void *);
void upcallret(lwp_t *);