Skip to content

Commit

Permalink
Add reverse edge pass.
Browse files Browse the repository at this point in the history
Incorporate reverse edges into html.
Use symbolic offsets instead of byte offsets for interior pointers.
  • Loading branch information
randall77 committed Oct 3, 2017
1 parent e4301ca commit 4634d5d
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 6 deletions.
3 changes: 3 additions & 0 deletions gocore/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ func Core(proc *core.Process, flags Flags) (p *Program, err error) {
if flags&FlagTypes != 0 {
p.typeHeap()
}
if flags&FlagReverse != 0 {
p.reverseEdges()
}

return p, nil
}
Expand Down
69 changes: 69 additions & 0 deletions gocore/reverse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package gocore

import (
"sort"

"github.com/randall77/corelib/core"
)

func (p *Program) reverseEdges() {
p.reverse = make([][]core.Address, p.nObj)
p.ForEachObject(func(x Object) bool {
p.ForEachPtr(x, func(i int64, y Object, _ int64) bool {
idx, _ := p.findObjectIndex(p.Addr(y))
p.reverse[idx] = append(p.reverse[idx], p.Addr(x).Add(i))
return true
})
return true
})
p.ForEachRoot(func(r *Root) bool {
p.ForEachRootPtr(r, func(i int64, y Object, j int64) bool {
idx, _ := p.findObjectIndex(p.Addr(y))
p.reverse[idx] = append(p.reverse[idx], r.Addr.Add(i))
return true
})
return true
})

// Make root index.
p.ForEachRoot(func(r *Root) bool {
p.rootIdx = append(p.rootIdx, r)
return true
})
sort.Slice(p.rootIdx, func(i, j int) bool { return p.rootIdx[i].Addr < p.rootIdx[j].Addr })
}

// ForEachReversePtr calls fn for all pointers it finds pointing to y.
// It calls fn with:
// the object or root which points to y (exactly one will be non-nil)
// the offset i in that object or root where the pointer appears.
// the offset j in y where the pointer points.
// If fn returns false, ForEachReversePtr returns immediately.
// FlagReverse must have been passed to Core when p was constructed.
func (p *Program) ForEachReversePtr(y Object, fn func(x Object, r *Root, i, j int64) bool) {
idx, _ := p.findObjectIndex(p.Addr(y))
for _, a := range p.reverse[idx] {
// Read pointer, compute offset in y.
ptr := p.proc.ReadPtr(a)
j := ptr.Sub(p.Addr(y))

// Find source of pointer.
x, i := p.FindObject(a)
if x != 0 {
// Source is an object.
if !fn(x, nil, i, j) {
return
}
continue
}
// Source is a root.
k := sort.Search(len(p.rootIdx), func(k int) bool {
r := p.rootIdx[k]
return a < r.Addr.Add(r.Type.Size)
})
r := p.rootIdx[k]
if !fn(0, r, a.Sub(r.Addr), j) {
return
}
}
}
9 changes: 9 additions & 0 deletions gocore/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Flags uint8

const (
FlagTypes Flags = 1 << iota
FlagReverse
)

type Program struct {
Expand Down Expand Up @@ -57,6 +58,14 @@ type Program struct {
// Types of each object, indexed by object index.
// Only initialized if FlagTypes is passed to Core.
types []typeInfo

// Reverse edges. reverse[i] contains all the locations
// where a pointer to object #i resides.
// Only initialized if FlagReverse is passed to Core.
reverse [][]core.Address
// Sorted list of all roots.
// Only initialized if FlagReverse is passed to Core.
rootIdx []*Root
}

// Process returns the core.Process used to construct this Program.
Expand Down
124 changes: 119 additions & 5 deletions html.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func serveHtml(c *gocore.Program) {
end = n * typ.Size
}
for i := end; i < size; i += c.Process().PtrSize() {
fmt.Fprintf(w, "<tr><td>f%d</td><td>?</td><td><pre>", i)
fmt.Fprintf(w, "<tr><td>f%d</td><td colspan=\"2\">?</td><td><pre>", i)
for j := int64(0); j < c.Process().PtrSize(); j++ {
fmt.Fprintf(w, "%02x ", c.Process().ReadUint8(addr.Add(i+j)))
}
Expand All @@ -83,6 +83,32 @@ func serveHtml(c *gocore.Program) {
fmt.Fprintf(w, "</tr>\n")
}
fmt.Fprintf(w, "</table>\n")
fmt.Fprintf(w, "<h3>references to this object</h3>\n")
nrev := 0
c.ForEachReversePtr(x, func(z gocore.Object, r *gocore.Root, i, j int64) bool {
if nrev == 10 {
fmt.Fprintf(w, "...additional references elided...<br/>\n")
return false
}
if r != nil {
fmt.Fprintf(w, "%s%s", r.Name, field(r.Type, i))
} else {
t, r := c.Type(z)
if t == nil {
fmt.Fprintf(w, "%s", htmlPointer(c, c.Addr(z).Add(i)))
} else {
idx := ""
if r > 1 {
idx = fmt.Sprintf("[%d]", i/t.Size)
i %= t.Size
}
fmt.Fprintf(w, "%s%s%s", htmlPointer(c, c.Addr(z)), idx, field(t, i))
}
}
fmt.Fprintf(w, " → %s<br/>\n", htmlPointer(c, a.Add(j)))
nrev++
return true
})
})
http.HandleFunc("/goroutines", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>goroutines</h1>\n")
Expand Down Expand Up @@ -279,7 +305,6 @@ func htmlObject(w http.ResponseWriter, c *gocore.Program, name string, a core.Ad
for _, f := range t.Fields {
htmlObject(w, c, name+"."+f.Name, a.Add(f.Off), f.Type, live)
}

}
}

Expand All @@ -292,10 +317,19 @@ func htmlPointer(c *gocore.Program, a core.Address) string {
return fmt.Sprintf("%x", a)
}
s := fmt.Sprintf("<a href=\"/object?o=%x\">object %x</a>", c.Addr(x), c.Addr(x))
if i != 0 {
s = fmt.Sprintf("%s+%d", s, i)
if i == 0 {
return s
}
return s
t, r := c.Type(x)
if t == nil {
return fmt.Sprintf("%s+%d", s, i)
}
idx := ""
if r > 1 {
idx = fmt.Sprintf("[%d]", i/t.Size)
i %= t.Size
}
return fmt.Sprintf("%s%s%s", s, idx, region(t, i))
}

func htmlPointerAt(c *gocore.Program, a core.Address, live map[core.Address]bool) string {
Expand All @@ -319,3 +353,83 @@ func tableStyle(w http.ResponseWriter) {
fmt.Fprintf(w, "</style>\n")

}

// Returns the name of the field at offset off in t.
func field(t *gocore.Type, off int64) string {
switch t.Kind {
default:
return ""
case gocore.KindComplex:
if off == 0 {
return ".real"
}
return ".imag"
case gocore.KindEface:
if off == 0 {
return ".type"
}
return ".data"
case gocore.KindIface:
if off == 0 {
return ".itab"
}
return ".data"
case gocore.KindString:
if off == 0 {
return ".ptr"
}
return ".len"
case gocore.KindSlice:
if off == 0 {
return ".ptr"
}
if off < t.Size/2 {
return ".len"
}
return ".cap"
case gocore.KindArray:
i := off / t.Elem.Size
return fmt.Sprintf("[%d]%s", i, field(t.Elem, off-i*t.Elem.Size))
case gocore.KindStruct:
for _, f := range t.Fields {
if off >= f.Off && off < f.Off+f.Type.Size {
return fmt.Sprintf(".%s%s", f.Name, field(f.Type, off-f.Off))
}
}
return ".???"
}
}

// Returns the name of the region starting at offset off in t.
func region(t *gocore.Type, off int64) string {
if off == 0 {
return ""
}
switch t.Kind {
default:
return ""
case gocore.KindComplex:
return ".imag"
case gocore.KindEface:
return ".data"
case gocore.KindIface:
return ".data"
case gocore.KindString:
return ".len"
case gocore.KindSlice:
if off < t.Size/2 {
return ".len"
}
return ".cap"
case gocore.KindArray:
i := off / t.Elem.Size
return fmt.Sprintf("[%d]%s", i, region(t.Elem, off-i*t.Elem.Size))
case gocore.KindStruct:
for _, f := range t.Fields {
if off >= f.Off && off < f.Off+f.Type.Size {
return fmt.Sprintf(".%s%s", f.Name, region(f.Type, off-f.Off))
}
}
return ".???"
}
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func main() {
case "reachable":
flags = gocore.FlagTypes
case "html":
flags = gocore.FlagTypes
flags = gocore.FlagTypes | gocore.FlagReverse
}

// All commands other than "help" need a core file.
Expand Down

0 comments on commit 4634d5d

Please sign in to comment.