proc: fix step stuttering when entering range-over-func bodies (#3788)

Fixes a repeated stop on the first line of a range-over-func body when
stepping through a program. The entry point breakpoint is changed to a
special breakpoint that, when hit:

- clears the current temporary breakpoints
- sets breakpoints like 'next' would, and
- resumes execution.
This commit is contained in:
Alessandro Arzilli 2024-08-11 06:15:26 +02:00 committed by GitHub
parent 64f3d34663
commit f2571fd46e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 32 additions and 61 deletions

@ -147,7 +147,9 @@ const (
// NextInactivatedBreakpoint a NextBreakpoint that has been inactivated, see rangeFrameInactivateNextBreakpoints
NextInactivatedBreakpoint
steppingMask = NextBreakpoint | NextDeferBreakpoint | StepBreakpoint | StepIntoNewProcBreakpoint | NextInactivatedBreakpoint
StepIntoRangeOverFuncBodyBreakpoint
steppingMask = NextBreakpoint | NextDeferBreakpoint | StepBreakpoint | StepIntoNewProcBreakpoint | NextInactivatedBreakpoint | StepIntoRangeOverFuncBodyBreakpoint
)
// WatchType is the watchpoint type
@ -226,6 +228,8 @@ func (bp *Breakpoint) VerboseDescr() []string {
r = append(r, "StepIntoNewProcBreakpoint")
case NextInactivatedBreakpoint:
r = append(r, "NextInactivatedBreakpoint")
case StepIntoRangeOverFuncBodyBreakpoint:
r = append(r, "StepIntoRangeOverFuncBodyBreakpoint Cond=%q", exprToString(breaklet.Cond))
default:
r = append(r, fmt.Sprintf("Unknown %d", breaklet.Kind))
}
@ -320,7 +324,7 @@ func (bpstate *BreakpointState) checkCond(tgt *Target, breaklet *Breaklet, threa
}
}
case StackResizeBreakpoint, PluginOpenBreakpoint, StepIntoNewProcBreakpoint:
case StackResizeBreakpoint, PluginOpenBreakpoint, StepIntoNewProcBreakpoint, StepIntoRangeOverFuncBodyBreakpoint:
// no further checks
case NextInactivatedBreakpoint:
@ -348,6 +352,9 @@ func (bpstate *BreakpointState) checkCond(tgt *Target, breaklet *Breaklet, threa
case StepBreakpoint:
bpstate.Stepping = true
bpstate.SteppingInto = true
case StepIntoRangeOverFuncBodyBreakpoint:
bpstate.Stepping = true
bpstate.SteppingIntoRangeOverFuncBody = true
}
}
}
@ -921,7 +928,8 @@ type BreakpointState struct {
Stepping bool
// SteppingInto is true if one of the active stepping breaklets has Kind ==
// StepBreakpoint.
SteppingInto bool
SteppingInto bool
SteppingIntoRangeOverFuncBody bool
// CondError contains any error encountered while evaluating the
// breakpoint's condition.
CondError error

@ -772,7 +772,6 @@ func TestRangeOverFuncNext(t *testing.T) {
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,
@ -800,7 +799,6 @@ func TestRangeOverFuncNext(t *testing.T) {
nx(38),
nx(39),
nx(40), // for _, x := range...
nx(40),
nx(41),
nx(42),
nx(40),
@ -818,7 +816,6 @@ func TestRangeOverFuncNext(t *testing.T) {
{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,
@ -826,7 +823,6 @@ func TestRangeOverFuncNext(t *testing.T) {
"x", "-1"),
nx(52), // for _, y := range... (y == 1)
nx(52),
nx(53), // if y == 3
assertLocals(t, "result", "x", "y"),
assertEval(t,
@ -860,7 +856,6 @@ func TestRangeOverFuncNext(t *testing.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),
@ -897,9 +892,7 @@ func TestRangeOverFuncNext(t *testing.T) {
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)
@ -920,7 +913,6 @@ func TestRangeOverFuncNext(t *testing.T) {
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)
@ -941,7 +933,6 @@ func TestRangeOverFuncNext(t *testing.T) {
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
@ -959,7 +950,6 @@ func TestRangeOverFuncNext(t *testing.T) {
nx(82),
nx(84),
nx(85), // for _, w := range (w == 1000)
nx(85),
nx(86), // result = append(result, w)
assertEval(t,
"w", "1000",
@ -968,9 +958,7 @@ func TestRangeOverFuncNext(t *testing.T) {
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,
@ -979,7 +967,6 @@ func TestRangeOverFuncNext(t *testing.T) {
"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,
@ -1031,7 +1018,6 @@ func TestRangeOverFuncNext(t *testing.T) {
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),
@ -1064,14 +1050,11 @@ func TestRangeOverFuncNext(t *testing.T) {
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),
@ -1095,15 +1078,12 @@ func TestRangeOverFuncNext(t *testing.T) {
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),
@ -1124,9 +1104,7 @@ func TestRangeOverFuncNext(t *testing.T) {
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),
@ -1141,11 +1119,9 @@ func TestRangeOverFuncNext(t *testing.T) {
{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),
@ -1177,11 +1153,9 @@ func TestRangeOverFuncNext(t *testing.T) {
{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),
@ -1213,11 +1187,10 @@ func TestRangeOverFuncNext(t *testing.T) {
nx(236), // for _, x := range (x == 10)
assertFunc(t, "main.TestRecur"),
nx(237), // result = ...
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),
@ -1345,7 +1318,6 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
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,
@ -1390,7 +1362,6 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
{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,
@ -1398,7 +1369,6 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
"x", "-1"),
nx(52), // for _, y := range... (y == 1)
nx(52),
nx(53), // if y == 3
assertLocals(t, "result", "x", "y"),
assertEval(t,
@ -1431,7 +1401,6 @@ func TestRangeOverFuncNextInlined(t *testing.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),
@ -1469,9 +1438,7 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
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)
@ -1493,7 +1460,6 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
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)
@ -1515,7 +1481,6 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
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
@ -1535,7 +1500,6 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
nx(82),
nx(84),
nx(85), // for _, w := range (w == 1000)
nx(85),
nx(86), // result = append(result, w)
assertEval(t,
"w", "1000",
@ -1544,9 +1508,7 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
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,
@ -1555,7 +1517,6 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
"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,
@ -1611,7 +1572,6 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
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),
@ -1644,14 +1604,11 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
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),
@ -1675,15 +1632,12 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
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),
@ -1704,9 +1658,7 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
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),
@ -1723,11 +1675,9 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
{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),
@ -1761,11 +1711,9 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
{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),
@ -1800,10 +1748,9 @@ func TestRangeOverFuncNextInlined(t *testing.T) {
nx(236), // for _, x := range (x == 10)
assertFunc(t, "main.TestRecur"),
assertEval(t, "n", "3"),
nx(236),
nx(237), // result = ...
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),

@ -202,7 +202,8 @@ func (grp *TargetGroup) Continue() error {
switch {
case curbp.Active && curbp.Stepping:
if curbp.SteppingInto {
switch {
case curbp.SteppingInto:
// See description of proc.(*Process).next for the meaning of StepBreakpoints
if err := conditionErrors(grp); err != nil {
return err
@ -213,7 +214,18 @@ func (grp *TargetGroup) Continue() error {
}
return grp.StepInstruction(false)
}
} else {
case curbp.SteppingIntoRangeOverFuncBody:
if err := conditionErrors(grp); err != nil {
return err
}
if err := dbp.ClearSteppingBreakpoints(); err != nil {
return err
}
if err := next(dbp, false, false); err != nil {
return err
}
// Target execution continues...
default:
curthread.Common().returnValues = curbp.Breakpoint.returnInfo.Collect(dbp, curthread)
if err := dbp.ClearSteppingBreakpoints(); err != nil {
return err
@ -843,7 +855,11 @@ func next(dbp *Target, stepInto, inlinedStepOut bool) error {
if err != nil {
return err
}
if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, pc, NextBreakpoint, rpc)); err != nil {
kind := NextBreakpoint
if f, ln := bi.pcToLine(fn, pc); f == topframe.Current.File && ln == topframe.Current.Line {
kind = StepIntoRangeOverFuncBodyBreakpoint
}
if _, err := allowDuplicateBreakpoint(dbp.SetBreakpoint(0, pc, kind, rpc)); err != nil {
return err
}
}