103 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			103 lines
		
	
	
		
			2.8 KiB
		
	
	
	
		
			Go
		
	
	
	
// 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
 | 
						|
}
 |