proc/native: fix FreeBSD backend (#3224)
- use PT_SUSPEND/PT_RESUME to control running threads in resume/stop/singleStep - change manual stop signal from SIGTRAP to SIGSTOP to make manual stop handling simpler - change (*nativeProcess).trapWaitInternal to suspend newly created threads when we are stepping a thread - change (*nativeProcess).trapWaitInternal to handle some unhandled stop events - remove misleading (*nativeProcess).waitFast which does not do anything different from the normal wait variant - rewrite (*nativeProcess).stop to only set breakpoints for threads of which we have received SIGTRAP - rewrite (*nativeThread).singleStep to actually execute a single instruction and to properly route signals
This commit is contained in:
parent
32df4071d2
commit
00df758d57
@ -11,8 +11,7 @@ Tests skipped by each supported backend:
|
|||||||
* 1 broken - cgo stacktraces
|
* 1 broken - cgo stacktraces
|
||||||
* darwin/lldb skipped = 1
|
* darwin/lldb skipped = 1
|
||||||
* 1 upstream issue
|
* 1 upstream issue
|
||||||
* freebsd skipped = 16
|
* freebsd skipped = 4
|
||||||
* 12 broken
|
|
||||||
* 4 not implemented
|
* 4 not implemented
|
||||||
* linux/386/pie skipped = 1
|
* linux/386/pie skipped = 1
|
||||||
* 1 broken
|
* 1 broken
|
||||||
|
@ -1036,11 +1036,6 @@ func TestTrace(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestTraceMultipleGoroutines(t *testing.T) {
|
func TestTraceMultipleGoroutines(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
//TODO(aarzilli): investigate further when the FreeBSD backend is more stable.
|
|
||||||
t.Skip("temporarily disabled due to issues with FreeBSD in Delve and Go")
|
|
||||||
}
|
|
||||||
|
|
||||||
dlvbin, tmpdir := getDlvBin(t)
|
dlvbin, tmpdir := getDlvBin(t)
|
||||||
defer os.RemoveAll(tmpdir)
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
|
@ -246,14 +246,12 @@ func (dbp *nativeProcess) initialize(path string, debugInfoDirs []string) (*proc
|
|||||||
// We disable asyncpreempt for the following reasons:
|
// We disable asyncpreempt for the following reasons:
|
||||||
// - on Windows asyncpreempt is incompatible with debuggers, see:
|
// - on Windows asyncpreempt is incompatible with debuggers, see:
|
||||||
// https://github.com/golang/go/issues/36494
|
// https://github.com/golang/go/issues/36494
|
||||||
// - freebsd's backend is generally broken and asyncpreempt makes it even more so, see:
|
|
||||||
// https://github.com/go-delve/delve/issues/1754
|
|
||||||
// - on linux/arm64 asyncpreempt can sometimes restart a sequence of
|
// - on linux/arm64 asyncpreempt can sometimes restart a sequence of
|
||||||
// instructions, if the sequence happens to contain a breakpoint it will
|
// instructions, if the sequence happens to contain a breakpoint it will
|
||||||
// look like the breakpoint was hit twice when it was "logically" only
|
// look like the breakpoint was hit twice when it was "logically" only
|
||||||
// executed once.
|
// executed once.
|
||||||
// See: https://go-review.googlesource.com/c/go/+/208126
|
// See: https://go-review.googlesource.com/c/go/+/208126
|
||||||
DisableAsyncPreempt: runtime.GOOS == "windows" || runtime.GOOS == "freebsd" || (runtime.GOOS == "linux" && runtime.GOARCH == "arm64"),
|
DisableAsyncPreempt: runtime.GOOS == "windows" || (runtime.GOOS == "linux" && runtime.GOARCH == "arm64"),
|
||||||
|
|
||||||
StopReason: stopReason,
|
StopReason: stopReason,
|
||||||
CanDump: runtime.GOOS == "linux" || (runtime.GOOS == "windows" && runtime.GOARCH == "amd64"),
|
CanDump: runtime.GOOS == "linux" || (runtime.GOOS == "windows" && runtime.GOARCH == "amd64"),
|
||||||
|
@ -42,6 +42,10 @@ const (
|
|||||||
type osProcessDetails struct {
|
type osProcessDetails struct {
|
||||||
comm string
|
comm string
|
||||||
tid int
|
tid int
|
||||||
|
|
||||||
|
delayedSignal syscall.Signal
|
||||||
|
trapThreads []int
|
||||||
|
selectedThread *nativeThread
|
||||||
}
|
}
|
||||||
|
|
||||||
func (os *osProcessDetails) Close() {}
|
func (os *osProcessDetails) Close() {}
|
||||||
@ -83,7 +87,6 @@ func Launch(cmd []string, wd string, flags proc.LaunchFlags, debugInfoDirs []str
|
|||||||
process.Stdout = stdout
|
process.Stdout = stdout
|
||||||
process.Stderr = stderr
|
process.Stderr = stderr
|
||||||
process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true, Foreground: foreground}
|
process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true, Foreground: foreground}
|
||||||
process.Env = proc.DisableAsyncPreemptEnv()
|
|
||||||
if foreground {
|
if foreground {
|
||||||
signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
|
signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
|
||||||
}
|
}
|
||||||
@ -152,7 +155,12 @@ func (dbp *nativeProcess) kill() (err error) {
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
dbp.execPtraceFunc(func() { err = ptraceCont(dbp.pid, int(sys.SIGKILL)) })
|
dbp.execPtraceFunc(func() {
|
||||||
|
for _, th := range dbp.threads {
|
||||||
|
ptraceResume(th.ID)
|
||||||
|
}
|
||||||
|
err = ptraceCont(dbp.pid, int(sys.SIGKILL))
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -165,7 +173,7 @@ func (dbp *nativeProcess) kill() (err error) {
|
|||||||
|
|
||||||
// Used by RequestManualStop
|
// Used by RequestManualStop
|
||||||
func (dbp *nativeProcess) requestManualStop() (err error) {
|
func (dbp *nativeProcess) requestManualStop() (err error) {
|
||||||
return sys.Kill(dbp.pid, sys.SIGTRAP)
|
return sys.Kill(dbp.pid, sys.SIGSTOP)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach to a newly created thread, and store that thread in our list of
|
// Attach to a newly created thread, and store that thread in our list of
|
||||||
@ -178,7 +186,7 @@ func (dbp *nativeProcess) addThread(tid int, attach bool) (*nativeThread, error)
|
|||||||
var err error
|
var err error
|
||||||
dbp.execPtraceFunc(func() { err = sys.PtraceLwpEvents(dbp.pid, 1) })
|
dbp.execPtraceFunc(func() { err = sys.PtraceLwpEvents(dbp.pid, 1) })
|
||||||
if err == syscall.ESRCH {
|
if err == syscall.ESRCH {
|
||||||
if _, _, err = dbp.waitFast(dbp.pid); err != nil {
|
if _, _, err = dbp.wait(dbp.pid, 0); err != nil {
|
||||||
return nil, fmt.Errorf("error while waiting after adding process: %d %s", dbp.pid, err)
|
return nil, fmt.Errorf("error while waiting after adding process: %d %s", dbp.pid, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,13 +228,29 @@ func findExecutable(path string, pid int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *nativeProcess) trapWait(pid int) (*nativeThread, error) {
|
func (dbp *nativeProcess) trapWait(pid int) (*nativeThread, error) {
|
||||||
return dbp.trapWaitInternal(pid, false)
|
return dbp.trapWaitInternal(pid, trapWaitNormal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type trapWaitMode uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
trapWaitNormal trapWaitMode = iota
|
||||||
|
trapWaitStepping
|
||||||
|
)
|
||||||
|
|
||||||
// Used by stop and trapWait
|
// Used by stop and trapWait
|
||||||
func (dbp *nativeProcess) trapWaitInternal(pid int, halt bool) (*nativeThread, error) {
|
func (dbp *nativeProcess) trapWaitInternal(pid int, mode trapWaitMode) (*nativeThread, error) {
|
||||||
|
if dbp.os.selectedThread != nil {
|
||||||
|
th := dbp.os.selectedThread
|
||||||
|
dbp.os.selectedThread = nil
|
||||||
|
return th, nil
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
wpid, status, err := dbp.wait(pid, 0)
|
wpid, status, err := dbp.wait(pid, 0)
|
||||||
|
if wpid != dbp.pid {
|
||||||
|
// possibly a delayed notification from a process we just detached and killed, freebsd bug?
|
||||||
|
continue
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("wait err %s %d", err, pid)
|
return nil, fmt.Errorf("wait err %s %d", err, pid)
|
||||||
}
|
}
|
||||||
@ -239,6 +263,11 @@ func (dbp *nativeProcess) trapWaitInternal(pid int, halt bool) (*nativeThread, e
|
|||||||
dbp.postExit()
|
dbp.postExit()
|
||||||
return nil, proc.ErrProcessExited{Pid: wpid, Status: status.ExitStatus()}
|
return nil, proc.ErrProcessExited{Pid: wpid, Status: status.ExitStatus()}
|
||||||
}
|
}
|
||||||
|
if status.Signaled() {
|
||||||
|
// Killed by a signal
|
||||||
|
dbp.postExit()
|
||||||
|
return nil, proc.ErrProcessExited{Pid: wpid, Status: -int(status.Signal())}
|
||||||
|
}
|
||||||
|
|
||||||
var info sys.PtraceLwpInfoStruct
|
var info sys.PtraceLwpInfoStruct
|
||||||
dbp.execPtraceFunc(func() { info, err = ptraceGetLwpInfo(wpid) })
|
dbp.execPtraceFunc(func() { info, err = ptraceGetLwpInfo(wpid) })
|
||||||
@ -269,10 +298,10 @@ func (dbp *nativeProcess) trapWaitInternal(pid int, halt bool) (*nativeThread, e
|
|||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if halt {
|
if mode == trapWaitStepping {
|
||||||
return nil, nil
|
dbp.execPtraceFunc(func() { ptraceSuspend(tid) })
|
||||||
}
|
}
|
||||||
if err = th.Continue(); err != nil {
|
if err = dbp.ptraceCont(0); err != nil {
|
||||||
if err == sys.ESRCH {
|
if err == sys.ESRCH {
|
||||||
// thread died while we were adding it
|
// thread died while we were adding it
|
||||||
delete(dbp.threads, int(tid))
|
delete(dbp.threads, int(tid))
|
||||||
@ -288,12 +317,16 @@ func (dbp *nativeProcess) trapWaitInternal(pid int, halt bool) (*nativeThread, e
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (halt && status.StopSignal() == sys.SIGSTOP) || (status.StopSignal() == sys.SIGTRAP) {
|
if mode == trapWaitStepping {
|
||||||
|
return th, nil
|
||||||
|
}
|
||||||
|
if status.StopSignal() == sys.SIGTRAP || status.Continued() {
|
||||||
|
// Continued in this case means we received the SIGSTOP signal
|
||||||
return th, nil
|
return th, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(dp) alert user about unexpected signals here.
|
// TODO(dp) alert user about unexpected signals here.
|
||||||
if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
|
if err := dbp.ptraceCont(int(status.StopSignal())); err != nil {
|
||||||
if err == sys.ESRCH {
|
if err == sys.ESRCH {
|
||||||
return nil, proc.ErrProcessExited{Pid: dbp.pid}
|
return nil, proc.ErrProcessExited{Pid: dbp.pid}
|
||||||
}
|
}
|
||||||
@ -309,14 +342,6 @@ func status(pid int) rune {
|
|||||||
return status
|
return status
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used by stop and singleStep
|
|
||||||
// waitFast is like wait but does not handle process-exit correctly
|
|
||||||
func (dbp *nativeProcess) waitFast(pid int) (int, *sys.WaitStatus, error) {
|
|
||||||
var s sys.WaitStatus
|
|
||||||
wpid, err := sys.Wait4(pid, &s, 0, nil)
|
|
||||||
return wpid, &s, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only used in this file
|
// Only used in this file
|
||||||
func (dbp *nativeProcess) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
func (dbp *nativeProcess) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||||
var s sys.WaitStatus
|
var s sys.WaitStatus
|
||||||
@ -330,7 +355,7 @@ func (dbp *nativeProcess) exitGuard(err error) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if status(dbp.pid) == statusZombie {
|
if status(dbp.pid) == statusZombie {
|
||||||
_, err := dbp.trapWaitInternal(-1, false)
|
_, err := dbp.trapWaitInternal(-1, trapWaitNormal)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,9 +373,31 @@ func (dbp *nativeProcess) resume() error {
|
|||||||
thread.CurrentBreakpoint.Clear()
|
thread.CurrentBreakpoint.Clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(dbp.os.trapThreads) > 0 {
|
||||||
|
// On these threads we have already received a SIGTRAP while stepping on a different thread,
|
||||||
|
// do not resume the process, instead record the breakpoint hits on them.
|
||||||
|
tt := dbp.os.trapThreads
|
||||||
|
dbp.os.trapThreads = dbp.os.trapThreads[:0]
|
||||||
|
var err error
|
||||||
|
dbp.os.selectedThread, err = dbp.postStop(tt...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
// all threads are resumed
|
// all threads are resumed
|
||||||
var err error
|
var err error
|
||||||
dbp.execPtraceFunc(func() { err = ptraceCont(dbp.pid, 0) })
|
dbp.execPtraceFunc(func() {
|
||||||
|
for _, th := range dbp.threads {
|
||||||
|
err = ptraceResume(th.ID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sig := int(dbp.os.delayedSignal)
|
||||||
|
dbp.os.delayedSignal = 0
|
||||||
|
for _, thread := range dbp.threads {
|
||||||
|
thread.Status = nil
|
||||||
|
}
|
||||||
|
err = ptraceCont(dbp.pid, sig)
|
||||||
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,19 +407,72 @@ func (dbp *nativeProcess) stop(cctx *proc.ContinueOnceContext, trapthread *nativ
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return nil, proc.ErrProcessExited{Pid: dbp.pid}
|
return nil, proc.ErrProcessExited{Pid: dbp.pid}
|
||||||
}
|
}
|
||||||
// set breakpoints on all threads
|
|
||||||
for _, th := range dbp.threads {
|
var err error
|
||||||
if th.CurrentBreakpoint.Breakpoint == nil {
|
dbp.execPtraceFunc(func() {
|
||||||
if err := th.SetCurrentBreakpoint(true); err != nil {
|
for _, th := range dbp.threads {
|
||||||
return nil, err
|
err = ptraceSuspend(th.ID)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return trapthread, nil
|
|
||||||
|
if trapthread.Status == nil || (*sys.WaitStatus)(trapthread.Status).StopSignal() != sys.SIGTRAP {
|
||||||
|
return trapthread, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return dbp.postStop(trapthread.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbp *nativeProcess) postStop(tids ...int) (*nativeThread, error) {
|
||||||
|
var pickedTrapThread *nativeThread
|
||||||
|
for _, tid := range tids {
|
||||||
|
trapthread := dbp.threads[tid]
|
||||||
|
if trapthread == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must either be a hardcoded breakpoint, a currently set breakpoint or a
|
||||||
|
// phantom breakpoint hit caused by a SIGTRAP that was delayed.
|
||||||
|
// If someone sent a SIGTRAP directly to the process this will fail, but we
|
||||||
|
// can't do better.
|
||||||
|
|
||||||
|
err := trapthread.SetCurrentBreakpoint(true)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if trapthread.CurrentBreakpoint.Breakpoint == nil {
|
||||||
|
// hardcoded breakpoint or phantom breakpoint hit
|
||||||
|
if dbp.BinInfo().Arch.BreakInstrMovesPC() {
|
||||||
|
pc, _ := trapthread.PC()
|
||||||
|
if !trapthread.atHardcodedBreakpoint(pc) {
|
||||||
|
// phantom breakpoint hit
|
||||||
|
_ = trapthread.setPC(pc - uint64(len(dbp.BinInfo().Arch.BreakpointInstruction())))
|
||||||
|
trapthread = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pickedTrapThread == nil && trapthread != nil {
|
||||||
|
pickedTrapThread = trapthread
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pickedTrapThread, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used by Detach
|
// Used by Detach
|
||||||
func (dbp *nativeProcess) detach(kill bool) error {
|
func (dbp *nativeProcess) detach(kill bool) error {
|
||||||
|
if !kill {
|
||||||
|
for _, th := range dbp.threads {
|
||||||
|
ptraceResume(th.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
return ptraceDetach(dbp.pid)
|
return ptraceDetach(dbp.pid)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,6 +495,12 @@ func (dbp *nativeProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams {
|
|||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dbp *nativeProcess) ptraceCont(sig int) error {
|
||||||
|
var err error
|
||||||
|
dbp.execPtraceFunc(func() { err = ptraceCont(dbp.pid, sig) })
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Usedy by Detach
|
// Usedy by Detach
|
||||||
func killProcess(pid int) error {
|
func killProcess(pid int) error {
|
||||||
return sys.Kill(pid, sys.SIGINT)
|
return sys.Kill(pid, sys.SIGINT)
|
||||||
|
@ -10,6 +10,7 @@ import "C"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
sys "golang.org/x/sys/unix"
|
sys "golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
@ -66,3 +67,35 @@ func ptraceReadData(id int, addr uintptr, data []byte) (n int, err error) {
|
|||||||
func ptraceWriteData(id int, addr uintptr, data []byte) (n int, err error) {
|
func ptraceWriteData(id int, addr uintptr, data []byte) (n int, err error) {
|
||||||
return sys.PtraceIO(sys.PIOD_WRITE_D, id, addr, data, len(data))
|
return sys.PtraceIO(sys.PIOD_WRITE_D, id, addr, data, len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ptraceSuspend(id int) error {
|
||||||
|
_, _, e1 := sys.Syscall6(sys.SYS_PTRACE, uintptr(C.PT_SUSPEND), uintptr(id), uintptr(1), uintptr(0), 0, 0)
|
||||||
|
if e1 != 0 {
|
||||||
|
return syscall.Errno(e1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptraceResume(id int) error {
|
||||||
|
_, _, e1 := sys.Syscall6(sys.SYS_PTRACE, uintptr(C.PT_RESUME), uintptr(id), uintptr(1), uintptr(0), 0, 0)
|
||||||
|
if e1 != 0 {
|
||||||
|
return syscall.Errno(e1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptraceSetStep(id int) error {
|
||||||
|
_, _, e1 := sys.Syscall6(sys.SYS_PTRACE, uintptr(C.PT_SETSTEP), uintptr(id), uintptr(0), uintptr(0), 0, 0)
|
||||||
|
if e1 != 0 {
|
||||||
|
return syscall.Errno(e1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ptraceClearStep(id int) error {
|
||||||
|
_, _, e1 := sys.Syscall6(sys.SYS_PTRACE, uintptr(C.PT_CLEARSTEP), uintptr(id), uintptr(0), uintptr(0), 0, 0)
|
||||||
|
if e1 != 0 {
|
||||||
|
return syscall.Errno(e1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -22,26 +22,6 @@ type nativeThread struct {
|
|||||||
common proc.CommonThread
|
common proc.CommonThread
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue the execution of this thread.
|
|
||||||
//
|
|
||||||
// If we are currently at a breakpoint, we'll clear it
|
|
||||||
// first and then resume execution. Thread will continue until
|
|
||||||
// it hits a breakpoint or is signaled.
|
|
||||||
func (t *nativeThread) Continue() error {
|
|
||||||
pc, err := t.PC()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Check whether we are stopped at a breakpoint, and
|
|
||||||
// if so, single step over it before continuing.
|
|
||||||
if _, ok := t.dbp.FindBreakpoint(pc, false); ok {
|
|
||||||
if err := t.StepInstruction(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return t.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
// StepInstruction steps a single instruction.
|
// StepInstruction steps a single instruction.
|
||||||
//
|
//
|
||||||
// Executes exactly one instruction and then returns.
|
// Executes exactly one instruction and then returns.
|
||||||
|
@ -143,3 +143,23 @@ func (t *nativeThread) withDebugRegisters(f func(*amd64util.DebugRegisters) erro
|
|||||||
func (t *nativeThread) SoftExc() bool {
|
func (t *nativeThread) SoftExc() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Continue the execution of this thread.
|
||||||
|
//
|
||||||
|
// If we are currently at a breakpoint, we'll clear it
|
||||||
|
// first and then resume execution. Thread will continue until
|
||||||
|
// it hits a breakpoint or is signaled.
|
||||||
|
func (t *nativeThread) Continue() error {
|
||||||
|
pc, err := t.PC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Check whether we are stopped at a breakpoint, and
|
||||||
|
// if so, single step over it before continuing.
|
||||||
|
if _, ok := t.dbp.FindBreakpoint(pc, false); ok {
|
||||||
|
if err := t.StepInstruction(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t.resume()
|
||||||
|
}
|
||||||
|
@ -1,16 +1,13 @@
|
|||||||
package native
|
package native
|
||||||
|
|
||||||
// #include <sys/thr.h>
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"bytes"
|
||||||
"github.com/go-delve/delve/pkg/proc/fbsdutil"
|
|
||||||
|
|
||||||
sys "golang.org/x/sys/unix"
|
sys "golang.org/x/sys/unix"
|
||||||
|
|
||||||
"github.com/go-delve/delve/pkg/proc"
|
"github.com/go-delve/delve/pkg/proc"
|
||||||
"github.com/go-delve/delve/pkg/proc/amd64util"
|
"github.com/go-delve/delve/pkg/proc/amd64util"
|
||||||
|
"github.com/go-delve/delve/pkg/proc/fbsdutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type waitStatus sys.WaitStatus
|
type waitStatus sys.WaitStatus
|
||||||
@ -20,59 +17,58 @@ type osSpecificDetails struct {
|
|||||||
registers sys.Reg
|
registers sys.Reg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *nativeThread) stop() (err error) {
|
|
||||||
_, err = C.thr_kill2(C.pid_t(t.dbp.pid), C.long(t.ID), C.int(sys.SIGSTOP))
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("stop err %s on thread %d", err, t.ID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// If the process is stopped, we must continue it so it can receive the
|
|
||||||
// signal
|
|
||||||
t.dbp.execPtraceFunc(func() { err = ptraceCont(t.dbp.pid, 0) })
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_, _, err = t.dbp.waitFast(t.dbp.pid)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("wait err %s on thread %d", err, t.ID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *nativeThread) Stopped() bool {
|
|
||||||
state := status(t.dbp.pid)
|
|
||||||
return state == statusStopped
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *nativeThread) resume() error {
|
|
||||||
return t.resumeWithSig(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *nativeThread) resumeWithSig(sig int) (err error) {
|
|
||||||
t.dbp.execPtraceFunc(func() { err = ptraceCont(t.ID, sig) })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *nativeThread) singleStep() (err error) {
|
func (t *nativeThread) singleStep() (err error) {
|
||||||
t.dbp.execPtraceFunc(func() { err = ptraceSingleStep(t.ID) })
|
t.dbp.execPtraceFunc(func() { err = ptraceSetStep(t.ID) })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer func() {
|
||||||
|
t.dbp.execPtraceFunc(func() { ptraceClearStep(t.ID) })
|
||||||
|
}()
|
||||||
|
|
||||||
|
t.dbp.execPtraceFunc(func() { err = ptraceResume(t.ID) })
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
t.dbp.execPtraceFunc(func() { ptraceSuspend(t.ID) })
|
||||||
|
}()
|
||||||
|
|
||||||
|
sig := 0
|
||||||
for {
|
for {
|
||||||
th, err := t.dbp.trapWait(t.dbp.pid)
|
err = t.dbp.ptraceCont(sig)
|
||||||
|
sig = 0
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if th.ID == t.ID {
|
|
||||||
break
|
trapthread, err := t.dbp.trapWaitInternal(-1, trapWaitStepping)
|
||||||
}
|
|
||||||
t.dbp.execPtraceFunc(func() { err = ptraceCont(th.ID, 0) })
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status := ((*sys.WaitStatus)(trapthread.Status))
|
||||||
|
|
||||||
|
if trapthread.ID == t.ID {
|
||||||
|
switch s := status.StopSignal(); s {
|
||||||
|
case sys.SIGTRAP:
|
||||||
|
return nil
|
||||||
|
case sys.SIGSTOP:
|
||||||
|
// delayed SIGSTOP, ignore it
|
||||||
|
case sys.SIGILL, sys.SIGBUS, sys.SIGFPE, sys.SIGSEGV:
|
||||||
|
// propagate signals that can be caused by current instruction
|
||||||
|
sig = int(s)
|
||||||
|
default:
|
||||||
|
t.dbp.os.delayedSignal = s
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if status.StopSignal() == sys.SIGTRAP {
|
||||||
|
t.dbp.os.trapThreads = append(t.dbp.os.trapThreads, trapthread.ID)
|
||||||
|
} else {
|
||||||
|
t.dbp.os.delayedSignal = status.StopSignal()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *nativeThread) restoreRegisters(savedRegs proc.Registers) error {
|
func (t *nativeThread) restoreRegisters(savedRegs proc.Registers) error {
|
||||||
@ -110,3 +106,19 @@ func (t *nativeThread) withDebugRegisters(f func(*amd64util.DebugRegisters) erro
|
|||||||
func (t *nativeThread) SoftExc() bool {
|
func (t *nativeThread) SoftExc() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *nativeThread) atHardcodedBreakpoint(pc uint64) bool {
|
||||||
|
for _, bpinstr := range [][]byte{
|
||||||
|
t.dbp.BinInfo().Arch.BreakpointInstruction(),
|
||||||
|
t.dbp.BinInfo().Arch.AltBreakpointInstruction()} {
|
||||||
|
if bpinstr == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf := make([]byte, len(bpinstr))
|
||||||
|
_, _ = t.ReadMemory(buf, pc-uint64(len(buf)))
|
||||||
|
if bytes.Equal(buf, bpinstr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -120,3 +120,23 @@ func (t *nativeThread) ReadMemory(data []byte, addr uint64) (n int, err error) {
|
|||||||
func (t *nativeThread) SoftExc() bool {
|
func (t *nativeThread) SoftExc() bool {
|
||||||
return t.os.setbp
|
return t.os.setbp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Continue the execution of this thread.
|
||||||
|
//
|
||||||
|
// If we are currently at a breakpoint, we'll clear it
|
||||||
|
// first and then resume execution. Thread will continue until
|
||||||
|
// it hits a breakpoint or is signaled.
|
||||||
|
func (t *nativeThread) Continue() error {
|
||||||
|
pc, err := t.PC()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Check whether we are stopped at a breakpoint, and
|
||||||
|
// if so, single step over it before continuing.
|
||||||
|
if _, ok := t.dbp.FindBreakpoint(pc, false); ok {
|
||||||
|
if err := t.StepInstruction(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t.resume()
|
||||||
|
}
|
||||||
|
@ -107,22 +107,6 @@ func (t *nativeThread) singleStep() error {
|
|||||||
return t.setContext(context)
|
return t.setContext(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *nativeThread) resume() error {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stopped returns whether the thread is stopped at the operating system
|
|
||||||
// level. On windows this always returns true.
|
|
||||||
func (t *nativeThread) Stopped() bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *nativeThread) WriteMemory(addr uint64, data []byte) (int, error) {
|
func (t *nativeThread) WriteMemory(addr uint64, data []byte) (int, error) {
|
||||||
if t.dbp.exited {
|
if t.dbp.exited {
|
||||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||||
|
@ -586,7 +586,6 @@ func TestNextGeneral(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNextConcurrent(t *testing.T) {
|
func TestNextConcurrent(t *testing.T) {
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
testcases := []nextTest{
|
testcases := []nextTest{
|
||||||
{8, 9},
|
{8, 9},
|
||||||
{9, 10},
|
{9, 10},
|
||||||
@ -622,7 +621,6 @@ func TestNextConcurrent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNextConcurrentVariant2(t *testing.T) {
|
func TestNextConcurrentVariant2(t *testing.T) {
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
// Just like TestNextConcurrent but instead of removing the initial breakpoint we check that when it happens is for other goroutines
|
// Just like TestNextConcurrent but instead of removing the initial breakpoint we check that when it happens is for other goroutines
|
||||||
testcases := []nextTest{
|
testcases := []nextTest{
|
||||||
{8, 9},
|
{8, 9},
|
||||||
@ -1472,7 +1470,6 @@ func TestIssue325(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestBreakpointCounts(t *testing.T) {
|
func TestBreakpointCounts(t *testing.T) {
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("bpcountstest", t, func(p *proc.Target, fixture protest.Fixture) {
|
withTestProcess("bpcountstest", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
bp := setFileBreakpoint(p, t, fixture.Source, 12)
|
bp := setFileBreakpoint(p, t, fixture.Source, 12)
|
||||||
@ -1504,7 +1501,6 @@ func TestBreakpointCounts(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHardcodedBreakpointCounts(t *testing.T) {
|
func TestHardcodedBreakpointCounts(t *testing.T) {
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
withTestProcess("hcbpcountstest", t, func(p *proc.Target, fixture protest.Fixture) {
|
withTestProcess("hcbpcountstest", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
counts := map[int64]int{}
|
counts := map[int64]int{}
|
||||||
for {
|
for {
|
||||||
@ -1716,7 +1712,6 @@ func BenchmarkLocalVariables(b *testing.B) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCondBreakpoint(t *testing.T) {
|
func TestCondBreakpoint(t *testing.T) {
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
|
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
bp := setFileBreakpoint(p, t, fixture.Source, 9)
|
bp := setFileBreakpoint(p, t, fixture.Source, 9)
|
||||||
@ -1738,7 +1733,6 @@ func TestCondBreakpoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCondBreakpointError(t *testing.T) {
|
func TestCondBreakpointError(t *testing.T) {
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
|
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
bp := setFileBreakpoint(p, t, fixture.Source, 9)
|
bp := setFileBreakpoint(p, t, fixture.Source, 9)
|
||||||
@ -2101,7 +2095,6 @@ func TestIssue462(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNextParked(t *testing.T) {
|
func TestNextParked(t *testing.T) {
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
|
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
bp := setFunctionBreakpoint(p, t, "main.sayhi")
|
bp := setFunctionBreakpoint(p, t, "main.sayhi")
|
||||||
@ -2152,7 +2145,6 @@ func TestNextParked(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStepParked(t *testing.T) {
|
func TestStepParked(t *testing.T) {
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
|
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
bp := setFunctionBreakpoint(p, t, "main.sayhi")
|
bp := setFunctionBreakpoint(p, t, "main.sayhi")
|
||||||
@ -2481,7 +2473,6 @@ func TestStepOut(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStepConcurrentDirect(t *testing.T) {
|
func TestStepConcurrentDirect(t *testing.T) {
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
skipOn(t, "broken - step concurrent", "windows", "arm64")
|
skipOn(t, "broken - step concurrent", "windows", "arm64")
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
|
withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
@ -2546,7 +2537,6 @@ func TestStepConcurrentDirect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestStepConcurrentPtr(t *testing.T) {
|
func TestStepConcurrentPtr(t *testing.T) {
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
|
withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
setFileBreakpoint(p, t, fixture.Source, 24)
|
setFileBreakpoint(p, t, fixture.Source, 24)
|
||||||
@ -4633,7 +4623,6 @@ func testCallConcurrentCheckReturns(p *proc.Target, t *testing.T, gid1, gid2 int
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCallConcurrent(t *testing.T) {
|
func TestCallConcurrent(t *testing.T) {
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
protest.MustSupportFunctionCalls(t, testBackend)
|
protest.MustSupportFunctionCalls(t, testBackend)
|
||||||
withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
|
withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
grp := proc.NewGroup(p)
|
grp := proc.NewGroup(p)
|
||||||
@ -5152,7 +5141,6 @@ func TestRequestManualStopWhileStopped(t *testing.T) {
|
|||||||
|
|
||||||
func TestStepOutPreservesGoroutine(t *testing.T) {
|
func TestStepOutPreservesGoroutine(t *testing.T) {
|
||||||
// Checks that StepOut preserves the currently selected goroutine.
|
// Checks that StepOut preserves the currently selected goroutine.
|
||||||
skipOn(t, "broken", "freebsd")
|
|
||||||
rand.Seed(time.Now().Unix())
|
rand.Seed(time.Now().Unix())
|
||||||
withTestProcess("issue2113", t, func(p *proc.Target, fixture protest.Fixture) {
|
withTestProcess("issue2113", t, func(p *proc.Target, fixture protest.Fixture) {
|
||||||
assertNoError(p.Continue(), t, "Continue()")
|
assertNoError(p.Continue(), t, "Continue()")
|
||||||
@ -5894,6 +5882,7 @@ func TestNilPtrDerefInBreakInstr(t *testing.T) {
|
|||||||
// this is also ok
|
// this is also ok
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
t.Logf("third continue")
|
||||||
assertNoError(err, t, "Continue()")
|
assertNoError(err, t, "Continue()")
|
||||||
bp := p.CurrentThread().Breakpoint()
|
bp := p.CurrentThread().Breakpoint()
|
||||||
if bp != nil {
|
if bp != nil {
|
||||||
|
@ -456,9 +456,6 @@ func TestScopePrefix(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOnPrefix(t *testing.T) {
|
func TestOnPrefix(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
t.Skip("test is not valid on FreeBSD")
|
|
||||||
}
|
|
||||||
const prefix = "\ti: "
|
const prefix = "\ti: "
|
||||||
test.AllowRecording(t)
|
test.AllowRecording(t)
|
||||||
lenient := false
|
lenient := false
|
||||||
@ -520,9 +517,6 @@ func TestNoVars(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestOnPrefixLocals(t *testing.T) {
|
func TestOnPrefixLocals(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
t.Skip("test is not valid on FreeBSD")
|
|
||||||
}
|
|
||||||
const prefix = "\ti: "
|
const prefix = "\ti: "
|
||||||
test.AllowRecording(t)
|
test.AllowRecording(t)
|
||||||
withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
|
withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {
|
||||||
|
@ -485,9 +485,6 @@ func TestLaunchStopOnEntry(t *testing.T) {
|
|||||||
|
|
||||||
// TestAttachStopOnEntry is like TestLaunchStopOnEntry, but with attach request.
|
// TestAttachStopOnEntry is like TestLaunchStopOnEntry, but with attach request.
|
||||||
func TestAttachStopOnEntry(t *testing.T) {
|
func TestAttachStopOnEntry(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
t.SkipNow()
|
|
||||||
}
|
|
||||||
runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
|
runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
// Start the program to attach to
|
// Start the program to attach to
|
||||||
cmd := exec.Command(fixture.Path)
|
cmd := exec.Command(fixture.Path)
|
||||||
@ -3435,9 +3432,6 @@ func TestHaltPreventsAutoResume(t *testing.T) {
|
|||||||
// goroutine is hit the correct number of times and log points set in the
|
// goroutine is hit the correct number of times and log points set in the
|
||||||
// children goroutines produce the correct number of output events.
|
// children goroutines produce the correct number of output events.
|
||||||
func TestConcurrentBreakpointsLogPoints(t *testing.T) {
|
func TestConcurrentBreakpointsLogPoints(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
t.SkipNow()
|
|
||||||
}
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
fixture string
|
fixture string
|
||||||
@ -3685,9 +3679,6 @@ func TestLaunchSubstitutePath(t *testing.T) {
|
|||||||
// that does not exist and expects the substitutePath attribute
|
// that does not exist and expects the substitutePath attribute
|
||||||
// in the launch configuration to take care of the mapping.
|
// in the launch configuration to take care of the mapping.
|
||||||
func TestAttachSubstitutePath(t *testing.T) {
|
func TestAttachSubstitutePath(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
t.SkipNow()
|
|
||||||
}
|
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
t.Skip("test skipped on windows, see https://delve.beta.teamcity.com/project/Delve_windows for details")
|
t.Skip("test skipped on windows, see https://delve.beta.teamcity.com/project/Delve_windows for details")
|
||||||
}
|
}
|
||||||
@ -4616,9 +4607,6 @@ func getPC(t *testing.T, client *daptest.Client, threadId int) (uint64, error) {
|
|||||||
// TestNextParked tests that we can switched selected goroutine to a parked one
|
// TestNextParked tests that we can switched selected goroutine to a parked one
|
||||||
// and perform next operation on it.
|
// and perform next operation on it.
|
||||||
func TestNextParked(t *testing.T) {
|
func TestNextParked(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
t.SkipNow()
|
|
||||||
}
|
|
||||||
runTest(t, "parallel_next", func(client *daptest.Client, fixture protest.Fixture) {
|
runTest(t, "parallel_next", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
runDebugSessionWithBPs(t, client, "launch",
|
runDebugSessionWithBPs(t, client, "launch",
|
||||||
// Launch
|
// Launch
|
||||||
@ -4681,7 +4669,8 @@ func testNextParkedHelper(t *testing.T, client *daptest.Client, fixture protest.
|
|||||||
// 2. hasn't called wg.Done yet
|
// 2. hasn't called wg.Done yet
|
||||||
// 3. is not the currently selected goroutine
|
// 3. is not the currently selected goroutine
|
||||||
for _, g := range threads.Body.Threads {
|
for _, g := range threads.Body.Threads {
|
||||||
if g.Id == se.Body.ThreadId { // Skip selected goroutine
|
if g.Id == se.Body.ThreadId || g.Id == 0 {
|
||||||
|
// Skip selected goroutine and goroutine 0
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
client.StackTraceRequest(g.Id, 0, 5)
|
client.StackTraceRequest(g.Id, 0, 5)
|
||||||
@ -4709,9 +4698,6 @@ func testNextParkedHelper(t *testing.T, client *daptest.Client, fixture protest.
|
|||||||
// and checks that StepOut preserves the currently selected goroutine.
|
// and checks that StepOut preserves the currently selected goroutine.
|
||||||
func TestStepOutPreservesGoroutine(t *testing.T) {
|
func TestStepOutPreservesGoroutine(t *testing.T) {
|
||||||
// Checks that StepOut preserves the currently selected goroutine.
|
// Checks that StepOut preserves the currently selected goroutine.
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
t.SkipNow()
|
|
||||||
}
|
|
||||||
rand.Seed(time.Now().Unix())
|
rand.Seed(time.Now().Unix())
|
||||||
runTest(t, "issue2113", func(client *daptest.Client, fixture protest.Fixture) {
|
runTest(t, "issue2113", func(client *daptest.Client, fixture protest.Fixture) {
|
||||||
runDebugSessionWithBPs(t, client, "launch",
|
runDebugSessionWithBPs(t, client, "launch",
|
||||||
@ -4862,9 +4848,6 @@ func TestBadAccess(t *testing.T) {
|
|||||||
// again will produce an error with a helpful message, and 'continue'
|
// again will produce an error with a helpful message, and 'continue'
|
||||||
// will resume the program.
|
// will resume the program.
|
||||||
func TestNextWhileNexting(t *testing.T) {
|
func TestNextWhileNexting(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
t.Skip("test is not valid on FreeBSD")
|
|
||||||
}
|
|
||||||
// a breakpoint triggering during a 'next' operation will interrupt 'next''
|
// a breakpoint triggering during a 'next' operation will interrupt 'next''
|
||||||
// Unlike the test for the terminal package, we cannot be certain
|
// Unlike the test for the terminal package, we cannot be certain
|
||||||
// of the number of breakpoints we expect to hit, since multiple
|
// of the number of breakpoints we expect to hit, since multiple
|
||||||
@ -5710,9 +5693,6 @@ func TestLaunchRequestWithEnv(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAttachRequest(t *testing.T) {
|
func TestAttachRequest(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
t.SkipNow()
|
|
||||||
}
|
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
t.Skip("test skipped on windows, see https://delve.beta.teamcity.com/project/Delve_windows for details")
|
t.Skip("test skipped on windows, see https://delve.beta.teamcity.com/project/Delve_windows for details")
|
||||||
}
|
}
|
||||||
@ -6638,9 +6618,6 @@ func TestAttachRemoteToDlvLaunchHaltedStopOnEntry(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAttachRemoteToDlvAttachHaltedStopOnEntry(t *testing.T) {
|
func TestAttachRemoteToDlvAttachHaltedStopOnEntry(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" || runtime.GOOS == "windows" {
|
|
||||||
t.SkipNow()
|
|
||||||
}
|
|
||||||
cmd, dbg := attachDebuggerWithTargetHalted(t, "http_server")
|
cmd, dbg := attachDebuggerWithTargetHalted(t, "http_server")
|
||||||
runTestWithDebugger(t, dbg, func(client *daptest.Client) {
|
runTestWithDebugger(t, dbg, func(client *daptest.Client) {
|
||||||
client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true})
|
client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true})
|
||||||
|
@ -985,9 +985,6 @@ func Test1NegativeStackDepthBug(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Test1ClientServer_CondBreakpoint(t *testing.T) {
|
func Test1ClientServer_CondBreakpoint(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
t.Skip("test is not valid on FreeBSD")
|
|
||||||
}
|
|
||||||
withTestClient1("parallel_next", t, func(c *rpc1.RPCClient) {
|
withTestClient1("parallel_next", t, func(c *rpc1.RPCClient) {
|
||||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: 1})
|
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: 1})
|
||||||
assertNoError(err, t, "CreateBreakpoint()")
|
assertNoError(err, t, "CreateBreakpoint()")
|
||||||
|
@ -1496,9 +1496,6 @@ func TestNegativeStackDepthBug(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestClientServer_CondBreakpoint(t *testing.T) {
|
func TestClientServer_CondBreakpoint(t *testing.T) {
|
||||||
if runtime.GOOS == "freebsd" {
|
|
||||||
t.Skip("test is not valid on FreeBSD")
|
|
||||||
}
|
|
||||||
protest.AllowRecording(t)
|
protest.AllowRecording(t)
|
||||||
withTestClient2("parallel_next", t, func(c service.Client) {
|
withTestClient2("parallel_next", t, func(c service.Client) {
|
||||||
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: 1})
|
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: 1})
|
||||||
|
Loading…
Reference in New Issue
Block a user