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