53
53
from typing_extensions import TypedDict
54
54
import sentry_sdk .tracing
55
55
56
- RawSampleData = Tuple [int , Sequence [Tuple [str , Sequence [RawFrameData ]]]]
56
+ RawStack = Tuple [RawFrameData , ...]
57
+ RawSample = Sequence [Tuple [str , RawStack ]]
58
+ RawSampleWithId = Sequence [Tuple [str , int , RawStack ]]
57
59
58
60
ProcessedStack = Tuple [int , ...]
59
61
@@ -153,7 +155,7 @@ def teardown_profiler():
153
155
154
156
155
157
def extract_stack (frame , max_stack_depth = MAX_STACK_DEPTH ):
156
- # type: (Optional[FrameType], int) -> Sequence [RawFrameData]
158
+ # type: (Optional[FrameType], int) -> Tuple [RawFrameData, ... ]
157
159
"""
158
160
Extracts the stack starting the specified frame. The extracted stack
159
161
assumes the specified frame is the top of the stack, and works back
@@ -328,12 +330,14 @@ class SampleBuffer(object):
328
330
def __init__ (self , capacity ):
329
331
# type: (int) -> None
330
332
331
- self .buffer = [None ] * capacity # type: List[Optional[RawSampleData]]
333
+ self .buffer = [
334
+ None
335
+ ] * capacity # type: List[Optional[Tuple[int, RawSampleWithId]]]
332
336
self .capacity = capacity # type: int
333
337
self .idx = 0 # type: int
334
338
335
- def write (self , sample ):
336
- # type: (RawSampleData ) -> None
339
+ def write (self , ts , raw_sample ):
340
+ # type: (int, RawSample ) -> None
337
341
"""
338
342
Writing to the buffer is not thread safe. There is the possibility
339
343
that parallel writes will overwrite one another.
@@ -346,7 +350,24 @@ def write(self, sample):
346
350
any synchronization mechanisms here like locks.
347
351
"""
348
352
idx = self .idx
349
- self .buffer [idx ] = sample
353
+
354
+ sample = [
355
+ (
356
+ thread_id ,
357
+ # Instead of mapping the stack into frame ids and hashing
358
+ # that as a tuple, we can directly hash the stack.
359
+ # This saves us from having to generate yet another list.
360
+ # Additionally, using the stack as the key directly is
361
+ # costly because the stack can be large, so we pre-hash
362
+ # the stack, and use the hash as the key as this will be
363
+ # needed a few times to improve performance.
364
+ hash (stack ),
365
+ stack ,
366
+ )
367
+ for thread_id , stack in raw_sample
368
+ ]
369
+
370
+ self .buffer [idx ] = (ts , sample )
350
371
self .idx = (idx + 1 ) % self .capacity
351
372
352
373
def slice_profile (self , start_ns , stop_ns ):
@@ -357,27 +378,13 @@ def slice_profile(self, start_ns, stop_ns):
357
378
frames = dict () # type: Dict[RawFrameData, int]
358
379
frames_list = list () # type: List[ProcessedFrame]
359
380
360
- # TODO: This is doing an naive iteration over the
361
- # buffer and extracting the appropriate samples.
362
- #
363
- # Is it safe to assume that the samples are always in
364
- # chronological order and binary search the buffer?
365
381
for ts , sample in filter (None , self .buffer ):
366
382
if start_ns > ts or ts > stop_ns :
367
383
continue
368
384
369
385
elapsed_since_start_ns = str (ts - start_ns )
370
386
371
- for tid , stack in sample :
372
- # Instead of mapping the stack into frame ids and hashing
373
- # that as a tuple, we can directly hash the stack.
374
- # This saves us from having to generate yet another list.
375
- # Additionally, using the stack as the key directly is
376
- # costly because the stack can be large, so we pre-hash
377
- # the stack, and use the hash as the key as this will be
378
- # needed a few times to improve performance.
379
- hashed_stack = hash (stack )
380
-
387
+ for tid , hashed_stack , stack in sample :
381
388
# Check if the stack is indexed first, this lets us skip
382
389
# indexing frames if it's not necessary
383
390
if hashed_stack not in stacks :
@@ -433,13 +440,11 @@ def _sample_stack(*args, **kwargs):
433
440
"""
434
441
435
442
self .write (
436
- (
437
- nanosecond_time (),
438
- [
439
- (str (tid ), extract_stack (frame ))
440
- for tid , frame in sys ._current_frames ().items ()
441
- ],
442
- )
443
+ nanosecond_time (),
444
+ [
445
+ (str (tid ), extract_stack (frame ))
446
+ for tid , frame in sys ._current_frames ().items ()
447
+ ],
443
448
)
444
449
445
450
return _sample_stack
0 commit comments