| // 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/oracle/json" |
| "code.google.com/p/go.tools/pointer" |
| ) |
| |
| // 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): elide nodes for synthetic functions? |
| // |
| func callgraph(o *oracle) (queryResult, error) { |
| buildSSA(o) |
| |
| // Run the pointer analysis and build the complete callgraph. |
| callgraph := make(pointer.CallGraph) |
| o.config.Call = callgraph.AddEdge |
| root := ptrAnalysis(o) |
| |
| // Assign (preorder) numbers to all the callgraph nodes. |
| // TODO(adonovan): the callgraph API should do this for us. |
| numbering := make(map[pointer.CallGraphNode]int) |
| var number func(cgn pointer.CallGraphNode) |
| number = func(cgn pointer.CallGraphNode) { |
| if _, ok := numbering[cgn]; !ok { |
| numbering[cgn] = len(numbering) |
| for callee := range callgraph[cgn] { |
| number(callee) |
| } |
| } |
| } |
| number(root) |
| |
| return &callgraphResult{ |
| root: root, |
| callgraph: callgraph, |
| numbering: numbering, |
| }, nil |
| } |
| |
| type callgraphResult struct { |
| root pointer.CallGraphNode |
| callgraph pointer.CallGraph |
| numbering map[pointer.CallGraphNode]int |
| } |
| |
| 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[pointer.CallGraphNode]bool) |
| var print func(cgn pointer.CallGraphNode, indent int) |
| print = func(cgn pointer.CallGraphNode, indent int) { |
| n := r.numbering[cgn] |
| if !seen[cgn] { |
| seen[cgn] = true |
| printf(cgn.Func(), "%d\t%s%s", n, strings.Repeat(" ", indent), cgn.Func()) |
| for callee := range r.callgraph[cgn] { |
| print(callee, indent+1) |
| } |
| } else { |
| printf(cgn.Func(), "\t%s%s (%d)", strings.Repeat(" ", indent), cgn.Func(), n) |
| } |
| } |
| print(r.root, 0) |
| } |
| |
| func (r *callgraphResult) toJSON(res *json.Result, fset *token.FileSet) { |
| cg := make([]json.CallGraph, len(r.numbering)) |
| for n, i := range r.numbering { |
| j := &cg[i] |
| fn := n.Func() |
| j.Name = fn.String() |
| j.Pos = fset.Position(fn.Pos()).String() |
| for callee := range r.callgraph[n] { |
| j.Children = append(j.Children, r.numbering[callee]) |
| } |
| } |
| res.Callgraph = cg |
| } |