
Fixes a repeated stop on the first line of a range-over-func body when stepping through a program. The entry point breakpoint is changed to a special breakpoint that, when hit: - clears the current temporary breakpoints - sets breakpoints like 'next' would, and - resumes execution.
1765 lines
45 KiB
Go
1765 lines
45 KiB
Go
package proc_test
|
|
|
|
import (
|
|
"fmt"
|
|
"go/constant"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/go-delve/delve/pkg/goversion"
|
|
"github.com/go-delve/delve/pkg/proc"
|
|
protest "github.com/go-delve/delve/pkg/proc/test"
|
|
"github.com/go-delve/delve/service/api"
|
|
)
|
|
|
|
type nextTest struct {
|
|
begin, end int
|
|
}
|
|
|
|
type contFunc int
|
|
|
|
const (
|
|
contContinue contFunc = iota
|
|
contNext
|
|
contStep
|
|
contStepout
|
|
contReverseNext
|
|
contReverseStep
|
|
contReverseStepout
|
|
contContinueToBreakpoint
|
|
contNothing
|
|
)
|
|
|
|
type seqTest struct {
|
|
cf contFunc
|
|
pos interface{}
|
|
}
|
|
|
|
func testseq(program string, contFunc contFunc, testcases []nextTest, initialLocation string, t *testing.T) {
|
|
seqTestcases := make([]seqTest, len(testcases)+1)
|
|
seqTestcases[0] = seqTest{contContinue, testcases[0].begin}
|
|
for i := range testcases {
|
|
if i > 0 {
|
|
if testcases[i-1].end != testcases[i].begin {
|
|
panic(fmt.Errorf("begin/end mismatch at index %d", i))
|
|
}
|
|
}
|
|
seqTestcases[i+1] = seqTest{contFunc, testcases[i].end}
|
|
}
|
|
testseq2(t, program, initialLocation, seqTestcases)
|
|
}
|
|
|
|
const traceTestseq2 = true
|
|
|
|
func testseq2(t *testing.T, program string, initialLocation string, testcases []seqTest) {
|
|
testseq2Args(".", []string{}, 0, t, program, initialLocation, testcases)
|
|
}
|
|
|
|
func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *testing.T, program string, initialLocation string, testcases []seqTest) {
|
|
protest.AllowRecording(t)
|
|
t.Helper()
|
|
withTestProcessArgs(program, t, wd, args, buildFlags, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
|
checkBreakpointClear := true
|
|
var bp *proc.Breakpoint
|
|
if initialLocation != "" {
|
|
bp = setFunctionBreakpoint(p, t, initialLocation)
|
|
} else if testcases[0].cf == contContinue {
|
|
bp = setFileBreakpoint(p, t, fixture.Source, testcases[0].pos.(int))
|
|
} else if testcases[0].cf == contNothing {
|
|
// Do nothing
|
|
checkBreakpointClear = false
|
|
} else {
|
|
panic("testseq2 can not set initial breakpoint")
|
|
}
|
|
if traceTestseq2 {
|
|
t.Logf("initial breakpoint %v", bp)
|
|
}
|
|
|
|
testseq2intl(t, fixture, grp, p, bp, testcases)
|
|
|
|
if countBreakpoints(p) != 0 && checkBreakpointClear {
|
|
t.Fatal("Not all breakpoints were cleaned up", len(p.Breakpoints().M))
|
|
}
|
|
})
|
|
}
|
|
|
|
func testseq2intl(t *testing.T, fixture protest.Fixture, grp *proc.TargetGroup, p *proc.Target, bp *proc.Breakpoint, testcases []seqTest) {
|
|
f, ln := currentLineNumber(p, t)
|
|
for i, tc := range testcases {
|
|
switch tc.cf {
|
|
case contNext:
|
|
if traceTestseq2 {
|
|
t.Log("next")
|
|
}
|
|
assertNoError(grp.Next(), t, "Next() returned an error")
|
|
case contStep:
|
|
if traceTestseq2 {
|
|
t.Log("step")
|
|
}
|
|
assertNoError(grp.Step(), t, "Step() returned an error")
|
|
case contStepout:
|
|
if traceTestseq2 {
|
|
t.Log("stepout")
|
|
}
|
|
assertNoError(grp.StepOut(), t, "StepOut() returned an error")
|
|
case contContinue:
|
|
if traceTestseq2 {
|
|
t.Log("continue")
|
|
}
|
|
assertNoError(grp.Continue(), t, "Continue() returned an error")
|
|
if i == 0 {
|
|
if traceTestseq2 {
|
|
t.Log("clearing initial breakpoint")
|
|
}
|
|
err := p.ClearBreakpoint(bp.Addr)
|
|
assertNoError(err, t, "ClearBreakpoint() returned an error")
|
|
}
|
|
case contReverseNext:
|
|
if traceTestseq2 {
|
|
t.Log("reverse-next")
|
|
}
|
|
assertNoError(grp.ChangeDirection(proc.Backward), t, "direction switch")
|
|
assertNoError(grp.Next(), t, "reverse Next() returned an error")
|
|
assertNoError(grp.ChangeDirection(proc.Forward), t, "direction switch")
|
|
case contReverseStep:
|
|
if traceTestseq2 {
|
|
t.Log("reverse-step")
|
|
}
|
|
assertNoError(grp.ChangeDirection(proc.Backward), t, "direction switch")
|
|
assertNoError(grp.Step(), t, "reverse Step() returned an error")
|
|
assertNoError(grp.ChangeDirection(proc.Forward), t, "direction switch")
|
|
case contReverseStepout:
|
|
if traceTestseq2 {
|
|
t.Log("reverse-stepout")
|
|
}
|
|
assertNoError(grp.ChangeDirection(proc.Backward), t, "direction switch")
|
|
assertNoError(grp.StepOut(), t, "reverse StepOut() returned an error")
|
|
assertNoError(grp.ChangeDirection(proc.Forward), t, "direction switch")
|
|
case contContinueToBreakpoint:
|
|
bp := setFileBreakpoint(p, t, fixture.Source, tc.pos.(int))
|
|
if traceTestseq2 {
|
|
t.Log("continue")
|
|
}
|
|
assertNoError(grp.Continue(), t, "Continue() returned an error")
|
|
err := p.ClearBreakpoint(bp.Addr)
|
|
assertNoError(err, t, "ClearBreakpoint() returned an error")
|
|
case contNothing:
|
|
// do nothing
|
|
}
|
|
|
|
if err := p.CurrentThread().Breakpoint().CondError; err != nil {
|
|
t.Logf("breakpoint condition error: %v", err)
|
|
}
|
|
|
|
f, ln = currentLineNumber(p, t)
|
|
regs, _ := p.CurrentThread().Registers()
|
|
pc := regs.PC()
|
|
_, _, fn := p.BinInfo().PCToLine(pc)
|
|
|
|
if traceTestseq2 {
|
|
fnname := "?"
|
|
if fn != nil {
|
|
fnname = fn.Name
|
|
}
|
|
t.Logf("at %#x (%s) %s:%d", pc, fnname, f, ln)
|
|
}
|
|
switch pos := tc.pos.(type) {
|
|
case int:
|
|
if pos >= 0 && ln != pos {
|
|
t.Fatalf("Program did not continue to correct next location expected %d was %s:%d (%#x) (testcase %d)", pos, filepath.Base(f), ln, pc, i)
|
|
}
|
|
case string:
|
|
v := strings.Split(pos, ":")
|
|
tgtln, _ := strconv.Atoi(v[1])
|
|
if !strings.HasSuffix(f, v[0]) || (ln != tgtln) {
|
|
t.Fatalf("Program did not continue to correct next location, expected %s was %s:%d (%#x) (testcase %d)", pos, filepath.Base(f), ln, pc, i)
|
|
}
|
|
case func(*proc.Target):
|
|
pos(p)
|
|
case func(*proc.TargetGroup, *proc.Target):
|
|
pos(grp, p)
|
|
default:
|
|
panic(fmt.Errorf("unexpected type %T", pos))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNextGeneral(t *testing.T) {
|
|
var testcases []nextTest
|
|
|
|
ver, _ := goversion.Parse(runtime.Version())
|
|
|
|
if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 7, Rev: -1}) {
|
|
testcases = []nextTest{
|
|
{17, 19},
|
|
{19, 20},
|
|
{20, 23},
|
|
{23, 24},
|
|
{24, 26},
|
|
{26, 31},
|
|
{31, 23},
|
|
{23, 24},
|
|
{24, 26},
|
|
{26, 31},
|
|
{31, 23},
|
|
{23, 24},
|
|
{24, 26},
|
|
{26, 27},
|
|
{27, 28},
|
|
{28, 34},
|
|
}
|
|
} else {
|
|
testcases = []nextTest{
|
|
{17, 19},
|
|
{19, 20},
|
|
{20, 23},
|
|
{23, 24},
|
|
{24, 26},
|
|
{26, 31},
|
|
{31, 23},
|
|
{23, 24},
|
|
{24, 26},
|
|
{26, 31},
|
|
{31, 23},
|
|
{23, 24},
|
|
{24, 26},
|
|
{26, 27},
|
|
{27, 34},
|
|
}
|
|
}
|
|
|
|
testseq("testnextprog", contNext, testcases, "main.testnext", t)
|
|
}
|
|
|
|
func TestNextFunctionReturn(t *testing.T) {
|
|
testcases := []nextTest{
|
|
{13, 14},
|
|
{14, 15},
|
|
{15, 35},
|
|
}
|
|
protest.AllowRecording(t)
|
|
testseq("testnextprog", contNext, testcases, "main.helloworld", t)
|
|
}
|
|
|
|
func TestNextFunctionReturnDefer(t *testing.T) {
|
|
var testcases []nextTest
|
|
|
|
ver, _ := goversion.Parse(runtime.Version())
|
|
|
|
if ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 9, Rev: -1}) {
|
|
testcases = []nextTest{
|
|
{5, 6},
|
|
{6, 9},
|
|
{9, 10},
|
|
}
|
|
} else {
|
|
testcases = []nextTest{
|
|
{5, 8},
|
|
{8, 9},
|
|
{9, 10},
|
|
}
|
|
}
|
|
protest.AllowRecording(t)
|
|
testseq("testnextdefer", contNext, testcases, "main.main", t)
|
|
}
|
|
|
|
func TestNextDeferReturnAndDirectCall(t *testing.T) {
|
|
// Next should not step into a deferred function if it is called
|
|
// directly, only if it is called through a panic or a deferreturn.
|
|
// Here we test the case where the function is called by a deferreturn
|
|
testseq("defercall", contNext, []nextTest{
|
|
{9, 10},
|
|
{10, 11},
|
|
{11, 12},
|
|
{12, 13},
|
|
{13, 28}}, "main.callAndDeferReturn", t)
|
|
}
|
|
|
|
func TestNextPanicAndDirectCall(t *testing.T) {
|
|
// Next should not step into a deferred function if it is called
|
|
// directly, only if it is called through a panic or a deferreturn.
|
|
// Here we test the case where the function is called by a panic
|
|
testseq("defercall", contNext, []nextTest{
|
|
{15, 16},
|
|
{16, 17},
|
|
{17, 18},
|
|
{18, 6}}, "main.callAndPanic2", t)
|
|
}
|
|
|
|
func TestStepCall(t *testing.T) {
|
|
testseq("testnextprog", contStep, []nextTest{
|
|
{34, 13},
|
|
{13, 14}}, "", t)
|
|
}
|
|
|
|
func TestStepCallPtr(t *testing.T) {
|
|
// Tests that Step works correctly when calling functions with a
|
|
// function pointer.
|
|
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 11) && !protest.RegabiSupported() {
|
|
testseq("teststepprog", contStep, []nextTest{
|
|
{9, 10},
|
|
{10, 6},
|
|
{6, 7},
|
|
{7, 11}}, "", t)
|
|
} else {
|
|
if runtime.GOOS == "linux" && runtime.GOARCH == "ppc64le" && buildMode == "pie" {
|
|
testseq("teststepprog", contStep, []nextTest{
|
|
{9, 10},
|
|
{10, 5},
|
|
{5, 6},
|
|
{6, 7},
|
|
{7, 10},
|
|
{10, 11}}, "", t)
|
|
} else {
|
|
testseq("teststepprog", contStep, []nextTest{
|
|
{9, 10},
|
|
{10, 5},
|
|
{5, 6},
|
|
{6, 7},
|
|
{7, 11}}, "", t)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStepReturnAndPanic(t *testing.T) {
|
|
// Tests that Step works correctly when returning from functions
|
|
// and when a deferred function is called when panic'ing.
|
|
testseq("defercall", contStep, []nextTest{
|
|
{17, 6},
|
|
{6, 7},
|
|
{7, 18},
|
|
{18, 6},
|
|
{6, 7}}, "", t)
|
|
}
|
|
|
|
func TestStepDeferReturn(t *testing.T) {
|
|
// Tests that Step works correctly when a deferred function is
|
|
// called during a return.
|
|
testseq("defercall", contStep, []nextTest{
|
|
{11, 6},
|
|
{6, 7},
|
|
{7, 12},
|
|
{12, 13},
|
|
{13, 6},
|
|
{6, 7},
|
|
{7, 13},
|
|
{13, 28}}, "", t)
|
|
}
|
|
|
|
func TestStepIgnorePrivateRuntime(t *testing.T) {
|
|
// Tests that Step will ignore calls to private runtime functions
|
|
// (such as runtime.convT2E in this case)
|
|
switch {
|
|
case goversion.VersionAfterOrEqual(runtime.Version(), 1, 17) && protest.RegabiSupported():
|
|
testseq("teststepprog", contStep, []nextTest{
|
|
{21, 13},
|
|
{13, 14},
|
|
{14, 15},
|
|
{15, 17},
|
|
{17, 22}}, "", t)
|
|
case goversion.VersionAfterOrEqual(runtime.Version(), 1, 17):
|
|
testseq("teststepprog", contStep, []nextTest{
|
|
{21, 14},
|
|
{14, 15},
|
|
{15, 17},
|
|
{17, 22}}, "", t)
|
|
case goversion.VersionAfterOrEqual(runtime.Version(), 1, 11):
|
|
testseq("teststepprog", contStep, []nextTest{
|
|
{21, 14},
|
|
{14, 15},
|
|
{15, 22}}, "", t)
|
|
default:
|
|
panic("too old")
|
|
}
|
|
}
|
|
|
|
func TestInlineStep(t *testing.T) {
|
|
skipOn(t, "broken", "ppc64le")
|
|
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) {
|
|
// Versions of go before 1.10 do not have DWARF information for inlined calls
|
|
t.Skip("inlining not supported")
|
|
}
|
|
testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{
|
|
{contContinue, 18},
|
|
{contStep, 6},
|
|
{contStep, 7},
|
|
{contStep, 24},
|
|
{contStep, 25},
|
|
{contStep, 7},
|
|
{contStep, 18},
|
|
{contStep, 19},
|
|
})
|
|
}
|
|
|
|
func TestInlineNext(t *testing.T) {
|
|
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) {
|
|
// Versions of go before 1.10 do not have DWARF information for inlined calls
|
|
t.Skip("inlining not supported")
|
|
}
|
|
testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{
|
|
{contContinue, 18},
|
|
{contStep, 6},
|
|
{contNext, 7},
|
|
{contNext, 18},
|
|
{contNext, 19},
|
|
})
|
|
}
|
|
|
|
func TestInlineStepOver(t *testing.T) {
|
|
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) {
|
|
// Versions of go before 1.10 do not have DWARF information for inlined calls
|
|
t.Skip("inlining not supported")
|
|
}
|
|
testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{
|
|
{contContinue, 18},
|
|
{contNext, 19},
|
|
{contNext, 20},
|
|
})
|
|
}
|
|
|
|
func TestInlineStepOut(t *testing.T) {
|
|
if ver, _ := goversion.Parse(runtime.Version()); ver.Major >= 0 && !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 10, Rev: -1}) {
|
|
// Versions of go before 1.10 do not have DWARF information for inlined calls
|
|
t.Skip("inlining not supported")
|
|
}
|
|
testseq2Args(".", []string{}, protest.EnableInlining, t, "testinline", "", []seqTest{
|
|
{contContinue, 18},
|
|
{contStep, 6},
|
|
{contStepout, 18},
|
|
})
|
|
}
|
|
|
|
func TestBackwardNextGeneral(t *testing.T) {
|
|
if testBackend != "rr" {
|
|
t.Skip("Reverse stepping test needs rr")
|
|
}
|
|
testseq2(t, "testnextprog", "main.helloworld", []seqTest{
|
|
{contContinue, 13},
|
|
{contNext, 14},
|
|
{contReverseNext, 13},
|
|
{contReverseNext, 34},
|
|
{contReverseNext, 28},
|
|
{contReverseNext, 27},
|
|
{contReverseNext, 26},
|
|
{contReverseNext, 24},
|
|
{contReverseNext, 23},
|
|
{contReverseNext, 31},
|
|
{contReverseNext, 26},
|
|
{contReverseNext, 24},
|
|
{contReverseNext, 23},
|
|
{contReverseNext, 31},
|
|
{contReverseNext, 26},
|
|
{contReverseNext, 24},
|
|
{contReverseNext, 23},
|
|
{contReverseNext, 20},
|
|
{contReverseNext, 19},
|
|
{contReverseNext, 17},
|
|
{contReverseNext, 39},
|
|
{contReverseNext, 38},
|
|
{contReverseNext, 37},
|
|
})
|
|
}
|
|
|
|
func TestBackwardStepOutGeneral(t *testing.T) {
|
|
if testBackend != "rr" {
|
|
t.Skip("Reverse stepping test needs rr")
|
|
}
|
|
testseq2(t, "testnextprog", "main.helloworld", []seqTest{
|
|
{contContinue, 13},
|
|
{contNext, 14},
|
|
{contReverseStepout, 34},
|
|
{contReverseStepout, 39},
|
|
})
|
|
}
|
|
|
|
func TestBackwardStepGeneral(t *testing.T) {
|
|
if testBackend != "rr" {
|
|
t.Skip("Reverse stepping test needs rr")
|
|
}
|
|
testseq2(t, "testnextprog", "main.helloworld", []seqTest{
|
|
{contContinue, 13},
|
|
{contNext, 14},
|
|
{contReverseStep, 13},
|
|
{contReverseStep, 34},
|
|
{contReverseStep, 28},
|
|
{contReverseNext, 27}, // skip fmt.Printf
|
|
{contReverseStep, 26},
|
|
{contReverseStep, 24},
|
|
{contReverseStep, 23},
|
|
{contReverseStep, 11},
|
|
{contReverseNext, 10}, // skip time.Sleep
|
|
{contReverseStep, 9},
|
|
|
|
{contReverseStep, 31},
|
|
{contReverseStep, 26},
|
|
{contReverseStep, 24},
|
|
{contReverseStep, 23},
|
|
{contReverseStep, 11},
|
|
{contReverseNext, 10}, // skip time.Sleep
|
|
{contReverseStep, 9},
|
|
|
|
{contReverseStep, 31},
|
|
{contReverseStep, 26},
|
|
{contReverseStep, 24},
|
|
{contReverseStep, 23},
|
|
{contReverseStep, 20},
|
|
{contReverseStep, 19},
|
|
{contReverseStep, 17},
|
|
{contReverseStep, 39},
|
|
{contReverseStep, 38},
|
|
{contReverseStep, 37},
|
|
})
|
|
}
|
|
|
|
func TestBackwardNextDeferPanic(t *testing.T) {
|
|
if testBackend != "rr" {
|
|
t.Skip("Reverse stepping test needs rr")
|
|
}
|
|
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 18) {
|
|
testseq2(t, "defercall", "", []seqTest{
|
|
{contContinue, 12},
|
|
{contReverseNext, 11},
|
|
{contReverseNext, 10},
|
|
{contReverseNext, 9},
|
|
{contReverseNext, 27},
|
|
|
|
{contContinueToBreakpoint, 12}, // skip first call to sampleFunction
|
|
{contContinueToBreakpoint, 6}, // go to call to sampleFunction through deferreturn
|
|
{contReverseNext, -1}, // runtime.deferreturn, maybe we should try to skip this
|
|
{contReverseStepout, 13},
|
|
{contReverseNext, 12},
|
|
{contReverseNext, 11},
|
|
{contReverseNext, 10},
|
|
{contReverseNext, 9},
|
|
{contReverseNext, 27},
|
|
|
|
{contContinueToBreakpoint, 18}, // go to panic call
|
|
{contNext, 6}, // panic so the deferred call happens
|
|
{contReverseNext, 18},
|
|
{contReverseNext, 17},
|
|
{contReverseNext, 16},
|
|
{contReverseNext, 15},
|
|
{contReverseNext, 23},
|
|
{contReverseNext, 22},
|
|
{contReverseNext, 21},
|
|
{contReverseNext, 28},
|
|
})
|
|
} else {
|
|
testseq2(t, "defercall", "", []seqTest{
|
|
{contContinue, 12},
|
|
{contReverseNext, 11},
|
|
{contReverseNext, 10},
|
|
{contReverseNext, 9},
|
|
{contReverseNext, 27},
|
|
|
|
{contContinueToBreakpoint, 12}, // skip first call to sampleFunction
|
|
{contContinueToBreakpoint, 6}, // go to call to sampleFunction through deferreturn
|
|
{contReverseNext, 13},
|
|
{contReverseNext, 12},
|
|
{contReverseNext, 11},
|
|
{contReverseNext, 10},
|
|
{contReverseNext, 9},
|
|
{contReverseNext, 27},
|
|
|
|
{contContinueToBreakpoint, 18}, // go to panic call
|
|
{contNext, 6}, // panic so the deferred call happens
|
|
{contReverseNext, 18},
|
|
{contReverseNext, 17},
|
|
{contReverseNext, 16},
|
|
{contReverseNext, 15},
|
|
{contReverseNext, 23},
|
|
{contReverseNext, 22},
|
|
{contReverseNext, 21},
|
|
{contReverseNext, 28},
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStepIntoWrapperForEmbeddedPointer(t *testing.T) {
|
|
skipOn(t, "N/A", "linux", "386", "pie") // skipping wrappers doesn't work on linux/386/PIE due to the use of get_pc_thunk
|
|
// Under some circumstances (when using an interface to call a method on an
|
|
// embedded field, see _fixtures/ifaceembcall.go) the compiler will
|
|
// autogenerate a wrapper function that uses a tail call (i.e. it ends in
|
|
// an unconditional jump instruction to a different function).
|
|
// Delve should be able to step into this tail call.
|
|
testseq2(t, "ifaceembcall", "", []seqTest{
|
|
{contContinue, 28}, // main.main, the line calling iface.PtrReceiver()
|
|
{contStep, 18}, // main.(*A).PtrReceiver
|
|
{contStep, 19},
|
|
{contStepout, 28},
|
|
{contContinueToBreakpoint, 29}, // main.main, the line calling iface.NonPtrReceiver()
|
|
{contStep, 22}, // main.(A).NonPtrReceiver
|
|
{contStep, 23},
|
|
{contStepout, 29}})
|
|
|
|
// same test but with next instead of stepout
|
|
if goversion.VersionAfterOrEqual(runtime.Version(), 1, 14) && runtime.GOARCH != "386" && !goversion.VersionAfterOrEqualRev(runtime.Version(), 1, 15, 4) {
|
|
// Line numbers generated for versions 1.14 through 1.15.3 on any system except linux/386
|
|
testseq2(t, "ifaceembcall", "", []seqTest{
|
|
{contContinue, 28}, // main.main, the line calling iface.PtrReceiver()
|
|
{contStep, 18}, // main.(*A).PtrReceiver
|
|
{contNext, 19},
|
|
{contNext, 19},
|
|
{contNext, 28},
|
|
{contContinueToBreakpoint, 29}, // main.main, the line calling iface.NonPtrReceiver()
|
|
{contStep, 22},
|
|
{contNext, 23},
|
|
{contNext, 23},
|
|
{contNext, 29},
|
|
})
|
|
} else {
|
|
testseq2(t, "ifaceembcall", "", []seqTest{
|
|
{contContinue, 28}, // main.main, the line calling iface.PtrReceiver()
|
|
{contStep, 18}, // main.(*A).PtrReceiver
|
|
{contNext, 19},
|
|
{contNext, 28},
|
|
{contContinueToBreakpoint, 29}, // main.main, the line calling iface.NonPtrReceiver()
|
|
{contStep, 22},
|
|
{contNext, 23},
|
|
{contNext, 29},
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNextGenericMethodThroughInterface(t *testing.T) {
|
|
// Tests that autogenerated wrappers for generic methods called through an
|
|
// interface are skipped.
|
|
|
|
varcheck := func(p *proc.Target) {
|
|
yvar := evalVariable(p, t, "y")
|
|
yval, _ := constant.Int64Val(yvar.Value)
|
|
if yval != 2 {
|
|
t.Errorf("expected 2 got %#v", yvar.Value)
|
|
}
|
|
}
|
|
|
|
if runtime.GOOS == "linux" && runtime.GOARCH == "386" {
|
|
testseq2(t, "genericintoiface", "main.callf", []seqTest{
|
|
{contContinue, 17},
|
|
{contStep, 18},
|
|
{contStep, 10},
|
|
{contNothing, varcheck},
|
|
{contNext, 11},
|
|
{contNext, 19},
|
|
})
|
|
} else {
|
|
testseq2(t, "genericintoiface", "main.callf", []seqTest{
|
|
{contContinue, 17},
|
|
{contStep, 18},
|
|
{contStep, 9},
|
|
{contNext, 10},
|
|
{contNothing, varcheck},
|
|
{contNext, 11},
|
|
{contNext, 19},
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRangeOverFuncNext(t *testing.T) {
|
|
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 23) {
|
|
t.Skip("N/A")
|
|
}
|
|
|
|
var bp *proc.Breakpoint
|
|
|
|
funcBreak := func(t *testing.T, fnname string) seqTest {
|
|
return seqTest{
|
|
contNothing,
|
|
func(p *proc.Target) {
|
|
bp = setFunctionBreakpoint(p, t, fnname)
|
|
}}
|
|
}
|
|
|
|
clearBreak := func(t *testing.T) seqTest {
|
|
return seqTest{
|
|
contNothing,
|
|
func(p *proc.Target) {
|
|
assertNoError(p.ClearBreakpoint(bp.Addr), t, "ClearBreakpoint")
|
|
}}
|
|
}
|
|
|
|
notAtEntryPoint := func(t *testing.T) seqTest {
|
|
return seqTest{contNothing, func(p *proc.Target) {
|
|
pc := currentPC(p, t)
|
|
fn := p.BinInfo().PCToFunc(pc)
|
|
if pc == fn.Entry {
|
|
t.Fatalf("current PC is entry point")
|
|
}
|
|
}}
|
|
}
|
|
|
|
nx := func(n int) seqTest {
|
|
return seqTest{contNext, n}
|
|
}
|
|
|
|
assertLocals := func(t *testing.T, varnames ...string) seqTest {
|
|
return seqTest{
|
|
contNothing,
|
|
func(p *proc.Target) {
|
|
scope, err := proc.GoroutineScope(p, p.CurrentThread())
|
|
assertNoError(err, t, "GoroutineScope")
|
|
vars, err := scope.Locals(0, "")
|
|
assertNoError(err, t, "Locals")
|
|
|
|
gotnames := make([]string, len(vars))
|
|
for i := range vars {
|
|
gotnames[i] = vars[i].Name
|
|
}
|
|
|
|
ok := true
|
|
if len(vars) != len(varnames) {
|
|
ok = false
|
|
} else {
|
|
for i := range vars {
|
|
if vars[i].Name != varnames[i] {
|
|
ok = false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if !ok {
|
|
t.Errorf("Wrong variable names, expected %q, got %q", varnames, gotnames)
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
assertEval := func(t *testing.T, exprvals ...string) seqTest {
|
|
return seqTest{
|
|
contNothing,
|
|
func(p *proc.Target) {
|
|
scope, err := proc.GoroutineScope(p, p.CurrentThread())
|
|
assertNoError(err, t, "GoroutineScope")
|
|
for i := 0; i < len(exprvals); i += 2 {
|
|
expr, tgt := exprvals[i], exprvals[i+1]
|
|
v, err := scope.EvalExpression(expr, normalLoadConfig)
|
|
if err != nil {
|
|
t.Errorf("Could not evaluate %q: %v", expr, err)
|
|
} else {
|
|
out := api.ConvertVar(v).SinglelineString()
|
|
if out != tgt {
|
|
t.Errorf("Wrong value for %q, got %q expected %q", expr, out, tgt)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
assertFunc := func(t *testing.T, fname string) seqTest {
|
|
return seqTest{
|
|
contNothing,
|
|
func(p *proc.Target) {
|
|
pc := currentPC(p, t)
|
|
fn := p.BinInfo().PCToFunc(pc)
|
|
if fn.Name != fname {
|
|
t.Errorf("Wrong function name, expected %s got %s", fname, fn.Name)
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
withTestProcessArgs("rangeoverfunc", t, ".", []string{}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
|
t.Run("TestTrickyIterAll1", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestTrickyIterAll"),
|
|
{contContinue, 24}, // TestTrickyIterAll
|
|
nx(25),
|
|
nx(26),
|
|
nx(27), // for _, x := range ...
|
|
assertLocals(t, "trickItAll", "i"),
|
|
assertEval(t, "i", "0"),
|
|
nx(28), // i += x
|
|
assertLocals(t, "trickItAll", "i", "x"),
|
|
assertEval(t,
|
|
"i", "0",
|
|
"x", "30"),
|
|
nx(29), // if i >= 36 {
|
|
nx(32),
|
|
nx(27), // for _, x := range ...
|
|
notAtEntryPoint(t),
|
|
nx(28), // i += x
|
|
assertEval(t,
|
|
"i", "30",
|
|
"x", "7"),
|
|
nx(29), // if i >= 36 {
|
|
nx(30), // break
|
|
nx(32),
|
|
nx(34), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestTrickyIterAll2", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestTrickyIterAll2"),
|
|
{contContinue, 37}, // TestTrickyIterAll2
|
|
nx(38),
|
|
nx(39),
|
|
nx(40), // for _, x := range...
|
|
nx(41),
|
|
nx(42),
|
|
nx(40),
|
|
notAtEntryPoint(t),
|
|
nx(41),
|
|
nx(42),
|
|
nx(42), // different function from the one above...
|
|
nx(43),
|
|
})
|
|
})
|
|
|
|
t.Run("TestBreak1", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestBreak1"),
|
|
{contContinue, 46}, // TestBreak1
|
|
nx(47),
|
|
nx(48), // for _, x := range... (x == -1)
|
|
nx(49), // if x == -4
|
|
assertLocals(t, "result", "x"),
|
|
assertEval(t,
|
|
"result", "[]int len: 0, cap: 0, nil",
|
|
"x", "-1"),
|
|
|
|
nx(52), // for _, y := range... (y == 1)
|
|
nx(53), // if y == 3
|
|
assertLocals(t, "result", "x", "y"),
|
|
assertEval(t,
|
|
"result", "[]int len: 0, cap: 0, nil",
|
|
"x", "-1",
|
|
"y", "1"),
|
|
nx(56), // result = append(result, y)
|
|
nx(57),
|
|
nx(52), // for _, y := range... (y == 2)
|
|
notAtEntryPoint(t),
|
|
nx(53), // if y == 3
|
|
assertEval(t,
|
|
"x", "-1",
|
|
"y", "2"),
|
|
nx(56), // result = append(result, y)
|
|
nx(57),
|
|
nx(52), // for _, y := range... (y == 3)
|
|
nx(53), // if y == 3
|
|
assertEval(t,
|
|
"x", "-1",
|
|
"y", "3"),
|
|
nx(54), // break
|
|
nx(57),
|
|
nx(58), // result = append(result, x)
|
|
nx(59),
|
|
|
|
nx(48), // for _, x := range... (x == -2)
|
|
notAtEntryPoint(t),
|
|
nx(49), // if x == -4
|
|
assertEval(t,
|
|
"result", "[]int len: 3, cap: 4, [1,2,-1]",
|
|
"x", "-2"),
|
|
nx(52), // for _, y := range... (y == 1)
|
|
nx(53), // if y == 3
|
|
nx(56), // result = append(result, y)
|
|
nx(57),
|
|
nx(52), // for _, y := range... (y == 2)
|
|
notAtEntryPoint(t),
|
|
nx(53), // if y == 3
|
|
nx(56), // result = append(result, y)
|
|
nx(57),
|
|
nx(52), // for _, y := range... (y == 3)
|
|
nx(53), // if y == 3
|
|
nx(54), // break
|
|
nx(57),
|
|
nx(58), // result = append(result, x)
|
|
nx(59),
|
|
|
|
nx(48), // for _, x := range... (x == -4)
|
|
assertEval(t,
|
|
"result", "[]int len: 6, cap: 8, [1,2,-1,1,2,-2]",
|
|
"x", "-4"),
|
|
nx(49), // if x == -4
|
|
nx(50), // break
|
|
nx(59),
|
|
nx(60),
|
|
nx(61),
|
|
})
|
|
})
|
|
|
|
t.Run("TestBreak2", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestBreak2"),
|
|
|
|
{contContinue, 63}, // TestBreak2
|
|
nx(64),
|
|
nx(65),
|
|
|
|
nx(66), // for _, x := range (x == -1)
|
|
nx(67), // for _, y := range (y == 1)
|
|
nx(68), // if y == 3
|
|
nx(71), // if x == -4
|
|
nx(74), // result = append(result, y)
|
|
nx(75),
|
|
|
|
nx(67), // for _, y := range (y == 2)
|
|
nx(68), // if y == 3
|
|
nx(71), // if x == -4
|
|
nx(74), // result = append(result, y)
|
|
nx(75),
|
|
|
|
nx(67), // for _, y := range (y == 3)
|
|
nx(68), // if y == 3
|
|
nx(69), // break
|
|
nx(75),
|
|
nx(76), // result = append(result, x)
|
|
nx(77),
|
|
|
|
nx(66), // for _, x := range (x == -2)
|
|
nx(67), // for _, y := range (y == 1)
|
|
nx(68), // if y == 3
|
|
nx(71), // if x == -4
|
|
nx(74), // result = append(result, y)
|
|
nx(75),
|
|
|
|
nx(67), // for _, y := range (y == 2)
|
|
nx(68), // if y == 3
|
|
nx(71), // if x == -4
|
|
nx(74), // result = append(result, y)
|
|
nx(75),
|
|
|
|
nx(67), // for _, y := range (y == 3)
|
|
nx(68), // if y == 3
|
|
nx(69), // break
|
|
nx(75),
|
|
nx(76), // result = append(result, x)
|
|
nx(77),
|
|
|
|
nx(66), // for _, x := range (x == -4)
|
|
nx(67), // for _, y := range (y == 1)
|
|
nx(68), // if y == 3
|
|
nx(71), // if x == -4
|
|
nx(72), // break outer
|
|
nx(75),
|
|
nx(77),
|
|
nx(78),
|
|
nx(79),
|
|
})
|
|
})
|
|
|
|
t.Run("TestMultiCont0", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestMultiCont0"),
|
|
{contContinue, 81},
|
|
nx(82),
|
|
nx(84),
|
|
nx(85), // for _, w := range (w == 1000)
|
|
nx(86), // result = append(result, w)
|
|
assertEval(t,
|
|
"w", "1000",
|
|
"result", "[]int len: 0, cap: 0, nil"),
|
|
nx(87), // if w == 2000
|
|
assertLocals(t, "result", "w"),
|
|
assertEval(t, "result", "[]int len: 1, cap: 1, [1000]"),
|
|
nx(90), // for _, x := range (x == 100)
|
|
nx(91), // for _, y := range (y == 10)
|
|
nx(92), // result = append(result, y)
|
|
assertLocals(t, "result", "w", "x", "y"),
|
|
assertEval(t,
|
|
"w", "1000",
|
|
"x", "100",
|
|
"y", "10"),
|
|
|
|
nx(93), // for _, z := range (z == 1)
|
|
nx(94), // if z&1 == 1
|
|
assertLocals(t, "result", "w", "x", "y", "z"),
|
|
assertEval(t,
|
|
"w", "1000",
|
|
"x", "100",
|
|
"y", "10",
|
|
"z", "1"),
|
|
nx(95), // continue
|
|
|
|
nx(93), // for _, z := range (z == 2)
|
|
nx(94), // if z&1 == 1
|
|
assertEval(t, "z", "2"),
|
|
nx(97), // result = append(result, z)
|
|
nx(98), // if z >= 4 {
|
|
nx(101),
|
|
|
|
nx(93), // for _, z := range (z == 3)
|
|
nx(94), // if z&1 == 1
|
|
assertEval(t, "z", "3"),
|
|
nx(95), // continue
|
|
|
|
nx(93), // for _, z := range (z == 4)
|
|
nx(94), // if z&1 == 1
|
|
assertEval(t, "z", "4"),
|
|
nx(97), // result = append(result, z)
|
|
assertEval(t, "result", "[]int len: 3, cap: 4, [1000,10,2]"),
|
|
nx(98), // if z >= 4 {
|
|
nx(99), // continue W
|
|
nx(101),
|
|
nx(103),
|
|
nx(105),
|
|
|
|
nx(85), // for _, w := range (w == 2000)
|
|
nx(86), // result = append(result, w)
|
|
nx(87), // if w == 2000
|
|
assertEval(t,
|
|
"w", "2000",
|
|
"result", "[]int len: 5, cap: 8, [1000,10,2,4,2000]"),
|
|
nx(88), // break
|
|
nx(106),
|
|
nx(107), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestPanickyIterator1", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestPanickyIterator1"),
|
|
{contContinue, 110},
|
|
nx(111),
|
|
nx(112),
|
|
nx(116), // for _, z := range (z == 1)
|
|
nx(117), // result = append(result, z)
|
|
nx(118), // if z == 4
|
|
nx(121),
|
|
|
|
nx(116), // for _, z := range (z == 2)
|
|
nx(117), // result = append(result, z)
|
|
nx(118), // if z == 4
|
|
nx(121),
|
|
|
|
nx(116), // for _, z := range (z == 3)
|
|
nx(117), // result = append(result, z)
|
|
nx(118), // if z == 4
|
|
nx(121),
|
|
|
|
nx(116), // for _, z := range (z == 4)
|
|
nx(117), // result = append(result, z)
|
|
nx(118), // if z == 4
|
|
nx(119), // break
|
|
|
|
nx(112), // defer func()
|
|
nx(113), // r := recover()
|
|
nx(114), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestPanickyIterator2", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestPanickyIterator2"),
|
|
{contContinue, 125},
|
|
nx(126),
|
|
nx(127),
|
|
nx(131), // for _, x := range (x == 100)
|
|
nx(132),
|
|
nx(133),
|
|
nx(135), // for _, y := range (y == 10)
|
|
nx(136), // result = append(result, y)
|
|
nx(139), // for k, z := range (k == 0, z == 1)
|
|
nx(140), // result = append(result, z)
|
|
nx(141), // if k == 1
|
|
nx(144),
|
|
|
|
nx(139), // for k, z := range (k == 1, z == 2)
|
|
nx(140), // result = append(result, z)
|
|
nx(141), // if k == 1
|
|
nx(142), // break Y
|
|
nx(135),
|
|
nx(145),
|
|
nx(127), // defer func()
|
|
nx(128), // r := recover()
|
|
nx(129), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestPanickyIteratorWithNewDefer", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestPanickyIteratorWithNewDefer"),
|
|
{contContinue, 149},
|
|
nx(150),
|
|
nx(151),
|
|
nx(155), // for _, x := range (x == 100)
|
|
nx(156),
|
|
nx(157),
|
|
nx(159), // for _, y := range (y == 10)
|
|
nx(160),
|
|
nx(163), // result = append(result, y)
|
|
nx(166), // for k, z := range (k == 0, z == 1)
|
|
nx(167), // result = append(result, z)
|
|
nx(168), // if k == 1
|
|
nx(171),
|
|
|
|
nx(166), // for k, z := range (k == 0, z == 1)
|
|
nx(167), // result = append(result, z)
|
|
nx(168), // if k == 1
|
|
nx(169), // break Y
|
|
nx(159),
|
|
nx(172),
|
|
nx(160), // defer func()
|
|
nx(161), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestLongReturn", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestLongReturn"),
|
|
{contContinue, 181},
|
|
nx(182), // for _, x := range (x == 1)
|
|
nx(183), // for _, y := range (y == 10)
|
|
nx(184), // if y == 10
|
|
nx(185), // return
|
|
nx(187),
|
|
nx(189),
|
|
nx(178), // into TestLongReturnWrapper, fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestGotoA1", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestGotoA1"),
|
|
{contContinue, 192},
|
|
nx(193),
|
|
nx(194), // for _, x := range (x == -1)
|
|
nx(195), // result = append(result, x)
|
|
nx(196), // if x == -4
|
|
nx(199), // for _, y := range (y == 1)
|
|
nx(200), // if y == 3
|
|
nx(203), // result = append(result, y)
|
|
nx(204),
|
|
|
|
nx(199), // for _, y := range (y == 2)
|
|
nx(200), // if y == 3
|
|
nx(203), // result = append(result, y)
|
|
nx(204),
|
|
|
|
nx(199), // for _, y := range (y == 3)
|
|
nx(200), // if y == 3
|
|
nx(201), // goto A
|
|
nx(204),
|
|
nx(206), // result = append(result, x)
|
|
nx(207),
|
|
|
|
nx(194), // for _, x := range (x == -4)
|
|
nx(195), // result = append(result, x)
|
|
nx(196), // if x == -4
|
|
nx(197), // break
|
|
nx(207),
|
|
nx(208), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestGotoB1", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestGotoB1"),
|
|
{contContinue, 211},
|
|
nx(212),
|
|
nx(213), // for _, x := range (x == -1)
|
|
nx(214), // result = append(result, x)
|
|
nx(215), // if x == -4
|
|
nx(218), // for _, y := range (y == 1)
|
|
nx(219), // if y == 3
|
|
nx(222), // result = append(result, y)
|
|
nx(223),
|
|
|
|
nx(218), // for _, y := range (y == 2)
|
|
nx(219), // if y == 3
|
|
nx(222), // result = append(result, y)
|
|
nx(223),
|
|
|
|
nx(218), // for _, y := range (y == 3)
|
|
nx(219), // if y == 3
|
|
nx(220), // goto B
|
|
nx(223),
|
|
nx(225),
|
|
nx(227), // result = append(result, 999)
|
|
nx(228), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestRecur", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestRecur"),
|
|
{contContinue, 231},
|
|
clearBreak(t),
|
|
nx(232), // result := []int{}
|
|
assertEval(t, "n", "3"),
|
|
nx(233), // if n > 0 {
|
|
nx(234), // TestRecur
|
|
|
|
nx(236), // for _, x := range (x == 10)
|
|
assertFunc(t, "main.TestRecur"),
|
|
nx(237), // result = ...
|
|
assertEval(t, "n", "3"),
|
|
assertFunc(t, "main.TestRecur-range1"),
|
|
assertEval(t, "x", "10", "n", "3"),
|
|
nx(238), // if n == 3
|
|
nx(239), // TestRecur(0)
|
|
nx(241),
|
|
|
|
nx(236), // for _, x := range (x == 20)
|
|
nx(237), // result = ...
|
|
assertEval(t, "x", "20", "n", "3"),
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestRangeOverFuncStepOut(t *testing.T) {
|
|
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 23) {
|
|
t.Skip("N/A")
|
|
}
|
|
|
|
testseq2(t, "rangeoverfunc", "", []seqTest{
|
|
{contContinue, 97},
|
|
{contStepout, 251},
|
|
})
|
|
}
|
|
|
|
func TestRangeOverFuncNextInlined(t *testing.T) {
|
|
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 23) {
|
|
t.Skip("N/A")
|
|
}
|
|
|
|
var bp *proc.Breakpoint
|
|
|
|
funcBreak := func(t *testing.T, fnname string) seqTest {
|
|
return seqTest{
|
|
contNothing,
|
|
func(p *proc.Target) {
|
|
bp = setFunctionBreakpoint(p, t, fnname)
|
|
}}
|
|
}
|
|
|
|
clearBreak := func(t *testing.T) seqTest {
|
|
return seqTest{
|
|
contNothing,
|
|
func(p *proc.Target) {
|
|
assertNoError(p.ClearBreakpoint(bp.Addr), t, "ClearBreakpoint")
|
|
}}
|
|
}
|
|
|
|
nx := func(n int) seqTest {
|
|
return seqTest{contNext, n}
|
|
}
|
|
|
|
assertLocals := func(t *testing.T, varnames ...string) seqTest {
|
|
return seqTest{
|
|
contNothing,
|
|
func(p *proc.Target) {
|
|
scope, err := proc.GoroutineScope(p, p.CurrentThread())
|
|
assertNoError(err, t, "GoroutineScope")
|
|
vars, err := scope.Locals(0, "")
|
|
assertNoError(err, t, "Locals")
|
|
|
|
gotnames := make([]string, len(vars))
|
|
for i := range vars {
|
|
gotnames[i] = vars[i].Name
|
|
}
|
|
|
|
ok := true
|
|
if len(vars) != len(varnames) {
|
|
ok = false
|
|
} else {
|
|
for i := range vars {
|
|
if vars[i].Name != varnames[i] {
|
|
ok = false
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if !ok {
|
|
t.Errorf("Wrong variable names, expected %q, got %q", varnames, gotnames)
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
assertEval := func(t *testing.T, exprvals ...string) seqTest {
|
|
return seqTest{
|
|
contNothing,
|
|
func(p *proc.Target) {
|
|
scope, err := proc.GoroutineScope(p, p.CurrentThread())
|
|
assertNoError(err, t, "GoroutineScope")
|
|
for i := 0; i < len(exprvals); i += 2 {
|
|
expr, tgt := exprvals[i], exprvals[i+1]
|
|
v, err := scope.EvalExpression(expr, normalLoadConfig)
|
|
if err != nil {
|
|
t.Errorf("Could not evaluate %q: %v", expr, err)
|
|
} else {
|
|
out := api.ConvertVar(v).SinglelineString()
|
|
if out != tgt {
|
|
t.Errorf("Wrong value for %q, got %q expected %q", expr, out, tgt)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
assertFunc := func(t *testing.T, fname string) seqTest {
|
|
return seqTest{
|
|
contNothing,
|
|
func(p *proc.Target) {
|
|
pc := currentPC(p, t)
|
|
fn := p.BinInfo().PCToFunc(pc)
|
|
if fn.Name != fname {
|
|
t.Errorf("Wrong function name, expected %s got %s", fname, fn.Name)
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
withTestProcessArgs("rangeoverfunc", t, ".", []string{}, protest.EnableInlining, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
|
t.Run("TestTrickyIterAll1", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestTrickyIterAll"),
|
|
{contContinue, 24}, // TestTrickyIterAll
|
|
nx(25),
|
|
nx(26),
|
|
nx(27), // for _, x := range ...
|
|
assertLocals(t, "trickItAll", "i"),
|
|
assertEval(t, "i", "0"),
|
|
nx(28), // i += x
|
|
assertLocals(t, "trickItAll", "i", "x"),
|
|
assertEval(t,
|
|
"i", "0",
|
|
"x", "30"),
|
|
nx(29), // if i >= 36 {
|
|
nx(32),
|
|
nx(27), // for _, x := range ...
|
|
nx(28), // i += x
|
|
assertEval(t,
|
|
"i", "30",
|
|
"x", "7"),
|
|
nx(29), // if i >= 36 {
|
|
nx(30), // break
|
|
nx(27),
|
|
nx(32),
|
|
nx(34), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestTrickyIterAll2", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestTrickyIterAll2"),
|
|
{contContinue, 37}, // TestTrickyIterAll2
|
|
nx(38),
|
|
nx(39),
|
|
nx(40), // for _, x := range...
|
|
nx(41),
|
|
nx(42),
|
|
nx(40),
|
|
nx(41),
|
|
nx(42),
|
|
nx(40),
|
|
nx(42),
|
|
nx(43),
|
|
})
|
|
})
|
|
|
|
t.Run("TestBreak1", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestBreak1"),
|
|
{contContinue, 46}, // TestBreak1
|
|
nx(47),
|
|
nx(48), // for _, x := range... (x == -1)
|
|
nx(49), // if x == -4
|
|
assertLocals(t, "result", "x"),
|
|
assertEval(t,
|
|
"result", "[]int len: 0, cap: 0, nil",
|
|
"x", "-1"),
|
|
|
|
nx(52), // for _, y := range... (y == 1)
|
|
nx(53), // if y == 3
|
|
assertLocals(t, "result", "x", "y"),
|
|
assertEval(t,
|
|
"result", "[]int len: 0, cap: 0, nil",
|
|
"x", "-1",
|
|
"y", "1"),
|
|
nx(56), // result = append(result, y)
|
|
nx(57),
|
|
nx(52), // for _, y := range... (y == 2)
|
|
nx(53), // if y == 3
|
|
assertEval(t,
|
|
"x", "-1",
|
|
"y", "2"),
|
|
nx(56), // result = append(result, y)
|
|
nx(57),
|
|
nx(52), // for _, y := range... (y == 3)
|
|
nx(53), // if y == 3
|
|
assertEval(t,
|
|
"x", "-1",
|
|
"y", "3"),
|
|
nx(54), // break
|
|
nx(52),
|
|
nx(57),
|
|
nx(58), // result = append(result, x)
|
|
nx(59),
|
|
|
|
nx(48), // for _, x := range... (x == -2)
|
|
nx(49), // if x == -4
|
|
assertEval(t,
|
|
"result", "[]int len: 3, cap: 4, [1,2,-1]",
|
|
"x", "-2"),
|
|
nx(52), // for _, y := range... (y == 1)
|
|
nx(53), // if y == 3
|
|
nx(56), // result = append(result, y)
|
|
nx(57),
|
|
nx(52), // for _, y := range... (y == 2)
|
|
nx(53), // if y == 3
|
|
nx(56), // result = append(result, y)
|
|
nx(57),
|
|
nx(52), // for _, y := range... (y == 3)
|
|
nx(53), // if y == 3
|
|
nx(54), // break
|
|
nx(52),
|
|
nx(57),
|
|
nx(58), // result = append(result, x)
|
|
nx(59),
|
|
|
|
nx(48), // for _, x := range... (x == -4)
|
|
assertEval(t,
|
|
"result", "[]int len: 6, cap: 8, [1,2,-1,1,2,-2]",
|
|
"x", "-4"),
|
|
nx(49), // if x == -4
|
|
nx(50), // break
|
|
nx(48),
|
|
nx(59),
|
|
nx(60),
|
|
nx(61),
|
|
})
|
|
})
|
|
|
|
t.Run("TestBreak2", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestBreak2"),
|
|
|
|
{contContinue, 63}, // TestBreak2
|
|
nx(64),
|
|
nx(65),
|
|
|
|
nx(66), // for _, x := range (x == -1)
|
|
nx(67), // for _, y := range (y == 1)
|
|
nx(68), // if y == 3
|
|
nx(71), // if x == -4
|
|
nx(74), // result = append(result, y)
|
|
nx(75),
|
|
|
|
nx(67), // for _, y := range (y == 2)
|
|
nx(68), // if y == 3
|
|
nx(71), // if x == -4
|
|
nx(74), // result = append(result, y)
|
|
nx(75),
|
|
|
|
nx(67), // for _, y := range (y == 3)
|
|
nx(68), // if y == 3
|
|
nx(69), // break
|
|
nx(67),
|
|
nx(75),
|
|
nx(76), // result = append(result, x)
|
|
nx(77),
|
|
|
|
nx(66), // for _, x := range (x == -2)
|
|
nx(67), // for _, y := range (y == 1)
|
|
nx(68), // if y == 3
|
|
nx(71), // if x == -4
|
|
nx(74), // result = append(result, y)
|
|
nx(75),
|
|
|
|
nx(67), // for _, y := range (y == 2)
|
|
nx(68), // if y == 3
|
|
nx(71), // if x == -4
|
|
nx(74), // result = append(result, y)
|
|
nx(75),
|
|
|
|
nx(67), // for _, y := range (y == 3)
|
|
nx(68), // if y == 3
|
|
nx(69), // break
|
|
nx(67),
|
|
nx(75),
|
|
nx(76), // result = append(result, x)
|
|
nx(77),
|
|
|
|
nx(66), // for _, x := range (x == -4)
|
|
nx(67), // for _, y := range (y == 1)
|
|
nx(68), // if y == 3
|
|
nx(71), // if x == -4
|
|
nx(72), // break outer
|
|
nx(67),
|
|
nx(75),
|
|
nx(66),
|
|
nx(77),
|
|
nx(78),
|
|
nx(79),
|
|
})
|
|
})
|
|
|
|
t.Run("TestMultiCont0", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestMultiCont0"),
|
|
{contContinue, 81},
|
|
nx(82),
|
|
nx(84),
|
|
nx(85), // for _, w := range (w == 1000)
|
|
nx(86), // result = append(result, w)
|
|
assertEval(t,
|
|
"w", "1000",
|
|
"result", "[]int len: 0, cap: 0, nil"),
|
|
nx(87), // if w == 2000
|
|
assertLocals(t, "result", "w"),
|
|
assertEval(t, "result", "[]int len: 1, cap: 1, [1000]"),
|
|
nx(90), // for _, x := range (x == 100)
|
|
nx(91), // for _, y := range (y == 10)
|
|
nx(92), // result = append(result, y)
|
|
assertLocals(t, "result", "w", "x", "y"),
|
|
assertEval(t,
|
|
"w", "1000",
|
|
"x", "100",
|
|
"y", "10"),
|
|
|
|
nx(93), // for _, z := range (z == 1)
|
|
nx(94), // if z&1 == 1
|
|
assertLocals(t, "result", "w", "x", "y", "z"),
|
|
assertEval(t,
|
|
"w", "1000",
|
|
"x", "100",
|
|
"y", "10",
|
|
"z", "1"),
|
|
nx(95), // continue
|
|
|
|
nx(93), // for _, z := range (z == 2)
|
|
nx(94), // if z&1 == 1
|
|
assertEval(t, "z", "2"),
|
|
nx(97), // result = append(result, z)
|
|
nx(98), // if z >= 4 {
|
|
nx(101),
|
|
|
|
nx(93), // for _, z := range (z == 3)
|
|
nx(94), // if z&1 == 1
|
|
assertEval(t, "z", "3"),
|
|
nx(95), // continue
|
|
|
|
nx(93), // for _, z := range (z == 4)
|
|
nx(94), // if z&1 == 1
|
|
assertEval(t, "z", "4"),
|
|
nx(97), // result = append(result, z)
|
|
assertEval(t, "result", "[]int len: 3, cap: 4, [1000,10,2]"),
|
|
nx(98), // if z >= 4 {
|
|
nx(99), // continue W
|
|
nx(93),
|
|
nx(101),
|
|
nx(91),
|
|
nx(103),
|
|
nx(90),
|
|
nx(105),
|
|
|
|
nx(85), // for _, w := range (w == 2000)
|
|
nx(86), // result = append(result, w)
|
|
nx(87), // if w == 2000
|
|
assertEval(t,
|
|
"w", "2000",
|
|
"result", "[]int len: 5, cap: 8, [1000,10,2,4,2000]"),
|
|
nx(88), // break
|
|
nx(85),
|
|
nx(106),
|
|
nx(107), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestPanickyIterator1", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestPanickyIterator1"),
|
|
{contContinue, 110},
|
|
nx(111),
|
|
nx(112),
|
|
nx(116), // for _, z := range (z == 1)
|
|
nx(117), // result = append(result, z)
|
|
nx(118), // if z == 4
|
|
nx(121),
|
|
|
|
nx(116), // for _, z := range (z == 2)
|
|
nx(117), // result = append(result, z)
|
|
nx(118), // if z == 4
|
|
nx(121),
|
|
|
|
nx(116), // for _, z := range (z == 3)
|
|
nx(117), // result = append(result, z)
|
|
nx(118), // if z == 4
|
|
nx(121),
|
|
|
|
nx(116), // for _, z := range (z == 4)
|
|
nx(117), // result = append(result, z)
|
|
nx(118), // if z == 4
|
|
nx(119), // break
|
|
|
|
nx(112), // defer func()
|
|
nx(113), // r := recover()
|
|
nx(114), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestPanickyIterator2", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestPanickyIterator2"),
|
|
{contContinue, 125},
|
|
nx(126),
|
|
nx(127),
|
|
nx(131), // for _, x := range (x == 100)
|
|
nx(132),
|
|
nx(133),
|
|
nx(135), // for _, y := range (y == 10)
|
|
nx(136), // result = append(result, y)
|
|
nx(139), // for k, z := range (k == 0, z == 1)
|
|
nx(140), // result = append(result, z)
|
|
nx(141), // if k == 1
|
|
nx(144),
|
|
|
|
nx(139), // for k, z := range (k == 1, z == 2)
|
|
nx(140), // result = append(result, z)
|
|
nx(141), // if k == 1
|
|
nx(142), // break Y
|
|
nx(135),
|
|
nx(145),
|
|
nx(127), // defer func()
|
|
nx(128), // r := recover()
|
|
nx(129), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestPanickyIteratorWithNewDefer", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestPanickyIteratorWithNewDefer"),
|
|
{contContinue, 149},
|
|
nx(150),
|
|
nx(151),
|
|
nx(155), // for _, x := range (x == 100)
|
|
nx(156),
|
|
nx(157),
|
|
nx(159), // for _, y := range (y == 10)
|
|
nx(160),
|
|
nx(163), // result = append(result, y)
|
|
nx(166), // for k, z := range (k == 0, z == 1)
|
|
nx(167), // result = append(result, z)
|
|
nx(168), // if k == 1
|
|
nx(171),
|
|
|
|
nx(166), // for k, z := range (k == 0, z == 1)
|
|
nx(167), // result = append(result, z)
|
|
nx(168), // if k == 1
|
|
nx(169), // break Y
|
|
nx(159),
|
|
nx(172),
|
|
nx(160), // defer func()
|
|
nx(161), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestLongReturn", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestLongReturn"),
|
|
{contContinue, 181},
|
|
nx(182), // for _, x := range (x == 1)
|
|
nx(183), // for _, y := range (y == 10)
|
|
nx(184), // if y == 10
|
|
nx(185), // return
|
|
nx(183),
|
|
nx(187),
|
|
nx(182),
|
|
nx(189),
|
|
nx(178), // into TestLongReturnWrapper, fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestGotoA1", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestGotoA1"),
|
|
{contContinue, 192},
|
|
nx(193),
|
|
nx(194), // for _, x := range (x == -1)
|
|
nx(195), // result = append(result, x)
|
|
nx(196), // if x == -4
|
|
nx(199), // for _, y := range (y == 1)
|
|
nx(200), // if y == 3
|
|
nx(203), // result = append(result, y)
|
|
nx(204),
|
|
|
|
nx(199), // for _, y := range (y == 2)
|
|
nx(200), // if y == 3
|
|
nx(203), // result = append(result, y)
|
|
nx(204),
|
|
|
|
nx(199), // for _, y := range (y == 3)
|
|
nx(200), // if y == 3
|
|
nx(201), // goto A
|
|
nx(199),
|
|
nx(204),
|
|
nx(206), // result = append(result, x)
|
|
nx(207),
|
|
|
|
nx(194), // for _, x := range (x == -4)
|
|
nx(195), // result = append(result, x)
|
|
nx(196), // if x == -4
|
|
nx(197), // break
|
|
nx(194),
|
|
nx(207),
|
|
nx(208), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestGotoB1", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestGotoB1"),
|
|
{contContinue, 211},
|
|
nx(212),
|
|
nx(213), // for _, x := range (x == -1)
|
|
nx(214), // result = append(result, x)
|
|
nx(215), // if x == -4
|
|
nx(218), // for _, y := range (y == 1)
|
|
nx(219), // if y == 3
|
|
nx(222), // result = append(result, y)
|
|
nx(223),
|
|
|
|
nx(218), // for _, y := range (y == 2)
|
|
nx(219), // if y == 3
|
|
nx(222), // result = append(result, y)
|
|
nx(223),
|
|
|
|
nx(218), // for _, y := range (y == 3)
|
|
nx(219), // if y == 3
|
|
nx(220), // goto B
|
|
nx(218),
|
|
nx(223),
|
|
nx(213),
|
|
nx(225),
|
|
nx(227), // result = append(result, 999)
|
|
nx(228), // fmt.Println
|
|
})
|
|
})
|
|
|
|
t.Run("TestRecur", func(t *testing.T) {
|
|
testseq2intl(t, fixture, grp, p, nil, []seqTest{
|
|
funcBreak(t, "main.TestRecur"),
|
|
{contContinue, 231},
|
|
clearBreak(t),
|
|
nx(232), // result := []int{}
|
|
assertEval(t, "n", "3"),
|
|
nx(233), // if n > 0 {
|
|
nx(234), // TestRecur
|
|
|
|
nx(236), // for _, x := range (x == 10)
|
|
assertFunc(t, "main.TestRecur"),
|
|
assertEval(t, "n", "3"),
|
|
nx(237), // result = ...
|
|
assertFunc(t, "main.TestRecur-range1"),
|
|
assertEval(t, "x", "10", "n", "3"),
|
|
nx(238), // if n == 3
|
|
nx(239), // TestRecur(0)
|
|
nx(241),
|
|
|
|
nx(236), // for _, x := range (x == 20)
|
|
nx(237), // result = ...
|
|
assertEval(t, "x", "20", "n", "3"),
|
|
})
|
|
})
|
|
})
|
|
}
|