From 64f3d346638c29aac3aba21b4d07fc7d55e4957b Mon Sep 17 00:00:00 2001 From: Alessandro Arzilli Date: Sat, 20 Jul 2024 06:29:56 +0200 Subject: [PATCH] proc: move stepping test to their own file (#3784) Move all tests of the functionality of proc.next, proc.(*TargetGroup).Next, proc.(*TargetGroup).Step, proc.(*TargetGroup).StepOut to their own test file. --- _scripts/gen-backend_test_health.go | 14 +- pkg/proc/proc_test.go | 1895 --------------------------- pkg/proc/stepping_test.go | 1817 +++++++++++++++++++++++++ pkg/proc/variables_test.go | 94 ++ 4 files changed, 1921 insertions(+), 1899 deletions(-) create mode 100644 pkg/proc/stepping_test.go diff --git a/_scripts/gen-backend_test_health.go b/_scripts/gen-backend_test_health.go index 387f1593de..008c83fdcf 100644 --- a/_scripts/gen-backend_test_health.go +++ b/_scripts/gen-backend_test_health.go @@ -13,13 +13,12 @@ import ( "strings" ) -func main() { - f, err := parser.ParseFile(new(token.FileSet), "pkg/proc/proc_test.go", nil, 0) +func process(path string, skipped map[string]map[string]int) { + f, err := parser.ParseFile(new(token.FileSet), path, nil, 0) if err != nil { log.Fatalf("could not compile proc_test.go: %v", err) } ntests := 0 - skipped := make(map[string]map[string]int) ast.Inspect(f, func(node ast.Node) bool { switch node := node.(type) { case *ast.File: @@ -62,10 +61,17 @@ func main() { } return false }) +} + +func main() { + skipped := make(map[string]map[string]int) + process("pkg/proc/proc_test.go", skipped) + process("pkg/proc/stepping_test.go", skipped) var fh io.WriteCloser if len(os.Args) > 1 && os.Args[1] == "-" { fh = os.Stdout } else { + var err error fh, err = os.Create("./Documentation/backend_test_health.md") if err != nil { log.Fatalf("could not create backend_test_health.md: %v", err) @@ -92,7 +98,7 @@ func main() { fmt.Fprintf(fh, "\t* %d %s\n", skipped[cond][reason], reason) } } - err = fh.Close() + err := fh.Close() if err != nil { log.Fatalf("could not close output file: %v", err) } diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index 3437a2a41c..d86bf67f19 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -20,7 +20,6 @@ import ( "reflect" "runtime" "sort" - "strconv" "strings" "sync" "testing" @@ -434,10 +433,6 @@ func TestClearBreakpointBreakpoint(t *testing.T) { }) } -type nextTest struct { - begin, end int -} - func countBreakpoints(p *proc.Target) int { bpcount := 0 for _, bp := range p.Breakpoints().M { @@ -448,221 +443,6 @@ func countBreakpoints(p *proc.Target) int { return bpcount } -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 TestNextConcurrent(t *testing.T) { skipOn(t, "broken", "windows", "arm64") testcases := []nextTest{ @@ -749,38 +529,6 @@ func TestNextConcurrentVariant2(t *testing.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 TestNextNetHTTP(t *testing.T) { testcases := []nextTest{ {11, 12}, @@ -1270,99 +1018,6 @@ func evalVariable(p *proc.Target, t testing.TB, symbol string) *proc.Variable { return v } -func setVariable(p *proc.Target, symbol, value string) error { - scope, err := proc.GoroutineScope(p, p.CurrentThread()) - if err != nil { - return err - } - return scope.SetVariable(symbol, value) -} - -func TestVariableEvaluation(t *testing.T) { - protest.AllowRecording(t) - testcases := []struct { - name string - st reflect.Kind - value interface{} - length, cap int64 - childrenlen int - }{ - {"a1", reflect.String, "foofoofoofoofoofoo", 18, 0, 0}, - {"a11", reflect.Array, nil, 3, -1, 3}, - {"a12", reflect.Slice, nil, 2, 2, 2}, - {"a13", reflect.Slice, nil, 3, 3, 3}, - {"a2", reflect.Int, int64(6), 0, 0, 0}, - {"a3", reflect.Float64, float64(7.23), 0, 0, 0}, - {"a4", reflect.Array, nil, 2, -1, 2}, - {"a5", reflect.Slice, nil, 5, 5, 5}, - {"a6", reflect.Struct, nil, 2, 0, 2}, - {"a7", reflect.Ptr, nil, 1, 0, 1}, - {"a8", reflect.Struct, nil, 2, 0, 2}, - {"a9", reflect.Ptr, nil, 1, 0, 1}, - {"baz", reflect.String, "bazburzum", 9, 0, 0}, - {"neg", reflect.Int, int64(-1), 0, 0, 0}, - {"f32", reflect.Float32, float64(float32(1.2)), 0, 0, 0}, - {"c64", reflect.Complex64, complex128(complex64(1 + 2i)), 0, 0, 0}, - {"c128", reflect.Complex128, complex128(2 + 3i), 0, 0, 0}, - {"a6.Baz", reflect.Int, int64(8), 0, 0, 0}, - {"a7.Baz", reflect.Int, int64(5), 0, 0, 0}, - {"a8.Baz", reflect.String, "feh", 3, 0, 0}, - {"a8", reflect.Struct, nil, 2, 0, 2}, - {"i32", reflect.Array, nil, 2, -1, 2}, - {"b1", reflect.Bool, true, 0, 0, 0}, - {"b2", reflect.Bool, false, 0, 0, 0}, - {"f", reflect.Func, "main.barfoo", 0, 0, 0}, - {"ba", reflect.Slice, nil, 200, 200, 64}, - } - - withTestProcess("testvariables", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { - assertNoError(grp.Continue(), t, "Continue() returned an error") - - for _, tc := range testcases { - v := evalVariable(p, t, tc.name) - - if v.Kind != tc.st { - t.Fatalf("%s simple type: expected: %s got: %s", tc.name, tc.st, v.Kind.String()) - } - if v.Value == nil && tc.value != nil { - t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) - } else { - switch v.Kind { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - x, _ := constant.Int64Val(v.Value) - if y, ok := tc.value.(int64); !ok || x != y { - t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) - } - case reflect.Float32, reflect.Float64: - x, _ := constant.Float64Val(v.Value) - if y, ok := tc.value.(float64); !ok || x != y { - t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) - } - case reflect.Complex64, reflect.Complex128: - xr, _ := constant.Float64Val(constant.Real(v.Value)) - xi, _ := constant.Float64Val(constant.Imag(v.Value)) - if y, ok := tc.value.(complex128); !ok || complex(xr, xi) != y { - t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) - } - case reflect.String: - if y, ok := tc.value.(string); !ok || constant.StringVal(v.Value) != y { - t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) - } - } - } - if v.Len != tc.length { - t.Fatalf("%s len: expected: %d got: %d", tc.name, tc.length, v.Len) - } - if v.Cap != tc.cap { - t.Fatalf("%s cap: expected: %d got: %d", tc.name, tc.cap, v.Cap) - } - if len(v.Children) != tc.childrenlen { - t.Fatalf("%s children len: expected %d got: %d", tc.name, tc.childrenlen, len(v.Children)) - } - } - }) -} - func TestFrameEvaluation(t *testing.T) { protest.AllowRecording(t) lenient := false @@ -2373,116 +2028,6 @@ func TestTestvariables2Prologue(t *testing.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 TestIssue561(t *testing.T) { // Step fails to make progress when PC is at a CALL instruction // where a breakpoint is also set. @@ -3988,62 +3533,6 @@ func TestInlinedStacktraceAndVariables(t *testing.T) { }) } -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 TestInlineFunctionList(t *testing.T) { // We should be able to list all functions, even inlined ones. ver, _ := goversion.Parse(runtime.Version()) @@ -4831,152 +4320,6 @@ func BenchmarkConditionalBreakpoints(b *testing.B) { }) } -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 TestIssue1925(t *testing.T) { // Calling a function should not leave cached goroutine information in an // inconsistent state. @@ -4995,52 +4338,6 @@ func TestIssue1925(t *testing.T) { }) } -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 TestStepoutOneliner(t *testing.T) { // The heuristic detecting autogenerated wrappers when stepping out should // not skip oneliner functions. @@ -6127,40 +5424,6 @@ func TestWaitForAttach(t *testing.T) { cmd.Wait() } -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 TestIssue3545(t *testing.T) { protest.AllowRecording(t) withTestProcessArgs("nilptr", t, "", []string{}, protest.EnableOptimization, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { @@ -6262,1164 +5525,6 @@ func TestStepIntoGoroutine(t *testing.T) { }) } -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(27), // for _, x := range ... (TODO: this probably shouldn't be here but it's also very hard to skip stopping here a second time) - 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(40), - 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(48), - 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(52), - 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(52), - 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(66), - nx(67), // for _, y := range (y == 1) - nx(67), - 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(67), - 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(67), - 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(85), - 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(90), - nx(91), // for _, y := range (y == 10) - nx(91), - 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(93), - 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(116), - 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(131), - nx(132), - nx(133), - nx(135), // for _, y := range (y == 10) - nx(135), - nx(136), // result = append(result, y) - nx(139), // for k, z := range (k == 0, z == 1) - nx(139), - 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(155), - nx(156), - nx(157), - nx(159), // for _, y := range (y == 10) - nx(159), - nx(160), - nx(163), // result = append(result, y) - nx(166), // for k, z := range (k == 0, z == 1) - nx(166), - 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(182), - nx(183), // for _, y := range (y == 10) - nx(183), - 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(194), - nx(195), // result = append(result, x) - nx(196), // if x == -4 - nx(199), // for _, y := range (y == 1) - nx(199), - 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(213), - nx(214), // result = append(result, x) - nx(215), // if x == -4 - nx(218), // for _, y := range (y == 1) - nx(218), - 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"), - assertEval(t, "n", "3"), - nx(236), - assertFunc(t, "main.TestRecur-range1"), - assertEval(t, "x", "10", "n", "3"), - nx(237), // result = ... - 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(27), // for _, x := range ... (TODO: this probably shouldn't be here but it's also very hard to skip stopping here a second time) - 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(48), - 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(52), - 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(52), - 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(66), - nx(67), // for _, y := range (y == 1) - nx(67), - 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(67), - 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(67), - 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(85), - 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(90), - nx(91), // for _, y := range (y == 10) - nx(91), - 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(93), - 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(116), - 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(131), - nx(132), - nx(133), - nx(135), // for _, y := range (y == 10) - nx(135), - nx(136), // result = append(result, y) - nx(139), // for k, z := range (k == 0, z == 1) - nx(139), - 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(155), - nx(156), - nx(157), - nx(159), // for _, y := range (y == 10) - nx(159), - nx(160), - nx(163), // result = append(result, y) - nx(166), // for k, z := range (k == 0, z == 1) - nx(166), - 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(182), - nx(183), // for _, y := range (y == 10) - nx(183), - 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(194), - nx(195), // result = append(result, x) - nx(196), // if x == -4 - nx(199), // for _, y := range (y == 1) - nx(199), - 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(213), - nx(214), // result = append(result, x) - nx(215), // if x == -4 - nx(218), // for _, y := range (y == 1) - nx(218), - 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(236), - assertFunc(t, "main.TestRecur-range1"), - assertEval(t, "x", "10", "n", "3"), - nx(237), // result = ... - 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 TestStackwatchClearBug(t *testing.T) { skipOn(t, "not implemented", "freebsd") skipOn(t, "not implemented", "386") diff --git a/pkg/proc/stepping_test.go b/pkg/proc/stepping_test.go new file mode 100644 index 0000000000..12530fcea5 --- /dev/null +++ b/pkg/proc/stepping_test.go @@ -0,0 +1,1817 @@ +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(27), // for _, x := range ... (TODO: this probably shouldn't be here but it's also very hard to skip stopping here a second time) + 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(40), + 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(48), + 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(52), + 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(52), + 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(66), + nx(67), // for _, y := range (y == 1) + nx(67), + 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(67), + 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(67), + 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(85), + 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(90), + nx(91), // for _, y := range (y == 10) + nx(91), + 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(93), + 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(116), + 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(131), + nx(132), + nx(133), + nx(135), // for _, y := range (y == 10) + nx(135), + nx(136), // result = append(result, y) + nx(139), // for k, z := range (k == 0, z == 1) + nx(139), + 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(155), + nx(156), + nx(157), + nx(159), // for _, y := range (y == 10) + nx(159), + nx(160), + nx(163), // result = append(result, y) + nx(166), // for k, z := range (k == 0, z == 1) + nx(166), + 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(182), + nx(183), // for _, y := range (y == 10) + nx(183), + 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(194), + nx(195), // result = append(result, x) + nx(196), // if x == -4 + nx(199), // for _, y := range (y == 1) + nx(199), + 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(213), + nx(214), // result = append(result, x) + nx(215), // if x == -4 + nx(218), // for _, y := range (y == 1) + nx(218), + 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"), + assertEval(t, "n", "3"), + nx(236), + assertFunc(t, "main.TestRecur-range1"), + assertEval(t, "x", "10", "n", "3"), + nx(237), // result = ... + 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(27), // for _, x := range ... (TODO: this probably shouldn't be here but it's also very hard to skip stopping here a second time) + 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(48), + 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(52), + 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(52), + 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(66), + nx(67), // for _, y := range (y == 1) + nx(67), + 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(67), + 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(67), + 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(85), + 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(90), + nx(91), // for _, y := range (y == 10) + nx(91), + 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(93), + 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(116), + 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(131), + nx(132), + nx(133), + nx(135), // for _, y := range (y == 10) + nx(135), + nx(136), // result = append(result, y) + nx(139), // for k, z := range (k == 0, z == 1) + nx(139), + 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(155), + nx(156), + nx(157), + nx(159), // for _, y := range (y == 10) + nx(159), + nx(160), + nx(163), // result = append(result, y) + nx(166), // for k, z := range (k == 0, z == 1) + nx(166), + 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(182), + nx(183), // for _, y := range (y == 10) + nx(183), + 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(194), + nx(195), // result = append(result, x) + nx(196), // if x == -4 + nx(199), // for _, y := range (y == 1) + nx(199), + 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(213), + nx(214), // result = append(result, x) + nx(215), // if x == -4 + nx(218), // for _, y := range (y == 1) + nx(218), + 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(236), + assertFunc(t, "main.TestRecur-range1"), + assertEval(t, "x", "10", "n", "3"), + nx(237), // result = ... + 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"), + }) + }) + }) +} diff --git a/pkg/proc/variables_test.go b/pkg/proc/variables_test.go index 459f910e4e..94f34d79c4 100644 --- a/pkg/proc/variables_test.go +++ b/pkg/proc/variables_test.go @@ -5,6 +5,7 @@ import ( "fmt" "go/constant" "os" + "reflect" "regexp" "runtime" "sort" @@ -100,6 +101,99 @@ func (tc *varTest) alternateVarTest() varTest { return r } +func setVariable(p *proc.Target, symbol, value string) error { + scope, err := proc.GoroutineScope(p, p.CurrentThread()) + if err != nil { + return err + } + return scope.SetVariable(symbol, value) +} + +func TestVariableEvaluation(t *testing.T) { + protest.AllowRecording(t) + testcases := []struct { + name string + st reflect.Kind + value interface{} + length, cap int64 + childrenlen int + }{ + {"a1", reflect.String, "foofoofoofoofoofoo", 18, 0, 0}, + {"a11", reflect.Array, nil, 3, -1, 3}, + {"a12", reflect.Slice, nil, 2, 2, 2}, + {"a13", reflect.Slice, nil, 3, 3, 3}, + {"a2", reflect.Int, int64(6), 0, 0, 0}, + {"a3", reflect.Float64, float64(7.23), 0, 0, 0}, + {"a4", reflect.Array, nil, 2, -1, 2}, + {"a5", reflect.Slice, nil, 5, 5, 5}, + {"a6", reflect.Struct, nil, 2, 0, 2}, + {"a7", reflect.Ptr, nil, 1, 0, 1}, + {"a8", reflect.Struct, nil, 2, 0, 2}, + {"a9", reflect.Ptr, nil, 1, 0, 1}, + {"baz", reflect.String, "bazburzum", 9, 0, 0}, + {"neg", reflect.Int, int64(-1), 0, 0, 0}, + {"f32", reflect.Float32, float64(float32(1.2)), 0, 0, 0}, + {"c64", reflect.Complex64, complex128(complex64(1 + 2i)), 0, 0, 0}, + {"c128", reflect.Complex128, complex128(2 + 3i), 0, 0, 0}, + {"a6.Baz", reflect.Int, int64(8), 0, 0, 0}, + {"a7.Baz", reflect.Int, int64(5), 0, 0, 0}, + {"a8.Baz", reflect.String, "feh", 3, 0, 0}, + {"a8", reflect.Struct, nil, 2, 0, 2}, + {"i32", reflect.Array, nil, 2, -1, 2}, + {"b1", reflect.Bool, true, 0, 0, 0}, + {"b2", reflect.Bool, false, 0, 0, 0}, + {"f", reflect.Func, "main.barfoo", 0, 0, 0}, + {"ba", reflect.Slice, nil, 200, 200, 64}, + } + + withTestProcess("testvariables", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { + assertNoError(grp.Continue(), t, "Continue() returned an error") + + for _, tc := range testcases { + v := evalVariable(p, t, tc.name) + + if v.Kind != tc.st { + t.Fatalf("%s simple type: expected: %s got: %s", tc.name, tc.st, v.Kind.String()) + } + if v.Value == nil && tc.value != nil { + t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) + } else { + switch v.Kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + x, _ := constant.Int64Val(v.Value) + if y, ok := tc.value.(int64); !ok || x != y { + t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) + } + case reflect.Float32, reflect.Float64: + x, _ := constant.Float64Val(v.Value) + if y, ok := tc.value.(float64); !ok || x != y { + t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) + } + case reflect.Complex64, reflect.Complex128: + xr, _ := constant.Float64Val(constant.Real(v.Value)) + xi, _ := constant.Float64Val(constant.Imag(v.Value)) + if y, ok := tc.value.(complex128); !ok || complex(xr, xi) != y { + t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) + } + case reflect.String: + if y, ok := tc.value.(string); !ok || constant.StringVal(v.Value) != y { + t.Fatalf("%s value: expected: %v got: %v", tc.name, tc.value, v.Value) + } + } + } + if v.Len != tc.length { + t.Fatalf("%s len: expected: %d got: %d", tc.name, tc.length, v.Len) + } + if v.Cap != tc.cap { + t.Fatalf("%s cap: expected: %d got: %d", tc.name, tc.cap, v.Cap) + } + if len(v.Children) != tc.childrenlen { + t.Fatalf("%s children len: expected %d got: %d", tc.name, tc.childrenlen, len(v.Children)) + } + } + }) +} + func TestVariableEvaluation2(t *testing.T) { testcases := []varTest{ {"a1", true, "\"foofoofoofoofoofoo\"", "", "string", nil},