Skip to content

Commit 8a0419f

Browse files
[python_basic_memory] Initial commit
1 parent 0253684 commit 8a0419f

File tree

5 files changed

+213
-0
lines changed

5 files changed

+213
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
ARG BASE_IMAGE="prof-python-3.11"
2+
FROM $BASE_IMAGE
3+
4+
# Copy the Python target into the container
5+
COPY ./scenarios/python_basic_memory_3.11/requirements.txt /app/requirements.txt
6+
RUN chmod 644 /app/*
7+
8+
RUN pip install -r /app/requirements.txt
9+
10+
COPY ./scenarios/python_basic_memory_3.11/main.py /app/main.py
11+
12+
# Set the working directory to the location of the program
13+
WORKDIR /app
14+
15+
ENV DD_TRACE_DEBUG=false
16+
17+
# Run the program when the container starts
18+
CMD python main.py
19+
# CMD ddprof -l notice --preset cpu_live_heap python main.py
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
## Basic Memory Profiling
2+
3+
This test validates that the Datadog Python profiler correctly profiles memory allocations.
4+
5+
## Test Application
6+
7+
- Creates a list of `None` of a target size (1e6 items)
8+
- The list is then filled by two functions `allocate_memory_1` and `allocate_memory_2` that allocate
9+
1024 bytes and 3 \* 1024 bytes respectively. The list is filled until the last item is not `None`.
10+
11+
## Expected Profile
12+
13+
### Samples
14+
15+
Those represent the number of times the function called the allocator. This is hard to estimate manually
16+
because of how the sampling logic works
17+
(see [here](https://github.com/datadog/dd-trace-py/blob/9af78604497d1993826c59c351e1a4e53f817783/ddtrace/profiling/collector/_memalloc_tb.cpp#L329-L360)),
18+
but the numbers are stable.
19+
20+
- `^<module>;run;allocate_memory_1$` should be present and should account for 25% of the samples.
21+
- `^<module>;run;allocate_memory_2$` should be present and should account for 45% of the samples.
22+
- `^<module>;__init__;grow_list$` should be present and should account for 20% of the samples.
23+
24+
### Space
25+
26+
- `^<module>;run;allocate_memory_1$` should be present and should account for 25% of the space (1024 bytes)
27+
- `^<module>;run;allocate_memory_2$` should be present and should account for 75% of the space (3 \* 1024 bytes)
28+
- `^<module>;__init__;grow_list$` should be present and should account for 3% of the space
29+
(list container, noise compared to the other allocations)
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
{
2+
"test_name": "python_memory_basic",
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: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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+
# Simple application that creates two threads with different durations:
29+
# - MainThread runs target() for 2 seconds
30+
# - Worker Thread-1 runs target() for 1 second
31+
# The profiler should capture both threads with their respective durations.
32+
prof = Profiler()
33+
prof.start() # Should be as early as possible, eg before other imports, to ensure everything is profiled
34+
35+
Target().run()
36+
37+
prof.stop()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ddtrace==4.0.0

0 commit comments

Comments
 (0)