1
0
mirror of https://github.com/golang/go synced 2024-11-18 12:04:57 -07:00

cmd/ssadump: use go/packages

Unlike go/loader's FromArgs, go/packages doesn't return the
non-package arguments, so we add a new repeated flag -arg=x -arg=y to
specify them.

Because we cannot add packages to a packages.Load query, we emit an
error if -run is specified and "runtime" is not among the
dependencies.

There is no easy way to distinguish synthetic test main packages
from regular main packages, so we no longer try.
This makes -test and -run orthogonal.

Change-Id: Ibd69b993c05de62df01dde52218a9e266cd63a71
Reviewed-on: https://go-review.googlesource.com/128837
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Alan Donovan 2018-08-09 13:20:37 -04:00
parent cf99646d84
commit 27709f6afe

View File

@ -15,7 +15,7 @@ import (
"runtime/pprof" "runtime/pprof"
"golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/packages"
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/interp" "golang.org/x/tools/go/ssa/interp"
"golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/go/ssa/ssautil"
@ -25,9 +25,9 @@ import (
var ( var (
mode = ssa.BuilderMode(0) mode = ssa.BuilderMode(0)
testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.") testFlag = flag.Bool("test", false, "include implicit test packages and executables")
runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.") runFlag = flag.Bool("run", false, "interpret the SSA program")
interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter. interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter.
The value is a sequence of zero or more more of these letters: The value is a sequence of zero or more more of these letters:
@ -36,23 +36,25 @@ T [T]race execution of the program. Best for single-threaded programs!
`) `)
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
args stringListValue
) )
func init() { func init() {
flag.Var(&mode, "build", ssa.BuilderModeDoc) flag.Var(&mode, "build", ssa.BuilderModeDoc)
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
flag.Var(&args, "arg", "add argument to interpreted program")
} }
const usage = `SSA builder and interpreter. const usage = `SSA builder and interpreter.
Usage: ssadump [<flag> ...] <args> ... Usage: ssadump [-build=[DBCSNFL]] [-test] [-run] [-interp=[TR]] [-arg=...] package...
Use -help flag to display options. Use -help flag to display options.
Examples: Examples:
% ssadump -build=F hello.go # dump SSA form of a single package % ssadump -build=F hello.go # dump SSA form of a single package
% ssadump -build=F -test fmt # dump SSA form of a package and its tests % ssadump -build=F -test fmt # dump SSA form of a package and its tests
% ssadump -run -interp=T hello.go # interpret a program, with tracing % ssadump -run -interp=T hello.go # interpret a program, with tracing
` + loader.FromArgsUsage +
`
The -run flag causes ssadump to run the first package named main. The -run flag causes ssadump to run the first package named main.
Interpretation of the standard "testing" package is no longer supported. Interpretation of the standard "testing" package is no longer supported.
@ -67,17 +69,24 @@ func main() {
func doMain() error { func doMain() error {
flag.Parse() flag.Parse()
args := flag.Args() if len(flag.Args()) == 0 {
fmt.Fprint(os.Stderr, usage)
os.Exit(1)
}
conf := loader.Config{Build: &build.Default} cfg := &packages.Config{
Mode: packages.LoadSyntax,
Tests: *testFlag,
}
// Choose types.Sizes from conf.Build. // Choose types.Sizes from conf.Build.
// TODO(adonovan): remove this when go/packages provides a better way.
var wordSize int64 = 8 var wordSize int64 = 8
switch conf.Build.GOARCH { switch build.Default.GOARCH {
case "386", "arm": case "386", "arm":
wordSize = 4 wordSize = 4
} }
conf.TypeChecker.Sizes = &types.StdSizes{ cfg.TypeChecker.Sizes = &types.StdSizes{
MaxAlign: 8, MaxAlign: 8,
WordSize: wordSize, WordSize: wordSize,
} }
@ -94,11 +103,6 @@ func doMain() error {
} }
} }
if len(args) == 0 {
fmt.Fprint(os.Stderr, usage)
os.Exit(1)
}
// Profiling support. // Profiling support.
if *cpuprofile != "" { if *cpuprofile != "" {
f, err := os.Create(*cpuprofile) f, err := os.Create(*cpuprofile)
@ -110,58 +114,46 @@ func doMain() error {
defer pprof.StopCPUProfile() defer pprof.StopCPUProfile()
} }
// Use the initial packages from the command line. // Load, parse and type-check the initial packages,
args, err := conf.FromArgs(args, *testFlag) // and, if -run, their dependencies.
if err != nil {
return err
}
// The interpreter needs the runtime package.
if *runFlag { if *runFlag {
conf.Import("runtime") cfg.Mode = packages.LoadAllSyntax
} }
initial, err := packages.Load(cfg, flag.Args()...)
// Load, parse and type-check the whole program.
lprog, err := conf.Load()
if err != nil { if err != nil {
return err return err
} }
if len(initial) == 0 {
return fmt.Errorf("no packages")
}
// Create and build SSA-form program representation. // Create SSA-form program representation.
prog := ssautil.CreateProgram(lprog, mode) prog, pkgs := ssautil.Packages(initial, mode)
for i, p := range pkgs {
if p == nil {
return fmt.Errorf("cannot build SSA for package %s", initial[i])
}
}
if !*runFlag {
// Build and display only the initial packages // Build and display only the initial packages
// (and synthetic wrappers), unless -run is specified. // (and synthetic wrappers).
var initpkgs []*ssa.Package for _, p := range pkgs {
for _, info := range lprog.InitialPackages() { p.Build()
ssapkg := prog.Package(info.Pkg)
ssapkg.Build()
if info.Pkg.Path() != "runtime" {
initpkgs = append(initpkgs, ssapkg)
}
} }
} else {
// Run the interpreter. // Run the interpreter.
if *runFlag { // Build SSA for all packages.
prog.Build() prog.Build()
var mains []*ssa.Package // The interpreter needs the runtime package.
if *testFlag { // It is a limitation of go/packages that
// If -test, run the tests. // we cannot add "runtime" to its initial set,
for _, pkg := range initpkgs { // we can only check that it is present.
if main := prog.CreateTestMainPackage(pkg); main != nil { if prog.ImportedPackage("runtime") == nil {
mains = append(mains, main) return fmt.Errorf("-run: program does not depend on runtime")
}
}
if mains == nil {
return fmt.Errorf("no tests")
}
} else {
// Otherwise, run the main packages.
mains = ssautil.MainPackages(initpkgs)
if len(mains) == 0 {
return fmt.Errorf("no main package")
}
} }
if runtime.GOARCH != build.Default.GOARCH { if runtime.GOARCH != build.Default.GOARCH {
@ -169,12 +161,27 @@ func doMain() error {
build.Default.GOARCH, runtime.GOARCH) build.Default.GOARCH, runtime.GOARCH)
} }
for _, main := range mains { // Run first main package.
if len(mains) > 1 { for _, main := range ssautil.MainPackages(pkgs) {
fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path()) fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path())
os.Exit(interp.Interpret(main, interpMode, cfg.TypeChecker.Sizes, main.Pkg.Path(), args))
} }
interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Pkg.Path(), args) return fmt.Errorf("no main package")
}
} }
return nil return nil
} }
// stringListValue is a flag.Value that accumulates strings.
// e.g. --flag=one --flag=two would produce []string{"one", "two"}.
type stringListValue []string
func newStringListValue(val []string, p *[]string) *stringListValue {
*p = val
return (*stringListValue)(p)
}
func (ss *stringListValue) Get() interface{} { return []string(*ss) }
func (ss *stringListValue) String() string { return fmt.Sprintf("%q", *ss) }
func (ss *stringListValue) Set(s string) error { *ss = append(*ss, s); return nil }