| // Copyright 2018 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. |
| |
| //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris |
| // +build darwin dragonfly freebsd linux netbsd openbsd solaris |
| |
| package gocore |
| |
| import ( |
| "fmt" |
| "os" |
| "testing" |
| ) |
| |
| func TestLT(t *testing.T) { |
| p := loadExample(t) |
| lt := runLT(p) |
| sanityCheck(t, lt) |
| if false { |
| lt.dot(os.Stdout) |
| } |
| } |
| |
| func TestDominators(t *testing.T) { |
| p := loadExample(t) |
| d := p.calculateDominators() |
| if size := d.size[pseudoRoot]; size < 100<<10 { |
| t.Errorf("total size of objects is only %v bytes, should be >100KiB", size) |
| } |
| } |
| |
| func sanityCheck(t *testing.T, d ltDom) bool { |
| t.Helper() |
| // Build pointer-y graph. |
| pRoot := sanityVertex{} |
| roots := make([]sanityVertex, d.nRoots) |
| objects := make([]sanityVertex, d.p.nObj) |
| |
| for i, r := range d.p.rootIdx { |
| v := &roots[i] |
| v.root = r |
| pRoot.succ = append(pRoot.succ, v) |
| v.pred = append(v.pred, &pRoot) |
| d.p.ForEachRootPtr(r, func(_ int64, x Object, _ int64) bool { |
| idx, _ := d.p.findObjectIndex(d.p.Addr(x)) |
| v.succ = append(v.succ, &objects[idx]) |
| objects[idx].pred = append(objects[idx].pred, v) |
| return true |
| }) |
| } |
| d.p.ForEachObject(func(x Object) bool { |
| xIdx, _ := d.p.findObjectIndex(d.p.Addr(x)) |
| v := &objects[xIdx] |
| v.obj = x |
| d.p.ForEachPtr(x, func(_ int64, y Object, _ int64) bool { |
| yIdx, _ := d.p.findObjectIndex(d.p.Addr(y)) |
| v.succ = append(v.succ, &objects[yIdx]) |
| objects[yIdx].pred = append(objects[yIdx].pred, v) |
| return true |
| }) |
| return true |
| }) |
| |
| // Precompute postorder traversal. |
| var postorder []*sanityVertex |
| type workItem struct { |
| v *sanityVertex |
| mode dfsMode |
| } |
| seen := make(map[*sanityVertex]bool, d.nVertices) |
| work := []workItem{{&pRoot, down}} |
| for len(work) > 0 { |
| item := &work[len(work)-1] |
| |
| if item.mode == down && len(item.v.succ) != 0 { |
| item.mode = up |
| for _, w := range item.v.succ { |
| // Only push each node once. |
| if seen[w] { |
| continue |
| } |
| seen[w] = true |
| |
| work = append(work, workItem{w, down}) |
| } |
| continue |
| } |
| |
| work = work[:len(work)-1] |
| postorder = append(postorder, item.v) |
| } |
| |
| // Make map from block id to order index (for intersect call) |
| postnum := make(map[*sanityVertex]int, d.nVertices) |
| for i, b := range postorder { |
| postnum[b] = i |
| } |
| |
| // Make the pseudo-root a self-loop |
| pRoot.idom = &pRoot |
| if postnum[&pRoot] != len(postorder)-1 { |
| panic("pseudo-root not last in postorder") |
| } |
| |
| // Compute relaxation of idom entries |
| for { |
| changed := false |
| |
| for i := len(postorder) - 2; i >= 0; i-- { |
| v := postorder[i] |
| var d *sanityVertex |
| |
| for _, pred := range v.pred { |
| if pred.idom == nil { |
| continue |
| } |
| |
| if d == nil { |
| d = pred |
| continue |
| } |
| |
| d = intersect(d, pred, postnum) |
| } |
| if v.idom != d { |
| v.idom = d |
| changed = true |
| } |
| } |
| |
| if !changed { |
| break |
| } |
| } |
| |
| pRoot.idom = nil |
| |
| getVertex := func(n vName) *sanityVertex { |
| r, o := d.findVertexByName(n) |
| switch { |
| case n == pseudoRoot: |
| return &pRoot |
| case r != nil: |
| return &roots[d.p.findRootIndex(r)] |
| default: |
| idx, _ := d.p.findObjectIndex(d.p.Addr(o)) |
| return &objects[idx] |
| } |
| } |
| |
| matches := true |
| for vertName, domName := range d.idom { |
| if vName(vertName) == pseudoRoot { |
| continue |
| } |
| vert := getVertex(vName(vertName)) |
| dom := getVertex(domName) |
| |
| if vert.idom != dom { |
| matches = false |
| t.Errorf("Mismatch in idom for %v, name #%04v: fast reports %v, sanity reports %v\n", vert.String(d.p), vertName, dom.String(d.p), vert.idom.String(d.p)) |
| } |
| } |
| return matches |
| } |
| |
| func intersect(v, w *sanityVertex, postnum map[*sanityVertex]int) *sanityVertex { |
| for v != w { |
| if postnum[v] < postnum[w] { |
| v = v.idom |
| } else { |
| w = w.idom |
| } |
| } |
| return v |
| } |
| |
| type sanityVertex struct { |
| root *Root |
| obj Object |
| pred []*sanityVertex |
| succ []*sanityVertex |
| idom *sanityVertex |
| } |
| |
| func (v *sanityVertex) String(p *Process) string { |
| switch { |
| case v.root != nil: |
| return fmt.Sprintf("root %s %#x (type %s)", v.root.Name, v.root.Addr, v.root.Type) |
| case v.obj != 0: |
| typ, _ := p.Type(v.obj) |
| var typeName string |
| if typ != nil { |
| typeName = typ.Name |
| } |
| return fmt.Sprintf("object %#x (type %s)", v.obj, typeName) |
| default: |
| return "pseudo-root" |
| } |
| } |