// 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> `))