proc: fix bug with range-over-func stepping (#3778)

Set a breakpoint on the return address of the current function, if it's
a range-over-func body, and clear the stepping breakpoints for the
current function (except the entry one) when its hit.

Without this what can happen is the following:

1. the range-over-func body finishes and returns to the iterator
2. the iterator calls back into the range-over-func body
3. a stepping breakpoint that's inside the prologue gets hit

Updates #3733
This commit is contained in:
Alessandro Arzilli
2024-07-15 06:27:47 +02:00
committed by GitHub
parent 3ae22627df
commit c1366e90cc
5 changed files with 149 additions and 70 deletions

View File

@ -144,7 +144,10 @@ const (
// goroutine.
StepIntoNewProcBreakpoint
steppingMask = NextBreakpoint | NextDeferBreakpoint | StepBreakpoint | StepIntoNewProcBreakpoint
// NextInactivatedBreakpoint a NextBreakpoint that has been inactivated, see rangeFrameInactivateNextBreakpoints
NextInactivatedBreakpoint
steppingMask = NextBreakpoint | NextDeferBreakpoint | StepBreakpoint | StepIntoNewProcBreakpoint | NextInactivatedBreakpoint
)
// WatchType is the watchpoint type
@ -221,6 +224,8 @@ func (bp *Breakpoint) VerboseDescr() []string {
r = append(r, "PluginOpenBreakpoint")
case StepIntoNewProcBreakpoint:
r = append(r, "StepIntoNewProcBreakpoint")
case NextInactivatedBreakpoint:
r = append(r, "NextInactivatedBreakpoint")
default:
r = append(r, fmt.Sprintf("Unknown %d", breaklet.Kind))
}
@ -318,6 +323,9 @@ func (bpstate *BreakpointState) checkCond(tgt *Target, breaklet *Breaklet, threa
case StackResizeBreakpoint, PluginOpenBreakpoint, StepIntoNewProcBreakpoint:
// no further checks
case NextInactivatedBreakpoint:
active = false
default:
bpstate.CondError = fmt.Errorf("internal error unknown breakpoint kind %v", breaklet.Kind)
}
@ -834,6 +842,29 @@ func (t *Target) ClearSteppingBreakpoints() error {
return nil
}
func (t *Target) clearInactivatedSteppingBreakpoint() error {
threads := t.ThreadList()
for _, bp := range t.Breakpoints().M {
for i := range bp.Breaklets {
if bp.Breaklets[i].Kind == NextInactivatedBreakpoint {
bp.Breaklets[i] = nil
}
}
cleared, err := t.finishClearBreakpoint(bp)
if err != nil {
return err
}
if cleared {
for _, thread := range threads {
if thread.Breakpoint().Breakpoint == bp {
thread.Breakpoint().Clear()
}
}
}
}
return nil
}
// finishClearBreakpoint clears nil breaklets from the breaklet list of bp
// and if it is empty erases the breakpoint.
// Returns true if the breakpoint was deleted