Skip to content

Commit

Permalink
Fix issues with latest Go and generics (#202)
Browse files Browse the repository at this point in the history
* Updated dependencies; removed `pointer` analyzer as it panics in go 1.22; updated docs

* Updated pipeline

* Alwaysa use InstantiateGenerics mode

* Improve margin when using go graphviz for rendering

* Fix detecting std packages

* Update deps with fixes for go callgraph analysis

* Handle synthetic function detection where callee pkg is nil

* Add logging for analysis process

Signed-off-by: Ondrej Fabry <[email protected]>

* Update CI workflow

Signed-off-by: Ondrej Fabry <[email protected]>

---------

Signed-off-by: Ondrej Fabry <[email protected]>
Co-authored-by: Egor Aristov <[email protected]>
  • Loading branch information
ondrajz and Egor3f authored Jan 1, 2025
1 parent 5341ae2 commit 25ddacd
Show file tree
Hide file tree
Showing 12 changed files with 200 additions and 208 deletions.
9 changes: 4 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@ name: CI

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
schedule:
- cron: '0 */12 * * *'
- cron: '0 3 * * *'

jobs:
build-test:
Expand All @@ -15,14 +14,14 @@ jobs:
strategy:
fail-fast: false
matrix:
go: [ '1.20', '1.19' ]
go: [ 'stable', 'oldstable', '1.21' ]

steps:
- name: "Checkout"
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: "Setup Go ${{ matrix.go }}"
uses: actions/setup-go@v3
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go }}

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ _testmain.go
# Builds
/.build/
go-callvis

# IDE
/.idea/
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

build: ## Build go-callvis
go build -tags $(GO_BUILD_TAGS) -ldflags "$(GO_LDFLAGS)" $(GO_BUILD_ARGS)
go build -v -tags $(GO_BUILD_TAGS) -ldflags "$(GO_LDFLAGS)" $(GO_BUILD_ARGS)

test: ## Run unit tests
go test -tags $(GO_BUILD_TAGS) -ldflags "$(GO_LDFLAGS)" $(GO_BUILD_ARGS) -short -race ./...
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ Usage of go-callvis:
a list of build tags to consider satisfied during the build. For more information about build tags, see the description of build constraints in the documentation for the go/build package
-tests
Include test code.
-algo string
Use specific algorithm for package analyzer: static, cha or rta (default "static")
-version
Show version and exit.
```
Expand Down
63 changes: 29 additions & 34 deletions analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,33 @@ import (
"fmt"
"go/build"
"go/types"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/callgraph/rta"
"golang.org/x/tools/go/callgraph/static"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strings"
"time"

"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/callgraph/rta"
"golang.org/x/tools/go/callgraph/static"

"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
)

type CallGraphType string

const (
CallGraphTypeStatic CallGraphType = "static"
CallGraphTypeCha = "cha"
CallGraphTypeRta = "rta"
CallGraphTypePointer = "pointer"
CallGraphTypeStatic CallGraphType = "static"
CallGraphTypeCha CallGraphType = "cha"
CallGraphTypeRta CallGraphType = "rta"
)

//==[ type def/func: analysis ]===============================================
// ==[ type def/func: analysis ]===============================================
type renderOpts struct {
cacheDir string
focus string
Expand Down Expand Up @@ -80,7 +80,7 @@ func initFuncs(pkgs []*ssa.Package) ([]*ssa.Function, error) {
return inits, nil
}

//==[ type def/func: analysis ]===============================================
// ==[ type def/func: analysis ]===============================================
type analysis struct {
opts *renderOpts
prog *ssa.Program
Expand All @@ -97,30 +97,35 @@ func (a *analysis) DoAnalysis(
tests bool,
args []string,
) error {
logf("begin analysis")
defer logf("analysis done")

cfg := &packages.Config{
Mode: packages.LoadAllSyntax,
Tests: tests,
Dir: dir,
BuildFlags: getBuildFlags(),
}

logf("loading packages")

initial, err := packages.Load(cfg, args...)
if err != nil {
return err
}

if packages.PrintErrors(initial) > 0 {
return fmt.Errorf("packages contain errors")
}

logf("loaded %d initial packages, building program", len(initial))

// Create and build SSA-form program representation.
mode := ssa.BuilderMode(0)
if algo == CallGraphTypeRta {
mode = ssa.InstantiateGenerics
}
mode := ssa.InstantiateGenerics
prog, pkgs := ssautil.AllPackages(initial, mode)
prog.Build()

logf("build done, computing callgraph (algo: %v)", algo)

var graph *callgraph.Graph
var mainPkg *ssa.Package

Expand All @@ -139,7 +144,7 @@ func (a *analysis) DoAnalysis(
for _, main := range mains {
roots = append(roots, main.Func("main"))
}

inits, err := initFuncs(prog.AllPackages())
if err != nil {
return err
Expand All @@ -149,26 +154,11 @@ func (a *analysis) DoAnalysis(
}

graph = rta.Analyze(roots, true).CallGraph
case CallGraphTypePointer:
mains, err := mainPackages(prog.AllPackages())
if err != nil {
return err
}
mainPkg = mains[0]
config := &pointer.Config{
Mains: mains,
BuildCallGraph: true,
}
ptares, err := pointer.Analyze(config)
if err != nil {
return err
}
graph = ptares.CallGraph
default:
return fmt.Errorf("invalid call graph type: %s", a.opts.algo)
}

//cg.DeleteSyntheticNodes()
logf("callgraph resolved with %d nodes", len(graph.Nodes))

a.prog = prog
a.pkgs = pkgs
Expand Down Expand Up @@ -276,6 +266,9 @@ func (a *analysis) Render() ([]byte, error) {
focusPkg *types.Package
)

start := time.Now()
logf("begin rendering")

if a.opts.focus != "" {
if ssaPkg = a.prog.ImportedPackage(a.opts.focus); ssaPkg == nil {
if strings.Contains(a.opts.focus, "/") {
Expand All @@ -302,7 +295,7 @@ func (a *analysis) Render() ([]byte, error) {
}
}
focusPkg = ssaPkg.Pkg
logf("focusing: %v", focusPkg.Path())
logf("focusing package: %v (path: %v)", focusPkg.Name(), focusPkg.Path())
}

dot, err := printOutput(
Expand All @@ -321,6 +314,8 @@ func (a *analysis) Render() ([]byte, error) {
return nil, fmt.Errorf("processing failed: %v", err)
}

logf("rendering done (took %v sec)", time.Since(start).Round(time.Millisecond).Seconds())

return dot, nil
}

Expand Down
Loading

0 comments on commit 25ddacd

Please sign in to comment.