129 lines
3.7 KiB
Go
129 lines
3.7 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 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 "honnef.co/go/tools/callgraph"
|
|
|
|
// TODO(adonovan): add a function to eliminate wrappers from the
|
|
// callgraph, preserving topology.
|
|
// More generally, we could eliminate "uninteresting" nodes such as
|
|
// nodes from packages we don't care about.
|
|
|
|
import (
|
|
"fmt"
|
|
"go/token"
|
|
|
|
"honnef.co/go/tools/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
|
|
Nodes map[*ssa.Function]*Node // all nodes by function
|
|
}
|
|
|
|
// New returns a new Graph with the specified 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.
|
|
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.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)
|
|
}
|