github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/cmd/trace/pprof.go (about)

     1  // Copyright 2014 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Serving of pprof-like profiles.
     6  
     7  package main
     8  
     9  import (
    10  	"bufio"
    11  	"fmt"
    12  	"internal/trace"
    13  	"io/ioutil"
    14  	"net/http"
    15  	"os"
    16  	"os/exec"
    17  )
    18  
    19  func init() {
    20  	http.HandleFunc("/io", httpIO)
    21  	http.HandleFunc("/block", httpBlock)
    22  	http.HandleFunc("/syscall", httpSyscall)
    23  	http.HandleFunc("/sched", httpSched)
    24  }
    25  
    26  // Record represents one entry in pprof-like profiles.
    27  type Record struct {
    28  	stk  []*trace.Frame
    29  	n    uint64
    30  	time int64
    31  }
    32  
    33  // httpIO serves IO pprof-like profile (time spent in IO wait).
    34  func httpIO(w http.ResponseWriter, r *http.Request) {
    35  	events, err := parseEvents()
    36  	if err != nil {
    37  		http.Error(w, err.Error(), http.StatusInternalServerError)
    38  		return
    39  	}
    40  	prof := make(map[uint64]Record)
    41  	for _, ev := range events {
    42  		if ev.Type != trace.EvGoBlockNet || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
    43  			continue
    44  		}
    45  		rec := prof[ev.StkID]
    46  		rec.stk = ev.Stk
    47  		rec.n++
    48  		rec.time += ev.Link.Ts - ev.Ts
    49  		prof[ev.StkID] = rec
    50  	}
    51  	serveSVGProfile(w, r, prof)
    52  }
    53  
    54  // httpBlock serves blocking pprof-like profile (time spent blocked on synchronization primitives).
    55  func httpBlock(w http.ResponseWriter, r *http.Request) {
    56  	events, err := parseEvents()
    57  	if err != nil {
    58  		http.Error(w, err.Error(), http.StatusInternalServerError)
    59  		return
    60  	}
    61  	prof := make(map[uint64]Record)
    62  	for _, ev := range events {
    63  		switch ev.Type {
    64  		case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect,
    65  			trace.EvGoBlockSync, trace.EvGoBlockCond:
    66  		default:
    67  			continue
    68  		}
    69  		if ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
    70  			continue
    71  		}
    72  		rec := prof[ev.StkID]
    73  		rec.stk = ev.Stk
    74  		rec.n++
    75  		rec.time += ev.Link.Ts - ev.Ts
    76  		prof[ev.StkID] = rec
    77  	}
    78  	serveSVGProfile(w, r, prof)
    79  }
    80  
    81  // httpSyscall serves syscall pprof-like profile (time spent blocked in syscalls).
    82  func httpSyscall(w http.ResponseWriter, r *http.Request) {
    83  	events, err := parseEvents()
    84  	if err != nil {
    85  		http.Error(w, err.Error(), http.StatusInternalServerError)
    86  		return
    87  	}
    88  	prof := make(map[uint64]Record)
    89  	for _, ev := range events {
    90  		if ev.Type != trace.EvGoSysCall || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
    91  			continue
    92  		}
    93  		rec := prof[ev.StkID]
    94  		rec.stk = ev.Stk
    95  		rec.n++
    96  		rec.time += ev.Link.Ts - ev.Ts
    97  		prof[ev.StkID] = rec
    98  	}
    99  	serveSVGProfile(w, r, prof)
   100  }
   101  
   102  // httpSched serves scheduler latency pprof-like profile
   103  // (time between a goroutine become runnable and actually scheduled for execution).
   104  func httpSched(w http.ResponseWriter, r *http.Request) {
   105  	events, err := parseEvents()
   106  	if err != nil {
   107  		http.Error(w, err.Error(), http.StatusInternalServerError)
   108  		return
   109  	}
   110  	prof := make(map[uint64]Record)
   111  	for _, ev := range events {
   112  		if (ev.Type != trace.EvGoUnblock && ev.Type != trace.EvGoCreate) ||
   113  			ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
   114  			continue
   115  		}
   116  		rec := prof[ev.StkID]
   117  		rec.stk = ev.Stk
   118  		rec.n++
   119  		rec.time += ev.Link.Ts - ev.Ts
   120  		prof[ev.StkID] = rec
   121  	}
   122  	serveSVGProfile(w, r, prof)
   123  }
   124  
   125  // generateSVGProfile generates pprof-like profile stored in prof and writes in to w.
   126  func serveSVGProfile(w http.ResponseWriter, r *http.Request, prof map[uint64]Record) {
   127  	if len(prof) == 0 {
   128  		http.Error(w, "The profile is empty", http.StatusNotFound)
   129  		return
   130  	}
   131  	blockf, err := ioutil.TempFile("", "block")
   132  	if err != nil {
   133  		http.Error(w, fmt.Sprintf("failed to create temp file: %v", err), http.StatusInternalServerError)
   134  		return
   135  	}
   136  	defer os.Remove(blockf.Name())
   137  	blockb := bufio.NewWriter(blockf)
   138  	fmt.Fprintf(blockb, "--- contention:\ncycles/second=1000000000\n")
   139  	for _, rec := range prof {
   140  		fmt.Fprintf(blockb, "%v %v @", rec.time, rec.n)
   141  		for _, f := range rec.stk {
   142  			fmt.Fprintf(blockb, " 0x%x", f.PC)
   143  		}
   144  		fmt.Fprintf(blockb, "\n")
   145  	}
   146  	err = blockb.Flush()
   147  	if err != nil {
   148  		http.Error(w, fmt.Sprintf("failed to flush temp file: %v", err), http.StatusInternalServerError)
   149  		return
   150  	}
   151  	err = blockf.Close()
   152  	if err != nil {
   153  		http.Error(w, fmt.Sprintf("failed to close temp file: %v", err), http.StatusInternalServerError)
   154  		return
   155  	}
   156  
   157  	svgFilename := blockf.Name() + ".svg"
   158  	_, err = exec.Command("go", "tool", "pprof", "-svg", "-output", svgFilename, programBinary, blockf.Name()).CombinedOutput()
   159  	if err != nil {
   160  		http.Error(w, fmt.Sprintf("failed to execute go tool pprof: %v", err), http.StatusInternalServerError)
   161  		return
   162  	}
   163  	defer os.Remove(svgFilename)
   164  	w.Header().Set("Content-Type", "image/svg+xml")
   165  	http.ServeFile(w, r, svgFilename)
   166  }