From 383f7cddf4f7bd8e3c08dd8e63631e2d16f07163 Mon Sep 17 00:00:00 2001 From: Artem Mihaylov <92049351+hihoak@users.noreply.github.com> Date: Fri, 11 Oct 2024 12:22:08 +0300 Subject: [PATCH] dramatically Increase performance of tool (#107) * done * fix hardcode * remove import * remove comments * Revert "remove comments" This reverts commit e74459a14067bd2c877a41f14f6af48335a5fdfb. * Revert "remove import" This reverts commit 5aa5ffbde28f625719f357b4255dc34b8e332128. * Revert "fix hardcode" This reverts commit e5e62ed3b4e59a586984d38f57bdd171c4461509. * Revert "done" This reverts commit 0fe7a9e946d00833e6b3762da42ad977c99e7606. * remade logic to support not only local modules * remove unused variables * rename variables * remade on package.Load function * make with one call * go mod tidy * fix potential bug with name collisions because of use HasSuffix * a bit optimize cycles and rename function to convertProfile back * change from filepath to path --------- Co-authored-by: artemikhaylov --- go.mod | 14 ++++++- go.sum | 15 +++---- gocov/convert/convert.go | 90 ++++++++++++++++++++++------------------ 3 files changed, 70 insertions(+), 49 deletions(-) diff --git a/go.mod b/go.mod index bd98ca7..254e818 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,18 @@ module github.com/axw/gocov -go 1.12 +go 1.22.0 + +toolchain go1.22.7 require ( github.com/stretchr/testify v1.7.1 - golang.org/x/tools v0.0.0-20190617190820-da514acc4774 + golang.org/x/tools v0.13.0 +) + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/mod v0.21.0 // indirect + golang.org/x/sys v0.12.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index 43c5cf6..3ef25ed 100644 --- a/go.sum +++ b/go.sum @@ -5,13 +5,14 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774 h1:CQVOmarCBFzTx0kbOU0ru54Cvot8SdSrNYjZPhQl+gk= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= diff --git a/gocov/convert/convert.go b/gocov/convert/convert.go index ec45bc0..c22a7f6 100644 --- a/gocov/convert/convert.go +++ b/gocov/convert/convert.go @@ -24,29 +24,26 @@ import ( "bytes" "encoding/json" "fmt" + "github.com/axw/gocov" + "github.com/axw/gocov/gocovutil" "go/ast" - "go/build" "go/parser" "go/token" + "golang.org/x/tools/cover" + goPackages "golang.org/x/tools/go/packages" "io" + "path" "path/filepath" "strings" - - "github.com/axw/gocov" - "github.com/axw/gocov/gocovutil" - "golang.org/x/tools/cover" ) func marshalJson(w io.Writer, packages []*gocov.Package) error { return json.NewEncoder(w).Encode(struct{ Packages []*gocov.Package }{packages}) } -type packagesCache map[string]*build.Package - func ConvertProfiles(filenames ...string) ([]byte, error) { var ( - ps gocovutil.Packages - packages = make(packagesCache) + ps gocovutil.Packages ) for i := range filenames { @@ -57,9 +54,42 @@ func ConvertProfiles(filenames ...string) ([]byte, error) { if err != nil { return nil, err } - for _, p := range profiles { - if err := converter.convertProfile(packages, p); err != nil { - return nil, err + + mapUniqPackageNames := make(map[string]interface{}) + uniqPackageNames := make([]string, 0, len(profiles)) + for _, profile := range profiles { + packageName := path.Dir(profile.FileName) + + if _, ok := mapUniqPackageNames[packageName]; ok { + continue + } + + mapUniqPackageNames[packageName] = nil + uniqPackageNames = append(uniqPackageNames, packageName) + } + + packages, err := goPackages.Load(&goPackages.Config{ + Mode: goPackages.NeedName | goPackages.NeedCompiledGoFiles, + }, uniqPackageNames...) + if err != nil { + return nil, fmt.Errorf("load packages: %v", err) + } + + pkgmap := make(map[string]*goPackages.Package, len(packages)) + for _, pkg := range packages { + pkgmap[pkg.PkgPath] = pkg + } + + for _, profile := range profiles { + pkgpath, filename := path.Split(profile.FileName) + pkgpath = strings.TrimSuffix(pkgpath, "/") + pkg := pkgmap[pkgpath] + for _, abspath := range pkg.CompiledGoFiles { + if filepath.Base(abspath) == filename { + if err := converter.convertProfile(profile, abspath, pkg.PkgPath); err != nil { + return nil, fmt.Errorf("convert profile %s: %w", profile.FileName, err) + } + } } } @@ -84,29 +114,26 @@ type statement struct { *StmtExtent } -func (c *converter) convertProfile(packages packagesCache, p *cover.Profile) error { - file, pkgpath, err := findFile(packages, p.FileName) - if err != nil { - return err - } - pkg := c.packages[pkgpath] +func (c *converter) convertProfile(p *cover.Profile, absFilePath, pkgPath string) error { + pkg := c.packages[pkgPath] if pkg == nil { - pkg = &gocov.Package{Name: pkgpath} - c.packages[pkgpath] = pkg + pkg = &gocov.Package{Name: pkgPath} + c.packages[pkgPath] = pkg } // Find function and statement extents; create corresponding // gocov.Functions and gocov.Statements, and keep a separate // slice of gocov.Statements so we can match them with profile // blocks. - extents, err := findFuncs(file) + extents, err := findFuncs(absFilePath) if err != nil { return err } + var stmts []statement for _, fe := range extents { f := &gocov.Function{ Name: fe.name, - File: file, + File: absFilePath, Start: fe.startOffset, End: fe.endOffset, } @@ -138,25 +165,8 @@ func (c *converter) convertProfile(packages packagesCache, p *cover.Profile) err break } } - return nil -} - -// findFile finds the location of the named file in GOROOT, GOPATH etc. -func findFile(packages packagesCache, file string) (filename, pkgpath string, err error) { - dir, file := filepath.Split(file) - if dir != "" { - dir = strings.TrimSuffix(dir, "/") - } - pkg, ok := packages[dir] - if !ok { - pkg, err = build.Import(dir, ".", build.FindOnly) - if err != nil { - return "", "", fmt.Errorf("can't find %q: %w", file, err) - } - packages[dir] = pkg - } - return filepath.Join(pkg.Dir, file), pkg.ImportPath, nil + return nil } // findFuncs parses the file and returns a slice of FuncExtent descriptors.