This repository was archived by the owner on Sep 6, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathblock.go
87 lines (77 loc) · 2.94 KB
/
block.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package godeltaprof
import (
"github.com/pyroscope-io/godeltaprof/internal/pprof"
"io"
"runtime"
"sort"
"sync"
)
// BlockProfiler is a stateful profiler for goroutine blocking events and mutex contention in Go programs.
// Depending on the function used to create the BlockProfiler, it uses either runtime.BlockProfile or runtime.MutexProfile.
// The BlockProfiler provides similar functionality to pprof.Lookup("block").WriteTo and pprof.Lookup("mutex").WriteTo,
// but with some key differences.
//
// The BlockProfiler tracks the delta of blocking events or mutex contention since the last
// profile was written, effectively providing a snapshot of the changes
// between two points in time. This is in contrast to the
// pprof.Lookup functions, which accumulate profiling data
// and result in profiles that represent the entire lifetime of the program.
//
// The BlockProfiler is safe for concurrent use, as it serializes access to
// its internal state using a sync.Mutex. This ensures that multiple goroutines
// can call the Profile method without causing any data race issues.
type BlockProfiler struct {
impl pprof.DeltaMutexProfiler
mutex sync.Mutex
runtimeProfile func([]runtime.BlockProfileRecord) (int, bool)
scaleProfile func(int64, float64) (int64, float64)
}
// NewMutexProfiler creates a new BlockProfiler instance for profiling mutex contention.
// The resulting BlockProfiler uses runtime.MutexProfile as its data source.
//
// Usage:
//
// mp := godeltaprof.NewMutexProfiler()
// ...
// err := mp.Profile(someWriter)
func NewMutexProfiler() *BlockProfiler {
return &BlockProfiler{runtimeProfile: runtime.MutexProfile, scaleProfile: scaleMutexProfile}
}
// NewBlockProfiler creates a new BlockProfiler instance for profiling goroutine blocking events.
// The resulting BlockProfiler uses runtime.BlockProfile as its data source.
//
// Usage:
//
// bp := godeltaprof.NewBlockProfiler()
// ...
// err := bp.Profile(someWriter)
func NewBlockProfiler() *BlockProfiler {
return &BlockProfiler{runtimeProfile: runtime.BlockProfile, scaleProfile: scaleBlockProfile}
}
func (d *BlockProfiler) Profile(w io.Writer) error {
d.mutex.Lock()
defer d.mutex.Unlock()
var p []runtime.BlockProfileRecord
n, ok := d.runtimeProfile(nil)
for {
p = make([]runtime.BlockProfileRecord, n+50)
n, ok = d.runtimeProfile(p)
if ok {
p = p[:n]
break
}
}
sort.Slice(p, func(i, j int) bool { return p[i].Cycles > p[j].Cycles })
return d.impl.PrintCountCycleProfile(w, "contentions", "delay", d.scaleProfile, p)
}
func scaleMutexProfile(cnt int64, ns float64) (int64, float64) {
period := runtime.SetMutexProfileFraction(-1)
return cnt * int64(period), ns * float64(period)
}
func scaleBlockProfile(cnt int64, ns float64) (int64, float64) {
// Do nothing.
// The current way of block profile sampling makes it
// hard to compute the unsampled number. The legacy block
// profile parse doesn't attempt to scale or unsample.
return cnt, ns
}