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