delve/pkg/proc/native/proc_windows.go

851 lines
23 KiB
Go
Raw Normal View History

package native
2016-01-15 05:26:54 +00:00
import (
"fmt"
"os"
"strings"
2016-01-15 05:26:54 +00:00
"syscall"
"unicode/utf16"
2016-01-15 05:26:54 +00:00
"unsafe"
sys "golang.org/x/sys/windows"
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/internal/ebpf"
2016-01-15 05:26:54 +00:00
)
// osProcessDetails holds Windows specific information.
type osProcessDetails struct {
hProcess syscall.Handle
2016-01-15 05:26:54 +00:00
breakThread int
entryPoint uint64
running bool
2016-01-15 05:26:54 +00:00
}
func (os *osProcessDetails) Close() {}
2016-01-15 05:26:54 +00:00
// Launch creates and begins debugging a new process.
pkg/proc,service/*: Supports sending output to clients when running programs remotely (#3253) * wip: Support sending output when remote debug * wip: Support local output and remote output * wip: fix stderr and stdout assignment error * wip: optimize code * wip: Only if outputMode is "remote" is the redirected console output * wip: Redirected debugMode output(Not tested on windows) * wip: support remote debugging output redirection of windows * wip: real-time write back output * wip: support for windows * wip: fix windows remote debug not output * wip: fix truncated output redirection * wip: delete printfln * wip: use debugger.Config to pass redirect(macOS) * wip: use debugger.Config to pass redirect(linux) * wip: Change redirect to a concrete type * wip: s.wg.wait before sending "terminated" * wip: add proc/redirect test(darwin and linux) * Merge branch 'master' of github.com:tttoad/delve into feat-console * wip: Fix test failure on windows * fix: undefined: proc.Redirects * fix: compile failure * wip: Remove useless code * fix: filename error * fix: os.file not close * test: add server_test.redirect * fix: Remove 'eol' from end of file * fix: gdbserial: File not closed in file mode. (in reality, gdbserial will never use file mode) * feat: Remove "only-remote". Fix spelling mistakes. * fix: spelling mistakes * refactor: redirect * fix: stdout and stderr are not set to default values * fix: Restore code logic for rr.openRedirects() * fix: Optimization Code * fix: utiltest * fix: execpt out * fix: Resource release for redirects * fix: build failure * fix: clean->clear * fix: build failure * fix: test failure * fix: Optimization Code * style: remove useless code * refactor: namedpipe * refactor: namedpipe, launch ... * fix: freebsd compile failure * fix: proc_darwin compile failure * style: remove useless code * feat: add d.config.Stdxx check on debug.Restart * style: formatting and adding comments * style: formatting and adding comments * feat: add d.config.Stdxx check on debug.Restart * style: namedpipe->redirector * style: namedPipe->redirector --------- Co-authored-by: 李翔 <qian.fu2@amh-group.com>
2023-07-05 15:39:01 +00:00
func Launch(cmd []string, wd string, flags proc.LaunchFlags, _ []string, _ string, stdinPath string, stdoutOR proc.OutputRedirect, stderrOR proc.OutputRedirect) (*proc.TargetGroup, error) {
argv0Go := cmd[0]
env := proc.DisableAsyncPreemptEnv()
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
pkg/proc,service/*: Supports sending output to clients when running programs remotely (#3253) * wip: Support sending output when remote debug * wip: Support local output and remote output * wip: fix stderr and stdout assignment error * wip: optimize code * wip: Only if outputMode is "remote" is the redirected console output * wip: Redirected debugMode output(Not tested on windows) * wip: support remote debugging output redirection of windows * wip: real-time write back output * wip: support for windows * wip: fix windows remote debug not output * wip: fix truncated output redirection * wip: delete printfln * wip: use debugger.Config to pass redirect(macOS) * wip: use debugger.Config to pass redirect(linux) * wip: Change redirect to a concrete type * wip: s.wg.wait before sending "terminated" * wip: add proc/redirect test(darwin and linux) * Merge branch 'master' of github.com:tttoad/delve into feat-console * wip: Fix test failure on windows * fix: undefined: proc.Redirects * fix: compile failure * wip: Remove useless code * fix: filename error * fix: os.file not close * test: add server_test.redirect * fix: Remove 'eol' from end of file * fix: gdbserial: File not closed in file mode. (in reality, gdbserial will never use file mode) * feat: Remove "only-remote". Fix spelling mistakes. * fix: spelling mistakes * refactor: redirect * fix: stdout and stderr are not set to default values * fix: Restore code logic for rr.openRedirects() * fix: Optimization Code * fix: utiltest * fix: execpt out * fix: Resource release for redirects * fix: build failure * fix: clean->clear * fix: build failure * fix: test failure * fix: Optimization Code * style: remove useless code * refactor: namedpipe * refactor: namedpipe, launch ... * fix: freebsd compile failure * fix: proc_darwin compile failure * style: remove useless code * feat: add d.config.Stdxx check on debug.Restart * style: formatting and adding comments * style: formatting and adding comments * feat: add d.config.Stdxx check on debug.Restart * style: namedpipe->redirector * style: namedPipe->redirector --------- Co-authored-by: 李翔 <qian.fu2@amh-group.com>
2023-07-05 15:39:01 +00:00
stdin, stdout, stderr, closefn, err := openRedirects(stdinPath, stdoutOR, stderrOR, true)
if err != nil {
return nil, err
}
creationFlags := uint32(_DEBUG_PROCESS)
if flags&proc.LaunchForeground == 0 {
creationFlags |= syscall.CREATE_NEW_PROCESS_GROUP
}
var p *os.Process
dbp := newProcess(0)
dbp.execPtraceFunc(func() {
attr := &os.ProcAttr{
Dir: wd,
Files: []*os.File{stdin, stdout, stderr},
Sys: &syscall.SysProcAttr{
CreationFlags: creationFlags,
},
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
Env: env,
}
p, err = os.StartProcess(argv0Go, cmd, attr)
})
closefn()
2016-01-15 05:26:54 +00:00
if err != nil {
return nil, err
}
defer p.Release()
2016-01-15 05:26:54 +00:00
dbp.pid = p.Pid
dbp.childProcess = true
tgt, err := dbp.initialize(argv0Go, []string{})
if err != nil {
detachWithoutGroup(dbp, true)
return nil, err
}
return tgt, nil
}
2016-01-15 05:26:54 +00:00
func initialize(dbp *nativeProcess) (string, error) {
// we need a fake procgrp to call waitForDebugEvent
procgrp := &processGroup{procs: []*nativeProcess{dbp}}
// It should not actually be possible for the
2016-01-15 05:26:54 +00:00
// call to waitForDebugEvent to fail, since Windows
// will always fire a CREATE_PROCESS_DEBUG_EVENT event
// immediately after launching under DEBUG_ONLY_THIS_PROCESS.
// Attaching with DebugActiveProcess has similar effect.
var err error
2016-01-15 05:26:54 +00:00
dbp.execPtraceFunc(func() {
_, err = procgrp.waitForDebugEvent(waitBlocking)
2016-01-15 05:26:54 +00:00
})
if err != nil {
return "", err
2016-01-15 05:26:54 +00:00
}
cmdline := getCmdLine(dbp.os.hProcess)
// Suspend all threads so that the call to _ContinueDebugEvent will
// not resume the target.
2017-02-08 00:23:47 +00:00
for _, thread := range dbp.threads {
if !thread.os.dbgUiRemoteBreakIn {
_, err := _SuspendThread(thread.os.hThread)
if err != nil {
return "", err
}
}
}
dbp.execPtraceFunc(func() {
2017-02-08 00:23:47 +00:00
err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
})
return cmdline, err
}
2016-01-15 05:26:54 +00:00
// findExePath searches for process pid, and returns its executable path
func findExePath(pid int) (string, error) {
// Original code suggested different approach (see below).
// Maybe it could be useful in the future.
//
// Find executable path from PID/handle on Windows:
// https://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
p, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid))
if err != nil {
return "", err
}
defer syscall.CloseHandle(p)
n := uint32(128)
for {
buf := make([]uint16, int(n))
err = _QueryFullProcessImageName(p, 0, &buf[0], &n)
switch err {
case syscall.ERROR_INSUFFICIENT_BUFFER:
// try bigger buffer
n *= 2
// but stop if it gets too big
if n > 10000 {
return "", err
}
case nil:
return syscall.UTF16ToString(buf[:n]), nil
default:
return "", err
}
}
2016-01-15 05:26:54 +00:00
}
var debugPrivilegeRequested = false
2016-01-15 05:26:54 +00:00
// Attach to an existing process with the given PID.
func Attach(pid int, waitFor *proc.WaitFor, _ []string) (*proc.TargetGroup, error) {
var aperr error
if !debugPrivilegeRequested {
debugPrivilegeRequested = true
// The following call will only work if the user is an administrator
// has the "Debug Programs" privilege in Local security settings.
// Since this privilege is not needed to debug processes owned by the
// current user, do not complain about this unless attach actually fails.
aperr = acquireDebugPrivilege()
}
if waitFor.Valid() {
var err error
pid, err = WaitFor(waitFor)
if err != nil {
return nil, err
}
}
dbp := newProcess(pid)
var err error
dbp.execPtraceFunc(func() {
// TODO: Probably should have SeDebugPrivilege before starting here.
err = _DebugActiveProcess(uint32(pid))
})
if err != nil {
if aperr != nil {
return nil, fmt.Errorf("%v also %v", err, aperr)
}
return nil, err
}
exepath, err := findExePath(pid)
if err != nil {
return nil, err
}
tgt, err := dbp.initialize(exepath, []string{})
if err != nil {
detachWithoutGroup(dbp, true)
return nil, err
}
return tgt, nil
2016-01-15 05:26:54 +00:00
}
// acquireDebugPrivilege acquires the debug privilege which is needed to
// debug other user's processes.
// See:
//
// - https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debug-privilege
// - https://github.com/go-delve/delve/issues/3136
func acquireDebugPrivilege() error {
var token sys.Token
err := sys.OpenProcessToken(sys.CurrentProcess(), sys.TOKEN_QUERY|sys.TOKEN_ADJUST_PRIVILEGES, &token)
if err != nil {
return fmt.Errorf("could not acquire debug privilege (OpenCurrentProcessToken): %v", err)
}
defer token.Close()
privName, _ := sys.UTF16FromString("SeDebugPrivilege")
var luid sys.LUID
err = sys.LookupPrivilegeValue(nil, &privName[0], &luid)
if err != nil {
return fmt.Errorf("could not acquire debug privilege (LookupPrivilegeValue): %v", err)
}
var tp sys.Tokenprivileges
tp.PrivilegeCount = 1
tp.Privileges[0].Luid = luid
tp.Privileges[0].Attributes = sys.SE_PRIVILEGE_ENABLED
err = sys.AdjustTokenPrivileges(token, false, &tp, 0, nil, nil)
if err != nil {
return fmt.Errorf("could not acquire debug privilege (AdjustTokenPrivileges): %v", err)
}
return nil
}
func waitForSearchProcess(pfx string, seen map[int]struct{}) (int, error) {
log := logflags.DebuggerLogger()
handle, err := sys.CreateToolhelp32Snapshot(sys.TH32CS_SNAPPROCESS, 0)
if err != nil {
return 0, fmt.Errorf("could not get process list: %v", err)
}
defer sys.CloseHandle(handle)
var entry sys.ProcessEntry32
entry.Size = uint32(unsafe.Sizeof(entry))
err = sys.Process32First(handle, &entry)
if err != nil {
return 0, fmt.Errorf("could not get process list: %v", err)
}
for err = sys.Process32First(handle, &entry); err == nil; err = sys.Process32Next(handle, &entry) {
if _, isseen := seen[int(entry.ProcessID)]; isseen {
continue
}
seen[int(entry.ProcessID)] = struct{}{}
hProcess, err := sys.OpenProcess(sys.PROCESS_QUERY_INFORMATION|sys.PROCESS_VM_READ, false, entry.ProcessID)
if err != nil {
continue
}
cmdline := getCmdLine(syscall.Handle(hProcess))
sys.CloseHandle(hProcess)
log.Debugf("waitfor: new process %q", cmdline)
if strings.HasPrefix(cmdline, pfx) {
return int(entry.ProcessID), nil
}
}
return 0, nil
}
// kill kills the process.
func (procgrp *processGroup) kill(dbp *nativeProcess) error {
2016-01-15 05:26:54 +00:00
if dbp.exited {
return nil
}
p, err := os.FindProcess(dbp.pid)
if err != nil {
return err
}
defer p.Release()
2016-01-15 05:26:54 +00:00
// TODO: Should not have to ignore failures here,
// but some tests appear to Kill twice causing
// this to fail on second attempt.
_ = syscall.TerminateProcess(dbp.os.hProcess, 1)
dbp.execPtraceFunc(func() {
procgrp.waitForDebugEvent(waitBlocking | waitDontHandleExceptions)
})
p.Wait()
dbp.postExit()
2016-01-15 05:26:54 +00:00
return nil
}
func (dbp *nativeProcess) requestManualStop() error {
if !dbp.os.running {
return nil
}
dbp.os.running = false
return _DebugBreakProcess(dbp.os.hProcess)
2016-01-15 05:26:54 +00:00
}
func (dbp *nativeProcess) updateThreadList() error {
2016-01-15 05:26:54 +00:00
// We ignore this request since threads are being
// tracked as they are created/killed in waitForDebugEvent.
return nil
}
func (dbp *nativeProcess) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool, dbgUiRemoteBreakIn bool) (*nativeThread, error) {
2017-02-08 00:23:47 +00:00
if thread, ok := dbp.threads[threadID]; ok {
2016-01-15 05:26:54 +00:00
return thread, nil
}
thread := &nativeThread{
2016-01-15 05:26:54 +00:00
ID: threadID,
dbp: dbp,
os: new(osSpecificDetails),
2016-01-15 05:26:54 +00:00
}
thread.os.dbgUiRemoteBreakIn = dbgUiRemoteBreakIn
2016-01-15 05:26:54 +00:00
thread.os.hThread = hThread
2017-02-08 00:23:47 +00:00
dbp.threads[threadID] = thread
proc/*: remove proc.Thread.Blocked, refactor memory access (#2206) On linux we can not read memory if the thread we use to do it is occupied doing certain system calls. The exact conditions when this happens have never been clear. This problem was worked around by using the Blocked method which recognized the most common circumstances where this would happen. However this is a hack: Blocked returning true doesn't mean that the problem will manifest and Blocked returning false doesn't necessarily mean the problem will not manifest. A side effect of this is issue #2151 where sometimes we can't read the memory of a thread and find its associated goroutine. This commit fixes this problem by always reading memory using a thread we know to be good for this, specifically the one returned by ContinueOnce. In particular the changes are as follows: 1. Remove (ProcessInternal).CurrentThread and (ProcessInternal).SetCurrentThread, the "current thread" becomes a field of Target, CurrentThread becomes a (*Target) method and (*Target).SwitchThread basically just sets a field Target. 2. The backends keep track of their own internal idea of what the current thread is, to use it to read memory, this is the thread they return from ContinueOnce as trapthread 3. The current thread in the backend and the current thread in Target only ever get synchronized in two places: when the backend creates a Target object the currentThread field of Target is initialized with the backend's current thread and when (*Target).Restart gets called (when a recording is rewound the currentThread used by Target might not exist anymore). 4. We remove the MemoryReadWriter interface embedded in Thread and instead add a Memory method to Process that returns a MemoryReadWriter. The backends will return something here that will read memory using the current thread saved by the backend. 5. The Thread.Blocked method is removed One possible problem with this change is processes that have threads with different memory maps. As far as I can determine this could happen on old versions of linux but this option was removed in linux 2.5. Fixes #2151
2020-11-09 19:28:40 +00:00
if dbp.memthread == nil {
dbp.memthread = dbp.threads[threadID]
2016-01-15 05:26:54 +00:00
}
if suspendNewThreads && !dbgUiRemoteBreakIn {
_, err := _SuspendThread(thread.os.hThread)
if err != nil {
return nil, err
}
}
for _, bp := range dbp.Breakpoints().M {
if bp.WatchType != 0 {
err := thread.writeHardwareBreakpoint(bp.Addr, bp.WatchType, bp.HWBreakIndex)
if err != nil {
return nil, err
}
}
}
2016-01-15 05:26:54 +00:00
return thread, nil
}
type waitForDebugEventFlags int
const (
waitBlocking waitForDebugEventFlags = 1 << iota
waitSuspendNewThreads
waitDontHandleExceptions
)
const _MS_VC_EXCEPTION = 0x406D1388 // part of VisualC protocol to set thread names
func (procgrp *processGroup) waitForDebugEvent(flags waitForDebugEventFlags) (threadID int, err error) {
var debugEvent _DEBUG_EVENT
2016-01-15 05:26:54 +00:00
for {
continueStatus := uint32(_DBG_CONTINUE)
var milliseconds uint32 = 0
if flags&waitBlocking != 0 {
milliseconds = syscall.INFINITE
}
2016-01-15 05:26:54 +00:00
// Wait for a debug event...
err := _WaitForDebugEvent(&debugEvent, milliseconds)
if err != nil {
return 0, err
2016-01-15 05:26:54 +00:00
}
// ... handle each event kind ...
unionPtr := unsafe.Pointer(&debugEvent.U[0])
switch debugEvent.DebugEventCode {
case _CREATE_PROCESS_DEBUG_EVENT:
debugInfo := (*_CREATE_PROCESS_DEBUG_INFO)(unionPtr)
hFile := debugInfo.File
if hFile != 0 && hFile != syscall.InvalidHandle {
err = syscall.CloseHandle(hFile)
if err != nil {
return 0, err
2016-01-15 05:26:54 +00:00
}
}
var dbp *nativeProcess
if procgrp.addTarget == nil {
// This is a fake process group and waitForDebugEvent has been called
// just after attach/launch, finish configuring the root process.
dbp = procgrp.procs[0]
} else {
// Add new child process
dbp = newChildProcess(procgrp.procs[0], int(debugEvent.ProcessId))
}
dbp.os.entryPoint = uint64(debugInfo.BaseOfImage)
dbp.os.hProcess = debugInfo.Process
_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false,
flags&waitSuspendNewThreads != 0, debugInfo.StartAddress == dbgUiRemoteBreakin.Addr())
2016-01-15 05:26:54 +00:00
if err != nil {
return 0, err
}
if procgrp.addTarget != nil {
exe, err := findExePath(dbp.pid)
if err != nil {
return 0, err
}
tgt, err := procgrp.add(dbp, dbp.pid, dbp.memthread, exe, proc.StopLaunched, getCmdLine(dbp.os.hProcess))
if err != nil {
return 0, err
}
if tgt == nil {
continue
}
2016-01-15 05:26:54 +00:00
}
break
case _CREATE_THREAD_DEBUG_EVENT:
debugInfo := (*_CREATE_THREAD_DEBUG_INFO)(unionPtr)
dbp := procgrp.procForPid(int(debugEvent.ProcessId))
_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false,
flags&waitSuspendNewThreads != 0, debugInfo.StartAddress == dbgUiRemoteBreakin.Addr())
2016-01-15 05:26:54 +00:00
if err != nil {
return 0, err
2016-01-15 05:26:54 +00:00
}
break
case _EXIT_THREAD_DEBUG_EVENT:
dbp := procgrp.procForPid(int(debugEvent.ProcessId))
2017-02-08 00:23:47 +00:00
delete(dbp.threads, int(debugEvent.ThreadId))
2016-01-15 05:26:54 +00:00
break
case _OUTPUT_DEBUG_STRING_EVENT:
2016-01-15 05:26:54 +00:00
//TODO: Handle debug output strings
break
case _LOAD_DLL_DEBUG_EVENT:
debugInfo := (*_LOAD_DLL_DEBUG_INFO)(unionPtr)
hFile := debugInfo.File
if hFile != 0 && hFile != syscall.InvalidHandle {
err = syscall.CloseHandle(hFile)
if err != nil {
return 0, err
2016-01-15 05:26:54 +00:00
}
}
break
case _UNLOAD_DLL_DEBUG_EVENT:
2016-01-15 05:26:54 +00:00
break
case _RIP_EVENT:
2016-01-15 05:26:54 +00:00
break
case _EXCEPTION_DEBUG_EVENT:
if flags&waitDontHandleExceptions != 0 {
continueStatus = _DBG_EXCEPTION_NOT_HANDLED
break
}
exception := (*_EXCEPTION_DEBUG_INFO)(unionPtr)
tid := int(debugEvent.ThreadId)
dbp := procgrp.procForPid(int(debugEvent.ProcessId))
switch code := exception.ExceptionRecord.ExceptionCode; code {
case _EXCEPTION_BREAKPOINT:
// check if the exception address really is a breakpoint instruction, if
// it isn't we already removed that breakpoint and we can't deal with
// this exception anymore.
atbp := true
if thread, found := dbp.threads[tid]; found {
data := make([]byte, dbp.bi.Arch.BreakpointSize())
if _, err := thread.ReadMemory(data, uint64(exception.ExceptionRecord.ExceptionAddress)); err == nil {
instr := dbp.bi.Arch.BreakpointInstruction()
for i := range instr {
if data[i] != instr[i] {
atbp = false
break
}
}
}
if !atbp {
thread.setPC(uint64(exception.ExceptionRecord.ExceptionAddress))
}
}
if atbp {
dbp.os.breakThread = tid
if th := dbp.threads[tid]; th != nil {
th.os.setbp = true
}
return tid, nil
} else {
continueStatus = _DBG_CONTINUE
}
case _EXCEPTION_SINGLE_STEP:
dbp.os.breakThread = tid
return tid, nil
case _MS_VC_EXCEPTION:
// This exception is sent to set the thread name in VisualC, we should
// mask it or it might crash the program.
continueStatus = _DBG_CONTINUE
default:
continueStatus = _DBG_EXCEPTION_NOT_HANDLED
}
case _EXIT_PROCESS_DEBUG_EVENT:
debugInfo := (*_EXIT_PROCESS_DEBUG_INFO)(unionPtr)
dbp := procgrp.procForPid(int(debugEvent.ProcessId))
dbp.postExit()
if procgrp.numValid() == 0 {
err = _ContinueDebugEvent(debugEvent.ProcessId, debugEvent.ThreadId, continueStatus)
if err != nil {
return 0, err
}
return 0, proc.ErrProcessExited{Pid: dbp.pid, Status: int(debugInfo.ExitCode)}
}
2016-01-15 05:26:54 +00:00
default:
return 0, fmt.Errorf("unknown debug event code: %d", debugEvent.DebugEventCode)
2016-01-15 05:26:54 +00:00
}
// .. and then continue unless we received an event that indicated we should break into debugger.
err = _ContinueDebugEvent(debugEvent.ProcessId, debugEvent.ThreadId, continueStatus)
if err != nil {
return 0, err
}
2016-01-15 05:26:54 +00:00
}
}
func trapWait(procgrp *processGroup, pid int) (*nativeThread, error) {
2016-01-15 05:26:54 +00:00
var err error
var tid int
procgrp.procs[0].execPtraceFunc(func() {
tid, err = procgrp.waitForDebugEvent(waitBlocking)
2016-01-15 05:26:54 +00:00
})
if err != nil {
return nil, err
}
th := procgrp.procForThread(tid).threads[tid]
2016-01-15 05:26:54 +00:00
return th, nil
}
func (dbp *nativeProcess) exitGuard(err error) error {
return err
}
func (procgrp *processGroup) resume() error {
for _, dbp := range procgrp.procs {
if valid, _ := dbp.Valid(); !valid {
continue
}
for _, thread := range dbp.threads {
if thread.CurrentBreakpoint.Breakpoint != nil {
if err := procgrp.stepInstruction(thread); err != nil {
return err
}
thread.CurrentBreakpoint.Clear()
}
}
}
for _, dbp := range procgrp.procs {
if valid, _ := dbp.Valid(); !valid {
continue
}
for _, thread := range dbp.threads {
_, err := _ResumeThread(thread.os.hThread)
if err != nil {
return err
}
}
dbp.os.running = true
}
return nil
}
// stop stops all running threads threads and sets breakpoints
func (procgrp *processGroup) stop(cctx *proc.ContinueOnceContext, trapthread *nativeThread) (*nativeThread, error) {
if procgrp.numValid() == 0 {
return nil, proc.ErrProcessExited{Pid: procgrp.procs[0].pid}
}
trapproc := procgrp.procForThread(trapthread.ID)
trapproc.os.running = false
for _, dbp := range procgrp.procs {
for _, th := range dbp.threads {
th.os.setbp = false
}
}
trapthread.os.setbp = true
// While the debug event that stopped the target was being propagated
// other target threads could generate other debug events.
// After this function we need to know about all the threads
// stopped on a breakpoint. To do that we first suspend all target
// threads and then repeatedly call _ContinueDebugEvent followed by
// waitForDebugEvent in non-blocking mode.
// We need to explicitly call SuspendThread because otherwise the
// call to _ContinueDebugEvent will resume execution of some of the
// target threads.
err := trapthread.SetCurrentBreakpoint(true)
if err != nil {
return nil, err
}
2016-01-15 05:26:54 +00:00
for _, dbp := range procgrp.procs {
if valid, _ := dbp.Valid(); !valid {
continue
}
dbp.suspendAllThreads()
}
lastEventProc := trapproc
for {
var err error
var tid int
lastEventProc.execPtraceFunc(func() {
err = _ContinueDebugEvent(uint32(lastEventProc.pid), uint32(lastEventProc.os.breakThread), _DBG_CONTINUE)
if err == nil {
tid, _ = procgrp.waitForDebugEvent(waitSuspendNewThreads)
}
})
if err != nil {
return nil, err
}
if tid == 0 {
break
}
dbp := procgrp.procForThread(tid)
err = dbp.threads[tid].SetCurrentBreakpoint(true)
if err != nil {
return nil, err
}
lastEventProc = dbp
}
2016-01-15 05:26:54 +00:00
// Check if trapthread still exist, if the process is dying it could have
// been removed while we were stopping the other threads.
trapthreadFound := false
for _, thread := range trapproc.threads {
if thread.ID == trapthread.ID {
trapthreadFound = true
}
if thread.os.delayErr != nil && thread.os.delayErr != syscall.Errno(0x5) {
// Do not report Access is denied error, it is caused by the thread
// having already died but we haven't been notified about it yet.
return nil, thread.os.delayErr
}
}
if !trapthreadFound {
wasDbgUiRemoteBreakIn := trapthread.os.dbgUiRemoteBreakIn
// trapthread exited during stop, pick another one
trapthread = nil
for _, thread := range trapproc.threads {
if thread.CurrentBreakpoint.Breakpoint != nil && thread.os.delayErr == nil {
trapthread = thread
break
}
}
if trapthread == nil && wasDbgUiRemoteBreakIn {
// If this was triggered by a manual stop request we should stop
// regardless, pick a thread.
for _, thread := range trapproc.threads {
return thread, nil
}
}
}
return trapthread, nil
2016-01-15 05:26:54 +00:00
}
func (dbp *nativeProcess) suspendAllThreads() {
context := newContext()
for _, thread := range dbp.threads {
thread.os.delayErr = nil
if !thread.os.dbgUiRemoteBreakIn {
// Wait before reporting the error, the thread could be removed when we
// call waitForDebugEvent in the next loop.
_, thread.os.delayErr = _SuspendThread(thread.os.hThread)
if thread.os.delayErr == nil {
// This call will block until the thread has stopped.
_ = thread.getContext(context)
}
}
}
}
func (procgrp *processGroup) detachChild(dbp *nativeProcess) error {
for _, thread := range dbp.threads {
_, err := _ResumeThread(thread.os.hThread)
if err != nil {
return err
}
}
err := _DebugActiveProcessStop(uint32(dbp.pid))
dbp.detached = true
dbp.postExit()
return err
}
func (dbp *nativeProcess) detach(kill bool) error {
if !kill {
*: Go 1.14 support branch (#1727) * tests: misc test fixes for go1.14 - math.go is now ambiguous due to changes to the go runtime so specify that we mean our own math.go in _fixtures - go list -m requires vendor-mode to be disabled so pass '-mod=' to it in case user has GOFLAGS=-mod=vendor - update version of go/packages, required to work with go 1.14 (and executed go mod vendor) - Increased goroutine migration in one development version of Go 1.14 revealed a problem with TestCheckpoints in command_test.go and rr_test.go. The tests were always wrong because Restart(checkpoint) doesn't change the current thread but we can't assume that when the checkpoint was taken the current goroutine was running on the same thread. * goversion: update maximum supported version * Makefile: disable testing lldb-server backend on linux with Go 1.14 There seems to be some incompatibility with lldb-server version 6.0.0 on linux and Go 1.14. * proc/gdbserial: better handling of signals - if multiple signals are received simultaneously propagate all of them to the target threads instead of only one. - debugserver will drop an interrupt request if a target thread simultaneously receives a signal, handle this situation. * dwarf/line: normalize backslashes for windows executables Starting with Go 1.14 the compiler sometimes emits backslashes as well as forward slashes in debug_line, normalize everything to / for conformity with the behavior of previous versions. * proc/native: partial support for Windows async preempt mechanism See https://github.com/golang/go/issues/36494 for a description of why full support for 1.14 under windows is problematic. * proc/native: disable Go 1.14 async preemption on Windows See https://github.com/golang/go/issues/36494
2020-02-11 01:31:54 +00:00
//TODO(aarzilli): when debug.Target exist Detach should be moved to
// debug.Target and the call to RestoreAsyncPreempt should be moved there.
for _, thread := range dbp.threads {
_, err := _ResumeThread(thread.os.hThread)
if err != nil {
return err
}
}
}
return _DebugActiveProcessStop(uint32(dbp.pid))
}
func (dbp *nativeProcess) EntryPoint() (uint64, error) {
return dbp.os.entryPoint, nil
}
func (dbp *nativeProcess) SupportsBPF() bool {
return false
}
func (dbp *nativeProcess) SetUProbe(fnName string, goidOffset int64, args []ebpf.UProbeArgMap) error {
return nil
}
func (dbp *nativeProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams {
return nil
}
type _PROCESS_BASIC_INFORMATION struct {
ExitStatus sys.NTStatus
PebBaseAddress uintptr
AffinityMask uintptr
BasePriority int32
UniqueProcessId uintptr
InheritedFromUniqueProcessId uintptr
}
type _PEB struct {
reserved1 [2]byte
BeingDebugged byte
BitField byte
reserved3 uintptr
ImageBaseAddress uintptr
Ldr uintptr
ProcessParameters uintptr
reserved4 [3]uintptr
AtlThunkSListPtr uintptr
reserved5 uintptr
reserved6 uint32
reserved7 uintptr
reserved8 uint32
AtlThunkSListPtr32 uint32
reserved9 [45]uintptr
reserved10 [96]byte
PostProcessInitRoutine uintptr
reserved11 [128]byte
reserved12 [1]uintptr
SessionId uint32
}
type _RTL_USER_PROCESS_PARAMETERS struct {
MaximumLength, Length uint32
Flags, DebugFlags uint32
ConsoleHandle sys.Handle
ConsoleFlags uint32
StandardInput, StandardOutput, StandardError sys.Handle
CurrentDirectory struct {
DosPath _NTUnicodeString
Handle sys.Handle
}
DllPath _NTUnicodeString
ImagePathName _NTUnicodeString
CommandLine _NTUnicodeString
Environment unsafe.Pointer
StartingX, StartingY, CountX, CountY, CountCharsX, CountCharsY, FillAttribute uint32
WindowFlags, ShowWindowFlags uint32
WindowTitle, DesktopInfo, ShellInfo, RuntimeData _NTUnicodeString
CurrentDirectories [32]struct {
Flags uint16
Length uint16
TimeStamp uint32
DosPath _NTString
}
EnvironmentSize, EnvironmentVersion uintptr
PackageDependencyData uintptr
ProcessGroupId uint32
LoaderThreads uint32
RedirectionDllName _NTUnicodeString
HeapPartitionName _NTUnicodeString
DefaultThreadpoolCpuSetMasks uintptr
DefaultThreadpoolCpuSetMaskCount uint32
}
type _NTString struct {
Length uint16
MaximumLength uint16
Buffer uintptr
}
type _NTUnicodeString struct {
Length uint16
MaximumLength uint16
Buffer uintptr
}
func getCmdLine(hProcess syscall.Handle) string {
logger := logflags.DebuggerLogger()
var info _PROCESS_BASIC_INFORMATION
err := sys.NtQueryInformationProcess(sys.Handle(hProcess), sys.ProcessBasicInformation, unsafe.Pointer(&info), uint32(unsafe.Sizeof(info)), nil)
if err != nil {
logger.Errorf("NtQueryInformationProcess: %v", err)
return ""
}
var peb _PEB
err = _ReadProcessMemory(hProcess, info.PebBaseAddress, (*byte)(unsafe.Pointer(&peb)), unsafe.Sizeof(peb), nil)
if err != nil {
logger.Errorf("Reading PEB: %v", err)
return ""
}
var upp _RTL_USER_PROCESS_PARAMETERS
err = _ReadProcessMemory(hProcess, peb.ProcessParameters, (*byte)(unsafe.Pointer(&upp)), unsafe.Sizeof(upp), nil)
if err != nil {
logger.Errorf("Reading ProcessParameters: %v", err)
return ""
}
if upp.CommandLine.Length%2 != 0 {
logger.Errorf("CommandLine length not a multiple of 2")
return ""
}
buf := make([]byte, upp.CommandLine.Length)
err = _ReadProcessMemory(hProcess, upp.CommandLine.Buffer, &buf[0], uintptr(len(buf)), nil)
if err != nil {
logger.Errorf("Reading CommandLine: %v", err)
return ""
}
utf16buf := make([]uint16, len(buf)/2)
for i := 0; i < len(buf); i += 2 {
utf16buf[i/2] = uint16(buf[i+1])<<8 + uint16(buf[i])
}
return string(utf16.Decode(utf16buf))
}
// FollowExec enables (or disables) follow exec mode
func (dbp *nativeProcess) FollowExec(v bool) error {
dbp.followExec = v
return nil
}
2016-01-15 05:26:54 +00:00
func killProcess(pid int) error {
p, err := os.FindProcess(pid)
if err != nil {
return err
}
defer p.Release()
return p.Kill()
2016-01-15 05:26:54 +00:00
}