Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
dffb41c
chore(profiling) enable memalloc to use sample
danielsn Nov 7, 2025
7880b12
cursor turned C into C++
danielsn Nov 7, 2025
550c89f
non editable builds too
danielsn Nov 7, 2025
994511c
Merge branch 'main' into dsn/memalloc-use-sample
danielsn Nov 7, 2025
1454456
Merge branch 'main' into dsn/memalloc-use-sample
danielsn Nov 10, 2025
00b74c0
try to fix ci
danielsn Nov 10, 2025
c56659a
fix atomic
danielsn Nov 10, 2025
7d4c808
fix ci again
danielsn Nov 10, 2025
0a0dc5b
format
danielsn Nov 10, 2025
7d9a2b1
more ci fixes
danielsn Nov 10, 2025
33734da
more ci fix
danielsn Nov 10, 2025
c30948c
more ci fixes
danielsn Nov 10, 2025
21065dd
even more ci fixes
danielsn Nov 10, 2025
104710d
more cmake changes
danielsn Nov 10, 2025
a6b3c43
format cmake
danielsn Nov 10, 2025
badaf86
AI trying again to fix ci
danielsn Nov 10, 2025
c4cd5c8
only build memalloc profiler for windows
danielsn Nov 10, 2025
ed8aa12
try to fix sanitizers
danielsn Nov 10, 2025
e55c5e0
sanitizer discovery mode
danielsn Nov 10, 2025
d7a05b2
yet another try at asan
danielsn Nov 10, 2025
5ae6cfd
format
danielsn Nov 10, 2025
d2c763b
Merge branch 'main' into dsn/memalloc-use-sample
danielsn Nov 10, 2025
55827a5
test C++ 20
danielsn Nov 11, 2025
402dc27
Merge branch 'main' into dsn/memalloc-use-sample
danielsn Nov 11, 2025
3620d53
test not installing libatomic at all
danielsn Nov 11, 2025
eacd51d
Update ddtrace/profiling/collector/_utils.h
danielsn Nov 11, 2025
dccc22e
cformat
danielsn Nov 11, 2025
64e0523
types in setup.py
danielsn Nov 11, 2025
e02dd16
Merge branch 'main' into dsn/memalloc-use-sample
danielsn Nov 11, 2025
1c4f663
setup.py uses append
danielsn Nov 11, 2025
7244664
cleaner setup.py
danielsn Nov 11, 2025
fd3cca6
setup.py type annotations
danielsn Nov 11, 2025
de890ab
Merge branch 'main' into dsn/memalloc-use-sample
danielsn Nov 11, 2025
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 .github/workflows/build_python_3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ jobs:
CIBW_BEFORE_ALL_LINUX: |
if [[ "$(uname -m)-$(uname -i)-$(uname -o | tr '[:upper:]' '[:lower:]')-$(ldd --version 2>&1 | head -n 1 | awk '{print $1}')" != "i686-unknown-linux-musl" ]]; then
if command -v yum &> /dev/null; then
yum install -y libatomic.i686
yum install -y libatomic.i686 || echo "libatomic.i686 not available, assuming it's already installed"
fi
curl -sSf https://sh.rustup.rs | sh -s -- -y;
fi
Expand Down
3 changes: 2 additions & 1 deletion ddtrace/internal/datadog/profiling/ddup/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ set_target_properties(${EXTENSION_NAME} PROPERTIES PREFIX "")
set_target_properties(${EXTENSION_NAME} PROPERTIES SUFFIX "")

# RPATH is needed for sofile discovery at runtime, since Python packages are not installed in the system path. This is
# typical.
# typical. Use BUILD_WITH_INSTALL_RPATH to avoid rpath manipulation during install phase which can cause conflicts
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
if(APPLE)
set_target_properties(${EXTENSION_NAME} PROPERTIES INSTALL_RPATH "@loader_path/..")
elseif(UNIX)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ set_target_properties(${EXTENSION_NAME} PROPERTIES PREFIX "")
set_target_properties(${EXTENSION_NAME} PROPERTIES SUFFIX "")

# RPATH is needed for sofile discovery at runtime, since Python packages are not installed in the system path. This is
# typical.
# typical. Use BUILD_WITH_INSTALL_RPATH to avoid rpath manipulation during install phase which can cause conflicts
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
if(APPLE)
set_target_properties(${EXTENSION_NAME} PROPERTIES INSTALL_RPATH "@loader_path/..")
elseif(UNIX)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ heap_tracker_thaw_no_cpython(heap_tracker_t* heap_tracker, size_t* n_to_free)
*n_to_free = heap_tracker->freezer.frees.count;
if (*n_to_free > 0) {
/* TODO: can we put traceback_t* directly in freezer.frees so we don't need new storage? */
to_free = malloc(*n_to_free * sizeof(traceback_t*));
to_free = static_cast<traceback_t**>(malloc(*n_to_free * sizeof(traceback_t*)));
for (size_t i = 0; i < *n_to_free; i++) {
traceback_t* tb = memalloc_heap_map_remove(heap_tracker->allocs_m, heap_tracker->freezer.frees.tab[i]);
to_free[i] = tb;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ typedef struct memalloc_heap_map_iter_t
memalloc_heap_map_t*
memalloc_heap_map_new()
{
memalloc_heap_map_t* m = calloc(sizeof(memalloc_heap_map_t), 1);
memalloc_heap_map_t* m = static_cast<memalloc_heap_map_t*>(calloc(sizeof(memalloc_heap_map_t), 1));
m->map = HeapSamples_new(0);
return m;
}
Expand All @@ -104,7 +104,9 @@ memalloc_heap_map_size(memalloc_heap_map_t* m)
traceback_t*
memalloc_heap_map_insert(memalloc_heap_map_t* m, void* key, traceback_t* value)
{
HeapSamples_Entry k = { key = key, value = value };
HeapSamples_Entry k;
k.key = key;
k.val = value;
HeapSamples_Insert res = HeapSamples_insert(&m->map, &k);
traceback_t* prev = NULL;
if (!res.inserted) {
Expand Down Expand Up @@ -187,7 +189,7 @@ memalloc_heap_map_delete(memalloc_heap_map_t* m)
memalloc_heap_map_iter_t*
memalloc_heap_map_iter_new(memalloc_heap_map_t* m)
{
memalloc_heap_map_iter_t* it = malloc(sizeof(memalloc_heap_map_iter_t));
memalloc_heap_map_iter_t* it = static_cast<memalloc_heap_map_iter_t*>(malloc(sizeof(memalloc_heap_map_iter_t)));
if (it) {
it->iter = HeapSamples_citer(&m->map);
}
Expand Down
4 changes: 4 additions & 0 deletions ddtrace/profiling/collector/_memalloc_reentrant.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <pthread.h>
#ifdef __cplusplus
#include <atomic>
#else
#include <stdatomic.h>
#endif
#include <time.h>
#include <unistd.h>
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ memalloc_tb_buffer_pool_get(memalloc_tb_buffer_pool* pool, uint16_t max_nframe)
pool->pool[pool->count - 1] = NULL;
pool->count--;
} else {
t = malloc(TRACEBACK_SIZE(max_nframe));
t = static_cast<traceback_t*>(malloc(TRACEBACK_SIZE(max_nframe)));
}
return t;
}
Expand Down Expand Up @@ -238,7 +238,7 @@ memalloc_frame_to_traceback(PyFrameObject* pyframe, uint16_t max_nframe)
}

size_t traceback_size = TRACEBACK_SIZE(traceback_buffer->nframe);
traceback_t* traceback = PyMem_RawMalloc(traceback_size);
traceback_t* traceback = static_cast<traceback_t*>(PyMem_RawMalloc(traceback_size));

if (traceback)
memcpy(traceback, traceback_buffer, traceback_size);
Expand Down
13 changes: 13 additions & 0 deletions ddtrace/profiling/collector/_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,27 @@ random_range(uint64_t max)

#define DO_NOTHING(...)

#ifdef __cplusplus
#define p_new(type, count) static_cast<type*>(PyMem_RawMalloc(sizeof(type) * (count)))
#else
#define p_new(type, count) PyMem_RawMalloc(sizeof(type) * (count))
#endif

#define p_delete(mem_p) PyMem_RawFree(mem_p);
// Allocate at least 16 and 50% more than requested to avoid allocating items one by one.
#define p_alloc_nr(x) (((x) + 16) * 3 / 2)

#ifdef __cplusplus
#define p_realloc(p, count) \
do { \
(p) = static_cast<decltype(p)>(PyMem_RawRealloc((p), sizeof(*p) * (count))); \
} while (0)
#else
#define p_realloc(p, count) \
do { \
(p) = PyMem_RawRealloc((p), sizeof(*p) * (count)); \
} while (0)
#endif

#define p_grow(p, goalnb, allocnb) \
do { \
Expand Down
69 changes: 58 additions & 11 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,31 @@ def build_extension(self, ext):
return
raise
else:
# For the memalloc extension, dynamically add libdd_wrapper to extra_objects
# if it's not already there (needed for non-editable builds)
if ext.name == "ddtrace.profiling.collector._memalloc" and CURRENT_OS in ("Linux", "Darwin"):
dd_wrapper_suffix = sysconfig.get_config_var("EXT_SUFFIX")

# For non-editable builds, remove any source directory paths and add build directory path
if not (IS_EDITABLE or getattr(self, "inplace", False)):
# Remove any source directory libdd_wrapper paths
source_wrapper_dir = Path(__file__).parent / "ddtrace" / "internal" / "datadog" / "profiling"
ext.extra_objects = [
obj for obj in ext.extra_objects if not obj.startswith(str(source_wrapper_dir))
]

# Add build directory path
wrapper_dir = (
Path(__file__).parent / Path(self.build_lib) / "ddtrace" / "internal" / "datadog" / "profiling"
)
wrapper_path = wrapper_dir / f"libdd_wrapper{dd_wrapper_suffix}"

if wrapper_path.exists():
wrapper_path_str = str(wrapper_path)
if wrapper_path_str not in ext.extra_objects:
ext.extra_objects.append(wrapper_path_str)
print(f"Added libdd_wrapper to link: {wrapper_path_str}")

super().build_extension(ext)

if COMPILE_MODE.lower() in ("release", "minsizerel"):
Expand Down Expand Up @@ -1038,27 +1063,49 @@ def get_exts_for(name):


if not IS_PYSTON:
# Determine the libdd_wrapper filename with the Python extension suffix
# For editable builds, the library will be in the source directory
# For regular builds, it will be built during build_ext and found via the build system
_dd_wrapper_suffix = sysconfig.get_config_var("EXT_SUFFIX")
_dd_wrapper_source_path = (
HERE / "ddtrace" / "internal" / "datadog" / "profiling" / f"libdd_wrapper{_dd_wrapper_suffix}"
)

# Only include extra_objects if the library already exists (editable builds)
# For non-editable builds, build_libdd_wrapper() will handle it and the linker will find it
_dd_wrapper_extra_objects = []
if CURRENT_OS in ("Linux", "Darwin") and _dd_wrapper_source_path.exists():
_dd_wrapper_extra_objects = [str(_dd_wrapper_source_path)]

ext_modules: t.List[t.Union[Extension, Cython.Distutils.Extension, RustExtension]] = [
Extension(
"ddtrace.profiling.collector._memalloc",
sources=[
"ddtrace/profiling/collector/_memalloc.c",
"ddtrace/profiling/collector/_memalloc_tb.c",
"ddtrace/profiling/collector/_memalloc_heap.c",
"ddtrace/profiling/collector/_memalloc_reentrant.c",
"ddtrace/profiling/collector/_memalloc_heap_map.c",
"ddtrace/profiling/collector/_memalloc.cpp",
"ddtrace/profiling/collector/_memalloc_tb.cpp",
"ddtrace/profiling/collector/_memalloc_heap.cpp",
"ddtrace/profiling/collector/_memalloc_reentrant.cpp",
"ddtrace/profiling/collector/_memalloc_heap_map.cpp",
],
include_dirs=[
"ddtrace/internal/datadog/profiling/dd_wrapper/include",
],
extra_objects=_dd_wrapper_extra_objects,
extra_link_args=(
["-Wl,-rpath,$ORIGIN/../../../internal/datadog/profiling", "-latomic"]
if CURRENT_OS == "Linux"
else ["-Wl,-rpath,@loader_path/../../../internal/datadog/profiling"]
if CURRENT_OS == "Darwin"
else []
),
language="c++",
extra_compile_args=(
debug_compile_args
# If NDEBUG is set, assert statements are compiled out. Make
# sure we explicitly set this for normal builds, and explicitly
# _unset_ it for debug builds in case the CFLAGS from sysconfig
# include -DNDEBUG
+ (["-DNDEBUG"] if not debug_compile_args else ["-UNDEBUG"])
+ ["-D_POSIX_C_SOURCE=200809L", "-std=c11"]
+ ["-D_POSIX_C_SOURCE=200809L", "-std=c++17"]
+ fast_build_args
if CURRENT_OS != "Windows"
else ["/std:c11", "/experimental:c11atomics"]
else ["/std:c++17", "/MT"]
),
),
Extension(
Expand Down
Loading