@@ -69,6 +69,20 @@ pub fn span(label: Label) -> ProfileSpan {
69
69
}
70
70
}
71
71
72
+ #[ inline]
73
+ pub fn heartbeat_span ( ) -> HeartbeatSpan {
74
+ let enabled = PROFILING_ENABLED . load ( Ordering :: Relaxed ) ;
75
+ HeartbeatSpan :: new ( enabled)
76
+ }
77
+
78
+ #[ inline]
79
+ pub fn heartbeat ( ) {
80
+ let enabled = PROFILING_ENABLED . load ( Ordering :: Relaxed ) ;
81
+ if enabled {
82
+ with_profile_stack ( |it| it. heartbeat ( 1 ) ) ;
83
+ }
84
+ }
85
+
72
86
pub struct ProfileSpan ( Option < ProfilerImpl > ) ;
73
87
74
88
struct ProfilerImpl {
@@ -92,6 +106,28 @@ impl Drop for ProfilerImpl {
92
106
}
93
107
}
94
108
109
+ pub struct HeartbeatSpan {
110
+ enabled : bool ,
111
+ }
112
+
113
+ impl HeartbeatSpan {
114
+ #[ inline]
115
+ pub fn new ( enabled : bool ) -> Self {
116
+ if enabled {
117
+ with_profile_stack ( |it| it. heartbeats ( true ) )
118
+ }
119
+ Self { enabled }
120
+ }
121
+ }
122
+
123
+ impl Drop for HeartbeatSpan {
124
+ fn drop ( & mut self ) {
125
+ if self . enabled {
126
+ with_profile_stack ( |it| it. heartbeats ( false ) )
127
+ }
128
+ }
129
+ }
130
+
95
131
static PROFILING_ENABLED : AtomicBool = AtomicBool :: new ( false ) ;
96
132
static FILTER : Lazy < RwLock < Filter > > = Lazy :: new ( Default :: default) ;
97
133
@@ -105,6 +141,7 @@ struct Filter {
105
141
depth : usize ,
106
142
allowed : HashSet < String > ,
107
143
longer_than : Duration ,
144
+ heartbeat_longer_than : Duration ,
108
145
version : usize ,
109
146
}
110
147
@@ -121,6 +158,7 @@ impl Filter {
121
158
} else {
122
159
Duration :: new ( 0 , 0 )
123
160
} ;
161
+ let heartbeat_longer_than = longer_than;
124
162
125
163
let depth = if let Some ( idx) = spec. rfind ( '@' ) {
126
164
let depth: usize = spec[ idx + 1 ..] . parse ( ) . expect ( "invalid profile depth" ) ;
@@ -131,7 +169,7 @@ impl Filter {
131
169
} ;
132
170
let allowed =
133
171
if spec == "*" { HashSet :: new ( ) } else { spec. split ( '|' ) . map ( String :: from) . collect ( ) } ;
134
- Filter { depth, allowed, longer_than, version : 0 }
172
+ Filter { depth, allowed, longer_than, heartbeat_longer_than , version : 0 }
135
173
}
136
174
137
175
fn install ( mut self ) {
@@ -143,9 +181,15 @@ impl Filter {
143
181
}
144
182
145
183
struct ProfileStack {
146
- starts : Vec < Instant > ,
184
+ frames : Vec < Frame > ,
147
185
filter : Filter ,
148
186
messages : Tree < Message > ,
187
+ heartbeats : bool ,
188
+ }
189
+
190
+ struct Frame {
191
+ t : Instant ,
192
+ heartbeats : u32 ,
149
193
}
150
194
151
195
#[ derive( Default ) ]
@@ -157,35 +201,49 @@ struct Message {
157
201
158
202
impl ProfileStack {
159
203
fn new ( ) -> ProfileStack {
160
- ProfileStack { starts : Vec :: new ( ) , messages : Tree :: default ( ) , filter : Default :: default ( ) }
204
+ ProfileStack {
205
+ frames : Vec :: new ( ) ,
206
+ messages : Tree :: default ( ) ,
207
+ filter : Default :: default ( ) ,
208
+ heartbeats : false ,
209
+ }
161
210
}
162
211
163
212
fn push ( & mut self , label : Label ) -> bool {
164
- if self . starts . is_empty ( ) {
213
+ if self . frames . is_empty ( ) {
165
214
if let Ok ( f) = FILTER . try_read ( ) {
166
215
if f. version > self . filter . version {
167
216
self . filter = f. clone ( ) ;
168
217
}
169
218
} ;
170
219
}
171
- if self . starts . len ( ) > self . filter . depth {
220
+ if self . frames . len ( ) > self . filter . depth {
172
221
return false ;
173
222
}
174
223
let allowed = & self . filter . allowed ;
175
- if self . starts . is_empty ( ) && !allowed. is_empty ( ) && !allowed. contains ( label) {
224
+ if self . frames . is_empty ( ) && !allowed. is_empty ( ) && !allowed. contains ( label) {
176
225
return false ;
177
226
}
178
227
179
- self . starts . push ( Instant :: now ( ) ) ;
228
+ self . frames . push ( Frame { t : Instant :: now ( ) , heartbeats : 0 } ) ;
180
229
self . messages . start ( ) ;
181
230
true
182
231
}
183
232
184
233
fn pop ( & mut self , label : Label , detail : Option < String > ) {
185
- let start = self . starts . pop ( ) . unwrap ( ) ;
186
- let duration = start. elapsed ( ) ;
234
+ let frame = self . frames . pop ( ) . unwrap ( ) ;
235
+ let duration = frame. t . elapsed ( ) ;
236
+
237
+ if self . heartbeats {
238
+ self . heartbeat ( frame. heartbeats ) ;
239
+ let avg_span = duration / ( frame. heartbeats + 1 ) ;
240
+ if avg_span > self . filter . heartbeat_longer_than {
241
+ eprintln ! ( "Too few heartbeats {} ({}/{:?})?" , label, frame. heartbeats, duration)
242
+ }
243
+ }
244
+
187
245
self . messages . finish ( Message { duration, label, detail } ) ;
188
- if self . starts . is_empty ( ) {
246
+ if self . frames . is_empty ( ) {
189
247
let longer_than = self . filter . longer_than ;
190
248
// Convert to millis for comparison to avoid problems with rounding
191
249
// (otherwise we could print `0ms` despite user's `>0` filter when
@@ -198,6 +256,15 @@ impl ProfileStack {
198
256
self . messages . clear ( ) ;
199
257
}
200
258
}
259
+
260
+ fn heartbeats ( & mut self , yes : bool ) {
261
+ self . heartbeats = yes;
262
+ }
263
+ fn heartbeat ( & mut self , n : u32 ) {
264
+ if let Some ( frame) = self . frames . last_mut ( ) {
265
+ frame. heartbeats += n;
266
+ }
267
+ }
201
268
}
202
269
203
270
fn print (
0 commit comments