blob: 6ebe1912d47520382967ce608aacde4af3120505 [file] [log] [blame]
// 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 (
"go/token"
"strings"
"code.google.com/p/go.tools/oracle/json"
"code.google.com/p/go.tools/pointer"
)
// callgraph displays the entire callgraph of the current program.
//
// Nodes may be seem to appear multiple times due to (limited)
// context sensitivity.
//
// TODO(adonovan): add options for restricting the display to a region
// of interest: function, package, subgraph, dirtree, goroutine, etc.
//
// TODO(adonovan): add an option to project away context sensitivity.
// The callgraph API should provide this feature.
//
// TODO(adonovan): elide nodes for synthetic functions?
//
func callgraph(o *oracle) (queryResult, error) {
buildSSA(o)
// Run the pointer analysis and build the complete callgraph.
callgraph := make(pointer.CallGraph)
o.config.Call = callgraph.AddEdge
root := ptrAnalysis(o)
// Assign (preorder) numbers to all the callgraph nodes.
// TODO(adonovan): the callgraph API should do this for us.
numbering := make(map[pointer.CallGraphNode]int)
var number func(cgn pointer.CallGraphNode)
number = func(cgn pointer.CallGraphNode) {
if _, ok := numbering[cgn]; !ok {
numbering[cgn] = len(numbering)
for callee := range callgraph[cgn] {
number(callee)
}
}
}
number(root)
return &callgraphResult{
root: root,
callgraph: callgraph,
numbering: numbering,
}, nil
}
type callgraphResult struct {
root pointer.CallGraphNode
callgraph pointer.CallGraph
numbering map[pointer.CallGraphNode]int
}
func (r *callgraphResult) display(printf printfFunc) {
printf(nil, `
Below is a call graph of the entire program.
The numbered nodes form a spanning tree.
Non-numbered nodes indicate back- or cross-edges to the node whose
number follows in parentheses.
Some nodes may appear multiple times due to context-sensitive
treatment of some calls.
`)
seen := make(map[pointer.CallGraphNode]bool)
var print func(cgn pointer.CallGraphNode, indent int)
print = func(cgn pointer.CallGraphNode, indent int) {
n := r.numbering[cgn]
if !seen[cgn] {
seen[cgn] = true
printf(cgn.Func(), "%d\t%s%s", n, strings.Repeat(" ", indent), cgn.Func())
for callee := range r.callgraph[cgn] {
print(callee, indent+1)
}
} else {
printf(cgn.Func(), "\t%s%s (%d)", strings.Repeat(" ", indent), cgn.Func(), n)
}
}
print(r.root, 0)
}
func (r *callgraphResult) toJSON(res *json.Result, fset *token.FileSet) {
cg := make([]json.CallGraph, len(r.numbering))
for n, i := range r.numbering {
j := &cg[i]
fn := n.Func()
j.Name = fn.String()
j.Pos = fset.Position(fn.Pos()).String()
for callee := range r.callgraph[n] {
j.Children = append(j.Children, r.numbering[callee])
}
}
res.Callgraph = cg
}