95 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			95 lines
		
	
	
		
			2.5 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/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
 | |
| }
 |