Skip to content

Commit f7d234a

Browse files
committed
Track Apache Arrow mimalloc allocations via arrow_mi_* hooks
1 parent d9e063f commit f7d234a

3 files changed

Lines changed: 86 additions & 0 deletions

File tree

src/memray/_memray/hooks.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,59 @@ aligned_alloc(size_t alignment, size_t size) noexcept
418418
return ret;
419419
}
420420

421+
// Apache Arrow mimalloc wrapper hooks (apache/arrow#41128). These wrap
422+
// mimalloc allocations that are otherwise opaque to memray because mimalloc
423+
// is statically linked into libarrow and serves allocations out of mmap
424+
// arenas. Tracked under the existing aligned-alloc / realloc / free families.
425+
void*
426+
arrow_mi_malloc_aligned(size_t size, size_t alignment) noexcept
427+
{
428+
assert(MEMRAY_ORIG(arrow_mi_malloc_aligned));
429+
430+
void* ret;
431+
{
432+
tracking_api::RecursionGuard guard;
433+
ret = MEMRAY_ORIG(arrow_mi_malloc_aligned)(size, alignment);
434+
}
435+
if (ret) {
436+
tracking_api::Tracker::trackAllocation(ret, size, hooks::Allocator::ALIGNED_ALLOC);
437+
}
438+
return ret;
439+
}
440+
441+
void*
442+
arrow_mi_realloc_aligned(void* ptr, size_t new_size, size_t alignment) noexcept
443+
{
444+
assert(MEMRAY_ORIG(arrow_mi_realloc_aligned));
445+
446+
void* ret;
447+
{
448+
tracking_api::RecursionGuard guard;
449+
ret = MEMRAY_ORIG(arrow_mi_realloc_aligned)(ptr, new_size, alignment);
450+
}
451+
if (ret) {
452+
if (ptr != nullptr) {
453+
tracking_api::Tracker::trackDeallocation(ptr, 0, hooks::Allocator::FREE);
454+
}
455+
tracking_api::Tracker::trackAllocation(ret, new_size, hooks::Allocator::REALLOC);
456+
}
457+
return ret;
458+
}
459+
460+
void
461+
arrow_mi_free(void* ptr) noexcept
462+
{
463+
assert(MEMRAY_ORIG(arrow_mi_free));
464+
465+
if (ptr != nullptr) {
466+
tracking_api::Tracker::trackDeallocation(ptr, 0, hooks::Allocator::FREE);
467+
}
468+
{
469+
tracking_api::RecursionGuard guard;
470+
MEMRAY_ORIG(arrow_mi_free)(ptr);
471+
}
472+
}
473+
421474
#if defined(__linux__)
422475

423476
void*

src/memray/_memray/hooks.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@
2424
#include "compat.h"
2525
#include "logging.h"
2626

27+
// Apache Arrow mimalloc wrapper symbols (apache/arrow#41128). Declared weak
28+
// so memray links even when the host binary doesn't depend on libarrow.
29+
// At runtime memray's phdr scanner resolves the real address from libarrow.so.
30+
extern "C" {
31+
__attribute__((weak)) void* arrow_mi_malloc_aligned(size_t size, size_t alignment);
32+
__attribute__((weak)) void* arrow_mi_realloc_aligned(void* ptr, size_t new_size,
33+
size_t alignment);
34+
__attribute__((weak)) void arrow_mi_free(void* ptr);
35+
}
36+
2737
#if defined(__APPLE__)
2838
# define MEMRAY_PLATFORM_HOOKED_FUNCTIONS
2939
#elif defined(__GLIBC__)
@@ -51,6 +61,9 @@
5161
FOR_EACH_HOOKED_FUNCTION(dlopen) \
5262
FOR_EACH_HOOKED_FUNCTION(dlclose) \
5363
FOR_EACH_HOOKED_FUNCTION(PyGILState_Ensure) \
64+
FOR_EACH_HOOKED_FUNCTION(arrow_mi_malloc_aligned) \
65+
FOR_EACH_HOOKED_FUNCTION(arrow_mi_realloc_aligned) \
66+
FOR_EACH_HOOKED_FUNCTION(arrow_mi_free) \
5467
MEMRAY_PLATFORM_HOOKED_FUNCTIONS
5568

5669
namespace memray::hooks {
@@ -179,6 +192,15 @@ posix_memalign(void** memptr, size_t alignment, size_t size) noexcept;
179192
void*
180193
aligned_alloc(size_t alignment, size_t size) noexcept;
181194

195+
void*
196+
arrow_mi_malloc_aligned(size_t size, size_t alignment) noexcept;
197+
198+
void*
199+
arrow_mi_realloc_aligned(void* ptr, size_t new_size, size_t alignment) noexcept;
200+
201+
void
202+
arrow_mi_free(void* ptr) noexcept;
203+
182204
void*
183205
memalign(size_t alignment, size_t size) noexcept;
184206

src/memray/_memray/tracking_api.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,11 @@ Tracker::~Tracker()
898898
{
899899
RecursionGuard guard;
900900
tracking_api::Tracker::deactivate();
901+
#ifdef MEMRAY_HAS_GHOST_STACK
902+
if (d_fast_unwind) {
903+
ghost_stack_dump_stats();
904+
}
905+
#endif
901906

902907
PythonStackTracker::s_native_tracking_enabled = false;
903908
d_background_thread->stop();
@@ -1233,6 +1238,12 @@ Tracker::trackObjectImpl(PyObject* obj, int event, const std::optional<NativeTra
12331238
void
12341239
Tracker::invalidate_module_cache_impl()
12351240
{
1241+
// Re-resolve hook originals before patching: hooked symbols defined in
1242+
// libraries loaded at runtime (e.g. libarrow.so's arrow_mi_*) are not
1243+
// present when ensureAllHooksAreValid() runs at tracker init; without
1244+
// this call, the patcher would rewrite GOT entries to point at our
1245+
// intercepts, which would then call a null d_original and crash.
1246+
hooks::ensureAllHooksAreValid();
12361247
d_patcher.overwrite_symbols();
12371248
updateModuleCacheImpl();
12381249
}

0 commit comments

Comments
 (0)