mirror of
https://github.com/golang/go
synced 2024-11-19 03:14:42 -07:00
99b2441d95
If a -pos argument is specified, a 'callgraph' query reports only the functions within the query package. This produces a far more manageable amount of information, and because we don't need to package-qualify the names, the result is easier to read. Added tests: - callgraph query with/without -pos (The test driver was extended to allow "nopos" queries.) - callers and callees queries don't return wrappers Also, in go/callgraph: - (*Node).String, (*Edge).String - (*Graph).DeleteSyntheticNodes eliminates synthetic wrapper functions, preserving topology. Used in all four oracle "call*" queries. - (*Graph).DeleteNode LGTM=crawshaw R=crawshaw CC=golang-codereviews https://golang.org/cl/66240044
153 lines
4.1 KiB
Go
153 lines
4.1 KiB
Go
// 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
|
|
}
|