delve/pkg/proc/threads_windows.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

152 lines
3.3 KiB
Go

package proc
import (
"syscall"
sys "golang.org/x/sys/windows"
)
// WaitStatus is a synonym for the platform-specific WaitStatus
type WaitStatus sys.WaitStatus
// OSSpecificDetails holds information specific to the Windows
// operating system / kernel.
type OSSpecificDetails struct {
hThread syscall.Handle
}
func (t *Thread) halt() (err error) {
// Ignore the request to halt. On Windows, all threads are halted
// on return from WaitForDebugEvent.
return nil
// TODO - This may not be correct in all usages of dbp.Halt. There
// are some callers who use dbp.Halt() to stop the process when it is not
// already broken on a debug event.
}
func (t *Thread) singleStep() error {
context := newCONTEXT()
context.ContextFlags = _CONTEXT_ALL
// Set the processor TRAP flag
err := _GetThreadContext(t.os.hThread, context)
if err != nil {
return err
}
context.EFlags |= 0x100
err = _SetThreadContext(t.os.hThread, context)
if err != nil {
return err
}
_, err = _ResumeThread(t.os.hThread)
if err != nil {
return err
}
for {
var tid, exitCode int
t.dbp.execPtraceFunc(func() {
tid, exitCode, err = t.dbp.waitForDebugEvent(waitBlocking | waitSuspendNewThreads)
})
if err != nil {
return err
}
if tid == 0 {
t.dbp.postExit()
return ProcessExitedError{Pid: t.dbp.pid, Status: exitCode}
}
if t.dbp.os.breakThread == t.ID {
break
}
t.dbp.execPtraceFunc(func() {
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.dbp.os.breakThread), _DBG_CONTINUE)
})
}
_, err = _SuspendThread(t.os.hThread)
if err != nil {
return err
}
t.dbp.execPtraceFunc(func() {
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
})
if err != nil {
return err
}
// Unset the processor TRAP flag
err = _GetThreadContext(t.os.hThread, context)
if err != nil {
return err
}
context.EFlags &= ^uint32(0x100)
return _SetThreadContext(t.os.hThread, context)
}
func (t *Thread) resume() error {
t.running = true
var err error
t.dbp.execPtraceFunc(func() {
//TODO: Note that we are ignoring the thread we were asked to continue and are continuing the
//thread that we last broke on.
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
})
return err
}
func (t *Thread) blocked() bool {
// TODO: Probably incorrect - what are the runtime functions that
// indicate blocking on Windows?
pc, err := t.PC()
if err != nil {
return false
}
fn := t.dbp.bi.goSymTable.PCToFunc(pc)
if fn == nil {
return false
}
switch fn.Name {
case "runtime.kevent", "runtime.usleep":
return true
default:
return false
}
}
func (t *Thread) stopped() bool {
// TODO: We are assuming that threads are always stopped
// during command execution.
return true
}
func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
var count uintptr
err := _WriteProcessMemory(t.dbp.os.hProcess, addr, &data[0], uintptr(len(data)), &count)
if err != nil {
return 0, err
}
return int(count), nil
}
func (t *Thread) readMemory(addr uintptr, size int) ([]byte, error) {
if size == 0 {
return nil, nil
}
var count uintptr
buf := make([]byte, size)
err := _ReadProcessMemory(t.dbp.os.hProcess, addr, &buf[0], uintptr(size), &count)
if err != nil {
return nil, err
}
return buf[:count], nil
}