Skip to content

Commit bac5bb1

Browse files
authored
tests(profiling): Add additional test coverage for profiler (#1877)
tests(profiling): Add additional test coverage for profiler
1 parent 9d23e5f commit bac5bb1

File tree

3 files changed

+125
-106
lines changed

3 files changed

+125
-106
lines changed

sentry_sdk/profiler.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def is_gevent():
137137

138138

139139
def setup_profiler(options):
140-
# type: (Dict[str, Any]) -> None
140+
# type: (Dict[str, Any]) -> bool
141141
"""
142142
`buffer_secs` determines the max time a sample will be buffered for
143143
`frequency` determines the number of samples to take per second (Hz)
@@ -147,11 +147,11 @@ def setup_profiler(options):
147147

148148
if _scheduler is not None:
149149
logger.debug("profiling is already setup")
150-
return
150+
return False
151151

152152
if not PY33:
153153
logger.warn("profiling is only supported on Python >= 3.3")
154-
return
154+
return False
155155

156156
frequency = 101
157157

@@ -184,6 +184,8 @@ def setup_profiler(options):
184184

185185
atexit.register(teardown_profiler)
186186

187+
return True
188+
187189

188190
def teardown_profiler():
189191
# type: () -> None
@@ -410,8 +412,7 @@ def __init__(
410412
#
411413
# We cannot keep a reference to the transaction around here because it'll create
412414
# a reference cycle. So we opt to pull out just the necessary attributes.
413-
self._transaction_sampled = transaction.sampled # type: Optional[bool]
414-
self.sampled = None # type: Optional[bool]
415+
self.sampled = transaction.sampled # type: Optional[bool]
415416

416417
# Various framework integrations are capable of overwriting the active thread id.
417418
# If it is set to `None` at the end of the profile, we fall back to the default.
@@ -448,7 +449,7 @@ def _set_initial_sampling_decision(self, sampling_context):
448449

449450
# The corresponding transaction was not sampled,
450451
# so don't generate a profile for it.
451-
if not self._transaction_sampled:
452+
if not self.sampled:
452453
self.sampled = False
453454
return
454455

@@ -485,19 +486,21 @@ def get_profile_context(self):
485486

486487
def start(self):
487488
# type: () -> None
488-
if not self.sampled:
489+
if not self.sampled or self.active:
489490
return
490491

491492
assert self.scheduler, "No scheduler specified"
493+
self.active = True
492494
self.start_ns = nanosecond_time()
493495
self.scheduler.start_profiling(self)
494496

495497
def stop(self):
496498
# type: () -> None
497-
if not self.sampled:
499+
if not self.sampled or not self.active:
498500
return
499501

500502
assert self.scheduler, "No scheduler specified"
503+
self.active = False
501504
self.scheduler.stop_profiling(self)
502505
self.stop_ns = nanosecond_time()
503506

@@ -526,11 +529,15 @@ def __exit__(self, ty, value, tb):
526529

527530
def write(self, ts, sample):
528531
# type: (int, RawSample) -> None
532+
if not self.active:
533+
return
534+
529535
if ts < self.start_ns:
530536
return
531537

532538
offset = ts - self.start_ns
533539
if offset > MAX_PROFILE_DURATION_NS:
540+
self.stop()
534541
return
535542

536543
elapsed_since_start_ns = str(offset)
@@ -666,12 +673,11 @@ def teardown(self):
666673

667674
def start_profiling(self, profile):
668675
# type: (Profile) -> None
669-
profile.active = True
670676
self.new_profiles.append(profile)
671677

672678
def stop_profiling(self, profile):
673679
# type: (Profile) -> None
674-
profile.active = False
680+
pass
675681

676682
def make_sampler(self):
677683
# type: () -> Callable[..., None]

tests/integrations/wsgi/test_wsgi.py

Lines changed: 5 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -287,49 +287,15 @@ def sample_app(environ, start_response):
287287
@pytest.mark.skipif(
288288
sys.version_info < (3, 3), reason="Profiling is only supported in Python >= 3.3"
289289
)
290-
@pytest.mark.parametrize(
291-
"profiles_sample_rate,profile_count",
292-
[
293-
pytest.param(1.0, 1, id="profiler sampled at 1.0"),
294-
pytest.param(0.75, 1, id="profiler sampled at 0.75"),
295-
pytest.param(0.25, 0, id="profiler not sampled at 0.25"),
296-
pytest.param(None, 0, id="profiler not enabled"),
297-
],
298-
)
299290
def test_profile_sent(
300291
sentry_init,
301292
capture_envelopes,
302293
teardown_profiling,
303-
profiles_sample_rate,
304-
profile_count,
305294
):
306295
def test_app(environ, start_response):
307296
start_response("200 OK", [])
308297
return ["Go get the ball! Good dog!"]
309298

310-
sentry_init(
311-
traces_sample_rate=1.0,
312-
_experiments={"profiles_sample_rate": profiles_sample_rate},
313-
)
314-
app = SentryWsgiMiddleware(test_app)
315-
envelopes = capture_envelopes()
316-
317-
with mock.patch("sentry_sdk.profiler.random.random", return_value=0.5):
318-
client = Client(app)
319-
client.get("/")
320-
321-
count_item_types = Counter()
322-
for envelope in envelopes:
323-
for item in envelope.items:
324-
count_item_types[item.type] += 1
325-
assert count_item_types["profile"] == profile_count
326-
327-
328-
def test_profile_context_sent(sentry_init, capture_envelopes, teardown_profiling):
329-
def test_app(environ, start_response):
330-
start_response("200 OK", [])
331-
return ["Go get the ball! Good dog!"]
332-
333299
sentry_init(
334300
traces_sample_rate=1.0,
335301
_experiments={"profiles_sample_rate": 1.0},
@@ -340,19 +306,8 @@ def test_app(environ, start_response):
340306
client = Client(app)
341307
client.get("/")
342308

343-
transaction = None
344-
profile = None
345-
for envelope in envelopes:
346-
for item in envelope.items:
347-
if item.type == "profile":
348-
assert profile is None # should only have 1 profile
349-
profile = item
350-
elif item.type == "transaction":
351-
assert transaction is None # should only have 1 transaction
352-
transaction = item
353-
354-
assert transaction is not None
355-
assert profile is not None
356-
assert transaction.payload.json["contexts"]["profile"] == {
357-
"profile_id": profile.payload.json["event_id"],
358-
}
309+
envelopes = [envelope for envelope in envelopes]
310+
assert len(envelopes) == 1
311+
312+
profiles = [item for item in envelopes[0].items if item.type == "profile"]
313+
assert len(profiles) == 1

0 commit comments

Comments
 (0)