github.com/april1989/origin-go-tools@v0.0.32/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 "github.com/april1989/origin-go-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 "github.com/april1989/origin-go-tools/go/buildutil" 18 "github.com/april1989/origin-go-tools/go/packages" 19 "github.com/april1989/origin-go-tools/go/ssa" 20 "github.com/april1989/origin-go-tools/go/ssa/interp" 21 "github.com/april1989/origin-go-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=[DBCSNFL]] [-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 run the first package named main. 59 60 Interpretation of the standard "golibexec_testing" package is no longer supported. 61 ` 62 63 func main() { 64 if err := doMain(); err != nil { 65 fmt.Fprintf(os.Stderr, "ssadump: %s\n", err) 66 os.Exit(1) 67 } 68 } 69 70 func doMain() error { 71 flag.Parse() 72 if len(flag.Args()) == 0 { 73 fmt.Fprint(os.Stderr, usage) 74 os.Exit(1) 75 } 76 77 cfg := &packages.Config{ 78 Mode: packages.LoadSyntax, 79 Tests: *testFlag, 80 } 81 82 // Choose types.Sizes from conf.Build. 83 // TODO(adonovan): remove this when go/packages provides a better way. 84 var wordSize int64 = 8 85 switch build.Default.GOARCH { 86 case "386", "arm": 87 wordSize = 4 88 } 89 sizes := &types.StdSizes{ 90 MaxAlign: 8, 91 WordSize: wordSize, 92 } 93 94 var interpMode interp.Mode 95 for _, c := range *interpFlag { 96 switch c { 97 case 'T': 98 interpMode |= interp.EnableTracing 99 case 'R': 100 interpMode |= interp.DisableRecover 101 default: 102 return fmt.Errorf("unknown -interp option: '%c'", c) 103 } 104 } 105 106 // Profiling support. 107 if *cpuprofile != "" { 108 f, err := os.Create(*cpuprofile) 109 if err != nil { 110 fmt.Fprintln(os.Stderr, err) 111 os.Exit(1) 112 } 113 pprof.StartCPUProfile(f) 114 defer pprof.StopCPUProfile() 115 } 116 117 // Load, parse and type-check the initial packages, 118 // and, if -run, their dependencies. 119 if *runFlag { 120 cfg.Mode = packages.LoadAllSyntax 121 } 122 initial, err := packages.Load(cfg, flag.Args()...) 123 if err != nil { 124 return err 125 } 126 if len(initial) == 0 { 127 return fmt.Errorf("no packages") 128 } 129 if packages.PrintErrors(initial) > 0 { 130 return fmt.Errorf("packages contain errors") 131 } 132 133 // Create SSA-form program representation. 134 prog, pkgs := ssautil.AllPackages(initial, mode) 135 136 for i, p := range pkgs { 137 if p == nil { 138 return fmt.Errorf("cannot build SSA for package %s", initial[i]) 139 } 140 } 141 142 if !*runFlag { 143 // Build and display only the initial packages 144 // (and synthetic wrappers). 145 for _, p := range pkgs { 146 p.Build() 147 } 148 149 } else { 150 // Run the interpreter. 151 // Build SSA for all packages. 152 prog.Build() 153 154 // The interpreter needs the runtime package. 155 // It is a limitation of go/packages that 156 // we cannot add "runtime" to its initial set, 157 // we can only check that it is present. 158 if prog.ImportedPackage("runtime") == nil { 159 return fmt.Errorf("-run: program does not depend on runtime") 160 } 161 162 if runtime.GOARCH != build.Default.GOARCH { 163 return fmt.Errorf("cross-interpretation is not supported (target has GOARCH %s, interpreter has %s)", 164 build.Default.GOARCH, runtime.GOARCH) 165 } 166 167 // Run first main package. 168 for _, main := range ssautil.MainPackages(pkgs) { 169 fmt.Fprintf(os.Stderr, "Running: %s\n", main.Pkg.Path()) 170 os.Exit(interp.Interpret(main, interpMode, sizes, main.Pkg.Path(), args)) 171 } 172 return fmt.Errorf("no main package") 173 } 174 return nil 175 } 176 177 // stringListValue is a flag.Value that accumulates strings. 178 // e.g. --flag=one --flag=two would produce []string{"one", "two"}. 179 type stringListValue []string 180 181 func newStringListValue(val []string, p *[]string) *stringListValue { 182 *p = val 183 return (*stringListValue)(p) 184 } 185 186 func (ss *stringListValue) Get() interface{} { return []string(*ss) } 187 188 func (ss *stringListValue) String() string { return fmt.Sprintf("%q", *ss) } 189 190 func (ss *stringListValue) Set(s string) error { *ss = append(*ss, s); return nil }