1
2
3
4
5 package pointer
6
7
8
9 import (
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
26 const (
27
28 optRenumber = true
29 optHVN = true
30
31
32 debugHVN = false
33 debugHVNVerbose = false
34 debugHVNCrossCheck = false
35 debugTimers = false
36 )
37
38
39 const (
40 otTagged = 1 << iota
41 otIndirect
42 otFunction
43 )
44
45
46
47
48
49
50 type object struct {
51
52 flags uint32
53
54
55
56 size uint32
57
58
59
60
61
62
63
64
65 data interface{}
66
67
68
69 cgn *cgnode
70 }
71
72
73
74
75
76
77
78 type nodeid uint32
79
80
81
82
83
84
85 type node struct {
86
87
88
89
90 obj *object
91
92
93
94
95 typ types.Type
96
97
98
99 subelement *fieldInfo
100
101
102
103
104 solve *solverState
105 }
106
107
108 type analysis struct {
109 config *Config
110 prog *ssa.Program
111 log io.Writer
112 panicNode nodeid
113 nodes []*node
114 flattenMemo map[types.Type][]*fieldInfo
115 trackTypes map[types.Type]bool
116 constraints []constraint
117 cgnodes []*cgnode
118 genq []*cgnode
119 intrinsics map[*ssa.Function]intrinsic
120 globalval map[ssa.Value]nodeid
121 globalobj map[ssa.Value]nodeid
122 localval map[ssa.Value]nodeid
123 localobj map[ssa.Value]nodeid
124 atFuncs map[*ssa.Function]bool
125 mapValues []nodeid
126 work nodeset
127 result *Result
128 track track
129 deltaSpace []int
130
131
132 hasher typeutil.Hasher
133 reflectValueObj types.Object
134 reflectValueCall *ssa.Function
135 reflectRtypeObj types.Object
136 reflectRtypePtr *types.Pointer
137 reflectType *types.Named
138 rtypes typeutil.Map
139 reflectZeros typeutil.Map
140 runtimeSetFinalizer *ssa.Function
141 }
142
143
144
145
146 func (a *analysis) enclosingObj(id nodeid) nodeid {
147
148 for i := id; i >= 0; i-- {
149 n := a.nodes[i]
150 if obj := n.obj; obj != nil {
151 if i+nodeid(obj.size) <= id {
152 break
153 }
154 return i
155 }
156 }
157 panic("node has no enclosing object")
158 }
159
160
161
162 func (a *analysis) labelFor(id nodeid) *Label {
163 return &Label{
164 obj: a.nodes[a.enclosingObj(id)].obj,
165 subelement: a.nodes[id].subelement,
166 }
167 }
168
169 func (a *analysis) warnf(pos token.Pos, format string, args ...interface{}) {
170 msg := fmt.Sprintf(format, args...)
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.Warnings, Warning{pos, msg})
175 }
176
177
178 func (a *analysis) computeTrackBits() {
179 if len(a.config.extendedQueries) != 0 {
180
181 a.track = trackAll
182 return
183 }
184 var queryTypes []types.Type
185 for v := range a.config.Queries {
186 queryTypes = append(queryTypes, v.Type())
187 }
188 for v := range a.config.IndirectQueries {
189 queryTypes = append(queryTypes, mustDeref(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.reflectValueObj; rVObj != nil && types.Identical(t, rVObj.Type()) {
206 a.track = trackAll
207 return
208 }
209 }
210 }
211
212
213
214
215
216
217 func Analyze(config *Config) (result *Result, err error) {
218 if config.Mains == nil {
219 return nil, fmt.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 flattenMemo: make(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 IndirectQueries: make(map[ssa.Value]Pointer),
243 },
244 deltaSpace: make([]int, 0, 100),
245 }
246
247 if false {
248 a.log = os.Stderr
249 }
250
251 if a.log != nil {
252 fmt.Fprintln(a.log, "==== Starting analysis")
253 }
254
255
256
257 for _, pkg := range a.prog.AllPackages() {
258
259
260 if !pkg.Pkg.Complete() {
261 return nil, fmt.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
274 tReflectValue := a.reflectValueObj.Type()
275 a.flattenMemo[tReflectValue] = []*fieldInfo{{typ: tReflectValue}}
276
277
278
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)
298
299 if optHVN {
300 if debugHVNCrossCheck {
301
302
303
304 savedConstraints := a.constraints
305
306 a.solve()
307 a.dumpSolution("A.pts", N)
308
309
310 a.constraints = savedConstraints
311 for _, n := range a.nodes {
312 n.solve = new(solverState)
313 }
314 a.nodes = a.nodes[:N]
315
316
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
332 if optHVN && debugHVNCrossCheck {
333 a.dumpSolution("B.pts", N)
334
335 if !diff("A.pts", "B.pts") {
336 return nil, fmt.Errorf("internal error: optimization changed solution")
337 }
338 }
339
340
341 if cg := a.result.CallGraph; cg != nil {
342 for _, caller := range a.cgnodes {
343 cg.CreateNode(caller.fn)
344 }
345 }
346
347
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(caller, site, nodeid(callee))
353 }
354 }
355 }
356
357 return a.result, nil
358 }
359
360
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
371
372
373
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
382
383 fn := callee.fn
384
385
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
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
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
405
406
407
408
409 func (a *analysis) dumpSolution(filename string, N int) {
410 f, err := os.Create(filename)
411 if err != nil {
412 panic(err)
413 }
414 for id, n := 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", sep, l)
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
434
435
436 func (a *analysis) showCounts() {
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 t, n := range counts {
445 line := fmt.Sprintf("%7d (%2d%%)\t%s", n, 100*n/len(a.constraints), t)
446 lines = append(lines, line)
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
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
View as plain text