in cmd/viewcore/html.go [25:221]
func serveHTML(c *gocore.Process, port int, async bool) {
http.HandleFunc("/object", func(w http.ResponseWriter, r *http.Request) {
objs, ok := r.URL.Query()["o"]
if !ok || len(objs) != 1 {
fmt.Fprintf(w, "wrong or missing o= object specification")
return
}
obj, err := strconv.ParseInt(objs[0], 16, 64)
if err != nil {
fmt.Fprintf(w, "unparseable o= object specification: %s", err)
return
}
a := core.Address(obj)
x, _ := c.FindObject(a)
if x == 0 {
fmt.Fprintf(w, "can't find object at %x", a)
return
}
addr := c.Addr(x)
size := c.Size(x)
typ, repeat := c.Type(x)
tableStyle(w)
fmt.Fprintf(w, "<h1>object %x</h1>\n", a)
fmt.Fprintf(w, "<h3>%s</h3>\n", html.EscapeString(typeName(c, x)))
fmt.Fprintf(w, "<h3>%d bytes</h3>\n", size)
if typ != nil && repeat == 1 && typ.String() == "runtime.g" {
found := false
for _, g := range c.Goroutines() {
if g.Addr() == addr {
found = true
break
}
}
if found {
fmt.Fprintf(w, "<h3><a href=\"goroutine?g=%x\">goroutine stack</a></h3>\n", addr)
}
}
fmt.Fprintf(w, "<table>\n")
fmt.Fprintf(w, "<tr><th align=left>field</th><th align=left colspan=\"2\">type</th><th align=left>value</th></tr>\n")
var end int64
if typ != nil {
n := size / typ.Size
if n > 1 {
for i := int64(0); i < n; i++ {
htmlObject(w, c, fmt.Sprintf("[%d]", i), addr.Add(i*typ.Size), typ, nil)
}
} else {
htmlObject(w, c, "", addr, typ, nil)
}
end = n * typ.Size
}
for i := end; i < size; i += c.Process().PtrSize() {
fmt.Fprintf(w, "<tr><td>f%d</td><td colspan=\"2\">?</td>", i)
if c.IsPtr(addr.Add(i)) {
fmt.Fprintf(w, "<td>%s</td>", htmlPointer(c, c.Process().ReadPtr(addr.Add(i))))
} else {
fmt.Fprintf(w, "<td><pre>")
for j := int64(0); j < c.Process().PtrSize(); j++ {
fmt.Fprintf(w, "%02x ", c.Process().ReadUint8(addr.Add(i+j)))
}
fmt.Fprintf(w, "</pre></td><td><pre>")
for j := int64(0); j < c.Process().PtrSize(); j++ {
r := c.Process().ReadUint8(addr.Add(i + j))
if r >= 32 && r <= 126 {
fmt.Fprintf(w, "%s", html.EscapeString(string(rune(r))))
} else {
fmt.Fprintf(w, ".")
}
}
fmt.Fprintf(w, "</pre></td>")
}
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, typeFieldName(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, typeFieldName(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")
tableStyle(w)
fmt.Fprintf(w, "<table>\n")
fmt.Fprintf(w, "<tr><th align=left>goroutine</th><th align=left>top of stack</th></tr>\n")
for _, g := range c.Goroutines() {
fmt.Fprintf(w, "<tr><td><a href=\"goroutine?g=%x\">%x</a></td><td>%s</td></tr>\n", g.Addr(), g.Addr(), g.Frames()[0].Func().Name())
}
fmt.Fprintf(w, "</table>\n")
// TODO: export goroutine state (runnable, running, syscall, ...) and print it here.
})
http.HandleFunc("/goroutine", func(w http.ResponseWriter, r *http.Request) {
gs, ok := r.URL.Query()["g"]
if !ok || len(gs) != 1 {
fmt.Fprintf(w, "wrong or missing g= goroutine specification")
return
}
addr, err := strconv.ParseInt(gs[0], 16, 64)
if err != nil {
fmt.Fprintf(w, "unparseable g= goroutine specification: %s\n", err)
return
}
a := core.Address(addr)
var g *gocore.Goroutine
for _, x := range c.Goroutines() {
if x.Addr() == a {
g = x
break
}
}
if g == nil {
fmt.Fprintf(w, "goroutine %x not found\n", a)
return
}
tableStyle(w)
fmt.Fprintf(w, "<h1>goroutine %x</h1>\n", g.Addr())
fmt.Fprintf(w, "<h3>%s</h3>\n", htmlPointer(c, g.Addr()))
fmt.Fprintf(w, "<h3>%d bytes of stack</h3>\n", g.Stack())
for _, f := range g.Frames() {
fmt.Fprintf(w, "<h3>%s+%d</h3>\n", f.Func().Name(), f.PC().Sub(f.Func().Entry()))
// TODO: convert fn+off to file+lineno.
fmt.Fprintf(w, "<table>\n")
fmt.Fprintf(w, "<tr><th align=left>field</th><th align=left colspan=\"2\">type</th><th align=left>value</th></tr>\n")
for _, r := range f.Roots() {
htmlObject(w, c, r.Name, r.Addr, r.Type, f.Live)
}
fmt.Fprintf(w, "</table>\n")
}
})
http.HandleFunc("/globals", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>globals</h1>\n")
tableStyle(w)
fmt.Fprintf(w, "<table>\n")
fmt.Fprintf(w, "<tr><th align=left>field</th><th align=left colspan=\"2\">type</th><th align=left>value</th></tr>\n")
for _, r := range c.Globals() {
htmlObject(w, c, r.Name, r.Addr, r.Type, nil)
}
fmt.Fprintf(w, "</table>\n")
})
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>core dump viewer</h1>\n")
fmt.Fprintf(w, "%s<br/>\n", c.Process().Arch())
fmt.Fprintf(w, "%s<br/>\n", c.BuildVersion())
fmt.Fprintf(w, "<a href=\"goroutines\">goroutines</a><br/>\n")
fmt.Fprintf(w, "<a href=\"globals\">globals</a><br/>\n")
tableStyle(w)
fmt.Fprintf(w, "<table>\n")
fmt.Fprintf(w, "<tr><th align=left>category</th><th align=left>bytes</th><th align=left>percent</th></tr>\n")
all := c.Stats().Size
var p func(*gocore.Stats, string)
p = func(s *gocore.Stats, prefix string) {
fmt.Fprintf(w, "<tr><td>%s%s</td><td align=right>%d</td><td align=right>%.2f</td></tr>\n", prefix, s.Name, s.Size, float64(s.Size)/float64(all)*100)
for _, c := range s.Children {
p(c, prefix+"..")
}
}
p(c.Stats(), "")
fmt.Fprintf(w, "</table>\n")
})
if port <= 0 {
port = 8080
}
fmt.Printf("start serving on http://localhost:%d\n", port)
httpAddr := fmt.Sprintf(":%d", port)
if async {
go http.ListenAndServe(httpAddr, nil)
return
}
http.ListenAndServe(httpAddr, nil)
}