Skip to content

Commit

Permalink
proc/native: disable Go 1.14 async preemption on Windows
Browse files Browse the repository at this point in the history
  • Loading branch information
aarzilli committed Jan 21, 2020
1 parent 8a54f13 commit 0cf32a6
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 5 deletions.
2 changes: 1 addition & 1 deletion pkg/proc/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ func OpenCore(corePath, exePath string, debugInfoDirs []string) (*Process, error
// initialize for core files doesn't do much
// aside from call the post initialization setup.
func (p *Process) initialize(path string, debugInfoDirs []string) error {
return proc.PostInitializationSetup(p, path, debugInfoDirs, p.writeBreakpoint)
return proc.PostInitializationSetup(p, path, debugInfoDirs, p.writeBreakpoint, false)
}

// BinInfo will return the binary info.
Expand Down
2 changes: 1 addition & 1 deletion pkg/proc/gdbserial/gdbserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ func (p *Process) initialize(path string, debugInfoDirs []string) error {
return err
}
}
if err = proc.PostInitializationSetup(p, path, debugInfoDirs, p.writeBreakpoint); err != nil {
if err = proc.PostInitializationSetup(p, path, debugInfoDirs, p.writeBreakpoint, false); err != nil {
p.conn.conn.Close()
return err
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/proc/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,9 @@ type CommonProcess struct {
goroutineCache

fncallEnabled bool
fncallForG map[int]*callInjection

fncallForG map[int]*callInjection
asyncPreemptOff int64 // cached value of runtime/debug.asyncpreemptoff
}

type goroutineCache struct {
Expand Down
2 changes: 1 addition & 1 deletion pkg/proc/native/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ func (dbp *Process) initialize(path string, debugInfoDirs []string) error {
if err := dbp.updateThreadList(); err != nil {
return err
}
return proc.PostInitializationSetup(dbp, path, debugInfoDirs, dbp.writeBreakpoint)
return proc.PostInitializationSetup(dbp, path, debugInfoDirs, dbp.writeBreakpoint, runtime.GOOS == "windows")
}

// SetSelectedGoroutine will set internally the goroutine that should be
Expand Down
14 changes: 14 additions & 0 deletions pkg/proc/native/proc_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"unsafe"

Expand Down Expand Up @@ -55,6 +56,15 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, err
}
closer.Close()

env := os.Environ()
for i := range env {
if strings.HasPrefix(env[i], "GODEBUG=") {
// Go 1.14 asynchronous preemption mechanism is incompatible with
// debuggers, see: https://github.com/golang/go/issues/36494
env[i] += ",asyncpreemptoff=1"
}
}

var p *os.Process
dbp := New(0)
dbp.common = proc.NewCommonProcess(true)
Expand All @@ -65,6 +75,7 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, err
Sys: &syscall.SysProcAttr{
CreationFlags: _DEBUG_ONLY_THIS_PROCESS,
},
Env: env,
}
p, err = os.StartProcess(argv0Go, cmd, attr)
})
Expand Down Expand Up @@ -473,6 +484,9 @@ func (dbp *Process) stop(trapthread *Thread) (err error) {

func (dbp *Process) detach(kill bool) error {
if !kill {
//TODO(aarzilli): when debug.Target exist Detach should be moved to
// debug.Target and the call to RestoreAsyncPreempt should be moved there.
proc.RestoreAsyncPreempt(dbp)
for _, thread := range dbp.threads {
_, err := _ResumeThread(thread.os.hThread)
if err != nil {
Expand Down
40 changes: 39 additions & 1 deletion pkg/proc/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import (
"errors"
"fmt"
"go/ast"
"go/constant"
"go/token"
"path/filepath"
"strconv"

"github.com/go-delve/delve/pkg/goversion"
)

// ErrNotExecutable is returned after attempting to execute a non-executable file
Expand Down Expand Up @@ -52,7 +55,7 @@ func (pe ProcessDetachedError) Error() string {

// PostInitializationSetup handles all of the initialization procedures
// that must happen after Delve creates or attaches to a process.
func PostInitializationSetup(p Process, path string, debugInfoDirs []string, writeBreakpoint WriteBreakpointFn) error {
func PostInitializationSetup(p Process, path string, debugInfoDirs []string, writeBreakpoint WriteBreakpointFn, disableAsyncPreempt bool) error {
entryPoint, err := p.EntryPoint()
if err != nil {
return err
Expand All @@ -76,6 +79,10 @@ func PostInitializationSetup(p Process, path string, debugInfoDirs []string, wri

p.Common().goroutineCache.init(p.BinInfo())

if disableAsyncPreempt {
setAsyncPreemptOff(p, 1)
}

return nil
}

Expand Down Expand Up @@ -819,3 +826,34 @@ func FirstPCAfterPrologue(p Process, fn *Function, sameline bool) (uint64, error

return pc, nil
}

func setAsyncPreemptOff(p Process, v int64) {
logger := p.BinInfo().logger
if producer := p.BinInfo().Producer(); producer == "" || !goversion.ProducerAfterOrEqual(producer, 1, 14) {
return
}
scope := globalScope(p.BinInfo(), p.BinInfo().Images[0], p.CurrentThread())
debugv, err := scope.findGlobal("runtime", "debug")
if err != nil || debugv.Unreadable != nil {
logger.Warnf("could not find runtime/debug variable (or unreadable): %v %v", err, debugv.Unreadable)
return
}
asyncpreemptoffv, err := debugv.structMember("asyncpreemptoff")
if err != nil {
logger.Warnf("could not find asyncpreemptoff field: %v", err)
return
}
asyncpreemptoffv.loadValue(loadFullValue)
if asyncpreemptoffv.Unreadable != nil {
logger.Warnf("asyncpreemptoff field unreadable: %v", asyncpreemptoffv.Unreadable)
return
}
p.Common().asyncPreemptOff, _ = constant.Int64Val(asyncpreemptoffv.Value)

err = scope.setValue(asyncpreemptoffv, newConstant(constant.MakeInt64(v), scope.Mem), "")
logger.Warnf("could not set asyncpreemptoff %v", err)
}

func RestoreAsyncPreempt(p Process) {
setAsyncPreemptOff(p, p.Common().asyncPreemptOff)
}

0 comments on commit 0cf32a6

Please sign in to comment.