Skip to content

Commit

Permalink
proc: fix TestCondBreakpointWithFrame flakes on 1.22rc1 (#3624)
Browse files Browse the repository at this point in the history
The flake manifests as an error where the variable i can not be found in
frame 1 and happens in go1.22rc1 between 0.1% and 0.5% of the time (it is highly dependent on CPU contention)
This problem is caused by the new code in evalop.PushLocal referencing the
stale value of SelectedGoroutine. This happens because:

-  evalop.PushLocal calls ConvertEvalScope
- ConvertEvalScope calls FindGoroutine
- FindGoroutine checks the value of selectedGoroutine

When breakpoint conditions are evaluated both the value of selectedGoroutine
and currentThread are stale because we can only set their definitive value
*after* all breakpoint conditions have been evaluated.

The fact that it only happens in 1.22rc1 is coincidental, it's probably
caused by the fact that 1.22rc1 migrates goroutines between threads more in
this particular circumstance.

This commit fixes the problem in two ways:

1. selectedGoroutine and currentThread are given temprorary non-stale values
   before breakpoint conditions are evaluated
2. evalop.PushLocal is changed so it takes a stack trace of the current
   thread rather than resolving it through the selected goroutine.

Either one would suffice however I think we should do both, (2) ensures that
the runtime.frame(n).var will work even if the current thread is not running
any goroutine and (1) ensures that we don't accidentally reference a stale
selectedGoroutine in the future.
  • Loading branch information
aarzilli authored Jan 10, 2024
1 parent f8de498 commit bf627d0
Show file tree
Hide file tree
Showing 2 changed files with 10 additions and 4 deletions.
7 changes: 3 additions & 4 deletions pkg/proc/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -966,10 +966,9 @@ func (stack *evalStack) executeOp() {
var vars []*Variable
var err error
if op.Frame != 0 {
var frameScope *EvalScope
frameScope, err = ConvertEvalScope(scope.target, scope.g.ID, int(op.Frame), 0)
if err != nil {
stack.err = err
frameScope, err2 := ConvertEvalScope(scope.target, -1, int(op.Frame), 0)
if err2 != nil {
stack.err = err2
return
}
vars, err = frameScope.Locals(0)
Expand Down
7 changes: 7 additions & 0 deletions pkg/proc/target_exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,18 @@ func (grp *TargetGroup) Continue() error {

it := ValidTargets{Group: grp}
for it.Next() {
// Both selectedGoroutine and current thread are stale here, since we can
// only set their definitive value *after* evaluating breakpoint
// conditions here we give them temporary non-stale values.
it.selectedGoroutine = nil
curthread := it.currentThread
for _, thread := range it.ThreadList() {
if thread.Breakpoint().Breakpoint != nil {
it.currentThread = thread
thread.Breakpoint().Breakpoint.checkCondition(it.Target, thread, thread.Breakpoint())
}
}
it.currentThread = curthread
}

if contOnceErr != nil {
Expand Down

0 comments on commit bf627d0

Please sign in to comment.