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:
Alessandro Arzilli 2022-12-20 18:54:16 +01:00 committed by GitHub
parent 32df4071d2
commit 00df758d57
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 269 additions and 168 deletions

@ -11,8 +11,7 @@ Tests skipped by each supported backend:
* 1 broken - cgo stacktraces
* darwin/lldb skipped = 1
* 1 upstream issue
* freebsd skipped = 16
* 12 broken
* freebsd skipped = 4
* 4 not implemented
* linux/386/pie skipped = 1
* 1 broken

@ -1036,11 +1036,6 @@ func TestTrace(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)
defer os.RemoveAll(tmpdir)

@ -246,14 +246,12 @@ func (dbp *nativeProcess) initialize(path string, debugInfoDirs []string) (*proc
// We disable asyncpreempt for the following reasons:
// - on Windows asyncpreempt is incompatible with debuggers, see:
// 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
// instructions, if the sequence happens to contain a breakpoint it will
// look like the breakpoint was hit twice when it was "logically" only
// executed once.
// 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,
CanDump: runtime.GOOS == "linux" || (runtime.GOOS == "windows" && runtime.GOARCH == "amd64"),

@ -42,6 +42,10 @@ const (
type osProcessDetails struct {
comm string
tid int
delayedSignal syscall.Signal
trapThreads []int
selectedThread *nativeThread
}
func (os *osProcessDetails) Close() {}
@ -83,7 +87,6 @@ func Launch(cmd []string, wd string, flags proc.LaunchFlags, debugInfoDirs []str
process.Stdout = stdout
process.Stderr = stderr
process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true, Foreground: foreground}
process.Env = proc.DisableAsyncPreemptEnv()
if foreground {
signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
}
@ -152,7 +155,12 @@ func (dbp *nativeProcess) kill() (err error) {
if dbp.exited {
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 {
return err
}
@ -165,7 +173,7 @@ func (dbp *nativeProcess) kill() (err error) {
// Used by RequestManualStop
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
@ -178,7 +186,7 @@ func (dbp *nativeProcess) addThread(tid int, attach bool) (*nativeThread, error)
var err error
dbp.execPtraceFunc(func() { err = sys.PtraceLwpEvents(dbp.pid, 1) })
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)
}
}
@ -220,13 +228,29 @@ func findExecutable(path string, pid int) string {
}
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
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 {
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 {
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()
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
dbp.execPtraceFunc(func() { info, err = ptraceGetLwpInfo(wpid) })
@ -269,10 +298,10 @@ func (dbp *nativeProcess) trapWaitInternal(pid int, halt bool) (*nativeThread, e
}
return nil, err
}
if halt {
return nil, nil
if mode == trapWaitStepping {
dbp.execPtraceFunc(func() { ptraceSuspend(tid) })
}
if err = th.Continue(); err != nil {
if err = dbp.ptraceCont(0); err != nil {
if err == sys.ESRCH {
// thread died while we were adding it
delete(dbp.threads, int(tid))
@ -288,12 +317,16 @@ func (dbp *nativeProcess) trapWaitInternal(pid int, halt bool) (*nativeThread, e
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
}
// 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 {
return nil, proc.ErrProcessExited{Pid: dbp.pid}
}
@ -309,14 +342,6 @@ func status(pid int) rune {
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
func (dbp *nativeProcess) wait(pid, options int) (int, *sys.WaitStatus, error) {
var s sys.WaitStatus
@ -330,7 +355,7 @@ func (dbp *nativeProcess) exitGuard(err error) error {
return err
}
if status(dbp.pid) == statusZombie {
_, err := dbp.trapWaitInternal(-1, false)
_, err := dbp.trapWaitInternal(-1, trapWaitNormal)
return err
}
@ -348,9 +373,31 @@ func (dbp *nativeProcess) resume() error {
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
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
}
@ -360,19 +407,72 @@ func (dbp *nativeProcess) stop(cctx *proc.ContinueOnceContext, trapthread *nativ
if dbp.exited {
return nil, proc.ErrProcessExited{Pid: dbp.pid}
}
// set breakpoints on all threads
for _, th := range dbp.threads {
if th.CurrentBreakpoint.Breakpoint == nil {
if err := th.SetCurrentBreakpoint(true); err != nil {
return nil, err
var err error
dbp.execPtraceFunc(func() {
for _, th := range dbp.threads {
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
func (dbp *nativeProcess) detach(kill bool) error {
if !kill {
for _, th := range dbp.threads {
ptraceResume(th.ID)
}
}
return ptraceDetach(dbp.pid)
}
@ -395,6 +495,12 @@ func (dbp *nativeProcess) GetBufferedTracepoints() []ebpf.RawUProbeParams {
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
func killProcess(pid int) error {
return sys.Kill(pid, sys.SIGINT)

@ -10,6 +10,7 @@ import "C"
import (
"unsafe"
"syscall"
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) {
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
}
// 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.
//
// 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 {
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
// #include <sys/thr.h>
import "C"
import (
"fmt"
"github.com/go-delve/delve/pkg/proc/fbsdutil"
"bytes"
sys "golang.org/x/sys/unix"
"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/amd64util"
"github.com/go-delve/delve/pkg/proc/fbsdutil"
)
type waitStatus sys.WaitStatus
@ -20,59 +17,58 @@ type osSpecificDetails struct {
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) {
t.dbp.execPtraceFunc(func() { err = ptraceSingleStep(t.ID) })
t.dbp.execPtraceFunc(func() { err = ptraceSetStep(t.ID) })
if err != nil {
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 {
th, err := t.dbp.trapWait(t.dbp.pid)
err = t.dbp.ptraceCont(sig)
sig = 0
if err != nil {
return err
}
if th.ID == t.ID {
break
}
t.dbp.execPtraceFunc(func() { err = ptraceCont(th.ID, 0) })
trapthread, err := t.dbp.trapWaitInternal(-1, trapWaitStepping)
if err != nil {
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 {
@ -110,3 +106,19 @@ func (t *nativeThread) withDebugRegisters(f func(*amd64util.DebugRegisters) erro
func (t *nativeThread) SoftExc() bool {
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 {
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)
}
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) {
if t.dbp.exited {
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}

@ -586,7 +586,6 @@ func TestNextGeneral(t *testing.T) {
}
func TestNextConcurrent(t *testing.T) {
skipOn(t, "broken", "freebsd")
testcases := []nextTest{
{8, 9},
{9, 10},
@ -622,7 +621,6 @@ func TestNextConcurrent(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
testcases := []nextTest{
{8, 9},
@ -1472,7 +1470,6 @@ func TestIssue325(t *testing.T) {
}
func TestBreakpointCounts(t *testing.T) {
skipOn(t, "broken", "freebsd")
protest.AllowRecording(t)
withTestProcess("bpcountstest", t, func(p *proc.Target, fixture protest.Fixture) {
bp := setFileBreakpoint(p, t, fixture.Source, 12)
@ -1504,7 +1501,6 @@ func TestBreakpointCounts(t *testing.T) {
}
func TestHardcodedBreakpointCounts(t *testing.T) {
skipOn(t, "broken", "freebsd")
withTestProcess("hcbpcountstest", t, func(p *proc.Target, fixture protest.Fixture) {
counts := map[int64]int{}
for {
@ -1716,7 +1712,6 @@ func BenchmarkLocalVariables(b *testing.B) {
}
func TestCondBreakpoint(t *testing.T) {
skipOn(t, "broken", "freebsd")
protest.AllowRecording(t)
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
bp := setFileBreakpoint(p, t, fixture.Source, 9)
@ -1738,7 +1733,6 @@ func TestCondBreakpoint(t *testing.T) {
}
func TestCondBreakpointError(t *testing.T) {
skipOn(t, "broken", "freebsd")
protest.AllowRecording(t)
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
bp := setFileBreakpoint(p, t, fixture.Source, 9)
@ -2101,7 +2095,6 @@ func TestIssue462(t *testing.T) {
}
func TestNextParked(t *testing.T) {
skipOn(t, "broken", "freebsd")
protest.AllowRecording(t)
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
bp := setFunctionBreakpoint(p, t, "main.sayhi")
@ -2152,7 +2145,6 @@ func TestNextParked(t *testing.T) {
}
func TestStepParked(t *testing.T) {
skipOn(t, "broken", "freebsd")
protest.AllowRecording(t)
withTestProcess("parallel_next", t, func(p *proc.Target, fixture protest.Fixture) {
bp := setFunctionBreakpoint(p, t, "main.sayhi")
@ -2481,7 +2473,6 @@ func TestStepOut(t *testing.T) {
}
func TestStepConcurrentDirect(t *testing.T) {
skipOn(t, "broken", "freebsd")
skipOn(t, "broken - step concurrent", "windows", "arm64")
protest.AllowRecording(t)
withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
@ -2546,7 +2537,6 @@ func TestStepConcurrentDirect(t *testing.T) {
}
func TestStepConcurrentPtr(t *testing.T) {
skipOn(t, "broken", "freebsd")
protest.AllowRecording(t)
withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
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) {
skipOn(t, "broken", "freebsd")
protest.MustSupportFunctionCalls(t, testBackend)
withTestProcess("teststepconcurrent", t, func(p *proc.Target, fixture protest.Fixture) {
grp := proc.NewGroup(p)
@ -5152,7 +5141,6 @@ func TestRequestManualStopWhileStopped(t *testing.T) {
func TestStepOutPreservesGoroutine(t *testing.T) {
// Checks that StepOut preserves the currently selected goroutine.
skipOn(t, "broken", "freebsd")
rand.Seed(time.Now().Unix())
withTestProcess("issue2113", t, func(p *proc.Target, fixture protest.Fixture) {
assertNoError(p.Continue(), t, "Continue()")
@ -5894,6 +5882,7 @@ func TestNilPtrDerefInBreakInstr(t *testing.T) {
// this is also ok
return
}
t.Logf("third continue")
assertNoError(err, t, "Continue()")
bp := p.CurrentThread().Breakpoint()
if bp != nil {

@ -456,9 +456,6 @@ func TestScopePrefix(t *testing.T) {
}
func TestOnPrefix(t *testing.T) {
if runtime.GOOS == "freebsd" {
t.Skip("test is not valid on FreeBSD")
}
const prefix = "\ti: "
test.AllowRecording(t)
lenient := false
@ -520,9 +517,6 @@ func TestNoVars(t *testing.T) {
}
func TestOnPrefixLocals(t *testing.T) {
if runtime.GOOS == "freebsd" {
t.Skip("test is not valid on FreeBSD")
}
const prefix = "\ti: "
test.AllowRecording(t)
withTestTerminal("goroutinestackprog", t, func(term *FakeTerminal) {

@ -485,9 +485,6 @@ func TestLaunchStopOnEntry(t *testing.T) {
// TestAttachStopOnEntry is like TestLaunchStopOnEntry, but with attach request.
func TestAttachStopOnEntry(t *testing.T) {
if runtime.GOOS == "freebsd" {
t.SkipNow()
}
runTest(t, "loopprog", func(client *daptest.Client, fixture protest.Fixture) {
// Start the program to attach to
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
// children goroutines produce the correct number of output events.
func TestConcurrentBreakpointsLogPoints(t *testing.T) {
if runtime.GOOS == "freebsd" {
t.SkipNow()
}
tests := []struct {
name string
fixture string
@ -3685,9 +3679,6 @@ func TestLaunchSubstitutePath(t *testing.T) {
// that does not exist and expects the substitutePath attribute
// in the launch configuration to take care of the mapping.
func TestAttachSubstitutePath(t *testing.T) {
if runtime.GOOS == "freebsd" {
t.SkipNow()
}
if runtime.GOOS == "windows" {
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
// and perform next operation on it.
func TestNextParked(t *testing.T) {
if runtime.GOOS == "freebsd" {
t.SkipNow()
}
runTest(t, "parallel_next", func(client *daptest.Client, fixture protest.Fixture) {
runDebugSessionWithBPs(t, client, "launch",
// Launch
@ -4681,7 +4669,8 @@ func testNextParkedHelper(t *testing.T, client *daptest.Client, fixture protest.
// 2. hasn't called wg.Done yet
// 3. is not the currently selected goroutine
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
}
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.
func TestStepOutPreservesGoroutine(t *testing.T) {
// Checks that StepOut preserves the currently selected goroutine.
if runtime.GOOS == "freebsd" {
t.SkipNow()
}
rand.Seed(time.Now().Unix())
runTest(t, "issue2113", func(client *daptest.Client, fixture protest.Fixture) {
runDebugSessionWithBPs(t, client, "launch",
@ -4862,9 +4848,6 @@ func TestBadAccess(t *testing.T) {
// again will produce an error with a helpful message, and 'continue'
// will resume the program.
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''
// Unlike the test for the terminal package, we cannot be certain
// 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) {
if runtime.GOOS == "freebsd" {
t.SkipNow()
}
if runtime.GOOS == "windows" {
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) {
if runtime.GOOS == "freebsd" || runtime.GOOS == "windows" {
t.SkipNow()
}
cmd, dbg := attachDebuggerWithTargetHalted(t, "http_server")
runTestWithDebugger(t, dbg, func(client *daptest.Client) {
client.AttachRequest(map[string]interface{}{"mode": "remote", "stopOnEntry": true})

@ -985,9 +985,6 @@ func Test1NegativeStackDepthBug(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) {
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: 1})
assertNoError(err, t, "CreateBreakpoint()")

@ -1496,9 +1496,6 @@ func TestNegativeStackDepthBug(t *testing.T) {
}
func TestClientServer_CondBreakpoint(t *testing.T) {
if runtime.GOOS == "freebsd" {
t.Skip("test is not valid on FreeBSD")
}
protest.AllowRecording(t)
withTestClient2("parallel_next", t, func(c service.Client) {
bp, err := c.CreateBreakpoint(&api.Breakpoint{FunctionName: "main.sayhi", Line: 1})