Skip to content

Commit 43654d5

Browse files
feat(python): add alloc gate scenarios for 3.14/3.15 migration
Add paired python_alloc scenarios asserting alloc-space and alloc-samples with thread-name labels, reusing the python_basic_memory workload. Gate grows from 12 to 14 scenarios.
1 parent 077f9e3 commit 43654d5

11 files changed

Lines changed: 397 additions & 6 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ Both use [dd-octo-sts](https://github.com/DataDog/dd-octo-sts-action) (`dd-trace
7777
**Inputs:**
7878

7979
- `dd_trace_py_commit_sha` — commit to test (required)
80-
- `test_scenarios` — regexp passed to `TEST_SCENARIOS` (dd-trace-py passes the [8-scenario 3.14/3.15 migration gate](scenarios/python_downstream_gate/README.md); the downstream workflow default alone is `python.*`)
80+
- `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.*`)
8181

8282
## Creating new tests
8383

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
ARG BASE_IMAGE="prof-python-3.14"
2+
FROM $BASE_IMAGE
3+
4+
COPY ./scenarios/python_alloc_3.14/requirements.txt /app/requirements.txt
5+
RUN pip install -r /app/requirements.txt
6+
7+
COPY ./scenarios/python_alloc_3.14/main.py /app/main.py
8+
WORKDIR /app
9+
10+
ENV DD_PROFILING_STACK_ENABLED=false
11+
ENV DD_PROFILING_LOCK_ENABLED=false
12+
13+
CMD python main.py
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## Allocation profiling (3.14 baseline)
2+
3+
Validates that the Datadog Python profiler's **memory collector** reports
4+
`alloc-space` and `alloc-samples` with correct call stacks and `thread name`
5+
labels. This is the **3.14 baseline** half of the `3.14 -> 3.15` migration pair
6+
(see `python_alloc_3.15`).
7+
8+
Reuses the workload from `scenarios/python_basic_memory_3.11`: two allocation
9+
sites with a 1:3 byte ratio (`allocate_memory_1` / `allocate_memory_2`).
10+
Stack and lock collectors are disabled to isolate allocation samples.
11+
12+
## Expected profile
13+
14+
Assertions match `python_basic_memory_3.11` (stable sampling proportions for
15+
`alloc-samples` and `alloc-space`).
16+
17+
## Notes
18+
19+
The 3.14 scenario runs on prof-correctness `main` CI (PyPI ddtrace). The 3.15
20+
candidate runs only via the dd-trace-py downstream gate (`DDTRACE_INSTALL_URL`).
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
{
2+
"test_name": "python_alloc_3.14",
3+
"pprof-regex": "",
4+
"stacks": [
5+
{
6+
"profile-type": "alloc-samples",
7+
"pprof-regex": "",
8+
"stack-content": [
9+
{
10+
"regular_expression": "^\u003cmodule\u003e;run;allocate_memory_1.*$",
11+
"percent": 35,
12+
"error_margin": 5,
13+
"labels": [
14+
{
15+
"key": "thread name",
16+
"values": [
17+
"MainThread"
18+
],
19+
"values_regex": ""
20+
}
21+
]
22+
}
23+
]
24+
},
25+
{
26+
"profile-type": "alloc-samples",
27+
"pprof-regex": "",
28+
"stack-content": [
29+
{
30+
"regular_expression": "^\u003cmodule\u003e;run;allocate_memory_2.*$",
31+
"percent": 45,
32+
"error_margin": 5,
33+
"labels": [
34+
{
35+
"key": "thread name",
36+
"values": [
37+
"MainThread"
38+
],
39+
"values_regex": ""
40+
}
41+
]
42+
}
43+
]
44+
},
45+
{
46+
"profile-type": "alloc-samples",
47+
"pprof-regex": "",
48+
"stack-content": [
49+
{
50+
"regular_expression": "^.*__init__;.*grow_list.*$",
51+
"percent": 19,
52+
"error_margin": 5,
53+
"labels": [
54+
{
55+
"key": "thread name",
56+
"values": [
57+
"MainThread"
58+
],
59+
"values_regex": ""
60+
}
61+
]
62+
}
63+
]
64+
},
65+
{
66+
"profile-type": "alloc-space",
67+
"pprof-regex": "",
68+
"stack-content": [
69+
{
70+
"regular_expression": "^\u003cmodule\u003e;run;allocate_memory_1.*$",
71+
"percent": 25,
72+
"error_margin": 5,
73+
"labels": [
74+
{
75+
"key": "thread name",
76+
"values": [
77+
"MainThread"
78+
],
79+
"values_regex": ""
80+
}
81+
]
82+
}
83+
]
84+
},
85+
{
86+
"profile-type": "alloc-space",
87+
"pprof-regex": "",
88+
"stack-content": [
89+
{
90+
"regular_expression": "^\u003cmodule\u003e;run;allocate_memory_2.*$",
91+
"percent": 75,
92+
"error_margin": 5,
93+
"labels": [
94+
{
95+
"key": "thread name",
96+
"values": [
97+
"MainThread"
98+
],
99+
"values_regex": ""
100+
}
101+
]
102+
}
103+
]
104+
},
105+
{
106+
"profile-type": "alloc-space",
107+
"pprof-regex": "",
108+
"stack-content": [
109+
{
110+
"regular_expression": "^.*grow_list.*$",
111+
"percent": 3,
112+
"error_margin": 3,
113+
"labels": [
114+
{
115+
"key": "thread name",
116+
"values": [
117+
"MainThread"
118+
],
119+
"values_regex": ""
120+
}
121+
]
122+
}
123+
]
124+
}
125+
],
126+
"scale_by_duration": false
127+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from ddtrace.profiling import Profiler
2+
3+
4+
class Target:
5+
def __init__(self) -> None:
6+
self.memory: list[bytearray | None] = []
7+
self.index = 0
8+
self.grow_list(target=int(1e6))
9+
10+
def run(self) -> None:
11+
while self.memory[-1] is None:
12+
self.allocate_memory_1(1024)
13+
self.allocate_memory_2(1024)
14+
15+
def grow_list(self, target: int) -> None:
16+
self.memory = [None for _ in range(target)]
17+
18+
def allocate_memory_1(self, size: int) -> None:
19+
self.memory[self.index] = bytearray(size)
20+
self.index += 1
21+
22+
def allocate_memory_2(self, size: int) -> None:
23+
self.memory[self.index] = bytearray(3 * size)
24+
self.index += 1
25+
26+
27+
if __name__ == "__main__":
28+
prof = Profiler()
29+
prof.start()
30+
31+
Target().run()
32+
33+
prof.stop()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ddtrace
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
ARG BASE_IMAGE="prof-python-3.15"
2+
FROM $BASE_IMAGE
3+
4+
# ddtrace is pre-installed in the base image when DDTRACE_INSTALL_URL is set
5+
# (dd-trace-py downstream CI). Do not pip-install here — PyPI wheels may not
6+
# exist for 3.15 yet.
7+
8+
COPY ./scenarios/python_alloc_3.15/main.py /app/main.py
9+
WORKDIR /app
10+
11+
ENV DD_PROFILING_STACK_ENABLED=false
12+
ENV DD_PROFILING_LOCK_ENABLED=false
13+
14+
CMD python main.py
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
## Allocation profiling (3.15 candidate)
2+
3+
Validates that the Datadog Python profiler's **memory collector** reports
4+
`alloc-space` and `alloc-samples` with correct call stacks and `thread name`
5+
labels. This is the **3.15 candidate** half of the `3.14 -> 3.15` migration pair
6+
(see `python_alloc_3.14`).
7+
8+
Reuses the workload from `scenarios/python_basic_memory_3.11`: two allocation
9+
sites with a 1:3 byte ratio (`allocate_memory_1` / `allocate_memory_2`).
10+
Stack and lock collectors are disabled to isolate allocation samples.
11+
12+
## Expected profile
13+
14+
Assertions match `python_basic_memory_3.11` (stable sampling proportions for
15+
`alloc-samples` and `alloc-space`).
16+
17+
## Notes
18+
19+
This scenario is wheel-only: PyPI ddtrace wheels may not exist for 3.15 yet, so
20+
it is excluded from prof-correctness `main` CI and runs via the dd-trace-py
21+
downstream gate (`DDTRACE_INSTALL_URL`).
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
{
2+
"test_name": "python_alloc_3.15",
3+
"pprof-regex": "",
4+
"stacks": [
5+
{
6+
"profile-type": "alloc-samples",
7+
"pprof-regex": "",
8+
"stack-content": [
9+
{
10+
"regular_expression": "^\u003cmodule\u003e;run;allocate_memory_1.*$",
11+
"percent": 35,
12+
"error_margin": 5,
13+
"labels": [
14+
{
15+
"key": "thread name",
16+
"values": [
17+
"MainThread"
18+
],
19+
"values_regex": ""
20+
}
21+
]
22+
}
23+
]
24+
},
25+
{
26+
"profile-type": "alloc-samples",
27+
"pprof-regex": "",
28+
"stack-content": [
29+
{
30+
"regular_expression": "^\u003cmodule\u003e;run;allocate_memory_2.*$",
31+
"percent": 45,
32+
"error_margin": 5,
33+
"labels": [
34+
{
35+
"key": "thread name",
36+
"values": [
37+
"MainThread"
38+
],
39+
"values_regex": ""
40+
}
41+
]
42+
}
43+
]
44+
},
45+
{
46+
"profile-type": "alloc-samples",
47+
"pprof-regex": "",
48+
"stack-content": [
49+
{
50+
"regular_expression": "^.*__init__;.*grow_list.*$",
51+
"percent": 19,
52+
"error_margin": 5,
53+
"labels": [
54+
{
55+
"key": "thread name",
56+
"values": [
57+
"MainThread"
58+
],
59+
"values_regex": ""
60+
}
61+
]
62+
}
63+
]
64+
},
65+
{
66+
"profile-type": "alloc-space",
67+
"pprof-regex": "",
68+
"stack-content": [
69+
{
70+
"regular_expression": "^\u003cmodule\u003e;run;allocate_memory_1.*$",
71+
"percent": 25,
72+
"error_margin": 5,
73+
"labels": [
74+
{
75+
"key": "thread name",
76+
"values": [
77+
"MainThread"
78+
],
79+
"values_regex": ""
80+
}
81+
]
82+
}
83+
]
84+
},
85+
{
86+
"profile-type": "alloc-space",
87+
"pprof-regex": "",
88+
"stack-content": [
89+
{
90+
"regular_expression": "^\u003cmodule\u003e;run;allocate_memory_2.*$",
91+
"percent": 75,
92+
"error_margin": 5,
93+
"labels": [
94+
{
95+
"key": "thread name",
96+
"values": [
97+
"MainThread"
98+
],
99+
"values_regex": ""
100+
}
101+
]
102+
}
103+
]
104+
},
105+
{
106+
"profile-type": "alloc-space",
107+
"pprof-regex": "",
108+
"stack-content": [
109+
{
110+
"regular_expression": "^.*grow_list.*$",
111+
"percent": 3,
112+
"error_margin": 3,
113+
"labels": [
114+
{
115+
"key": "thread name",
116+
"values": [
117+
"MainThread"
118+
],
119+
"values_regex": ""
120+
}
121+
]
122+
}
123+
]
124+
}
125+
],
126+
"scale_by_duration": false
127+
}

0 commit comments

Comments
 (0)