mirror of
https://github.com/golang/go
synced 2024-11-08 11:06:21 -07:00
5bd66e5e1e
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>
172 lines
4.1 KiB
Go
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>
|
|
`))
|