Skip to content
Merged
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
22 changes: 7 additions & 15 deletions ddtrace/profiling/collector/_memalloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ typedef struct
PyMemAllocatorEx pymem_allocator_obj;
/* The domain we are tracking */
PyMemAllocatorDomain domain;
/* The maximum number of events for allocation tracking */
uint16_t max_events;
/* The maximum number of frames collected in stack traces */
uint16_t max_nframe;

Expand Down Expand Up @@ -93,15 +91,16 @@ memalloc_realloc(void* ctx, void* ptr, size_t new_size)
}

PyDoc_STRVAR(memalloc_start__doc__,
"start($module, max_nframe, max_events, heap_sample_size)\n"
"start($module, max_nframe, heap_sample_interval)\n"
"--\n"
"\n"
"Start tracing Python memory allocations.\n"
"\n"
"Sets the maximum number of frames stored in the traceback of a\n"
"trace to max_nframe and the maximum number of events to max_events.\n"
"Set heap_sample_size to the granularity of the heap profiler, in bytes.\n"
"If heap_sample_size is set to 0, it is disabled entirely.\n");
"trace to max_nframe.\n"
"Sets the average number of bytes allocated between samples to\n"
"heap_sample_interval.\n"
"If heap_sample_interval is set to 0, it is disabled entirely.\n");
static PyObject*
memalloc_start(PyObject* Py_UNUSED(module), PyObject* args)
{
Expand All @@ -117,11 +116,11 @@ memalloc_start(PyObject* Py_UNUSED(module), PyObject* args)
srand(atoi(val));
}

long max_nframe, max_events;
long max_nframe;
long long int heap_sample_size;

/* Store short ints in ints so we're sure they fit */
if (!PyArg_ParseTuple(args, "llL", &max_nframe, &max_events, &heap_sample_size))
if (!PyArg_ParseTuple(args, "lL", &max_nframe, &heap_sample_size))
return NULL;

if (max_nframe < 1 || max_nframe > TRACEBACK_MAX_NFRAME) {
Expand All @@ -131,13 +130,6 @@ memalloc_start(PyObject* Py_UNUSED(module), PyObject* args)

global_memalloc_ctx.max_nframe = (uint16_t)max_nframe;

if (max_events < 1 || max_events > TRACEBACK_ARRAY_MAX_COUNT) {
PyErr_Format(PyExc_ValueError, "the number of events must be in range [1; %u]", TRACEBACK_ARRAY_MAX_COUNT);
return NULL;
}

global_memalloc_ctx.max_events = (uint16_t)max_events;

if (heap_sample_size < 0 || heap_sample_size > MAX_HEAP_SAMPLE_SIZE) {
PyErr_Format(PyExc_ValueError, "the heap sample size must be in range [0; %u]", MAX_HEAP_SAMPLE_SIZE);
return NULL;
Expand Down
2 changes: 1 addition & 1 deletion ddtrace/profiling/collector/_memalloc.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ StackType = event.StackTraceType
# (stack, thread_id)
TracebackType = typing.Tuple[StackType, int]

def start(max_nframe: int, max_events: int, heap_sample_size: int) -> None: ...
def start(max_nframe: int, heap_sample_interval: int) -> None: ...
def stop() -> None: ...
def heap() -> typing.List[typing.Tuple[TracebackType, int, int, int]]: ...
11 changes: 2 additions & 9 deletions ddtrace/profiling/collector/memalloc.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,14 @@
class MemoryCollector(collector.PeriodicCollector):
"""Memory allocation collector."""

_DEFAULT_MAX_EVENTS = 16
_DEFAULT_INTERVAL = 0.5

def __init__(
self,
_interval: float = _DEFAULT_INTERVAL,
_max_events: Optional[int] = None,
max_nframe: Optional[int] = None,
heap_sample_size: Optional[int] = None,
ignore_profiler: Optional[bool] = None,
):
super().__init__()
self._interval: float = _interval
# TODO make this dynamic based on the 1. interval and 2. the max number of events allowed in the Recorder
self._max_events: int = _max_events if _max_events is not None else config.memory.events_buffer
self.max_nframe: int = max_nframe if max_nframe is not None else config.max_frames
self.heap_sample_size: int = heap_sample_size if heap_sample_size is not None else config.heap.sample_size
self.ignore_profiler: bool = ignore_profiler if ignore_profiler is not None else config.ignore_profiler
Expand All @@ -56,13 +49,13 @@ def _start_service(self):
raise collector.CollectorUnavailable

try:
_memalloc.start(self.max_nframe, self._max_events, self.heap_sample_size)
_memalloc.start(self.max_nframe, self.heap_sample_size)
except RuntimeError:
# This happens on fork because we don't call the shutdown hook since
# the thread responsible for doing so is not running in the child
# process. Therefore we stop and restart the collector instead.
_memalloc.stop()
_memalloc.start(self.max_nframe, self._max_events, self.heap_sample_size)
_memalloc.start(self.max_nframe, self.heap_sample_size)

super(MemoryCollector, self)._start_service()

Expand Down
10 changes: 1 addition & 9 deletions ddtrace/settings/profiling.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,6 @@ class ProfilingConfig(DDConfig):
help="",
)

max_events = DDConfig.v(
int,
"max_events",
default=16384,
help_type="Integer",
help="",
)

upload_interval = DDConfig.v(
float,
"upload_interval",
Expand Down Expand Up @@ -339,7 +331,7 @@ class ProfilingConfigHeap(DDConfig):
"sample_size",
default=None,
help_type="Integer",
help="",
help="Average number of bytes allocated between memory profiler samples",
)
sample_size = DDConfig.d(int, _derive_default_heap_sample_size)

Expand Down
29 changes: 13 additions & 16 deletions tests/profiling/collector/test_memalloc.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,40 +19,37 @@


def test_start_twice():
_memalloc.start(64, 1000, 512)
_memalloc.start(64, 512)
with pytest.raises(RuntimeError):
_memalloc.start(64, 1000, 512)
_memalloc.start(64, 512)
_memalloc.stop()


def test_start_wrong_arg():
with pytest.raises(TypeError, match="function takes exactly 3 arguments \\(1 given\\)"):
with pytest.raises(TypeError, match="function takes exactly 2 arguments \\(1 given\\)"):
_memalloc.start(2)

with pytest.raises(ValueError, match="the number of frames must be in range \\[1; 65535\\]"):
_memalloc.start(429496, 1000, 1)
_memalloc.start(429496, 1)

with pytest.raises(ValueError, match="the number of frames must be in range \\[1; 65535\\]"):
_memalloc.start(-1, 1000, 1)

with pytest.raises(ValueError, match="the number of events must be in range \\[1; 65535\\]"):
_memalloc.start(64, -1, 1)
_memalloc.start(-1, 1)

with pytest.raises(
ValueError,
match="the heap sample size must be in range \\[0; 4294967295\\]",
):
_memalloc.start(64, 1000, -1)
_memalloc.start(64, -1)

with pytest.raises(
ValueError,
match="the heap sample size must be in range \\[0; 4294967295\\]",
):
_memalloc.start(64, 1000, 345678909876)
_memalloc.start(64, 345678909876)


def test_start_stop():
_memalloc.start(1, 1, 1)
_memalloc.start(1, 1)
_memalloc.stop()


Expand All @@ -69,7 +66,7 @@ def _pre_allocate_1k():

def test_iter_events():
max_nframe = 32
collector = memalloc.MemoryCollector(max_nframe=max_nframe, _max_events=10000, heap_sample_size=64)
collector = memalloc.MemoryCollector(max_nframe=max_nframe, heap_sample_size=64)
with collector:
_allocate_1k()
samples = collector.test_snapshot()
Expand Down Expand Up @@ -105,7 +102,7 @@ def test_iter_events():

def test_iter_events_dropped():
max_nframe = 32
collector = memalloc.MemoryCollector(max_nframe=max_nframe, _max_events=100, heap_sample_size=64)
collector = memalloc.MemoryCollector(max_nframe=max_nframe, heap_sample_size=64)
with collector:
_allocate_1k()
samples = collector.test_snapshot()
Expand All @@ -130,7 +127,7 @@ def test_iter_events_not_started():
def test_iter_events_multi_thread():
max_nframe = 32
t = threading.Thread(target=_allocate_1k)
collector = memalloc.MemoryCollector(max_nframe=max_nframe, _max_events=10000, heap_sample_size=64)
collector = memalloc.MemoryCollector(max_nframe=max_nframe, heap_sample_size=64)
with collector:
_allocate_1k()
t.start()
Expand Down Expand Up @@ -179,7 +176,7 @@ def test_iter_events_multi_thread():

def test_heap():
max_nframe = 32
collector = memalloc.MemoryCollector(max_nframe=max_nframe, _max_events=10000, heap_sample_size=1024)
collector = memalloc.MemoryCollector(max_nframe=max_nframe, heap_sample_size=1024)
with collector:
_test_heap_impl(collector, max_nframe)

Expand Down Expand Up @@ -298,7 +295,7 @@ def _test_heap_impl(collector, max_nframe):

def test_heap_stress():
# This should run for a few seconds, and is enough to spot potential segfaults.
_memalloc.start(64, 64, 1024)
_memalloc.start(64, 1024)
try:
x = []

Expand Down
4 changes: 2 additions & 2 deletions tests/profiling_v2/collector/test_memalloc.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ def test_heap_profiler_sampling_accuracy(sample_interval):
# pass for an arbitrary seed.
old = os.environ.get("_DD_MEMALLOC_DEBUG_RNG_SEED")
os.environ["_DD_MEMALLOC_DEBUG_RNG_SEED"] = "42"
_memalloc.start(32, 1000, sample_interval)
_memalloc.start(32, sample_interval)
# Put the env var back in the state we found it
if old is not None:
os.environ["_DD_MEMALLOC_DEBUG_RNG_SEED"] = old
Expand Down Expand Up @@ -679,7 +679,7 @@ def test_memory_collector_allocation_during_shutdown():

from ddtrace.profiling.collector import _memalloc

_memalloc.start(32, 1000, 512)
_memalloc.start(32, 512)

shutdown_event = threading.Event()
allocation_thread = None
Expand Down
1 change: 0 additions & 1 deletion tests/telemetry/test_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,6 @@ def test_app_started_event_configuration_override(test_agent_session, run_python
{"name": "DD_PROFILING_IGNORE_PROFILER", "origin": "default", "value": False},
{"name": "DD_PROFILING_LOCK_ENABLED", "origin": "env_var", "value": False},
{"name": "DD_PROFILING_LOCK_NAME_INSPECT_DIR", "origin": "default", "value": True},
{"name": "DD_PROFILING_MAX_EVENTS", "origin": "default", "value": 16384},
{"name": "DD_PROFILING_MAX_FRAMES", "origin": "env_var", "value": 512},
{"name": "DD_PROFILING_MAX_TIME_USAGE_PCT", "origin": "default", "value": 1.0},
{"name": "DD_PROFILING_MEMORY_ENABLED", "origin": "env_var", "value": False},
Expand Down
Loading