From 9af43658fda35f2b6303b14385d84bb6c709657e Mon Sep 17 00:00:00 2001 From: not-matthias Date: Thu, 12 Feb 2026 15:43:44 +0100 Subject: [PATCH 1/6] feat(memtrack): support more standard allocation APIs --- crates/memtrack/src/ebpf/memtrack.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/memtrack/src/ebpf/memtrack.rs b/crates/memtrack/src/ebpf/memtrack.rs index 1e9cd415..611dda55 100644 --- a/crates/memtrack/src/ebpf/memtrack.rs +++ b/crates/memtrack/src/ebpf/memtrack.rs @@ -314,12 +314,17 @@ impl MemtrackBpf { for prefix in &prefixes_with_base { for suffix in &suffixes_with_base { self.try_attach_malloc(lib_path, &format!("{prefix}malloc{suffix}")); + self.try_attach_malloc(lib_path, &format!("{prefix}valloc{suffix}")); + self.try_attach_malloc(lib_path, &format!("{prefix}pvalloc{suffix}")); self.try_attach_calloc(lib_path, &format!("{prefix}calloc{suffix}")); self.try_attach_realloc(lib_path, &format!("{prefix}realloc{suffix}")); self.try_attach_aligned_alloc(lib_path, &format!("{prefix}aligned_alloc{suffix}")); self.try_attach_memalign(lib_path, &format!("{prefix}memalign{suffix}")); self.try_attach_memalign(lib_path, &format!("{prefix}posix_memalign{suffix}")); self.try_attach_free(lib_path, &format!("{prefix}free{suffix}")); + self.try_attach_free(lib_path, &format!("{prefix}free_sized{suffix}")); + self.try_attach_free(lib_path, &format!("{prefix}free_aligned_sized{suffix}")); + self.try_attach_free(lib_path, &format!("{prefix}cfree{suffix}")); } } From d3093e32e4f4d67942bd8c39d0be1fe6825dd71f Mon Sep 17 00:00:00 2001 From: not-matthias Date: Thu, 12 Feb 2026 15:44:49 +0100 Subject: [PATCH 2/6] feat(memtrack): support tcmalloc --- crates/memtrack/src/allocators/dynamic.rs | 19 +++++++++++++++ crates/memtrack/src/allocators/mod.rs | 24 ++++++++++--------- .../memtrack/src/allocators/static_linked.rs | 13 ++++++++++ crates/memtrack/src/ebpf/memtrack.rs | 21 ++++++++++++++++ 4 files changed, 66 insertions(+), 11 deletions(-) diff --git a/crates/memtrack/src/allocators/dynamic.rs b/crates/memtrack/src/allocators/dynamic.rs index 7bc270ac..794f012a 100644 --- a/crates/memtrack/src/allocators/dynamic.rs +++ b/crates/memtrack/src/allocators/dynamic.rs @@ -46,6 +46,25 @@ fn get_allocator_paths(lib: &AllocatorKind) -> &'static [&'static str] { // NixOS "/nix/store/*mimalloc*/lib/libmimalloc.so*", ], + AllocatorKind::Tcmalloc => &[ + // gperftools tcmalloc variants + // Debian, Ubuntu: Standard Linux multiarch paths + "/lib/*-linux-gnu/libtcmalloc.so*", + "/lib/*-linux-gnu/libtcmalloc_minimal.so*", + "/lib/*-linux-gnu/libtcmalloc_debug.so*", + "/lib/*-linux-gnu/libtcmalloc_and_profiler.so*", + "/usr/lib/*-linux-gnu/libtcmalloc.so*", + "/usr/lib/*-linux-gnu/libtcmalloc_minimal.so*", + "/usr/lib/*-linux-gnu/libtcmalloc_debug.so*", + "/usr/lib/*-linux-gnu/libtcmalloc_and_profiler.so*", + // RHEL, Fedora, CentOS, Arch + "/lib*/libtcmalloc*.so*", + "/usr/lib*/libtcmalloc*.so*", + "/usr/local/lib*/libtcmalloc*.so*", + // NixOS + "/nix/store/*tcmalloc*/lib/libtcmalloc*.so*", + "/nix/store/*gperftools*/lib/libtcmalloc*.so*", + ], } } diff --git a/crates/memtrack/src/allocators/mod.rs b/crates/memtrack/src/allocators/mod.rs index 141c2020..5b35efc6 100644 --- a/crates/memtrack/src/allocators/mod.rs +++ b/crates/memtrack/src/allocators/mod.rs @@ -19,8 +19,18 @@ pub enum AllocatorKind { Jemalloc, /// mimalloc - Microsoft's allocator Mimalloc, + /// TCMalloc - Google's thread-caching malloc + /// + /// Two variants exist: + /// - **gperftools** (github.com/gperftools/gperftools): Original ~2005 release. + /// Exports both standard symbols (malloc/free) AND tc_* prefixed symbols. + /// - **google/tcmalloc** (github.com/google/tcmalloc): Modern ~2020 rewrite. + /// Exports ONLY standard symbols (malloc/free/etc.) - no tc_* prefix. + /// + /// We'll always try to attach to both the standard and `tc_*` API. If the newer rewrite is + /// used, we'll only attach to the standard API. + Tcmalloc, // Future allocators: - // Tcmalloc, // Hoard, // Rpmalloc, } @@ -33,6 +43,7 @@ impl AllocatorKind { &[ AllocatorKind::Jemalloc, AllocatorKind::Mimalloc, + AllocatorKind::Tcmalloc, AllocatorKind::LibCpp, AllocatorKind::Libc, ] @@ -45,6 +56,7 @@ impl AllocatorKind { AllocatorKind::LibCpp => "libc++", AllocatorKind::Jemalloc => "jemalloc", AllocatorKind::Mimalloc => "mimalloc", + AllocatorKind::Tcmalloc => "tcmalloc", } } @@ -52,16 +64,6 @@ impl AllocatorKind { pub fn is_required(&self) -> bool { matches!(self, AllocatorKind::Libc) } - - /// Returns the symbol names used to detect this allocator in binaries. - pub fn symbols(&self) -> &'static [&'static str] { - match self { - AllocatorKind::Libc => &["malloc", "free"], - AllocatorKind::LibCpp => &["_Znwm", "_Znam", "_ZdlPv", "_ZdaPv"], - AllocatorKind::Jemalloc => &["_rjem_malloc", "je_malloc", "je_malloc_default"], - AllocatorKind::Mimalloc => &["mi_malloc_aligned", "mi_malloc", "mi_free"], - } - } } /// Discovered allocator library with its kind and path. diff --git a/crates/memtrack/src/allocators/static_linked.rs b/crates/memtrack/src/allocators/static_linked.rs index 092b9e7e..9695070d 100644 --- a/crates/memtrack/src/allocators/static_linked.rs +++ b/crates/memtrack/src/allocators/static_linked.rs @@ -4,6 +4,19 @@ use std::path::{Path, PathBuf}; use crate::allocators::{AllocatorKind, AllocatorLib}; +impl AllocatorKind { + /// Returns the symbol names used to detect this allocator in binaries. + pub fn symbols(&self) -> &'static [&'static str] { + match self { + AllocatorKind::Libc => &["malloc", "free"], + AllocatorKind::LibCpp => &["_Znwm", "_Znam", "_ZdlPv", "_ZdaPv"], + AllocatorKind::Jemalloc => &["_rjem_malloc", "je_malloc", "je_malloc_default"], + AllocatorKind::Mimalloc => &["mi_malloc_aligned", "mi_malloc", "mi_free"], + AllocatorKind::Tcmalloc => &["tc_malloc", "tc_free", "tc_version"], + } + } +} + /// Walk upward and downward from current directory to find build directories. /// Returns all found build directories in order of preference. fn find_build_dirs() -> Vec { diff --git a/crates/memtrack/src/ebpf/memtrack.rs b/crates/memtrack/src/ebpf/memtrack.rs index 611dda55..72462a31 100644 --- a/crates/memtrack/src/ebpf/memtrack.rs +++ b/crates/memtrack/src/ebpf/memtrack.rs @@ -291,6 +291,11 @@ impl MemtrackBpf { let _ = self.attach_libcpp_probes(lib_path); self.attach_mimalloc_probes(lib_path) } + AllocatorKind::Tcmalloc => { + // Try C++ operators (tcmalloc exports these for C++ programs) + let _ = self.attach_libcpp_probes(lib_path); + self.attach_tcmalloc_probes(lib_path) + } } } @@ -405,6 +410,22 @@ impl MemtrackBpf { Ok(()) } + + /// Attach TCMalloc probes ( tc_* API). + /// + /// See: + /// - https://github.com/google/tcmalloc/blob/master/docs/reference.md + /// - https://github.com/gperftools/gperftools/blob/a47243150ec41097602730ff8779fafcc172d1fb/src/tcmalloc.cc#L178-L190 + + fn attach_tcmalloc_probes(&mut self, lib_path: &Path) -> Result<()> { + self.attach_standard_probes(lib_path, &["tc_"], &[])?; + + self.try_attach_free(lib_path, "free_sized"); + self.try_attach_free(lib_path, "free_aligned_sized"); + self.try_attach_free(lib_path, "sdallocx"); + + Ok(()) + } attach_tracepoint!(sched_fork); pub fn attach_tracepoints(&mut self) -> Result<()> { From 0feebe9a28372226e0621ac3517ce79ca1daae15 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Fri, 13 Feb 2026 11:58:30 +0100 Subject: [PATCH 3/6] feat(memtrack): always try to attach to libc-compatible API --- crates/memtrack/src/ebpf/memtrack.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/memtrack/src/ebpf/memtrack.rs b/crates/memtrack/src/ebpf/memtrack.rs index 72462a31..965a33a7 100644 --- a/crates/memtrack/src/ebpf/memtrack.rs +++ b/crates/memtrack/src/ebpf/memtrack.rs @@ -282,17 +282,20 @@ impl MemtrackBpf { self.attach_libcpp_probes(lib_path) } AllocatorKind::Jemalloc => { - // Try C++ operators (jemalloc exports these for C++ programs) + // Jemalloc exposes libc/libcpp compatible allocator functions: + let _ = self.attach_libc_probes(lib_path); let _ = self.attach_libcpp_probes(lib_path); self.attach_jemalloc_probes(lib_path) } AllocatorKind::Mimalloc => { - // Try C++ operators (mimalloc exports these for C++ programs) + // Mimalloc exposes libc/libcpp compatible allocator functions: + let _ = self.attach_libc_probes(lib_path); let _ = self.attach_libcpp_probes(lib_path); self.attach_mimalloc_probes(lib_path) } AllocatorKind::Tcmalloc => { - // Try C++ operators (tcmalloc exports these for C++ programs) + // Tcmalloc exposes libc/libcpp compatible allocator functions: + let _ = self.attach_libc_probes(lib_path); let _ = self.attach_libcpp_probes(lib_path); self.attach_tcmalloc_probes(lib_path) } @@ -416,7 +419,6 @@ impl MemtrackBpf { /// See: /// - https://github.com/google/tcmalloc/blob/master/docs/reference.md /// - https://github.com/gperftools/gperftools/blob/a47243150ec41097602730ff8779fafcc172d1fb/src/tcmalloc.cc#L178-L190 - fn attach_tcmalloc_probes(&mut self, lib_path: &Path) -> Result<()> { self.attach_standard_probes(lib_path, &["tc_"], &[])?; From 18ea9c3fbc42cd4e5027684709a27c8221404c17 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Thu, 12 Feb 2026 15:52:08 +0100 Subject: [PATCH 4/6] chore(memtrack): add tcmalloc test --- .../testdata/alloc_cpp/CMakeLists.txt | 111 ++++++++++++++++++ crates/memtrack/testdata/alloc_cpp/cmake.toml | 86 ++++++++++++++ .../memtrack/testdata/alloc_cpp/src/main.cpp | 3 + crates/memtrack/tests/cpp_tests.rs | 2 + ...cpp_tests__alloc_cpp_tcmalloc_dynamic.snap | 13 ++ .../cpp_tests__alloc_cpp_tcmalloc_static.snap | 13 ++ 6 files changed, 228 insertions(+) create mode 100644 crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_tcmalloc_dynamic.snap create mode 100644 crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_tcmalloc_static.snap diff --git a/crates/memtrack/testdata/alloc_cpp/CMakeLists.txt b/crates/memtrack/testdata/alloc_cpp/CMakeLists.txt index 1811a71f..e7033df4 100644 --- a/crates/memtrack/testdata/alloc_cpp/CMakeLists.txt +++ b/crates/memtrack/testdata/alloc_cpp/CMakeLists.txt @@ -50,6 +50,53 @@ if(NOT JEMALLOC_STATIC_LIB) message(STATUS "Static jemalloc not found, will fall back to dynamic linking") endif() +# TCMalloc support via system gperftools package +# gperftools provides libtcmalloc on most Linux distributions +# (Ubuntu: libtcmalloc-minimal4, Fedora: gperftools-libs, NixOS: gperftools) +find_package(Threads REQUIRED) + +# Search in common paths including NixOS store +file(GLOB TCMALLOC_NIX_PATHS "/nix/store/*gperftools*/lib") + +# Find static library +set(_old_suffixes ${CMAKE_FIND_LIBRARY_SUFFIXES}) +set(CMAKE_FIND_LIBRARY_SUFFIXES .a) +find_library(TCMALLOC_STATIC_LIB + NAMES libtcmalloc_minimal.a libtcmalloc.a tcmalloc_minimal tcmalloc + HINTS ${TCMALLOC_NIX_PATHS} + DOC "Static TCMalloc library (from gperftools)" +) +set(CMAKE_FIND_LIBRARY_SUFFIXES ${_old_suffixes}) + +if(NOT TCMALLOC_STATIC_LIB) + message(STATUS "Static TCMalloc not found, will fall back to dynamic linking") +endif() + +# Find dynamic library +find_package(PkgConfig) +if(PKG_CONFIG_FOUND) + pkg_check_modules(TCMALLOC QUIET libtcmalloc_minimal libtcmalloc) +endif() + +if(NOT TCMALLOC_FOUND) + find_library(TCMALLOC_LIBRARY + NAMES tcmalloc_minimal tcmalloc + HINTS ${TCMALLOC_NIX_PATHS} + DOC "TCMalloc library (from gperftools)" + ) + if(TCMALLOC_LIBRARY) + set(TCMALLOC_FOUND TRUE) + set(TCMALLOC_LIBRARIES ${TCMALLOC_LIBRARY}) + endif() +endif() + +if(TCMALLOC_FOUND OR TCMALLOC_STATIC_LIB) + message(STATUS "Found TCMalloc: static=${TCMALLOC_STATIC_LIB}, dynamic=${TCMALLOC_LIBRARIES}") + set(TCMALLOC_AVAILABLE ON) +else() + message(STATUS "TCMalloc not found, tcmalloc test targets will not link correctly") +endif() + # Target: alloc_cpp_system set(alloc_cpp_system_SOURCES cmake.toml @@ -169,3 +216,67 @@ get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} D if(NOT CMKR_VS_STARTUP_PROJECT) set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT alloc_cpp_mimalloc_dynamic) endif() + +# Target: alloc_cpp_tcmalloc_static +set(alloc_cpp_tcmalloc_static_SOURCES + cmake.toml + "src/main.cpp" +) + +add_executable(alloc_cpp_tcmalloc_static) + +target_sources(alloc_cpp_tcmalloc_static PRIVATE ${alloc_cpp_tcmalloc_static_SOURCES}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${alloc_cpp_tcmalloc_static_SOURCES}) + +target_compile_definitions(alloc_cpp_tcmalloc_static PRIVATE + USE_TCMALLOC=1 +) + +get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT) +if(NOT CMKR_VS_STARTUP_PROJECT) + set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT alloc_cpp_tcmalloc_static) +endif() + +set(CMKR_TARGET alloc_cpp_tcmalloc_static) +if(TCMALLOC_STATIC_LIB) + # Use --whole-archive to ensure malloc override symbols are included + target_link_options(alloc_cpp_tcmalloc_static PRIVATE "LINKER:--whole-archive") + target_link_libraries(alloc_cpp_tcmalloc_static PRIVATE ${TCMALLOC_STATIC_LIB}) + target_link_options(alloc_cpp_tcmalloc_static PRIVATE "LINKER:--no-whole-archive") + target_link_libraries(alloc_cpp_tcmalloc_static PRIVATE Threads::Threads) +elseif(TCMALLOC_LIBRARIES) + # Fallback to dynamic linking + target_link_libraries(alloc_cpp_tcmalloc_static PRIVATE ${TCMALLOC_LIBRARIES}) +else() + message(WARNING "TCMalloc not found, alloc_cpp_tcmalloc_static will not link correctly") +endif() + +# Target: alloc_cpp_tcmalloc_dynamic +set(alloc_cpp_tcmalloc_dynamic_SOURCES + cmake.toml + "src/main.cpp" +) + +add_executable(alloc_cpp_tcmalloc_dynamic) + +target_sources(alloc_cpp_tcmalloc_dynamic PRIVATE ${alloc_cpp_tcmalloc_dynamic_SOURCES}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${alloc_cpp_tcmalloc_dynamic_SOURCES}) + +target_compile_definitions(alloc_cpp_tcmalloc_dynamic PRIVATE + USE_TCMALLOC=1 +) + +get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT) +if(NOT CMKR_VS_STARTUP_PROJECT) + set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT alloc_cpp_tcmalloc_dynamic) +endif() + +set(CMKR_TARGET alloc_cpp_tcmalloc_dynamic) +if(TCMALLOC_AVAILABLE) + target_link_libraries(alloc_cpp_tcmalloc_dynamic PRIVATE ${TCMALLOC_LIBRARIES}) + if(TCMALLOC_INCLUDE_DIRS) + target_include_directories(alloc_cpp_tcmalloc_dynamic PRIVATE ${TCMALLOC_INCLUDE_DIRS}) + endif() +else() + message(WARNING "TCMalloc not found, alloc_cpp_tcmalloc_dynamic will not link correctly") +endif() diff --git a/crates/memtrack/testdata/alloc_cpp/cmake.toml b/crates/memtrack/testdata/alloc_cpp/cmake.toml index 4b7631aa..3b56c099 100644 --- a/crates/memtrack/testdata/alloc_cpp/cmake.toml +++ b/crates/memtrack/testdata/alloc_cpp/cmake.toml @@ -21,6 +21,53 @@ endif() if(NOT JEMALLOC_STATIC_LIB) message(STATUS "Static jemalloc not found, will fall back to dynamic linking") endif() + +# TCMalloc support via system gperftools package +# gperftools provides libtcmalloc on most Linux distributions +# (Ubuntu: libtcmalloc-minimal4, Fedora: gperftools-libs, NixOS: gperftools) +find_package(Threads REQUIRED) + +# Search in common paths including NixOS store +file(GLOB TCMALLOC_NIX_PATHS "/nix/store/*gperftools*/lib") + +# Find static library +set(_old_suffixes ${CMAKE_FIND_LIBRARY_SUFFIXES}) +set(CMAKE_FIND_LIBRARY_SUFFIXES .a) +find_library(TCMALLOC_STATIC_LIB + NAMES libtcmalloc_minimal.a libtcmalloc.a tcmalloc_minimal tcmalloc + HINTS ${TCMALLOC_NIX_PATHS} + DOC "Static TCMalloc library (from gperftools)" +) +set(CMAKE_FIND_LIBRARY_SUFFIXES ${_old_suffixes}) + +if(NOT TCMALLOC_STATIC_LIB) + message(STATUS "Static TCMalloc not found, will fall back to dynamic linking") +endif() + +# Find dynamic library +find_package(PkgConfig) +if(PKG_CONFIG_FOUND) + pkg_check_modules(TCMALLOC QUIET libtcmalloc_minimal libtcmalloc) +endif() + +if(NOT TCMALLOC_FOUND) + find_library(TCMALLOC_LIBRARY + NAMES tcmalloc_minimal tcmalloc + HINTS ${TCMALLOC_NIX_PATHS} + DOC "TCMalloc library (from gperftools)" + ) + if(TCMALLOC_LIBRARY) + set(TCMALLOC_FOUND TRUE) + set(TCMALLOC_LIBRARIES ${TCMALLOC_LIBRARY}) + endif() +endif() + +if(TCMALLOC_FOUND OR TCMALLOC_STATIC_LIB) + message(STATUS "Found TCMalloc: static=${TCMALLOC_STATIC_LIB}, dynamic=${TCMALLOC_LIBRARIES}") + set(TCMALLOC_AVAILABLE ON) +else() + message(STATUS "TCMalloc not found, tcmalloc test targets will not link correctly") +endif() """ # System allocator (libc default malloc) @@ -69,3 +116,42 @@ type = "executable" sources = ["src/main.cpp"] compile-definitions = ["USE_MIMALLOC=1"] link-libraries = ["mimalloc"] + +# TCMalloc (gperftools) - static linking +# Uses system-installed libtcmalloc.a from gperftools package +[target.alloc_cpp_tcmalloc_static] +type = "executable" +sources = ["src/main.cpp"] +compile-definitions = ["USE_TCMALLOC=1"] +cmake-after = """ +if(TCMALLOC_STATIC_LIB) + # Use --whole-archive to ensure malloc override symbols are included + target_link_options(alloc_cpp_tcmalloc_static PRIVATE "LINKER:--whole-archive") + target_link_libraries(alloc_cpp_tcmalloc_static PRIVATE ${TCMALLOC_STATIC_LIB}) + target_link_options(alloc_cpp_tcmalloc_static PRIVATE "LINKER:--no-whole-archive") + target_link_libraries(alloc_cpp_tcmalloc_static PRIVATE Threads::Threads) +elseif(TCMALLOC_LIBRARIES) + # Fallback to dynamic linking + target_link_libraries(alloc_cpp_tcmalloc_static PRIVATE ${TCMALLOC_LIBRARIES}) +else() + message(WARNING "TCMalloc not found, alloc_cpp_tcmalloc_static will not link correctly") +endif() +""" + +# TCMalloc (gperftools) - dynamic linking +# Uses system-installed libtcmalloc from gperftools package +# (Ubuntu: libtcmalloc-minimal4, Fedora: gperftools-libs) +[target.alloc_cpp_tcmalloc_dynamic] +type = "executable" +sources = ["src/main.cpp"] +compile-definitions = ["USE_TCMALLOC=1"] +cmake-after = """ +if(TCMALLOC_AVAILABLE) + target_link_libraries(alloc_cpp_tcmalloc_dynamic PRIVATE ${TCMALLOC_LIBRARIES}) + if(TCMALLOC_INCLUDE_DIRS) + target_include_directories(alloc_cpp_tcmalloc_dynamic PRIVATE ${TCMALLOC_INCLUDE_DIRS}) + endif() +else() + message(WARNING "TCMalloc not found, alloc_cpp_tcmalloc_dynamic will not link correctly") +endif() +""" diff --git a/crates/memtrack/testdata/alloc_cpp/src/main.cpp b/crates/memtrack/testdata/alloc_cpp/src/main.cpp index f1edfc77..14b5f4ad 100644 --- a/crates/memtrack/testdata/alloc_cpp/src/main.cpp +++ b/crates/memtrack/testdata/alloc_cpp/src/main.cpp @@ -12,6 +12,9 @@ #include #endif +// USE_TCMALLOC: Uses gperftools libtcmalloc via dynamic linking +// No header include needed - tcmalloc overrides malloc/free symbols + // Prevent compiler from optimizing away allocations // Similar to Rust's core::hint::black_box template diff --git a/crates/memtrack/tests/cpp_tests.rs b/crates/memtrack/tests/cpp_tests.rs index 584cd9ad..2a219441 100644 --- a/crates/memtrack/tests/cpp_tests.rs +++ b/crates/memtrack/tests/cpp_tests.rs @@ -50,6 +50,8 @@ fn compile_cpp_project(project_dir: &Path, target: &str) -> anyhow::Result Result<(), Box> { let project_path = Path::new("testdata/alloc_cpp"); diff --git a/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_tcmalloc_dynamic.snap b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_tcmalloc_dynamic.snap new file mode 100644 index 00000000..ced328f7 --- /dev/null +++ b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_tcmalloc_dynamic.snap @@ -0,0 +1,13 @@ +--- +source: crates/memtrack/tests/cpp_tests.rs +expression: formatted_events +--- +[ + "Malloc { size: 44444 }", + "Free", + "Malloc { size: 8 }", + "Free", + "Malloc { size: 88888 }", + "AlignedAlloc { size: 32768 }", + "Free", +] diff --git a/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_tcmalloc_static.snap b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_tcmalloc_static.snap new file mode 100644 index 00000000..ced328f7 --- /dev/null +++ b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_tcmalloc_static.snap @@ -0,0 +1,13 @@ +--- +source: crates/memtrack/tests/cpp_tests.rs +expression: formatted_events +--- +[ + "Malloc { size: 44444 }", + "Free", + "Malloc { size: 8 }", + "Free", + "Malloc { size: 88888 }", + "AlignedAlloc { size: 32768 }", + "Free", +] From 2dbb7cd5b6b5a78a762dc6eb977ad74a67c2f247 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Fri, 13 Feb 2026 11:33:35 +0100 Subject: [PATCH 5/6] refactor(memtrack): use common lib paths for all allocators --- crates/memtrack/src/allocators/dynamic.rs | 103 ++++++++-------------- 1 file changed, 38 insertions(+), 65 deletions(-) diff --git a/crates/memtrack/src/allocators/dynamic.rs b/crates/memtrack/src/allocators/dynamic.rs index 794f012a..fc15882f 100644 --- a/crates/memtrack/src/allocators/dynamic.rs +++ b/crates/memtrack/src/allocators/dynamic.rs @@ -1,70 +1,43 @@ use crate::{AllocatorKind, AllocatorLib}; use std::path::PathBuf; -/// Returns the glob patterns used to find this allocator's shared libraries. -fn get_allocator_paths(lib: &AllocatorKind) -> &'static [&'static str] { - match lib { - AllocatorKind::Libc => &[ - // Debian, Ubuntu: Standard Linux multiarch paths - "/lib/*-linux-gnu/libc.so.6", - "/usr/lib/*-linux-gnu/libc.so.6", +impl AllocatorKind { + /// Build glob patterns for finding this allocator's shared libraries. + fn search_patterns(&self) -> Vec { + const LIB_DIRS: &[&str] = &[ + // Debian, Ubuntu: multiarch paths + "/lib/*-linux-gnu", + "/usr/lib/*-linux-gnu", // RHEL, Fedora, CentOS, Arch - "/lib*/libc.so.6", - "/usr/lib*/libc.so.6", - // NixOS: find all glibc versions in the Nix store - "/nix/store/*glibc*/lib/libc.so.6", - ], - AllocatorKind::LibCpp => &[ - // Standard Linux multiarch paths - "/lib/*-linux-gnu/libstdc++.so*", - "/usr/lib/*-linux-gnu/libstdc++.so*", - // RHEL, Fedora, CentOS, Arch - "/lib*/libstdc++.so*", - "/usr/lib*/libstdc++.so*", - // NixOS: find all gcc lib versions in the Nix store - "/nix/store/*gcc*/lib/libstdc++.so*", - ], - AllocatorKind::Jemalloc => &[ - // Debian, Ubuntu: Standard Linux multiarch paths - "/lib/*-linux-gnu/libjemalloc.so*", - "/usr/lib/*-linux-gnu/libjemalloc.so*", - // RHEL, Fedora, CentOS, Arch - "/lib*/libjemalloc.so*", - "/usr/lib*/libjemalloc.so*", - "/usr/local/lib*/libjemalloc.so*", - // NixOS - "/nix/store/*jemalloc*/lib/libjemalloc.so*", - ], - AllocatorKind::Mimalloc => &[ - // Debian, Ubuntu: Standard Linux multiarch paths - "/lib/*-linux-gnu/libmimalloc.so*", - "/usr/lib/*-linux-gnu/libmimalloc.so*", - // RHEL, Fedora, CentOS, Arch - "/lib*/libmimalloc.so*", - "/usr/lib*/libmimalloc.so*", - "/usr/local/lib*/libmimalloc.so*", - // NixOS - "/nix/store/*mimalloc*/lib/libmimalloc.so*", - ], - AllocatorKind::Tcmalloc => &[ - // gperftools tcmalloc variants - // Debian, Ubuntu: Standard Linux multiarch paths - "/lib/*-linux-gnu/libtcmalloc.so*", - "/lib/*-linux-gnu/libtcmalloc_minimal.so*", - "/lib/*-linux-gnu/libtcmalloc_debug.so*", - "/lib/*-linux-gnu/libtcmalloc_and_profiler.so*", - "/usr/lib/*-linux-gnu/libtcmalloc.so*", - "/usr/lib/*-linux-gnu/libtcmalloc_minimal.so*", - "/usr/lib/*-linux-gnu/libtcmalloc_debug.so*", - "/usr/lib/*-linux-gnu/libtcmalloc_and_profiler.so*", - // RHEL, Fedora, CentOS, Arch - "/lib*/libtcmalloc*.so*", - "/usr/lib*/libtcmalloc*.so*", - "/usr/local/lib*/libtcmalloc*.so*", - // NixOS - "/nix/store/*tcmalloc*/lib/libtcmalloc*.so*", - "/nix/store/*gperftools*/lib/libtcmalloc*.so*", - ], + "/lib*", + "/usr/lib*", + // Local installs + "/usr/local/lib*", + ]; + + let (filenames, nix_hints): (&[&str], &[&str]) = match self { + AllocatorKind::Libc => (&["libc.so.6"], &["glibc"]), + AllocatorKind::LibCpp => (&["libstdc++.so*"], &["gcc"]), + AllocatorKind::Jemalloc => (&["libjemalloc.so*"], &["jemalloc"]), + AllocatorKind::Mimalloc => (&["libmimalloc.so*"], &["mimalloc"]), + AllocatorKind::Tcmalloc => (&["libtcmalloc*.so*"], &["tcmalloc", "gperftools"]), + }; + + let mut patterns = Vec::new(); + + for dir in LIB_DIRS { + for filename in filenames { + patterns.push(format!("{dir}/{filename}")); + } + } + + for hint in nix_hints { + for filename in filenames { + patterns.push(format!("/nix/store/*{hint}*/lib/{filename}")); + } + } + + patterns } } @@ -78,8 +51,8 @@ pub fn find_all() -> anyhow::Result> { for kind in AllocatorKind::all() { let mut found_any = false; - for pattern in get_allocator_paths(kind) { - let paths = glob::glob(pattern) + for pattern in kind.search_patterns() { + let paths = glob::glob(&pattern) .ok() .into_iter() .flatten() From ad6011b8d1fadad5bc5a025fd54571a871232a74 Mon Sep 17 00:00:00 2001 From: not-matthias Date: Fri, 13 Feb 2026 12:02:45 +0100 Subject: [PATCH 6/6] chore(memtrack): remove outdated allocator check --- crates/memtrack/src/allocators/dynamic.rs | 8 -------- crates/memtrack/src/allocators/mod.rs | 5 ----- 2 files changed, 13 deletions(-) diff --git a/crates/memtrack/src/allocators/dynamic.rs b/crates/memtrack/src/allocators/dynamic.rs index fc15882f..db5b4df8 100644 --- a/crates/memtrack/src/allocators/dynamic.rs +++ b/crates/memtrack/src/allocators/dynamic.rs @@ -49,8 +49,6 @@ pub fn find_all() -> anyhow::Result> { let mut seen_paths: HashSet = HashSet::new(); for kind in AllocatorKind::all() { - let mut found_any = false; - for pattern in kind.search_patterns() { let paths = glob::glob(&pattern) .ok() @@ -69,15 +67,9 @@ pub fn find_all() -> anyhow::Result> { for path in paths { if seen_paths.insert(path.clone()) { results.push(AllocatorLib { kind: *kind, path }); - found_any = true; } } } - - // FIXME: Do we still need this? - if kind.is_required() && !found_any { - anyhow::bail!("Could not find required allocator: {}", kind.name()); - } } Ok(results) diff --git a/crates/memtrack/src/allocators/mod.rs b/crates/memtrack/src/allocators/mod.rs index 5b35efc6..f6602051 100644 --- a/crates/memtrack/src/allocators/mod.rs +++ b/crates/memtrack/src/allocators/mod.rs @@ -59,11 +59,6 @@ impl AllocatorKind { AllocatorKind::Tcmalloc => "tcmalloc", } } - - /// Returns true if this allocator is required (must be found). - pub fn is_required(&self) -> bool { - matches!(self, AllocatorKind::Libc) - } } /// Discovered allocator library with its kind and path.