Skip to content

Commit

Permalink
feat: support go 1.23 (#24)
Browse files Browse the repository at this point in the history
* feat: support go 1.23

* feat: support range over frames

* chore: fix build commit id

* test: go 1.23 testdata
  • Loading branch information
jayantxie authored Sep 14, 2024
1 parent 98cc600 commit e607ae4
Show file tree
Hide file tree
Showing 12 changed files with 334 additions and 76 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
unit-benchmark-test:
strategy:
matrix:
go: [ "1.21", "1.22" ]
go: [ "1.21", "1.22", "1.23" ]
os: [ X64 ]
runs-on: ${{ matrix.os }}
steps:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ successfully output to `grf.out`

## Go Version Constraints

- Executable file: go1.17 ~ go1.22.
- Executable file: go1.17 ~ go1.23.
- Compile goref tool: >= go1.21.


Expand Down
16 changes: 16 additions & 0 deletions cmd/grf/cmds/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/spf13/cobra"

myproc "github.com/cloudwego/goref/pkg/proc"
"github.com/cloudwego/goref/pkg/version"
)

var (
Expand Down Expand Up @@ -88,6 +89,21 @@ You'll have to wait for goref until it outputs 'successfully output to ...', or
coreCommand.Flags().StringVarP(&outFile, "out", "o", "grf.out", "output file name")
rootCommand.AddCommand(coreCommand)

versionVerbose := false
versionCommand := &cobra.Command{
Use: "version",
Short: "Prints version.",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("Goref Tool\n%s\n", version.DelveVersion)
if versionVerbose {
fmt.Printf("Build Details: %s\n", version.BuildInfo())
}
},
ValidArgsFunction: cobra.NoFileCompletions,
}
versionCommand.Flags().BoolVarP(&versionVerbose, "verbose", "v", false, "print verbose version info")
rootCommand.AddCommand(versionCommand)

return rootCommand
}

Expand Down
82 changes: 37 additions & 45 deletions pkg/proc/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,65 +41,38 @@ type myEvalScope struct {
proc.EvalScope

dictAddr uint64 // dictionary address for instantiated generic functions

// enclosingRangeScopes []*proc.EvalScope
// rangeFrames []proc.Stackframe
}

func (scope *myEvalScope) Locals(mds []proc.ModuleData) ([]*ReferenceVariable, error) {
// var scopes [][]*Variable
vars0, err := scope.simpleLocals(mds)
func (scope *myEvalScope) Locals(t *proc.Target, g *proc.G, threadID int, mds []proc.ModuleData) ([]*ReferenceVariable, error) {
vars, err := scope.simpleLocals(mds)
if err != nil {
return nil, err
}
return vars0, nil
// TODO: support range-over-func
/*
if scope.Fn.extra(scope.BinInfo).rangeParent == nil || scope.target == nil || scope.g == nil {
return vars0, nil
if rpn := rangeParentName(scope.Fn.Name); rpn == "" {
return vars, nil
}
scopes = append(scopes, vars0)
if scope.rangeFrames == nil {
scope.rangeFrames, err = rangeFuncStackTrace(scope.target, scope.g)
if err != nil {
return nil, err
}
scope.rangeFrames = scope.rangeFrames[1:]
scope.enclosingRangeScopes = make([]*EvalScope, len(scope.rangeFrames))
rangeFrames, err := rangeFuncStackTrace(t, g)
if err != nil {
return vars, nil
}
for i, scope2 := range scope.enclosingRangeScopes {
if i == len(scope.enclosingRangeScopes)-1 {
// Last one is the caller frame, we shouldn't check it
break
}
rangeFrames = rangeFrames[2:] // skip the first frame and its return frame
enclosingRangeScopes := make([]*myEvalScope, len(rangeFrames)/2)
for i, scope2 := range enclosingRangeScopes {
if scope2 == nil {
scope2 = FrameToScope(scope.target, scope.target.Memory(), scope.g, scope.threadID, scope.rangeFrames[i:]...)
scope.enclosingRangeScopes[i] = scope2
scope2 := &myEvalScope{EvalScope: *proc.FrameToScope(t, t.Memory(), g, threadID, rangeFrames[2*i:]...)}
enclosingRangeScopes[i] = scope2
}
vars, err := scope2.simpleLocals0(flags, mds)
vars2, err := scope2.simpleLocals(mds)
if err != nil {
return nil, err
}
scopes = append(scopes, vars)
}
vars := []*Variable{}
for i := len(scopes) - 1; i >= 0; i-- {
vars = append(vars, scopes[i]...)
}
// Apply shadowning
lvn := map[string]*Variable{}
for _, v := range vars {
if otherv := lvn[v.Name]; otherv != nil {
otherv.Flags |= VariableShadowed
continue
}
lvn[v.Name] = v
vars = append(vars, vars2...)
}
return vars, nil
*/
return vars, nil
}

func (scope *myEvalScope) simpleLocals(mds []proc.ModuleData) ([]*ReferenceVariable, error) {
Expand Down Expand Up @@ -149,7 +122,7 @@ func (scope *myEvalScope) simpleLocals(mds []proc.ModuleData) ([]*ReferenceVaria
if name == goDictionaryName || name == goClosurePtr || strings.HasPrefix(name, "#state") || strings.HasPrefix(name, "&#state") || strings.HasPrefix(name, "#next") || strings.HasPrefix(name, "&#next") || strings.HasPrefix(name, "#yield") {
continue
}
if rangeParentName(scope.Fn) != "" {
if rangeParentName(scope.Fn.Name) != "" {
// Skip return values and closure variables for range-over-func closure bodies
if strings.HasPrefix(name, "~") {
continue
Expand Down Expand Up @@ -246,6 +219,25 @@ func runtimeTypeTypename(bi *proc.BinaryInfo) string {
return "runtime._type"
}

func rangeParentName(fnname string) string {
const rangeSuffix = "-range"
ridx := strings.Index(fnname, rangeSuffix)
if ridx <= 0 {
return ""
}
ok := true
for i := ridx + len(rangeSuffix); i < len(fnname); i++ {
if fnname[i] < '0' || fnname[i] > '9' {
ok = false
break
}
}
if !ok {
return ""
}
return fnname[:ridx]
}

type variablesByDepthAndDeclLine struct {
vars []*ReferenceVariable
depths []int
Expand Down
3 changes: 3 additions & 0 deletions pkg/proc/heap.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"math"
"math/bits"

"github.com/go-delve/delve/pkg/dwarf/godwarf"
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/proc"
)
Expand Down Expand Up @@ -133,6 +134,8 @@ type HeapScope struct {
scope *proc.EvalScope

finalMarks []finalMarkParam

closureStructTypes map[*proc.Function]*godwarf.StructType
}

func (s *HeapScope) readHeap() error {
Expand Down
53 changes: 47 additions & 6 deletions pkg/proc/reference.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"strconv"

"github.com/go-delve/delve/pkg/dwarf/godwarf"
"github.com/go-delve/delve/pkg/dwarf/reader"
"github.com/go-delve/delve/pkg/logflags"
"github.com/go-delve/delve/pkg/proc"
)
Expand Down Expand Up @@ -350,10 +351,7 @@ func (s *ObjRefScope) findRef(x *ReferenceVariable, idx *pprofIndex) (err error)
funcAddr, err = readUintRaw(proc.DereferenceMemory(x.mem), closureAddr, int64(s.bi.Arch.PtrSize()))
if err == nil && funcAddr != 0 {
if fn := s.bi.PCToFunc(funcAddr); fn != nil {
// cst := extra(fn, s.bi).closureStructType
cst = &godwarf.StructType{
Kind: "struct",
}
cst = s.closureStructType(fn)
}
}
if cst == nil {
Expand All @@ -375,6 +373,49 @@ func (s *ObjRefScope) findRef(x *ReferenceVariable, idx *pprofIndex) (err error)
return
}

func (s *ObjRefScope) closureStructType(fn *proc.Function) *godwarf.StructType {
if st := s.closureStructTypes[fn]; st != nil {
return st
}
image := funcToImage(s.bi, fn)
dwarfTree, err := getDwarfTree(image, getFunctionOffset(fn))
if err != nil {
return nil
}
st := &godwarf.StructType{
Kind: "struct",
}
vars := reader.Variables(dwarfTree, 0, 0, reader.VariablesNoDeclLineCheck|reader.VariablesSkipInlinedSubroutines)
for _, v := range vars {
off, ok := v.Val(godwarf.AttrGoClosureOffset).(int64)
if ok {
n, typ, err := readVarEntry(v.Tree, image)
if err == nil {
if len(n) > 0 && n[0] == '&' {
// escaped variables
n = n[1:]
}
sz := typ.Common().ByteSize
st.Field = append(st.Field, &godwarf.StructField{
Name: n,
Type: typ,
ByteOffset: off,
ByteSize: sz,
BitOffset: off * 8,
BitSize: sz * 8,
})
}
}
}

if len(st.Field) > 0 {
lf := st.Field[len(st.Field)-1]
st.ByteSize = lf.ByteOffset + lf.Type.Common().ByteSize
}
s.closureStructTypes[fn] = st
return st
}

var atomicPointerRegex = regexp.MustCompile(`^sync/atomic\.Pointer\[.*\]$`)

func (s *ObjRefScope) specialStructTypes(st *godwarf.StructType) *godwarf.StructType {
Expand Down Expand Up @@ -419,7 +460,7 @@ func ObjectReference(t *proc.Target, filename string) error {
return err
}

heapScope := &HeapScope{mem: t.Memory(), bi: t.BinInfo(), scope: scope}
heapScope := &HeapScope{mem: t.Memory(), bi: t.BinInfo(), scope: scope, closureStructTypes: make(map[*proc.Function]*godwarf.StructType)}
err = heapScope.readHeap()
if err != nil {
return err
Expand Down Expand Up @@ -464,7 +505,7 @@ func ObjectReference(t *proc.Target, filename string) error {
if len(sf) > 0 {
for i := range sf {
ms := myEvalScope{EvalScope: *proc.FrameToScope(t, t.Memory(), gr, threadID, sf[i:]...)}
locals, err := ms.Locals(mds)
locals, err := ms.Locals(t, gr, threadID, mds)
if err != nil {
logflags.DebuggerLogger().Warnf("local variables err: %v", err)
continue
Expand Down
28 changes: 5 additions & 23 deletions pkg/proc/unsafe.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package proc
import (
"debug/dwarf"
"unsafe"
_ "unsafe"

"github.com/go-delve/delve/pkg/dwarf/godwarf"
"github.com/go-delve/delve/pkg/proc"
Expand Down Expand Up @@ -67,26 +66,6 @@ func getFunctionOffset(f *proc.Function) (offset dwarf.Offset) {
return *offsetField.Get(f).(*dwarf.Offset)
}

/*
type functionExtra struct {
// closureStructType is the cached struct type for closures for this function
closureStructType *godwarf.StructType
// rangeParent is set when this function is a range-over-func body closure
// and points to the function that the closure was generated from.
rangeParent *Function
// rangeBodies is the list of range-over-func body closures for this
// function. Only one between rangeParent and rangeBodies should be set at
// any given time.
rangeBodies []*Function
}
// Not support closure type before go1.23. TODO: support go1.23
//
//go:linkname extra github.com/go-delve/delve/pkg/proc.(*Function).extra
func extra(f *Function, bi *BinaryInfo) (e *functionExtra)
*/

//go:linkname image github.com/go-delve/delve/pkg/proc.(*EvalScope).image
func image(scope *proc.EvalScope) *proc.Image

Expand All @@ -96,8 +75,11 @@ func getDwarfTree(image *proc.Image, off dwarf.Offset) (*godwarf.Tree, error)
//go:linkname findType github.com/go-delve/delve/pkg/proc.(*BinaryInfo).findType
func findType(bi *proc.BinaryInfo, name string) (godwarf.Type, error)

//go:linkname rangeParentName github.com/go-delve/delve/pkg/proc.(*Function).rangeParentName
func rangeParentName(fn *proc.Function) string
//go:linkname funcToImage github.com/go-delve/delve/pkg/proc.(*BinaryInfo).funcToImage
func funcToImage(bi *proc.BinaryInfo, fn *proc.Function) *proc.Image

//go:linkname rangeFuncStackTrace github.com/go-delve/delve/pkg/proc.rangeFuncStackTrace
func rangeFuncStackTrace(tgt *proc.Target, g *proc.G) ([]proc.Stackframe, error)

//go:linkname readVarEntry github.com/go-delve/delve/pkg/proc.readVarEntry
func readVarEntry(entry *godwarf.Tree, image *proc.Image) (name string, typ godwarf.Type, err error)
Expand Down
44 changes: 44 additions & 0 deletions pkg/version/buildinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2024 CloudWeGo Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package version

import (
"bytes"
"runtime/debug"
"text/template"
)

func init() {
buildInfo = moduleBuildInfo
}

var buildInfoTmpl = ` mod {{.Main.Path}} {{.Main.Version}} {{.Main.Sum}}
{{range .Deps}} dep {{.Path}} {{.Version}} {{.Sum}}{{if .Replace}}
=> {{.Replace.Path}} {{.Replace.Version}} {{.Replace.Sum}}{{end}}
{{end}}`

func moduleBuildInfo() string {
info, ok := debug.ReadBuildInfo()
if !ok {
return "not built in module mode"
}

buf := new(bytes.Buffer)
err := template.Must(template.New("buildinfo").Parse(buildInfoTmpl)).Execute(buf, info)
if err != nil {
panic(err)
}
return buf.String()
}
Loading

0 comments on commit e607ae4

Please sign in to comment.