1
0
mirror of https://github.com/golang/go synced 2024-09-30 14:18:32 -06: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"
"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/interp"
"golang.org/x/tools/go/ssa/ssautil"
@ -25,9 +25,9 @@ import (
var (
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.
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")
args stringListValue
)
func init() {
flag.Var(&mode, "build", ssa.BuilderModeDoc)
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.
Usage: ssadump [<flag> ...] <args> ...
Usage: ssadump [-build=[DBCSNFL]] [-test] [-run] [-interp=[TR]] [-arg=...] package...
Use -help flag to display options.
Examples:
% 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 -run -interp=T hello.go # interpret a program, with tracing
` + loader.FromArgsUsage +
`
The -run flag causes ssadump to run the first package named main.
Interpretation of the standard "testing" package is no longer supported.
@ -67,17 +69,24 @@ func main() {
func doMain() error {
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.
// TODO(adonovan): remove this when go/packages provides a better way.
var wordSize int64 = 8
switch conf.Build.GOARCH {
switch build.Default.GOARCH {
case "386", "arm":
wordSize = 4
}
conf.TypeChecker.Sizes = &types.StdSizes{
cfg.TypeChecker.Sizes = &types.StdSizes{
MaxAlign: 8,
WordSize: wordSize,
}
@ -94,11 +103,6 @@ func doMain() error {
}
}
if len(args) == 0 {
fmt.Fprint(os.Stderr, usage)
os.Exit(1)
}
// Profiling support.
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
@ -110,58 +114,46 @@ func doMain() error {
defer pprof.StopCPUProfile()
}
// Use the initial packages from the command line.
args, err := conf.FromArgs(args, *testFlag)
if err != nil {
return err
}
// The interpreter needs the runtime package.
// Load, parse and type-check the initial packages,
// and, if -run, their dependencies.
if *runFlag {
conf.Import("runtime")
cfg.Mode = packages.LoadAllSyntax
}
// Load, parse and type-check the whole program.
lprog, err := conf.Load()
initial, err := packages.Load(cfg, flag.Args()...)
if err != nil {
return err
}
if len(initial) == 0 {
return fmt.Errorf("no packages")
}
// Create and build SSA-form program representation.
prog := ssautil.CreateProgram(lprog, mode)
// Create SSA-form program representation.
prog, pkgs := ssautil.Packages(initial, mode)
// Build and display only the initial packages
// (and synthetic wrappers), unless -run is specified.
var initpkgs []*ssa.Package
for _, info := range lprog.InitialPackages() {
ssapkg := prog.Package(info.Pkg)
ssapkg.Build()
if info.Pkg.Path() != "runtime" {
initpkgs = append(initpkgs, ssapkg)
for i, p := range pkgs {
if p == nil {
return fmt.Errorf("cannot build SSA for package %s", initial[i])
}
}
// Run the interpreter.
if *runFlag {
if !*runFlag {
// Build and display only the initial packages
// (and synthetic wrappers).
for _, p := range pkgs {
p.Build()
}
} else {
// Run the interpreter.
// Build SSA for all packages.
prog.Build()
var mains []*ssa.Package
if *testFlag {
// If -test, run the tests.
for _, pkg := range initpkgs {
if main := prog.CreateTestMainPackage(pkg); main != nil {
mains = append(mains, main)
}
}
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")
}
// The interpreter needs the runtime package.
// It is a limitation of go/packages that
// we cannot add "runtime" to its initial set,
// we can only check that it is present.
if prog.ImportedPackage("runtime") == nil {
return fmt.Errorf("-run: program does not depend on runtime")
}
if runtime.GOARCH != build.Default.GOARCH {
@ -169,12 +161,27 @@ func doMain() error {
build.Default.GOARCH, runtime.GOARCH)
}
for _, main := range mains {
if len(mains) > 1 {
fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path())
}
interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Pkg.Path(), args)
// Run first main package.
for _, main := range ssautil.MainPackages(pkgs) {
fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path())
os.Exit(interp.Interpret(main, interpMode, cfg.TypeChecker.Sizes, main.Pkg.Path(), args))
}
return fmt.Errorf("no main package")
}
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 }