proc: for optimized functions allow .closureptr to not exist (#3808)
* proc: flag variables correctly when range-over-func stmts are used Argument variables of a range-over-func body closure should be returned flagged as normal local variables, not as function arguments. Updates #3806 * proc: for optimized functions allow .closureptr to not exist For optimized functions .closureptr is sometimes omitted from DWARF, allow it to be 0 and try to recover the range-over-func stack by best effort. Fixes #3806
This commit is contained in:
parent
b9fadbae9b
commit
582305a813
51
_fixtures/setiterator.go
Normal file
51
_fixtures/setiterator.go
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
set := New[string]()
|
||||
for i := 10; i < 100; i++ {
|
||||
set.Add(strconv.Itoa(i))
|
||||
}
|
||||
PrintAllElements[string](set)
|
||||
}
|
||||
|
||||
// Set holds a set of elements.
|
||||
type Set[E comparable] struct {
|
||||
m map[E]struct{}
|
||||
}
|
||||
|
||||
// New returns a new [Set].
|
||||
func New[E comparable]() *Set[E] {
|
||||
return &Set[E]{m: make(map[E]struct{})}
|
||||
}
|
||||
|
||||
// All is an iterator over the elements of s.
|
||||
func (s *Set[E]) All() iter.Seq[E] {
|
||||
return func(yield func(E) bool) {
|
||||
for v := range s.m {
|
||||
tmp := make([]byte, 1024)
|
||||
str := string(tmp)
|
||||
if !yield(v) {
|
||||
return
|
||||
}
|
||||
go func() { println(str) }()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Set[E]) Add(v E) {
|
||||
s.m[v] = struct{}{}
|
||||
}
|
||||
|
||||
func PrintAllElements[E comparable](s *Set[E]) {
|
||||
for v := range s.All() {
|
||||
time.Sleep(100 * time.Second)
|
||||
fmt.Println(v)
|
||||
}
|
||||
}
|
@ -463,7 +463,7 @@ type compileUnit struct {
|
||||
entry *dwarf.Entry // debug_info entry describing this compile unit
|
||||
isgo bool // true if this is the go compile unit
|
||||
lineInfo *line.DebugLineInfo // debug_line segment associated with this compile unit
|
||||
optimized bool // this compile unit is optimized
|
||||
optimized optimizedFlags // this compile unit is optimized
|
||||
producer string // producer attribute
|
||||
|
||||
offset dwarf.Offset // offset of the entry describing the compile unit
|
||||
@ -471,6 +471,13 @@ type compileUnit struct {
|
||||
image *Image // parent image of this compilation unit.
|
||||
}
|
||||
|
||||
type optimizedFlags uint8
|
||||
|
||||
const (
|
||||
optimizedInlined optimizedFlags = 1 << iota
|
||||
optimizedOptimized
|
||||
)
|
||||
|
||||
type fileLine struct {
|
||||
file string
|
||||
line int
|
||||
@ -605,7 +612,7 @@ func (fn *Function) NameWithoutTypeParams() string {
|
||||
|
||||
// Optimized returns true if the function was optimized by the compiler.
|
||||
func (fn *Function) Optimized() bool {
|
||||
return fn.cu.optimized
|
||||
return fn.cu.optimized != 0
|
||||
}
|
||||
|
||||
// PrologueEndPC returns the PC just after the function prologue
|
||||
@ -2472,9 +2479,18 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugInfoBytes, debugLineB
|
||||
if cu.isgo && cu.producer != "" {
|
||||
semicolon := strings.Index(cu.producer, ";")
|
||||
if semicolon < 0 {
|
||||
cu.optimized = goversion.ProducerAfterOrEqual(cu.producer, 1, 10)
|
||||
cu.optimized = 0
|
||||
if goversion.ProducerAfterOrEqual(cu.producer, 1, 10) {
|
||||
cu.optimized = optimizedInlined | optimizedOptimized
|
||||
}
|
||||
} else {
|
||||
cu.optimized = !strings.Contains(cu.producer[semicolon:], "-N") || !strings.Contains(cu.producer[semicolon:], "-l")
|
||||
cu.optimized = optimizedInlined | optimizedOptimized
|
||||
if strings.Contains(cu.producer[semicolon:], "-N") {
|
||||
cu.optimized &^= optimizedOptimized
|
||||
}
|
||||
if strings.Contains(cu.producer[semicolon:], "-l") {
|
||||
cu.optimized &^= optimizedInlined
|
||||
}
|
||||
const regabi = " regabi"
|
||||
if i := strings.Index(cu.producer[semicolon:], regabi); i > 0 {
|
||||
i += semicolon
|
||||
|
@ -71,6 +71,10 @@ const (
|
||||
// If localsOnlyRangeBodyClosures is set simpleLocals only returns
|
||||
// variables containing the range body closure.
|
||||
localsOnlyRangeBodyClosures
|
||||
|
||||
// If localsIsRangeBody is set DW_AT_formal_parameter variables will be
|
||||
// considered local variables.
|
||||
localsIsRangeBody
|
||||
)
|
||||
|
||||
// ConvertEvalScope returns a new EvalScope in the context of the
|
||||
@ -319,7 +323,12 @@ func (scope *EvalScope) Locals(flags localsFlags, wantedName string) ([]*Variabl
|
||||
return vars2
|
||||
}
|
||||
|
||||
vars0, err := scope.simpleLocals(flags, wantedName)
|
||||
rangeBodyFlags := localsFlags(0)
|
||||
if scope.Fn != nil && scope.Fn.rangeParentName() != "" {
|
||||
rangeBodyFlags = localsFlags(localsIsRangeBody)
|
||||
}
|
||||
|
||||
vars0, err := scope.simpleLocals(flags|rangeBodyFlags, wantedName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -344,7 +353,11 @@ func (scope *EvalScope) Locals(flags localsFlags, wantedName string) ([]*Variabl
|
||||
scope2 = FrameToScope(scope.target, scope.target.Memory(), scope.g, scope.threadID, scope.rangeFrames[2*i:]...)
|
||||
scope.enclosingRangeScopes[i] = scope2
|
||||
}
|
||||
vars, err := scope2.simpleLocals(flags, wantedName)
|
||||
rangeBodyFlags := localsFlags(localsIsRangeBody)
|
||||
if i == len(scope.enclosingRangeScopes)-1 {
|
||||
rangeBodyFlags = 0
|
||||
}
|
||||
vars, err := scope2.simpleLocals(flags|rangeBodyFlags, wantedName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -377,7 +390,9 @@ func (scope *EvalScope) setupRangeFrames() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scope.rangeFrames = scope.rangeFrames[2:] // skip the first frame and its return frame
|
||||
if len(scope.rangeFrames) > 0 {
|
||||
scope.rangeFrames = scope.rangeFrames[2:] // skip the first frame and its return frame
|
||||
}
|
||||
scope.enclosingRangeScopes = make([]*EvalScope, len(scope.rangeFrames)/2)
|
||||
return nil
|
||||
}
|
||||
@ -455,7 +470,7 @@ func (scope *EvalScope) simpleLocals(flags localsFlags, wantedName string) ([]*V
|
||||
}
|
||||
vars = append(vars, val)
|
||||
depth := entry.Depth
|
||||
if entry.Tag == dwarf.TagFormalParameter {
|
||||
if (flags&localsIsRangeBody == 0) && (entry.Tag == dwarf.TagFormalParameter) {
|
||||
if depth <= 1 {
|
||||
depth = 0
|
||||
}
|
||||
|
@ -573,7 +573,7 @@ func funcCallArgs(fn *Function, bi *BinaryInfo, includeRet bool) (argFrameSize i
|
||||
return 0, nil, fmt.Errorf("DWARF read error: %v", err)
|
||||
}
|
||||
|
||||
if bi.regabi && fn.cu.optimized {
|
||||
if bi.regabi && fn.Optimized() {
|
||||
if runtimeWhitelist[fn.Name] {
|
||||
runtimeOptimizedWorkaround(bi, fn.cu.image, dwarfTree)
|
||||
} else {
|
||||
|
@ -948,6 +948,10 @@ func rangeFuncStackTrace(tgt *Target, g *G) ([]Stackframe, error) {
|
||||
nonMonotonicSP := false
|
||||
var closurePtr int64
|
||||
|
||||
optimized := func(fn *Function) bool {
|
||||
return fn.cu.optimized&optimizedOptimized != 0
|
||||
}
|
||||
|
||||
appendFrame := func(fr Stackframe) {
|
||||
frames = append(frames, fr)
|
||||
if fr.closurePtr != 0 {
|
||||
@ -960,6 +964,9 @@ func rangeFuncStackTrace(tgt *Target, g *G) ([]Stackframe, error) {
|
||||
if fr.SystemStack {
|
||||
return false
|
||||
}
|
||||
if closurePtr == 0 && optimized(fr.Call.Fn) {
|
||||
return true
|
||||
}
|
||||
if closurePtr < 0 {
|
||||
// closure is stack allocated, check that it is on this frame
|
||||
return fr.contains(closurePtr)
|
||||
@ -994,12 +1001,30 @@ func rangeFuncStackTrace(tgt *Target, g *G) ([]Stackframe, error) {
|
||||
frames = append(frames, fr)
|
||||
}
|
||||
|
||||
if fr.Call.Fn == nil {
|
||||
if stage == startStage {
|
||||
frames = nil
|
||||
addRetFrame = false
|
||||
stage = doneStage
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
switch stage {
|
||||
case startStage:
|
||||
appendFrame(fr)
|
||||
rangeParent = fr.Call.Fn.extra(tgt.BinInfo()).rangeParent
|
||||
stage = normalStage
|
||||
if rangeParent == nil || closurePtr == 0 {
|
||||
stop := false
|
||||
if rangeParent == nil {
|
||||
stop = true
|
||||
}
|
||||
if !optimized(fr.Call.Fn) && closurePtr == 0 {
|
||||
stop = true
|
||||
}
|
||||
if stop {
|
||||
frames = nil
|
||||
addRetFrame = false
|
||||
stage = doneStage
|
||||
@ -1011,7 +1036,7 @@ func rangeFuncStackTrace(tgt *Target, g *G) ([]Stackframe, error) {
|
||||
stage = lastFrameStage
|
||||
} else if fr.Call.Fn.extra(tgt.BinInfo()).rangeParent == rangeParent && closurePtrOk(&fr) {
|
||||
appendFrame(fr)
|
||||
if closurePtr == 0 {
|
||||
if !optimized(fr.Call.Fn) && closurePtr == 0 {
|
||||
frames = nil
|
||||
addRetFrame = false
|
||||
stage = doneStage
|
||||
|
@ -1836,8 +1836,31 @@ func TestCapturedVariable(t *testing.T) {
|
||||
assertVariable(t, v, varTest{
|
||||
name: "c",
|
||||
preserveName: true,
|
||||
value: "struct { main.name string; main.thing main.Thing } {name: \"Success\", thing: main.Thing {str: \"hello\"}}",
|
||||
varType: "struct { main.name string; main.thing main.Thing }",
|
||||
|
||||
value: "struct { main.name string; main.thing main.Thing } {name: \"Success\", thing: main.Thing {str: \"hello\"}}",
|
||||
varType: "struct { main.name string; main.thing main.Thing }",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetupRangeFramesCrash(t *testing.T) {
|
||||
// See issue #3806
|
||||
if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 23) {
|
||||
t.Skip("N/A")
|
||||
}
|
||||
|
||||
for _, options := range []protest.BuildFlags{0, protest.EnableInlining | protest.EnableOptimization} {
|
||||
withTestProcessArgs("setiterator", t, ".", []string{}, options, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
|
||||
setFileBreakpoint(p, t, fixture.Source, 48)
|
||||
assertNoError(grp.Continue(), t, "Continue")
|
||||
scope, err := evalScope(p)
|
||||
assertNoError(err, t, "EvalScope")
|
||||
v, err := scope.LocalVariables(normalLoadConfig)
|
||||
assertNoError(err, t, "LocalVariables")
|
||||
t.Logf("%#v", v)
|
||||
if len(v) != 1 {
|
||||
t.Fatalf("wrong number of variables")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user