Skip to content
Draft
Show file tree
Hide file tree
Changes from 13 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
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ class Sample
std::vector<int64_t> values = {};

// Additional metadata
int64_t endtime_ns = 0; // end of the event
int64_t endtime_ns = 0; // end of the event
bool reverse_locations = false; // whether to reverse locations when exporting/flushing

// Backing memory for string copies
internal::StringArena string_storage{};
Expand All @@ -101,6 +102,8 @@ class Sample
bool push_release(int64_t lock_time, int64_t count);
bool push_alloc(int64_t size, int64_t count);
bool push_heap(int64_t size);
void reset_alloc();
void reset_heap();
bool push_gpu_gputime(int64_t time, int64_t count);
bool push_gpu_memory(int64_t size, int64_t count);
bool push_gpu_flops(int64_t flops, int64_t count);
Expand Down Expand Up @@ -132,8 +135,15 @@ class Sample
int64_t line // for ddog_prof_Location
);

// Set whether to reverse locations when exporting/flushing
void set_reverse_locations(bool reverse) { reverse_locations = reverse; }

// Flushes the current buffer, clearing it
bool flush_sample(bool reverse_locations = false);
bool flush_sample();

// Exports the sample data to the profile without clearing buffers
// This is useful when the Sample object is embedded and will be destroyed later
bool export_sample();

static ProfileBorrow profile_borrow();
static void postfork_child();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ ddup_flush_sample(Datadog::Sample* sample) // cppcheck-suppress unusedFunction
void
ddup_flush_sample_v2(Datadog::Sample* sample) // cppcheck-suppress unusedFunction
{
sample->flush_sample(/*reverse_locations*/ true);
sample->set_reverse_locations(true);
sample->flush_sample();
}

void
Expand Down
45 changes: 41 additions & 4 deletions ddtrace/internal/datadog/profiling/dd_wrapper/src/sample.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,25 @@ Datadog::Sample::clear_buffers()
labels.clear();
locations.clear();
dropped_frames = 0;
reverse_locations = false;
string_storage.reset();
}

bool
Datadog::Sample::flush_sample(bool reverse_locations)
Datadog::Sample::flush_sample()
{
// Export the sample data (export_sample handles dropped frames)
auto ret = export_sample();

// Clear buffers after exporting
clear_buffers();
return ret;
}

bool
Datadog::Sample::export_sample()
{
// Handle dropped frames by adding them to locations if needed
if (dropped_frames > 0) {
const std::string name =
"<" + std::to_string(dropped_frames) + " frame" + (1 == dropped_frames ? "" : "s") + " omitted>";
Expand All @@ -146,6 +159,7 @@ Datadog::Sample::flush_sample(bool reverse_locations)

if (reverse_locations) {
std::reverse(locations.begin(), locations.end());
reverse_locations = false; // Reset after reversing
}

const ddog_prof_Sample sample = {
Expand All @@ -154,9 +168,8 @@ Datadog::Sample::flush_sample(bool reverse_locations)
.labels = { labels.data(), labels.size() },
};

const bool ret = profile_state.collect(sample, endtime_ns);
clear_buffers();
return ret;
// Export to profile without clearing buffers
return profile_state.collect(sample, endtime_ns);
}

bool
Expand Down Expand Up @@ -292,6 +305,30 @@ Datadog::Sample::push_heap(int64_t size)
return false;
}

void
Datadog::Sample::reset_alloc()
{
if (0U != (type_mask & SampleType::Allocation)) {
const size_t alloc_space_idx = profile_state.val().alloc_space;
const size_t alloc_count_idx = profile_state.val().alloc_count;
if (alloc_space_idx < values.size() && alloc_count_idx < values.size()) {
values[alloc_space_idx] = 0;
values[alloc_count_idx] = 0;
}
}
}

void
Datadog::Sample::reset_heap()
{
if (0U != (type_mask & SampleType::Heap)) {
const size_t heap_space_idx = profile_state.val().heap_space;
if (heap_space_idx < values.size()) {
values[heap_space_idx] = 0;
}
}
}

bool
Datadog::Sample::push_gpu_gputime(int64_t time, int64_t count)
{
Expand Down
27 changes: 15 additions & 12 deletions ddtrace/profiling/collector/_memalloc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
#include "_memalloc_tb.h"
#include "_pymacro.h"

// Ensure profile_state is initialized before creating Sample objects
#include "../../internal/datadog/profiling/dd_wrapper/include/ddup_interface.hpp"

typedef struct
{
PyMemAllocatorEx pymem_allocator_obj;
Expand Down Expand Up @@ -40,7 +43,7 @@ memalloc_free(void* ctx, void* ptr)
if (ptr == NULL)
return;

memalloc_heap_untrack(ptr);
memalloc_heap_untrack_no_cpython(ptr);

alloc->free(alloc->ctx, ptr);
}
Expand Down Expand Up @@ -82,7 +85,7 @@ memalloc_realloc(void* ctx, void* ptr, size_t new_size)
void* ptr2 = memalloc_ctx->pymem_allocator_obj.realloc(memalloc_ctx->pymem_allocator_obj.ctx, ptr, new_size);

if (ptr2) {
memalloc_heap_untrack(ptr);
memalloc_heap_untrack_no_cpython(ptr);
memalloc_heap_track(memalloc_ctx->max_nframe, ptr2, new_size, memalloc_ctx->domain);
}

Expand All @@ -108,6 +111,11 @@ memalloc_start(PyObject* Py_UNUSED(module), PyObject* args)
return NULL;
}

// Ensure profile_state is initialized before creating Sample objects
// This initializes the Sample::profile_state which is required for Sample objects to work correctly
// ddup_start() uses std::call_once, so it's safe to call multiple times
ddup_start();

char* val = getenv("_DD_MEMALLOC_DEBUG_RNG_SEED");
if (val) {
/* NB: we don't bother checking whether val is actually a valid integer.
Expand Down Expand Up @@ -144,7 +152,7 @@ memalloc_start(PyObject* Py_UNUSED(module), PyObject* args)
PyUnicode_InternInPlace(&object_string);
}

if (!memalloc_heap_tracker_init((uint32_t)heap_sample_size))
if (!memalloc_heap_tracker_init_no_cpython((uint32_t)heap_sample_size))
return NULL;

PyMemAllocatorEx alloc;
Expand Down Expand Up @@ -187,7 +195,7 @@ memalloc_stop(PyObject* Py_UNUSED(module), PyObject* Py_UNUSED(args))
* or memalloc_heap. The higher-level collector deals with this. */
PyMem_SetAllocator(PYMEM_DOMAIN_OBJ, &global_memalloc_ctx.pymem_allocator_obj);

memalloc_heap_tracker_deinit();
memalloc_heap_tracker_deinit_no_cpython();

/* Finally, we know in-progress sampling won't use the buffer pool, so clear it out */
traceback_t::deinit();
Expand All @@ -201,7 +209,7 @@ PyDoc_STRVAR(memalloc_heap_py__doc__,
"heap($module, /)\n"
"--\n"
"\n"
"Get the sampled heap representation.\n");
"Export sampled heap allocations to the pprof profile.\n");
static PyObject*
memalloc_heap_py(PyObject* Py_UNUSED(module), PyObject* Py_UNUSED(args))
{
Expand All @@ -210,7 +218,8 @@ memalloc_heap_py(PyObject* Py_UNUSED(module), PyObject* Py_UNUSED(args))
return NULL;
}

return memalloc_heap();
memalloc_heap_no_cpython();
Py_RETURN_NONE;
}

static PyMethodDef module_methods[] = { { "start", (PyCFunction)memalloc_start, METH_VARARGS, memalloc_start__doc__ },
Expand All @@ -234,11 +243,5 @@ PyInit__memalloc(void)
if (m == NULL)
return NULL;

// Initialize the DDFrame namedtuple class
// Do this early so we don't have complicated cleanup
if (!memalloc_ddframe_class_init()) {
return NULL;
}

return m;
}
Loading
Loading