golang.org/x/tools@v0.21.0/cmd/ssadump/main.go (about) 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. 6 package main // import "golang.org/x/tools/cmd/ssadump" 7 8 import ( 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/packages" 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 25 var ( 26 mode = ssa.BuilderMode(0) 27 28 testFlag = flag.Bool("test", false, "include implicit test packages and executables") 29 30 runFlag = flag.Bool("run", false, "interpret the SSA program") 31 32 interpFlag = flag.String("interp", "", `Options controlling the SSA test interpreter. 33 The value is a sequence of zero or more more of these letters: 34 R disable [R]ecover() from panic; show interpreter crash instead. 35 T [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 args stringListValue 41 ) 42 43 func init() { 44 flag.Var(&mode, "build", ssa.BuilderModeDoc) 45 flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) 46 flag.Var(&args, "arg", "add argument to interpreted program") 47 } 48 49 const usage = `SSA builder and interpreter. 50 Usage: ssadump [-build=[DBCSNFLG]] [-test] [-run] [-interp=[TR]] [-arg=...] package... 51 Use -help flag to display options. 52 53 Examples: 54 % ssadump -build=F hello.go # dump SSA form of a single package 55 % ssadump -build=F -test fmt # dump SSA form of a package and its tests 56 % ssadump -run -interp=T hello.go # interpret a program, with tracing 57 58 The -run flag causes ssadump to build the code in a runnable form and run the first 59 package named main. 60 61 Interpretation of the standard "testing" package is no longer supported. 62 ` 63 64 func main() { 65 if err := doMain(); err != nil { 66 fmt.Fprintf(os.Stderr, "ssadump: %s\n", err) 67 os.Exit(1) 68 } 69 } 70 71 func doMain() error { 72 flag.Parse() 73 if len(flag.Args()) == 0 { 74 fmt.Fprint(os.Stderr, usage) 75 os.Exit(1) 76 } 77 78 cfg := &packages.Config{ 79 Mode: packages.LoadSyntax, 80 Tests: *testFlag, 81 } 82 83 // Choose types.Sizes from conf.Build. 84 // TODO(adonovan): remove this when go/packages provides a better way. 85 var wordSize int64 = 8 86 switch build.Default.GOARCH { 87 case "386", "arm": 88 wordSize = 4 89 } 90 sizes := &types.StdSizes{ 91 MaxAlign: 8, 92 WordSize: wordSize, 93 } 94 95 var interpMode interp.Mode 96 for _, c := range *interpFlag { 97 switch c { 98 case 'T': 99 interpMode |= interp.EnableTracing 100 case 'R': 101 interpMode |= interp.DisableRecover 102 default: 103 return fmt.Errorf("unknown -interp option: '%c'", c) 104 } 105 } 106 107 // Profiling support. 108 if *cpuprofile != "" { 109 f, err := os.Create(*cpuprofile) 110 if err != nil { 111 fmt.Fprintln(os.Stderr, err) 112 os.Exit(1) 113 } 114 pprof.StartCPUProfile(f) 115 defer pprof.StopCPUProfile() 116 } 117 118 // Load, parse and type-check the initial packages, 119 // and, if -run, their dependencies. 120 if *runFlag { 121 cfg.Mode = packages.LoadAllSyntax 122 } 123 initial, err := packages.Load(cfg, flag.Args()...) 124 if err != nil { 125 return err 126 } 127 if len(initial) == 0 { 128 return fmt.Errorf("no packages") 129 } 130 if packages.PrintErrors(initial) > 0 { 131 return fmt.Errorf("packages contain errors") 132 } 133 134 // Turn on instantiating generics during build if the program will be run. 135 if *runFlag { 136 mode |= ssa.InstantiateGenerics 137 } 138 139 // Create SSA-form program representation. 140 prog, pkgs := ssautil.AllPackages(initial, mode) 141 142 for i, p := range pkgs { 143 if p == nil { 144 return fmt.Errorf("cannot build SSA for package %s", initial[i]) 145 } 146 } 147 148 if !*runFlag { 149 // Build and display only the initial packages 150 // (and synthetic wrappers). 151 for _, p := range pkgs { 152 p.Build() 153 } 154 155 } else { 156 // Run the interpreter. 157 // Build SSA for all packages. 158 prog.Build() 159 160 // Earlier versions of the interpreter needed the runtime 161 // package; however, interp cannot handle unsafe constructs 162 // used during runtime's package initialization at the moment. 163 // The key construct blocking support is: 164 // *((*T)(unsafe.Pointer(p))) 165 // Unfortunately, this means only trivial programs can be 166 // interpreted by ssadump. 167 if prog.ImportedPackage("runtime") != nil { 168 return fmt.Errorf("-run: program depends on runtime package (interpreter can run only trivial programs)") 169 } 170 171 if runtime.GOARCH != build.Default.GOARCH { 172 return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)", 173 build.Default.GOARCH, runtime.GOARCH) 174 } 175 176 // Run first main package. 177 for _, main := range ssautil.MainPackages(pkgs) { 178 fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path()) 179 os.Exit(interp.Interpret(main, interpMode, sizes, main.Pkg.Path(), args)) 180 } 181 return fmt.Errorf("no main package") 182 } 183 return nil 184 } 185 186 // stringListValue is a flag.Value that accumulates strings. 187 // e.g. --flag=one --flag=two would produce []string{"one", "two"}. 188 type stringListValue []string 189 190 func newStringListValue(val []string, p *[]string) *stringListValue { 191 *p = val 192 return (*stringListValue)(p) 193 } 194 195 func (ss *stringListValue) Get() interface{} { return []string(*ss) } 196 197 func (ss *stringListValue) String() string { return fmt.Sprintf("%q", *ss) } 198 199 func (ss *stringListValue) Set(s string) error { *ss = append(*ss, s); return nil }