2018-08-07 15:51:34 +00:00
|
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// ssadump: a tool for displaying and interpreting the SSA form of Go programs.
|
|
|
|
package main // import "golang.org/x/tools/cmd/ssadump"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"go/build"
|
|
|
|
"go/types"
|
|
|
|
"os"
|
|
|
|
"runtime"
|
|
|
|
"runtime/pprof"
|
|
|
|
|
|
|
|
"golang.org/x/tools/go/buildutil"
|
2018-09-05 15:36:14 +00:00
|
|
|
"golang.org/x/tools/go/packages"
|
2018-08-07 15:51:34 +00:00
|
|
|
"golang.org/x/tools/go/ssa"
|
|
|
|
"golang.org/x/tools/go/ssa/interp"
|
|
|
|
"golang.org/x/tools/go/ssa/ssautil"
|
|
|
|
)
|
|
|
|
|
|
|
|
// flags
|
|
|
|
var (
|
|
|
|
mode = ssa.BuilderMode(0)
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
testFlag = flag.Bool("test", false, "include implicit test packages and executables")
|
2018-08-07 15:51:34 +00:00
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
runFlag = flag.Bool("run", false, "interpret the SSA program")
|
2018-08-07 15:51:34 +00:00
|
|
|
|
|
|
|
interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter.
|
|
|
|
The value is a sequence of zero or more more of these letters:
|
|
|
|
R disable [R]ecover() from panic; show interpreter crash instead.
|
|
|
|
T [T]race execution of the program. Best for single-threaded programs!
|
|
|
|
`)
|
|
|
|
|
|
|
|
cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
|
2018-09-05 15:36:14 +00:00
|
|
|
|
|
|
|
args stringListValue
|
2018-08-07 15:51:34 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
flag.Var(&mode, "build", ssa.BuilderModeDoc)
|
|
|
|
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
|
2018-09-05 15:36:14 +00:00
|
|
|
flag.Var(&args, "arg", "add argument to interpreted program")
|
2018-08-07 15:51:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const usage = `SSA builder and interpreter.
|
2018-09-05 15:36:14 +00:00
|
|
|
Usage: ssadump [-build=[DBCSNFL]] [-test] [-run] [-interp=[TR]] [-arg=...] package...
|
2018-08-07 15:51:34 +00:00
|
|
|
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
|
2018-09-05 15:36:14 +00:00
|
|
|
|
2018-08-07 15:51:34 +00:00
|
|
|
The -run flag causes ssadump to run the first package named main.
|
|
|
|
|
|
|
|
Interpretation of the standard "testing" package is no longer supported.
|
|
|
|
`
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
if err := doMain(); err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "ssadump: %s\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func doMain() error {
|
|
|
|
flag.Parse()
|
2018-09-05 15:36:14 +00:00
|
|
|
if len(flag.Args()) == 0 {
|
|
|
|
fmt.Fprint(os.Stderr, usage)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2018-08-07 15:51:34 +00:00
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
cfg := &packages.Config{
|
|
|
|
Mode: packages.LoadSyntax,
|
|
|
|
Tests: *testFlag,
|
|
|
|
}
|
2018-08-07 15:51:34 +00:00
|
|
|
|
|
|
|
// Choose types.Sizes from conf.Build.
|
2018-09-05 15:36:14 +00:00
|
|
|
// TODO(adonovan): remove this when go/packages provides a better way.
|
2018-08-07 15:51:34 +00:00
|
|
|
var wordSize int64 = 8
|
2018-09-05 15:36:14 +00:00
|
|
|
switch build.Default.GOARCH {
|
2018-08-07 15:51:34 +00:00
|
|
|
case "386", "arm":
|
|
|
|
wordSize = 4
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
sizes := &types.StdSizes{
|
2018-08-07 15:51:34 +00:00
|
|
|
MaxAlign: 8,
|
|
|
|
WordSize: wordSize,
|
|
|
|
}
|
|
|
|
|
|
|
|
var interpMode interp.Mode
|
|
|
|
for _, c := range *interpFlag {
|
|
|
|
switch c {
|
|
|
|
case 'T':
|
|
|
|
interpMode |= interp.EnableTracing
|
|
|
|
case 'R':
|
|
|
|
interpMode |= interp.DisableRecover
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("unknown -interp option: '%c'", c)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Profiling support.
|
|
|
|
if *cpuprofile != "" {
|
|
|
|
f, err := os.Create(*cpuprofile)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
pprof.StartCPUProfile(f)
|
|
|
|
defer pprof.StopCPUProfile()
|
|
|
|
}
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
// Load, parse and type-check the initial packages,
|
|
|
|
// and, if -run, their dependencies.
|
2018-08-07 15:51:34 +00:00
|
|
|
if *runFlag {
|
2018-09-05 15:36:14 +00:00
|
|
|
cfg.Mode = packages.LoadAllSyntax
|
2018-08-07 15:51:34 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
initial, err := packages.Load(cfg, flag.Args()...)
|
2018-08-07 15:51:34 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
if len(initial) == 0 {
|
|
|
|
return fmt.Errorf("no packages")
|
|
|
|
}
|
|
|
|
if packages.PrintErrors(initial) > 0 {
|
|
|
|
return fmt.Errorf("packages contain errors")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create SSA-form program representation.
|
|
|
|
prog, pkgs := ssautil.Packages(initial, mode)
|
2018-08-07 15:51:34 +00:00
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
for i, p := range pkgs {
|
|
|
|
if p == nil {
|
|
|
|
return fmt.Errorf("cannot build SSA for package %s", initial[i])
|
2018-08-07 15:51:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
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.
|
2018-08-07 15:51:34 +00:00
|
|
|
prog.Build()
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
// 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")
|
2018-08-07 15:51:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if runtime.GOARCH != build.Default.GOARCH {
|
|
|
|
return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)",
|
|
|
|
build.Default.GOARCH, runtime.GOARCH)
|
|
|
|
}
|
|
|
|
|
2018-09-05 15:36:14 +00:00
|
|
|
// 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, sizes, main.Pkg.Path(), args))
|
2018-08-07 15:51:34 +00:00
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
return fmt.Errorf("no main package")
|
2018-08-07 15:51:34 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2018-09-05 15:36:14 +00:00
|
|
|
|
|
|
|
// 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 }
|