github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/oracle/callgraph.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 package oracle 6 7 import ( 8 "fmt" 9 "go/token" 10 "sort" 11 12 "golang.org/x/tools/go/callgraph" 13 "golang.org/x/tools/go/ssa" 14 "golang.org/x/tools/go/types" 15 "golang.org/x/tools/oracle/serial" 16 ) 17 18 // doCallgraph displays the entire callgraph of the current program, 19 // or if a query -pos was provided, the query package. 20 func doCallgraph(o *Oracle, qpos *QueryPos) (queryResult, error) { 21 buildSSA(o) 22 23 // Run the pointer analysis and build the callgraph. 24 o.ptaConfig.BuildCallGraph = true 25 cg := ptrAnalysis(o).CallGraph 26 cg.DeleteSyntheticNodes() 27 28 var qpkg *types.Package 29 var isQueryPkg func(fn *ssa.Function) bool 30 var keep, remove, roots []*callgraph.Node 31 if qpos == nil { 32 // No -pos provided: show complete callgraph. 33 roots = append(roots, cg.Root) 34 isQueryPkg = func(fn *ssa.Function) bool { return true } 35 } else { 36 // A query -pos was provided: restrict result to 37 // functions belonging to the query package. 38 qpkg = qpos.info.Pkg 39 isQueryPkg = func(fn *ssa.Function) bool { 40 return fn.Pkg != nil && fn.Pkg.Object == qpkg 41 } 42 } 43 44 // First compute the nodes to keep and remove. 45 for fn, cgn := range cg.Nodes { 46 if isQueryPkg(fn) { 47 keep = append(keep, cgn) 48 } else { 49 remove = append(remove, cgn) 50 } 51 } 52 53 // Compact the Node.ID sequence of the kept nodes, 54 // preserving the original order. 55 sort.Sort(nodesByID(keep)) 56 for i, cgn := range keep { 57 cgn.ID = i 58 } 59 60 // Compute the set of roots: 61 // in-package nodes with out-of-package callers. 62 // For determinism, roots are ordered by original Node.ID. 63 for _, cgn := range keep { 64 for _, e := range cgn.In { 65 if !isQueryPkg(e.Caller.Func) { 66 roots = append(roots, cgn) 67 break 68 } 69 } 70 } 71 72 // Finally, discard all out-of-package nodes. 73 for _, cgn := range remove { 74 cg.DeleteNode(cgn) 75 } 76 77 return &callgraphResult{qpkg, cg.Nodes, roots}, nil 78 } 79 80 type callgraphResult struct { 81 qpkg *types.Package 82 nodes map[*ssa.Function]*callgraph.Node 83 roots []*callgraph.Node 84 } 85 86 func (r *callgraphResult) display(printf printfFunc) { 87 descr := "the entire program" 88 if r.qpkg != nil { 89 descr = fmt.Sprintf("package %s", r.qpkg.Path()) 90 } 91 92 printf(nil, ` 93 Below is a call graph of %s. 94 The numbered nodes form a spanning tree. 95 Non-numbered nodes indicate back- or cross-edges to the node whose 96 number follows in parentheses. 97 `, descr) 98 99 printed := make(map[*callgraph.Node]int) 100 var print func(caller *callgraph.Node, indent int) 101 print = func(caller *callgraph.Node, indent int) { 102 if num, ok := printed[caller]; !ok { 103 num = len(printed) 104 printed[caller] = num 105 106 // Sort the children into name order for deterministic* output. 107 // (*mostly: anon funcs' names are not globally unique.) 108 var funcs funcsByName 109 for callee := range callgraph.CalleesOf(caller) { 110 funcs = append(funcs, callee.Func) 111 } 112 sort.Sort(funcs) 113 114 printf(caller.Func, "%d\t%*s%s", num, 4*indent, "", caller.Func.RelString(r.qpkg)) 115 for _, callee := range funcs { 116 print(r.nodes[callee], indent+1) 117 } 118 } else { 119 printf(caller.Func, "\t%*s%s (%d)", 4*indent, "", caller.Func.RelString(r.qpkg), num) 120 } 121 } 122 for _, root := range r.roots { 123 print(root, 0) 124 } 125 } 126 127 type nodesByID []*callgraph.Node 128 129 func (s nodesByID) Len() int { return len(s) } 130 func (s nodesByID) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 131 func (s nodesByID) Less(i, j int) bool { return s[i].ID < s[j].ID } 132 133 type funcsByName []*ssa.Function 134 135 func (s funcsByName) Len() int { return len(s) } 136 func (s funcsByName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 137 func (s funcsByName) Less(i, j int) bool { return s[i].String() < s[j].String() } 138 139 func (r *callgraphResult) toSerial(res *serial.Result, fset *token.FileSet) { 140 cg := make([]serial.CallGraph, len(r.nodes)) 141 for _, n := range r.nodes { 142 j := &cg[n.ID] 143 fn := n.Func 144 j.Name = fn.String() 145 j.Pos = fset.Position(fn.Pos()).String() 146 for callee := range callgraph.CalleesOf(n) { 147 j.Children = append(j.Children, callee.ID) 148 } 149 sort.Ints(j.Children) 150 } 151 res.Callgraph = cg 152 }