1
0
mirror of https://github.com/golang/go synced 2024-11-18 23:34:45 -07:00

go.tools/oracle: make callgraph printing deterministic (and context-insensitive)

+ re-enable test.

R=crawshaw
CC=golang-dev
https://golang.org/cl/25730044
This commit is contained in:
Alan Donovan 2013-11-13 09:11:10 -05:00
parent e7a2e079b0
commit 775fb1976b
4 changed files with 57 additions and 24 deletions

View File

@ -6,10 +6,11 @@ package oracle
import ( import (
"go/token" "go/token"
"strings" "sort"
"code.google.com/p/go.tools/call" "code.google.com/p/go.tools/call"
"code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/oracle/serial"
"code.google.com/p/go.tools/ssa"
) )
// callgraph displays the entire callgraph of the current program. // callgraph displays the entire callgraph of the current program.
@ -49,29 +50,66 @@ Below is a call graph of the entire program.
The numbered nodes form a spanning tree. The numbered nodes form a spanning tree.
Non-numbered nodes indicate back- or cross-edges to the node whose Non-numbered nodes indicate back- or cross-edges to the node whose
number follows in parentheses. number follows in parentheses.
Some nodes may appear multiple times due to context-sensitive
treatment of some calls.
`) `)
root := r.callgraph.Root()
seen := make(map[call.GraphNode]int) // context-insensitive (CI) call graph.
var print func(cgn call.GraphNode, indent int) ci := make(map[*ssa.Function]map[*ssa.Function]bool)
print = func(cgn call.GraphNode, indent int) {
fn := cgn.Func() // 1. Visit the CS call graph and build the CI call graph.
if num, ok := seen[cgn]; !ok { visited := make(map[call.GraphNode]bool)
num = len(seen) var visit func(caller call.GraphNode)
seen[cgn] = num visit = func(caller call.GraphNode) {
printf(fn, "%d\t%s%s", num, strings.Repeat(" ", indent), fn) if !visited[caller] {
// Don't use Edges(), which distinguishes callees by call site. visited[caller] = true
for callee := range call.CalleesOf(cgn) {
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) print(callee, indent+1)
} }
} else { } else {
printf(fn, "\t%s%s (%d)", strings.Repeat(" ", indent), fn, num) printf(caller, "\t%*s%s (%d)", 4*indent, "", caller, num)
} }
} }
print(r.callgraph.Root(), 0) 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) { func (r *callgraphResult) toSerial(res *serial.Result, fset *token.FileSet) {
nodes := r.callgraph.Nodes() nodes := r.callgraph.Nodes()

View File

@ -200,7 +200,7 @@ func TestOracle(t *testing.T) {
for _, filename := range []string{ for _, filename := range []string{
"testdata/src/main/calls.go", "testdata/src/main/calls.go",
"testdata/src/main/callgraph.go", "testdata/src/main/callgraph.go",
// "testdata/src/main/callgraph2.go", // TODO(adonovan): make printing deterministic "testdata/src/main/callgraph2.go",
"testdata/src/main/describe.go", "testdata/src/main/describe.go",
"testdata/src/main/freevars.go", "testdata/src/main/freevars.go",
"testdata/src/main/implements.go", "testdata/src/main/implements.go",

View File

@ -4,8 +4,6 @@ Below is a call graph of the entire program.
The numbered nodes form a spanning tree. The numbered nodes form a spanning tree.
Non-numbered nodes indicate back- or cross-edges to the node whose Non-numbered nodes indicate back- or cross-edges to the node whose
number follows in parentheses. number follows in parentheses.
Some nodes may appear multiple times due to context-sensitive
treatment of some calls.
0 <root> 0 <root>
1 main.init 1 main.init
@ -13,8 +11,7 @@ Some nodes may appear multiple times due to context-sensitive
3 main.call 3 main.call
4 main.A 4 main.A
5 main.B 5 main.B
6 main.nop 6 main.call2
7 main.nop 7 func@31.8
8 main.call2 8 main.nop
9 func@31.8

View File

@ -4,8 +4,6 @@ Below is a call graph of the entire program.
The numbered nodes form a spanning tree. The numbered nodes form a spanning tree.
Non-numbered nodes indicate back- or cross-edges to the node whose Non-numbered nodes indicate back- or cross-edges to the node whose
number follows in parentheses. number follows in parentheses.
Some nodes may appear multiple times due to context-sensitive
treatment of some calls.
0 <root> 0 <root>
1 main.init 1 main.init