delve/pkg/proc/threads_linux.go
Alessandro Arzilli 436a3c2149 proc refactor: split out BinaryInfo implementation (#745)
* proc: refactor BinaryInfo part of proc.Process to own type

The data structures and associated code used by proc.Process
to implement target.BinaryInfo will also be useful to support a
backend for examining core dumps, split this part of proc.Process
to a different type.

* proc: compile support for all executable formats unconditionally

So far we only compiled in support for loading the executable format
supported by the host operating system.
Once support for core files is introduced it is however useful to
support loading in all executable formats, there is no reason why it
shouldn't be possible to examine a linux coredump on windows, or
viceversa.

* proc: bugfix: do not resume threads on detach if killing

* Replace BinaryInfo interface with BinInfo() method returning proc.BinaryInfo
2017-04-06 11:14:01 -07:00

109 lines
2.4 KiB
Go

package proc
import (
"fmt"
sys "golang.org/x/sys/unix"
)
type WaitStatus sys.WaitStatus
// OSSpecificDetails hold Linux specific
// process details.
type OSSpecificDetails struct {
registers sys.PtraceRegs
}
func (t *Thread) halt() (err error) {
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 = t.dbp.wait(t.ID, 0)
if err != nil {
err = fmt.Errorf("wait err %s on thread %d", err, t.ID)
return
}
return
}
func (t *Thread) stopped() bool {
state := status(t.ID, t.dbp.os.comm)
return state == StatusTraceStop || state == StatusTraceStopT
}
func (t *Thread) resume() error {
return t.resumeWithSig(0)
}
func (t *Thread) resumeWithSig(sig int) (err error) {
t.running = true
t.dbp.execPtraceFunc(func() { err = PtraceCont(t.ID, sig) })
return
}
func (t *Thread) singleStep() (err error) {
for {
t.dbp.execPtraceFunc(func() { err = sys.PtraceSingleStep(t.ID) })
if err != nil {
return err
}
wpid, status, err := t.dbp.waitFast(t.ID)
if err != nil {
return err
}
if (status == nil || status.Exited()) && wpid == t.dbp.pid {
t.dbp.postExit()
rs := 0
if status != nil {
rs = status.ExitStatus()
}
return ProcessExitedError{Pid: t.dbp.pid, Status: rs}
}
if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
return nil
}
}
}
func (t *Thread) blocked() bool {
pc, _ := t.PC()
fn := t.dbp.bi.goSymTable.PCToFunc(pc)
if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
return true
}
return false
}
func (t *Thread) saveRegisters() (Registers, error) {
var err error
t.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(t.ID, &t.os.registers) })
if err != nil {
return nil, fmt.Errorf("could not save register contents")
}
return &Regs{&t.os.registers, nil}, nil
}
func (t *Thread) restoreRegisters() (err error) {
t.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(t.ID, &t.os.registers) })
return
}
func (t *Thread) writeMemory(addr uintptr, data []byte) (written int, err error) {
if len(data) == 0 {
return
}
t.dbp.execPtraceFunc(func() { written, err = sys.PtracePokeData(t.ID, addr, data) })
return
}
func (t *Thread) readMemory(addr uintptr, size int) (data []byte, err error) {
if size == 0 {
return
}
data = make([]byte, size)
t.dbp.execPtraceFunc(func() { _, err = sys.PtracePeekData(t.ID, addr, data) })
return
}