1
0
mirror of https://github.com/golang/go synced 2024-10-01 16:38:34 -06:00
go/oracle/callgraph.go
Alan Donovan 785cfaa938 go.tools/pointer: use new callgraph API.
Also: pointer.Analyze now returns a pointer.Result object,
containing the callgraph and the results of ssa.Value queries.

The oracle has been updated to use the new call and pointer APIs.

R=crawshaw, gri
CC=golang-dev
https://golang.org/cl/13915043
2013-09-25 17:17:42 -04:00

95 lines
2.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"
"strings"
"code.google.com/p/go.tools/call"
"code.google.com/p/go.tools/oracle/serial"
)
// 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): add an option to partition edges by call site.
//
// TODO(adonovan): elide nodes for synthetic functions?
//
func callgraph(o *Oracle, _ *QueryPos) (queryResult, error) {
buildSSA(o)
// Run the pointer analysis and build the complete callgraph.
o.config.BuildCallGraph = true
ptares := ptrAnalysis(o)
return &callgraphResult{
callgraph: ptares.CallGraph,
}, nil
}
type callgraphResult struct {
callgraph call.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.
Some nodes may appear multiple times due to context-sensitive
treatment of some calls.
`)
seen := make(map[call.GraphNode]int)
var print func(cgn call.GraphNode, indent int)
print = func(cgn call.GraphNode, indent int) {
fn := cgn.Func()
if num, ok := seen[cgn]; !ok {
num = len(seen)
seen[cgn] = num
printf(fn, "%d\t%s%s", num, strings.Repeat(" ", indent), fn)
// Don't use Edges(), which distinguishes callees by call site.
for callee := range call.CalleesOf(cgn) {
print(callee, indent+1)
}
} else {
printf(fn, "\t%s%s (%d)", strings.Repeat(" ", indent), fn, num)
}
}
print(r.callgraph.Root(), 0)
}
func (r *callgraphResult) toSerial(res *serial.Result, fset *token.FileSet) {
nodes := r.callgraph.Nodes()
numbering := make(map[call.GraphNode]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 call.CalleesOf(n) {
j.Children = append(j.Children, numbering[callee])
}
}
res.Callgraph = cg
}