| // 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 callgraph defines the call graph and various algorithms | |
| and utilities to operate on it. | |
| A call graph is a labelled directed graph whose nodes represent | |
| functions and whose edge labels represent syntactic function call | |
| sites. The presence of a labelled edge (caller, site, callee) | |
| indicates that caller may call callee at the specified call site. | |
| A call graph is a multigraph: it may contain multiple edges (caller, | |
| *, callee) connecting the same pair of nodes, so long as the edges | |
| differ by label; this occurs when one function calls another function | |
| from multiple call sites. Also, it may contain multiple edges | |
| (caller, site, *) that differ only by callee; this indicates a | |
| polymorphic call. | |
| A SOUND call graph is one that overapproximates the dynamic calling | |
| behaviors of the program in all possible executions. One call graph | |
| is more PRECISE than another if it is a smaller overapproximation of | |
| the dynamic behavior. | |
| All call graphs have a synthetic root node which is responsible for | |
| calling main() and init(). | |
| Calls to built-in functions (e.g. panic, println) are not represented | |
| in the call graph; they are treated like built-in operators of the | |
| language. | |
| */ | |
| package callgraph // import "golang.org/x/tools/go/callgraph" | |
| // TODO(zpavlinovic): decide how callgraphs handle calls to and from generic function bodies. | |
| import ( | |
| "fmt" | |
| "go/token" | |
| "golang.org/x/tools/go/ssa" | |
| ) | |
| // A Graph represents a call graph. | |
| // | |
| // A graph may contain nodes that are not reachable from the root. | |
| // If the call graph is sound, such nodes indicate unreachable | |
| // functions. | |
| type Graph struct { | |
| Root *Node // the distinguished root node (Root.Func may be nil) | |
| Nodes map[*ssa.Function]*Node // all nodes by function | |
| } | |
| // New returns a new Graph with the specified (optional) root node. | |
| func New(root *ssa.Function) *Graph { | |
| g := &Graph{Nodes: make(map[*ssa.Function]*Node)} | |
| g.Root = g.CreateNode(root) | |
| return g | |
| } | |
| // CreateNode returns the Node for fn, creating it if not present. | |
| // The root node may have fn=nil. | |
| func (g *Graph) CreateNode(fn *ssa.Function) *Node { | |
| n, ok := g.Nodes[fn] | |
| if !ok { | |
| n = &Node{Func: fn, ID: len(g.Nodes)} | |
| g.Nodes[fn] = n | |
| } | |
| return n | |
| } | |
| // A Node represents a node in a call graph. | |
| type Node struct { | |
| Func *ssa.Function // the function this node represents | |
| ID int // 0-based sequence number | |
| In []*Edge // unordered set of incoming call edges (n.In[*].Callee == n) | |
| Out []*Edge // unordered set of outgoing call edges (n.Out[*].Caller == n) | |
| } | |
| func (n *Node) String() string { | |
| return fmt.Sprintf("n%d:%s", n.ID, n.Func) | |
| } | |
| // A Edge represents an edge in the call graph. | |
| // | |
| // Site is nil for edges originating in synthetic or intrinsic | |
| // functions, e.g. reflect.Value.Call or the root of the call graph. | |
| type Edge struct { | |
| Caller *Node | |
| Site ssa.CallInstruction | |
| Callee *Node | |
| } | |
| func (e Edge) String() string { | |
| return fmt.Sprintf("%s --> %s", e.Caller, e.Callee) | |
| } | |
| func (e Edge) Description() string { | |
| var prefix string | |
| switch e.Site.(type) { | |
| case nil: | |
| return "synthetic call" | |
| case *ssa.Go: | |
| prefix = "concurrent " | |
| case *ssa.Defer: | |
| prefix = "deferred " | |
| } | |
| return prefix + e.Site.Common().Description() | |
| } | |
| func (e Edge) Pos() token.Pos { | |
| if e.Site == nil { | |
| return token.NoPos | |
| } | |
| return e.Site.Pos() | |
| } | |
| // AddEdge adds the edge (caller, site, callee) to the call graph. | |
| // Elimination of duplicate edges is the caller's responsibility. | |
| func AddEdge(caller *Node, site ssa.CallInstruction, callee *Node) { | |
| e := &Edge{caller, site, callee} | |
| callee.In = append(callee.In, e) | |
| caller.Out = append(caller.Out, e) | |
| } |