analyzeGoroutines(events)
var glist gdescList
for _, g := range gs {
- if g.PC != pc || g.ExecTime == 0 {
+ if g.PC != pc {
continue
}
glist = append(glist, g)
}
sort.Sort(glist)
- err = templGoroutine.Execute(w, 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
<th> Goroutine </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> <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 $}}
+{{range .GList}}
<tr>
<td> <a href="/trace?goid={{.ID}}">{{.ID}}</a> </td>
<td> {{.TotalTime}} </td>
"os/exec"
"path/filepath"
"runtime"
+ "strconv"
"github.com/google/pprof/profile"
)
time int64
}
-// pprofIO generates IO pprof-like profile (time spent in IO wait).
-func pprofIO(w io.Writer) error {
+// pprofMatchingGoroutines parses the goroutine type id string (i.e. pc)
+// and returns the ids of goroutines of the matching type.
+// If the id string is empty, returns nil without an error.
+func pprofMatchingGoroutines(id string, events []*trace.Event) (map[uint64]bool, error) {
+ if id == "" {
+ return nil, nil
+ }
+ pc, err := strconv.ParseUint(id, 10, 64) // id is string
+ if err != nil {
+ return nil, fmt.Errorf("invalid goroutine type: %v", id)
+ }
+ analyzeGoroutines(events)
+ var res map[uint64]bool
+ for _, g := range gs {
+ if g.PC != pc {
+ continue
+ }
+ if res == nil {
+ res = make(map[uint64]bool)
+ }
+ res[g.ID] = true
+ }
+ if len(res) == 0 && id != "" {
+ return nil, fmt.Errorf("failed to find matching goroutines for id: %s", id)
+ }
+ return res, nil
+}
+
+// pprofIO generates IO pprof-like profile (time spent in IO wait,
+// currently only network blocking event).
+func pprofIO(w io.Writer, id string) error {
events, err := parseEvents()
if err != nil {
return err
}
+ goroutines, err := pprofMatchingGoroutines(id, events)
+ if err != nil {
+ return err
+ }
+
prof := make(map[uint64]Record)
for _, ev := range events {
if ev.Type != trace.EvGoBlockNet || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
continue
}
+ if goroutines != nil && !goroutines[ev.G] {
+ continue
+ }
rec := prof[ev.StkID]
rec.stk = ev.Stk
rec.n++
}
// pprofBlock generates blocking pprof-like profile (time spent blocked on synchronization primitives).
-func pprofBlock(w io.Writer) error {
+func pprofBlock(w io.Writer, id string) error {
events, err := parseEvents()
if err != nil {
return err
}
+ goroutines, err := pprofMatchingGoroutines(id, events)
+ if err != nil {
+ return err
+ }
+
prof := make(map[uint64]Record)
for _, ev := range events {
switch ev.Type {
case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect,
trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockGC:
+ // TODO(hyangah): figure out why EvGoBlockGC should be here.
+ // EvGoBlockGC indicates the goroutine blocks on GC assist, not
+ // on synchronization primitives.
default:
continue
}
if ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
continue
}
+ if goroutines != nil && !goroutines[ev.G] {
+ continue
+ }
rec := prof[ev.StkID]
rec.stk = ev.Stk
rec.n++
}
// pprofSyscall generates syscall pprof-like profile (time spent blocked in syscalls).
-func pprofSyscall(w io.Writer) error {
+func pprofSyscall(w io.Writer, id string) error {
+
events, err := parseEvents()
if err != nil {
return err
}
+ goroutines, err := pprofMatchingGoroutines(id, events)
+ if err != nil {
+ return err
+ }
+
prof := make(map[uint64]Record)
for _, ev := range events {
if ev.Type != trace.EvGoSysCall || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
continue
}
+ if goroutines != nil && !goroutines[ev.G] {
+ continue
+ }
rec := prof[ev.StkID]
rec.stk = ev.Stk
rec.n++
// pprofSched generates scheduler latency pprof-like profile
// (time between a goroutine become runnable and actually scheduled for execution).
-func pprofSched(w io.Writer) error {
+func pprofSched(w io.Writer, id string) error {
events, err := parseEvents()
if err != nil {
return err
}
+ goroutines, err := pprofMatchingGoroutines(id, events)
+ if err != nil {
+ return err
+ }
+
prof := make(map[uint64]Record)
for _, ev := range events {
if (ev.Type != trace.EvGoUnblock && ev.Type != trace.EvGoCreate) ||
ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
continue
}
+ if goroutines != nil && !goroutines[ev.G] {
+ continue
+ }
rec := prof[ev.StkID]
rec.stk = ev.Stk
rec.n++
}
// serveSVGProfile serves pprof-like profile generated by prof as svg.
-func serveSVGProfile(prof func(w io.Writer) error) http.HandlerFunc {
+func serveSVGProfile(prof func(w io.Writer, id string) error) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
+
+ if r.FormValue("raw") != "" {
+ w.Header().Set("Content-Type", "application/octet-stream")
+ if err := prof(w, r.FormValue("id")); err != nil {
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ w.Header().Set("X-Go-Pprof", "1")
+ http.Error(w, fmt.Sprintf("failed to get profile: %v", err), http.StatusInternalServerError)
+ return
+ }
+ return
+ }
+
blockf, err := ioutil.TempFile("", "block")
if err != nil {
http.Error(w, fmt.Sprintf("failed to create temp file: %v", err), http.StatusInternalServerError)
os.Remove(blockf.Name())
}()
blockb := bufio.NewWriter(blockf)
- if err := prof(blockb); err != nil {
+ if err := prof(blockb, r.FormValue("id")); err != nil {
http.Error(w, fmt.Sprintf("failed to generate profile: %v", err), http.StatusInternalServerError)
return
}