
The next-instruction (nexti) command behaves like step-instruction (stepi) however, similar to the `next` command it will step over function calls.
132 lines
5.2 KiB
Go
132 lines
5.2 KiB
Go
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(false), 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
|
|
})
|
|
}
|