diff options
| author | Hana Kim <hakim@google.com> | 2018-03-28 15:22:26 -0400 |
|---|---|---|
| committer | Hyang-Ah Hana Kim <hyangah@gmail.com> | 2018-03-29 17:56:54 +0000 |
| commit | 3ac17f86be1bb9b6a7cfde2b1dcef88bc5c2c1a6 (patch) | |
| tree | 9d61bd8e87e8fb50165a9898a555c0787ef1d7dd /src/cmd/trace/annotations.go | |
| parent | 9761a162f066911a76fb7b45c383490ca3bcdcc8 (diff) | |
| download | go-3ac17f86be1bb9b6a7cfde2b1dcef88bc5c2c1a6.tar.xz | |
cmd/trace: make span tables pretty
Mostly same as golang.org/cl/102156, except the parts that
deal with different data types.
Change-Id: I061b858b73898725e3bf175ed022c2e3e55fc485
Reviewed-on: https://go-review.googlesource.com/103158
Reviewed-by: Andrew Bonventre <andybons@golang.org>
Diffstat (limited to 'src/cmd/trace/annotations.go')
| -rw-r--r-- | src/cmd/trace/annotations.go | 160 |
1 files changed, 131 insertions, 29 deletions
diff --git a/src/cmd/trace/annotations.go b/src/cmd/trace/annotations.go index 35f0ee39d2..ffe8ed48ae 100644 --- a/src/cmd/trace/annotations.go +++ b/src/cmd/trace/annotations.go @@ -8,6 +8,7 @@ import ( "log" "math" "net/http" + "reflect" "sort" "strconv" "strings" @@ -111,21 +112,41 @@ func httpUserSpan(w http.ResponseWriter, r *http.Request) { var data []spanDesc + var maxTotal int64 for id, spans := range allSpans { for _, s := range spans { if !filter.match(id, s) { continue } data = append(data, s) + if maxTotal < s.TotalTime { + maxTotal = s.TotalTime + } } } + sortby := r.FormValue("sortby") + _, ok := reflect.TypeOf(spanDesc{}).FieldByNameFunc(func(s string) bool { + return s == sortby + }) + if !ok { + sortby = "TotalTime" + } + sort.Slice(data, func(i, j int) bool { + ival := reflect.ValueOf(data[i]).FieldByName(sortby).Int() + jval := reflect.ValueOf(data[j]).FieldByName(sortby).Int() + return ival > jval + }) + err = templUserSpanType.Execute(w, struct { - Data []spanDesc - Title string + MaxTotal int64 + Data []spanDesc + Name string }{ - Data: data, - Title: filter.name}) + MaxTotal: maxTotal, + Data: data, + Name: filter.name, + }) if err != nil { http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) return @@ -1113,40 +1134,121 @@ func isUserAnnotationEvent(ev *trace.Event) bool { return false } -var templUserSpanType = template.Must(template.New("").Parse(` -<html> -<body> -<h2>{{.Title}}</h2> -<table border="1" sortable="1"> +var templUserSpanType = template.Must(template.New("").Funcs(template.FuncMap{ + "prettyDuration": func(nsec int64) template.HTML { + d := time.Duration(nsec) * time.Nanosecond + return template.HTML(niceDuration(d)) + }, + "percent": func(dividened, divisor int64) template.HTML { + if divisor == 0 { + return "" + } + return template.HTML(fmt.Sprintf("(%.1f%%)", float64(dividened)/float64(divisor)*100)) + }, + "barLen": func(dividened, divisor int64) template.HTML { + if divisor == 0 { + return "0" + } + return template.HTML(fmt.Sprintf("%.2f%%", float64(dividened)/float64(divisor)*100)) + }, + "unknownTime": func(desc spanDesc) int64 { + sum := desc.ExecTime + desc.IOTime + desc.BlockTime + desc.SyscallTime + desc.SchedWaitTime + if sum < desc.TotalTime { + return desc.TotalTime - sum + } + return 0 + }, +}).Parse(` +<!DOCTYPE html> +<title>Goroutine {{.Name}}</title> +<style> +th { + background-color: #050505; + color: #fff; +} +table { + border-collapse: collapse; +} +.details tr:hover { + background-color: #f2f2f2; +} +.details td { + text-align: right; + border: 1px solid #000; +} +.details td.id { + text-align: left; +} +.stacked-bar-graph { + width: 300px; + height: 10px; + color: #414042; + white-space: nowrap; + font-size: 5px; +} +.stacked-bar-graph span { + display: inline-block; + width: 100%; + height: 100%; + box-sizing: border-box; + float: left; + padding: 0; +} +.unknown-time { background-color: #636363; } +.exec-time { background-color: #d7191c; } +.io-time { background-color: #fdae61; } +.block-time { background-color: #d01c8b; } +.syscall-time { background-color: #7b3294; } +.sched-time { background-color: #2c7bb6; } +</style> + +<script> +function reloadTable(key, value) { + let params = new URLSearchParams(window.location.search); + params.set(key, value); + window.location.search = params.toString(); +} +</script> + +<h2>{{.Name}}</h2> + +<table class="details"> <tr> <th> Goroutine </th> <th> Task </th> -<th> Total time, ns </th> -<th> Execution time, ns </th> -<th> Network wait time, ns </th> -<th> Sync block time, ns </th> -<th> Blocking syscall time, ns </th> -<th> Scheduler wait time, ns </th> -<th> GC sweeping time, ns </th> -<th> GC pause time, ns </th> -<th> Logs </th> +<th onclick="reloadTable('sortby', 'TotalTime')"> Total</th> +<th></th> +<th onclick="reloadTable('sortby', 'ExecTime')" class="exec-time"> Execution</th> +<th onclick="reloadTable('sortby', 'IOTime')" class="io-time"> Network wait</th> +<th onclick="reloadTable('sortby', 'BlockTime')" class="block-time"> Sync block </th> +<th onclick="reloadTable('sortby', 'SyscallTime')" class="syscall-time"> Blocking syscall</th> +<th onclick="reloadTable('sortby', 'SchedWaitTime')" class="sched-time"> Scheduler wait</th> +<th onclick="reloadTable('sortby', 'SweepTime')"> GC sweeping</th> +<th onclick="reloadTable('sortby', 'GCTime')"> GC pause</th> </tr> {{range .Data}} <tr> <td> <a href="/trace?goid={{.G}}">{{.G}}</a> </td> <td> <a href="/trace?taskid={{.TaskID}}">{{.TaskID}}</a> </td> - <td> {{.TotalTime}} </td> - <td> {{.ExecTime}} </td> - <td> {{.IOTime}} </td> - <td> {{.BlockTime}} </td> - <td> {{.SyscallTime}} </td> - <td> {{.SchedWaitTime}} </td> - <td> {{.SweepTime}} </td> - <td> {{.GCTime}} </td> - <td> /* TODO */ </td> + <td> {{prettyDuration .TotalTime}} </td> + <td> + <div class="stacked-bar-graph"> + {{if unknownTime .}}<span style="width:{{barLen (unknownTime .) $.MaxTotal}}" class="unknown-time"> </span>{{end}} + {{if .ExecTime}}<span style="width:{{barLen .ExecTime $.MaxTotal}}" class="exec-time"> </span>{{end}} + {{if .IOTime}}<span style="width:{{barLen .IOTime $.MaxTotal}}" class="io-time"> </span>{{end}} + {{if .BlockTime}}<span style="width:{{barLen .BlockTime $.MaxTotal}}" class="block-time"> </span>{{end}} + {{if .SyscallTime}}<span style="width:{{barLen .SyscallTime $.MaxTotal}}" class="syscall-time"> </span>{{end}} + {{if .SchedWaitTime}}<span style="width:{{barLen .SchedWaitTime $.MaxTotal}}" class="sched-time"> </span>{{end}} + </div> + </td> + <td> {{prettyDuration .ExecTime}}</td> + <td> {{prettyDuration .IOTime}}</td> + <td> {{prettyDuration .BlockTime}}</td> + <td> {{prettyDuration .SyscallTime}}</td> + <td> {{prettyDuration .SchedWaitTime}}</td> + <td> {{prettyDuration .SweepTime}} {{percent .SweepTime .TotalTime}}</td> + <td> {{prettyDuration .GCTime}} {{percent .GCTime .TotalTime}}</td> </tr> {{end}} </table> -</body> -</html> `)) |
