1
0
mirror of https://github.com/golang/go synced 2024-10-01 10:28:31 -06:00
go/oracle/callgraph.go
Alan Donovan b856247075 go.tools/call: rename package to go/callgraph
Was:		Now:
call.Graph	callgraph.Graph
call.GraphNode	callgraph.Node
call.Edge	callgraph.Edge

Though call.Graph was cute, the original naming was a mistake:
'call' is too useful a var name to waste on a package.

R=gri, crawshaw
CC=golang-codereviews
https://golang.org/cl/53190043
2014-01-16 14:04:19 -05:00

134 lines
3.5 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 (
"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/oracle/serial"
)
// doCallgraph 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): add an option to partition edges by call site.
//
// TODO(adonovan): elide nodes for synthetic functions?
//
func doCallgraph(o *Oracle, _ *QueryPos) (queryResult, error) {
buildSSA(o)
// Run the pointer analysis and build the complete callgraph.
o.ptaConfig.BuildCallGraph = true
ptares := ptrAnalysis(o)
return &callgraphResult{
callgraph: ptares.CallGraph,
}, nil
}
type callgraphResult struct {
callgraph callgraph.Graph
}
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.
`)
root := r.callgraph.Root()
// context-insensitive (CI) call graph.
ci := make(map[*ssa.Function]map[*ssa.Function]bool)
// 1. Visit the CS call graph and build the CI call graph.
visited := make(map[callgraph.Node]bool)
var visit func(caller callgraph.Node)
visit = func(caller callgraph.Node) {
if !visited[caller] {
visited[caller] = true
cicallees := ci[caller.Func()]
if cicallees == nil {
cicallees = make(map[*ssa.Function]bool)
ci[caller.Func()] = cicallees
}
for _, e := range caller.Edges() {
cicallees[e.Callee.Func()] = true
visit(e.Callee)
}
}
}
visit(root)
// 2. Print the CI callgraph.
printed := make(map[*ssa.Function]int)
var print func(caller *ssa.Function, indent int)
print = func(caller *ssa.Function, 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 ci[caller] {
funcs = append(funcs, callee)
}
sort.Sort(funcs)
printf(caller, "%d\t%*s%s", num, 4*indent, "", caller)
for _, callee := range funcs {
print(callee, indent+1)
}
} else {
printf(caller, "\t%*s%s (%d)", 4*indent, "", caller, num)
}
}
print(root.Func(), 0)
}
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) {
nodes := r.callgraph.Nodes()
numbering := make(map[callgraph.Node]int)
for i, n := range nodes {
numbering[n] = i
}
cg := make([]serial.CallGraph, len(nodes))
for i, n := range nodes {
j := &cg[i]
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, numbering[callee])
}
sort.Ints(j.Children)
}
res.Callgraph = cg
}