github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/tools/go/pointer/analysis.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  // +build go1.5
     6  
     7  package pointer
     8  
     9  // This file defines the main datatypes and Analyze function of the pointer analysis.
    10  
    11  import (
    12  	"fmt"
    13  	"go/token"
    14  	"go/types"
    15  	"io"
    16  	"os"
    17  	"reflect"
    18  	"runtime"
    19  	"runtime/debug"
    20  	"sort"
    21  
    22  	"golang.org/x/tools/go/callgraph"
    23  	"golang.org/x/tools/go/ssa"
    24  	"golang.org/x/tools/go/types/typeutil"
    25  )
    26  
    27  const (
    28  	// optimization options; enable all when committing
    29  	optRenumber = true // enable renumbering optimization (makes logs hard to read)
    30  	optHVN      = true // enable pointer equivalence via Hash-Value Numbering
    31  
    32  	// debugging options; disable all when committing
    33  	debugHVN           = false // enable assertions in HVN
    34  	debugHVNVerbose    = false // enable extra HVN logging
    35  	debugHVNCrossCheck = false // run solver with/without HVN and compare (caveats below)
    36  	debugTimers        = false // show running time of each phase
    37  )
    38  
    39  // object.flags bitmask values.
    40  const (
    41  	otTagged   = 1 << iota // type-tagged object
    42  	otIndirect             // type-tagged object with indirect payload
    43  	otFunction             // function object
    44  )
    45  
    46  // An object represents a contiguous block of memory to which some
    47  // (generalized) pointer may point.
    48  //
    49  // (Note: most variables called 'obj' are not *objects but nodeids
    50  // such that a.nodes[obj].obj != nil.)
    51  //
    52  type object struct {
    53  	// flags is a bitset of the node type (ot*) flags defined above.
    54  	flags uint32
    55  
    56  	// Number of following nodes belonging to the same "object"
    57  	// allocation.  Zero for all other nodes.
    58  	size uint32
    59  
    60  	// data describes this object; it has one of these types:
    61  	//
    62  	// ssa.Value	for an object allocated by an SSA operation.
    63  	// types.Type	for an rtype instance object or *rtype-tagged object.
    64  	// string	for an instrinsic object, e.g. the array behind os.Args.
    65  	// nil		for an object allocated by an instrinsic.
    66  	//		(cgn provides the identity of the intrinsic.)
    67  	data interface{}
    68  
    69  	// The call-graph node (=context) in which this object was allocated.
    70  	// May be nil for global objects: Global, Const, some Functions.
    71  	cgn *cgnode
    72  }
    73  
    74  // nodeid denotes a node.
    75  // It is an index within analysis.nodes.
    76  // We use small integers, not *node pointers, for many reasons:
    77  // - they are smaller on 64-bit systems.
    78  // - sets of them can be represented compactly in bitvectors or BDDs.
    79  // - order matters; a field offset can be computed by simple addition.
    80  type nodeid uint32
    81  
    82  // A node is an equivalence class of memory locations.
    83  // Nodes may be pointers, pointed-to locations, neither, or both.
    84  //
    85  // Nodes that are pointed-to locations ("labels") have an enclosing
    86  // object (see analysis.enclosingObject).
    87  //
    88  type node struct {
    89  	// If non-nil, this node is the start of an object
    90  	// (addressable memory location).
    91  	// The following obj.size nodes implicitly belong to the object;
    92  	// they locate their object by scanning back.
    93  	obj *object
    94  
    95  	// The type of the field denoted by this node.  Non-aggregate,
    96  	// unless this is an tagged.T node (i.e. the thing
    97  	// pointed to by an interface) in which case typ is that type.
    98  	typ types.Type
    99  
   100  	// subelement indicates which directly embedded subelement of
   101  	// an object of aggregate type (struct, tuple, array) this is.
   102  	subelement *fieldInfo // e.g. ".a.b[*].c"
   103  
   104  	// Solver state for the canonical node of this pointer-
   105  	// equivalence class.  Each node is created with its own state
   106  	// but they become shared after HVN.
   107  	solve *solverState
   108  }
   109  
   110  // An analysis instance holds the state of a single pointer analysis problem.
   111  type analysis struct {
   112  	config      *Config                     // the client's control/observer interface
   113  	prog        *ssa.Program                // the program being analyzed
   114  	log         io.Writer                   // log stream; nil to disable
   115  	panicNode   nodeid                      // sink for panic, source for recover
   116  	nodes       []*node                     // indexed by nodeid
   117  	flattenMemo map[types.Type][]*fieldInfo // memoization of flatten()
   118  	trackTypes  map[types.Type]bool         // memoization of shouldTrack()
   119  	constraints []constraint                // set of constraints
   120  	cgnodes     []*cgnode                   // all cgnodes
   121  	genq        []*cgnode                   // queue of functions to generate constraints for
   122  	intrinsics  map[*ssa.Function]intrinsic // non-nil values are summaries for intrinsic fns
   123  	globalval   map[ssa.Value]nodeid        // node for each global ssa.Value
   124  	globalobj   map[ssa.Value]nodeid        // maps v to sole member of pts(v), if singleton
   125  	localval    map[ssa.Value]nodeid        // node for each local ssa.Value
   126  	localobj    map[ssa.Value]nodeid        // maps v to sole member of pts(v), if singleton
   127  	atFuncs     map[*ssa.Function]bool      // address-taken functions (for presolver)
   128  	mapValues   []nodeid                    // values of makemap objects (indirect in HVN)
   129  	work        nodeset                     // solver's worklist
   130  	result      *Result                     // results of the analysis
   131  	track       track                       // pointerlike types whose aliasing we track
   132  	deltaSpace  []int                       // working space for iterating over PTS deltas
   133  
   134  	// Reflection & intrinsics:
   135  	hasher              typeutil.Hasher // cache of type hashes
   136  	reflectValueObj     types.Object    // type symbol for reflect.Value (if present)
   137  	reflectValueCall    *ssa.Function   // (reflect.Value).Call
   138  	reflectRtypeObj     types.Object    // *types.TypeName for reflect.rtype (if present)
   139  	reflectRtypePtr     *types.Pointer  // *reflect.rtype
   140  	reflectType         *types.Named    // reflect.Type
   141  	rtypes              typeutil.Map    // nodeid of canonical *rtype-tagged object for type T
   142  	reflectZeros        typeutil.Map    // nodeid of canonical T-tagged object for zero value
   143  	runtimeSetFinalizer *ssa.Function   // runtime.SetFinalizer
   144  }
   145  
   146  // enclosingObj returns the first node of the addressable memory
   147  // object that encloses node id.  Panic ensues if that node does not
   148  // belong to any object.
   149  func (a *analysis) enclosingObj(id nodeid) nodeid {
   150  	// Find previous node with obj != nil.
   151  	for i := id; i >= 0; i-- {
   152  		n := a.nodes[i]
   153  		if obj := n.obj; obj != nil {
   154  			if i+nodeid(obj.size) <= id {
   155  				break // out of bounds
   156  			}
   157  			return i
   158  		}
   159  	}
   160  	panic("node has no enclosing object")
   161  }
   162  
   163  // labelFor returns the Label for node id.
   164  // Panic ensues if that node is not addressable.
   165  func (a *analysis) labelFor(id nodeid) *Label {
   166  	return &Label{
   167  		obj:        a.nodes[a.enclosingObj(id)].obj,
   168  		subelement: a.nodes[id].subelement,
   169  	}
   170  }
   171  
   172  func (a *analysis) warnf(pos token.Pos, format string, args ...interface{}) {
   173  	msg := fmt.Sprintf(format, args...)
   174  	if a.log != nil {
   175  		fmt.Fprintf(a.log, "%s: warning: %s\n", a.prog.Fset.Position(pos), msg)
   176  	}
   177  	a.result.Warnings = append(a.result.Warnings, Warning{pos, msg})
   178  }
   179  
   180  // computeTrackBits sets a.track to the necessary 'track' bits for the pointer queries.
   181  func (a *analysis) computeTrackBits() {
   182  	var queryTypes []types.Type
   183  	for v := range a.config.Queries {
   184  		queryTypes = append(queryTypes, v.Type())
   185  	}
   186  	for v := range a.config.IndirectQueries {
   187  		queryTypes = append(queryTypes, mustDeref(v.Type()))
   188  	}
   189  	for _, t := range queryTypes {
   190  		switch t.Underlying().(type) {
   191  		case *types.Chan:
   192  			a.track |= trackChan
   193  		case *types.Map:
   194  			a.track |= trackMap
   195  		case *types.Pointer:
   196  			a.track |= trackPtr
   197  		case *types.Slice:
   198  			a.track |= trackSlice
   199  		case *types.Interface:
   200  			a.track = trackAll
   201  			return
   202  		}
   203  		if rVObj := a.reflectValueObj; rVObj != nil && types.Identical(t, rVObj.Type()) {
   204  			a.track = trackAll
   205  			return
   206  		}
   207  	}
   208  }
   209  
   210  // Analyze runs the pointer analysis with the scope and options
   211  // specified by config, and returns the (synthetic) root of the callgraph.
   212  //
   213  // Pointer analysis of a transitively closed well-typed program should
   214  // always succeed.  An error can occur only due to an internal bug.
   215  //
   216  func Analyze(config *Config) (result *Result, err error) {
   217  	if config.Mains == nil {
   218  		return nil, fmt.Errorf("no main/test packages to analyze (check $GOROOT/$GOPATH)")
   219  	}
   220  	defer func() {
   221  		if p := recover(); p != nil {
   222  			err = fmt.Errorf("internal error in pointer analysis: %v (please report this bug)", p)
   223  			fmt.Fprintln(os.Stderr, "Internal panic in pointer analysis:")
   224  			debug.PrintStack()
   225  		}
   226  	}()
   227  
   228  	a := &analysis{
   229  		config:      config,
   230  		log:         config.Log,
   231  		prog:        config.prog(),
   232  		globalval:   make(map[ssa.Value]nodeid),
   233  		globalobj:   make(map[ssa.Value]nodeid),
   234  		flattenMemo: make(map[types.Type][]*fieldInfo),
   235  		trackTypes:  make(map[types.Type]bool),
   236  		atFuncs:     make(map[*ssa.Function]bool),
   237  		hasher:      typeutil.MakeHasher(),
   238  		intrinsics:  make(map[*ssa.Function]intrinsic),
   239  		result: &Result{
   240  			Queries:         make(map[ssa.Value]Pointer),
   241  			IndirectQueries: make(map[ssa.Value]Pointer),
   242  		},
   243  		deltaSpace: make([]int, 0, 100),
   244  	}
   245  
   246  	if false {
   247  		a.log = os.Stderr // for debugging crashes; extremely verbose
   248  	}
   249  
   250  	if a.log != nil {
   251  		fmt.Fprintln(a.log, "==== Starting analysis")
   252  	}
   253  
   254  	// Pointer analysis requires a complete program for soundness.
   255  	// Check to prevent accidental misconfiguration.
   256  	for _, pkg := range a.prog.AllPackages() {
   257  		// (This only checks that the package scope is complete,
   258  		// not that func bodies exist, but it's a good signal.)
   259  		if !pkg.Pkg.Complete() {
   260  			return nil, fmt.Errorf(`pointer analysis requires a complete program yet package %q was incomplete`, pkg.Pkg.Path())
   261  		}
   262  	}
   263  
   264  	if reflect := a.prog.ImportedPackage("reflect"); reflect != nil {
   265  		rV := reflect.Pkg.Scope().Lookup("Value")
   266  		a.reflectValueObj = rV
   267  		a.reflectValueCall = a.prog.LookupMethod(rV.Type(), nil, "Call")
   268  		a.reflectType = reflect.Pkg.Scope().Lookup("Type").Type().(*types.Named)
   269  		a.reflectRtypeObj = reflect.Pkg.Scope().Lookup("rtype")
   270  		a.reflectRtypePtr = types.NewPointer(a.reflectRtypeObj.Type())
   271  
   272  		// Override flattening of reflect.Value, treating it like a basic type.
   273  		tReflectValue := a.reflectValueObj.Type()
   274  		a.flattenMemo[tReflectValue] = []*fieldInfo{{typ: tReflectValue}}
   275  
   276  		// Override shouldTrack of reflect.Value and *reflect.rtype.
   277  		// Always track pointers of these types.
   278  		a.trackTypes[tReflectValue] = true
   279  		a.trackTypes[a.reflectRtypePtr] = true
   280  
   281  		a.rtypes.SetHasher(a.hasher)
   282  		a.reflectZeros.SetHasher(a.hasher)
   283  	}
   284  	if runtime := a.prog.ImportedPackage("runtime"); runtime != nil {
   285  		a.runtimeSetFinalizer = runtime.Func("SetFinalizer")
   286  	}
   287  	a.computeTrackBits()
   288  
   289  	a.generate()
   290  	a.showCounts()
   291  
   292  	if optRenumber {
   293  		a.renumber()
   294  	}
   295  
   296  	N := len(a.nodes) // excludes solver-created nodes
   297  
   298  	if optHVN {
   299  		if debugHVNCrossCheck {
   300  			// Cross-check: run the solver once without
   301  			// optimization, once with, and compare the
   302  			// solutions.
   303  			savedConstraints := a.constraints
   304  
   305  			a.solve()
   306  			a.dumpSolution("A.pts", N)
   307  
   308  			// Restore.
   309  			a.constraints = savedConstraints
   310  			for _, n := range a.nodes {
   311  				n.solve = new(solverState)
   312  			}
   313  			a.nodes = a.nodes[:N]
   314  
   315  			// rtypes is effectively part of the solver state.
   316  			a.rtypes = typeutil.Map{}
   317  			a.rtypes.SetHasher(a.hasher)
   318  		}
   319  
   320  		a.hvn()
   321  	}
   322  
   323  	if debugHVNCrossCheck {
   324  		runtime.GC()
   325  		runtime.GC()
   326  	}
   327  
   328  	a.solve()
   329  
   330  	// Compare solutions.
   331  	if optHVN && debugHVNCrossCheck {
   332  		a.dumpSolution("B.pts", N)
   333  
   334  		if !diff("A.pts", "B.pts") {
   335  			return nil, fmt.Errorf("internal error: optimization changed solution")
   336  		}
   337  	}
   338  
   339  	// Create callgraph.Nodes in deterministic order.
   340  	if cg := a.result.CallGraph; cg != nil {
   341  		for _, caller := range a.cgnodes {
   342  			cg.CreateNode(caller.fn)
   343  		}
   344  	}
   345  
   346  	// Add dynamic edges to call graph.
   347  	var space [100]int
   348  	for _, caller := range a.cgnodes {
   349  		for _, site := range caller.sites {
   350  			for _, callee := range a.nodes[site.targets].solve.pts.AppendTo(space[:0]) {
   351  				a.callEdge(caller, site, nodeid(callee))
   352  			}
   353  		}
   354  	}
   355  
   356  	return a.result, nil
   357  }
   358  
   359  // callEdge is called for each edge in the callgraph.
   360  // calleeid is the callee's object node (has otFunction flag).
   361  //
   362  func (a *analysis) callEdge(caller *cgnode, site *callsite, calleeid nodeid) {
   363  	obj := a.nodes[calleeid].obj
   364  	if obj.flags&otFunction == 0 {
   365  		panic(fmt.Sprintf("callEdge %s -> n%d: not a function object", site, calleeid))
   366  	}
   367  	callee := obj.cgn
   368  
   369  	if cg := a.result.CallGraph; cg != nil {
   370  		// TODO(adonovan): opt: I would expect duplicate edges
   371  		// (to wrappers) to arise due to the elimination of
   372  		// context information, but I haven't observed any.
   373  		// Understand this better.
   374  		callgraph.AddEdge(cg.CreateNode(caller.fn), site.instr, cg.CreateNode(callee.fn))
   375  	}
   376  
   377  	if a.log != nil {
   378  		fmt.Fprintf(a.log, "\tcall edge %s -> %s\n", site, callee)
   379  	}
   380  
   381  	// Warn about calls to non-intrinsic external functions.
   382  	// TODO(adonovan): de-dup these messages.
   383  	if fn := callee.fn; fn.Blocks == nil && a.findIntrinsic(fn) == nil {
   384  		a.warnf(site.pos(), "unsound call to unknown intrinsic: %s", fn)
   385  		a.warnf(fn.Pos(), " (declared here)")
   386  	}
   387  }
   388  
   389  // dumpSolution writes the PTS solution to the specified file.
   390  //
   391  // It only dumps the nodes that existed before solving.  The order in
   392  // which solver-created nodes are created depends on pre-solver
   393  // optimization, so we can't include them in the cross-check.
   394  //
   395  func (a *analysis) dumpSolution(filename string, N int) {
   396  	f, err := os.Create(filename)
   397  	if err != nil {
   398  		panic(err)
   399  	}
   400  	for id, n := range a.nodes[:N] {
   401  		if _, err := fmt.Fprintf(f, "pts(n%d) = {", id); err != nil {
   402  			panic(err)
   403  		}
   404  		var sep string
   405  		for _, l := range n.solve.pts.AppendTo(a.deltaSpace) {
   406  			if l >= N {
   407  				break
   408  			}
   409  			fmt.Fprintf(f, "%s%d", sep, l)
   410  			sep = " "
   411  		}
   412  		fmt.Fprintf(f, "} : %s\n", n.typ)
   413  	}
   414  	if err := f.Close(); err != nil {
   415  		panic(err)
   416  	}
   417  }
   418  
   419  // showCounts logs the size of the constraint system.  A typical
   420  // optimized distribution is 65% copy, 13% load, 11% addr, 5%
   421  // offsetAddr, 4% store, 2% others.
   422  //
   423  func (a *analysis) showCounts() {
   424  	if a.log != nil {
   425  		counts := make(map[reflect.Type]int)
   426  		for _, c := range a.constraints {
   427  			counts[reflect.TypeOf(c)]++
   428  		}
   429  		fmt.Fprintf(a.log, "# constraints:\t%d\n", len(a.constraints))
   430  		var lines []string
   431  		for t, n := range counts {
   432  			line := fmt.Sprintf("%7d  (%2d%%)\t%s", n, 100*n/len(a.constraints), t)
   433  			lines = append(lines, line)
   434  		}
   435  		sort.Sort(sort.Reverse(sort.StringSlice(lines)))
   436  		for _, line := range lines {
   437  			fmt.Fprintf(a.log, "\t%s\n", line)
   438  		}
   439  
   440  		fmt.Fprintf(a.log, "# nodes:\t%d\n", len(a.nodes))
   441  
   442  		// Show number of pointer equivalence classes.
   443  		m := make(map[*solverState]bool)
   444  		for _, n := range a.nodes {
   445  			m[n.solve] = true
   446  		}
   447  		fmt.Fprintf(a.log, "# ptsets:\t%d\n", len(m))
   448  	}
   449  }