@@ -262,40 +262,6 @@ impl MemtrackBpf {
262262 // Attach methods grouped by allocator
263263 // =========================================================================
264264
265- /// Attach standard library allocation probes (libc-style: malloc, free, calloc, etc.)
266- /// This works for libc and allocators that export standard symbol names.
267- /// For non-libc allocators, standard names are optional - just try them silently.
268- pub fn attach_libc_probes ( & mut self , lib_path : & Path ) -> Result < ( ) > {
269- self . try_attach_malloc ( lib_path, "malloc" ) ;
270- self . try_attach_calloc ( lib_path, "calloc" ) ;
271- self . try_attach_realloc ( lib_path, "realloc" ) ;
272- self . try_attach_free ( lib_path, "free" ) ;
273- self . try_attach_aligned_alloc ( lib_path, "aligned_alloc" ) ;
274- self . try_attach_memalign ( lib_path, "posix_memalign" ) ;
275- self . try_attach_memalign ( lib_path, "memalign" ) ;
276- Ok ( ( ) )
277- }
278-
279- /// Attach C++ operator new/delete probes.
280- /// These are mangled C++ symbols that wrap the underlying allocator.
281- /// C++ operators have identical signatures to malloc/free, so we reuse those handlers.
282- pub fn attach_libcpp_probes ( & mut self , lib_path : & Path ) -> Result < ( ) > {
283- self . try_attach_malloc ( lib_path, "_Znwm" ) ; // operator new(size_t)
284- self . try_attach_malloc ( lib_path, "_Znam" ) ; // operator new[](size_t)
285- self . try_attach_malloc ( lib_path, "_ZnwmSt11align_val_t" ) ; // operator new(size_t, std::align_val_t)
286- self . try_attach_malloc ( lib_path, "_ZnamSt11align_val_t" ) ; // operator new[](size_t, std::align_val_t)
287- self . try_attach_free ( lib_path, "_ZdlPv" ) ; // operator delete(void*)
288- self . try_attach_free ( lib_path, "_ZdaPv" ) ; // operator delete[](void*)
289- self . try_attach_free ( lib_path, "_ZdlPvm" ) ; // operator delete(void*, size_t) - C++14 sized delete
290- self . try_attach_free ( lib_path, "_ZdaPvm" ) ; // operator delete[](void*, size_t) - C++14 sized delete
291- self . try_attach_free ( lib_path, "_ZdlPvSt11align_val_t" ) ; // operator delete(void*, std::align_val_t)
292- self . try_attach_free ( lib_path, "_ZdaPvSt11align_val_t" ) ; // operator delete[](void*, std::align_val_t)
293- self . try_attach_free ( lib_path, "_ZdlPvmSt11align_val_t" ) ; // operator delete(void*, size_t, std::align_val_t)
294- self . try_attach_free ( lib_path, "_ZdaPvmSt11align_val_t" ) ; // operator delete[](void*, size_t, std::align_val_t)
295-
296- Ok ( ( ) )
297- }
298-
299265 /// Attach probes for a specific allocator kind.
300266 /// This attaches both standard probes (if the allocator exports them) and
301267 /// allocator-specific prefixed probes.
@@ -316,22 +282,77 @@ impl MemtrackBpf {
316282 self . attach_libcpp_probes ( lib_path)
317283 }
318284 AllocatorKind :: Jemalloc => {
319- // Try standard names (jemalloc may export these as drop-in replacements)
320- let _ = self . attach_libc_probes ( lib_path) ;
321285 // Try C++ operators (jemalloc exports these for C++ programs)
322286 let _ = self . attach_libcpp_probes ( lib_path) ;
323287 self . attach_jemalloc_probes ( lib_path)
324288 }
325289 AllocatorKind :: Mimalloc => {
326- // Try standard names (mimalloc may export these as drop-in replacements)
327- let _ = self . attach_libc_probes ( lib_path) ;
328290 // Try C++ operators (mimalloc exports these for C++ programs)
329291 let _ = self . attach_libcpp_probes ( lib_path) ;
330292 self . attach_mimalloc_probes ( lib_path)
331293 }
332294 }
333295 }
334296
297+ fn attach_standard_probes (
298+ & mut self ,
299+ lib_path : & Path ,
300+ prefixes : & [ & str ] ,
301+ suffixes : & [ & str ] ,
302+ ) -> Result < ( ) > {
303+ // Always include "" to capture the basic case
304+ let prefixes_with_base: Vec < & str > = std:: iter:: once ( "" )
305+ . chain ( prefixes. iter ( ) . copied ( ) )
306+ . unique ( )
307+ . collect ( ) ;
308+
309+ let suffixes_with_base: Vec < & str > = std:: iter:: once ( "" )
310+ . chain ( suffixes. iter ( ) . copied ( ) )
311+ . unique ( )
312+ . collect ( ) ;
313+
314+ for prefix in & prefixes_with_base {
315+ for suffix in & suffixes_with_base {
316+ self . try_attach_malloc ( lib_path, & format ! ( "{prefix}malloc{suffix}" ) ) ;
317+ self . try_attach_calloc ( lib_path, & format ! ( "{prefix}calloc{suffix}" ) ) ;
318+ self . try_attach_realloc ( lib_path, & format ! ( "{prefix}realloc{suffix}" ) ) ;
319+ self . try_attach_aligned_alloc ( lib_path, & format ! ( "{prefix}aligned_alloc{suffix}" ) ) ;
320+ self . try_attach_memalign ( lib_path, & format ! ( "{prefix}memalign{suffix}" ) ) ;
321+ self . try_attach_memalign ( lib_path, & format ! ( "{prefix}posix_memalign{suffix}" ) ) ;
322+ self . try_attach_free ( lib_path, & format ! ( "{prefix}free{suffix}" ) ) ;
323+ }
324+ }
325+
326+ Ok ( ( ) )
327+ }
328+
329+ /// Attach standard library allocation probes (libc-style: malloc, free, calloc, etc.)
330+ /// This works for libc and allocators that export standard symbol names.
331+ /// For non-libc allocators, standard names are optional - just try them silently.
332+ fn attach_libc_probes ( & mut self , lib_path : & Path ) -> Result < ( ) > {
333+ self . attach_standard_probes ( lib_path, & [ ] , & [ ] )
334+ }
335+
336+ /// Attach C++ operator new/delete probes.
337+ /// These are mangled C++ symbols that wrap the underlying allocator.
338+ /// C++ operators have identical signatures to malloc/free, so we reuse those handlers.
339+ fn attach_libcpp_probes ( & mut self , lib_path : & Path ) -> Result < ( ) > {
340+ self . try_attach_malloc ( lib_path, "_Znwm" ) ; // operator new(size_t)
341+ self . try_attach_malloc ( lib_path, "_Znam" ) ; // operator new[](size_t)
342+ self . try_attach_malloc ( lib_path, "_ZnwmSt11align_val_t" ) ; // operator new(size_t, std::align_val_t)
343+ self . try_attach_malloc ( lib_path, "_ZnamSt11align_val_t" ) ; // operator new[](size_t, std::align_val_t)
344+ self . try_attach_free ( lib_path, "_ZdlPv" ) ; // operator delete(void*)
345+ self . try_attach_free ( lib_path, "_ZdaPv" ) ; // operator delete[](void*)
346+ self . try_attach_free ( lib_path, "_ZdlPvm" ) ; // operator delete(void*, size_t) - C++14 sized delete
347+ self . try_attach_free ( lib_path, "_ZdaPvm" ) ; // operator delete[](void*, size_t) - C++14 sized delete
348+ self . try_attach_free ( lib_path, "_ZdlPvSt11align_val_t" ) ; // operator delete(void*, std::align_val_t)
349+ self . try_attach_free ( lib_path, "_ZdaPvSt11align_val_t" ) ; // operator delete[](void*, std::align_val_t)
350+ self . try_attach_free ( lib_path, "_ZdlPvmSt11align_val_t" ) ; // operator delete(void*, size_t, std::align_val_t)
351+ self . try_attach_free ( lib_path, "_ZdaPvmSt11align_val_t" ) ; // operator delete[](void*, size_t, std::align_val_t)
352+
353+ Ok ( ( ) )
354+ }
355+
335356 /// Attach jemalloc-specific probes (prefixed and extended API).
336357 fn attach_jemalloc_probes ( & mut self , lib_path : & Path ) -> Result < ( ) > {
337358 // The following functions are used in Rust when setting a global allocator:
@@ -340,36 +361,23 @@ impl MemtrackBpf {
340361 // - rust_dealloc: _rjem_sdallocx
341362 // - rust_realloc: _rjem_realloc / _rjem_rallocx
342363
343- // je_*_default API (C++ with static linking)
344- self . try_attach_malloc ( lib_path, "je_malloc_default" ) ;
345- self . try_attach_malloc ( lib_path, "je_mallocx_default" ) ;
346- self . try_attach_free ( lib_path, "je_free_default" ) ;
347- self . try_attach_free ( lib_path, "je_sdallocx_default" ) ;
348- self . try_attach_realloc ( lib_path, "je_realloc_default" ) ;
349- self . try_attach_realloc ( lib_path, "je_rallocx_default" ) ;
350- self . try_attach_calloc ( lib_path, "je_calloc_default" ) ;
351-
352364 // je_* API (internal jemalloc functions, static linking)
353- self . try_attach_malloc ( lib_path, "je_malloc" ) ;
354- self . try_attach_malloc ( lib_path, "je_mallocx" ) ;
355- self . try_attach_calloc ( lib_path, "je_calloc" ) ;
356- self . try_attach_realloc ( lib_path, "je_realloc" ) ;
357- self . try_attach_realloc ( lib_path, "je_rallocx" ) ;
358- self . try_attach_aligned_alloc ( lib_path, "je_aligned_alloc" ) ;
359- self . try_attach_memalign ( lib_path, "je_memalign" ) ;
360- self . try_attach_free ( lib_path, "je_free" ) ;
361- self . try_attach_free ( lib_path, "je_sdallocx" ) ;
362-
363365 // _rjem_* API (Rust jemalloc crate, dynamic linking)
364- self . try_attach_malloc ( lib_path, "_rjem_malloc" ) ;
365- self . try_attach_malloc ( lib_path, "_rjem_mallocx" ) ; // Also used for `calloc`
366- self . try_attach_calloc ( lib_path, "_rjem_calloc" ) ;
367- self . try_attach_realloc ( lib_path, "_rjem_realloc" ) ;
368- self . try_attach_realloc ( lib_path, "_rjem_rallocx" ) ;
369- self . try_attach_aligned_alloc ( lib_path, "_rjem_aligned_alloc" ) ;
370- self . try_attach_memalign ( lib_path, "_rjem_memalign" ) ;
371- self . try_attach_free ( lib_path, "_rjem_free" ) ;
372- self . try_attach_free ( lib_path, "_rjem_sdallocx" ) ;
366+ let prefixes = [ "je_" , "_rjem_" ] ;
367+ let suffixes = [ "" , "_default" ] ;
368+
369+ self . attach_standard_probes ( lib_path, & prefixes, & suffixes) ?;
370+
371+ // Non-standard API that has an additional flag parameter
372+ // See: https://jemalloc.net/jemalloc.3.html
373+ for prefix in prefixes {
374+ for suffix in suffixes {
375+ self . try_attach_malloc ( lib_path, & format ! ( "{prefix}mallocx{suffix}" ) ) ;
376+ self . try_attach_realloc ( lib_path, & format ! ( "{prefix}rallocx{suffix}" ) ) ;
377+ self . try_attach_free ( lib_path, & format ! ( "{prefix}dallocx{suffix}" ) ) ;
378+ self . try_attach_free ( lib_path, & format ! ( "{prefix}sdallocx{suffix}" ) ) ;
379+ }
380+ }
373381
374382 Ok ( ( ) )
375383 }
@@ -382,16 +390,10 @@ impl MemtrackBpf {
382390 // - mi_realloc_aligned
383391 // - mi_zalloc_aligned
384392
385- // Core API
386- self . try_attach_malloc ( lib_path, "mi_malloc" ) ;
387- self . try_attach_malloc ( lib_path, "mi_malloc_aligned" ) ;
388- self . try_attach_calloc ( lib_path, "mi_calloc" ) ;
389- self . try_attach_realloc ( lib_path, "mi_realloc" ) ;
390- self . try_attach_aligned_alloc ( lib_path, "mi_aligned_alloc" ) ;
391- self . try_attach_memalign ( lib_path, "mi_memalign" ) ;
392- self . try_attach_free ( lib_path, "mi_free" ) ;
393+ self . attach_standard_probes ( lib_path, & [ "mi_" ] , & [ ] ) ?;
393394
394395 // Zero-initialized and aligned variants
396+ self . try_attach_calloc ( lib_path, "mi_malloc_aligned" ) ;
395397 self . try_attach_calloc ( lib_path, "mi_zalloc" ) ;
396398 self . try_attach_calloc ( lib_path, "mi_zalloc_aligned" ) ;
397399 self . try_attach_realloc ( lib_path, "mi_realloc_aligned" ) ;
0 commit comments