@@ -111,17 +111,16 @@ def setup_profiler(options):
111
111
# To buffer samples for `buffer_secs` at `frequency` Hz, we need
112
112
# a capcity of `buffer_secs * frequency`.
113
113
_sample_buffer = SampleBuffer (capacity = buffer_secs * frequency )
114
- _sampler = _init_sample_stack_fn (_sample_buffer )
115
114
116
115
profiler_mode = options ["_experiments" ].get ("profiler_mode" , SigprofScheduler .mode )
117
116
if profiler_mode == SigprofScheduler .mode :
118
- _scheduler = SigprofScheduler (sampler = _sampler , frequency = frequency )
117
+ _scheduler = SigprofScheduler (sample_buffer = _sample_buffer , frequency = frequency )
119
118
elif profiler_mode == SigalrmScheduler .mode :
120
- _scheduler = SigalrmScheduler (sampler = _sampler , frequency = frequency )
119
+ _scheduler = SigalrmScheduler (sample_buffer = _sample_buffer , frequency = frequency )
121
120
elif profiler_mode == SleepScheduler .mode :
122
- _scheduler = SleepScheduler (sampler = _sampler , frequency = frequency )
121
+ _scheduler = SleepScheduler (sample_buffer = _sample_buffer , frequency = frequency )
123
122
elif profiler_mode == EventScheduler .mode :
124
- _scheduler = EventScheduler (sampler = _sampler , frequency = frequency )
123
+ _scheduler = EventScheduler (sample_buffer = _sample_buffer , frequency = frequency )
125
124
else :
126
125
raise ValueError ("Unknown profiler mode: {}" .format (profiler_mode ))
127
126
_scheduler .setup ()
@@ -142,29 +141,6 @@ def teardown_profiler():
142
141
_scheduler = None
143
142
144
143
145
- def _init_sample_stack_fn (buffer ):
146
- # type: (SampleBuffer) -> Callable[..., None]
147
-
148
- def _sample_stack (* args , ** kwargs ):
149
- # type: (*Any, **Any) -> None
150
- """
151
- Take a sample of the stack on all the threads in the process.
152
- This should be called at a regular interval to collect samples.
153
- """
154
-
155
- buffer .write (
156
- (
157
- nanosecond_time (),
158
- [
159
- (tid , extract_stack (frame ))
160
- for tid , frame in sys ._current_frames ().items ()
161
- ],
162
- )
163
- )
164
-
165
- return _sample_stack
166
-
167
-
168
144
# We want to impose a stack depth limit so that samples aren't too large.
169
145
MAX_STACK_DEPTH = 128
170
146
@@ -242,8 +218,14 @@ def get_frame_name(frame):
242
218
243
219
244
220
class Profile (object ):
245
- def __init__ (self , transaction , hub = None ):
246
- # type: (sentry_sdk.tracing.Transaction, Optional[sentry_sdk.Hub]) -> None
221
+ def __init__ (
222
+ self ,
223
+ scheduler , # type: Scheduler
224
+ transaction , # type: sentry_sdk.tracing.Transaction
225
+ hub = None , # type: Optional[sentry_sdk.Hub]
226
+ ):
227
+ # type: (...) -> None
228
+ self .scheduler = scheduler
247
229
self .transaction = transaction
248
230
self .hub = hub
249
231
self ._start_ns = None # type: Optional[int]
@@ -253,27 +235,26 @@ def __init__(self, transaction, hub=None):
253
235
254
236
def __enter__ (self ):
255
237
# type: () -> None
256
- assert _scheduler is not None
257
238
self ._start_ns = nanosecond_time ()
258
- _scheduler .start_profiling ()
239
+ self . scheduler .start_profiling ()
259
240
260
241
def __exit__ (self , ty , value , tb ):
261
242
# type: (Optional[Any], Optional[Any], Optional[Any]) -> None
262
- assert _scheduler is not None
263
- _scheduler .stop_profiling ()
243
+ self .scheduler .stop_profiling ()
264
244
self ._stop_ns = nanosecond_time ()
265
245
266
246
def to_json (self , event_opt ):
267
247
# type: (Any) -> Dict[str, Any]
268
- assert _sample_buffer is not None
269
248
assert self ._start_ns is not None
270
249
assert self ._stop_ns is not None
271
250
272
251
return {
273
252
"environment" : event_opt .get ("environment" ),
274
253
"event_id" : uuid .uuid4 ().hex ,
275
254
"platform" : "python" ,
276
- "profile" : _sample_buffer .slice_profile (self ._start_ns , self ._stop_ns ),
255
+ "profile" : self .scheduler .sample_buffer .slice_profile (
256
+ self ._start_ns , self ._stop_ns
257
+ ),
277
258
"release" : event_opt .get ("release" , "" ),
278
259
"timestamp" : event_opt ["timestamp" ],
279
260
"version" : "1" ,
@@ -406,13 +387,36 @@ def slice_profile(self, start_ns, stop_ns):
406
387
"thread_metadata" : thread_metadata ,
407
388
}
408
389
390
+ def make_sampler (self ):
391
+ # type: () -> Callable[..., None]
392
+
393
+ def _sample_stack (* args , ** kwargs ):
394
+ # type: (*Any, **Any) -> None
395
+ """
396
+ Take a sample of the stack on all the threads in the process.
397
+ This should be called at a regular interval to collect samples.
398
+ """
399
+
400
+ self .write (
401
+ (
402
+ nanosecond_time (),
403
+ [
404
+ (tid , extract_stack (frame ))
405
+ for tid , frame in sys ._current_frames ().items ()
406
+ ],
407
+ )
408
+ )
409
+
410
+ return _sample_stack
411
+
409
412
410
413
class Scheduler (object ):
411
414
mode = "unknown"
412
415
413
- def __init__ (self , sampler , frequency ):
414
- # type: (Callable[..., None], int) -> None
415
- self .sampler = sampler
416
+ def __init__ (self , sample_buffer , frequency ):
417
+ # type: (SampleBuffer, int) -> None
418
+ self .sample_buffer = sample_buffer
419
+ self .sampler = sample_buffer .make_sampler ()
416
420
self ._lock = threading .Lock ()
417
421
self ._count = 0
418
422
self ._interval = 1.0 / frequency
@@ -447,9 +451,11 @@ class ThreadScheduler(Scheduler):
447
451
mode = "thread"
448
452
name = None # type: Optional[str]
449
453
450
- def __init__ (self , sampler , frequency ):
451
- # type: (Callable[..., None], int) -> None
452
- super (ThreadScheduler , self ).__init__ (sampler = sampler , frequency = frequency )
454
+ def __init__ (self , sample_buffer , frequency ):
455
+ # type: (SampleBuffer, int) -> None
456
+ super (ThreadScheduler , self ).__init__ (
457
+ sample_buffer = sample_buffer , frequency = frequency
458
+ )
453
459
self .stop_events = Queue ()
454
460
455
461
def setup (self ):
@@ -716,7 +722,8 @@ def start_profiling(transaction, hub=None):
716
722
717
723
# if profiling was not enabled, this should be a noop
718
724
if _should_profile (transaction , hub ):
719
- with Profile (transaction , hub = hub ):
725
+ assert _scheduler is not None
726
+ with Profile (_scheduler , transaction , hub = hub ):
720
727
yield
721
728
else :
722
729
yield
0 commit comments