提交 07e53f7c 编写于 作者: A Alessandro Arzilli 提交者: Derek Parker

proc: fix interaction of RequestManualStop and conditional breakpoints (#876)

* proc: fix interaction of RequestManualStop and conditional breakpoints

A conditional breakpoint that is hit but has the condition evaluate to
false can block a RequestManualStop from working. If the conditional
breakpoint is set on an instruction that is executed very frequently by
multiple goroutines (or many conditional breakpoints are set) it could
prevent all calls to RequestManualStop from working.

This commit fixes the problem by changing proc.Continue to exit
unconditionally after a RequestManualStop is called.

* proc/gdbserial: fix ContinueOnce getting stuck on macOS

Fixes #902
上级 8276ba06
...@@ -269,6 +269,10 @@ func (p *Process) RequestManualStop() error { ...@@ -269,6 +269,10 @@ func (p *Process) RequestManualStop() error {
return nil return nil
} }
func (p *Process) ManualStopRequested() bool {
return false
}
func (p *Process) CurrentThread() proc.Thread { func (p *Process) CurrentThread() proc.Thread {
return &Thread{p.currentThread, p} return &Thread{p.currentThread, p}
} }
......
...@@ -108,6 +108,8 @@ type Process struct { ...@@ -108,6 +108,8 @@ type Process struct {
exited bool exited bool
ctrlC bool // ctrl-c was sent to stop inferior ctrlC bool // ctrl-c was sent to stop inferior
manualStopRequested bool
breakpoints map[uint64]*proc.Breakpoint breakpoints map[uint64]*proc.Breakpoint
breakpointIDCounter int breakpointIDCounter int
internalBreakpointIDCounter int internalBreakpointIDCounter int
...@@ -540,7 +542,7 @@ func (p *Process) ContinueOnce() (proc.Thread, error) { ...@@ -540,7 +542,7 @@ func (p *Process) ContinueOnce() (proc.Thread, error) {
th.clearBreakpointState() th.clearBreakpointState()
} }
p.ctrlC = false p.setCtrlC(false)
// resume all threads // resume all threads
var threadID string var threadID string
...@@ -565,7 +567,7 @@ continueLoop: ...@@ -565,7 +567,7 @@ continueLoop:
// the ctrlC flag to know that we are the originators. // the ctrlC flag to know that we are the originators.
switch sig { switch sig {
case interruptSignal: // interrupt case interruptSignal: // interrupt
if p.ctrlC { if p.getCtrlC() {
break continueLoop break continueLoop
} }
case breakpointSignal: // breakpoint case breakpointSignal: // breakpoint
...@@ -655,18 +657,42 @@ func (p *Process) SwitchGoroutine(gid int) error { ...@@ -655,18 +657,42 @@ func (p *Process) SwitchGoroutine(gid int) error {
} }
func (p *Process) RequestManualStop() error { func (p *Process) RequestManualStop() error {
p.conn.manualStopMutex.Lock()
p.manualStopRequested = true
if !p.conn.running { if !p.conn.running {
p.conn.manualStopMutex.Unlock()
return nil return nil
} }
p.ctrlC = true p.ctrlC = true
p.conn.manualStopMutex.Unlock()
return p.conn.sendCtrlC() return p.conn.sendCtrlC()
} }
func (p *Process) ManualStopRequested() bool {
p.conn.manualStopMutex.Lock()
msr := p.manualStopRequested
p.manualStopRequested = false
p.conn.manualStopMutex.Unlock()
return msr
}
func (p *Process) setCtrlC(v bool) {
p.conn.manualStopMutex.Lock()
p.ctrlC = v
p.conn.manualStopMutex.Unlock()
}
func (p *Process) getCtrlC() bool {
p.conn.manualStopMutex.Lock()
defer p.conn.manualStopMutex.Unlock()
return p.ctrlC
}
func (p *Process) Halt() error { func (p *Process) Halt() error {
if p.exited { if p.exited {
return nil return nil
} }
p.ctrlC = true p.setCtrlC(true)
return p.conn.sendCtrlC() return p.conn.sendCtrlC()
} }
...@@ -715,7 +741,7 @@ func (p *Process) Restart(pos string) error { ...@@ -715,7 +741,7 @@ func (p *Process) Restart(pos string) error {
th.clearBreakpointState() th.clearBreakpointState()
} }
p.ctrlC = false p.setCtrlC(false)
err := p.conn.restart(pos) err := p.conn.restart(pos)
if err != nil { if err != nil {
...@@ -1129,8 +1155,11 @@ func (t *Thread) Blocked() bool { ...@@ -1129,8 +1155,11 @@ func (t *Thread) Blocked() bool {
return false return false
} }
pc := regs.PC() pc := regs.PC()
fn := t.BinInfo().PCToFunc(pc) f, ln, fn := t.BinInfo().PCToLine(pc)
if fn == nil { if fn == nil {
if f == "" && ln == 0 {
return true
}
return false return false
} }
switch fn.Name { switch fn.Name {
...@@ -1306,6 +1335,12 @@ func (t *Thread) reloadGAtPC() error { ...@@ -1306,6 +1335,12 @@ func (t *Thread) reloadGAtPC() error {
_, _, err = t.p.conn.step(t.strID, nil) _, _, err = t.p.conn.step(t.strID, nil)
if err != nil { if err != nil {
if err == threadBlockedError {
t.regs.tls = 0
t.regs.gaddr = 0
t.regs.hasgaddr = true
return nil
}
return err return err
} }
...@@ -1353,6 +1388,12 @@ func (t *Thread) reloadGAlloc() error { ...@@ -1353,6 +1388,12 @@ func (t *Thread) reloadGAlloc() error {
_, _, err = t.p.conn.step(t.strID, nil) _, _, err = t.p.conn.step(t.strID, nil)
if err != nil { if err != nil {
if err == threadBlockedError {
t.regs.tls = 0
t.regs.gaddr = 0
t.regs.hasgaddr = true
return nil
}
return err return err
} }
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/derekparker/delve/pkg/proc" "github.com/derekparker/delve/pkg/proc"
...@@ -23,8 +24,9 @@ type gdbConn struct { ...@@ -23,8 +24,9 @@ type gdbConn struct {
inbuf []byte inbuf []byte
outbuf bytes.Buffer outbuf bytes.Buffer
running bool manualStopMutex sync.Mutex
resumeChan chan<- struct{} running bool
resumeChan chan<- struct{}
direction proc.Direction // direction of execution direction proc.Direction // direction of execution
...@@ -536,12 +538,17 @@ func (conn *gdbConn) resume(sig uint8, tu *threadUpdater) (string, uint8, error) ...@@ -536,12 +538,17 @@ func (conn *gdbConn) resume(sig uint8, tu *threadUpdater) (string, uint8, error)
conn.outbuf.Reset() conn.outbuf.Reset()
fmt.Fprint(&conn.outbuf, "$bc") fmt.Fprint(&conn.outbuf, "$bc")
} }
conn.manualStopMutex.Lock()
if err := conn.send(conn.outbuf.Bytes()); err != nil { if err := conn.send(conn.outbuf.Bytes()); err != nil {
conn.manualStopMutex.Unlock()
return "", 0, err return "", 0, err
} }
conn.running = true conn.running = true
conn.manualStopMutex.Unlock()
defer func() { defer func() {
conn.manualStopMutex.Lock()
conn.running = false conn.running = false
conn.manualStopMutex.Unlock()
}() }()
if conn.resumeChan != nil { if conn.resumeChan != nil {
close(conn.resumeChan) close(conn.resumeChan)
...@@ -568,7 +575,11 @@ func (conn *gdbConn) step(threadID string, tu *threadUpdater) (string, uint8, er ...@@ -568,7 +575,11 @@ func (conn *gdbConn) step(threadID string, tu *threadUpdater) (string, uint8, er
return conn.waitForvContStop("singlestep", threadID, tu) return conn.waitForvContStop("singlestep", threadID, tu)
} }
var threadBlockedError = errors.New("thread blocked")
func (conn *gdbConn) waitForvContStop(context string, threadID string, tu *threadUpdater) (string, uint8, error) { func (conn *gdbConn) waitForvContStop(context string, threadID string, tu *threadUpdater) (string, uint8, error) {
count := 0
failed := false
for { for {
conn.conn.SetReadDeadline(time.Now().Add(heartbeatInterval)) conn.conn.SetReadDeadline(time.Now().Add(heartbeatInterval))
resp, err := conn.recv(nil, context) resp, err := conn.recv(nil, context)
...@@ -581,6 +592,13 @@ func (conn *gdbConn) waitForvContStop(context string, threadID string, tu *threa ...@@ -581,6 +592,13 @@ func (conn *gdbConn) waitForvContStop(context string, threadID string, tu *threa
if conn.isDebugserver { if conn.isDebugserver {
conn.send([]byte("$?")) conn.send([]byte("$?"))
} }
if count > 1 && context == "singlestep" {
failed = true
conn.sendCtrlC()
}
count++
} else if failed {
return "", 0, threadBlockedError
} else if err != nil { } else if err != nil {
return "", 0, err return "", 0, err
} else { } else {
......
...@@ -87,6 +87,9 @@ type ProcessManipulation interface { ...@@ -87,6 +87,9 @@ type ProcessManipulation interface {
SwitchThread(int) error SwitchThread(int) error
SwitchGoroutine(int) error SwitchGoroutine(int) error
RequestManualStop() error RequestManualStop() error
// ManualStopRequested returns true the first time it's called after a call
// to RequestManualStop.
ManualStopRequested() bool
Halt() error Halt() error
Kill() error Kill() error
Detach(bool) error Detach(bool) error
......
...@@ -44,6 +44,7 @@ type Process struct { ...@@ -44,6 +44,7 @@ type Process struct {
ptraceChan chan func() ptraceChan chan func()
ptraceDoneChan chan interface{} ptraceDoneChan chan interface{}
childProcess bool // this process was launched, not attached to childProcess bool // this process was launched, not attached to
manualStopRequested bool
} }
// New returns an initialized Process struct. Before returning, // New returns an initialized Process struct. Before returning,
...@@ -179,10 +180,19 @@ func (dbp *Process) RequestManualStop() error { ...@@ -179,10 +180,19 @@ func (dbp *Process) RequestManualStop() error {
} }
dbp.haltMu.Lock() dbp.haltMu.Lock()
defer dbp.haltMu.Unlock() defer dbp.haltMu.Unlock()
dbp.manualStopRequested = true
dbp.halt = true dbp.halt = true
return dbp.requestManualStop() return dbp.requestManualStop()
} }
func (dbp *Process) ManualStopRequested() bool {
dbp.haltMu.Lock()
msr := dbp.manualStopRequested
dbp.manualStopRequested = false
dbp.haltMu.Unlock()
return msr
}
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide // SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
// break point table. Setting a break point must be thread specific due to // break point table. Setting a break point must be thread specific due to
// ptrace actions needing the thread to be in a signal-delivery-stop. // ptrace actions needing the thread to be in a signal-delivery-stop.
......
...@@ -95,7 +95,11 @@ func Next(dbp Process) (err error) { ...@@ -95,7 +95,11 @@ func Next(dbp Process) (err error) {
// process. It will continue until it hits a breakpoint // process. It will continue until it hits a breakpoint
// or is otherwise stopped. // or is otherwise stopped.
func Continue(dbp Process) error { func Continue(dbp Process) error {
dbp.ManualStopRequested()
for { for {
if dbp.ManualStopRequested() {
return nil
}
trapthread, err := dbp.ContinueOnce() trapthread, err := dbp.ContinueOnce()
if err != nil { if err != nil {
return err return err
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册