github.com/lrita/cache@v1.0.1/pprof.go (about)

     1  // Copyright 2018 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  package cache
     6  
     7  import (
     8  	"bufio"
     9  	"fmt"
    10  	"io"
    11  	"runtime"
    12  	"runtime/pprof"
    13  	"sort"
    14  	"sync"
    15  	"sync/atomic"
    16  	"text/tabwriter"
    17  	"unsafe"
    18  )
    19  
    20  var (
    21  	profilerate int64 // fraction sampled
    22  	profile     = pprof.NewProfile("github.com/lrita/cache")
    23  
    24  	evtsmu sync.Mutex
    25  	events map[runtime.StackRecord]*runtime.BlockProfileRecord
    26  )
    27  
    28  func init() {
    29  	type profilehook struct {
    30  		name  string
    31  		mu    sync.Mutex
    32  		m     map[interface{}][]uintptr
    33  		count func() int
    34  		write func(io.Writer, int) error
    35  	}
    36  	(*profilehook)(unsafe.Pointer(profile)).count = cacheProfileCount
    37  	(*profilehook)(unsafe.Pointer(profile)).write = cacheProfileWrite
    38  }
    39  
    40  func cacheProfileCount() int {
    41  	evtsmu.Lock()
    42  	defer evtsmu.Unlock()
    43  	return len(events)
    44  }
    45  
    46  func cacheProfileWrite(w io.Writer, debug int) error {
    47  	evtsmu.Lock()
    48  	p := make([]runtime.BlockProfileRecord, 0, len(events))
    49  	for _, evt := range events {
    50  		p = append(p, *evt)
    51  	}
    52  	evtsmu.Unlock()
    53  
    54  	sort.Slice(p, func(i, j int) bool { return p[i].Cycles > p[j].Cycles })
    55  
    56  	if debug <= 0 {
    57  		return printCountCycleProfile(w, "count", "missing", scaleNothing, p)
    58  	}
    59  
    60  	b := bufio.NewWriter(w)
    61  	tw := tabwriter.NewWriter(w, 1, 8, 1, '\t', 0)
    62  	w = tw
    63  
    64  	fmt.Fprintf(w, "--- github.com/lrita/cache:\n")
    65  	fmt.Fprintf(w, "cycles/second=%v\n", 1)
    66  	fmt.Fprintf(w, "sampling period=%d\n", SetProfileFraction(-1))
    67  	for i := range p {
    68  		r := &p[i]
    69  		fmt.Fprintf(w, "%v %v @", r.Cycles, r.Count)
    70  		for _, pc := range r.Stack() {
    71  			fmt.Fprintf(w, " %#x", pc)
    72  		}
    73  		fmt.Fprint(w, "\n")
    74  		if debug > 0 {
    75  			printStackRecord(w, r.Stack(), true)
    76  		}
    77  	}
    78  
    79  	if tw != nil {
    80  		tw.Flush()
    81  	}
    82  	return b.Flush()
    83  }
    84  
    85  // SetProfileFraction controls the fraction of cache get missing events
    86  // that are reported in the "github.com/lrita/cache" profile. On average
    87  // 1/rate events are reported. The previous rate is returned.
    88  //
    89  // To turn off profiling entirely, pass rate 0.
    90  // To just read the current rate, pass rate < 0.
    91  // (For n>1 the details of sampling may change.)
    92  func SetProfileFraction(rate int) int {
    93  	if rate < 0 {
    94  		return int(atomic.LoadInt64(&profilerate))
    95  	}
    96  	old := int(atomic.SwapInt64(&profilerate, int64(rate)))
    97  	if rate == 0 {
    98  		// clean last profiling record.
    99  		evtsmu.Lock()
   100  		events = nil
   101  		evtsmu.Unlock()
   102  	}
   103  	return old
   104  }
   105  
   106  func getmissingevent() {
   107  	rate := atomic.LoadInt64(&profilerate)
   108  	if rate <= 0 || int64(fastrand())%rate != 0 {
   109  		return
   110  	}
   111  
   112  	var stk runtime.StackRecord
   113  	if nstk := runtime.Callers(3, stk.Stack0[:]); nstk == 0 {
   114  		return
   115  	}
   116  
   117  	evtsmu.Lock()
   118  	defer evtsmu.Unlock()
   119  	evt, ok := events[stk]
   120  	if !ok {
   121  		evt = &runtime.BlockProfileRecord{StackRecord: stk}
   122  		if events == nil {
   123  			events = make(map[runtime.StackRecord]*runtime.BlockProfileRecord)
   124  		}
   125  		events[stk] = evt
   126  	}
   127  	evt.Count++
   128  	evt.Cycles++
   129  }
   130  
   131  func scaleNothing(cnt int64, ns float64) (int64, float64) { return cnt, ns }
   132  
   133  // from runtime
   134  //go:linkname fastrand runtime.fastrand
   135  func fastrand() uint32
   136  
   137  //go:linkname printStackRecord runtime/pprof.printStackRecord
   138  func printStackRecord(io.Writer, []uintptr, bool)
   139  
   140  //go:linkname printCountCycleProfile runtime/pprof.printCountCycleProfile
   141  func printCountCycleProfile(io.Writer, string, string,
   142  	func(int64, float64) (int64, float64), []runtime.BlockProfileRecord) error