You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
4.4 KiB

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