From 70e1d79dfdbcaf6d71c8825afbb16e355a375208 Mon Sep 17 00:00:00 2001 From: houseme Date: Thu, 2 Jul 2026 19:03:38 +0800 Subject: [PATCH] feat: replace jemalloc with mimalloc (#4174) * feat: replace jemalloc with mimalloc * docs: record allocator rounds5 retest * fix(replication): satisfy clippy unwrap lints * docs: keep allocator migration plan local only * feat(profiling): rely on pyroscope cpu profiling * refactor(profiling): centralize unsupported pprof responses * chore(deps): update s3s revision --- Cargo.lock | 552 +++------------- Cargo.toml | 16 +- crates/config/src/constants/profiler.rs | 9 +- crates/extension-schema/src/lib.rs | 38 +- crates/obs/Cargo.toml | 3 +- crates/obs/src/telemetry/guard.rs | 30 - crates/obs/src/telemetry/local.rs | 2 - crates/obs/src/telemetry/otel.rs | 63 +- crates/targets/src/catalog/extension.rs | 8 +- crates/targets/src/runtime/ops_profiler.rs | 4 +- rustfs/Cargo.toml | 13 +- rustfs/src/admin/handlers/profile.rs | 55 +- rustfs/src/admin/handlers/profile_admin.rs | 190 +----- rustfs/src/allocator_reclaim.rs | 35 +- rustfs/src/main.rs | 5 - rustfs/src/profiling.rs | 711 +++------------------ rustfs/src/runtime_capabilities.rs | 12 +- 17 files changed, 217 insertions(+), 1529 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ee45f5cea..49bd58601 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1659,12 +1659,6 @@ version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" -[[package]] -name = "bytemuck" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" - [[package]] name = "byteorder" version = "1.5.0" @@ -2172,15 +2166,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "cpp_demangle" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2bb79cb74d735044c972aae58ed0aaa9a837e85b01106a54c39e42e97f62253" -dependencies = [ - "cfg-if", -] - [[package]] name = "cpubits" version = "0.1.1" @@ -3619,7 +3604,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3901,26 +3886,6 @@ dependencies = [ "syn 2.0.118", ] -[[package]] -name = "env_filter" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900d271a03799a1ee8d1ca9b19893b48ca674a9284fefcfb85f05e74ed314217" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.11.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de671bd27a75a797dc9ae289ba1e77276e75e2026408aab65185384e2d5cd3f6" -dependencies = [ - "env_filter", - "log", -] - [[package]] name = "equator" version = "0.4.2" @@ -3963,7 +3928,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -4901,15 +4866,6 @@ dependencies = [ "digest 0.11.3", ] -[[package]] -name = "home" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "hostname" version = "0.4.2" @@ -5246,46 +5202,6 @@ dependencies = [ "serde_core", ] -[[package]] -name = "inferno" -version = "0.11.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" -dependencies = [ - "ahash", - "indexmap 2.14.0", - "is-terminal", - "itoa", - "log", - "num-format", - "once_cell", - "quick-xml 0.26.0", - "rgb", - "str_stack", -] - -[[package]] -name = "inferno" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90807d610575744524d9bdc69f3885d96f0e6c3354565b0828354a7ff2a262b8" -dependencies = [ - "ahash", - "clap", - "crossbeam-channel", - "crossbeam-utils", - "dashmap", - "env_logger", - "indexmap 2.14.0", - "itoa", - "log", - "num-format", - "once_cell", - "quick-xml 0.39.4", - "rgb", - "str_stack", -] - [[package]] name = "inout" version = "0.1.4" @@ -5373,17 +5289,6 @@ dependencies = [ "serde", ] -[[package]] -name = "is-terminal" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.61.2", -] - [[package]] name = "is_debug" version = "1.1.0" @@ -5429,23 +5334,6 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" -[[package]] -name = "jemalloc_pprof" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d44c349cfe2654897fadcb9de4f0bfbf48288ec344f700b2bd59f152dd209" -dependencies = [ - "anyhow", - "libc", - "mappings", - "once_cell", - "pprof_util", - "tempfile", - "tikv-jemalloc-ctl", - "tokio", - "tracing", -] - [[package]] name = "jiff" version = "0.2.31" @@ -6022,19 +5910,6 @@ dependencies = [ "quote", ] -[[package]] -name = "mappings" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bab1e61a4b76757edb59cd81fcaa7f3ba9018d43b527d9abfad877b4c6c60f2" -dependencies = [ - "anyhow", - "libc", - "once_cell", - "pprof_util", - "tracing", -] - [[package]] name = "matchers" version = "0.2.0" @@ -6088,15 +5963,6 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" -[[package]] -name = "memmap2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" -dependencies = [ - "libc", -] - [[package]] name = "memmap2" version = "0.9.11" @@ -6615,7 +6481,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -6627,20 +6493,6 @@ dependencies = [ "rand 0.8.6", ] -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -6683,16 +6535,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" -[[package]] -name = "num-format" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" -dependencies = [ - "arrayvec", - "itoa", -] - [[package]] name = "num-integer" version = "0.1.46" @@ -6713,17 +6555,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -6788,7 +6619,7 @@ version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" dependencies = [ - "base64 0.22.1", + "base64 0.21.7", "chrono", "getrandom 0.2.17", "http 1.4.2", @@ -6867,16 +6698,6 @@ dependencies = [ "objc2-core-foundation", ] -[[package]] -name = "object" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" -dependencies = [ - "flate2", - "memchr", -] - [[package]] name = "object" version = "0.37.3" @@ -6886,6 +6707,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "object" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" +dependencies = [ + "flate2", + "memchr", + "ruzstd", +] + [[package]] name = "object_store" version = "0.13.2" @@ -7734,48 +7566,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24" -[[package]] -name = "pprof-pyroscope-fork" -version = "0.1500.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228f0967ab4c785d2daa6afdecd844a85a08227b19bfe14f1cf3f7819bf42f43" -dependencies = [ - "aligned-vec", - "backtrace", - "cfg-if", - "findshlibs", - "framehop", - "inferno 0.11.21", - "libc", - "log", - "memmap2 0.5.10", - "nix 0.26.4", - "object 0.29.0", - "once_cell", - "protobuf", - "protobuf-codegen", - "smallvec", - "spin 0.10.0", - "symbolic-demangle", - "tempfile", - "thiserror 2.0.18", -] - -[[package]] -name = "pprof_util" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea0cc524de808a6d98d192a3d99fe95617031ad4a52ec0a0f987ef4432e8fe1" -dependencies = [ - "anyhow", - "backtrace", - "flate2", - "inferno 0.12.6", - "num", - "paste", - "prost 0.14.4", -] - [[package]] name = "ppv-lite86" version = "0.2.21" @@ -7959,7 +7749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" dependencies = [ "heck", - "itertools 0.14.0", + "itertools 0.10.5", "log", "multimap", "once_cell", @@ -7979,7 +7769,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03da047801ff44bb6a4d407d4860c05fd70bb81714e6b2f3812603d5b145b042" dependencies = [ "heck", - "itertools 0.14.0", + "itertools 0.10.5", "log", "multimap", "petgraph 0.8.3", @@ -8000,7 +7790,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.118", @@ -8013,7 +7803,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", - "itertools 0.14.0", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.118", @@ -8037,57 +7827,6 @@ dependencies = [ "prost 0.14.4", ] -[[package]] -name = "protobuf" -version = "3.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" -dependencies = [ - "once_cell", - "protobuf-support", - "thiserror 1.0.69", -] - -[[package]] -name = "protobuf-codegen" -version = "3.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3976825c0014bbd2f3b34f0001876604fe87e0c86cd8fa54251530f1544ace" -dependencies = [ - "anyhow", - "once_cell", - "protobuf", - "protobuf-parse", - "regex", - "tempfile", - "thiserror 1.0.69", -] - -[[package]] -name = "protobuf-parse" -version = "3.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973" -dependencies = [ - "anyhow", - "indexmap 2.14.0", - "log", - "protobuf", - "protobuf-support", - "tempfile", - "thiserror 1.0.69", - "which", -] - -[[package]] -name = "protobuf-support" -version = "3.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" -dependencies = [ - "thiserror 1.0.69", -] - [[package]] name = "proxy-protocol" version = "0.5.0" @@ -8161,19 +7900,29 @@ dependencies = [ [[package]] name = "pyroscope" -version = "2.0.5" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ebd83db08e07a69b4291e7043f5f2ca964c8869e0bd0b6d09b8b062de775bd" +checksum = "b6869e0f2cae5cef10281c57d5a260f91372d7f190a7f717eb1a54f643d92151" dependencies = [ - "jemalloc_pprof", + "aligned-vec", + "backtrace", + "findshlibs", + "framehop", "lazy_static", "libc", "libflate", "log", - "pprof-pyroscope-fork", + "memmap2", + "nix 0.26.4", + "object 0.39.1", + "once_cell", "prost 0.14.4", "reqwest", "serde_json", + "smallvec", + "spin 0.12.1", + "symbolic-demangle", + "tempfile", "thiserror 2.0.18", "url", "uuid", @@ -8206,34 +7955,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-xml" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" -dependencies = [ - "memchr", -] - -[[package]] -name = "quick-xml" -version = "0.39.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" -dependencies = [ - "memchr", -] - -[[package]] -name = "quick-xml" -version = "0.40.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2474bd2e5029e7ccb6abb2ba48cf2383a333851dedf495901544281590c7da7f" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "quick-xml" version = "0.41.0" @@ -8299,7 +8020,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -8728,15 +8449,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "rgb" -version = "0.8.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" -dependencies = [ - "bytemuck", -] - [[package]] name = "ring" version = "0.17.14" @@ -9076,7 +8788,6 @@ dependencies = [ "hyper", "hyper-util", "insta", - "jemalloc_pprof", "jiff", "libc", "libmimalloc-sys", @@ -9090,7 +8801,6 @@ dependencies = [ "opentelemetry_sdk", "percent-encoding", "pin-project-lite", - "pprof-pyroscope-fork", "proptest", "rand 0.10.1", "rcgen", @@ -9153,8 +8863,6 @@ dependencies = [ "temp-env", "tempfile", "thiserror 2.0.18", - "tikv-jemalloc-ctl", - "tikv-jemallocator", "time", "tokio", "tokio-rustls", @@ -9333,7 +9041,7 @@ dependencies = [ "lazy_static", "libc", "md-5 0.11.0", - "memmap2 0.9.11", + "memmap2", "metrics", "moka", "num_cpus", @@ -9343,7 +9051,7 @@ dependencies = [ "path-absolutize", "pin-project-lite", "proptest", - "quick-xml 0.41.0", + "quick-xml", "rand 0.10.1", "ratelimit", "rcgen", @@ -9524,7 +9232,7 @@ name = "rustfs-io-core" version = "1.0.0-beta.8" dependencies = [ "bytes", - "memmap2 0.9.11", + "memmap2", "rustfs-io-metrics", "thiserror 2.0.18", "tokio", @@ -9696,7 +9404,7 @@ dependencies = [ "hashbrown 0.17.1", "metrics", "percent-encoding", - "quick-xml 0.41.0", + "quick-xml", "rayon", "rustc-hash", "rustfs-config", @@ -9745,7 +9453,6 @@ dependencies = [ "dial9-tokio-telemetry", "flate2", "glob", - "jemalloc_pprof", "jiff", "metrics", "num_cpus", @@ -9835,7 +9542,7 @@ dependencies = [ "md5", "percent-encoding", "proptest", - "quick-xml 0.41.0", + "quick-xml", "regex", "russh", "russh-sftp", @@ -10303,7 +10010,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -10377,7 +10084,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -10416,6 +10123,15 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "ruzstd" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c1c839d570d835527c9a5e4db7cb2198683a988cb9d7293fc8674e6bd58fc8" +dependencies = [ + "twox-hash", +] + [[package]] name = "ryu" version = "1.0.23" @@ -10425,7 +10141,7 @@ checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "s3s" version = "0.14.0" -source = "git+https://github.com/rustfs/s3s?rev=300c657fb895b87e01e30b06e41a7996e57191f9#300c657fb895b87e01e30b06e41a7996e57191f9" +source = "git+https://github.com/rustfs/s3s?rev=921e9c5911951ae9efb91dc237e11be9f2f10a14#921e9c5911951ae9efb91dc237e11be9f2f10a14" dependencies = [ "arc-swap", "arrayvec", @@ -10452,7 +10168,7 @@ dependencies = [ "nom 8.0.0", "numeric_cast", "pin-project-lite", - "quick-xml 0.40.1", + "quick-xml", "serde", "serde_json", "serde_urlencoded", @@ -11142,15 +10858,15 @@ name = "spin" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" -dependencies = [ - "lock_api", -] [[package]] name = "spin" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd5231412d905519dca6a5deb0327d407be68d6c941feec004533401d3a0a715" +dependencies = [ + "lock_api", +] [[package]] name = "spki" @@ -11316,12 +11032,6 @@ dependencies = [ "regex", ] -[[package]] -name = "str_stack" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f446288b699d66d0fd2e30d1cfe7869194312524b3b9252594868ed26ef056a" - [[package]] name = "stringprep" version = "0.1.5" @@ -11404,23 +11114,22 @@ dependencies = [ [[package]] name = "symbolic-common" -version = "12.18.3" +version = "13.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332615d90111d8eeaf86a84dc9bbe9f65d0d8c5cf11b4caccedc37754eb0dcfd" +checksum = "82bfbe03a011e4d3e2a94f03e054c33b101767f6905ec2a608dd85aa0cd71c97" dependencies = [ "debugid", - "memmap2 0.9.11", + "memmap2", "stable_deref_trait", "uuid", ] [[package]] name = "symbolic-demangle" -version = "12.18.3" +version = "13.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912017718eb4d21930546245af9a3475c9dccf15675a5c215664e76621afc471" +checksum = "f9d2ed98c0fd513dd4788554e1a62d9736f3642f8aaf7923644ec647158c1ded" dependencies = [ - "cpp_demangle", "rustc-demangle", "symbolic-common", ] @@ -11561,7 +11270,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -11655,37 +11364,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "tikv-jemalloc-ctl" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "661f1f6a57b3a36dc9174a2c10f19513b4866816e13425d3e418b11cc37bc24c" -dependencies = [ - "libc", - "paste", - "tikv-jemalloc-sys", -] - -[[package]] -name = "tikv-jemalloc-sys" -version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "tikv-jemallocator" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0359b4327f954e0567e69fb191cf1436617748813819c94b8cd4a431422d053a" -dependencies = [ - "libc", - "tikv-jemalloc-sys", -] - [[package]] name = "time" version = "0.3.53" @@ -12625,18 +12303,6 @@ dependencies = [ "rustls-pki-types", ] -[[package]] -name = "which" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" -dependencies = [ - "either", - "home", - "once_cell", - "rustix 0.38.44", -] - [[package]] name = "whoami" version = "2.1.2" @@ -12687,7 +12353,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -12814,7 +12480,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -12823,16 +12489,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -12850,31 +12507,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] [[package]] @@ -12892,96 +12532,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "1.0.3" diff --git a/Cargo.toml b/Cargo.toml index 00bcc8d1a..e3de0dd8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -279,7 +279,7 @@ redis = { version = "1.3.0", features = ["connection-manager", "tokio-rustls-com rustix = { version = "1.1.4", features = ["fs"] } rust-embed = { version = "8.11.0" } rustc-hash = { version = "2.1.2" } -s3s = { git = "https://github.com/rustfs/s3s", rev = "300c657fb895b87e01e30b06e41a7996e57191f9", features = ["minio"] } +s3s = { git = "https://github.com/rustfs/s3s", rev = "921e9c5911951ae9efb91dc237e11be9f2f10a14", features = ["minio"] } serial_test = "3.5.0" shadow-rs = { version = "2.0.0", default-features = false } siphasher = "1.0.3" @@ -320,7 +320,7 @@ opentelemetry-otlp = { version = "0.32.0", features = ["gzip-http", "reqwest-rus opentelemetry_sdk = { version = "0.32.1", features = ["rt-tokio"] } opentelemetry-semantic-conventions = { version = "0.32.1", features = ["semconv_experimental"] } opentelemetry-stdout = { version = "0.32.0" } -pyroscope = { version = "=2.0.5", features = ["backend-pprof-rs", "backend-jemalloc"] } +pyroscope = { version = "2.0.6", features = ["backend-pprof-rs"] } # FTP and SFTP libunftp = { version = "0.23.0", features = ["experimental"] } @@ -335,20 +335,8 @@ dav-server = "0.11.0" # Performance Analysis and Memory Profiling mimalloc = "0.1" -# Use tikv-jemallocator as memory allocator and enable performance analysis -tikv-jemallocator = { version = "0.6", features = ["profiling", "stats", "unprefixed_malloc_on_supported_platforms", "background_threads"] } -# Used to control and obtain statistics for jemalloc at runtime -tikv-jemalloc-ctl = { version = "0.6", features = ["use_std", "stats", "profiling"] } # Snapshot testing for output format regression detection insta = { version = "1.48", features = ["yaml", "json"] } -# Used to generate pprof-compatible memory profiling data and support symbolization and flame graphs -jemalloc_pprof = { version = "0.8.2", features = ["symbolize", "flamegraph"] } -# Used to generate CPU performance analysis data and flame diagrams -# pprof = { version = "0.15.0", features = ["flamegraph", "protobuf-codec"] } -# Use pprof-pyroscope-fork to match pyroscope 2.0.5's internal pprof dependency (v0.1500.4). -# Cargo unifies them into a single instance, avoiding duplicate perf_signal_handler symbols. -# NOTE: Do NOT upgrade pyroscope to >=2.0.6 — it vendors pprof-rs internally, causing symbol collision. -pprof = { package = "pprof-pyroscope-fork", version = "0.1500.4", features = ["flamegraph", "protobuf-codec"] } [workspace.metadata.cargo-shear] ignored = ["rustfs"] diff --git a/crates/config/src/constants/profiler.rs b/crates/config/src/constants/profiler.rs index 282b5b5c8..df5b2ed4e 100644 --- a/crates/config/src/constants/profiler.rs +++ b/crates/config/src/constants/profiler.rs @@ -15,7 +15,8 @@ /// Profiler related environment variable names and default values pub const ENV_ENABLE_PROFILING: &str = "RUSTFS_ENABLE_PROFILING"; -// CPU profiling +// Legacy local CPU pprof controls. Local pprof dumps are currently unsupported; +// Pyroscope export is configured through observability settings. pub const ENV_CPU_MODE: &str = "RUSTFS_PROF_CPU_MODE"; // off|continuous|periodic /// Frequency of CPU profiling samples pub const ENV_CPU_FREQ: &str = "RUSTFS_PROF_CPU_FREQ"; @@ -24,7 +25,7 @@ pub const ENV_CPU_INTERVAL_SECS: &str = "RUSTFS_PROF_CPU_INTERVAL_SECS"; /// Duration of each CPU profiling session (for periodic mode) pub const ENV_CPU_DURATION_SECS: &str = "RUSTFS_PROF_CPU_DURATION_SECS"; -/// Memory profiling (jemalloc) +/// Periodic memory pprof dumps (currently unsupported with mimalloc) pub const ENV_MEM_PERIODIC: &str = "RUSTFS_PROF_MEM_PERIODIC"; /// Interval between memory profiling snapshots (for periodic mode) pub const ENV_MEM_INTERVAL_SECS: &str = "RUSTFS_PROF_MEM_INTERVAL_SECS"; @@ -34,7 +35,7 @@ pub const ENV_OUTPUT_DIR: &str = "RUSTFS_PROF_OUTPUT_DIR"; /// Defaults for profiler settings pub const DEFAULT_ENABLE_PROFILING: bool = false; -/// CPU profiling +/// Legacy local CPU pprof mode pub const DEFAULT_CPU_MODE: &str = "off"; /// Frequency of CPU profiling samples pub const DEFAULT_CPU_FREQ: usize = 100; @@ -42,7 +43,7 @@ pub const DEFAULT_CPU_FREQ: usize = 100; pub const DEFAULT_CPU_INTERVAL_SECS: u64 = 300; /// Duration of each CPU profiling session (for periodic mode) pub const DEFAULT_CPU_DURATION_SECS: u64 = 60; -/// Memory profiling (jemalloc) +/// Periodic memory pprof dumps (currently unsupported with mimalloc) pub const DEFAULT_MEM_PERIODIC: bool = false; /// Interval between memory profiling snapshots (for periodic mode) pub const DEFAULT_MEM_INTERVAL_SECS: u64 = 300; diff --git a/crates/extension-schema/src/lib.rs b/crates/extension-schema/src/lib.rs index 66465db6e..74ec6e0e9 100644 --- a/crates/extension-schema/src/lib.rs +++ b/crates/extension-schema/src/lib.rs @@ -782,7 +782,7 @@ mod tests { let contract = OpsProfilerContract { mode: OpsProfilerContractMode::CapabilityDescription, backends: vec![ - profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Enabled, true), + profiler_backend("pyroscope", OpsProfilerBackendStatus::Enabled, true), profiler_backend("memory_pprof", OpsProfilerBackendStatus::Disabled, false), profiler_backend("ebpf", OpsProfilerBackendStatus::Unsupported, false), profiler_backend("future_kernel_profiler", OpsProfilerBackendStatus::Unknown, false), @@ -803,7 +803,7 @@ mod tests { fn ops_profiler_schema_serializes_stable_json_shape() { let contract = OpsProfilerContract { mode: OpsProfilerContractMode::CapabilityDescription, - backends: vec![profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Enabled, true)], + backends: vec![profiler_backend("pyroscope", OpsProfilerBackendStatus::Enabled, true)], }; let value = serde_json::to_value(contract).expect("ops profiler schema should serialize"); @@ -813,7 +813,7 @@ mod tests { json!({ "mode": "capability_description", "backends": [{ - "backend": "cpu_pprof", + "backend": "pyroscope", "status": "enabled", "supports_profile_export": true, "redaction_required": ["secret", "token", "local_path", "host"], @@ -860,7 +860,7 @@ mod tests { contract: OpsProfilerContract { mode: OpsProfilerContractMode::CapabilityDescription, backends: vec![ - profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Enabled, true), + profiler_backend("pyroscope", OpsProfilerBackendStatus::Enabled, true), profiler_backend("memory_pprof", OpsProfilerBackendStatus::Disabled, false), profiler_backend("ebpf", OpsProfilerBackendStatus::Unsupported, false), ], @@ -898,7 +898,7 @@ mod tests { }, contract: OpsProfilerContract { mode: OpsProfilerContractMode::CapabilityDescription, - backends: vec![profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Enabled, true)], + backends: vec![profiler_backend("pyroscope", OpsProfilerBackendStatus::Enabled, true)], }, }; @@ -916,7 +916,7 @@ mod tests { "contract": { "mode": "capability_description", "backends": [{ - "backend": "cpu_pprof", + "backend": "pyroscope", "status": "enabled", "supports_profile_export": true, "redaction_required": ["secret", "token", "local_path", "host"], @@ -942,7 +942,7 @@ mod tests { }, contract: OpsProfilerContract { mode: OpsProfilerContractMode::CapabilityDescription, - backends: vec![profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Enabled, true)], + backends: vec![profiler_backend("pyroscope", OpsProfilerBackendStatus::Enabled, true)], }, }; @@ -976,7 +976,7 @@ mod tests { fn rejects_ops_profiler_execution_requests_and_empty_backends() { let mut contract = OpsProfilerContract { mode: OpsProfilerContractMode::ExecutionRequest, - backends: vec![profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Enabled, true)], + backends: vec![profiler_backend("pyroscope", OpsProfilerBackendStatus::Enabled, true)], }; assert_eq!( @@ -998,7 +998,7 @@ mod tests { let err = serde_json::from_value::(json!({ "mode": "capability_description", "backends": [{ - "backend": "cpu_pprof", + "backend": "pyroscope", "status": "enabled", "supports_profile_export": true, "redaction_required": ["local_path"] @@ -1014,7 +1014,7 @@ mod tests { let err = serde_json::from_value::(json!({ "mode": "capability_description", "backends": [{ - "backend": "cpu_pprof", + "backend": "pyroscope", "status": "enabled", "supports_profile_export": true, "redaction_required": ["local_path"], @@ -1035,7 +1035,7 @@ mod tests { fn rejects_ops_profiler_invalid_backend_and_redaction_shape() { let mut contract = OpsProfilerContract { mode: OpsProfilerContractMode::CapabilityDescription, - backends: vec![profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Enabled, true)], + backends: vec![profiler_backend("pyroscope", OpsProfilerBackendStatus::Enabled, true)], }; contract.backends[0].backend = OpsProfilerBackendName::new(" "); @@ -1044,15 +1044,15 @@ mod tests { ExtensionContractError::EmptyOpsProfilerBackend ); - contract.backends[0] = profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Enabled, true); + contract.backends[0] = profiler_backend("pyroscope", OpsProfilerBackendStatus::Enabled, true); contract .backends - .push(profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Disabled, false)); + .push(profiler_backend("pyroscope", OpsProfilerBackendStatus::Disabled, false)); assert_eq!( validate_ops_profiler_contract(&contract).expect_err("duplicate backends should fail validation"), ExtensionContractError::DuplicateOpsProfilerBackend { - backend: "cpu_pprof".to_string() + backend: "pyroscope".to_string() } ); @@ -1062,7 +1062,7 @@ mod tests { assert_eq!( validate_ops_profiler_contract(&contract).expect_err("duplicate redaction fields should fail validation"), ExtensionContractError::DuplicateOpsProfilerRedactionField { - backend: "cpu_pprof".to_string(), + backend: "pyroscope".to_string(), field: OpsProfilerRedactionField::Host } ); @@ -1072,7 +1072,7 @@ mod tests { assert_eq!( validate_ops_profiler_contract(&contract).expect_err("profile export requires local path redaction"), ExtensionContractError::OpsProfilerMissingLocalPathRedaction { - backend: "cpu_pprof".to_string() + backend: "pyroscope".to_string() } ); } @@ -1081,14 +1081,14 @@ mod tests { fn rejects_ops_profiler_missing_provenance_boundary() { let mut contract = OpsProfilerContract { mode: OpsProfilerContractMode::CapabilityDescription, - backends: vec![profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Enabled, true)], + backends: vec![profiler_backend("pyroscope", OpsProfilerBackendStatus::Enabled, true)], }; contract.backends[0].provenance.source = " ".to_string(); assert_eq!( validate_ops_profiler_contract(&contract).expect_err("provenance source should be required"), ExtensionContractError::EmptyOpsProfilerProvenanceSource { - backend: "cpu_pprof".to_string() + backend: "pyroscope".to_string() } ); @@ -1098,7 +1098,7 @@ mod tests { assert_eq!( validate_ops_profiler_contract(&contract).expect_err("collection boundary should be required"), ExtensionContractError::EmptyOpsProfilerCollectionBoundary { - backend: "cpu_pprof".to_string() + backend: "pyroscope".to_string() } ); } diff --git a/crates/obs/Cargo.toml b/crates/obs/Cargo.toml index d61f8635a..3e27e4696 100644 --- a/crates/obs/Cargo.toml +++ b/crates/obs/Cargo.toml @@ -28,7 +28,7 @@ documentation = "https://docs.rs/rustfs-obs/latest/rustfs_obs/" [features] default = [] gpu = ["dep:nvml-wrapper"] -pyroscope = ["dep:jemalloc_pprof", "dep:pyroscope"] +pyroscope = ["dep:pyroscope"] [lints] workspace = true @@ -83,7 +83,6 @@ nvml-wrapper = { workspace = true, optional = true } [target.'cfg(any(target_os = "macos", all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))'.dependencies] pyroscope = { workspace = true, features = ["backend-pprof-rs"], optional = true } -jemalloc_pprof = { workspace = true, optional = true } [dev-dependencies] diff --git a/crates/obs/src/telemetry/guard.rs b/crates/obs/src/telemetry/guard.rs index 537abe988..868ef3f19 100644 --- a/crates/obs/src/telemetry/guard.rs +++ b/crates/obs/src/telemetry/guard.rs @@ -39,10 +39,6 @@ pub(crate) type ProfilingAgent = pyroscope::PyroscopeAgent; -#[cfg(not(all(feature = "pyroscope", target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] -pub(crate) type MemoryProfilingAgent = (); const LOG_COMPONENT_OBS: &str = "obs"; const LOG_SUBSYSTEM_GUARD: &str = "guard"; @@ -67,7 +63,6 @@ pub struct OtelGuard { /// Optional logger provider for OTLP log export. pub(crate) logger_provider: Option, pub(crate) profiling_agent: Option, - pub(crate) memory_profiling_agent: Option, /// Handle to the background log-cleanup task; aborted on drop. pub(crate) cleanup_handle: Option>, /// Worker guard that keeps the non-blocking `tracing_appender` thread @@ -84,7 +79,6 @@ impl std::fmt::Debug for OtelGuard { .field("meter_provider", &self.meter_provider.is_some()) .field("logger_provider", &self.logger_provider.is_some()) .field("profiling_agent", &self.profiling_agent.is_some()) - .field("memory_profiling_agent", &self.memory_profiling_agent.is_some()) .field("cleanup_handle", &self.cleanup_handle.is_some()) .field("tracing_guard", &self.tracing_guard.is_some()) .field("stdout_guard", &self.stdout_guard.is_some()) @@ -161,30 +155,6 @@ impl Drop for OtelGuard { } } - #[cfg(all(feature = "pyroscope", target_os = "linux", target_env = "gnu", target_arch = "x86_64"))] - if let Some(agent) = self.memory_profiling_agent.take() { - match agent.stop() { - Err(err) => { - if tracing::dispatcher::has_been_set() { - error!( - event = EVENT_OBS_GUARD_SHUTDOWN, - component = LOG_COMPONENT_OBS, - subsystem = LOG_SUBSYSTEM_GUARD, - resource = "memory_profiling_agent", - result = "shutdown_failed", - error = %err, - "observability guard shutdown failed" - ); - } else { - eprintln!("{}", format_guard_shutdown_stderr_message("memory_profiling_agent", err)); - } - } - Ok(stopped) => { - stopped.shutdown(); - } - } - } - if let Some(handle) = self.cleanup_handle.take() { debug!( event = EVENT_OBS_GUARD_SHUTDOWN, diff --git a/crates/obs/src/telemetry/local.rs b/crates/obs/src/telemetry/local.rs index e1b7ea5c3..65f01c4a0 100644 --- a/crates/obs/src/telemetry/local.rs +++ b/crates/obs/src/telemetry/local.rs @@ -271,7 +271,6 @@ fn init_stdout_only(_config: &OtelConfig, logger_level: &str, is_production: boo meter_provider: None, logger_provider: None, profiling_agent: None, - memory_profiling_agent: None, tracing_guard: Some(guard), stdout_guard: None, cleanup_handle: None, @@ -389,7 +388,6 @@ fn init_file_logging_internal( meter_provider: None, logger_provider: None, profiling_agent: None, - memory_profiling_agent: None, tracing_guard: Some(guard), stdout_guard, cleanup_handle: Some(cleanup_handle), diff --git a/crates/obs/src/telemetry/otel.rs b/crates/obs/src/telemetry/otel.rs index f2688f420..d955e180e 100644 --- a/crates/obs/src/telemetry/otel.rs +++ b/crates/obs/src/telemetry/otel.rs @@ -40,7 +40,7 @@ use crate::cleaner::types::FileMatchMode; use crate::config::OtelConfig; use crate::global::set_observability_metric_enabled; use crate::telemetry::filter::build_env_filter; -use crate::telemetry::guard::{MemoryProfilingAgent, OtelGuard, ProfilingAgent}; +use crate::telemetry::guard::{OtelGuard, ProfilingAgent}; use crate::telemetry::local::{build_json_log_layer, spawn_cleanup_task}; use crate::telemetry::recorder::Recorder; use crate::telemetry::resource::build_resource; @@ -173,7 +173,6 @@ pub(super) fn init_observability_http( let meter_provider = build_meter_provider(&metric_ep, config, res.clone(), &service_name, use_stdout)?; let profiling_agent = init_profiler(config); - let memory_profiling_agent = init_memory_profiler(config); // ── Logger Logic ────────────────────────────────────────────────────────── // Logging is the only signal that may intentionally route to either OTLP @@ -317,7 +316,6 @@ pub(super) fn init_observability_http( meter_provider, logger_provider, profiling_agent, - memory_profiling_agent, tracing_guard, stdout_guard, cleanup_handle, @@ -563,65 +561,6 @@ fn init_profiler(_config: &OtelConfig) -> Option { None } -/// Initialise a Pyroscope agent for continuous **memory** profiling via jemalloc. -/// -/// This is only available on `linux + gnu + x86_64` where tikv-jemallocator -/// is the global allocator and `jemalloc_pprof::PROF_CTL` is accessible. -/// -/// Returns `None` when profiling export is disabled, the endpoint is missing, -/// jemalloc profiling is not activated, or the agent fails to build/start. -#[cfg(all(feature = "pyroscope", target_os = "linux", target_env = "gnu", target_arch = "x86_64"))] -fn init_memory_profiler(config: &OtelConfig) -> Option { - use pyroscope::backend::jemalloc_backend; - use pyroscope::pyroscope::PyroscopeAgentBuilder; - use rustfs_config::VERSION; - - if !config - .profiling_export_enabled - .unwrap_or(rustfs_config::DEFAULT_OBS_PROFILING_EXPORT_ENABLED) - { - return None; - } - - let endpoint = config.profiling_endpoint.as_ref()?.as_str(); - if endpoint.is_empty() { - return None; - } - - // Verify jemalloc profiling is available and activated - { - let prof_ctl = jemalloc_pprof::PROF_CTL.as_ref()?; - let ctl = prof_ctl.try_lock().ok()?; - if !ctl.activated() { - eprintln!("Memory profiling skipped: jemalloc profiling is not activated"); - return None; - } - } - - let backend = jemalloc_backend(); - let service_name = config.service_name.as_deref().unwrap_or(APP_NAME); - let version = config.service_version.as_deref().unwrap_or(VERSION); - let sample_rate = 100; - - let agent = PyroscopeAgentBuilder::new(endpoint, service_name, sample_rate, "pyroscope-rs", "1.0.1", backend) - .tags(vec![("version", version), ("profile_type", "memory")]) - .build() - .ok()?; - - match agent.start() { - Ok(agent) => Some(agent), - Err(err) => { - eprintln!("Memory profiling agent start error: {err:?}"); - None - } - } -} - -#[cfg(not(all(feature = "pyroscope", target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] -fn init_memory_profiler(_config: &OtelConfig) -> Option { - None -} - /// Create a stdout periodic metrics reader for the given interval. /// /// This helper is primarily used for local development and diagnostics when diff --git a/crates/targets/src/catalog/extension.rs b/crates/targets/src/catalog/extension.rs index 219f24f6c..81f901c3c 100644 --- a/crates/targets/src/catalog/extension.rs +++ b/crates/targets/src/catalog/extension.rs @@ -123,8 +123,8 @@ pub fn builtin_ops_profiler_contract() -> OpsProfilerContract { OpsProfilerContract { mode: OpsProfilerContractMode::CapabilityDescription, backends: vec![ - builtin_ops_profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Enabled, true), - builtin_ops_profiler_backend("memory_pprof", OpsProfilerBackendStatus::Disabled, true), + builtin_ops_profiler_backend("cpu_pprof", OpsProfilerBackendStatus::Unsupported, false), + builtin_ops_profiler_backend("memory_pprof", OpsProfilerBackendStatus::Unsupported, false), builtin_ops_profiler_backend("ebpf", OpsProfilerBackendStatus::Unsupported, false), ], } @@ -391,8 +391,8 @@ mod tests { assert_eq!( backends, vec![ - ("cpu_pprof", OpsProfilerBackendStatus::Enabled, true), - ("memory_pprof", OpsProfilerBackendStatus::Disabled, true), + ("cpu_pprof", OpsProfilerBackendStatus::Unsupported, false), + ("memory_pprof", OpsProfilerBackendStatus::Unsupported, false), ("ebpf", OpsProfilerBackendStatus::Unsupported, false), ] ); diff --git a/crates/targets/src/runtime/ops_profiler.rs b/crates/targets/src/runtime/ops_profiler.rs index 11eae712a..e8ae572a0 100644 --- a/crates/targets/src/runtime/ops_profiler.rs +++ b/crates/targets/src/runtime/ops_profiler.rs @@ -172,13 +172,13 @@ mod tests { assert_eq!(registry.registered_backend_count(), contract.backends.len()); assert_eq!( registry.registration_for("cpu_pprof").map(|registration| registration.status), - Some(OpsProfilerBackendStatus::Enabled) + Some(OpsProfilerBackendStatus::Unsupported) ); assert_eq!( registry .registration_for("memory_pprof") .map(|registration| registration.supports_profile_export), - Some(true) + Some(false) ); assert_eq!( registry.authorize_read(OpsProfilerReadRequest { diff --git a/rustfs/Cargo.toml b/rustfs/Cargo.toml index e5f29b4e1..4f6a3ae0e 100644 --- a/rustfs/Cargo.toml +++ b/rustfs/Cargo.toml @@ -186,25 +186,16 @@ opentelemetry = { workspace = true } tracing-opentelemetry = { workspace = true } # Data structures hashbrown = { workspace = true } +mimalloc = { workspace = true } [target.'cfg(target_os = "linux")'.dependencies] libsystemd.workspace = true # io-uring is Linux-only. Scope the feature to Linux targets. tokio = { workspace = true, features = ["io-uring"] } -[target.'cfg(not(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))'.dependencies] -mimalloc = { workspace = true } +[target.'cfg(not(target_os = "windows"))'.dependencies] libmimalloc-sys = { version = "0.1.49", features = ["extended"] } -[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] -pprof = { workspace = true } - -# jemalloc-based allocator and memory profiling are only enabled on validated linux-gnu-x86_64 targets. -[target.'cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))'.dependencies] -tikv-jemallocator = { workspace = true } -tikv-jemalloc-ctl = { workspace = true } -jemalloc_pprof = { workspace = true } - [dev-dependencies] uuid = { workspace = true, features = ["v4"] } serial_test = { workspace = true } diff --git a/rustfs/src/admin/handlers/profile.rs b/rustfs/src/admin/handlers/profile.rs index aa836ad97..6a0771a80 100644 --- a/rustfs/src/admin/handlers/profile.rs +++ b/rustfs/src/admin/handlers/profile.rs @@ -15,11 +15,7 @@ use crate::admin::{auth::validate_admin_request, router::Operation}; use crate::auth::{check_key_valid, get_session_token}; use crate::server::RemoteAddr; -#[cfg(any(target_os = "macos", all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] -use http::HeaderMap; use http::StatusCode; -#[cfg(any(target_os = "macos", all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] -use http::header::CONTENT_TYPE; use matchit::Params; use rustfs_policy::policy::action::{Action, AdminAction}; use s3s::{Body, S3Request, S3Response, S3Result, s3_error}; @@ -45,6 +41,10 @@ pub(super) async fn authorize_profile_request(req: &S3Request) -> S3Result .await } +pub(super) fn profile_not_implemented_response(message: String) -> S3Response<(StatusCode, Body)> { + S3Response::new((StatusCode::NOT_IMPLEMENTED, Body::from(message))) +} + pub struct TriggerProfileCPU {} #[async_trait::async_trait] impl Operation for TriggerProfileCPU { @@ -52,30 +52,8 @@ impl Operation for TriggerProfileCPU { authorize_profile_request(&req).await?; info!("Triggering CPU profile dump via S3 request..."); - #[cfg(not(any(target_os = "macos", all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))))] - { - return Ok(S3Response::new(( - StatusCode::NOT_IMPLEMENTED, - Body::from( - crate::profiling::dump_cpu_pprof_for(std::time::Duration::from_secs(0)) - .await - .unwrap_err(), - ), - ))); - } - - #[cfg(any(target_os = "macos", all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] - { - let dur = std::time::Duration::from_secs(60); - match crate::profiling::dump_cpu_pprof_for(dur).await { - Ok(path) => { - let mut header = HeaderMap::new(); - header.insert(CONTENT_TYPE, "text/html".parse().expect("operation should succeed")); - Ok(S3Response::with_headers((StatusCode::OK, Body::from(path.display().to_string())), header)) - } - Err(e) => Err(s3s::s3_error!(InternalError, "{}", format!("Failed to dump CPU profile: {e}"))), - } - } + crate::profiling::log_cpu_pprof_dump_skipped(); + Ok(profile_not_implemented_response(crate::profiling::local_cpu_pprof_unsupported_message())) } } @@ -86,25 +64,8 @@ impl Operation for TriggerProfileMemory { authorize_profile_request(&req).await?; info!("Triggering Memory profile dump via S3 request..."); - #[cfg(not(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] - { - return Ok(S3Response::new(( - StatusCode::NOT_IMPLEMENTED, - Body::from(crate::profiling::dump_memory_pprof_now().await.unwrap_err()), - ))); - } - - #[cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))] - { - match crate::profiling::dump_memory_pprof_now().await { - Ok(path) => { - let mut header = HeaderMap::new(); - header.insert(CONTENT_TYPE, "text/html".parse().expect("operation should succeed")); - Ok(S3Response::with_headers((StatusCode::OK, Body::from(path.display().to_string())), header)) - } - Err(e) => Err(s3s::s3_error!(InternalError, "{}", format!("Failed to dump Memory profile: {e}"))), - } - } + crate::profiling::log_memory_pprof_dump_skipped(); + Ok(profile_not_implemented_response(crate::profiling::memory_pprof_unsupported_message())) } } diff --git a/rustfs/src/admin/handlers/profile_admin.rs b/rustfs/src/admin/handlers/profile_admin.rs index d56a16c9f..029e4f58c 100644 --- a/rustfs/src/admin/handlers/profile_admin.rs +++ b/rustfs/src/admin/handlers/profile_admin.rs @@ -12,28 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -use super::profile::authorize_profile_request; +use super::profile::{authorize_profile_request, profile_not_implemented_response}; use crate::admin::router::{AdminOperation, Operation, S3Router}; use crate::server::ADMIN_PREFIX; -use http::{HeaderMap, HeaderValue, Uri}; +use http::{HeaderMap, HeaderValue}; use hyper::{Method, StatusCode}; use matchit::Params; use s3s::header::CONTENT_TYPE; use s3s::{Body, S3Request, S3Response, S3Result}; -use std::collections::HashMap; +use serde::Serialize; use tracing::error; -#[allow(dead_code)] -fn extract_query_params(uri: &Uri) -> HashMap { - let mut params = HashMap::new(); - - if let Some(query) = uri.query() { - for (key, value) in url::form_urlencoded::parse(query.as_bytes()) { - params.insert(key.into_owned(), value.into_owned()); - } - } - - params +#[derive(Serialize)] +struct ProfileStatus { + enabled: &'static str, + status: &'static str, + platform: &'static str, + message: &'static str, } pub fn register_profiling_route(r: &mut S3Router) -> std::io::Result<()> { @@ -54,114 +49,17 @@ pub fn register_profiling_route(r: &mut S3Router) -> std::io::Re pub struct ProfileHandler {} -#[allow(dead_code)] -fn map_cpu_profile_collect_error_message(err: &str) -> (StatusCode, String) { - if err.contains("start running cpu profiler error") { - return ( - StatusCode::CONFLICT, - "CPU profiler is already running. Disable RUSTFS_OBS_PROFILING_EXPORT_ENABLED or retry later.".to_string(), - ); - } - (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to collect CPU profile: {err}")) -} - #[async_trait::async_trait] impl Operation for ProfileHandler { async fn call(&self, req: S3Request, _params: Params<'_, '_>) -> S3Result> { authorize_profile_request(&req).await?; - #[cfg(not(any(target_os = "macos", all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))))] - { - let requested_url = req.uri.to_string(); - let target_os = std::env::consts::OS; - let target_arch = std::env::consts::ARCH; - let target_env = option_env!("CARGO_CFG_TARGET_ENV").unwrap_or("unknown"); - let msg = format!( - "CPU profiling is not supported on this platform. target_os={target_os}, target_env={target_env}, target_arch={target_arch}, requested_url={requested_url}" - ); - return Ok(S3Response::new((StatusCode::NOT_IMPLEMENTED, Body::from(msg)))); - } - - #[cfg(any(target_os = "macos", all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] - { - use rustfs_config::{DEFAULT_CPU_FREQ, ENV_CPU_FREQ}; - use rustfs_utils::get_env_usize; - - let queries = extract_query_params(&req.uri); - let seconds = queries.get("seconds").and_then(|s| s.parse::().ok()).unwrap_or(30); - let format = queries.get("format").cloned().unwrap_or_else(|| "protobuf".to_string()); - - if seconds > 300 { - return Ok(S3Response::new(( - StatusCode::BAD_REQUEST, - Body::from("Profile duration cannot exceed 300 seconds".to_string()), - ))); - } - - match format.as_str() { - "protobuf" | "pb" => match crate::profiling::dump_cpu_pprof_for(std::time::Duration::from_secs(seconds)).await { - Ok(path) => match tokio::fs::read(&path).await { - Ok(bytes) => { - let mut headers = HeaderMap::new(); - headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/octet-stream")); - Ok(S3Response::with_headers((StatusCode::OK, Body::from(bytes)), headers)) - } - Err(e) => { - error!("Failed to read profile file {}: {}", path.display(), e); - Ok(S3Response::new(( - StatusCode::INTERNAL_SERVER_ERROR, - Body::from(format!("Failed to read profile file: {e}")), - ))) - } - }, - Err(e) => { - let (status, message) = map_cpu_profile_collect_error_message(&e); - error!("CPU protobuf profile collection failed: {}", e); - Ok(S3Response::new((status, Body::from(message)))) - } - }, - "flamegraph" | "svg" => { - let freq = get_env_usize(ENV_CPU_FREQ, DEFAULT_CPU_FREQ) as i32; - let guard = match pprof::ProfilerGuard::new(freq) { - Ok(g) => g, - Err(e) => { - return Ok(S3Response::new(( - StatusCode::INTERNAL_SERVER_ERROR, - Body::from(format!("Failed to create profiler: {e}")), - ))); - } - }; - - tokio::time::sleep(std::time::Duration::from_secs(seconds)).await; - - let report = match guard.report().build() { - Ok(r) => r, - Err(e) => { - return Ok(S3Response::new(( - StatusCode::INTERNAL_SERVER_ERROR, - Body::from(format!("Failed to build profile report: {e}")), - ))); - } - }; - - let mut flamegraph_buf = Vec::new(); - if let Err(e) = report.flamegraph(&mut flamegraph_buf) { - return Ok(S3Response::new(( - StatusCode::INTERNAL_SERVER_ERROR, - Body::from(format!("Failed to generate flamegraph: {e}")), - ))); - } - - let mut headers = HeaderMap::new(); - headers.insert(CONTENT_TYPE, HeaderValue::from_static("image/svg+xml")); - Ok(S3Response::with_headers((StatusCode::OK, Body::from(flamegraph_buf)), headers)) - } - _ => Ok(S3Response::new(( - StatusCode::BAD_REQUEST, - Body::from("Unsupported format. Use 'protobuf' or 'flamegraph'".to_string()), - ))), - } - } + let requested_url = req.uri.to_string(); + crate::profiling::log_cpu_pprof_dump_skipped(); + Ok(profile_not_implemented_response(format!( + "{}; requested_url={requested_url}", + crate::profiling::local_cpu_pprof_unsupported_message() + ))) } } @@ -172,37 +70,11 @@ impl Operation for ProfileStatusHandler { async fn call(&self, req: S3Request, _params: Params<'_, '_>) -> S3Result> { authorize_profile_request(&req).await?; - #[cfg(not(any(target_os = "macos", all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))))] - let message = format!("CPU profiling is not supported on {} platform", std::env::consts::OS); - #[cfg(not(any(target_os = "macos", all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))))] - let status = HashMap::from([ - ("enabled", "false"), - ("status", "not_supported"), - ("platform", std::env::consts::OS), - ("message", message.as_str()), - ]); - - #[cfg(any(target_os = "macos", all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] - let status = { - use rustfs_config::{DEFAULT_ENABLE_PROFILING, ENV_ENABLE_PROFILING}; - use rustfs_utils::get_env_bool; - - let enabled = get_env_bool(ENV_ENABLE_PROFILING, DEFAULT_ENABLE_PROFILING); - if enabled { - HashMap::from([ - ("enabled", "true"), - ("status", "running"), - ("supported_formats", "protobuf, flamegraph"), - ("max_duration_seconds", "300"), - ("endpoint", "/rustfs/admin/debug/pprof/profile"), - ]) - } else { - HashMap::from([ - ("enabled", "false"), - ("status", "disabled"), - ("message", "Set RUSTFS_ENABLE_PROFILING=true to enable profiling"), - ]) - } + let status = ProfileStatus { + enabled: "false", + status: "not_supported", + platform: std::env::consts::OS, + message: crate::profiling::LOCAL_CPU_PPROF_UNSUPPORTED_SUMMARY, }; match serde_json::to_string(&status) { @@ -224,11 +96,10 @@ impl Operation for ProfileStatusHandler { #[cfg(test)] mod tests { - use super::{ProfileHandler, ProfileStatusHandler, extract_query_params}; + use super::{ProfileHandler, ProfileStatusHandler}; use crate::admin::router::Operation; use http::{Extensions, HeaderMap, Uri}; use hyper::Method; - use hyper::StatusCode; use matchit::Params; use s3s::{Body, S3ErrorCode, S3Request}; @@ -246,17 +117,6 @@ mod tests { } } - #[test] - fn test_extract_query_params_decodes_percent_encoded_values() { - let uri: Uri = "/rustfs/admin/debug/pprof/profile?format=flamegraph¬e=a%2Bb+value" - .parse() - .expect("uri should parse"); - let params = extract_query_params(&uri); - - assert_eq!(params.get("format"), Some(&"flamegraph".to_string())); - assert_eq!(params.get("note"), Some(&"a+b value".to_string())); - } - #[tokio::test] async fn profile_handler_rejects_missing_credentials() { let result = ProfileHandler {} @@ -284,12 +144,4 @@ mod tests { assert_eq!(err.code(), &S3ErrorCode::AccessDenied); assert_eq!(err.message(), Some("Signature is required")); } - - #[test] - fn cpu_profile_collect_error_maps_profiler_conflict_to_409() { - let (status, message) = - super::map_cpu_profile_collect_error_message("create profiler failed: start running cpu profiler error"); - assert_eq!(status, StatusCode::CONFLICT); - assert!(message.contains("CPU profiler is already running")); - } } diff --git a/rustfs/src/allocator_reclaim.rs b/rustfs/src/allocator_reclaim.rs index e04fb396e..8535eb13e 100644 --- a/rustfs/src/allocator_reclaim.rs +++ b/rustfs/src/allocator_reclaim.rs @@ -111,15 +111,7 @@ impl AllocatorReclaimController { } pub fn allocator_backend() -> &'static str { - #[cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))] - { - "jemalloc" - } - - #[cfg(all( - not(target_os = "windows"), - not(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")) - ))] + #[cfg(not(target_os = "windows"))] { "mimalloc" } @@ -206,7 +198,7 @@ fn configured_allocator_reclaim_interval_secs() -> u64 { } fn effective_allocator_reclaim_force(backend: &str, configured_force: bool) -> bool { - configured_force && backend != "jemalloc" + configured_force && backend != "mimalloc-windows" } fn build_allocator_reclaim_desired_snapshot( @@ -302,10 +294,7 @@ pub fn allocator_reclaim_controller_snapshot(ctx: &CancellationToken) -> Allocat ) } -#[cfg(all( - not(target_os = "windows"), - not(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")) -))] +#[cfg(not(target_os = "windows"))] #[allow(unsafe_code)] fn collect_allocator_memory(force: bool) -> Result<(), String> { // SAFETY: `mi_collect` is provided by the active global allocator backend @@ -317,13 +306,6 @@ fn collect_allocator_memory(force: bool) -> Result<(), String> { Ok(()) } -#[cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))] -fn collect_allocator_memory(_force: bool) -> Result<(), String> { - let _ = tikv_jemalloc_ctl::background_thread::write(true); - tikv_jemalloc_ctl::epoch::advance().map_err(|err| err.to_string())?; - Ok(()) -} - #[cfg(target_os = "windows")] fn collect_allocator_memory(_force: bool) -> Result<(), String> { Err("allocator reclaim is not supported on Windows".to_string()) @@ -368,13 +350,6 @@ pub fn init_allocator_reclaim(ctx: CancellationToken) { } let configured_force = configured_allocator_reclaim_force(); - if backend == "jemalloc" && configured_force { - warn!( - backend, - env = rustfs_config::ENV_ALLOCATOR_RECLAIM_FORCE, - "allocator reclaim force mode is not supported on jemalloc backend; ignoring configured force flag" - ); - } let force = effective_allocator_reclaim_force(backend, configured_force); let idle_intervals = configured_allocator_reclaim_idle_intervals(); let interval = Duration::from_secs(configured_allocator_reclaim_interval_secs()); @@ -473,8 +448,8 @@ mod tests { } #[test] - fn allocator_reclaim_force_preserves_jemalloc_override() { - assert!(!effective_allocator_reclaim_force("jemalloc", true)); + fn allocator_reclaim_force_is_disabled_only_on_windows_backend() { + assert!(!effective_allocator_reclaim_force("mimalloc-windows", true)); assert!(effective_allocator_reclaim_force("mimalloc", true)); assert!(!effective_allocator_reclaim_force("mimalloc", false)); } diff --git a/rustfs/src/main.rs b/rustfs/src/main.rs index 8a91c3bcc..4b0a22a36 100644 --- a/rustfs/src/main.rs +++ b/rustfs/src/main.rs @@ -12,11 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))] -#[global_allocator] -static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; - -#[cfg(not(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] #[global_allocator] static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; diff --git a/rustfs/src/profiling.rs b/rustfs/src/profiling.rs index 7e41ad577..42369f483 100644 --- a/rustfs/src/profiling.rs +++ b/rustfs/src/profiling.rs @@ -12,647 +12,82 @@ // See the License for the specific language governing permissions and // limitations under the License. -#[cfg(not(any(target_os = "linux", target_os = "macos")))] -mod unsupported_impl { - use std::path::PathBuf; - use std::time::Duration; - use tracing::{debug, info}; +use tracing::{debug, info}; - const LOG_COMPONENT_PROFILING: &str = "profiling"; - const LOG_SUBSYSTEM_PLATFORM: &str = "platform"; +const LOG_COMPONENT_PROFILING: &str = "profiling"; +const LOG_SUBSYSTEM_CPU: &str = "cpu"; +const LOG_SUBSYSTEM_MEMORY: &str = "memory"; +const LOG_SUBSYSTEM_RUNTIME: &str = "runtime"; +const LOCAL_CPU_PPROF_UNSUPPORTED_REASON: &str = "local_cpu_pprof_unsupported"; +const MEMORY_PPROF_UNSUPPORTED_REASON: &str = "mimalloc_memory_pprof_unsupported"; +pub const LOCAL_CPU_PPROF_UNSUPPORTED_SUMMARY: &str = "local CPU pprof dumps are not supported; use Pyroscope export instead"; +pub const MEMORY_PPROF_UNSUPPORTED_SUMMARY: &str = "memory pprof dumps are not supported with the mimalloc allocator"; - pub async fn init_from_env() { - let target_env = option_env!("CARGO_CFG_TARGET_ENV").unwrap_or("unknown"); - info!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_PLATFORM, - event = "profiling_runtime_skipped", - reason = "unsupported_platform", - target_os = std::env::consts::OS, - target_env, - target_arch = std::env::consts::ARCH, - "Profiling runtime skipped" - ); - } - - /// Stop all background profiling tasks - pub fn shutdown_profiling() { - let target_env = option_env!("CARGO_CFG_TARGET_ENV").unwrap_or("unknown"); - debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_PLATFORM, - event = "profiling_shutdown_skipped", - reason = "unsupported_platform", - target_os = std::env::consts::OS, - target_env, - target_arch = std::env::consts::ARCH, - "Profiling shutdown skipped" - ); - } - - pub async fn dump_cpu_pprof_for(_duration: Duration) -> Result { - Err(unsupported_message("CPU profiling")) - } - - pub async fn dump_memory_pprof_now() -> Result { - Err(unsupported_message("Memory profiling")) - } - - fn unsupported_message(feature: &str) -> String { - let target_env = option_env!("CARGO_CFG_TARGET_ENV").unwrap_or("unknown"); - format!( - "{feature} is only supported on linux x86_64 gnu. target_os={}, target_env={target_env}, target_arch={}", - std::env::consts::OS, - std::env::consts::ARCH - ) - } +pub async fn init_from_env() { + info!( + component = LOG_COMPONENT_PROFILING, + subsystem = LOG_SUBSYSTEM_RUNTIME, + event = "profiling_runtime_skipped", + reason = LOCAL_CPU_PPROF_UNSUPPORTED_REASON, + target_os = std::env::consts::OS, + target_env = target_env(), + target_arch = std::env::consts::ARCH, + "Local pprof profiling runtime skipped" + ); } -#[cfg(not(any(target_os = "linux", target_os = "macos")))] -pub use unsupported_impl::{dump_cpu_pprof_for, dump_memory_pprof_now, init_from_env, shutdown_profiling}; - -#[cfg(any(target_os = "linux", target_os = "macos"))] -mod linux_impl { - use pprof::protos::Message; - use rustfs_config::{ - DEFAULT_CPU_DURATION_SECS, DEFAULT_CPU_FREQ, DEFAULT_CPU_INTERVAL_SECS, DEFAULT_CPU_MODE, DEFAULT_ENABLE_PROFILING, - DEFAULT_MEM_INTERVAL_SECS, DEFAULT_MEM_PERIODIC, DEFAULT_OUTPUT_DIR, ENV_CPU_DURATION_SECS, ENV_CPU_FREQ, - ENV_CPU_INTERVAL_SECS, ENV_CPU_MODE, ENV_ENABLE_PROFILING, ENV_MEM_INTERVAL_SECS, ENV_MEM_PERIODIC, ENV_OUTPUT_DIR, - }; - use rustfs_utils::{get_env_bool, get_env_str, get_env_u64, get_env_usize}; - use std::fs::{File, create_dir_all}; - use std::io::Write; - use std::path::{Path, PathBuf}; - use std::sync::{Arc, OnceLock}; - use std::time::Duration; - use tokio::sync::Mutex; - use tokio::time::sleep; - use tokio_util::sync::CancellationToken; - use tracing::{debug, info, warn}; - - const LOG_COMPONENT_PROFILING: &str = "profiling"; - const LOG_SUBSYSTEM_CPU: &str = "cpu"; - const LOG_SUBSYSTEM_MEMORY: &str = "memory"; - const LOG_SUBSYSTEM_JEMALLOC: &str = "jemalloc"; - const LOG_SUBSYSTEM_RUNTIME: &str = "runtime"; - - static CPU_CONT_GUARD: OnceLock>>>> = OnceLock::new(); - static PROFILING_CANCEL_TOKEN: OnceLock = OnceLock::new(); - - /// CPU profiling mode - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - enum CpuMode { - Off, - Continuous, - Periodic, - } - - /// Get or create output directory - fn output_dir() -> PathBuf { - let dir = get_env_str(ENV_OUTPUT_DIR, DEFAULT_OUTPUT_DIR); - let p = PathBuf::from(dir); - if let Err(e) = create_dir_all(&p) { - warn!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_RUNTIME, - event = "profiling_output_dir_fallback", - path = %p.display(), - error = %e, - fallback = ".", - "Profiling output directory fallback applied" - ); - return PathBuf::from("."); - } - p - } - - /// Read CPU profiling mode from env - fn read_cpu_mode() -> CpuMode { - match get_env_str(ENV_CPU_MODE, DEFAULT_CPU_MODE).to_lowercase().as_str() { - "continuous" => CpuMode::Continuous, - "periodic" => CpuMode::Periodic, - _ => CpuMode::Off, - } - } - - /// Generate timestamp string for filenames - fn ts() -> String { - jiff::Zoned::now().strftime("%Y%m%dT%H%M%S").to_string() - } - - /// Write pprof report to file in protobuf format - fn write_pprof_report_pb(report: &pprof::Report, path: &Path) -> Result<(), String> { - let profile = report.pprof().map_err(|e| format!("pprof() failed: {e}"))?; - let mut buf = Vec::with_capacity(512 * 1024); - profile.write_to_vec(&mut buf).map_err(|e| format!("encode failed: {e}"))?; - let mut f = File::create(path).map_err(|e| format!("create file failed: {e}"))?; - f.write_all(&buf).map_err(|e| format!("write file failed: {e}"))?; - Ok(()) - } - - /// Internal: dump CPU pprof from existing guard - async fn dump_cpu_with_guard(guard: &pprof::ProfilerGuard<'_>) -> Result { - let report = guard.report().build().map_err(|e| format!("build report failed: {e}"))?; - let out = output_dir().join(format!("cpu_profile_{}.pb", ts())); - write_pprof_report_pb(&report, &out)?; - info!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_dump_exported", - profile_type = "cpu", - path = %out.display(), - "Profiling dump exported" - ); - Ok(out) - } - - // Public API: dump CPU for a duration; if continuous guard exists, snapshot immediately. - pub async fn dump_cpu_pprof_for(duration: Duration) -> Result { - if let Some(cell) = CPU_CONT_GUARD.get() { - let guard_slot = cell.lock().await; - if let Some(ref guard) = *guard_slot { - debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_dump_source", - profile_type = "cpu", - source = "continuous_guard", - "Using continuous CPU profiling guard for dump" - ); - return dump_cpu_with_guard(guard).await; - } - } - - let freq = get_env_usize(ENV_CPU_FREQ, DEFAULT_CPU_FREQ) as i32; - let guard = pprof::ProfilerGuard::new(freq).map_err(|e| format!("create profiler failed: {e}"))?; - sleep(duration).await; - - dump_cpu_with_guard(&guard).await - } - - // Public API: dump memory pprof now (jemalloc) - #[cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))] - pub async fn dump_memory_pprof_now() -> Result { - let out = output_dir().join(format!("mem_profile_{}.pb", ts())); - let mut f = File::create(&out).map_err(|e| format!("create file failed: {e}"))?; - - let prof_ctl_cell = jemalloc_pprof::PROF_CTL - .as_ref() - .ok_or_else(|| "jemalloc profiling control not available".to_string())?; - let mut prof_ctl = prof_ctl_cell.lock().await; - - if !prof_ctl.activated() { - return Err("jemalloc profiling is not active".to_string()); - } - - let bytes = prof_ctl.dump_pprof().map_err(|e| format!("dump pprof failed: {e}"))?; - f.write_all(&bytes).map_err(|e| format!("write file failed: {e}"))?; - info!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_MEMORY, - event = "profiling_dump_exported", - profile_type = "memory", - path = %out.display(), - "Profiling dump exported" - ); - Ok(out) - } - - #[cfg(not(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] - pub async fn dump_memory_pprof_now() -> Result { - Err(memory_profiling_unsupported_message()) - } - - // Jemalloc status check (No forced placement, only status observation) - #[cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))] - pub async fn check_jemalloc_profiling() { - use tikv_jemalloc_ctl::{config, epoch, stats}; - - if let Err(e) = epoch::advance() { - warn!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_JEMALLOC, - event = "jemalloc_epoch_advance_failed", - error = %e, - "Jemalloc profiling state changed" - ); - } - - match config::malloc_conf::read() { - Ok(conf) => debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_JEMALLOC, - event = "jemalloc_malloc_conf", - result = "ok", - malloc_conf = %conf, - "Jemalloc profiling state checked" - ), - Err(e) => debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_JEMALLOC, - event = "jemalloc_malloc_conf", - result = "read_failed", - error = %e, - "Jemalloc profiling state checked" - ), - } - - match std::env::var("MALLOC_CONF") { - Ok(v) => debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_JEMALLOC, - event = "jemalloc_malloc_conf_env", - state = "set", - malloc_conf = %v, - "Jemalloc profiling state checked" - ), - Err(_) => debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_JEMALLOC, - event = "jemalloc_malloc_conf_env", - state = "unset", - "Jemalloc profiling state checked" - ), - } - - if let Some(lock) = jemalloc_pprof::PROF_CTL.as_ref() { - let ctl = lock.lock().await; - info!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_JEMALLOC, - event = "jemalloc_profiling_status", - activated = ctl.activated(), - "Jemalloc profiling status checked" - ); - } else { - debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_JEMALLOC, - event = "jemalloc_profiling_status", - state = "unavailable", - "Jemalloc profiling status checked" - ); - } - - let _ = epoch::advance(); - macro_rules! show { - ($name:literal, $reader:expr) => { - match $reader { - Ok(v) => debug!(concat!($name, "={}"), v), - Err(e) => debug!(concat!($name, " read failed: {}"), e), - } - }; - } - show!("allocated", stats::allocated::read()); - show!("resident", stats::resident::read()); - show!("mapped", stats::mapped::read()); - show!("metadata", stats::metadata::read()); - show!("active", stats::active::read()); - } - - #[cfg(not(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] - pub async fn check_jemalloc_profiling() { - debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_JEMALLOC, - event = "jemalloc_profiling_status", - result = "skipped", - reason = "unsupported_target", - "Jemalloc profiling status checked" - ); - } - - // Internal: start continuous CPU profiling - async fn start_cpu_continuous(freq_hz: i32) { - let cell = CPU_CONT_GUARD.get_or_init(|| Arc::new(Mutex::new(None))).clone(); - let mut slot = cell.lock().await; - if slot.is_some() { - warn!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_state", - profile_type = "cpu_continuous", - state = "already_running", - "CPU profiling already running" - ); - return; - } - match pprof::ProfilerGuardBuilder::default() - .frequency(freq_hz) - .blocklist(&["libc", "libgcc", "pthread", "vdso"]) - .build() - { - Ok(guard) => { - *slot = Some(guard); - info!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_state", - profile_type = "cpu_continuous", - state = "started", - freq_hz, - "CPU profiling started" - ); - } - Err(e) => warn!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_state", - profile_type = "cpu_continuous", - state = "start_failed", - error = %e, - "CPU profiling failed to start" - ), - } - } - - // Internal: start periodic CPU sampling loop - async fn start_cpu_periodic(freq_hz: i32, interval: Duration, duration: Duration, token: CancellationToken) { - info!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_state", - profile_type = "cpu_periodic", - state = "started", - freq_hz, - ?interval, - ?duration, - "Periodic CPU profiling started" - ); - tokio::spawn(async move { - loop { - tokio::select! { - _ = token.cancelled() => { - info!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_state", - profile_type = "cpu_periodic", - state = "cancelled", - "Periodic CPU profiling cancelled" - ); - break; - } - _ = sleep(interval) => {} - } - - if token.is_cancelled() { - break; - } - - let guard = match pprof::ProfilerGuard::new(freq_hz) { - Ok(g) => g, - Err(e) => { - warn!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_capture_failed", - profile_type = "cpu_periodic", - stage = "create_guard", - error = %e, - "Profiling capture failed" - ); - continue; - } - }; - - tokio::select! { - _ = token.cancelled() => { - info!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_state", - profile_type = "cpu_periodic", - state = "cancelled_during_capture", - "Periodic CPU profiling cancelled during capture" - ); - break; - } - _ = sleep(duration) => {} - } - - match guard.report().build() { - Ok(report) => { - let out = output_dir().join(format!("cpu_profile_{}.pb", ts())); - if let Err(e) = write_pprof_report_pb(&report, &out) { - warn!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_dump_failed", - profile_type = "cpu_periodic", - stage = "write_dump", - path = %out.display(), - error = %e, - "Periodic CPU dump write failed" - ); - } else { - debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_dump_exported", - profile_type = "cpu_periodic", - path = %out.display(), - "Profiling dump exported" - ); - } - } - Err(e) => warn!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_capture_failed", - profile_type = "cpu_periodic", - stage = "build_report", - error = %e, - "Profiling capture failed" - ), - } - } - }); - } - - // Internal: start periodic memory dump when jemalloc profiling is active - #[cfg(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64"))] - async fn start_memory_periodic(interval: Duration, token: CancellationToken) { - info!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_MEMORY, - event = "profiling_state", - profile_type = "memory_periodic", - state = "started", - ?interval, - "Periodic memory profiling started" - ); - tokio::spawn(async move { - loop { - tokio::select! { - _ = token.cancelled() => { - info!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_MEMORY, - event = "profiling_state", - profile_type = "memory_periodic", - state = "cancelled", - "Periodic memory profiling cancelled" - ); - break; - } - _ = sleep(interval) => {} - } - - let Some(lock) = jemalloc_pprof::PROF_CTL.as_ref() else { - debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_MEMORY, - event = "profiling_dump_skipped", - profile_type = "memory_periodic", - reason = "prof_ctl_unavailable", - "Profiling dump skipped" - ); - continue; - }; - - let mut ctl = lock.lock().await; - if !ctl.activated() { - debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_MEMORY, - event = "profiling_dump_skipped", - profile_type = "memory_periodic", - reason = "jemalloc_inactive", - "Profiling dump skipped" - ); - continue; - } - let out = output_dir().join(format!("mem_profile_periodic_{}.pb", ts())); - match File::create(&out) { - Err(e) => { - tracing::error!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_MEMORY, - event = "profiling_dump_failed", - profile_type = "memory_periodic", - stage = "create_file", - path = %out.display(), - error = %e, - "Periodic memory dump file creation failed" - ); - continue; - } - Ok(mut f) => match ctl.dump_pprof() { - Ok(bytes) => { - if let Err(e) = f.write_all(&bytes) { - tracing::error!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_MEMORY, - event = "profiling_dump_failed", - profile_type = "memory_periodic", - stage = "write_dump", - path = %out.display(), - error = %e, - "Periodic memory dump write failed" - ); - } else { - debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_MEMORY, - event = "profiling_dump_exported", - profile_type = "memory_periodic", - path = %out.display(), - "Profiling dump exported" - ); - } - } - Err(e) => tracing::error!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_MEMORY, - event = "profiling_dump_failed", - profile_type = "memory_periodic", - stage = "dump_pprof", - error = %e, - "Periodic memory dump export failed" - ), - }, - } - } - }); - } - - #[cfg(not(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] - async fn start_memory_periodic(_interval: Duration, _token: CancellationToken) { - debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_MEMORY, - event = "profiling_runtime_skipped", - profile_type = "memory_periodic", - reason = "unsupported_target", - "Profiling runtime skipped" - ); - } - - // Public: unified init entry, avoid duplication/conflict - pub async fn init_from_env() { - let enabled = get_env_bool(ENV_ENABLE_PROFILING, DEFAULT_ENABLE_PROFILING); - if !enabled { - debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_RUNTIME, - event = "profiling_runtime_disabled", - reason = "env_flag", - "Profiling runtime disabled" - ); - return; - } - - // Jemalloc state check once (no dump) - check_jemalloc_profiling().await; - - // Initialize cancellation token - let token = PROFILING_CANCEL_TOKEN.get_or_init(CancellationToken::new).clone(); - - // CPU - let cpu_mode = read_cpu_mode(); - let cpu_freq = get_env_usize(ENV_CPU_FREQ, DEFAULT_CPU_FREQ) as i32; - let cpu_interval = Duration::from_secs(get_env_u64(ENV_CPU_INTERVAL_SECS, DEFAULT_CPU_INTERVAL_SECS)); - let cpu_duration = Duration::from_secs(get_env_u64(ENV_CPU_DURATION_SECS, DEFAULT_CPU_DURATION_SECS)); - - match cpu_mode { - CpuMode::Off => debug!( - component = LOG_COMPONENT_PROFILING, - subsystem = LOG_SUBSYSTEM_CPU, - event = "profiling_mode_selected", - profile_type = "cpu", - state = "off", - "Profiling mode selected" - ), - CpuMode::Continuous => start_cpu_continuous(cpu_freq).await, - CpuMode::Periodic => start_cpu_periodic(cpu_freq, cpu_interval, cpu_duration, token.clone()).await, - } - - // Memory - let mem_periodic = get_env_bool(ENV_MEM_PERIODIC, DEFAULT_MEM_PERIODIC); - let mem_interval = Duration::from_secs(get_env_u64(ENV_MEM_INTERVAL_SECS, DEFAULT_MEM_INTERVAL_SECS)); - if mem_periodic { - start_memory_periodic(mem_interval, token).await; - } - } - - #[cfg(not(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")))] - fn memory_profiling_unsupported_message() -> String { - let target_env = option_env!("CARGO_CFG_TARGET_ENV").unwrap_or("unknown"); - format!( - "Memory profiling is only supported on linux x86_64 gnu. target_os={}, target_env={target_env}, target_arch={}", - std::env::consts::OS, - std::env::consts::ARCH - ) - } - - /// Stop all background profiling tasks - pub fn shutdown_profiling() { - if let Some(token) = PROFILING_CANCEL_TOKEN.get() { - token.cancel(); - } - } +pub fn shutdown_profiling() { + debug!( + component = LOG_COMPONENT_PROFILING, + subsystem = LOG_SUBSYSTEM_RUNTIME, + event = "profiling_shutdown_skipped", + reason = LOCAL_CPU_PPROF_UNSUPPORTED_REASON, + target_os = std::env::consts::OS, + target_env = target_env(), + target_arch = std::env::consts::ARCH, + "Local pprof profiling shutdown skipped" + ); } -#[cfg(any(target_os = "linux", target_os = "macos"))] -pub use linux_impl::{dump_cpu_pprof_for, dump_memory_pprof_now, init_from_env, shutdown_profiling}; +pub fn log_cpu_pprof_dump_skipped() { + debug!( + component = LOG_COMPONENT_PROFILING, + subsystem = LOG_SUBSYSTEM_CPU, + event = "profiling_dump_skipped", + profile_type = "cpu", + reason = LOCAL_CPU_PPROF_UNSUPPORTED_REASON, + "Local CPU pprof dump skipped" + ); +} + +pub fn log_memory_pprof_dump_skipped() { + debug!( + component = LOG_COMPONENT_PROFILING, + subsystem = LOG_SUBSYSTEM_MEMORY, + event = "profiling_dump_skipped", + profile_type = "memory", + reason = MEMORY_PPROF_UNSUPPORTED_REASON, + "Memory pprof dump skipped" + ); +} + +pub fn local_cpu_pprof_unsupported_message() -> String { + unsupported_message(LOCAL_CPU_PPROF_UNSUPPORTED_SUMMARY) +} + +pub fn memory_pprof_unsupported_message() -> String { + unsupported_message(MEMORY_PPROF_UNSUPPORTED_SUMMARY) +} + +fn unsupported_message(summary: &str) -> String { + format!( + "{summary}. target_os={}, target_env={}, target_arch={}", + std::env::consts::OS, + target_env(), + std::env::consts::ARCH + ) +} + +fn target_env() -> &'static str { + option_env!("CARGO_CFG_TARGET_ENV").unwrap_or("unknown") +} diff --git a/rustfs/src/runtime_capabilities.rs b/rustfs/src/runtime_capabilities.rs index 446ef2ecd..dd0fc9987 100644 --- a/rustfs/src/runtime_capabilities.rs +++ b/rustfs/src/runtime_capabilities.rs @@ -143,11 +143,7 @@ fn target_env_name() -> Option<&'static str> { } fn cpu_profiling_status() -> CapabilityStatus { - if cfg!(any(target_os = "linux", target_os = "macos")) { - CapabilityStatus::supported() - } else { - CapabilityStatus::unsupported().with_reason("userspace CPU profiling supports linux and macos targets") - } + CapabilityStatus::unsupported().with_reason(crate::profiling::LOCAL_CPU_PPROF_UNSUPPORTED_SUMMARY) } fn ebpf_status() -> CapabilityStatus { @@ -167,11 +163,7 @@ fn numa_status() -> CapabilityStatus { } fn memory_profiling_status() -> CapabilityStatus { - if cfg!(all(target_os = "linux", target_env = "gnu", target_arch = "x86_64")) { - CapabilityStatus::supported().with_reason("jemalloc memory profiling target") - } else { - CapabilityStatus::unsupported().with_reason("memory profiling supports linux gnu x86_64 targets") - } + CapabilityStatus::unsupported().with_reason(crate::profiling::MEMORY_PPROF_UNSUPPORTED_SUMMARY) } fn cgroup_memory_status() -> CapabilityStatus {