mirror of
				https://github.com/go-delve/delve.git
				synced 2025-11-04 06:32:16 +08:00 
			
		
		
		
	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
 | 
						|
	})
 | 
						|
}
 |