Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / cmd / callgraph / main.go
1 // Copyright 2014 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 // callgraph: a tool for reporting the call graph of a Go program.
6 // See Usage for details, or run with -help.
7 package main // import "golang.org/x/tools/cmd/callgraph"
8
9 // TODO(adonovan):
10 //
11 // Features:
12 // - restrict graph to a single package
13 // - output
14 //   - functions reachable from root (use digraph tool?)
15 //   - unreachable functions (use digraph tool?)
16 //   - dynamic (runtime) types
17 //   - indexed output (numbered nodes)
18 //   - JSON output
19 //   - additional template fields:
20 //     callee file/line/col
21
22 import (
23         "bufio"
24         "bytes"
25         "flag"
26         "fmt"
27         "go/build"
28         "go/token"
29         "io"
30         "log"
31         "os"
32         "runtime"
33         "text/template"
34
35         "golang.org/x/tools/go/buildutil"
36         "golang.org/x/tools/go/callgraph"
37         "golang.org/x/tools/go/callgraph/cha"
38         "golang.org/x/tools/go/callgraph/rta"
39         "golang.org/x/tools/go/callgraph/static"
40         "golang.org/x/tools/go/packages"
41         "golang.org/x/tools/go/pointer"
42         "golang.org/x/tools/go/ssa"
43         "golang.org/x/tools/go/ssa/ssautil"
44 )
45
46 // flags
47 var (
48         algoFlag = flag.String("algo", "rta",
49                 `Call graph construction algorithm (static, cha, rta, pta)`)
50
51         testFlag = flag.Bool("test", false,
52                 "Loads test code (*_test.go) for imported packages")
53
54         formatFlag = flag.String("format",
55                 "{{.Caller}}\t--{{.Dynamic}}-{{.Line}}:{{.Column}}-->\t{{.Callee}}",
56                 "A template expression specifying how to format an edge")
57
58         ptalogFlag = flag.String("ptalog", "",
59                 "Location of the points-to analysis log file, or empty to disable logging.")
60 )
61
62 func init() {
63         flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
64 }
65
66 const Usage = `callgraph: display the call graph of a Go program.
67
68 Usage:
69
70   callgraph [-algo=static|cha|rta|pta] [-test] [-format=...] package...
71
72 Flags:
73
74 -algo      Specifies the call-graph construction algorithm, one of:
75
76             static      static calls only (unsound)
77             cha         Class Hierarchy Analysis
78             rta         Rapid Type Analysis
79             pta         inclusion-based Points-To Analysis
80
81            The algorithms are ordered by increasing precision in their
82            treatment of dynamic calls (and thus also computational cost).
83            RTA and PTA require a whole program (main or test), and
84            include only functions reachable from main.
85
86 -test      Include the package's tests in the analysis.
87
88 -format    Specifies the format in which each call graph edge is displayed.
89            One of:
90
91             digraph     output suitable for input to
92                         golang.org/x/tools/cmd/digraph.
93             graphviz    output in AT&T GraphViz (.dot) format.
94
95            All other values are interpreted using text/template syntax.
96            The default value is:
97
98             {{.Caller}}\t--{{.Dynamic}}-{{.Line}}:{{.Column}}-->\t{{.Callee}}
99
100            The structure passed to the template is (effectively):
101
102                    type Edge struct {
103                            Caller      *ssa.Function // calling function
104                            Callee      *ssa.Function // called function
105
106                            // Call site:
107                            Filename    string // containing file
108                            Offset      int    // offset within file of '('
109                            Line        int    // line number
110                            Column      int    // column number of call
111                            Dynamic     string // "static" or "dynamic"
112                            Description string // e.g. "static method call"
113                    }
114
115            Caller and Callee are *ssa.Function values, which print as
116            "(*sync/atomic.Mutex).Lock", but other attributes may be
117            derived from them, e.g. Caller.Pkg.Pkg.Path yields the
118            import path of the enclosing package.  Consult the go/ssa
119            API documentation for details.
120
121 Examples:
122
123   Show the call graph of the trivial web server application:
124
125     callgraph -format digraph $GOROOT/src/net/http/triv.go
126
127   Same, but show only the packages of each function:
128
129     callgraph -format '{{.Caller.Pkg.Pkg.Path}} -> {{.Callee.Pkg.Pkg.Path}}' \
130       $GOROOT/src/net/http/triv.go | sort | uniq
131
132   Show functions that make dynamic calls into the 'fmt' test package,
133   using the pointer analysis algorithm:
134
135     callgraph -format='{{.Caller}} -{{.Dynamic}}-> {{.Callee}}' -test -algo=pta fmt |
136       sed -ne 's/-dynamic-/--/p' |
137       sed -ne 's/-->.*fmt_test.*$//p' | sort | uniq
138
139   Show all functions directly called by the callgraph tool's main function:
140
141     callgraph -format=digraph golang.org/x/tools/cmd/callgraph |
142       digraph succs golang.org/x/tools/cmd/callgraph.main
143 `
144
145 func init() {
146         // If $GOMAXPROCS isn't set, use the full capacity of the machine.
147         // For small machines, use at least 4 threads.
148         if os.Getenv("GOMAXPROCS") == "" {
149                 n := runtime.NumCPU()
150                 if n < 4 {
151                         n = 4
152                 }
153                 runtime.GOMAXPROCS(n)
154         }
155 }
156
157 func main() {
158         flag.Parse()
159         if err := doCallgraph("", "", *algoFlag, *formatFlag, *testFlag, flag.Args()); err != nil {
160                 fmt.Fprintf(os.Stderr, "callgraph: %s\n", err)
161                 os.Exit(1)
162         }
163 }
164
165 var stdout io.Writer = os.Stdout
166
167 func doCallgraph(dir, gopath, algo, format string, tests bool, args []string) error {
168         if len(args) == 0 {
169                 fmt.Fprintln(os.Stderr, Usage)
170                 return nil
171         }
172
173         cfg := &packages.Config{
174                 Mode:  packages.LoadAllSyntax,
175                 Tests: tests,
176                 Dir:   dir,
177         }
178         if gopath != "" {
179                 cfg.Env = append(os.Environ(), "GOPATH="+gopath) // to enable testing
180         }
181         initial, err := packages.Load(cfg, args...)
182         if err != nil {
183                 return err
184         }
185         if packages.PrintErrors(initial) > 0 {
186                 return fmt.Errorf("packages contain errors")
187         }
188
189         // Create and build SSA-form program representation.
190         prog, pkgs := ssautil.AllPackages(initial, 0)
191         prog.Build()
192
193         // -- call graph construction ------------------------------------------
194
195         var cg *callgraph.Graph
196
197         switch algo {
198         case "static":
199                 cg = static.CallGraph(prog)
200
201         case "cha":
202                 cg = cha.CallGraph(prog)
203
204         case "pta":
205                 // Set up points-to analysis log file.
206                 var ptalog io.Writer
207                 if *ptalogFlag != "" {
208                         if f, err := os.Create(*ptalogFlag); err != nil {
209                                 log.Fatalf("Failed to create PTA log file: %s", err)
210                         } else {
211                                 buf := bufio.NewWriter(f)
212                                 ptalog = buf
213                                 defer func() {
214                                         if err := buf.Flush(); err != nil {
215                                                 log.Printf("flush: %s", err)
216                                         }
217                                         if err := f.Close(); err != nil {
218                                                 log.Printf("close: %s", err)
219                                         }
220                                 }()
221                         }
222                 }
223
224                 mains, err := mainPackages(pkgs)
225                 if err != nil {
226                         return err
227                 }
228                 config := &pointer.Config{
229                         Mains:          mains,
230                         BuildCallGraph: true,
231                         Log:            ptalog,
232                 }
233                 ptares, err := pointer.Analyze(config)
234                 if err != nil {
235                         return err // internal error in pointer analysis
236                 }
237                 cg = ptares.CallGraph
238
239         case "rta":
240                 mains, err := mainPackages(pkgs)
241                 if err != nil {
242                         return err
243                 }
244                 var roots []*ssa.Function
245                 for _, main := range mains {
246                         roots = append(roots, main.Func("init"), main.Func("main"))
247                 }
248                 rtares := rta.Analyze(roots, true)
249                 cg = rtares.CallGraph
250
251                 // NB: RTA gives us Reachable and RuntimeTypes too.
252
253         default:
254                 return fmt.Errorf("unknown algorithm: %s", algo)
255         }
256
257         cg.DeleteSyntheticNodes()
258
259         // -- output------------------------------------------------------------
260
261         var before, after string
262
263         // Pre-canned formats.
264         switch format {
265         case "digraph":
266                 format = `{{printf "%q %q" .Caller .Callee}}`
267
268         case "graphviz":
269                 before = "digraph callgraph {\n"
270                 after = "}\n"
271                 format = `  {{printf "%q" .Caller}} -> {{printf "%q" .Callee}}`
272         }
273
274         tmpl, err := template.New("-format").Parse(format)
275         if err != nil {
276                 return fmt.Errorf("invalid -format template: %v", err)
277         }
278
279         // Allocate these once, outside the traversal.
280         var buf bytes.Buffer
281         data := Edge{fset: prog.Fset}
282
283         fmt.Fprint(stdout, before)
284         if err := callgraph.GraphVisitEdges(cg, func(edge *callgraph.Edge) error {
285                 data.position.Offset = -1
286                 data.edge = edge
287                 data.Caller = edge.Caller.Func
288                 data.Callee = edge.Callee.Func
289
290                 buf.Reset()
291                 if err := tmpl.Execute(&buf, &data); err != nil {
292                         return err
293                 }
294                 stdout.Write(buf.Bytes())
295                 if len := buf.Len(); len == 0 || buf.Bytes()[len-1] != '\n' {
296                         fmt.Fprintln(stdout)
297                 }
298                 return nil
299         }); err != nil {
300                 return err
301         }
302         fmt.Fprint(stdout, after)
303         return nil
304 }
305
306 // mainPackages returns the main packages to analyze.
307 // Each resulting package is named "main" and has a main function.
308 func mainPackages(pkgs []*ssa.Package) ([]*ssa.Package, error) {
309         var mains []*ssa.Package
310         for _, p := range pkgs {
311                 if p != nil && p.Pkg.Name() == "main" && p.Func("main") != nil {
312                         mains = append(mains, p)
313                 }
314         }
315         if len(mains) == 0 {
316                 return nil, fmt.Errorf("no main packages")
317         }
318         return mains, nil
319 }
320
321 type Edge struct {
322         Caller *ssa.Function
323         Callee *ssa.Function
324
325         edge     *callgraph.Edge
326         fset     *token.FileSet
327         position token.Position // initialized lazily
328 }
329
330 func (e *Edge) pos() *token.Position {
331         if e.position.Offset == -1 {
332                 e.position = e.fset.Position(e.edge.Pos()) // called lazily
333         }
334         return &e.position
335 }
336
337 func (e *Edge) Filename() string { return e.pos().Filename }
338 func (e *Edge) Column() int      { return e.pos().Column }
339 func (e *Edge) Line() int        { return e.pos().Line }
340 func (e *Edge) Offset() int      { return e.pos().Offset }
341
342 func (e *Edge) Dynamic() string {
343         if e.edge.Site != nil && e.edge.Site.Common().StaticCallee() == nil {
344                 return "dynamic"
345         }
346         return "static"
347 }
348
349 func (e *Edge) Description() string { return e.edge.Description() }