Skip to content

Commit 73f2611

Browse files
authored
chore(profiling): convert memory profiler to C++ (#15236)
## Description Converts the memory profiler from C to C++. This enables us to use things like RAII to manage memory, and collections like std::vector instead of hand-rolled array lists. There should be no functional differences. ## Testing Refactor change that is covered by existing tests. ## Risks <!-- Note any risks associated with this change, or "None" if no risks --> ## Additional Notes <!-- Any other information that would be helpful for reviewers -->
1 parent bfc2ca1 commit 73f2611

16 files changed

+1249
-917
lines changed

ddtrace/profiling/collector/_memalloc.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include "_memalloc_reentrant.h"
1111
#include "_memalloc_tb.h"
1212
#include "_pymacro.h"
13-
#include "_utils.h"
1413

1514
typedef struct
1615
{
@@ -135,7 +134,7 @@ memalloc_start(PyObject* Py_UNUSED(module), PyObject* args)
135134
return NULL;
136135
}
137136

138-
if (memalloc_tb_init(global_memalloc_ctx.max_nframe) < 0)
137+
if (!traceback_t::init())
139138
return NULL;
140139

141140
if (object_string == NULL) {
@@ -145,7 +144,8 @@ memalloc_start(PyObject* Py_UNUSED(module), PyObject* args)
145144
PyUnicode_InternInPlace(&object_string);
146145
}
147146

148-
memalloc_heap_tracker_init((uint32_t)heap_sample_size);
147+
if (!memalloc_heap_tracker_init((uint32_t)heap_sample_size))
148+
return NULL;
149149

150150
PyMemAllocatorEx alloc;
151151

@@ -190,7 +190,7 @@ memalloc_stop(PyObject* Py_UNUSED(module), PyObject* Py_UNUSED(args))
190190
memalloc_heap_tracker_deinit();
191191

192192
/* Finally, we know in-progress sampling won't use the buffer pool, so clear it out */
193-
memalloc_tb_deinit();
193+
traceback_t::deinit();
194194

195195
memalloc_enabled = false;
196196

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
#ifndef _DDTRACE_MEMALLOC_DEBUG_H
2-
#define _DDTRACE_MEMALLOC_DEBUG_H
1+
#pragma once
32

4-
#include <assert.h>
5-
#include <stdbool.h>
3+
#include <cassert>
64

75
#include <Python.h>
86

@@ -17,47 +15,54 @@ memalloc_debug_gil_release(void)
1715
#endif
1816
}
1917

20-
typedef struct
18+
class memalloc_gil_debug_check_t
2119
{
22-
bool acquired;
23-
} memalloc_gil_debug_check_t;
20+
public:
21+
memalloc_gil_debug_check_t() = default;
2422

25-
static void
26-
memalloc_gil_debug_check_init(memalloc_gil_debug_check_t* c)
27-
{
28-
c->acquired = false;
29-
}
23+
bool acquired = false;
24+
};
3025

3126
#ifndef NDEBUG
32-
/* Annotate that we are beginning a critical section where we don't want other
33-
* memalloc code to run. If compiled assertions enabled, this will check that the
34-
* GIL is held and that the guard has not already been acquired elsewhere.
35-
*
36-
* This is a macro so we get file/line info where it's actually used */
37-
#define MEMALLOC_GIL_DEBUG_CHECK_ACQUIRE(c) \
38-
do { \
39-
memalloc_gil_debug_check_t* p = c; \
40-
assert(PyGILState_Check()); \
41-
assert(!p->acquired); \
42-
p->acquired = true; \
43-
} while (0)
44-
45-
/* Annotate that we are ending a critical section where we don't want other
46-
* memalloc code to run. If compiled assertions enabled, this will check that the
47-
* guard is acquired.
48-
*
49-
* This is a macro so we get file/line info where it's actually used */
50-
#define MEMALLOC_GIL_DEBUG_CHECK_RELEASE(c) \
51-
do { \
52-
memalloc_gil_debug_check_t* p = c; \
53-
assert(p->acquired); \
54-
p->acquired = false; \
55-
} while (0)
56-
#else
27+
/* RAII guard for GIL debug checking. Automatically acquires the guard in the
28+
* constructor and releases it in the destructor. */
29+
class memalloc_gil_debug_guard_t
30+
{
31+
public:
32+
explicit memalloc_gil_debug_guard_t(memalloc_gil_debug_check_t& guard)
33+
: guard_(guard)
34+
{
35+
assert(PyGILState_Check());
36+
assert(!guard_.acquired);
37+
guard_.acquired = true;
38+
}
5739

58-
#define MEMALLOC_GIL_DEBUG_CHECK_ACQUIRE(c)
59-
#define MEMALLOC_GIL_DEBUG_CHECK_RELEASE(c)
40+
~memalloc_gil_debug_guard_t()
41+
{
42+
assert(guard_.acquired);
43+
guard_.acquired = false;
44+
}
6045

61-
#endif
46+
// Non-copyable, non-movable
47+
memalloc_gil_debug_guard_t(const memalloc_gil_debug_guard_t&) = delete;
48+
memalloc_gil_debug_guard_t& operator=(const memalloc_gil_debug_guard_t&) = delete;
49+
memalloc_gil_debug_guard_t(memalloc_gil_debug_guard_t&&) = delete;
50+
memalloc_gil_debug_guard_t& operator=(memalloc_gil_debug_guard_t&&) = delete;
51+
52+
private:
53+
memalloc_gil_debug_check_t& guard_;
54+
};
55+
#else
56+
/* In release builds, the guard is a no-op */
57+
class memalloc_gil_debug_guard_t
58+
{
59+
public:
60+
explicit memalloc_gil_debug_guard_t(memalloc_gil_debug_check_t&) {}
6261

62+
// Non-copyable, non-movable
63+
memalloc_gil_debug_guard_t(const memalloc_gil_debug_guard_t&) = delete;
64+
memalloc_gil_debug_guard_t& operator=(const memalloc_gil_debug_guard_t&) = delete;
65+
memalloc_gil_debug_guard_t(memalloc_gil_debug_guard_t&&) = delete;
66+
memalloc_gil_debug_guard_t& operator=(memalloc_gil_debug_guard_t&&) = delete;
67+
};
6368
#endif

0 commit comments

Comments
 (0)