1
0
mirror of https://github.com/golang/go synced 2024-11-08 11:06:21 -07:00
go/src/cmd/trace/goroutines.go
Hana (Hyang-Ah) Kim 5bd66e5e1e cmd/trace: compute pprof-style output per goroutine type
The trace command computes IO, Schedule, Block, and Syscall profiles
by following the unblocking links in the execution trace and summing
up the duration.  This change offers variations of those profiles
that include only selected goroutine types. The id parameter takes the
goroutine type - i.e. pc of the goroutine.

The output is available from the /goroutine view. So, users can see
where the goroutines of interest typically block.

Also, these profiles are available for download so users can use
pprof or other tools to interpret the output. This change adds links
for download of global profile in the main page.

Change-Id: I35699252056d164e60de282b0406caf96d629c85
Reviewed-on: https://go-review.googlesource.com/75710
Reviewed-by: Sameer Ajmani <sameer@golang.org>
2017-11-30 19:34:32 +00:00

172 lines
4.1 KiB
Go

// Copyright 2014 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.
// Goroutine-related profiles.
package main
import (
"fmt"
"html/template"
"internal/trace"
"net/http"
"sort"
"strconv"
"sync"
)
func init() {
http.HandleFunc("/goroutines", httpGoroutines)
http.HandleFunc("/goroutine", httpGoroutine)
}
// gtype describes a group of goroutines grouped by start PC.
type gtype struct {
ID uint64 // Unique identifier (PC).
Name string // Start function.
N int // Total number of goroutines in this group.
ExecTime int64 // Total execution time of all goroutines in this group.
}
type gtypeList []gtype
func (l gtypeList) Len() int {
return len(l)
}
func (l gtypeList) Less(i, j int) bool {
return l[i].ExecTime > l[j].ExecTime
}
func (l gtypeList) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
type gdescList []*trace.GDesc
func (l gdescList) Len() int {
return len(l)
}
func (l gdescList) Less(i, j int) bool {
return l[i].TotalTime > l[j].TotalTime
}
func (l gdescList) Swap(i, j int) {
l[i], l[j] = l[j], l[i]
}
var (
gsInit sync.Once
gs map[uint64]*trace.GDesc
)
// analyzeGoroutines generates statistics about execution of all goroutines and stores them in gs.
func analyzeGoroutines(events []*trace.Event) {
gsInit.Do(func() {
gs = trace.GoroutineStats(events)
})
}
// httpGoroutines serves list of goroutine groups.
func httpGoroutines(w http.ResponseWriter, r *http.Request) {
events, err := parseEvents()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
analyzeGoroutines(events)
gss := make(map[uint64]gtype)
for _, g := range gs {
gs1 := gss[g.PC]
gs1.ID = g.PC
gs1.Name = g.Name
gs1.N++
gs1.ExecTime += g.ExecTime
gss[g.PC] = gs1
}
var glist gtypeList
for k, v := range gss {
v.ID = k
glist = append(glist, v)
}
sort.Sort(glist)
templGoroutines.Execute(w, glist)
}
var templGoroutines = template.Must(template.New("").Parse(`
<html>
<body>
Goroutines: <br>
{{range $}}
<a href="/goroutine?id={{.ID}}">{{.Name}}</a> N={{.N}} <br>
{{end}}
</body>
</html>
`))
// httpGoroutine serves list of goroutines in a particular group.
func httpGoroutine(w http.ResponseWriter, r *http.Request) {
events, err := parseEvents()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
pc, err := strconv.ParseUint(r.FormValue("id"), 10, 64)
if err != nil {
http.Error(w, fmt.Sprintf("failed to parse id parameter '%v': %v", r.FormValue("id"), err), http.StatusInternalServerError)
return
}
analyzeGoroutines(events)
var glist gdescList
for _, g := range gs {
if g.PC != pc {
continue
}
glist = append(glist, g)
}
sort.Sort(glist)
err = templGoroutine.Execute(w, struct {
PC uint64
GList gdescList
}{pc, glist})
if err != nil {
http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError)
return
}
}
var templGoroutine = template.Must(template.New("").Parse(`
<html>
<body>
<table border="1" sortable="1">
<tr>
<th> Goroutine </th>
<th> Total time, ns </th>
<th> Execution time, ns </th>
<th> <a href="/io?id={{.PC}}">Network wait time, ns</a><a href="/io?id={{.PC}}&raw=1" download="io.profile">⬇</a> </th>
<th> <a href="/block?id={{.PC}}">Sync block time, ns</a><a href="/block?id={{.PC}}&raw=1" download="block.profile">⬇</a> </th>
<th> <a href="/syscall?id={{.PC}}">Blocking syscall time, ns</a><a href="/syscall?id={{.PC}}&raw=1" download="syscall.profile">⬇</a> </th>
<th> <a href="/sched?id={{.PC}}">Scheduler wait time, ns</a><a href="/sched?id={{.PC}}&raw=1" download="sched.profile">⬇</a> </th>
<th> GC sweeping time, ns </th>
<th> GC pause time, ns </th>
</tr>
{{range .GList}}
<tr>
<td> <a href="/trace?goid={{.ID}}">{{.ID}}</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>
</tr>
{{end}}
</table>
</body>
</html>
`))