github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/cmd/callgraph/main.go (about) 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/loader" 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 the call graph of a Go program. 67 68 Usage: 69 70 callgraph [-algo=static|cha|rta|pta] [-test] [-format=...] <args>... 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.Object.Path yields the 118 import path of the enclosing package. Consult the go/ssa 119 API documentation for details. 120 121 ` + loader.FromArgsUsage + ` 122 123 Examples: 124 125 Show the call graph of the trivial web server application: 126 127 callgraph -format digraph $GOROOT/src/net/http/triv.go 128 129 Same, but show only the packages of each function: 130 131 callgraph -format '{{.Caller.Pkg.Object.Path}} -> {{.Callee.Pkg.Object.Path}}' \ 132 $GOROOT/src/net/http/triv.go | sort | uniq 133 134 Show functions that make dynamic calls into the 'fmt' test package, 135 using the pointer analysis algorithm: 136 137 callgraph -format='{{.Caller}} -{{.Dynamic}}-> {{.Callee}}' -test -algo=pta fmt | 138 sed -ne 's/-dynamic-/--/p' | 139 sed -ne 's/-->.*fmt_test.*$//p' | sort | uniq 140 141 Show all functions directly called by the callgraph tool's main function: 142 143 callgraph -format=digraph golang.org/x/tools/cmd/callgraph | 144 digraph succs golang.org/x/tools/cmd/callgraph.main 145 ` 146 147 func init() { 148 // If $GOMAXPROCS isn't set, use the full capacity of the machine. 149 // For small machines, use at least 4 threads. 150 if os.Getenv("GOMAXPROCS") == "" { 151 n := runtime.NumCPU() 152 if n < 4 { 153 n = 4 154 } 155 runtime.GOMAXPROCS(n) 156 } 157 } 158 159 func main() { 160 flag.Parse() 161 if err := doCallgraph(&build.Default, *algoFlag, *formatFlag, *testFlag, flag.Args()); err != nil { 162 fmt.Fprintf(os.Stderr, "callgraph: %s\n", err) 163 os.Exit(1) 164 } 165 } 166 167 var stdout io.Writer = os.Stdout 168 169 func doCallgraph(ctxt *build.Context, algo, format string, tests bool, args []string) error { 170 conf := loader.Config{Build: ctxt} 171 172 if len(args) == 0 { 173 fmt.Fprintln(os.Stderr, Usage) 174 return nil 175 } 176 177 // Use the initial packages from the command line. 178 args, err := conf.FromArgs(args, tests) 179 if err != nil { 180 return err 181 } 182 183 // Load, parse and type-check the whole program. 184 iprog, err := conf.Load() 185 if err != nil { 186 return err 187 } 188 189 // Create and build SSA-form program representation. 190 prog := ssautil.CreateProgram(iprog, 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 main, err := mainPackage(prog, tests) 225 if err != nil { 226 return err 227 } 228 config := &pointer.Config{ 229 Mains: []*ssa.Package{main}, 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 main, err := mainPackage(prog, tests) 241 if err != nil { 242 return err 243 } 244 roots := []*ssa.Function{ 245 main.Func("init"), 246 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 // mainPackage returns the main package to analyze. 307 // The resulting package has a main() function. 308 func mainPackage(prog *ssa.Program, tests bool) (*ssa.Package, error) { 309 pkgs := prog.AllPackages() 310 311 // TODO(adonovan): allow independent control over tests, mains and libraries. 312 // TODO(adonovan): put this logic in a library; we keep reinventing it. 313 314 if tests { 315 // If -test, use all packages' tests. 316 if len(pkgs) > 0 { 317 if main := prog.CreateTestMainPackage(pkgs...); main != nil { 318 return main, nil 319 } 320 } 321 return nil, fmt.Errorf("no tests") 322 } 323 324 // Otherwise, use the first package named main. 325 for _, pkg := range pkgs { 326 if pkg.Pkg.Name() == "main" { 327 if pkg.Func("main") == nil { 328 return nil, fmt.Errorf("no func main() in main package") 329 } 330 return pkg, nil 331 } 332 } 333 334 return nil, fmt.Errorf("no main package") 335 } 336 337 type Edge struct { 338 Caller *ssa.Function 339 Callee *ssa.Function 340 341 edge *callgraph.Edge 342 fset *token.FileSet 343 position token.Position // initialized lazily 344 } 345 346 func (e *Edge) pos() *token.Position { 347 if e.position.Offset == -1 { 348 e.position = e.fset.Position(e.edge.Pos()) // called lazily 349 } 350 return &e.position 351 } 352 353 func (e *Edge) Filename() string { return e.pos().Filename } 354 func (e *Edge) Column() int { return e.pos().Column } 355 func (e *Edge) Line() int { return e.pos().Line } 356 func (e *Edge) Offset() int { return e.pos().Offset } 357 358 func (e *Edge) Dynamic() string { 359 if e.edge.Site != nil && e.edge.Site.Common().StaticCallee() == nil { 360 return "dynamic" 361 } 362 return "static" 363 } 364 365 func (e *Edge) Description() string { return e.edge.Description() }