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