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:
parent
e7a2e079b0
commit
775fb1976b
@ -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()
|
||||||
|
|
||||||
|
@ -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",
|
||||||
|
9
oracle/testdata/src/main/callgraph.golden
vendored
9
oracle/testdata/src/main/callgraph.golden
vendored
@ -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
|
|
||||||
|
|
||||||
|
2
oracle/testdata/src/main/callgraph2.golden
vendored
2
oracle/testdata/src/main/callgraph2.golden
vendored
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user