GoPLS Viewer

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