// 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/call" "code.google.com/p/go.tools/oracle/serial" "code.google.com/p/go.tools/ssa" ) // 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. `) 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[call.GraphNode]bool) var visit func(caller call.GraphNode) visit = func(caller call.GraphNode) { 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[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 }