Skip to content

Commit 4d1bffd

Browse files
committed
to_thread test
1 parent 5ed5296 commit 4d1bffd

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

tests/profiling/collector/test_asyncio_executor.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,81 @@ async def main() -> None:
8383
)
8484

8585

86+
@pytest.mark.subprocess(
87+
env=dict(
88+
DD_PROFILING_OUTPUT_PPROF="/tmp/test_asyncio_to_thread_origin_task",
89+
),
90+
err=None,
91+
)
92+
# For macOS: err=None ignores expected stderr from tracer failing to connect to agent (not relevant to this test)
93+
def test_asyncio_to_thread_origin_task_labels() -> None:
94+
# asyncio.to_thread is a higher-level wrapper around run_in_executor; origin
95+
# task labels should still identify the awaiting asyncio task.
96+
import asyncio
97+
import os
98+
import time
99+
100+
from ddtrace import patch
101+
from ddtrace.internal.datadog.profiling import stack
102+
from ddtrace.profiling import profiler
103+
from tests.profiling.collector import pprof_utils
104+
from tests.profiling.collector.test_utils import async_run
105+
106+
assert stack.is_available, stack.failure_msg
107+
108+
patch(futures=True)
109+
110+
def slow_sync_function() -> None:
111+
time.sleep(1)
112+
113+
async def asynchronous_function() -> None:
114+
await asyncio.to_thread(slow_sync_function)
115+
116+
async def main() -> None:
117+
await asynchronous_function()
118+
119+
p = profiler.Profiler()
120+
p.start()
121+
122+
async_run(main())
123+
124+
p.stop()
125+
126+
output_filename = os.environ["DD_PROFILING_OUTPUT_PPROF"] + "." + str(os.getpid())
127+
profile = pprof_utils.parse_newest_profile(output_filename)
128+
string_table = profile.string_table
129+
130+
origin_task_samples = pprof_utils.get_samples_with_label_key(profile, "origin task id")
131+
assert len(origin_task_samples) > 0, "expected to_thread samples labeled with the originating asyncio task"
132+
133+
awaiting_task_ids = set()
134+
for sample in pprof_utils.get_samples_with_label_key(profile, "task id"):
135+
awaiting_task_ids.add(pprof_utils.get_label_with_key(string_table, sample, "task id").num)
136+
assert awaiting_task_ids, "expected at least one asyncio task sample on the event-loop thread"
137+
138+
for sample in origin_task_samples:
139+
origin_task_id = pprof_utils.get_label_with_key(string_table, sample, "origin task id").num
140+
assert origin_task_id in awaiting_task_ids, (
141+
f"origin task id {origin_task_id} does not match any awaiting task id {awaiting_task_ids}"
142+
)
143+
origin_task_name = pprof_utils.get_label_with_key(string_table, sample, "origin task name")
144+
assert origin_task_name is not None and string_table[origin_task_name.str], "expected origin task name label"
145+
146+
pprof_utils.assert_profile_has_sample(
147+
profile,
148+
origin_task_samples,
149+
expected_sample=pprof_utils.StackEvent(
150+
locations=[
151+
pprof_utils.StackLocation(
152+
function_name="slow_sync_function",
153+
filename="test_asyncio_executor.py",
154+
line_no=slow_sync_function.__code__.co_firstlineno + 1,
155+
),
156+
],
157+
),
158+
)
159+
160+
86161
@pytest.mark.subprocess(
87162
env=dict(
88163
DD_PROFILING_OUTPUT_PPROF="/tmp/test_asyncio_executor",

0 commit comments

Comments
 (0)