1// Copyright 2013 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// ssadump: a tool for displaying and interpreting the SSA form of Go programs. 6package main // import "golang.org/x/tools/cmd/ssadump" 7 8import ( 9 "flag" 10 "fmt" 11 "go/build" 12 "go/types" 13 "os" 14 "runtime" 15 "runtime/pprof" 16 17 "golang.org/x/tools/go/buildutil" 18 "golang.org/x/tools/go/loader" 19 "golang.org/x/tools/go/ssa" 20 "golang.org/x/tools/go/ssa/interp" 21 "golang.org/x/tools/go/ssa/ssautil" 22) 23 24// flags 25var ( 26 mode = ssa.BuilderMode(0) 27 28 testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages.") 29 30 runFlag = flag.Bool("run", false, "Invokes the SSA interpreter on the program.") 31 32 interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter. 33The value is a sequence of zero or more more of these letters: 34R disable [R]ecover() from panic; show interpreter crash instead. 35T [T]race execution of the program. Best for single-threaded programs! 36`) 37 38 cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") 39) 40 41func init() { 42 flag.Var(&mode, "build", ssa.BuilderModeDoc) 43 flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) 44} 45 46const usage = `SSA builder and interpreter. 47Usage: ssadump [<flag> ...] <args> ... 48Use -help flag to display options. 49 50Examples: 51% ssadump -build=F hello.go # dump SSA form of a single package 52% ssadump -build=F -test fmt # dump SSA form of a package and its tests 53% ssadump -run -interp=T hello.go # interpret a program, with tracing 54` + loader.FromArgsUsage + 55 ` 56The -run flag causes ssadump to run the first package named main. 57 58Interpretation of the standard "testing" package is no longer supported. 59` 60 61func main() { 62 if err := doMain(); err != nil { 63 fmt.Fprintf(os.Stderr, "ssadump: %s\n", err) 64 os.Exit(1) 65 } 66} 67 68func doMain() error { 69 flag.Parse() 70 args := flag.Args() 71 72 conf := loader.Config{Build: &build.Default} 73 74 // Choose types.Sizes from conf.Build. 75 var wordSize int64 = 8 76 switch conf.Build.GOARCH { 77 case "386", "arm": 78 wordSize = 4 79 } 80 conf.TypeChecker.Sizes = &types.StdSizes{ 81 MaxAlign: 8, 82 WordSize: wordSize, 83 } 84 85 var interpMode interp.Mode 86 for _, c := range *interpFlag { 87 switch c { 88 case 'T': 89 interpMode |= interp.EnableTracing 90 case 'R': 91 interpMode |= interp.DisableRecover 92 default: 93 return fmt.Errorf("unknown -interp option: '%c'", c) 94 } 95 } 96 97 if len(args) == 0 { 98 fmt.Fprint(os.Stderr, usage) 99 os.Exit(1) 100 } 101 102 // Profiling support. 103 if *cpuprofile != "" { 104 f, err := os.Create(*cpuprofile) 105 if err != nil { 106 fmt.Fprintln(os.Stderr, err) 107 os.Exit(1) 108 } 109 pprof.StartCPUProfile(f) 110 defer pprof.StopCPUProfile() 111 } 112 113 // Use the initial packages from the command line. 114 args, err := conf.FromArgs(args, *testFlag) 115 if err != nil { 116 return err 117 } 118 119 // The interpreter needs the runtime package. 120 if *runFlag { 121 conf.Import("runtime") 122 } 123 124 // Load, parse and type-check the whole program. 125 lprog, err := conf.Load() 126 if err != nil { 127 return err 128 } 129 130 // Create and build SSA-form program representation. 131 prog := ssautil.CreateProgram(lprog, mode) 132 133 // Build and display only the initial packages 134 // (and synthetic wrappers), unless -run is specified. 135 var initpkgs []*ssa.Package 136 for _, info := range lprog.InitialPackages() { 137 ssapkg := prog.Package(info.Pkg) 138 ssapkg.Build() 139 if info.Pkg.Path() != "runtime" { 140 initpkgs = append(initpkgs, ssapkg) 141 } 142 } 143 144 // Run the interpreter. 145 if *runFlag { 146 prog.Build() 147 148 var mains []*ssa.Package 149 if *testFlag { 150 // If -test, run the tests. 151 for _, pkg := range initpkgs { 152 if main := prog.CreateTestMainPackage(pkg); main != nil { 153 mains = append(mains, main) 154 } 155 } 156 if mains == nil { 157 return fmt.Errorf("no tests") 158 } 159 } else { 160 // Otherwise, run the main packages. 161 mains = ssautil.MainPackages(initpkgs) 162 if len(mains) == 0 { 163 return fmt.Errorf("no main package") 164 } 165 } 166 167 if runtime.GOARCH != build.Default.GOARCH { 168 return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)", 169 build.Default.GOARCH, runtime.GOARCH) 170 } 171 172 for _, main := range mains { 173 if len(mains) > 1 { 174 fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path()) 175 } 176 interp.Interpret(main, interpMode, conf.TypeChecker.Sizes, main.Pkg.Path(), args) 177 } 178 } 179 return nil 180} 181