-
Notifications
You must be signed in to change notification settings - Fork 5
/
sample.go
157 lines (134 loc) · 4.08 KB
/
sample.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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package wzprof
import (
"context"
"math"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental"
)
// Flag returns a function listener factory which creates listeners where
// calls to their Before/After methods are gated by the boolean flag pointed
// at by the first argument.
//
// The sampling mechanism is similar to the one implemented by Sample but it
// gives the application control over when the listeners are enabled instead
// of leaving the selection up to a probabilistic model.
func Flag(flag *bool, factory experimental.FunctionListenerFactory) experimental.FunctionListenerFactory {
return experimental.FunctionListenerFactoryFunc(func(def api.FunctionDefinition) experimental.FunctionListener {
lstn := factory.NewFunctionListener(def)
if lstn == nil {
return nil
}
flagged := &flaggedFunctionListener{
flag: flag,
lstn: lstn,
}
flagged.stack.bits = flagged.bits[:]
return flagged
})
}
type flaggedFunctionListener struct {
flag *bool
bits [1]uint64
stack bitstack
lstn experimental.FunctionListener
}
func (s *flaggedFunctionListener) Before(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, stack experimental.StackIterator) {
bit := uint(0)
if *s.flag {
s.lstn.Before(ctx, mod, def, params, stack)
bit = 1
}
s.stack.push(bit)
}
func (s *flaggedFunctionListener) After(ctx context.Context, mod api.Module, def api.FunctionDefinition, results []uint64) {
if s.stack.pop() != 0 {
s.lstn.After(ctx, mod, def, results)
}
}
func (s *flaggedFunctionListener) Abort(ctx context.Context, mod api.Module, def api.FunctionDefinition, err error) {
if s.stack.pop() != 0 {
s.lstn.Abort(ctx, mod, def, err)
}
}
// Sample returns a function listener factory which creates listeners where
// calls to their Before/After methods is sampled at the given sample rate.
//
// Giving a zero or negative sampling rate disables the function listeners
// entirely.
//
// Giving a sampling rate of one or more disables sampling, function listeners
// are invoked for all function calls.
func Sample(sampleRate float64, factory experimental.FunctionListenerFactory) experimental.FunctionListenerFactory {
if sampleRate <= 0 {
return emptyFunctionListenerFactory{}
}
if sampleRate >= 1 {
return factory
}
cycle := uint32(math.Ceil(1 / sampleRate))
return experimental.FunctionListenerFactoryFunc(func(def api.FunctionDefinition) experimental.FunctionListener {
lstn := factory.NewFunctionListener(def)
if lstn == nil {
return nil
}
sampled := &sampledFunctionListener{
cycle: cycle,
count: cycle,
lstn: lstn,
}
sampled.stack.bits = sampled.bits[:]
return sampled
})
}
type emptyFunctionListenerFactory struct{}
func (emptyFunctionListenerFactory) NewFunctionListener(api.FunctionDefinition) experimental.FunctionListener {
return nil
}
type sampledFunctionListener struct {
count uint32
cycle uint32
bits [1]uint64
stack bitstack
lstn experimental.FunctionListener
}
func (s *sampledFunctionListener) Before(ctx context.Context, mod api.Module, def api.FunctionDefinition, params []uint64, stack experimental.StackIterator) {
bit := uint(0)
if s.count--; s.count == 0 {
s.count = s.cycle
s.lstn.Before(ctx, mod, def, params, stack)
bit = 1
}
s.stack.push(bit)
}
func (s *sampledFunctionListener) After(ctx context.Context, mod api.Module, def api.FunctionDefinition, results []uint64) {
if s.stack.pop() != 0 {
s.lstn.After(ctx, mod, def, results)
}
}
func (s *sampledFunctionListener) Abort(ctx context.Context, mod api.Module, def api.FunctionDefinition, err error) {
if s.stack.pop() != 0 {
s.lstn.Abort(ctx, mod, def, err)
}
}
type bitstack struct {
size uint
bits []uint64
}
func (s *bitstack) push(bit uint) {
index := s.size / 64
shift := s.size % 64
if index >= uint(len(s.bits)) {
bits := make([]uint64, index+1)
copy(bits, s.bits)
s.bits = bits
}
s.bits[index] &= ^(uint64(1) << shift)
s.bits[index] |= uint64(bit&1) << shift
s.size++
}
func (s *bitstack) pop() uint {
s.size--
index := s.size / 64
shift := s.size % 64
return uint(s.bits[index]>>shift) & 1
}