Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Both use [dd-octo-sts](https://github.com/DataDog/dd-octo-sts-action) (`dd-trace
**Inputs:**

- `dd_trace_py_commit_sha` — commit to test (required)
- `test_scenarios` — regexp passed to `TEST_SCENARIOS` (dd-trace-py passes the [10-scenario 3.14/3.15 migration gate](scenarios/python_downstream_gate/README.md); the downstream workflow default alone is `python.*`)
- `test_scenarios` — regexp passed to `TEST_SCENARIOS` (dd-trace-py passes the [16-scenario 3.14/3.15 migration gate](scenarios/python_downstream_gate/README.md); the downstream workflow default alone is `python.*`)

## Creating new tests

Expand Down
15 changes: 15 additions & 0 deletions scenarios/python_asyncio_3.14/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ARG BASE_IMAGE="prof-python-3.14"
FROM $BASE_IMAGE

COPY ./scenarios/python_asyncio_3.14/requirements.txt /app/requirements.txt
RUN pip install -r /app/requirements.txt

COPY ./scenarios/python_asyncio_3.14/main.py /app/main.py
WORKDIR /app

ENV EXECUTION_TIME_SEC=5
ENV ECHION_USE_FAST_COPY_MEMORY=1
ENV _DD_PROFILING_STACK_ADAPTIVE_SAMPLING_ENABLED=0
ENV DD_PROFILING_ENABLED=true

CMD ddtrace-run python main.py
6 changes: 6 additions & 0 deletions scenarios/python_asyncio_3.14/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## Asyncio task-name labels (3.14 baseline)

Validates **wall-time** stack samples carry asyncio `task name` labels (named and
unnamed tasks). Migration pair baseline; see `python_asyncio_3.15`.

Reuses `scenarios/python_asyncio_3.11` workload.
52 changes: 52 additions & 0 deletions scenarios/python_asyncio_3.14/expected_profile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"test_name": "python_asyncio_3.14",
"stacks": [
{
"profile-type": "wall-time",
"stack-content": [
{
"regular_expression": ".*my_coroutine.*",
"percent": 99,
"error_margin": 0,
"labels": [
{
"key": "thread name",
"values": [
"MainThread"
]
},
{
"key": "task name",
"values": [
"short_task"
]
}
]
}
]
},
{
"profile-type": "wall-time",
"stack-content": [
{
"regular_expression": ".*my_coroutine.*",
"percent": 99,
"error_margin": 0,
"labels": [
{
"key": "thread name",
"values": [
"MainThread"
]
},
{
"key": "task name",
"values_regex": "Task-[0-9]+"
}
]
}
]
}
],
"scale_by_duration": false
}
27 changes: 27 additions & 0 deletions scenarios/python_asyncio_3.14/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import asyncio
import os


async def my_coroutine(n: float) -> None:
await asyncio.sleep(n)


async def main() -> None:
# Simple application that creates two Tasks with different durations:
# - "unnamed Task" runs my_coroutine() for 2 second
# - short_task runs my_coroutine() for 1 second
# The profiler should capture both Tasks with their respective durations.

# Give the Profiler some time to start up
await asyncio.sleep(0.5)

execution_time_sec = float(os.environ.get("EXECUTION_TIME_SEC", "5"))

short_task = asyncio.create_task(my_coroutine(execution_time_sec / 2.0), name="short_task")

# asyncio.gather will automatically wrap my_coroutine into a Task
await asyncio.gather(short_task, my_coroutine(execution_time_sec))


if __name__ == "__main__":
asyncio.run(main())
1 change: 1 addition & 0 deletions scenarios/python_asyncio_3.14/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ddtrace
12 changes: 12 additions & 0 deletions scenarios/python_asyncio_3.15/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
ARG BASE_IMAGE="prof-python-3.15"
FROM $BASE_IMAGE

COPY ./scenarios/python_asyncio_3.15/main.py /app/main.py
WORKDIR /app

ENV EXECUTION_TIME_SEC=5
ENV ECHION_USE_FAST_COPY_MEMORY=1
ENV _DD_PROFILING_STACK_ADAPTIVE_SAMPLING_ENABLED=0
ENV DD_PROFILING_ENABLED=true

CMD ddtrace-run python main.py
4 changes: 4 additions & 0 deletions scenarios/python_asyncio_3.15/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Asyncio task-name labels (3.15 candidate)

Wheel-only 3.15 candidate half of the asyncio task-name gate pair. See
`python_asyncio_3.14`.
52 changes: 52 additions & 0 deletions scenarios/python_asyncio_3.15/expected_profile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"test_name": "python_asyncio_3.15",
"stacks": [
{
"profile-type": "wall-time",
"stack-content": [
{
"regular_expression": ".*my_coroutine.*",
"percent": 50,
"error_margin": 100,
"labels": [
{
"key": "thread name",
"values": [
"MainThread"
]
},
{
"key": "task name",
"values": [
"short_task"
]
}
]
}
]
},
{
"profile-type": "wall-time",
"stack-content": [
{
"regular_expression": ".*my_coroutine.*",
"percent": 50,
"error_margin": 100,
"labels": [
{
"key": "thread name",
"values": [
"MainThread"
]
},
{
"key": "task name",
"values_regex": "Task-[0-9]+"
}
]
}
]
}
],
"scale_by_duration": false
}
27 changes: 27 additions & 0 deletions scenarios/python_asyncio_3.15/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import asyncio
import os


async def my_coroutine(n: float) -> None:
await asyncio.sleep(n)


async def main() -> None:
# Simple application that creates two Tasks with different durations:
# - "unnamed Task" runs my_coroutine() for 2 second
# - short_task runs my_coroutine() for 1 second
# The profiler should capture both Tasks with their respective durations.

# Give the Profiler some time to start up
await asyncio.sleep(0.5)

execution_time_sec = float(os.environ.get("EXECUTION_TIME_SEC", "5"))

short_task = asyncio.create_task(my_coroutine(execution_time_sec / 2.0), name="short_task")

# asyncio.gather will automatically wrap my_coroutine into a Task
await asyncio.gather(short_task, my_coroutine(execution_time_sec))


if __name__ == "__main__":
asyncio.run(main())
14 changes: 14 additions & 0 deletions scenarios/python_deep_stack_3.14/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ARG BASE_IMAGE="prof-python-3.14"
FROM $BASE_IMAGE

COPY ./scenarios/python_deep_stack_3.14/main.py \
./scenarios/python_deep_stack_3.14/requirements.txt \
/app/

WORKDIR /app
RUN pip install -r requirements.txt

ENV EXECUTION_TIME_SEC=10
ENV DD_PROFILING_ENABLED=true

CMD python main.py
4 changes: 4 additions & 0 deletions scenarios/python_deep_stack_3.14/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## Deep stack unwinding (3.14 baseline)

Validates **cpu-time** samples through a 400-frame recursive stack. See
`python_deep_stack_3.15`.
16 changes: 16 additions & 0 deletions scenarios/python_deep_stack_3.14/expected_profile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"test_name": "python_deep_stack_3.14",
"stacks": [
{
"profile-type": "cpu-time",
"stack-content": [
{
"regular_expression": "[0-9]+ frames omitted\u003e;(recurse;){50,}burn",
"percent": 100,
"error_margin": 10
}
]
}
],
"scale_by_duration": false
}
32 changes: 32 additions & 0 deletions scenarios/python_deep_stack_3.14/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import os
import sys
from time import time

from ddtrace.profiling import Profiler

DEPTH = 400


def burn(end: float) -> None:
x = 0
while time() < end:
for i in range(10000):
x += i


def recurse(depth: int, end: float) -> None:
if depth <= 0:
burn(end)
return
recurse(depth - 1, end)


if __name__ == "__main__":
sys.setrecursionlimit(10000)

prof = Profiler()
prof.start()

execution_time = float(os.environ.get("EXECUTION_TIME_SEC", "30"))
end = time() + execution_time
recurse(DEPTH, end)
1 change: 1 addition & 0 deletions scenarios/python_deep_stack_3.14/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ddtrace
10 changes: 10 additions & 0 deletions scenarios/python_deep_stack_3.15/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ARG BASE_IMAGE="prof-python-3.15"
FROM $BASE_IMAGE

COPY ./scenarios/python_deep_stack_3.15/main.py /app/main.py
WORKDIR /app

ENV EXECUTION_TIME_SEC=10
ENV DD_PROFILING_ENABLED=true

CMD python main.py
3 changes: 3 additions & 0 deletions scenarios/python_deep_stack_3.15/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## Deep stack unwinding (3.15 candidate)

Wheel-only candidate half of the deep-stack gate pair. See `python_deep_stack_3.14`.
16 changes: 16 additions & 0 deletions scenarios/python_deep_stack_3.15/expected_profile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"test_name": "python_deep_stack_3.15",
"stacks": [
{
"profile-type": "cpu-time",
"stack-content": [
{
"regular_expression": "[0-9]+ frames omitted\u003e;(recurse;){50,}burn",
"percent": 100,
"error_margin": 10
}
]
}
],
"scale_by_duration": false
}
32 changes: 32 additions & 0 deletions scenarios/python_deep_stack_3.15/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import os
import sys
from time import time

from ddtrace.profiling import Profiler

DEPTH = 400


def burn(end: float) -> None:
x = 0
while time() < end:
for i in range(10000):
x += i


def recurse(depth: int, end: float) -> None:
if depth <= 0:
burn(end)
return
recurse(depth - 1, end)


if __name__ == "__main__":
sys.setrecursionlimit(10000)

prof = Profiler()
prof.start()

execution_time = float(os.environ.get("EXECUTION_TIME_SEC", "30"))
end = time() + execution_time
recurse(DEPTH, end)
Loading
Loading