diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 128a9cb4..8f05994b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: with: lfs: true - - name: 'Install rust-toolchain.toml' + - name: "Install rust-toolchain.toml" run: rustup toolchain install # We use Swatinem/rust-cache to cache cargo registry, index and target in this job - uses: Swatinem/rust-cache@v2 @@ -58,16 +58,26 @@ jobs: - uses: moonrepo/setup-rust@v1 - name: Install dependencies required for libbpf-sys (vendored feature) run: sudo apt-get update && sudo apt-get install -y autopoint bison flex + + - name: Install additional allocators + run: sudo apt-get install -y libmimalloc-dev libjemalloc-dev + - name: Run tests - run: sudo -E $(which cargo) test + env: + RUST_LOG: debug + run: sudo -E $(which cargo) test -- --test-threads 1 --nocapture working-directory: crates/memtrack + # Since we ran the tests with sudo, the build artifacts will have root ownership + - name: Clean up + run: sudo chown -R $USER:$USER . + benchmarks: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: 'Install rust-toolchain.toml' + - name: "Install rust-toolchain.toml" run: rustup toolchain install - uses: Swatinem/rust-cache@v2 - name: Install cargo codspeed diff --git a/Cargo.lock b/Cargo.lock index c20f38fa..937db874 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,17 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -156,6 +167,12 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.0" @@ -168,6 +185,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bincode" version = "1.3.3" @@ -209,6 +232,18 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -218,6 +253,29 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borsh" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "bstr" version = "1.12.1" @@ -235,6 +293,40 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +[[package]] +name = "byte-unit" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c6d47a4e2961fb8721bcfc54feae6455f2f64e7054f9bc67e875f0e77f4c58d" +dependencies = [ + "rust_decimal", + "schemars 1.2.0", + "serde", + "utf8-width", +] + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "bytecount" version = "0.6.9" @@ -325,8 +417,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ "iana-time-zone", + "js-sys", "num-traits", - "windows-link", + "wasm-bindgen", + "windows-link 0.2.1", ] [[package]] @@ -449,7 +543,7 @@ dependencies = [ "anyhow", "async-compression", "async-trait", - "base64", + "base64 0.21.7", "bincode", "clap", "console", @@ -478,14 +572,14 @@ dependencies = [ "rand", "rayon", "regex", - "reqwest", + "reqwest 0.11.27", "reqwest-middleware", "reqwest-retry", "rmp-serde", "rstest 0.25.0", "rstest_reuse", "runner-shared", - "schemars", + "schemars 0.8.22", "semver", "serde", "serde_json", @@ -495,12 +589,12 @@ dependencies = [ "shell-words", "shellexpand", "simplelog", - "sysinfo", + "sysinfo 0.33.1", "tabled", "temp-env", "tempfile", "test-log", - "test-with", + "test-with 0.15.5", "tokio", "tokio-tar", "tokio-util", @@ -754,6 +848,12 @@ dependencies = [ "regex", ] +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + [[package]] name = "env_logger" version = "0.11.8" @@ -872,6 +972,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.31" @@ -1040,7 +1146,7 @@ version = "1.0.7" source = "git+https://github.com/CodSpeedHQ/gql-client-rs#83610cc89083cf3b18d7b7e539e76e82121b6ebb" dependencies = [ "log", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", ] @@ -1056,7 +1162,26 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.0", "indexmap", "slab", "tokio", @@ -1064,6 +1189,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -1076,6 +1210,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "hex" version = "0.4.3" @@ -1093,6 +1233,16 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -1100,7 +1250,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.4.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1132,9 +1305,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1146,6 +1319,44 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2 0.4.13", + "http 1.4.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http 1.4.0", + "hyper 1.8.1", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1153,10 +1364,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.32", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.8.1", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "hyper 1.8.1", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.1", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", ] [[package]] @@ -1292,7 +1545,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", ] [[package]] @@ -1310,9 +1563,9 @@ dependencies = [ [[package]] name = "insta" -version = "1.44.3" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5c943d4415edd8153251b6f197de5eb1640e56d84e8d9159bea190421c73698" +checksum = "248b42847813a1550dafd15296fd9748c651d0c32194559dbc05d804d54b21e8" dependencies = [ "console", "once_cell", @@ -1320,6 +1573,7 @@ dependencies = [ "pest_derive", "serde", "similar", + "tempfile", ] [[package]] @@ -1359,6 +1613,16 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +[[package]] +name = "iri-string" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-docker" version = "0.2.0" @@ -1527,7 +1791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -1681,6 +1945,7 @@ dependencies = [ "clap", "env_logger", "glob", + "insta", "ipc-channel", "itertools 0.14.0", "libbpf-cargo", @@ -1696,6 +1961,7 @@ dependencies = [ "static_assertions", "tempfile", "test-log", + "test-with 0.14.11", "vmlinux", ] @@ -1839,12 +2105,41 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "number_prefix" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "object" version = "0.36.7" @@ -2008,7 +2303,7 @@ dependencies = [ "libc", "redox_syscall 0.5.18", "smallvec", - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -2084,6 +2379,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "ping" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122ee1f5a6843bec84fcbd5c6ba3622115337a6b8965b93a61aad347648f4e8d" +dependencies = [ + "rand", + "socket2 0.4.10", + "thiserror 1.0.69", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -2250,6 +2556,26 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quote" version = "1.0.42" @@ -2265,6 +2591,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -2353,6 +2685,26 @@ dependencies = [ "thiserror 2.0.17", ] +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "regex" version = "1.12.2" @@ -2394,22 +2746,31 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "reqwest" version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper-tls 0.5.0", "ipnet", "js-sys", "log", @@ -2423,7 +2784,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -2438,19 +2799,61 @@ dependencies = [ ] [[package]] -name = "reqwest-middleware" -version = "0.2.5" +name = "reqwest" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a735987236a8e238bf0296c7e351b999c188ccc11477f311b82b55c93984216" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "anyhow", - "async-trait", - "http", - "reqwest", - "serde", - "task-local-extensions", - "thiserror 1.0.69", -] + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.4.13", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-rustls", + "hyper-tls 0.6.0", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "reqwest-middleware" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a735987236a8e238bf0296c7e351b999c188ccc11477f311b82b55c93984216" +dependencies = [ + "anyhow", + "async-trait", + "http 0.2.12", + "reqwest 0.11.27", + "serde", + "task-local-extensions", + "thiserror 1.0.69", +] [[package]] name = "reqwest-retry" @@ -2463,10 +2866,10 @@ dependencies = [ "chrono", "futures", "getrandom 0.2.16", - "http", - "hyper", + "http 0.2.12", + "hyper 0.14.32", "parking_lot 0.11.2", - "reqwest", + "reqwest 0.11.27", "reqwest-middleware", "retry-policies", "task-local-extensions", @@ -2486,6 +2889,49 @@ dependencies = [ "rand", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rmp" version = "0.8.14" @@ -2594,6 +3040,22 @@ dependencies = [ "zstd", ] +[[package]] +name = "rust_decimal" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f703d19852dbf87cbc513643fa81428361eb6940f1ac14fd58155d295a3eb0" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -2641,13 +3103,46 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.23.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64", + "base64 0.21.7", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] @@ -2701,6 +3196,18 @@ dependencies = [ "serde_json", ] +[[package]] +name = "schemars" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "schemars_derive" version = "0.8.22" @@ -2719,6 +3226,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "security-framework" version = "2.11.1" @@ -2901,6 +3414,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + [[package]] name = "similar" version = "2.7.0" @@ -2930,6 +3449,16 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "socket2" version = "0.5.10" @@ -2978,6 +3507,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -2985,6 +3520,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", + "quote", "unicode-ident", ] @@ -3005,6 +3541,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.2" @@ -3031,6 +3576,20 @@ dependencies = [ "windows 0.57.0", ] +[[package]] +name = "sysinfo" +version = "0.35.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows 0.61.3", +] + [[package]] name = "system-configuration" version = "0.5.1" @@ -3078,6 +3637,12 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "task-local-extensions" version = "0.1.4" @@ -3151,6 +3716,27 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "test-with" +version = "0.14.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75791778b593ea6d76454886a69cd37245c4c9af6fee37c42f024593bf1fb03" +dependencies = [ + "byte-unit", + "chrono", + "num_cpus", + "ping", + "proc-macro-error2", + "proc-macro2", + "quote", + "regex", + "reqwest 0.12.28", + "syn 2.0.111", + "sysinfo 0.35.2", + "uzers", + "which", +] + [[package]] name = "test-with" version = "0.15.5" @@ -3264,6 +3850,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.48.0" @@ -3300,6 +3901,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.17" @@ -3369,6 +3980,45 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -3500,6 +4150,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.7" @@ -3512,6 +4168,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf8-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -3535,6 +4197,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "uzers" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d283dc7e8c901e79e32d077866eaf599156cbf427fffa8289aecc52c5c3f63" +dependencies = [ + "libc", + "log", +] + [[package]] name = "valuable" version = "0.1.1" @@ -3708,6 +4380,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "which" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" +dependencies = [ + "env_home", + "rustix 1.1.2", + "winsafe", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3759,6 +4442,28 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + [[package]] name = "windows-core" version = "0.57.0" @@ -3784,6 +4489,19 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -3792,11 +4510,22 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement 0.60.2", "windows-interface 0.59.3", - "windows-link", + "windows-link 0.2.1", "windows-result 0.4.1", "windows-strings 0.5.1", ] +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.57.0" @@ -3863,12 +4592,39 @@ dependencies = [ "syn 2.0.111", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + [[package]] name = "windows-result" version = "0.1.2" @@ -3887,13 +4643,22 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-result" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -3906,13 +4671,22 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows-strings" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -3957,7 +4731,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link", + "windows-link 0.2.1", ] [[package]] @@ -3997,7 +4771,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link", + "windows-link 0.2.1", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -4008,6 +4782,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4165,6 +4948,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "wit-bindgen" version = "0.46.0" @@ -4177,6 +4966,15 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "xattr" version = "1.6.1" @@ -4251,6 +5049,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + [[package]] name = "zerotrie" version = "0.2.3" diff --git a/crates/memtrack/Cargo.toml b/crates/memtrack/Cargo.toml index 9d8d238d..e20c7ff6 100644 --- a/crates/memtrack/Cargo.toml +++ b/crates/memtrack/Cargo.toml @@ -44,6 +44,8 @@ bindgen = "0.71" tempfile = { workspace = true } rstest = "0.21" test-log = "0.2" +insta = { version = "1.46.1", default-features = false } +test-with = "0.14" [package.metadata.dist] dist = true diff --git a/crates/memtrack/src/allocators/dynamic.rs b/crates/memtrack/src/allocators/dynamic.rs index c35e427c..7bc270ac 100644 --- a/crates/memtrack/src/allocators/dynamic.rs +++ b/crates/memtrack/src/allocators/dynamic.rs @@ -1,6 +1,5 @@ -use std::path::PathBuf; - 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] { @@ -15,6 +14,16 @@ fn get_allocator_paths(lib: &AllocatorKind) -> &'static [&'static str] { // 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*", @@ -62,6 +71,7 @@ pub fn find_all() -> anyhow::Result> { .map(|m| m.is_file()) .unwrap_or(false) }) + .filter(|path| super::is_elf(path)) .collect::>(); for path in paths { diff --git a/crates/memtrack/src/allocators/mod.rs b/crates/memtrack/src/allocators/mod.rs index 70ad34ec..141c2020 100644 --- a/crates/memtrack/src/allocators/mod.rs +++ b/crates/memtrack/src/allocators/mod.rs @@ -13,6 +13,8 @@ mod static_linked; pub enum AllocatorKind { /// Standard C library (glibc, musl, etc.) Libc, + /// C++ standard library (libstdc++, libc++) - provides operator new/delete + LibCpp, /// jemalloc - used by FreeBSD, Firefox, many Rust projects Jemalloc, /// mimalloc - Microsoft's allocator @@ -26,10 +28,13 @@ pub enum AllocatorKind { impl AllocatorKind { /// Returns all supported allocator kinds. pub fn all() -> &'static [AllocatorKind] { + // IMPORTANT: Check non-default allocators first, because they will contain compatibility + // layers for the default allocators. &[ - AllocatorKind::Libc, AllocatorKind::Jemalloc, AllocatorKind::Mimalloc, + AllocatorKind::LibCpp, + AllocatorKind::Libc, ] } @@ -37,6 +42,7 @@ impl AllocatorKind { pub fn name(&self) -> &'static str { match self { AllocatorKind::Libc => "libc", + AllocatorKind::LibCpp => "libc++", AllocatorKind::Jemalloc => "jemalloc", AllocatorKind::Mimalloc => "mimalloc", } @@ -51,7 +57,8 @@ impl AllocatorKind { pub fn symbols(&self) -> &'static [&'static str] { match self { AllocatorKind::Libc => &["malloc", "free"], - AllocatorKind::Jemalloc => &["_rjem_malloc", "_rjem_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"], } } @@ -71,3 +78,22 @@ impl AllocatorLib { Ok(allocators) } } + +/// Check if a file is an ELF binary by reading its magic bytes. +fn is_elf(path: &std::path::Path) -> bool { + use std::fs; + use std::io::Read; + + let mut file = match fs::File::open(path) { + Ok(f) => f, + Err(_) => return false, + }; + + let mut magic = [0u8; 4]; + if file.read_exact(&mut magic).is_err() { + return false; + } + + // ELF magic: 0x7F 'E' 'L' 'F' + magic == [0x7F, b'E', b'L', b'F'] +} diff --git a/crates/memtrack/src/allocators/static_linked.rs b/crates/memtrack/src/allocators/static_linked.rs index 2a0f9bab..45df99d8 100644 --- a/crates/memtrack/src/allocators/static_linked.rs +++ b/crates/memtrack/src/allocators/static_linked.rs @@ -4,23 +4,6 @@ use std::path::{Path, PathBuf}; use crate::allocators::{AllocatorKind, AllocatorLib}; -/// Check if a file is an ELF binary by reading its magic bytes. -fn is_elf(path: &Path) -> bool { - let mut file = match fs::File::open(path) { - Ok(f) => f, - Err(_) => return false, - }; - - let mut magic = [0u8; 4]; - use std::io::Read; - if file.read_exact(&mut magic).is_err() { - return false; - } - - // ELF magic: 0x7F 'E' 'L' 'F' - magic == [0x7F, b'E', b'L', b'F'] -} - /// Walk upward from current directory to find build directories. /// Returns all found build directories in order of preference. fn find_build_dirs() -> Vec { @@ -61,7 +44,7 @@ fn find_binaries_in_dir(dir: &Path) -> Vec { .into_iter() .flatten() .filter_map(Result::ok) - .filter(|p| p.is_file() && is_elf(p)) + .filter(|p| p.is_file() && super::is_elf(p)) .collect::>() } @@ -107,3 +90,13 @@ pub fn find_all() -> anyhow::Result> { Ok(allocators) } + +impl AllocatorLib { + pub fn from_path_static(path: &Path) -> Result> { + let kind = find_statically_linked_allocator(path).ok_or("No allocator found")?; + Ok(Self { + kind, + path: path.to_path_buf(), + }) + } +} diff --git a/crates/memtrack/src/ebpf/events.rs b/crates/memtrack/src/ebpf/events.rs index e33acbf0..3ecb7a67 100644 --- a/crates/memtrack/src/ebpf/events.rs +++ b/crates/memtrack/src/ebpf/events.rs @@ -1,5 +1,4 @@ use runner_shared::artifacts::{MemtrackEvent, MemtrackEventKind}; -use serde::{Deserialize, Serialize}; // Include the bindings for event.h pub mod bindings { @@ -12,55 +11,6 @@ pub mod bindings { } use bindings::*; -#[repr(u8)] -#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)] -pub enum EventType { - Malloc = EVENT_TYPE_MALLOC as u8, - Free = EVENT_TYPE_FREE as u8, - Calloc = EVENT_TYPE_CALLOC as u8, - Realloc = EVENT_TYPE_REALLOC as u8, - AlignedAlloc = EVENT_TYPE_ALIGNED_ALLOC as u8, - Mmap = EVENT_TYPE_MMAP as u8, - Munmap = EVENT_TYPE_MUNMAP as u8, - Brk = EVENT_TYPE_BRK as u8, -} - -impl From for EventType { - fn from(val: u8) -> Self { - match val as u32 { - bindings::EVENT_TYPE_MALLOC => EventType::Malloc, - bindings::EVENT_TYPE_FREE => EventType::Free, - bindings::EVENT_TYPE_CALLOC => EventType::Calloc, - bindings::EVENT_TYPE_REALLOC => EventType::Realloc, - bindings::EVENT_TYPE_ALIGNED_ALLOC => EventType::AlignedAlloc, - bindings::EVENT_TYPE_MMAP => EventType::Mmap, - bindings::EVENT_TYPE_MUNMAP => EventType::Munmap, - bindings::EVENT_TYPE_BRK => EventType::Brk, - _ => panic!("Unknown event type: {val}"), - } - } -} - -/// Extension trait for MemtrackEvent to get the EventType -pub trait MemtrackEventExt { - fn event_type(&self) -> EventType; -} - -impl MemtrackEventExt for MemtrackEvent { - fn event_type(&self) -> EventType { - match self.kind { - MemtrackEventKind::Malloc { .. } => EventType::Malloc, - MemtrackEventKind::Free => EventType::Free, - MemtrackEventKind::Calloc { .. } => EventType::Calloc, - MemtrackEventKind::Realloc { .. } => EventType::Realloc, - MemtrackEventKind::AlignedAlloc { .. } => EventType::AlignedAlloc, - MemtrackEventKind::Mmap { .. } => EventType::Mmap, - MemtrackEventKind::Munmap { .. } => EventType::Munmap, - MemtrackEventKind::Brk { .. } => EventType::Brk, - } - } -} - /// Parse an event from raw bytes into MemtrackEvent /// /// SAFETY: The data must be a valid `bindings::event` @@ -70,7 +20,6 @@ pub fn parse_event(data: &[u8]) -> Option { } let event = unsafe { &*(data.as_ptr() as *const bindings::event) }; - let event_type = EventType::from(event.header.event_type); // Common fields from header let pid = event.header.pid as i32; @@ -80,51 +29,54 @@ pub fn parse_event(data: &[u8]) -> Option { // Parse event data based on type // SAFETY: The fields must be properly initialized in eBPF let (addr, kind) = unsafe { - match event_type { - EventType::Malloc => ( + match event.header.event_type as u32 { + EVENT_TYPE_MALLOC => ( event.data.alloc.addr, MemtrackEventKind::Malloc { size: event.data.alloc.size, }, ), - EventType::Free => (event.data.free.addr, MemtrackEventKind::Free), - EventType::Calloc => ( + EVENT_TYPE_FREE => (event.data.free.addr, MemtrackEventKind::Free), + EVENT_TYPE_CALLOC => ( event.data.alloc.addr, MemtrackEventKind::Calloc { size: event.data.alloc.size, }, ), - EventType::Realloc => ( + EVENT_TYPE_REALLOC => ( event.data.realloc.new_addr, MemtrackEventKind::Realloc { old_addr: Some(event.data.realloc.old_addr), size: event.data.realloc.size, }, ), - EventType::AlignedAlloc => ( + EVENT_TYPE_ALIGNED_ALLOC => ( event.data.alloc.addr, MemtrackEventKind::AlignedAlloc { size: event.data.alloc.size, }, ), - EventType::Mmap => ( + EVENT_TYPE_MMAP => ( event.data.mmap.addr, MemtrackEventKind::Mmap { size: event.data.mmap.size, }, ), - EventType::Munmap => ( + EVENT_TYPE_MUNMAP => ( event.data.mmap.addr, MemtrackEventKind::Munmap { size: event.data.mmap.size, }, ), - EventType::Brk => ( + EVENT_TYPE_BRK => ( event.data.mmap.addr, MemtrackEventKind::Brk { size: event.data.mmap.size, }, ), + unknown => { + panic!("Unknown event type: {unknown}"); + } } }; diff --git a/crates/memtrack/src/ebpf/memtrack.rs b/crates/memtrack/src/ebpf/memtrack.rs index a8105107..1e9cd415 100644 --- a/crates/memtrack/src/ebpf/memtrack.rs +++ b/crates/memtrack/src/ebpf/memtrack.rs @@ -262,19 +262,6 @@ impl MemtrackBpf { // Attach methods grouped by allocator // ========================================================================= - /// Attach standard library allocation probes (libc-style: malloc, free, calloc, etc.) - /// This works for libc and allocators that export standard symbol names. - /// For non-libc allocators, standard names are optional - just try them silently. - pub fn attach_libc_probes(&mut self, lib_path: &Path) -> Result<()> { - self.try_attach_malloc(lib_path, "malloc"); - self.try_attach_calloc(lib_path, "calloc"); - self.try_attach_realloc(lib_path, "realloc"); - self.try_attach_free(lib_path, "free"); - self.try_attach_aligned_alloc(lib_path, "aligned_alloc"); - self.try_attach_memalign(lib_path, "memalign"); - Ok(()) - } - /// Attach probes for a specific allocator kind. /// This attaches both standard probes (if the allocator exports them) and /// allocator-specific prefixed probes. @@ -290,19 +277,82 @@ impl MemtrackBpf { // Libc only has standard probes, and they must succeed self.attach_libc_probes(lib_path) } + AllocatorKind::LibCpp => { + // libc++ exports C++ operator new/delete symbols + self.attach_libcpp_probes(lib_path) + } AllocatorKind::Jemalloc => { - // Try standard names (jemalloc may export these as drop-in replacements) - let _ = self.attach_libc_probes(lib_path); + // Try C++ operators (jemalloc exports these for C++ programs) + let _ = self.attach_libcpp_probes(lib_path); self.attach_jemalloc_probes(lib_path) } AllocatorKind::Mimalloc => { - // Try standard names (mimalloc may export these as drop-in replacements) - let _ = self.attach_libc_probes(lib_path); + // Try C++ operators (mimalloc exports these for C++ programs) + let _ = self.attach_libcpp_probes(lib_path); self.attach_mimalloc_probes(lib_path) } } } + fn attach_standard_probes( + &mut self, + lib_path: &Path, + prefixes: &[&str], + suffixes: &[&str], + ) -> Result<()> { + // Always include "" to capture the basic case + let prefixes_with_base: Vec<&str> = std::iter::once("") + .chain(prefixes.iter().copied()) + .unique() + .collect(); + + let suffixes_with_base: Vec<&str> = std::iter::once("") + .chain(suffixes.iter().copied()) + .unique() + .collect(); + + 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_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}")); + } + } + + Ok(()) + } + + /// Attach standard library allocation probes (libc-style: malloc, free, calloc, etc.) + /// This works for libc and allocators that export standard symbol names. + /// For non-libc allocators, standard names are optional - just try them silently. + fn attach_libc_probes(&mut self, lib_path: &Path) -> Result<()> { + self.attach_standard_probes(lib_path, &[], &[]) + } + + /// Attach C++ operator new/delete probes. + /// These are mangled C++ symbols that wrap the underlying allocator. + /// C++ operators have identical signatures to malloc/free, so we reuse those handlers. + fn attach_libcpp_probes(&mut self, lib_path: &Path) -> Result<()> { + self.try_attach_malloc(lib_path, "_Znwm"); // operator new(size_t) + self.try_attach_malloc(lib_path, "_Znam"); // operator new[](size_t) + self.try_attach_malloc(lib_path, "_ZnwmSt11align_val_t"); // operator new(size_t, std::align_val_t) + self.try_attach_malloc(lib_path, "_ZnamSt11align_val_t"); // operator new[](size_t, std::align_val_t) + self.try_attach_free(lib_path, "_ZdlPv"); // operator delete(void*) + self.try_attach_free(lib_path, "_ZdaPv"); // operator delete[](void*) + self.try_attach_free(lib_path, "_ZdlPvm"); // operator delete(void*, size_t) - C++14 sized delete + self.try_attach_free(lib_path, "_ZdaPvm"); // operator delete[](void*, size_t) - C++14 sized delete + self.try_attach_free(lib_path, "_ZdlPvSt11align_val_t"); // operator delete(void*, std::align_val_t) + self.try_attach_free(lib_path, "_ZdaPvSt11align_val_t"); // operator delete[](void*, std::align_val_t) + self.try_attach_free(lib_path, "_ZdlPvmSt11align_val_t"); // operator delete(void*, size_t, std::align_val_t) + self.try_attach_free(lib_path, "_ZdaPvmSt11align_val_t"); // operator delete[](void*, size_t, std::align_val_t) + + Ok(()) + } + /// Attach jemalloc-specific probes (prefixed and extended API). fn attach_jemalloc_probes(&mut self, lib_path: &Path) -> Result<()> { // The following functions are used in Rust when setting a global allocator: @@ -311,16 +361,23 @@ impl MemtrackBpf { // - rust_dealloc: _rjem_sdallocx // - rust_realloc: _rjem_realloc / _rjem_rallocx - // Prefixed standard API - self.try_attach_malloc(lib_path, "_rjem_malloc"); - self.try_attach_malloc(lib_path, "_rjem_mallocx"); // Also used for `calloc` - self.try_attach_calloc(lib_path, "_rjem_calloc"); - self.try_attach_realloc(lib_path, "_rjem_realloc"); - self.try_attach_realloc(lib_path, "_rjem_rallocx"); - self.try_attach_aligned_alloc(lib_path, "_rjem_aligned_alloc"); - self.try_attach_memalign(lib_path, "_rjem_memalign"); - self.try_attach_free(lib_path, "_rjem_free"); - self.try_attach_free(lib_path, "_rjem_sdallocx"); + // je_* API (internal jemalloc functions, static linking) + // _rjem_* API (Rust jemalloc crate, dynamic linking) + let prefixes = ["je_", "_rjem_"]; + let suffixes = ["", "_default"]; + + self.attach_standard_probes(lib_path, &prefixes, &suffixes)?; + + // Non-standard API that has an additional flag parameter + // See: https://jemalloc.net/jemalloc.3.html + for prefix in prefixes { + for suffix in suffixes { + self.try_attach_malloc(lib_path, &format!("{prefix}mallocx{suffix}")); + self.try_attach_realloc(lib_path, &format!("{prefix}rallocx{suffix}")); + self.try_attach_free(lib_path, &format!("{prefix}dallocx{suffix}")); + self.try_attach_free(lib_path, &format!("{prefix}sdallocx{suffix}")); + } + } Ok(()) } @@ -333,16 +390,10 @@ impl MemtrackBpf { // - mi_realloc_aligned // - mi_zalloc_aligned - // Core API - self.try_attach_malloc(lib_path, "mi_malloc"); - self.try_attach_malloc(lib_path, "mi_malloc_aligned"); - self.try_attach_calloc(lib_path, "mi_calloc"); - self.try_attach_realloc(lib_path, "mi_realloc"); - self.try_attach_aligned_alloc(lib_path, "mi_aligned_alloc"); - self.try_attach_memalign(lib_path, "mi_memalign"); - self.try_attach_free(lib_path, "mi_free"); + self.attach_standard_probes(lib_path, &["mi_"], &[])?; // Zero-initialized and aligned variants + self.try_attach_malloc(lib_path, "mi_malloc_aligned"); self.try_attach_calloc(lib_path, "mi_zalloc"); self.try_attach_calloc(lib_path, "mi_zalloc_aligned"); self.try_attach_realloc(lib_path, "mi_realloc_aligned"); diff --git a/crates/memtrack/src/ebpf/mod.rs b/crates/memtrack/src/ebpf/mod.rs index 26c95f41..0badf542 100644 --- a/crates/memtrack/src/ebpf/mod.rs +++ b/crates/memtrack/src/ebpf/mod.rs @@ -3,6 +3,5 @@ mod memtrack; mod poller; mod tracker; -pub use events::{EventType, MemtrackEventExt}; pub use memtrack::MemtrackBpf; pub use tracker::Tracker; diff --git a/crates/memtrack/src/ebpf/tracker.rs b/crates/memtrack/src/ebpf/tracker.rs index 1f6d9457..b4a7a06e 100644 --- a/crates/memtrack/src/ebpf/tracker.rs +++ b/crates/memtrack/src/ebpf/tracker.rs @@ -16,22 +16,36 @@ impl Tracker { /// - Attach uprobes to all libc instances /// - Attach tracepoints for fork tracking pub fn new() -> Result { + let mut instance = Self::new_without_allocators()?; + + let allocators = AllocatorLib::find_all()?; + debug!("Found {} allocator instance(s)", allocators.len()); + instance.attach_allocators(&allocators)?; + + Ok(instance) + } + + pub fn new_without_allocators() -> Result { // Bump memlock limits Self::bump_memlock_rlimit()?; let mut bpf = MemtrackBpf::new()?; bpf.attach_tracepoints()?; - // Find and attach to all allocators - let allocators = AllocatorLib::find_all()?; - debug!("Found {} allocator instance(s)", allocators.len()); + Ok(Self { bpf }) + } - for allocator in &allocators { - debug!("Attaching uprobes to: {}", allocator.path.display()); - bpf.attach_allocator_probes(allocator.kind, &allocator.path)?; + pub fn attach_allocators(&mut self, libs: &[AllocatorLib]) -> Result<()> { + for allocator in libs { + self.bpf + .attach_allocator_probes(allocator.kind, &allocator.path)?; } - Ok(Self { bpf }) + Ok(()) + } + + pub fn attach_allocator(&mut self, lib: &AllocatorLib) -> Result<()> { + self.bpf.attach_allocator_probes(lib.kind, &lib.path) } /// Start tracking allocations for a specific PID diff --git a/crates/memtrack/testdata/alloc_cpp/.gitignore b/crates/memtrack/testdata/alloc_cpp/.gitignore new file mode 100644 index 00000000..567609b1 --- /dev/null +++ b/crates/memtrack/testdata/alloc_cpp/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/crates/memtrack/testdata/alloc_cpp/CMakeLists.txt b/crates/memtrack/testdata/alloc_cpp/CMakeLists.txt new file mode 100644 index 00000000..1811a71f --- /dev/null +++ b/crates/memtrack/testdata/alloc_cpp/CMakeLists.txt @@ -0,0 +1,171 @@ +# This file is automatically generated from cmake.toml - DO NOT EDIT +# See https://github.com/build-cpp/cmkr for more information + +cmake_minimum_required(VERSION 3.15) + +if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) + message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build") +endif() + +set(CMKR_ROOT_PROJECT OFF) +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(CMKR_ROOT_PROJECT ON) + + # Bootstrap cmkr and automatically regenerate CMakeLists.txt + include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT) + if(CMKR_INCLUDE_RESULT) + cmkr() + endif() + + # Enable folder support + set_property(GLOBAL PROPERTY USE_FOLDERS ON) + + # Create a configure-time dependency on cmake.toml to improve IDE support + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS cmake.toml) +endif() + +project(alloc_cpp + LANGUAGES + CXX + VERSION + 0.1.0 +) + +# Find static libraries - CMake searches CMAKE_PREFIX_PATH (NixOS) and system paths (Ubuntu) +find_library(MIMALLOC_STATIC_LIB + NAMES libmimalloc.a mimalloc-static + DOC "Static mimalloc library" +) + +find_library(JEMALLOC_STATIC_LIB + NAMES libjemalloc.a jemalloc_pic + DOC "Static jemalloc library" +) + +if(NOT MIMALLOC_STATIC_LIB) + message(STATUS "Static mimalloc not found, will fall back to dynamic linking") +endif() + +if(NOT JEMALLOC_STATIC_LIB) + message(STATUS "Static jemalloc not found, will fall back to dynamic linking") +endif() + +# Target: alloc_cpp_system +set(alloc_cpp_system_SOURCES + cmake.toml + "src/main.cpp" +) + +add_executable(alloc_cpp_system) + +target_sources(alloc_cpp_system PRIVATE ${alloc_cpp_system_SOURCES}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${alloc_cpp_system_SOURCES}) + +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_system) +endif() + +# Target: alloc_cpp_jemalloc_static +set(alloc_cpp_jemalloc_static_SOURCES + cmake.toml + "src/main.cpp" +) + +add_executable(alloc_cpp_jemalloc_static) + +target_sources(alloc_cpp_jemalloc_static PRIVATE ${alloc_cpp_jemalloc_static_SOURCES}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${alloc_cpp_jemalloc_static_SOURCES}) + +target_compile_definitions(alloc_cpp_jemalloc_static PRIVATE + USE_JEMALLOC=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_jemalloc_static) +endif() + +set(CMKR_TARGET alloc_cpp_jemalloc_static) +if(JEMALLOC_STATIC_LIB) + target_link_libraries(alloc_cpp_jemalloc_static PRIVATE ${JEMALLOC_STATIC_LIB}) +else() + # Fallback to dynamic linking + target_link_libraries(alloc_cpp_jemalloc_static PRIVATE jemalloc) +endif() + +# Target: alloc_cpp_jemalloc_dynamic +set(alloc_cpp_jemalloc_dynamic_SOURCES + cmake.toml + "src/main.cpp" +) + +add_executable(alloc_cpp_jemalloc_dynamic) + +target_sources(alloc_cpp_jemalloc_dynamic PRIVATE ${alloc_cpp_jemalloc_dynamic_SOURCES}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${alloc_cpp_jemalloc_dynamic_SOURCES}) + +target_compile_definitions(alloc_cpp_jemalloc_dynamic PRIVATE + USE_JEMALLOC=1 +) + +target_link_libraries(alloc_cpp_jemalloc_dynamic PRIVATE + jemalloc +) + +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_jemalloc_dynamic) +endif() + +# Target: alloc_cpp_mimalloc_static +set(alloc_cpp_mimalloc_static_SOURCES + cmake.toml + "src/main.cpp" +) + +add_executable(alloc_cpp_mimalloc_static) + +target_sources(alloc_cpp_mimalloc_static PRIVATE ${alloc_cpp_mimalloc_static_SOURCES}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${alloc_cpp_mimalloc_static_SOURCES}) + +target_compile_definitions(alloc_cpp_mimalloc_static PRIVATE + USE_MIMALLOC=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_mimalloc_static) +endif() + +set(CMKR_TARGET alloc_cpp_mimalloc_static) +if(MIMALLOC_STATIC_LIB) + target_link_libraries(alloc_cpp_mimalloc_static PRIVATE ${MIMALLOC_STATIC_LIB}) +else() + # Fallback to dynamic linking + target_link_libraries(alloc_cpp_mimalloc_static PRIVATE mimalloc) +endif() + +# Target: alloc_cpp_mimalloc_dynamic +set(alloc_cpp_mimalloc_dynamic_SOURCES + cmake.toml + "src/main.cpp" +) + +add_executable(alloc_cpp_mimalloc_dynamic) + +target_sources(alloc_cpp_mimalloc_dynamic PRIVATE ${alloc_cpp_mimalloc_dynamic_SOURCES}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${alloc_cpp_mimalloc_dynamic_SOURCES}) + +target_compile_definitions(alloc_cpp_mimalloc_dynamic PRIVATE + USE_MIMALLOC=1 +) + +target_link_libraries(alloc_cpp_mimalloc_dynamic PRIVATE + mimalloc +) + +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_mimalloc_dynamic) +endif() diff --git a/crates/memtrack/testdata/alloc_cpp/cmake.toml b/crates/memtrack/testdata/alloc_cpp/cmake.toml new file mode 100644 index 00000000..4b7631aa --- /dev/null +++ b/crates/memtrack/testdata/alloc_cpp/cmake.toml @@ -0,0 +1,71 @@ +[project] +name = "alloc_cpp" +version = "0.1.0" +languages = ["CXX"] +cmake-after = """ +# Find static libraries - CMake searches CMAKE_PREFIX_PATH (NixOS) and system paths (Ubuntu) +find_library(MIMALLOC_STATIC_LIB + NAMES libmimalloc.a mimalloc-static + DOC "Static mimalloc library" +) + +find_library(JEMALLOC_STATIC_LIB + NAMES libjemalloc.a jemalloc_pic + DOC "Static jemalloc library" +) + +if(NOT MIMALLOC_STATIC_LIB) + message(STATUS "Static mimalloc not found, will fall back to dynamic linking") +endif() + +if(NOT JEMALLOC_STATIC_LIB) + message(STATUS "Static jemalloc not found, will fall back to dynamic linking") +endif() +""" + +# System allocator (libc default malloc) +[target.alloc_cpp_system] +type = "executable" +sources = ["src/main.cpp"] + +# Jemalloc - static linking +[target.alloc_cpp_jemalloc_static] +type = "executable" +sources = ["src/main.cpp"] +compile-definitions = ["USE_JEMALLOC=1"] +cmake-after = """ +if(JEMALLOC_STATIC_LIB) + target_link_libraries(alloc_cpp_jemalloc_static PRIVATE ${JEMALLOC_STATIC_LIB}) +else() + # Fallback to dynamic linking + target_link_libraries(alloc_cpp_jemalloc_static PRIVATE jemalloc) +endif() +""" + +# Jemalloc - dynamic linking +[target.alloc_cpp_jemalloc_dynamic] +type = "executable" +sources = ["src/main.cpp"] +compile-definitions = ["USE_JEMALLOC=1"] +link-libraries = ["jemalloc"] + +# Mimalloc - static linking +[target.alloc_cpp_mimalloc_static] +type = "executable" +sources = ["src/main.cpp"] +compile-definitions = ["USE_MIMALLOC=1"] +cmake-after = """ +if(MIMALLOC_STATIC_LIB) + target_link_libraries(alloc_cpp_mimalloc_static PRIVATE ${MIMALLOC_STATIC_LIB}) +else() + # Fallback to dynamic linking + target_link_libraries(alloc_cpp_mimalloc_static PRIVATE mimalloc) +endif() +""" + +# Mimalloc - dynamic linking +[target.alloc_cpp_mimalloc_dynamic] +type = "executable" +sources = ["src/main.cpp"] +compile-definitions = ["USE_MIMALLOC=1"] +link-libraries = ["mimalloc"] diff --git a/crates/memtrack/testdata/alloc_cpp/cmkr.cmake b/crates/memtrack/testdata/alloc_cpp/cmkr.cmake new file mode 100644 index 00000000..6e88a86b --- /dev/null +++ b/crates/memtrack/testdata/alloc_cpp/cmkr.cmake @@ -0,0 +1,260 @@ +include_guard() + +# Change these defaults to point to your infrastructure if desired +set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE) +set(CMKR_TAG "v0.2.45" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE) +set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE) + +# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake +if(CMAKE_SCRIPT_MODE_FILE) + set(CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}/build") + set(CMAKE_CURRENT_BINARY_DIR "${CMAKE_BINARY_DIR}") + file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}") +endif() + +# Set these from the command line to customize for development/debugging purposes +set(CMKR_EXECUTABLE "" CACHE FILEPATH "cmkr executable") +set(CMKR_SKIP_GENERATION OFF CACHE BOOL "skip automatic cmkr generation") +set(CMKR_BUILD_TYPE "Debug" CACHE STRING "cmkr build configuration") +mark_as_advanced(CMKR_REPO CMKR_TAG CMKR_COMMIT_HASH CMKR_EXECUTABLE CMKR_SKIP_GENERATION CMKR_BUILD_TYPE) + +# Disable cmkr if generation is disabled +if(DEFINED ENV{CI} OR CMKR_SKIP_GENERATION OR CMKR_BUILD_SKIP_GENERATION) + message(STATUS "[cmkr] Skipping automatic cmkr generation") + unset(CMKR_BUILD_SKIP_GENERATION CACHE) + macro(cmkr) + endmacro() + return() +endif() + +# Disable cmkr if no cmake.toml file is found +if(NOT CMAKE_SCRIPT_MODE_FILE AND NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml") + message(AUTHOR_WARNING "[cmkr] Not found: ${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml") + macro(cmkr) + endmacro() + return() +endif() + +# Convert a Windows native path to CMake path +if(CMKR_EXECUTABLE MATCHES "\\\\") + string(REPLACE "\\" "/" CMKR_EXECUTABLE_CMAKE "${CMKR_EXECUTABLE}") + set(CMKR_EXECUTABLE "${CMKR_EXECUTABLE_CMAKE}" CACHE FILEPATH "" FORCE) + unset(CMKR_EXECUTABLE_CMAKE) +endif() + +# Helper macro to execute a process (COMMAND_ERROR_IS_FATAL ANY is 3.19 and higher) +function(cmkr_exec) + execute_process(COMMAND ${ARGV} RESULT_VARIABLE CMKR_EXEC_RESULT) + if(NOT CMKR_EXEC_RESULT EQUAL 0) + message(FATAL_ERROR "cmkr_exec(${ARGV}) failed (exit code ${CMKR_EXEC_RESULT})") + endif() +endfunction() + +# Windows-specific hack (CMAKE_EXECUTABLE_PREFIX is not set at the moment) +if(WIN32) + set(CMKR_EXECUTABLE_NAME "cmkr.exe") +else() + set(CMKR_EXECUTABLE_NAME "cmkr") +endif() + +# Use cached cmkr if found +if(DEFINED ENV{CMKR_CACHE}) + set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}") + string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}") + if(CMKR_DIRECTORY_PREFIX MATCHES "^~") + if(WIN32) + string(REGEX REPLACE "^~" "$ENV{USERPROFILE}" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}") + elseif(UNIX) + string(REGEX REPLACE "^~" "$ENV{HOME}" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}") + endif() + endif() + if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$") + set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/") + endif() + # Build in release mode for the cache + set(CMKR_BUILD_TYPE "Release") +else() + set(CMKR_DIRECTORY_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/_cmkr_") +endif() +set(CMKR_DIRECTORY "${CMKR_DIRECTORY_PREFIX}${CMKR_TAG}") +set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}") + +# Helper function to check if a string starts with a prefix +# Cannot use MATCHES, see: https://github.com/build-cpp/cmkr/issues/61 +function(cmkr_startswith str prefix result) + string(LENGTH "${prefix}" prefix_length) + string(LENGTH "${str}" str_length) + if(prefix_length LESS_EQUAL str_length) + string(SUBSTRING "${str}" 0 ${prefix_length} str_prefix) + if(prefix STREQUAL str_prefix) + set("${result}" ON PARENT_SCOPE) + return() + endif() + endif() + set("${result}" OFF PARENT_SCOPE) +endfunction() + +# Handle upgrading logic +if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE) + cmkr_startswith("${CMKR_EXECUTABLE}" "${CMAKE_CURRENT_BINARY_DIR}/_cmkr" CMKR_STARTSWITH_BUILD) + cmkr_startswith("${CMKR_EXECUTABLE}" "${CMKR_DIRECTORY_PREFIX}" CMKR_STARTSWITH_CACHE) + if(CMKR_STARTSWITH_BUILD) + if(DEFINED ENV{CMKR_CACHE}) + message(AUTHOR_WARNING "[cmkr] Switching to cached cmkr: '${CMKR_CACHED_EXECUTABLE}'") + if(EXISTS "${CMKR_CACHED_EXECUTABLE}") + set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE) + else() + unset(CMKR_EXECUTABLE CACHE) + endif() + else() + message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'") + unset(CMKR_EXECUTABLE CACHE) + endif() + elseif(DEFINED ENV{CMKR_CACHE} AND CMKR_STARTSWITH_CACHE) + message(AUTHOR_WARNING "[cmkr] Upgrading cached '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'") + unset(CMKR_EXECUTABLE CACHE) + endif() +endif() + +if(CMKR_EXECUTABLE AND EXISTS "${CMKR_EXECUTABLE}") + message(VERBOSE "[cmkr] Found cmkr: '${CMKR_EXECUTABLE}'") +elseif(CMKR_EXECUTABLE AND NOT CMKR_EXECUTABLE STREQUAL CMKR_CACHED_EXECUTABLE) + message(FATAL_ERROR "[cmkr] '${CMKR_EXECUTABLE}' not found") +elseif(NOT CMKR_EXECUTABLE AND EXISTS "${CMKR_CACHED_EXECUTABLE}") + set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE) + message(STATUS "[cmkr] Found cached cmkr: '${CMKR_EXECUTABLE}'") +else() + set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE) + message(VERBOSE "[cmkr] Bootstrapping '${CMKR_EXECUTABLE}'") + + message(STATUS "[cmkr] Fetching cmkr...") + if(EXISTS "${CMKR_DIRECTORY}") + cmkr_exec("${CMAKE_COMMAND}" -E rm -rf "${CMKR_DIRECTORY}") + endif() + find_package(Git QUIET REQUIRED) + cmkr_exec("${GIT_EXECUTABLE}" + clone + --config advice.detachedHead=false + --branch ${CMKR_TAG} + --depth 1 + ${CMKR_REPO} + "${CMKR_DIRECTORY}" + ) + if(CMKR_COMMIT_HASH) + execute_process( + COMMAND "${GIT_EXECUTABLE}" checkout -q "${CMKR_COMMIT_HASH}" + RESULT_VARIABLE CMKR_EXEC_RESULT + WORKING_DIRECTORY "${CMKR_DIRECTORY}" + ) + if(NOT CMKR_EXEC_RESULT EQUAL 0) + message(FATAL_ERROR "Tag '${CMKR_TAG}' hash is not '${CMKR_COMMIT_HASH}'") + endif() + endif() + message(STATUS "[cmkr] Building cmkr (using system compiler)...") + cmkr_exec("${CMAKE_COMMAND}" + --no-warn-unused-cli + "${CMKR_DIRECTORY}" + "-B${CMKR_DIRECTORY}/build" + "-DCMAKE_BUILD_TYPE=${CMKR_BUILD_TYPE}" + "-DCMAKE_UNITY_BUILD=ON" + "-DCMAKE_INSTALL_PREFIX=${CMKR_DIRECTORY}" + "-DCMKR_GENERATE_DOCUMENTATION=OFF" + ) + cmkr_exec("${CMAKE_COMMAND}" + --build "${CMKR_DIRECTORY}/build" + --config "${CMKR_BUILD_TYPE}" + --parallel + ) + cmkr_exec("${CMAKE_COMMAND}" + --install "${CMKR_DIRECTORY}/build" + --config "${CMKR_BUILD_TYPE}" + --prefix "${CMKR_DIRECTORY}" + --component cmkr + ) + if(NOT EXISTS ${CMKR_EXECUTABLE}) + message(FATAL_ERROR "[cmkr] Failed to bootstrap '${CMKR_EXECUTABLE}'") + endif() + cmkr_exec("${CMKR_EXECUTABLE}" version) + message(STATUS "[cmkr] Bootstrapped ${CMKR_EXECUTABLE}") +endif() +execute_process(COMMAND "${CMKR_EXECUTABLE}" version + RESULT_VARIABLE CMKR_EXEC_RESULT +) +if(NOT CMKR_EXEC_RESULT EQUAL 0) + message(FATAL_ERROR "[cmkr] Failed to get version, try clearing the cache and rebuilding") +endif() + +# Use cmkr.cmake as a script +if(CMAKE_SCRIPT_MODE_FILE) + if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake.toml") + execute_process(COMMAND "${CMKR_EXECUTABLE}" init + RESULT_VARIABLE CMKR_EXEC_RESULT + ) + if(NOT CMKR_EXEC_RESULT EQUAL 0) + message(FATAL_ERROR "[cmkr] Failed to bootstrap cmkr project. Please report an issue: https://github.com/build-cpp/cmkr/issues/new") + else() + message(STATUS "[cmkr] Modify cmake.toml and then configure using: cmake -B build") + endif() + else() + execute_process(COMMAND "${CMKR_EXECUTABLE}" gen + RESULT_VARIABLE CMKR_EXEC_RESULT + ) + if(NOT CMKR_EXEC_RESULT EQUAL 0) + message(FATAL_ERROR "[cmkr] Failed to generate project.") + else() + message(STATUS "[cmkr] Configure using: cmake -B build") + endif() + endif() +endif() + +# This is the macro that contains black magic +macro(cmkr) + # When this macro is called from the generated file, fake some internal CMake variables + get_source_file_property(CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}" CMKR_CURRENT_LIST_FILE) + if(CMKR_CURRENT_LIST_FILE) + set(CMAKE_CURRENT_LIST_FILE "${CMKR_CURRENT_LIST_FILE}") + get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY) + endif() + + # File-based include guard (include_guard is not documented to work) + get_source_file_property(CMKR_INCLUDE_GUARD "${CMAKE_CURRENT_LIST_FILE}" CMKR_INCLUDE_GUARD) + if(NOT CMKR_INCLUDE_GUARD) + set_source_files_properties("${CMAKE_CURRENT_LIST_FILE}" PROPERTIES CMKR_INCLUDE_GUARD TRUE) + + file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_PRE) + + # Generate CMakeLists.txt + cmkr_exec("${CMKR_EXECUTABLE}" gen + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + ) + + file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_POST) + + # Delete the temporary file if it was left for some reason + set(CMKR_TEMP_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CMakerLists.txt") + if(EXISTS "${CMKR_TEMP_FILE}") + file(REMOVE "${CMKR_TEMP_FILE}") + endif() + + if(NOT CMKR_LIST_FILE_SHA256_PRE STREQUAL CMKR_LIST_FILE_SHA256_POST) + # Copy the now-generated CMakeLists.txt to CMakerLists.txt + # This is done because you cannot include() a file you are currently in + configure_file(CMakeLists.txt "${CMKR_TEMP_FILE}" COPYONLY) + + # Add the macro required for the hack at the start of the cmkr macro + set_source_files_properties("${CMKR_TEMP_FILE}" PROPERTIES + CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}" + ) + + # 'Execute' the newly-generated CMakeLists.txt + include("${CMKR_TEMP_FILE}") + + # Delete the generated file + file(REMOVE "${CMKR_TEMP_FILE}") + + # Do not execute the rest of the original CMakeLists.txt + return() + endif() + # Resume executing the unmodified CMakeLists.txt + endif() +endmacro() diff --git a/crates/memtrack/testdata/alloc_cpp/justfile b/crates/memtrack/testdata/alloc_cpp/justfile new file mode 100644 index 00000000..a0a2f179 --- /dev/null +++ b/crates/memtrack/testdata/alloc_cpp/justfile @@ -0,0 +1,65 @@ +# C++ Allocator Test Fixture Build System + +# Default recipe - show available commands +default: + @just --list + +# Configure the build with CMake +configure: + cmake -B build -DCMAKE_BUILD_TYPE=Release + +# Clean build directory +clean: + rm -rf build + +# Reconfigure from scratch +reconfigure: clean configure + +# Build a specific target +build target: + cmake --build build --target {{target}} -j + +# Build all targets +build-all: configure + cmake --build build -j + +# Build system allocator target +build-system: configure + cmake --build build --target alloc_cpp_system -j + +# Build jemalloc static target +build-jemalloc-static: configure + cmake --build build --target alloc_cpp_jemalloc_static -j + +# Build jemalloc dynamic target +build-jemalloc-dynamic: configure + cmake --build build --target alloc_cpp_jemalloc_dynamic -j + +# Build mimalloc static target +build-mimalloc-static: configure + cmake --build build --target alloc_cpp_mimalloc_static -j + +# Build mimalloc dynamic target +build-mimalloc-dynamic: configure + cmake --build build --target alloc_cpp_mimalloc_dynamic -j + +# Run a specific target binary +run target: (build target) + ./build/{{target}} + +# Run system allocator binary +run-system: build-system + ./build/alloc_cpp_system + +# Run all binaries +run-all: build-all + @echo "Running system allocator..." + ./build/alloc_cpp_system + @echo "\nRunning jemalloc static..." + ./build/alloc_cpp_jemalloc_static + @echo "\nRunning jemalloc dynamic..." + ./build/alloc_cpp_jemalloc_dynamic + @echo "\nRunning mimalloc static..." + ./build/alloc_cpp_mimalloc_static + @echo "\nRunning mimalloc dynamic..." + ./build/alloc_cpp_mimalloc_dynamic diff --git a/crates/memtrack/testdata/alloc_cpp/src/main.cpp b/crates/memtrack/testdata/alloc_cpp/src/main.cpp new file mode 100644 index 00000000..f1edfc77 --- /dev/null +++ b/crates/memtrack/testdata/alloc_cpp/src/main.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include +#include + +#ifdef USE_JEMALLOC +#include +#endif + +#ifdef USE_MIMALLOC +#include +#endif + +// Prevent compiler from optimizing away allocations +// Similar to Rust's core::hint::black_box +template +inline void black_box(T* ptr) { + asm volatile("" : : "r,m"(ptr) : "memory"); +} + +int main() { + std::this_thread::sleep_for(std::chrono::seconds(1)); + + auto emit_marker = []() { + uint8_t* ptr = new uint8_t[0xC0D59EED]; + black_box(ptr); + delete[] ptr; + }; + + emit_marker(); + + // array: + uint32_t* allocated = new uint32_t[11111]; + black_box(allocated); + delete[] allocated; + + // single element: + uint64_t* var = new uint64_t; + black_box(var); + delete var; + + // vector: + std::vector vec(22222, 0); + black_box(vec.data()); + + // aligned allocation (64-byte alignment for cache line): + uint8_t* aligned = static_cast(aligned_alloc(64, 64 * 512)); + black_box(aligned); + free(aligned); + + emit_marker(); +} diff --git a/crates/memtrack/testdata/alloc_rust/.gitignore b/crates/memtrack/testdata/alloc_rust/.gitignore new file mode 100644 index 00000000..2f7896d1 --- /dev/null +++ b/crates/memtrack/testdata/alloc_rust/.gitignore @@ -0,0 +1 @@ +target/ diff --git a/crates/memtrack/testdata/alloc_rust/Cargo.lock b/crates/memtrack/testdata/alloc_rust/Cargo.lock new file mode 100644 index 00000000..a221693f --- /dev/null +++ b/crates/memtrack/testdata/alloc_rust/Cargo.lock @@ -0,0 +1,78 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "alloc_rust" +version = "0.1.0" +dependencies = [ + "jemallocator", + "mimalloc", +] + +[[package]] +name = "cc" +version = "1.2.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755d2fce177175ffca841e9a06afdb2c4ab0f593d53b4dee48147dfaade85932" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8591b0bcc8a98a64310a2fae1bb3e9b8564dd10e381e6e28010fde8e8e8568db" + +[[package]] +name = "jemalloc-sys" +version = "0.5.4+5.3.0-patched" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "jemallocator" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0de374a9f8e63150e6f5e8a60cc14c668226d7a347d8aee1a45766e3c4dd3bc" +dependencies = [ + "jemalloc-sys", + "libc", +] + +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libmimalloc-sys" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "mimalloc" +version = "0.1.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8" +dependencies = [ + "libmimalloc-sys", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" diff --git a/crates/memtrack/testdata/alloc_rust/Cargo.toml b/crates/memtrack/testdata/alloc_rust/Cargo.toml new file mode 100644 index 00000000..e06594c5 --- /dev/null +++ b/crates/memtrack/testdata/alloc_rust/Cargo.toml @@ -0,0 +1,19 @@ +[workspace] + +[package] +name = "alloc_rust" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "alloc_rust" +path = "src/main.rs" + +[dependencies] +jemallocator = { version = "0.5", optional = true } +mimalloc = { version = "0.1", optional = true } + +[features] +default = [] +with-jemalloc = ["jemallocator"] +with-mimalloc = ["mimalloc"] diff --git a/crates/memtrack/testdata/alloc_rust/src/main.rs b/crates/memtrack/testdata/alloc_rust/src/main.rs new file mode 100644 index 00000000..42088656 --- /dev/null +++ b/crates/memtrack/testdata/alloc_rust/src/main.rs @@ -0,0 +1,73 @@ +use std::alloc::GlobalAlloc; + +#[cfg(feature = "with-mimalloc")] +#[global_allocator] +pub static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; + +#[cfg(feature = "with-jemalloc")] +#[global_allocator] +pub static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; + +#[cfg(not(any(feature = "with-mimalloc", feature = "with-jemalloc")))] +#[global_allocator] +static GLOBAL: std::alloc::System = std::alloc::System; + +fn main() -> Result<(), Box> { + std::thread::sleep(std::time::Duration::from_secs(1)); + + // All the functions exposed by the GlobalAlloc trait (https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html) + // IMPORTANT: We need the `black_box` to avoid LLVM from optimizing away our allocations + + let emit_marker = || unsafe { + let layout = std::alloc::Layout::array::(0xC0D59EED).unwrap(); + let ptr = GLOBAL.alloc(layout); + core::hint::black_box(ptr); + GLOBAL.dealloc(ptr, layout); + }; + + emit_marker(); + + // alloc (array) + unsafe { + let layout = std::alloc::Layout::array::(4321)?; + let ptr = GLOBAL.alloc(layout); + core::hint::black_box(ptr); + GLOBAL.dealloc(ptr, layout); + } + + // alloc zeroed (array) + unsafe { + let layout = std::alloc::Layout::array::(1234)?; + let ptr = GLOBAL.alloc_zeroed(layout); + core::hint::black_box(ptr); + GLOBAL.dealloc(ptr, layout); + } + + // alloc (single value) + unsafe { + let layout = std::alloc::Layout::new::(); + let ptr = GLOBAL.alloc(layout); + core::hint::black_box(ptr); + GLOBAL.dealloc(ptr, layout); + } + + // realloc (allocate new size, copy data, deallocate old) + unsafe { + let old_layout = std::alloc::Layout::array::(1111)?; + let old_ptr = GLOBAL.alloc(old_layout); + + // Write some data to the old allocation + std::ptr::write_bytes(old_ptr, 0x42, 1111); + core::hint::black_box(old_ptr); + + // Reallocate to a larger size + let new_ptr = GLOBAL.realloc(old_ptr, old_layout, 2222); + + core::hint::black_box(new_ptr); + GLOBAL.dealloc(new_ptr, std::alloc::Layout::array::(2222)?); + } + + emit_marker(); + + Ok(()) +} diff --git a/crates/memtrack/tests/c_tests.rs b/crates/memtrack/tests/c_tests.rs new file mode 100644 index 00000000..736e1a6a --- /dev/null +++ b/crates/memtrack/tests/c_tests.rs @@ -0,0 +1,98 @@ +#[macro_use] +mod shared; + +use rstest::rstest; +use std::fs; +use std::path::Path; +use std::process::Command; +use tempfile::TempDir; + +/// Compiles C source code and returns the binary path +fn compile_c_source( + source_code: &str, + name: &str, + output_dir: &Path, +) -> Result> { + let source_path = output_dir.join(format!("{name}.c")); + let binary_path = output_dir.join(name); + + fs::write(&source_path, source_code)?; + + let output = Command::new("gcc") + .args(["-o", binary_path.to_str().unwrap()]) + .arg(&source_path) + .output()?; + + if !output.status.success() { + eprintln!("gcc stderr: {}", String::from_utf8_lossy(&output.stderr)); + return Err("Failed to compile C fixture".into()); + } + + Ok(binary_path) +} + +struct AllocationTestCase { + name: &'static str, + source: &'static str, +} + +const ALLOCATION_TEST_CASES: &[AllocationTestCase] = &[ + AllocationTestCase { + name: "double_malloc", + source: include_str!("../testdata/double_malloc.c"), + }, + AllocationTestCase { + name: "malloc_free", + source: include_str!("../testdata/malloc_free.c"), + }, + AllocationTestCase { + name: "calloc_test", + source: include_str!("../testdata/calloc_test.c"), + }, + AllocationTestCase { + name: "realloc_test", + source: include_str!("../testdata/realloc_test.c"), + }, + AllocationTestCase { + name: "aligned_alloc_test", + source: include_str!("../testdata/aligned_alloc_test.c"), + }, + AllocationTestCase { + name: "many_allocs", + source: include_str!("../testdata/many_allocs.c"), + }, + AllocationTestCase { + name: "fork_test", + source: include_str!("../testdata/fork_test.c"), + }, + AllocationTestCase { + name: "alloc_size", + source: include_str!("../testdata/alloc_size.c"), + }, +]; + +#[test_with::env(GITHUB_ACTIONS)] +#[rstest] +#[case(&ALLOCATION_TEST_CASES[0])] +#[case(&ALLOCATION_TEST_CASES[1])] +#[case(&ALLOCATION_TEST_CASES[2])] +#[case(&ALLOCATION_TEST_CASES[3])] +#[case(&ALLOCATION_TEST_CASES[4])] +#[case(&ALLOCATION_TEST_CASES[5])] +#[case(&ALLOCATION_TEST_CASES[6])] +#[case(&ALLOCATION_TEST_CASES[7])] +#[test_log::test] +fn test_allocation_tracking( + #[case] test_case: &AllocationTestCase, +) -> Result<(), Box> { + let temp_dir = TempDir::new()?; + let binary = compile_c_source(test_case.source, test_case.name, temp_dir.path())?; + + let (events, thread_handle) = shared::track_binary(&binary)?; + + assert_events_snapshot!(test_case.name, events); + + thread_handle.join().unwrap(); + + Ok(()) +} diff --git a/crates/memtrack/tests/cpp_tests.rs b/crates/memtrack/tests/cpp_tests.rs new file mode 100644 index 00000000..584cd9ad --- /dev/null +++ b/crates/memtrack/tests/cpp_tests.rs @@ -0,0 +1,69 @@ +#[macro_use] +mod shared; + +use memtrack::AllocatorLib; +use rstest::rstest; +use std::path::Path; +use std::process::Command; + +fn compile_cpp_project(project_dir: &Path, target: &str) -> anyhow::Result { + let build_exists = project_dir.join("build").exists(); + if !build_exists { + // Configure with cmake -B build + let config = Command::new("cmake") + .current_dir(project_dir) + .args(["-B", "build", "-DCMAKE_BUILD_TYPE=Release"]) + .output()?; + + if !config.status.success() { + eprintln!( + "cmake configure failed: {}", + String::from_utf8_lossy(&config.stderr) + ); + return Err(anyhow::anyhow!("Failed to configure C++ project")); + } + } + + // Build specific target + let build = Command::new("cmake") + .current_dir(project_dir) + .args(["--build", "build", "--target", target, "-j"]) + .output()?; + + if !build.status.success() { + eprintln!( + "cmake build failed: {}", + String::from_utf8_lossy(&build.stderr) + ); + eprintln!("cmake stdout: {}", String::from_utf8_lossy(&build.stdout)); + return Err(anyhow::anyhow!("Failed to build target: {target}")); + } + + let binary_path = project_dir.join(format!("build/{target}")); + Ok(binary_path) +} + +#[test_with::env(GITHUB_ACTIONS)] +#[rstest] +#[case("alloc_cpp_system")] +#[case("alloc_cpp_jemalloc_static")] +#[case("alloc_cpp_jemalloc_dynamic")] +#[case("alloc_cpp_mimalloc_static")] +#[case("alloc_cpp_mimalloc_dynamic")] +#[test_log::test] +fn test_cpp_alloc_tracking(#[case] target: &str) -> Result<(), Box> { + let project_path = Path::new("testdata/alloc_cpp"); + let binary = compile_cpp_project(project_path, target)?; + + // Try to find a static allocator in the binary, then attach to it as well + // This is needed because the CWD is different, which breaks the heuristics. + let allocators = AllocatorLib::from_path_static(&binary) + .map(|a| vec![a]) + .unwrap_or_default(); + + let (events, thread_handle) = shared::track_binary_with_opts(&binary, &allocators)?; + assert_events_with_marker!(target, &events); + + thread_handle.join().unwrap(); + Ok(()) +} diff --git a/crates/memtrack/tests/integration_test.rs b/crates/memtrack/tests/integration_test.rs deleted file mode 100644 index 0070f67d..00000000 --- a/crates/memtrack/tests/integration_test.rs +++ /dev/null @@ -1,243 +0,0 @@ -use libbpf_rs::ErrorExt; -use memtrack::{EventType, MemtrackEventExt, Tracker}; -use rstest::rstest; -use runner_shared::artifacts::{MemtrackEvent as Event, MemtrackEventKind}; -use std::fs; -use std::path::Path; -use std::process::Command; -use std::time::Duration; -use tempfile::TempDir; - -pub fn track_binary(binary: &Path) -> anyhow::Result<(Vec, std::thread::JoinHandle<()>)> { - let mut tracker = Tracker::new()?; - - let child = Command::new(binary) - .spawn() - .context("Failed to spawn command")?; - let root_pid = child.id() as i32; - - tracker.enable()?; - let rx = tracker.track(root_pid)?; - - let mut events = Vec::new(); - while let Ok(event) = rx.recv_timeout(Duration::from_secs(10)) { - events.push(event); - } - - // Drop the tracker in a new thread to not block the test - let thread_handle = std::thread::spawn(move || core::mem::drop(tracker)); - - eprintln!("Tracked {} events", events.len()); - eprintln!("Events: {events:#?}"); - - Ok((events, thread_handle)) -} - -/// Compiles C source code and returns the binary path -fn compile_c_source( - source_code: &str, - name: &str, - output_dir: &Path, -) -> Result> { - let source_path = output_dir.join(format!("{name}.c")); - let binary_path = output_dir.join(name); - - fs::write(&source_path, source_code)?; - - let output = Command::new("gcc") - .args(["-o", binary_path.to_str().unwrap()]) - .arg(&source_path) - .output()?; - - if !output.status.success() { - eprintln!("gcc stderr: {}", String::from_utf8_lossy(&output.stderr)); - return Err("Failed to compile C fixture".into()); - } - - Ok(binary_path) -} - -/// Helper to count events of a specific type -fn count_events_by_type(events: &[Event], event_type: EventType) -> usize { - events - .iter() - .filter(|e| e.event_type() == event_type) - .count() -} - -// ============================================================================ -// PARAMETERIZED ALLOCATION TESTS -// ============================================================================ - -/// Test case definition for allocation tracking tests -struct AllocationTestCase { - name: &'static str, - source: &'static str, - assertions: &'static [(EventType, usize)], - allow_excess: bool, // Whether to allow >= instead of exact == for expected counts -} - -const ALLOCATION_TEST_CASES: &[AllocationTestCase] = &[ - AllocationTestCase { - name: "double_malloc", - source: include_str!("../testdata/double_malloc.c"), - assertions: &[(EventType::Malloc, 2)], - allow_excess: false, - }, - AllocationTestCase { - name: "malloc_free", - source: include_str!("../testdata/malloc_free.c"), - assertions: &[(EventType::Malloc, 1), (EventType::Free, 1)], - allow_excess: false, - }, - AllocationTestCase { - name: "calloc_test", - source: include_str!("../testdata/calloc_test.c"), - assertions: &[(EventType::Calloc, 1), (EventType::Free, 1)], - allow_excess: false, - }, - AllocationTestCase { - name: "realloc_test", - source: include_str!("../testdata/realloc_test.c"), - assertions: &[ - (EventType::Malloc, 1), - (EventType::Realloc, 1), - (EventType::Free, 1), - ], - allow_excess: false, - }, - AllocationTestCase { - name: "aligned_alloc_test", - source: include_str!("../testdata/aligned_alloc_test.c"), - assertions: &[(EventType::AlignedAlloc, 1), (EventType::Free, 1)], - allow_excess: false, - }, - AllocationTestCase { - name: "many_allocs", - source: include_str!("../testdata/many_allocs.c"), - assertions: &[(EventType::Malloc, 100), (EventType::Free, 100)], - allow_excess: true, // Allow >= because we allocate ptrs array + 100 allocations - }, -]; - -#[rstest] -#[case(&ALLOCATION_TEST_CASES[0])] -#[case(&ALLOCATION_TEST_CASES[1])] -#[case(&ALLOCATION_TEST_CASES[2])] -#[case(&ALLOCATION_TEST_CASES[3])] -#[case(&ALLOCATION_TEST_CASES[4])] -#[case(&ALLOCATION_TEST_CASES[5])] -#[test_log::test] -fn test_allocation_tracking( - #[case] test_case: &AllocationTestCase, -) -> Result<(), Box> { - let temp_dir = TempDir::new()?; - let binary = compile_c_source(test_case.source, test_case.name, temp_dir.path())?; - - let (events, thread_handle) = track_binary(&binary)?; - - for (event_type, expected_count) in test_case.assertions { - let actual_count = count_events_by_type(&events, *event_type); - - if test_case.allow_excess { - assert!( - actual_count >= *expected_count, - "Test '{}': Expected at least {} {:?} events, got {}", - test_case.name, - expected_count, - event_type, - actual_count - ); - } else { - assert_eq!( - actual_count, *expected_count, - "Test '{}': Expected {} {:?} events, got {}", - test_case.name, expected_count, event_type, actual_count - ); - } - } - - thread_handle.join().unwrap(); - - Ok(()) -} - -#[test] -fn test_fork_tracking() -> Result<(), Box> { - let temp_dir = TempDir::new()?; - let source = include_str!("../testdata/fork_test.c"); - let binary = compile_c_source(source, "fork_test", temp_dir.path())?; - - let (events, thread_handle) = track_binary(&binary)?; - - let malloc_count = count_events_by_type(&events, EventType::Malloc); - let free_count = count_events_by_type(&events, EventType::Free); - - // Should have at least 2 mallocs (parent + child) - assert!( - malloc_count >= 2, - "Expected at least 2 malloc events (parent + child), got {malloc_count}" - ); - - // Should have at least 2 frees (parent + child) - assert!( - free_count >= 2, - "Expected at least 2 free events (parent + child), got {free_count}" - ); - - // Verify we have events from different PIDs (parent and child) - let pids: std::collections::HashSet = events.iter().map(|e| e.pid as u32).collect(); - assert!( - !pids.is_empty(), - "Expected to track at least parent process" - ); - - thread_handle.join().unwrap(); - - Ok(()) -} - -#[test] -fn test_allocation_sizes() -> Result<(), Box> { - let temp_dir = TempDir::new()?; - let source = include_str!("../testdata/alloc_size.c"); - let binary = compile_c_source(source, "alloc_size", temp_dir.path())?; - - let (events, thread_handle) = track_binary(&binary)?; - - // Filter malloc events and collect their sizes - let malloc_events: Vec = events - .iter() - .filter_map(|e| match e.kind { - MemtrackEventKind::Malloc { size } => Some(size), - _ => None, - }) - .collect(); - - // Expected sizes from alloc_size.c: 1024, 2048, 512, 4096 - let expected_sizes = vec![1024u64, 2048, 512, 4096]; - - // Check that we have exactly 4 malloc events - assert_eq!( - malloc_events.len(), - 4, - "Expected 4 malloc events, got {}", - malloc_events.len() - ); - - // Check that all expected sizes are present - for expected_size in &expected_sizes { - assert!( - malloc_events.contains(expected_size), - "Expected allocation size {expected_size} not found in malloc events: {malloc_events:?}" - ); - } - - // Check that we have 4 free events - let free_count = count_events_by_type(&events, EventType::Free); - assert_eq!(free_count, 4, "Expected 4 free events, got {free_count}"); - - thread_handle.join().unwrap(); - - Ok(()) -} diff --git a/crates/memtrack/tests/rust_tests.rs b/crates/memtrack/tests/rust_tests.rs new file mode 100644 index 00000000..77b1804b --- /dev/null +++ b/crates/memtrack/tests/rust_tests.rs @@ -0,0 +1,57 @@ +#[macro_use] +mod shared; + +use memtrack::AllocatorLib; +use rstest::rstest; +use std::path::Path; +use std::process::Command; + +fn compile_rust_crate( + crate_dir: &Path, + name: &str, + features: &[&str], +) -> anyhow::Result { + let mut cmd = Command::new("cargo"); + cmd.current_dir(crate_dir) + .args(["build", "--release", "--bin", name]); + + if !features.is_empty() { + cmd.arg("--features").arg(features.join(",")); + } + + let output = cmd.output()?; + if !output.status.success() { + eprintln!("cargo stderr: {}", String::from_utf8_lossy(&output.stderr)); + eprintln!("cargo stdout: {}", String::from_utf8_lossy(&output.stdout)); + return Err(anyhow::anyhow!("Failed to compile Rust crate")); + } + + let binary_path = crate_dir.join(format!("target/release/{name}")); + Ok(binary_path) +} + +#[test_with::env(GITHUB_ACTIONS)] +#[rstest] +#[case("system", &[])] +#[case("jemalloc", &["with-jemalloc"])] +#[case("mimalloc", &["with-mimalloc"])] +#[test_log::test] +fn test_rust_alloc_tracking( + #[case] name: &str, + #[case] features: &[&str], +) -> Result<(), Box> { + let crate_path = Path::new("testdata/alloc_rust"); + let binary = compile_rust_crate(crate_path, "alloc_rust", features)?; + + // Try to find a static allocator in the binary, then attach to it as well + // This is needed because the CWD is different, which breaks the heuristics. + let allocators = AllocatorLib::from_path_static(&binary) + .map(|a| vec![a]) + .unwrap_or_default(); + + let (events, thread_handle) = shared::track_binary_with_opts(&binary, &allocators)?; + assert_events_with_marker!(name, &events); + + thread_handle.join().unwrap(); + Ok(()) +} diff --git a/crates/memtrack/tests/shared.rs b/crates/memtrack/tests/shared.rs new file mode 100644 index 00000000..8966b901 --- /dev/null +++ b/crates/memtrack/tests/shared.rs @@ -0,0 +1,133 @@ +#![allow(dead_code, unused)] + +use anyhow::Context; +use memtrack::prelude::*; +use memtrack::{AllocatorLib, Tracker}; +use runner_shared::artifacts::{MemtrackEvent as Event, MemtrackEventKind}; +use std::path::Path; +use std::process::Command; +use std::time::Duration; + +type TrackResult = anyhow::Result<(Vec, std::thread::JoinHandle<()>)>; + +/// Asserts memory events using snapshot testing without marker filtering. +/// +/// Formats and snapshots all memory events for regression testing. +/// Deduplicates by address and type to remove duplicate tracking. +/// +/// # Example +/// ```no_run +/// let (events, _handle) = track_binary(&binary)?; +/// assert_events_snapshot!("test_name", events); +/// ``` +macro_rules! assert_events_snapshot { + ($name:expr, $events:expr) => {{ + use itertools::Itertools; + use runner_shared::artifacts::MemtrackEventKind; + use std::mem::discriminant; + + // Dedup events by address and type to remove duplicates + let events = $events + .iter() + .sorted_by_key(|e| e.timestamp) + .dedup_by(|a, b| a.addr == b.addr && discriminant(&a.kind) == discriminant(&b.kind)) + .collect::>(); + + let formatted_events: Vec = events + .iter() + .map(|e| match e.kind { + // Exclude address in snapshots: + MemtrackEventKind::Realloc { size, .. } => format!("Realloc {{ size: {} }}", size), + _ => format!("{:?}", e.kind), + }) + .collect(); + insta::assert_debug_snapshot!($name, formatted_events); + }}; +} + +/// Asserts events, filtered by a 0xC0D59EED allocation marker to +/// exclude noise. +/// +/// Processes events by: +/// 1. Deduplicating by address and event type +/// 2. Filtering to events between 0xC0D59EED marker allocations +/// 3. Creating an insta snapshot for regression testing +/// +/// Binary must alloc `0xC0D59EED` memory before and after. +/// +/// # Example +/// +/// Do the following in the test: +/// ```no_run +/// malloc(0xC0D59EED) +/// // do you allocations/frees here +/// malloc(0xC0D59EED) +/// ``` +/// +/// Then in Rust: +/// ```no_run +/// let (events, _handle) = track_binary(&binary)?; +/// assert_events_with_marker!("test_name", events); +/// ``` +macro_rules! assert_events_with_marker { + ($name:expr, $events:expr) => {{ + use itertools::Itertools; + use runner_shared::artifacts::MemtrackEventKind; + use std::mem::discriminant; + + // Remove events outside our 0xC0D59EED marker allocations + let filtered_events = $events + .iter() + .sorted_by_key(|e| e.timestamp) + .dedup_by(|a, b| a.addr == b.addr && discriminant(&a.kind) == discriminant(&b.kind)) + .skip_while(|e| { + let MemtrackEventKind::Malloc { size } = e.kind else { + return true; + }; + size != 0xC0D59EED + }) + .skip(2) // Skip the marker allocation and free + .take_while(|e| { + let MemtrackEventKind::Malloc { size } = e.kind else { + return true; + }; + size != 0xC0D59EED + }) + .cloned() + .collect::>(); + + assert_events_snapshot!($name, filtered_events); + }}; +} + +pub fn track_binary_with_opts(binary: &Path, extra_allocators: &[AllocatorLib]) -> TrackResult { + // IMPORTANT: Always initialize the tracker BEFORE spawning the binary, as it can take some time to + // attach to all the allocator libraries (especially when using NixOS). + let mut tracker = memtrack::Tracker::new()?; + tracker.attach_allocators(extra_allocators)?; + + let child = Command::new(binary) + .spawn() + .context("Failed to spawn command")?; + let root_pid = child.id() as i32; + + tracker.enable()?; + let rx = tracker.track(root_pid)?; + + let mut events = Vec::new(); + while let Ok(event) = rx.recv_timeout(Duration::from_secs(10)) { + events.push(event); + } + + // Drop the tracker in a new thread to not block the test + let thread_handle = std::thread::spawn(move || core::mem::drop(tracker)); + + info!("Tracked {} events", events.len()); + trace!("Events: {events:#?}"); + + Ok((events, thread_handle)) +} + +pub fn track_binary(binary: &Path) -> TrackResult { + track_binary_with_opts(binary, &[]) +} diff --git a/crates/memtrack/tests/snapshots/c_tests__aligned_alloc_test.snap b/crates/memtrack/tests/snapshots/c_tests__aligned_alloc_test.snap new file mode 100644 index 00000000..60cb0cdb --- /dev/null +++ b/crates/memtrack/tests/snapshots/c_tests__aligned_alloc_test.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91d4e06649982eb834c3ffbce75470324fae786469874aedfadd5f87cda37ee0 +size 128 diff --git a/crates/memtrack/tests/snapshots/c_tests__alloc_size.snap b/crates/memtrack/tests/snapshots/c_tests__alloc_size.snap new file mode 100644 index 00000000..001da5c1 --- /dev/null +++ b/crates/memtrack/tests/snapshots/c_tests__alloc_size.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fdbeda51b2bf51acc75eabe785bd572338865a0def4f4d947959725ff3a16186 +size 245 diff --git a/crates/memtrack/tests/snapshots/c_tests__calloc_test.snap b/crates/memtrack/tests/snapshots/c_tests__calloc_test.snap new file mode 100644 index 00000000..fcec9bbd --- /dev/null +++ b/crates/memtrack/tests/snapshots/c_tests__calloc_test.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c84a78877fad79cfe6ff6e53063e83621161e2434bc22b7dc1c831b517285ae3 +size 123 diff --git a/crates/memtrack/tests/snapshots/c_tests__double_malloc.snap b/crates/memtrack/tests/snapshots/c_tests__double_malloc.snap new file mode 100644 index 00000000..6b99d589 --- /dev/null +++ b/crates/memtrack/tests/snapshots/c_tests__double_malloc.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c012736a07a2647759a9e2b911bdad5fa0235e67caff5b3193c52152b7e518d +size 164 diff --git a/crates/memtrack/tests/snapshots/c_tests__fork_test.snap b/crates/memtrack/tests/snapshots/c_tests__fork_test.snap new file mode 100644 index 00000000..23fb0a59 --- /dev/null +++ b/crates/memtrack/tests/snapshots/c_tests__fork_test.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:599ea5a09b03e13113442e9b1c72ddf6e082183f919a96bb805a431baf0513aa +size 162 diff --git a/crates/memtrack/tests/snapshots/c_tests__malloc_free.snap b/crates/memtrack/tests/snapshots/c_tests__malloc_free.snap new file mode 100644 index 00000000..5516cf76 --- /dev/null +++ b/crates/memtrack/tests/snapshots/c_tests__malloc_free.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2aafb88fb293f951f50b9184e65a45f04646741458a01f585863031d777df89c +size 122 diff --git a/crates/memtrack/tests/snapshots/c_tests__many_allocs.snap b/crates/memtrack/tests/snapshots/c_tests__many_allocs.snap new file mode 100644 index 00000000..4473c29f --- /dev/null +++ b/crates/memtrack/tests/snapshots/c_tests__many_allocs.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d41fae845b051524bdca4b6f0c6bb866b608920fa6d2f19297dc32b611f4f12f +size 4022 diff --git a/crates/memtrack/tests/snapshots/c_tests__realloc_test.snap b/crates/memtrack/tests/snapshots/c_tests__realloc_test.snap new file mode 100644 index 00000000..a26f4645 --- /dev/null +++ b/crates/memtrack/tests/snapshots/c_tests__realloc_test.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c305ebbb7ecbecf845deb341eb0644d714d4ffbf3bbd410c186b12c55536754f +size 151 diff --git a/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_jemalloc_dynamic.snap b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_jemalloc_dynamic.snap new file mode 100644 index 00000000..e62abd59 --- /dev/null +++ b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_jemalloc_dynamic.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab312b18f1d5e64ceffbfd383551461c22599d5cd6842315c23b1370cb143803 +size 242 diff --git a/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_jemalloc_static.snap b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_jemalloc_static.snap new file mode 100644 index 00000000..e62abd59 --- /dev/null +++ b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_jemalloc_static.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab312b18f1d5e64ceffbfd383551461c22599d5cd6842315c23b1370cb143803 +size 242 diff --git a/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_mimalloc_dynamic.snap b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_mimalloc_dynamic.snap new file mode 100644 index 00000000..78e96fe8 --- /dev/null +++ b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_mimalloc_dynamic.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33702691dfd6e60d557946e8ec2b4d429d72c922a5063545cdca267e3e94ff09 +size 272 diff --git a/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_mimalloc_static.snap b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_mimalloc_static.snap new file mode 100644 index 00000000..78e96fe8 --- /dev/null +++ b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_mimalloc_static.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33702691dfd6e60d557946e8ec2b4d429d72c922a5063545cdca267e3e94ff09 +size 272 diff --git a/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_system.snap b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_system.snap new file mode 100644 index 00000000..e62abd59 --- /dev/null +++ b/crates/memtrack/tests/snapshots/cpp_tests__alloc_cpp_system.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab312b18f1d5e64ceffbfd383551461c22599d5cd6842315c23b1370cb143803 +size 242 diff --git a/crates/memtrack/tests/snapshots/rust_tests__jemalloc.snap b/crates/memtrack/tests/snapshots/rust_tests__jemalloc.snap new file mode 100644 index 00000000..6f15ed4c --- /dev/null +++ b/crates/memtrack/tests/snapshots/rust_tests__jemalloc.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8dec7a68b94d727fd81b84f2b38d9143b91c3027b80ccff2e20fc410f4190add +size 276 diff --git a/crates/memtrack/tests/snapshots/rust_tests__mimalloc.snap b/crates/memtrack/tests/snapshots/rust_tests__mimalloc.snap new file mode 100644 index 00000000..2a8f3fc1 --- /dev/null +++ b/crates/memtrack/tests/snapshots/rust_tests__mimalloc.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e83ce9d4112900c05b3081b7d063cb3af53cdfdc5051f4471cde2ed94b980d9 +size 288 diff --git a/crates/memtrack/tests/snapshots/rust_tests__system.snap b/crates/memtrack/tests/snapshots/rust_tests__system.snap new file mode 100644 index 00000000..6f15ed4c --- /dev/null +++ b/crates/memtrack/tests/snapshots/rust_tests__system.snap @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8dec7a68b94d727fd81b84f2b38d9143b91c3027b80ccff2e20fc410f4190add +size 276