mirror of
https://github.com/go-delve/delve.git
synced 2025-10-30 18:27:37 +08:00
proc/native/linuc: Better native.Process.stop performance (#1874)
* proc/native/linux: only set breakpoints on threads that receive SIGTRAP * proc/native/linux: do not call (*Thread).Stopped inside (*Process).stop (*Thread).Stopped is slow because it needs to open, read and parse a file in /proc, we don't actually need to do that, we can just rely on the value of Thread.os.running. Benchmark before: BenchmarkConditionalBreakpoints-4 1 12476166303 ns/op Benchmark after: BenchmarkConditionalBreakpoints-4 1 10403533675 ns/op Conditional breakpoint evaluation: 1.24ms -> 1ms Updates #1549
This commit is contained in:
committed by
GitHub
parent
17e70a908e
commit
e9b2da17cb
@ -250,16 +250,31 @@ func findExecutable(path string, pid int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||||
return dbp.trapWaitInternal(pid, false)
|
return dbp.trapWaitInternal(pid, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) trapWaitInternal(pid int, halt bool) (*Thread, error) {
|
type trapWaitOptions uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
trapWaitHalt trapWaitOptions = 1 << iota
|
||||||
|
trapWaitNohang
|
||||||
|
)
|
||||||
|
|
||||||
|
func (dbp *Process) trapWaitInternal(pid int, options trapWaitOptions) (*Thread, error) {
|
||||||
|
halt := options&trapWaitHalt != 0
|
||||||
for {
|
for {
|
||||||
wpid, status, err := dbp.wait(pid, 0)
|
wopt := 0
|
||||||
|
if options&trapWaitNohang != 0 {
|
||||||
|
wopt = sys.WNOHANG
|
||||||
|
}
|
||||||
|
wpid, status, err := dbp.wait(pid, wopt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("wait err %s %d", err, pid)
|
return nil, fmt.Errorf("wait err %s %d", err, pid)
|
||||||
}
|
}
|
||||||
if wpid == 0 {
|
if wpid == 0 {
|
||||||
|
if options&trapWaitNohang != 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
th, ok := dbp.threads[wpid]
|
th, ok := dbp.threads[wpid]
|
||||||
@ -321,6 +336,9 @@ func (dbp *Process) trapWaitInternal(pid int, halt bool) (*Thread, error) {
|
|||||||
}
|
}
|
||||||
if (halt && status.StopSignal() == sys.SIGSTOP) || (status.StopSignal() == sys.SIGTRAP) {
|
if (halt && status.StopSignal() == sys.SIGSTOP) || (status.StopSignal() == sys.SIGTRAP) {
|
||||||
th.os.running = false
|
th.os.running = false
|
||||||
|
if status.StopSignal() == sys.SIGTRAP {
|
||||||
|
th.os.setbp = true
|
||||||
|
}
|
||||||
return th, nil
|
return th, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -411,7 +429,7 @@ func (dbp *Process) exitGuard(err error) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if status(dbp.pid, dbp.os.comm) == StatusZombie {
|
if status(dbp.pid, dbp.os.comm) == StatusZombie {
|
||||||
_, err := dbp.trapWaitInternal(-1, false)
|
_, err := dbp.trapWaitInternal(-1, 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,14 +460,26 @@ func (dbp *Process) stop(trapthread *Thread) (err error) {
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
return &proc.ErrProcessExited{Pid: dbp.Pid()}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.threads {
|
||||||
if !th.Stopped() {
|
th.os.setbp = false
|
||||||
|
}
|
||||||
|
trapthread.os.setbp = true
|
||||||
|
|
||||||
|
// check if any other thread simultaneously received a SIGTRAP
|
||||||
|
for {
|
||||||
|
th, _ := dbp.trapWaitInternal(-1, trapWaitNohang)
|
||||||
|
if th == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop all threads that are still running
|
||||||
|
for _, th := range dbp.threads {
|
||||||
|
if th.os.running {
|
||||||
if err := th.stop(); err != nil {
|
if err := th.stop(); err != nil {
|
||||||
return dbp.exitGuard(err)
|
return dbp.exitGuard(err)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Thread is already in a trace stop but we didn't get the notification yet.
|
|
||||||
th.os.running = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,7 +495,7 @@ func (dbp *Process) stop(trapthread *Thread) (err error) {
|
|||||||
if allstopped {
|
if allstopped {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
_, err := dbp.trapWaitInternal(-1, true)
|
_, err := dbp.trapWaitInternal(-1, trapWaitHalt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -475,9 +505,9 @@ func (dbp *Process) stop(trapthread *Thread) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// set breakpoints on all threads
|
// set breakpoints on SIGTRAP threads
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.threads {
|
||||||
if th.CurrentBreakpoint.Breakpoint == nil {
|
if th.CurrentBreakpoint.Breakpoint == nil && th.os.setbp {
|
||||||
if err := th.SetCurrentBreakpoint(true); err != nil {
|
if err := th.SetCurrentBreakpoint(true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ type OSSpecificDetails struct {
|
|||||||
delayedSignal int
|
delayedSignal int
|
||||||
registers sys.PtraceRegs
|
registers sys.PtraceRegs
|
||||||
running bool
|
running bool
|
||||||
|
setbp bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) stop() (err error) {
|
func (t *Thread) stop() (err error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user