// 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" "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" ) // flags var ( mode = ssa.BuilderMode(0) testFlag = flag.Bool("test", false, "include implicit test packages and executables") 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: 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") 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 [-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 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() if len(flag.Args()) == 0 { fmt.Fprint(os.Stderr, usage) os.Exit(1) } 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 build.Default.GOARCH { case "386", "arm": wordSize = 4 } 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) } } // 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() } // Load, parse and type-check the initial packages, // and, if -run, their dependencies. if *runFlag { cfg.Mode = packages.LoadAllSyntax } initial, err := packages.Load(cfg, flag.Args()...) if err != nil { return err } 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) 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 // (and synthetic wrappers). for _, p := range pkgs { p.Build() } } else { // Run the interpreter. // Build SSA for all packages. prog.Build() // 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 { return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)", build.Default.GOARCH, runtime.GOARCH) } // 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)) } 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 }