Skip to content

Commit 36a2f3b

Browse files
authored
feat(profiling): stack_v2 supports CPython 3.13 (#12672)
## Checklist - [x] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [x] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)
1 parent b863cfb commit 36a2f3b

File tree

8 files changed

+117
-38
lines changed

8 files changed

+117
-38
lines changed

.github/workflows/profiling-native.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
fail-fast: false
2020
matrix:
2121
os: [ubuntu-24.04]
22-
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
22+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
2323
sanitizer: ["safety", "thread", "valgrind"]
2424

2525
steps:

.riot/requirements/955b213.txt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#
2+
# This file is autogenerated by pip-compile with Python 3.13
3+
# by the following command:
4+
#
5+
# pip-compile --allow-unsafe --no-annotate .riot/requirements/955b213.in
6+
#
7+
attrs==25.1.0
8+
coverage[toml]==7.6.12
9+
gevent==24.11.1
10+
greenlet==3.1.1
11+
gunicorn[gevent]==23.0.0
12+
hypothesis==6.45.0
13+
iniconfig==2.0.0
14+
lz4==4.4.3
15+
mock==5.2.0
16+
opentracing==2.4.0
17+
packaging==24.2
18+
pluggy==1.5.0
19+
py-cpuinfo==8.0.0
20+
pytest==8.3.5
21+
pytest-asyncio==0.21.1
22+
pytest-benchmark==5.1.0
23+
pytest-cov==6.0.0
24+
pytest-cpp==2.6.0
25+
pytest-mock==3.14.0
26+
pytest-randomly==3.16.0
27+
sortedcontainers==2.4.0
28+
uwsgi==2.0.28
29+
zope-event==5.0
30+
zope-interface==7.2
31+
32+
# The following packages are considered to be unsafe in a requirements file:
33+
setuptools==75.8.2

.riot/requirements/cb9a5da.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#
2+
# This file is autogenerated by pip-compile with Python 3.13
3+
# by the following command:
4+
#
5+
# pip-compile --allow-unsafe --no-annotate .riot/requirements/cb9a5da.in
6+
#
7+
attrs==25.1.0
8+
coverage[toml]==7.6.12
9+
gunicorn==23.0.0
10+
hypothesis==6.45.0
11+
iniconfig==2.0.0
12+
lz4==4.4.3
13+
mock==5.2.0
14+
opentracing==2.4.0
15+
packaging==24.2
16+
pluggy==1.5.0
17+
protobuf==4.22.0
18+
py-cpuinfo==8.0.0
19+
pytest==8.3.5
20+
pytest-asyncio==0.21.1
21+
pytest-benchmark==5.1.0
22+
pytest-cov==6.0.0
23+
pytest-cpp==2.6.0
24+
pytest-mock==3.14.0
25+
pytest-randomly==3.16.0
26+
sortedcontainers==2.4.0
27+
uwsgi==2.0.28

.riot/requirements/d449a90.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#
2+
# This file is autogenerated by pip-compile with Python 3.13
3+
# by the following command:
4+
#
5+
# pip-compile --allow-unsafe --no-annotate .riot/requirements/d449a90.in
6+
#
7+
attrs==25.1.0
8+
coverage[toml]==7.6.12
9+
gunicorn==23.0.0
10+
hypothesis==6.45.0
11+
iniconfig==2.0.0
12+
lz4==4.4.3
13+
mock==5.2.0
14+
opentracing==2.4.0
15+
packaging==24.2
16+
pluggy==1.5.0
17+
protobuf==6.30.0
18+
py-cpuinfo==8.0.0
19+
pytest==8.3.5
20+
pytest-asyncio==0.21.1
21+
pytest-benchmark==5.1.0
22+
pytest-cov==6.0.0
23+
pytest-cpp==2.6.0
24+
pytest-mock==3.14.0
25+
pytest-randomly==3.16.0
26+
sortedcontainers==2.4.0
27+
uwsgi==2.0.28
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
features:
3+
- |
4+
profiling: Stack v2 supports CPython 3.13.
5+

riotfile.py

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2926,28 +2926,9 @@ def select_pys(min_version: str = MIN_PYTHON_VERSION, max_version: str = MAX_PYT
29262926
),
29272927
],
29282928
),
2929-
# Python 3.11
2930-
Venv(
2931-
pys="3.11",
2932-
pkgs={"uwsgi": latest},
2933-
venvs=[
2934-
Venv(
2935-
pkgs={
2936-
"protobuf": ["==4.22.0", latest],
2937-
},
2938-
),
2939-
# Gevent
2940-
Venv(
2941-
env={
2942-
"DD_PROFILE_TEST_GEVENT": "1",
2943-
},
2944-
pkgs={"gunicorn[gevent]": latest, "gevent": latest},
2945-
),
2946-
],
2947-
),
2948-
# Python 3.12
2929+
# Python >= 3.11
29492930
Venv(
2950-
pys="3.12",
2931+
pys=select_pys("3.11", "3.13"),
29512932
pkgs={"uwsgi": latest},
29522933
venvs=[
29532934
Venv(

setup.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -666,14 +666,13 @@ def get_exts_for(name):
666666
)
667667
)
668668

669-
if sys.version_info < (3, 13):
670-
ext_modules.append(
671-
CMakeExtension(
672-
"ddtrace.internal.datadog.profiling.stack_v2._stack_v2",
673-
source_dir=STACK_V2_DIR,
674-
optional=False,
675-
),
676-
)
669+
ext_modules.append(
670+
CMakeExtension(
671+
"ddtrace.internal.datadog.profiling.stack_v2._stack_v2",
672+
source_dir=STACK_V2_DIR,
673+
optional=False,
674+
),
675+
)
677676

678677
else:
679678
ext_modules = []

tests/profiling_v2/collector/test_memalloc.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import inspect
22
import os
3+
import sys
34
import threading
45

56
import pytest
@@ -9,6 +10,9 @@
910
from tests.profiling.collector import pprof_utils
1011

1112

13+
PY_313_OR_ABOVE = sys.version_info[:2] >= (3, 13)
14+
15+
1216
def _allocate_1k():
1317
return [object() for _ in range(1000)]
1418

@@ -140,6 +144,7 @@ def test_heap_profiler_large_heap_overhead():
140144
# Un-skip this test if/when we improve the worst-case performance of the
141145
# heap profiler for large heaps
142146
from ddtrace.profiling import Profiler
147+
from tests.profiling_v2.collector.test_memalloc import one
143148

144149
p = Profiler()
145150
p.start()
@@ -149,10 +154,10 @@ def test_heap_profiler_large_heap_overhead():
149154

150155
junk = []
151156
for i in range(count):
152-
b1 = bytearray(thing_size)
153-
b2 = bytearray(2 * thing_size)
154-
b3 = bytearray(3 * thing_size)
155-
b4 = bytearray(4 * thing_size)
157+
b1 = one(thing_size)
158+
b2 = one(2 * thing_size)
159+
b3 = one(3 * thing_size)
160+
b4 = one(4 * thing_size)
156161
t = (b1, b2, b3, b4)
157162
junk.append(t)
158163

@@ -164,20 +169,22 @@ def test_heap_profiler_large_heap_overhead():
164169
# one, two, three, and four exist to give us distinct things
165170
# we can find in the profile without depending on something
166171
# like the line number at which an allocation happens
172+
# Python 3.13 changed bytearray to use an allocation domain that we don't
173+
# currently profile, so we use None instead of bytearray to test.
167174
def one(size):
168-
return bytearray(size)
175+
return (None,) * size if PY_313_OR_ABOVE else bytearray(size)
169176

170177

171178
def two(size):
172-
return bytearray(size)
179+
return (None,) * size if PY_313_OR_ABOVE else bytearray(size)
173180

174181

175182
def three(size):
176-
return bytearray(size)
183+
return (None,) * size if PY_313_OR_ABOVE else bytearray(size)
177184

178185

179186
def four(size):
180-
return bytearray(size)
187+
return (None,) * size if PY_313_OR_ABOVE else bytearray(size)
181188

182189

183190
class HeapInfo:

0 commit comments

Comments
 (0)