How to use `golang.org/x/tools/go/packages` to build a multi SSA with multi-package project

  • My project is in /home/hbb/projs/zhai/atmodels-server/, and the project struct is like this:
.
├── Makefile
├── go.mod
├── go.sum
├── go.work
├── go.work.sum
├── atms
│   ├── cmds
│   │   ├── executor.go
│   │   └── root.go
│   ├── main.go
├── pkg
│   ├── app
│   │   ├── appcm
│   │   │   ├── appcm.go
│   ├── errs
│   │   ├── errors.go
│   │   ├── errors_test.go
│   │   └── stack.go
│   ├── go.mod
│   ├── go.sum
  • So, as the above code shows, my pkg sub-folder is an independent go pakcage;
  • The following code is what i was running with golang.org/x/tools/go/packages, i want to build a callgraph for my project:
package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"strings"

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

// mainPackages returns the main packages to analyze.
// Each resulting package is named "main" and has a main function.
func mainPackages(pkgs []*ssa.Package) ([]*ssa.Package, error) {
	var mains []*ssa.Package
	for _, p := range pkgs {
		if p != nil && p.Pkg.Name() == "main" && !strings.Contains(p.Pkg.Path(), "test") && p.Func("main") != nil {
			mains = append(mains, p)
		}
	}
	if len(mains) == 0 {
		return nil, fmt.Errorf("no main packages")
	}
	return mains, nil
}

func main() {
	flag.Parse()

	// Many tools pass their command-line arguments (after any flags)
	// uninterpreted to packages.Load so that it can interpret them
	// according to the conventions of the underlying build system.
	cfg := &packages.Config{
		Dir:  "/home/hbb/projs/zhai/atmodels-server/",
		Mode: packages.LoadAllSyntax,
	}
	pkgs, err := packages.Load(cfg, flag.Args()...)
	if err != nil {
		fmt.Fprintf(os.Stderr, "load: %v\n", err)
		os.Exit(1)
	}
	if packages.PrintErrors(pkgs) > 0 {
		os.Exit(1)
	}

	// Print the names of the source files
	// for each package listed on the command line.
	for _, pkg := range pkgs {
		fmt.Println(pkg.ID, pkg.GoFiles)
	}

	// Create and build SSA-form program representation.
	prog, _ := ssautil.AllPackages(pkgs, 0)
	prog.Build()

	mains, err := mainPackages(prog.AllPackages())
	if err != nil {
		panic(err)
	}
	var roots []*ssa.Function
	mainPkg := mains[0]
	for _, main := range mains {
		roots = append(roots, main.Func("main"))
	}
	log.Println("roots:", mainPkg, roots)
	graph := rta.Analyze(roots, true).CallGraph
	log.Println("graph:", graph)
}
  • My command is: go run main.go ./..., and I got this error:
...  // more pakcages
atmodels-server/atms/tests/https []
atmodels-server/atms/tests/timer []
atmodels-server/atms/utils/retry [/home/hbb/projs/zhai/atmodels-server/atms/utils/retry/retry.go /home/hbb/projs/zhai/atmodels-server/atms/utils/retry/types.go]
atmodels-server/atms/wssvr/contorllers [/home/hbb/projs/zhai/atmodels-server/atms/wssvr/contorllers/types.go]
atmodels-server/atms/wssvr/websocketsvr [/home/hbb/projs/zhai/atmodels-server/atms/wssvr/websocketsvr/server.go]
2024/11/04 22:04:32 roots: package atmodels-server/atms [atmodels-server/atms.main]
panic: T

goroutine 1 [running]:
golang.org/x/tools/go/callgraph/rta.(*rta).addRuntimeType(0xc001243b98, {0x743620?, 0xc00fad05d0?}, 0x0)
        /home/hbb/go/pkg/mod/golang.org/x/tools@v0.26.0/go/callgraph/rta/rta.go:535 +0x8fa
golang.org/x/tools/go/callgraph/rta.(*rta).visitFunc(0xc001243b98, 0xc085f85c00)
        /home/hbb/go/pkg/mod/golang.org/x/tools@v0.26.0/go/callgraph/rta/rta.go:284 +0x14e
golang.org/x/tools/go/callgraph/rta.Analyze({0xc0c25ad6d0, 0x1, 0x2?}, 0x1)
        /home/hbb/go/pkg/mod/golang.org/x/tools@v0.26.0/go/callgraph/rta/rta.go:351 +0x5aa
main.main()
        /home/hbb/tmp/go-ssa/loguse.go:70 +0x5d8
exit status 2
  • It seems the atmodels-server/pkg is not recognized and included in the pkgs,so how can i resolve the problem.

Thanks.