Skip to content

Commit 6e7fbdd

Browse files
authored
Merge branch 'master' into antonpirker/openai-agents-integration
2 parents 3f48a1c + ae06ef1 commit 6e7fbdd

File tree

4 files changed

+55
-16
lines changed

4 files changed

+55
-16
lines changed

scripts/populate_tox/tox.jinja

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ deps =
329329
sanic-v{24.6}: sanic_testing
330330
sanic-latest: sanic_testing
331331
{py3.6}-sanic: aiocontextvars==0.2.1
332+
{py3.8}-sanic: tracerite<1.1.2
332333
sanic-v0.8: sanic~=0.8.0
333334
sanic-v20: sanic~=20.0
334335
sanic-v24.6: sanic~=24.6.0

sentry_sdk/profiler/continuous_profiler.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ def __init__(self, frequency, options, sdk_info, capture_func):
236236
self.pid = None # type: Optional[int]
237237

238238
self.running = False
239+
self.soft_shutdown = False
239240

240241
self.new_profiles = deque(maxlen=128) # type: Deque[ContinuousProfile]
241242
self.active_profiles = set() # type: Set[ContinuousProfile]
@@ -317,24 +318,23 @@ def profiler_id(self):
317318
return self.buffer.profiler_id
318319

319320
def make_sampler(self):
320-
# type: () -> Callable[..., None]
321+
# type: () -> Callable[..., bool]
321322
cwd = os.getcwd()
322323

323324
cache = LRUCache(max_size=256)
324325

325326
if self.lifecycle == "trace":
326327

327328
def _sample_stack(*args, **kwargs):
328-
# type: (*Any, **Any) -> None
329+
# type: (*Any, **Any) -> bool
329330
"""
330331
Take a sample of the stack on all the threads in the process.
331332
This should be called at a regular interval to collect samples.
332333
"""
333334

334335
# no profiles taking place, so we can stop early
335336
if not self.new_profiles and not self.active_profiles:
336-
self.running = False
337-
return
337+
return True
338338

339339
# This is the number of profiles we want to pop off.
340340
# It's possible another thread adds a new profile to
@@ -357,7 +357,7 @@ def _sample_stack(*args, **kwargs):
357357
# For some reason, the frame we get doesn't have certain attributes.
358358
# When this happens, we abandon the current sample as it's bad.
359359
capture_internal_exception(sys.exc_info())
360-
return
360+
return False
361361

362362
# Move the new profiles into the active_profiles set.
363363
#
@@ -374,9 +374,7 @@ def _sample_stack(*args, **kwargs):
374374
inactive_profiles = []
375375

376376
for profile in self.active_profiles:
377-
if profile.active:
378-
pass
379-
else:
377+
if not profile.active:
380378
# If a profile is marked inactive, we buffer it
381379
# to `inactive_profiles` so it can be removed.
382380
# We cannot remove it here as it would result
@@ -389,10 +387,12 @@ def _sample_stack(*args, **kwargs):
389387
if self.buffer is not None:
390388
self.buffer.write(ts, sample)
391389

390+
return False
391+
392392
else:
393393

394394
def _sample_stack(*args, **kwargs):
395-
# type: (*Any, **Any) -> None
395+
# type: (*Any, **Any) -> bool
396396
"""
397397
Take a sample of the stack on all the threads in the process.
398398
This should be called at a regular interval to collect samples.
@@ -409,19 +409,21 @@ def _sample_stack(*args, **kwargs):
409409
# For some reason, the frame we get doesn't have certain attributes.
410410
# When this happens, we abandon the current sample as it's bad.
411411
capture_internal_exception(sys.exc_info())
412-
return
412+
return False
413413

414414
if self.buffer is not None:
415415
self.buffer.write(ts, sample)
416416

417+
return False
418+
417419
return _sample_stack
418420

419421
def run(self):
420422
# type: () -> None
421423
last = time.perf_counter()
422424

423425
while self.running:
424-
self.sampler()
426+
self.soft_shutdown = self.sampler()
425427

426428
# some time may have elapsed since the last time
427429
# we sampled, so we need to account for that and
@@ -430,6 +432,15 @@ def run(self):
430432
if elapsed < self.interval:
431433
thread_sleep(self.interval - elapsed)
432434

435+
# the soft shutdown happens here to give it a chance
436+
# for the profiler to be reused
437+
if self.soft_shutdown:
438+
self.running = False
439+
440+
# make sure to explicitly exit the profiler here or there might
441+
# be multiple profilers at once
442+
break
443+
433444
# after sleeping, make sure to take the current
434445
# timestamp so we can use it next iteration
435446
last = time.perf_counter()
@@ -458,6 +469,8 @@ def __init__(self, frequency, options, sdk_info, capture_func):
458469
def ensure_running(self):
459470
# type: () -> None
460471

472+
self.soft_shutdown = False
473+
461474
pid = os.getpid()
462475

463476
# is running on the right process
@@ -532,6 +545,9 @@ def __init__(self, frequency, options, sdk_info, capture_func):
532545

533546
def ensure_running(self):
534547
# type: () -> None
548+
549+
self.soft_shutdown = False
550+
535551
pid = os.getpid()
536552

537553
# is running on the right process

tests/profiler/test_continuous_profiler.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -459,33 +459,54 @@ def test_continuous_profiler_auto_start_and_stop_sampled(
459459

460460
thread = threading.current_thread()
461461

462+
all_profiler_ids = set()
463+
462464
for _ in range(3):
463465
envelopes.clear()
464466

467+
profiler_ids = set()
468+
465469
with sentry_sdk.start_transaction(name="profiling 1"):
466-
assert get_profiler_id() is not None, "profiler should be running"
470+
profiler_id = get_profiler_id()
471+
assert profiler_id is not None, "profiler should be running"
472+
profiler_ids.add(profiler_id)
467473
with sentry_sdk.start_span(op="op"):
468474
time.sleep(0.1)
469-
assert get_profiler_id() is not None, "profiler should be running"
475+
profiler_id = get_profiler_id()
476+
assert profiler_id is not None, "profiler should be running"
477+
profiler_ids.add(profiler_id)
478+
479+
time.sleep(0.03)
470480

471481
# the profiler takes a while to stop in auto mode so if we start
472482
# a transaction immediately, it'll be part of the same chunk
473-
assert get_profiler_id() is not None, "profiler should be running"
483+
profiler_id = get_profiler_id()
484+
assert profiler_id is not None, "profiler should be running"
485+
profiler_ids.add(profiler_id)
474486

475487
with sentry_sdk.start_transaction(name="profiling 2"):
476-
assert get_profiler_id() is not None, "profiler should be running"
488+
profiler_id = get_profiler_id()
489+
assert profiler_id is not None, "profiler should be running"
490+
profiler_ids.add(profiler_id)
477491
with sentry_sdk.start_span(op="op"):
478492
time.sleep(0.1)
479-
assert get_profiler_id() is not None, "profiler should be running"
493+
profiler_id = get_profiler_id()
494+
assert profiler_id is not None, "profiler should be running"
495+
profiler_ids.add(profiler_id)
480496

481497
# wait at least 1 cycle for the profiler to stop
482498
time.sleep(0.2)
483499
assert get_profiler_id() is None, "profiler should not be running"
484500

501+
assert len(profiler_ids) == 1
502+
all_profiler_ids.add(profiler_ids.pop())
503+
485504
assert_single_transaction_with_profile_chunks(
486505
envelopes, thread, max_chunks=1, transactions=2
487506
)
488507

508+
assert len(all_profiler_ids) == 3
509+
489510

490511
@pytest.mark.parametrize(
491512
"mode",

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ deps =
492492
sanic-v{24.6}: sanic_testing
493493
sanic-latest: sanic_testing
494494
{py3.6}-sanic: aiocontextvars==0.2.1
495+
{py3.8}-sanic: tracerite<1.1.2
495496
sanic-v0.8: sanic~=0.8.0
496497
sanic-v20: sanic~=20.0
497498
sanic-v24.6: sanic~=24.6.0

0 commit comments

Comments
 (0)