github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/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"
    14  	"io/ioutil"
    15  	"net/http"
    16  	"os"
    17  	"os/exec"
    18  
    19  	"github.com/google/pprof/profile"
    20  )
    21  
    22  func init() {
    23  	http.HandleFunc("/io", serveSVGProfile(pprofIO))
    24  	http.HandleFunc("/block", serveSVGProfile(pprofBlock))
    25  	http.HandleFunc("/syscall", serveSVGProfile(pprofSyscall))
    26  	http.HandleFunc("/sched", serveSVGProfile(pprofSched))
    27  }
    28  
    29  // Record represents one entry in pprof-like profiles.
    30  type Record struct {
    31  	stk  []*trace.Frame
    32  	n    uint64
    33  	time int64
    34  }
    35  
    36  // pprofIO generates IO pprof-like profile (time spent in IO wait).
    37  func pprofIO(w io.Writer) error {
    38  	events, err := parseEvents()
    39  	if err != nil {
    40  		return err
    41  	}
    42  	prof := make(map[uint64]Record)
    43  	for _, ev := range events {
    44  		if ev.Type != trace.EvGoBlockNet || ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
    45  			continue
    46  		}
    47  		rec := prof[ev.StkID]
    48  		rec.stk = ev.Stk
    49  		rec.n++
    50  		rec.time += ev.Link.Ts - ev.Ts
    51  		prof[ev.StkID] = rec
    52  	}
    53  	return buildProfile(prof).Write(w)
    54  }
    55  
    56  // pprofBlock generates blocking pprof-like profile (time spent blocked on synchronization primitives).
    57  func pprofBlock(w io.Writer) error {
    58  	events, err := parseEvents()
    59  	if err != nil {
    60  		return err
    61  	}
    62  	prof := make(map[uint64]Record)
    63  	for _, ev := range events {
    64  		switch ev.Type {
    65  		case trace.EvGoBlockSend, trace.EvGoBlockRecv, trace.EvGoBlockSelect,
    66  			trace.EvGoBlockSync, trace.EvGoBlockCond, trace.EvGoBlockGC:
    67  		default:
    68  			continue
    69  		}
    70  		if ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
    71  			continue
    72  		}
    73  		rec := prof[ev.StkID]
    74  		rec.stk = ev.Stk
    75  		rec.n++
    76  		rec.time += ev.Link.Ts - ev.Ts
    77  		prof[ev.StkID] = rec
    78  	}
    79  	return buildProfile(prof).Write(w)
    80  }
    81  
    82  // pprofSyscall generates syscall pprof-like profile (time spent blocked in syscalls).
    83  func pprofSyscall(w io.Writer) error {
    84  	events, err := parseEvents()
    85  	if err != nil {
    86  		return err
    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  	return buildProfile(prof).Write(w)
   100  }
   101  
   102  // pprofSched generates scheduler latency pprof-like profile
   103  // (time between a goroutine become runnable and actually scheduled for execution).
   104  func pprofSched(w io.Writer) error {
   105  	events, err := parseEvents()
   106  	if err != nil {
   107  		return err
   108  	}
   109  	prof := make(map[uint64]Record)
   110  	for _, ev := range events {
   111  		if (ev.Type != trace.EvGoUnblock && ev.Type != trace.EvGoCreate) ||
   112  			ev.Link == nil || ev.StkID == 0 || len(ev.Stk) == 0 {
   113  			continue
   114  		}
   115  		rec := prof[ev.StkID]
   116  		rec.stk = ev.Stk
   117  		rec.n++
   118  		rec.time += ev.Link.Ts - ev.Ts
   119  		prof[ev.StkID] = rec
   120  	}
   121  	return buildProfile(prof).Write(w)
   122  }
   123  
   124  // serveSVGProfile serves pprof-like profile generated by prof as svg.
   125  func serveSVGProfile(prof func(w io.Writer) error) http.HandlerFunc {
   126  	return func(w http.ResponseWriter, r *http.Request) {
   127  		blockf, err := ioutil.TempFile("", "block")
   128  		if err != nil {
   129  			http.Error(w, fmt.Sprintf("failed to create temp file: %v", err), http.StatusInternalServerError)
   130  			return
   131  		}
   132  		defer func() {
   133  			blockf.Close()
   134  			os.Remove(blockf.Name())
   135  		}()
   136  		blockb := bufio.NewWriter(blockf)
   137  		if err := prof(blockb); err != nil {
   138  			http.Error(w, fmt.Sprintf("failed to generate profile: %v", err), http.StatusInternalServerError)
   139  			return
   140  		}
   141  		if err := blockb.Flush(); err != nil {
   142  			http.Error(w, fmt.Sprintf("failed to flush temp file: %v", err), http.StatusInternalServerError)
   143  			return
   144  		}
   145  		if err := blockf.Close(); err != nil {
   146  			http.Error(w, fmt.Sprintf("failed to close temp file: %v", err), http.StatusInternalServerError)
   147  			return
   148  		}
   149  		svgFilename := blockf.Name() + ".svg"
   150  		if output, err := exec.Command("go", "tool", "pprof", "-svg", "-output", svgFilename, blockf.Name()).CombinedOutput(); err != nil {
   151  			http.Error(w, fmt.Sprintf("failed to execute go tool pprof: %v\n%s", err, output), http.StatusInternalServerError)
   152  			return
   153  		}
   154  		defer os.Remove(svgFilename)
   155  		w.Header().Set("Content-Type", "image/svg+xml")
   156  		http.ServeFile(w, r, svgFilename)
   157  	}
   158  }
   159  
   160  func buildProfile(prof map[uint64]Record) *profile.Profile {
   161  	p := &profile.Profile{
   162  		PeriodType: &profile.ValueType{Type: "trace", Unit: "count"},
   163  		Period:     1,
   164  		SampleType: []*profile.ValueType{
   165  			{Type: "contentions", Unit: "count"},
   166  			{Type: "delay", Unit: "nanoseconds"},
   167  		},
   168  	}
   169  	locs := make(map[uint64]*profile.Location)
   170  	funcs := make(map[string]*profile.Function)
   171  	for _, rec := range prof {
   172  		var sloc []*profile.Location
   173  		for _, frame := range rec.stk {
   174  			loc := locs[frame.PC]
   175  			if loc == nil {
   176  				fn := funcs[frame.File+frame.Fn]
   177  				if fn == nil {
   178  					fn = &profile.Function{
   179  						ID:         uint64(len(p.Function) + 1),
   180  						Name:       frame.Fn,
   181  						SystemName: frame.Fn,
   182  						Filename:   frame.File,
   183  					}
   184  					p.Function = append(p.Function, fn)
   185  					funcs[frame.File+frame.Fn] = fn
   186  				}
   187  				loc = &profile.Location{
   188  					ID:      uint64(len(p.Location) + 1),
   189  					Address: frame.PC,
   190  					Line: []profile.Line{
   191  						profile.Line{
   192  							Function: fn,
   193  							Line:     int64(frame.Line),
   194  						},
   195  					},
   196  				}
   197  				p.Location = append(p.Location, loc)
   198  				locs[frame.PC] = loc
   199  			}
   200  			sloc = append(sloc, loc)
   201  		}
   202  		p.Sample = append(p.Sample, &profile.Sample{
   203  			Value:    []int64{int64(rec.n), rec.time},
   204  			Location: sloc,
   205  		})
   206  	}
   207  	return p
   208  }