cmd/geth, mobile: add memsize to pprof server (#16532)
* cmd/geth, mobile: add memsize to pprof server This is a temporary change, to be reverted before the next release. * cmd/geth: fix variable name
This commit is contained in:
committed by
Péter Szilágyi
parent
9586f2acc7
commit
e7067be94f
106
vendor/github.com/fjl/memsize/memsizeui/template.go
generated
vendored
Normal file
106
vendor/github.com/fjl/memsize/memsizeui/template.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
package memsizeui
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/fjl/memsize"
|
||||
)
|
||||
|
||||
var (
|
||||
base *template.Template // the "base" template
|
||||
baseInitOnce sync.Once
|
||||
)
|
||||
|
||||
func baseInit() {
|
||||
base = template.Must(template.New("base").Parse(`<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>memsize</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
button, .button {
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
font-size: inherit;
|
||||
padding: 3pt;
|
||||
margin: 3pt;
|
||||
background-color: #eee;
|
||||
border: 1px solid #999;
|
||||
border-radius: 2pt;
|
||||
}
|
||||
form.inline {
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{{template "content" .}}
|
||||
</body>
|
||||
</html>`))
|
||||
|
||||
base.Funcs(template.FuncMap{
|
||||
"quote": strconv.Quote,
|
||||
"humansize": memsize.HumanSize,
|
||||
})
|
||||
|
||||
template.Must(base.New("rootbuttons").Parse(`
|
||||
<a class="button" href="{{$.Link ""}}">Overview</a>
|
||||
{{- range $root := .Roots -}}
|
||||
<form class="inline" method="POST" action="{{$.Link "scan?root=" $root}}">
|
||||
<button type="submit">Scan {{quote $root}}</button>
|
||||
</form>
|
||||
{{- end -}}`))
|
||||
}
|
||||
|
||||
func contentTemplate(source string) *template.Template {
|
||||
baseInitOnce.Do(baseInit)
|
||||
t := template.Must(base.Clone())
|
||||
template.Must(t.New("content").Parse(source))
|
||||
return t
|
||||
}
|
||||
|
||||
var rootTemplate = contentTemplate(`
|
||||
<h1>Memsize</h1>
|
||||
{{template "rootbuttons" .}}
|
||||
<hr/>
|
||||
<h3>Reports</h3>
|
||||
<ul>
|
||||
{{range .Reports}}
|
||||
<li><a href="{{printf "%d" | $.Link "report/"}}">{{quote .RootName}} @ {{.Date}}</a></li>
|
||||
{{else}}
|
||||
No reports yet, hit a scan button to create one.
|
||||
{{end}}
|
||||
</ul>
|
||||
`)
|
||||
|
||||
var notFoundTemplate = contentTemplate(`
|
||||
<h1>{{.Data}}</h1>
|
||||
{{template "rootbuttons" .}}
|
||||
`)
|
||||
|
||||
var reportTemplate = contentTemplate(`
|
||||
{{- $report := .Data -}}
|
||||
<h1>Memsize Report {{$report.ID}}</h1>
|
||||
<form method="POST" action="{{$.Link "scan?root=" $report.RootName}}">
|
||||
<a class="button" href="{{$.Link ""}}">Overview</a>
|
||||
<button type="submit">Scan Again</button>
|
||||
</form>
|
||||
<pre>
|
||||
Root: {{quote $report.RootName}}
|
||||
Date: {{$report.Date}}
|
||||
Duration: {{$report.Duration}}
|
||||
Bitmap Size: {{$report.Sizes.BitmapSize | humansize}}
|
||||
Bitmap Utilization: {{$report.Sizes.BitmapUtilization}}
|
||||
</pre>
|
||||
<hr/>
|
||||
<pre>
|
||||
{{$report.Sizes.Report}}
|
||||
</pre>
|
||||
`)
|
153
vendor/github.com/fjl/memsize/memsizeui/ui.go
generated
vendored
Normal file
153
vendor/github.com/fjl/memsize/memsizeui/ui.go
generated
vendored
Normal file
@ -0,0 +1,153 @@
|
||||
package memsizeui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fjl/memsize"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
init sync.Once
|
||||
mux http.ServeMux
|
||||
mu sync.Mutex
|
||||
reports map[int]Report
|
||||
roots map[string]interface{}
|
||||
reportID int
|
||||
}
|
||||
|
||||
type Report struct {
|
||||
ID int
|
||||
Date time.Time
|
||||
Duration time.Duration
|
||||
RootName string
|
||||
Sizes memsize.Sizes
|
||||
}
|
||||
|
||||
type templateInfo struct {
|
||||
Roots []string
|
||||
Reports map[int]Report
|
||||
PathDepth int
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
func (ti *templateInfo) Link(path ...string) string {
|
||||
prefix := strings.Repeat("../", ti.PathDepth)
|
||||
return prefix + strings.Join(path, "")
|
||||
}
|
||||
|
||||
func (h *Handler) Add(name string, v interface{}) {
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Kind() != reflect.Ptr || rv.IsNil() {
|
||||
panic("root must be non-nil pointer")
|
||||
}
|
||||
h.mu.Lock()
|
||||
if h.roots == nil {
|
||||
h.roots = make(map[string]interface{})
|
||||
}
|
||||
h.roots[name] = v
|
||||
h.mu.Unlock()
|
||||
}
|
||||
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.init.Do(func() {
|
||||
h.reports = make(map[int]Report)
|
||||
h.mux.HandleFunc("/", h.handleRoot)
|
||||
h.mux.HandleFunc("/scan", h.handleScan)
|
||||
h.mux.HandleFunc("/report/", h.handleReport)
|
||||
})
|
||||
h.mux.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (h *Handler) templateInfo(r *http.Request, data interface{}) *templateInfo {
|
||||
h.mu.Lock()
|
||||
roots := make([]string, 0, len(h.roots))
|
||||
for name := range h.roots {
|
||||
roots = append(roots, name)
|
||||
}
|
||||
h.mu.Unlock()
|
||||
sort.Strings(roots)
|
||||
|
||||
return &templateInfo{
|
||||
Roots: roots,
|
||||
Reports: h.reports,
|
||||
PathDepth: strings.Count(r.URL.Path, "/") - 1,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) handleRoot(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
serveHTML(w, rootTemplate, http.StatusOK, h.templateInfo(r, nil))
|
||||
}
|
||||
|
||||
func (h *Handler) handleScan(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "invalid HTTP method, want POST", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
ti := h.templateInfo(r, "Unknown root")
|
||||
id, ok := h.scan(r.URL.Query().Get("root"))
|
||||
if !ok {
|
||||
serveHTML(w, notFoundTemplate, http.StatusNotFound, ti)
|
||||
return
|
||||
}
|
||||
w.Header().Add("Location", ti.Link(fmt.Sprintf("report/%d", id)))
|
||||
w.WriteHeader(http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (h *Handler) handleReport(w http.ResponseWriter, r *http.Request) {
|
||||
var id int
|
||||
fmt.Sscan(strings.TrimPrefix(r.URL.Path, "/report/"), &id)
|
||||
h.mu.Lock()
|
||||
report, ok := h.reports[id]
|
||||
h.mu.Unlock()
|
||||
|
||||
if !ok {
|
||||
serveHTML(w, notFoundTemplate, http.StatusNotFound, h.templateInfo(r, "Report not found"))
|
||||
} else {
|
||||
serveHTML(w, reportTemplate, http.StatusOK, h.templateInfo(r, report))
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) scan(root string) (int, bool) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
val, ok := h.roots[root]
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
id := h.reportID
|
||||
start := time.Now()
|
||||
sizes := memsize.Scan(val)
|
||||
h.reports[id] = Report{
|
||||
ID: id,
|
||||
RootName: root,
|
||||
Date: start.Truncate(1 * time.Second),
|
||||
Duration: time.Since(start),
|
||||
Sizes: sizes,
|
||||
}
|
||||
h.reportID++
|
||||
return id, true
|
||||
}
|
||||
|
||||
func serveHTML(w http.ResponseWriter, tpl *template.Template, status int, ti *templateInfo) {
|
||||
w.Header().Set("content-type", "text/html")
|
||||
var buf bytes.Buffer
|
||||
if err := tpl.Execute(&buf, ti); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
buf.WriteTo(w)
|
||||
}
|
Reference in New Issue
Block a user