delve/proc/threads_linux.go
Derek Parker b9846c7684 command (next): Improvements for parallel programs
This patch aims to improve how Delve tracks the current goroutine,
especially in very highly parallel programs. The main spirit of this
patch is to ensure that even in situations where the goroutine we care
about is not executing (common for len(g) > len(m)) we still end up back
on that goroutine as a result of executing the 'next' command.

We accomplish this by tracking our original goroutine id, and any time a
breakpoint is hit or a threads stops, we examine the stopped threads and
see if any are executing the goroutine we care about. If not, we set
'next' breakpoint for them again and continue them. This is done so that
one of those threads can eventually pick up the goroutine we care about
and begin executing it again.
2015-08-20 09:32:59 -05:00

96 lines
2.1 KiB
Go

package proc
import (
"fmt"
sys "golang.org/x/sys/unix"
)
// Not actually used, but necessary
// to be defined.
type OSSpecificDetails struct {
registers sys.PtraceRegs
}
func (t *Thread) Halt() (err error) {
defer func() {
if err == nil {
t.running = false
}
}()
if t.Stopped() {
return
}
err = sys.Tgkill(t.dbp.Pid, t.Id, sys.SIGSTOP)
if err != nil {
err = fmt.Errorf("halt err %s on thread %d", err, t.Id)
return
}
_, _, err = wait(t.Id, t.dbp.Pid, 0)
if err != nil {
err = fmt.Errorf("wait err %s on thread %d", err, t.Id)
return
}
return
}
func (thread *Thread) stopped() bool {
state := status(thread.Id)
return state == STATUS_TRACE_STOP
}
func (t *Thread) resume() (err error) {
t.running = true
t.dbp.execPtraceFunc(func() { err = PtraceCont(t.Id, 0) })
return
}
func (t *Thread) singleStep() (err error) {
t.dbp.execPtraceFunc(func() { err = sys.PtraceSingleStep(t.Id) })
if err != nil {
return err
}
_, _, err = wait(t.Id, t.dbp.Pid, 0)
return err
}
func (t *Thread) blocked() bool {
pc, _ := t.PC()
fn := t.dbp.goSymTable.PCToFunc(pc)
if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
return true
}
return false
}
func (thread *Thread) saveRegisters() (Registers, error) {
var err error
thread.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(thread.Id, &thread.os.registers) })
if err != nil {
return nil, fmt.Errorf("could not save register contents")
}
return &Regs{&thread.os.registers}, nil
}
func (thread *Thread) restoreRegisters() (err error) {
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.Id, &thread.os.registers) })
return
}
func (thread *Thread) writeMemory(addr uintptr, data []byte) (written int, err error) {
if len(data) == 0 {
return
}
thread.dbp.execPtraceFunc(func() { written, err = sys.PtracePokeData(thread.Id, addr, data) })
return
}
func (thread *Thread) readMemory(addr uintptr, size int) (data []byte, err error) {
if size == 0 {
return
}
data = make([]byte, size)
thread.dbp.execPtraceFunc(func() { _, err = sys.PtracePeekData(thread.Id, addr, data) })
return
}