mirror of
https://github.com/go-delve/delve.git
synced 2025-10-28 12:47:22 +08:00
Refactor: Use thread-locked goroutine for ptrace ops
Previously either the terminal client or the debugger service would either lock main goroutine to a thread or provide a locked goroutine to run _all_ DebuggedProcess functions in. This is unnecessary because only ptrace functions need to be run from the same thread that originated the PT_ATTACH request. Here we use a specific thread-locked goroutine to service any ptrace request. That goroutine is also responsible for the initial spawning / attaching of the process, since it must be responsible for the PT_ATTACH request.
This commit is contained in:
83
proc/proc.go
83
proc/proc.go
@ -49,6 +49,23 @@ type DebuggedProcess struct {
|
||||
running bool
|
||||
halt bool
|
||||
exited bool
|
||||
ptraceChan chan func()
|
||||
ptraceDoneChan chan interface{}
|
||||
}
|
||||
|
||||
func New(pid int) *DebuggedProcess {
|
||||
dbp := &DebuggedProcess{
|
||||
Pid: pid,
|
||||
Threads: make(map[int]*Thread),
|
||||
Breakpoints: make(map[uint64]*Breakpoint),
|
||||
firstStart: true,
|
||||
os: new(OSProcessDetails),
|
||||
ast: source.New(),
|
||||
ptraceChan: make(chan func()),
|
||||
ptraceDoneChan: make(chan interface{}),
|
||||
}
|
||||
go dbp.handlePtraceFuncs()
|
||||
return dbp
|
||||
}
|
||||
|
||||
// A ManualStopError happens when the user triggers a
|
||||
@ -72,22 +89,33 @@ func (pe ProcessExitedError) Error() string {
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
func Attach(pid int) (*DebuggedProcess, error) {
|
||||
dbp := &DebuggedProcess{
|
||||
Pid: pid,
|
||||
Threads: make(map[int]*Thread),
|
||||
Breakpoints: make(map[uint64]*Breakpoint),
|
||||
os: new(OSProcessDetails),
|
||||
ast: source.New(),
|
||||
}
|
||||
dbp, err := initializeDebugProcess(dbp, "", true)
|
||||
dbp, err := initializeDebugProcess(New(pid), "", true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
}
|
||||
|
||||
func (dbp *DebuggedProcess) Detach() error {
|
||||
return PtraceDetach(dbp.Pid, int(sys.SIGINT))
|
||||
func (dbp *DebuggedProcess) Detach(kill bool) (err error) {
|
||||
// Clean up any breakpoints we've set.
|
||||
for _, bp := range dbp.arch.HardwareBreakpoints() {
|
||||
if bp != nil {
|
||||
dbp.Clear(bp.Addr)
|
||||
}
|
||||
}
|
||||
for _, bp := range dbp.Breakpoints {
|
||||
if bp != nil {
|
||||
dbp.Clear(bp.Addr)
|
||||
}
|
||||
}
|
||||
dbp.execPtraceFunc(func() {
|
||||
var sig int
|
||||
if kill {
|
||||
sig = int(sys.SIGINT)
|
||||
}
|
||||
err = PtraceDetach(dbp.Pid, sig)
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Returns whether or not Delve thinks the debugged
|
||||
@ -497,6 +525,21 @@ func (dbp *DebuggedProcess) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
||||
return dbp.goSymTable.PCToLine(pc)
|
||||
}
|
||||
|
||||
// Finds the breakpoint for the given ID.
|
||||
func (dbp *DebuggedProcess) FindBreakpointByID(id int) (*Breakpoint, bool) {
|
||||
for _, bp := range dbp.arch.HardwareBreakpoints() {
|
||||
if bp != nil && bp.ID == id {
|
||||
return bp, true
|
||||
}
|
||||
}
|
||||
for _, bp := range dbp.Breakpoints {
|
||||
if bp.ID == id {
|
||||
return bp, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Finds the breakpoint for the given pc.
|
||||
func (dbp *DebuggedProcess) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
|
||||
for _, bp := range dbp.arch.HardwareBreakpoints() {
|
||||
@ -513,7 +556,8 @@ func (dbp *DebuggedProcess) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
|
||||
// Returns a new DebuggedProcess struct.
|
||||
func initializeDebugProcess(dbp *DebuggedProcess, path string, attach bool) (*DebuggedProcess, error) {
|
||||
if attach {
|
||||
err := sys.PtraceAttach(dbp.Pid)
|
||||
var err error
|
||||
dbp.execPtraceFunc(func() { err = sys.PtraceAttach(dbp.Pid) })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -623,3 +667,20 @@ func (dbp *DebuggedProcess) run(fn func() error) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *DebuggedProcess) handlePtraceFuncs() {
|
||||
// We must ensure here that we are running on the same thread during
|
||||
// the execution of dbg. This is due to the fact that ptrace(2) expects
|
||||
// all commands after PTRACE_ATTACH to come from the same thread.
|
||||
runtime.LockOSThread()
|
||||
|
||||
for fn := range dbp.ptraceChan {
|
||||
fn()
|
||||
dbp.ptraceDoneChan <- nil
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *DebuggedProcess) execPtraceFunc(fn func()) {
|
||||
dbp.ptraceChan <- fn
|
||||
<-dbp.ptraceDoneChan
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user