// 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 }