github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/oracle/callgraph.go (about)

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package oracle
     6  
     7  import (
     8  	"fmt"
     9  	"go/token"
    10  	"sort"
    11  
    12  	"golang.org/x/tools/go/callgraph"
    13  	"golang.org/x/tools/go/ssa"
    14  	"golang.org/x/tools/go/types"
    15  	"golang.org/x/tools/oracle/serial"
    16  )
    17  
    18  // doCallgraph displays the entire callgraph of the current program,
    19  // or if a query -pos was provided, the query package.
    20  func doCallgraph(o *Oracle, qpos *QueryPos) (queryResult, error) {
    21  	buildSSA(o)
    22  
    23  	// Run the pointer analysis and build the callgraph.
    24  	o.ptaConfig.BuildCallGraph = true
    25  	cg := ptrAnalysis(o).CallGraph
    26  	cg.DeleteSyntheticNodes()
    27  
    28  	var qpkg *types.Package
    29  	var isQueryPkg func(fn *ssa.Function) bool
    30  	var keep, remove, roots []*callgraph.Node
    31  	if qpos == nil {
    32  		// No -pos provided: show complete callgraph.
    33  		roots = append(roots, cg.Root)
    34  		isQueryPkg = func(fn *ssa.Function) bool { return true }
    35  	} else {
    36  		// A query -pos was provided: restrict result to
    37  		// functions belonging to the query package.
    38  		qpkg = qpos.info.Pkg
    39  		isQueryPkg = func(fn *ssa.Function) bool {
    40  			return fn.Pkg != nil && fn.Pkg.Object == qpkg
    41  		}
    42  	}
    43  
    44  	// First compute the nodes to keep and remove.
    45  	for fn, cgn := range cg.Nodes {
    46  		if isQueryPkg(fn) {
    47  			keep = append(keep, cgn)
    48  		} else {
    49  			remove = append(remove, cgn)
    50  		}
    51  	}
    52  
    53  	// Compact the Node.ID sequence of the kept nodes,
    54  	// preserving the original order.
    55  	sort.Sort(nodesByID(keep))
    56  	for i, cgn := range keep {
    57  		cgn.ID = i
    58  	}
    59  
    60  	// Compute the set of roots:
    61  	// in-package nodes with out-of-package callers.
    62  	// For determinism, roots are ordered by original Node.ID.
    63  	for _, cgn := range keep {
    64  		for _, e := range cgn.In {
    65  			if !isQueryPkg(e.Caller.Func) {
    66  				roots = append(roots, cgn)
    67  				break
    68  			}
    69  		}
    70  	}
    71  
    72  	// Finally, discard all out-of-package nodes.
    73  	for _, cgn := range remove {
    74  		cg.DeleteNode(cgn)
    75  	}
    76  
    77  	return &callgraphResult{qpkg, cg.Nodes, roots}, nil
    78  }
    79  
    80  type callgraphResult struct {
    81  	qpkg  *types.Package
    82  	nodes map[*ssa.Function]*callgraph.Node
    83  	roots []*callgraph.Node
    84  }
    85  
    86  func (r *callgraphResult) display(printf printfFunc) {
    87  	descr := "the entire program"
    88  	if r.qpkg != nil {
    89  		descr = fmt.Sprintf("package %s", r.qpkg.Path())
    90  	}
    91  
    92  	printf(nil, `
    93  Below is a call graph of %s.
    94  The numbered nodes form a spanning tree.
    95  Non-numbered nodes indicate back- or cross-edges to the node whose
    96   number follows in parentheses.
    97  `, descr)
    98  
    99  	printed := make(map[*callgraph.Node]int)
   100  	var print func(caller *callgraph.Node, indent int)
   101  	print = func(caller *callgraph.Node, indent int) {
   102  		if num, ok := printed[caller]; !ok {
   103  			num = len(printed)
   104  			printed[caller] = num
   105  
   106  			// Sort the children into name order for deterministic* output.
   107  			// (*mostly: anon funcs' names are not globally unique.)
   108  			var funcs funcsByName
   109  			for callee := range callgraph.CalleesOf(caller) {
   110  				funcs = append(funcs, callee.Func)
   111  			}
   112  			sort.Sort(funcs)
   113  
   114  			printf(caller.Func, "%d\t%*s%s", num, 4*indent, "", caller.Func.RelString(r.qpkg))
   115  			for _, callee := range funcs {
   116  				print(r.nodes[callee], indent+1)
   117  			}
   118  		} else {
   119  			printf(caller.Func, "\t%*s%s (%d)", 4*indent, "", caller.Func.RelString(r.qpkg), num)
   120  		}
   121  	}
   122  	for _, root := range r.roots {
   123  		print(root, 0)
   124  	}
   125  }
   126  
   127  type nodesByID []*callgraph.Node
   128  
   129  func (s nodesByID) Len() int           { return len(s) }
   130  func (s nodesByID) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   131  func (s nodesByID) Less(i, j int) bool { return s[i].ID < s[j].ID }
   132  
   133  type funcsByName []*ssa.Function
   134  
   135  func (s funcsByName) Len() int           { return len(s) }
   136  func (s funcsByName) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
   137  func (s funcsByName) Less(i, j int) bool { return s[i].String() < s[j].String() }
   138  
   139  func (r *callgraphResult) toSerial(res *serial.Result, fset *token.FileSet) {
   140  	cg := make([]serial.CallGraph, len(r.nodes))
   141  	for _, n := range r.nodes {
   142  		j := &cg[n.ID]
   143  		fn := n.Func
   144  		j.Name = fn.String()
   145  		j.Pos = fset.Position(fn.Pos()).String()
   146  		for callee := range callgraph.CalleesOf(n) {
   147  			j.Children = append(j.Children, callee.ID)
   148  		}
   149  		sort.Ints(j.Children)
   150  	}
   151  	res.Callgraph = cg
   152  }