pkg/proc: unskip passing tests and reorganize (#3561)

This commit is contained in:
Derek Parker 2023-11-12 05:01:21 -06:00 committed by GitHub
parent d186e14fd9
commit 0466226b1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 279 additions and 260 deletions

@ -9,8 +9,8 @@ Tests skipped by each supported backend:
* darwin skipped = 3
* 2 follow exec not implemented on macOS
* 1 waitfor implementation is delegated to debugserver
* darwin/arm64 skipped = 2
* 2 broken - cgo stacktraces
* darwin/arm64 skipped = 1
* 1 broken - cgo stacktraces
* darwin/lldb skipped = 1
* 1 upstream issue
* freebsd skipped = 9

131
pkg/proc/proc_amd64_test.go Normal file

@ -0,0 +1,131 @@
package proc_test
import (
"go/constant"
"path/filepath"
"runtime"
"testing"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"github.com/go-delve/delve/pkg/goversion"
"github.com/go-delve/delve/pkg/proc"
protest "github.com/go-delve/delve/pkg/proc/test"
)
func TestStepInstructionOnBreakpoint(t *testing.T) {
// StepInstruction should step one instruction forward when
// PC is on a 1 byte instruction with a software breakpoint.
protest.AllowRecording(t)
withTestProcess("break/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFileBreakpoint(p, t, filepath.ToSlash(filepath.Join(fixture.BuildDir, "break_amd64.s")), 4)
assertNoError(grp.Continue(), t, "Continue()")
pc := getRegisters(p, t).PC()
assertNoError(grp.StepInstruction(), t, "StepInstruction()")
if pc == getRegisters(p, t).PC() {
t.Fatal("Could not step a single instruction")
}
})
}
func TestNextUnknownInstr(t *testing.T) {
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 10) {
t.Skip("versions of Go before 1.10 can't assemble the instruction VPUNPCKLWD")
}
withTestProcess("nodisasm/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFunctionBreakpoint(p, t, "main.asmFunc")
assertNoError(grp.Continue(), t, "Continue()")
assertNoError(grp.Next(), t, "Next()")
})
}
func TestIssue1656(t *testing.T) {
withTestProcess("issue1656/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFileBreakpoint(p, t, filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.s")), 5)
assertNoError(grp.Continue(), t, "Continue()")
t.Logf("step1\n")
assertNoError(grp.Step(), t, "Step()")
assertLineNumber(p, t, 8, "wrong line number after first step")
t.Logf("step2\n")
assertNoError(grp.Step(), t, "Step()")
assertLineNumber(p, t, 9, "wrong line number after second step")
})
}
func TestBreakpointConfusionOnResume(t *testing.T) {
// Checks that SetCurrentBreakpoint, (*Thread).StepInstruction and
// native.(*Thread).singleStep all agree on which breakpoint the thread is
// stopped at.
// This test checks for a regression introduced when fixing Issue #1656
withTestProcess("nopbreakpoint/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
maindots := filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.s"))
maindotgo := filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.go"))
setFileBreakpoint(p, t, maindots, 5) // line immediately after the NOP
assertNoError(grp.Continue(), t, "First Continue")
assertLineNumber(p, t, 5, "not on main.s:5")
setFileBreakpoint(p, t, maindots, 4) // sets a breakpoint on the NOP line, which will be one byte before the breakpoint we currently are stopped at.
setFileBreakpoint(p, t, maindotgo, 18) // set one extra breakpoint so that we can recover execution and check the global variable g
assertNoError(grp.Continue(), t, "Second Continue")
gvar := evalVariable(p, t, "g")
if n, _ := constant.Int64Val(gvar.Value); n != 1 {
t.Fatalf("wrong value of global variable 'g': %v (expected 1)", gvar.Value)
}
})
}
func TestCallInjectionFlagCorruption(t *testing.T) {
// debugCallV2 has a bug in amd64 where its tail corrupts the FLAGS register by running an ADD instruction.
// Since this problem exists in many versions of Go, instead of fixing
// debugCallV2, we work around this problem by restoring FLAGS, one extra
// time, after stepping out of debugCallV2.
// Fixes issue https://github.com/go-delve/delve/issues/2985
protest.MustSupportFunctionCalls(t, testBackend)
withTestProcessArgs("badflags", t, ".", []string{"0"}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
mainfn := p.BinInfo().LookupFunc()["main.main"][0]
// Find JNZ instruction on line :14
var addr uint64
text, err := proc.Disassemble(p.Memory(), nil, p.Breakpoints(), p.BinInfo(), mainfn.Entry, mainfn.End)
assertNoError(err, t, "Disassemble")
for _, instr := range text {
if instr.Loc.Line != 14 {
continue
}
if proc.IsJNZ(instr.Inst) {
addr = instr.Loc.PC
}
}
if addr == 0 {
t.Fatalf("Could not find JNZ instruction at line :14")
}
// Create breakpoint
_, err = p.SetBreakpoint(0, addr, proc.UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint")
// Continue to breakpoint
assertNoError(grp.Continue(), t, "Continue()")
assertLineNumber(p, t, 14, "expected line :14")
// Save RFLAGS register
rflagsBeforeCall := p.BinInfo().Arch.RegistersToDwarfRegisters(0, getRegisters(p, t)).Uint64Val(regnum.AMD64_Rflags)
t.Logf("rflags before = %#x", rflagsBeforeCall)
// Inject call to main.g()
assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "g()", normalLoadConfig, true), t, "Call")
// Check RFLAGS register after the call
rflagsAfterCall := p.BinInfo().Arch.RegistersToDwarfRegisters(0, getRegisters(p, t)).Uint64Val(regnum.AMD64_Rflags)
t.Logf("rflags after = %#x", rflagsAfterCall)
if rflagsBeforeCall != rflagsAfterCall {
t.Errorf("mismatched rflags value")
}
// Single step and check where we end up
assertNoError(grp.Step(), t, "Step()")
assertLineNumber(p, t, 17, "expected line :17") // since we passed "0" as argument we should be going into the false branch at line :17
})
}

@ -0,0 +1,49 @@
package proc_test
import (
"testing"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"github.com/go-delve/delve/pkg/proc"
protest "github.com/go-delve/delve/pkg/proc/test"
)
func TestSetYMMRegister(t *testing.T) {
// Checks that setting a XMM register works. This checks that the
// workaround for a bug in debugserver works.
// See issue #2767.
withTestProcess("setymmreg/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFunctionBreakpoint(p, t, "main.asmFunc")
assertNoError(grp.Continue(), t, "Continue()")
getReg := func(pos string) *op.DwarfRegister {
regs := getRegisters(p, t)
arch := p.BinInfo().Arch
dregs := arch.RegistersToDwarfRegisters(0, regs)
r := dregs.Reg(regnum.AMD64_XMM0)
t.Logf("%s: %#v", pos, r)
return r
}
getReg("before")
p.CurrentThread().SetReg(regnum.AMD64_XMM0, op.DwarfRegisterFromBytes([]byte{
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44}))
assertNoError(grp.StepInstruction(), t, "SetpInstruction")
xmm0 := getReg("after")
for i := range xmm0.Bytes {
if xmm0.Bytes[i] != 0x44 {
t.Fatalf("wrong register value")
}
}
})
}

@ -0,0 +1,51 @@
package proc_test
import (
"testing"
"github.com/go-delve/delve/pkg/proc"
protest "github.com/go-delve/delve/pkg/proc/test"
)
func TestStacktraceExtlinkMac(t *testing.T) {
// Tests stacktrace for programs built using external linker.
// See issue #3194
skipOn(t, "broken on darwin/amd64/pie", "darwin", "amd64", "pie")
withTestProcess("issue3194", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFunctionBreakpoint(p, t, "main.main")
assertNoError(grp.Continue(), t, "First Continue()")
frames, err := proc.ThreadStacktrace(p, p.CurrentThread(), 10)
assertNoError(err, t, "ThreadStacktrace")
logStacktrace(t, p, frames)
if len(frames) < 2 || frames[0].Call.Fn.Name != "main.main" || frames[1].Call.Fn.Name != "runtime.main" {
t.Fatalf("bad stacktrace")
}
})
}
func TestRefreshCurThreadSelGAfterContinueOnceError(t *testing.T) {
// Issue #2078:
// Tests that on macOS/lldb the current thread/selected goroutine are
// refreshed after ContinueOnce returns an error due to a segmentation
// fault.
skipUnlessOn(t, "N/A", "lldb")
withTestProcess("issue2078", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFileBreakpoint(p, t, fixture.Source, 4)
assertNoError(grp.Continue(), t, "Continue() (first)")
if grp.Continue() == nil {
pc := currentPC(p, t)
f, l, fn := p.BinInfo().PCToLine(pc)
t.Logf("Second continue did not return an error %s:%d %#v", f, l, fn)
if fn != nil && fn.Name == "runtime.fatalpanic" {
// this is also ok, it just means this debugserver supports --unmask-signals and it's working as intended.
return
}
}
g := p.SelectedGoroutine()
if g.CurrentLoc.Line != 9 {
t.Fatalf("wrong current location %s:%d (expected :9)", g.CurrentLoc.File, g.CurrentLoc.Line)
}
})
}

@ -1,9 +1,12 @@
package proc_test
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"testing"
"github.com/go-delve/delve/pkg/proc"
@ -22,6 +25,49 @@ func TestLoadingExternalDebugInfo(t *testing.T) {
p.Detach(true)
}
func TestGnuDebuglink(t *testing.T) {
// build math.go and make a copy of the executable
fixture := protest.BuildFixture("math", 0)
buf, err := os.ReadFile(fixture.Path)
assertNoError(err, t, "ReadFile")
debuglinkPath := fixture.Path + "-gnu_debuglink"
assertNoError(os.WriteFile(debuglinkPath, buf, 0666), t, "WriteFile")
defer os.Remove(debuglinkPath)
run := func(exe string, args ...string) {
cmd := exec.Command(exe, args...)
out, err := cmd.CombinedOutput()
assertNoError(err, t, fmt.Sprintf("%s %q: %s", cmd, strings.Join(args, " "), out))
}
// convert the executable copy to use .gnu_debuglink
debuglinkDwoPath := debuglinkPath + ".dwo"
run("objcopy", "--only-keep-debug", debuglinkPath, debuglinkDwoPath)
defer os.Remove(debuglinkDwoPath)
run("objcopy", "--strip-debug", debuglinkPath)
run("objcopy", "--add-gnu-debuglink="+debuglinkDwoPath, debuglinkPath)
// open original executable
normalBinInfo := proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH)
assertNoError(normalBinInfo.LoadBinaryInfo(fixture.Path, 0, []string{"/debugdir"}), t, "LoadBinaryInfo (normal exe)")
// open .gnu_debuglink executable
debuglinkBinInfo := proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH)
assertNoError(debuglinkBinInfo.LoadBinaryInfo(debuglinkPath, 0, []string{"/debugdir"}), t, "LoadBinaryInfo (gnu_debuglink exe)")
if len(normalBinInfo.Functions) != len(debuglinkBinInfo.Functions) {
t.Fatalf("function list mismatch")
}
for i := range normalBinInfo.Functions {
normalFn := normalBinInfo.Functions[i]
debuglinkFn := debuglinkBinInfo.Functions[i]
if normalFn.Entry != debuglinkFn.Entry || normalFn.Name != debuglinkFn.Name {
t.Fatalf("function definition mismatch")
}
}
}
func stripAndCopyDebugInfo(f protest.Fixture, t *testing.T) {
name := filepath.Base(f.Path)
// Copy the debug information to an external file.

@ -28,7 +28,6 @@ import (
"github.com/go-delve/delve/pkg/dwarf/frame"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"github.com/go-delve/delve/pkg/goversion"
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/proc"
@ -875,7 +874,6 @@ func TestCGONext(t *testing.T) {
}
protest.MustHaveCgo(t)
skipOn(t, "broken - cgo stacktraces", "darwin", "arm64")
skipOn(t, "broken - see https://github.com/go-delve/delve/issues/3158", "darwin", "amd64")
protest.AllowRecording(t)
@ -996,8 +994,6 @@ func stackMatch(stack []loc, locations []proc.Stackframe, skipRuntime bool) bool
}
func TestStacktraceGoroutine(t *testing.T) {
skipOn(t, "broken - cgo stacktraces", "darwin", "arm64")
mainStack := []loc{{14, "main.stacktraceme"}, {29, "main.main"}}
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) {
mainStack[0].line = 15
@ -1072,7 +1068,6 @@ func TestStacktraceGoroutine(t *testing.T) {
}
func TestKill(t *testing.T) {
skipOn(t, "N/A", "lldb") // k command presumably works but leaves the process around?
withTestProcess("testprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
if err := grp.Detach(true); err != nil {
t.Fatal(err)
@ -2701,26 +2696,6 @@ func TestStepOutDeferReturnAndDirectCall(t *testing.T) {
{contStepout, 28}})
}
func TestStepInstructionOnBreakpoint(t *testing.T) {
if runtime.GOARCH != "amd64" {
t.Skipf("skipping since not amd64")
}
// StepInstruction should step one instruction forward when
// PC is on a 1 byte instruction with a software breakpoint.
protest.AllowRecording(t)
withTestProcess("break/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFileBreakpoint(p, t, filepath.ToSlash(filepath.Join(fixture.BuildDir, "break_amd64.s")), 4)
assertNoError(grp.Continue(), t, "Continue()")
pc := getRegisters(p, t).PC()
assertNoError(grp.StepInstruction(), t, "StepInstruction()")
if pc == getRegisters(p, t).PC() {
t.Fatal("Could not step a single instruction")
}
})
}
func TestStepOnCallPtrInstr(t *testing.T) {
protest.AllowRecording(t)
withTestProcess("teststepprog", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
@ -4295,18 +4270,6 @@ func TestReadDefer(t *testing.T) {
})
}
func TestNextUnknownInstr(t *testing.T) {
skipUnlessOn(t, "amd64 only", "amd64")
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 10) {
t.Skip("versions of Go before 1.10 can't assemble the instruction VPUNPCKLWD")
}
withTestProcess("nodisasm/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFunctionBreakpoint(p, t, "main.asmFunc")
assertNoError(grp.Continue(), t, "Continue()")
assertNoError(grp.Next(), t, "Next()")
})
}
func TestReadDeferArgs(t *testing.T) {
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 17) {
// When regabi is enabled in Go 1.17 and later, reading arguments of
@ -4705,42 +4668,6 @@ func TestCgoStacktrace2(t *testing.T) {
})
}
func TestIssue1656(t *testing.T) {
skipUnlessOn(t, "amd64 only", "amd64")
withTestProcess("issue1656/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFileBreakpoint(p, t, filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.s")), 5)
assertNoError(grp.Continue(), t, "Continue()")
t.Logf("step1\n")
assertNoError(grp.Step(), t, "Step()")
assertLineNumber(p, t, 8, "wrong line number after first step")
t.Logf("step2\n")
assertNoError(grp.Step(), t, "Step()")
assertLineNumber(p, t, 9, "wrong line number after second step")
})
}
func TestBreakpointConfusionOnResume(t *testing.T) {
// Checks that SetCurrentBreakpoint, (*Thread).StepInstruction and
// native.(*Thread).singleStep all agree on which breakpoint the thread is
// stopped at.
// This test checks for a regression introduced when fixing Issue #1656
skipUnlessOn(t, "amd64 only", "amd64")
withTestProcess("nopbreakpoint/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
maindots := filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.s"))
maindotgo := filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.go"))
setFileBreakpoint(p, t, maindots, 5) // line immediately after the NOP
assertNoError(grp.Continue(), t, "First Continue")
assertLineNumber(p, t, 5, "not on main.s:5")
setFileBreakpoint(p, t, maindots, 4) // sets a breakpoint on the NOP line, which will be one byte before the breakpoint we currently are stopped at.
setFileBreakpoint(p, t, maindotgo, 18) // set one extra breakpoint so that we can recover execution and check the global variable g
assertNoError(grp.Continue(), t, "Second Continue")
gvar := evalVariable(p, t, "g")
if n, _ := constant.Int64Val(gvar.Value); n != 1 {
t.Fatalf("wrong value of global variable 'g': %v (expected 1)", gvar.Value)
}
})
}
func TestIssue1736(t *testing.T) {
withTestProcess("testvariables2", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
assertNoError(grp.Continue(), t, "Continue()")
@ -5050,33 +4977,6 @@ func TestStepIntoWrapperForEmbeddedPointer(t *testing.T) {
}
}
func TestRefreshCurThreadSelGAfterContinueOnceError(t *testing.T) {
// Issue #2078:
// Tests that on macOS/lldb the current thread/selected goroutine are
// refreshed after ContinueOnce returns an error due to a segmentation
// fault.
skipUnlessOn(t, "N/A", "darwin", "lldb")
withTestProcess("issue2078", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFileBreakpoint(p, t, fixture.Source, 4)
assertNoError(grp.Continue(), t, "Continue() (first)")
if grp.Continue() == nil {
pc := currentPC(p, t)
f, l, fn := p.BinInfo().PCToLine(pc)
t.Logf("Second continue did not return an error %s:%d %#v", f, l, fn)
if fn != nil && fn.Name == "runtime.fatalpanic" {
// this is also ok, it just means this debugserver supports --unmask-signals and it's working as intended.
return
}
}
g := p.SelectedGoroutine()
if g.CurrentLoc.Line != 9 {
t.Fatalf("wrong current location %s:%d (expected :9)", g.CurrentLoc.File, g.CurrentLoc.Line)
}
})
}
func TestStepoutOneliner(t *testing.T) {
// The heuristic detecting autogenerated wrappers when stepping out should
// not skip oneliner functions.
@ -5806,45 +5706,6 @@ func TestSetOnFunctions(t *testing.T) {
})
}
func TestSetYMMRegister(t *testing.T) {
skipUnlessOn(t, "N/A", "darwin", "amd64")
// Checks that setting a XMM register works. This checks that the
// workaround for a bug in debugserver works.
// See issue #2767.
withTestProcess("setymmreg/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFunctionBreakpoint(p, t, "main.asmFunc")
assertNoError(grp.Continue(), t, "Continue()")
getReg := func(pos string) *op.DwarfRegister {
regs := getRegisters(p, t)
arch := p.BinInfo().Arch
dregs := arch.RegistersToDwarfRegisters(0, regs)
r := dregs.Reg(regnum.AMD64_XMM0)
t.Logf("%s: %#v", pos, r)
return r
}
getReg("before")
p.CurrentThread().SetReg(regnum.AMD64_XMM0, op.DwarfRegisterFromBytes([]byte{
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44}))
assertNoError(grp.StepInstruction(), t, "SetpInstruction")
xmm0 := getReg("after")
for i := range xmm0.Bytes {
if xmm0.Bytes[i] != 0x44 {
t.Fatalf("wrong register value")
}
}
})
}
func TestNilPtrDerefInBreakInstr(t *testing.T) {
// Checks that having a breakpoint on the exact instruction that causes a
// nil pointer dereference does not cause problems.
@ -5898,125 +5759,6 @@ func TestStepIntoAutogeneratedSkip(t *testing.T) {
})
}
func TestCallInjectionFlagCorruption(t *testing.T) {
// debugCallV2 has a bug in amd64 where its tail corrupts the FLAGS register by running an ADD instruction.
// Since this problem exists in many versions of Go, instead of fixing
// debugCallV2, we work around this problem by restoring FLAGS, one extra
// time, after stepping out of debugCallV2.
// Fixes issue https://github.com/go-delve/delve/issues/2985
skipUnlessOn(t, "not relevant", "amd64")
protest.MustSupportFunctionCalls(t, testBackend)
withTestProcessArgs("badflags", t, ".", []string{"0"}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
mainfn := p.BinInfo().LookupFunc()["main.main"][0]
// Find JNZ instruction on line :14
var addr uint64
text, err := proc.Disassemble(p.Memory(), nil, p.Breakpoints(), p.BinInfo(), mainfn.Entry, mainfn.End)
assertNoError(err, t, "Disassemble")
for _, instr := range text {
if instr.Loc.Line != 14 {
continue
}
if proc.IsJNZ(instr.Inst) {
addr = instr.Loc.PC
}
}
if addr == 0 {
t.Fatalf("Could not find JNZ instruction at line :14")
}
// Create breakpoint
_, err = p.SetBreakpoint(0, addr, proc.UserBreakpoint, nil)
assertNoError(err, t, "SetBreakpoint")
// Continue to breakpoint
assertNoError(grp.Continue(), t, "Continue()")
assertLineNumber(p, t, 14, "expected line :14")
// Save RFLAGS register
rflagsBeforeCall := p.BinInfo().Arch.RegistersToDwarfRegisters(0, getRegisters(p, t)).Uint64Val(regnum.AMD64_Rflags)
t.Logf("rflags before = %#x", rflagsBeforeCall)
// Inject call to main.g()
assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "g()", normalLoadConfig, true), t, "Call")
// Check RFLAGS register after the call
rflagsAfterCall := p.BinInfo().Arch.RegistersToDwarfRegisters(0, getRegisters(p, t)).Uint64Val(regnum.AMD64_Rflags)
t.Logf("rflags after = %#x", rflagsAfterCall)
if rflagsBeforeCall != rflagsAfterCall {
t.Errorf("mismatched rflags value")
}
// Single step and check where we end up
assertNoError(grp.Step(), t, "Step()")
assertLineNumber(p, t, 17, "expected line :17") // since we passed "0" as argument we should be going into the false branch at line :17
})
}
func TestGnuDebuglink(t *testing.T) {
skipUnlessOn(t, "N/A", "linux")
// build math.go and make a copy of the executable
fixture := protest.BuildFixture("math", 0)
buf, err := os.ReadFile(fixture.Path)
assertNoError(err, t, "ReadFile")
debuglinkPath := fixture.Path + "-gnu_debuglink"
assertNoError(os.WriteFile(debuglinkPath, buf, 0666), t, "WriteFile")
defer os.Remove(debuglinkPath)
run := func(exe string, args ...string) {
cmd := exec.Command(exe, args...)
out, err := cmd.CombinedOutput()
assertNoError(err, t, fmt.Sprintf("%s %q: %s", cmd, strings.Join(args, " "), out))
}
// convert the executable copy to use .gnu_debuglink
debuglinkDwoPath := debuglinkPath + ".dwo"
run("objcopy", "--only-keep-debug", debuglinkPath, debuglinkDwoPath)
defer os.Remove(debuglinkDwoPath)
run("objcopy", "--strip-debug", debuglinkPath)
run("objcopy", "--add-gnu-debuglink="+debuglinkDwoPath, debuglinkPath)
// open original executable
normalBinInfo := proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH)
assertNoError(normalBinInfo.LoadBinaryInfo(fixture.Path, 0, []string{"/debugdir"}), t, "LoadBinaryInfo (normal exe)")
// open .gnu_debuglink executable
debuglinkBinInfo := proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH)
assertNoError(debuglinkBinInfo.LoadBinaryInfo(debuglinkPath, 0, []string{"/debugdir"}), t, "LoadBinaryInfo (gnu_debuglink exe)")
if len(normalBinInfo.Functions) != len(debuglinkBinInfo.Functions) {
t.Fatalf("function list mismatch")
}
for i := range normalBinInfo.Functions {
normalFn := normalBinInfo.Functions[i]
debuglinkFn := debuglinkBinInfo.Functions[i]
if normalFn.Entry != debuglinkFn.Entry || normalFn.Name != debuglinkFn.Name {
t.Fatalf("function definition mismatch")
}
}
}
func TestStacktraceExtlinkMac(t *testing.T) {
// Tests stacktrace for programs built using external linker.
// See issue #3194
skipUnlessOn(t, "darwin only", "darwin")
skipOn(t, "broken on darwin/amd64/pie", "darwin", "amd64", "pie")
withTestProcess("issue3194", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
setFunctionBreakpoint(p, t, "main.main")
assertNoError(grp.Continue(), t, "First Continue()")
frames, err := proc.ThreadStacktrace(p, p.CurrentThread(), 10)
assertNoError(err, t, "ThreadStacktrace")
logStacktrace(t, p, frames)
if len(frames) < 2 || frames[0].Call.Fn.Name != "main.main" || frames[1].Call.Fn.Name != "runtime.main" {
t.Fatalf("bad stacktrace")
}
})
}
func TestFollowExec(t *testing.T) {
skipOn(t, "follow exec not implemented on freebsd", "freebsd")
skipOn(t, "follow exec not implemented on macOS", "darwin")