mirror of
https://github.com/golang/go
synced 2024-11-05 15:46:11 -07:00
c6a14e550a
Change-Id: I95fee5042e39156462a5c21ade29d7216474be02 Reviewed-on: https://go-review.googlesource.com/8384 Reviewed-by: Robert Griesemer <gri@golang.org>
183 lines
4.5 KiB
Go
183 lines
4.5 KiB
Go
// 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"
|
|
"os"
|
|
"runtime"
|
|
"runtime/pprof"
|
|
|
|
"golang.org/x/tools/go/loader"
|
|
"golang.org/x/tools/go/ssa"
|
|
"golang.org/x/tools/go/ssa/interp"
|
|
"golang.org/x/tools/go/types"
|
|
)
|
|
|
|
var (
|
|
importbinFlag = flag.Bool("importbin", false,
|
|
"Import binary export data from gc's object files, not source. "+
|
|
"Imported functions will have no bodies.")
|
|
|
|
modeFlag = ssa.BuilderModeFlag(flag.CommandLine, "build", 0)
|
|
|
|
testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.")
|
|
|
|
runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.")
|
|
|
|
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!
|
|
`)
|
|
)
|
|
|
|
const usage = `SSA builder and interpreter.
|
|
Usage: ssadump [<flag> ...] <args> ...
|
|
Use -help flag to display options.
|
|
|
|
Examples:
|
|
% ssadump -build=FP -importbin hello.go # quickly dump SSA form of a single package
|
|
% ssadump -run -interp=T hello.go # interpret a program, with tracing
|
|
% ssadump -run -test unicode -- -test.v # interpret the unicode package's tests, verbosely
|
|
` + loader.FromArgsUsage +
|
|
`
|
|
When -run is specified, ssadump will run the program.
|
|
The entry point depends on the -test flag:
|
|
if clear, it runs the first package named main.
|
|
if set, it runs the tests of each package.
|
|
`
|
|
|
|
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
|
|
|
|
func init() {
|
|
// If $GOMAXPROCS isn't set, use the full capacity of the machine.
|
|
// For small machines, use at least 4 threads.
|
|
if os.Getenv("GOMAXPROCS") == "" {
|
|
n := runtime.NumCPU()
|
|
if n < 4 {
|
|
n = 4
|
|
}
|
|
runtime.GOMAXPROCS(n)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
if err := doMain(); err != nil {
|
|
fmt.Fprintf(os.Stderr, "ssadump: %s\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func doMain() error {
|
|
flag.Parse()
|
|
args := flag.Args()
|
|
|
|
conf := loader.Config{
|
|
Build: &build.Default,
|
|
ImportFromBinary: *importbinFlag,
|
|
}
|
|
// TODO(adonovan): make go/types choose its default Sizes from
|
|
// build.Default or a specified *build.Context.
|
|
var wordSize int64 = 8
|
|
switch conf.Build.GOARCH {
|
|
case "386", "arm":
|
|
wordSize = 4
|
|
}
|
|
conf.TypeChecker.Sizes = &types.StdSizes{
|
|
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)
|
|
}
|
|
}
|
|
|
|
if len(args) == 0 {
|
|
fmt.Fprint(os.Stderr, usage)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// 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()
|
|
}
|
|
|
|
// 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.
|
|
if *runFlag {
|
|
conf.Import("runtime")
|
|
}
|
|
|
|
// Load, parse and type-check the whole program.
|
|
iprog, err := conf.Load()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Create and build SSA-form program representation.
|
|
prog := ssa.Create(iprog, *modeFlag)
|
|
prog.BuildAll()
|
|
|
|
// Run the interpreter.
|
|
if *runFlag {
|
|
var main *ssa.Package
|
|
pkgs := prog.AllPackages()
|
|
if *testFlag {
|
|
// If -test, run all packages' tests.
|
|
if len(pkgs) > 0 {
|
|
main = prog.CreateTestMainPackage(pkgs...)
|
|
}
|
|
if main == nil {
|
|
return fmt.Errorf("no tests")
|
|
}
|
|
} else {
|
|
// Otherwise, run main.main.
|
|
for _, pkg := range pkgs {
|
|
if pkg.Object.Name() == "main" {
|
|
main = pkg
|
|
if main.Func("main") == nil {
|
|
return fmt.Errorf("no func main() in main package")
|
|
}
|
|
break
|
|
}
|
|
}
|
|
if main == nil {
|
|
return fmt.Errorf("no main package")
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Object.Path(), args)
|
|
}
|
|
return nil
|
|
}
|