diff --git a/Cargo.lock b/Cargo.lock index 7783381d5..3ce5d57d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy 0.7.35", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -135,6 +147,29 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "aws-lc-rs" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b756939cb2f8dc900aa6dcd505e6e2428e9cae7ff7b028c49e3946efa70878" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f7720b74ed28ca77f90769a71fd8c637a0137f6fae4ae947e1050229cff57f" +dependencies = [ + "bindgen 0.69.5", + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -156,6 +191,29 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.9.0", + "cexpr", + "clang-sys", + "itertools 0.12.1", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + [[package]] name = "bindgen" version = "0.70.1" @@ -214,9 +272,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "serde", @@ -273,7 +331,7 @@ checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" dependencies = [ "clap", "heck 0.4.1", - "indexmap 2.8.0", + "indexmap 2.9.0", "log", "proc-macro2", "quote", @@ -370,18 +428,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.32" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83" +checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.32" +version = "4.5.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8" +checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" dependencies = [ "anstream", "anstyle", @@ -395,12 +453,31 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -547,23 +624,23 @@ dependencies = [ [[package]] name = "dirs" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -577,12 +654,24 @@ dependencies = [ "syn", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "env_filter" version = "0.1.3" @@ -614,9 +703,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -644,6 +733,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -653,6 +748,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.31" @@ -789,14 +890,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.6", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -807,9 +908,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "git2" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fda788993cc341f69012feba8bf45c0ba4f3291fcc08e214b4d5a7332d88aff" +checksum = "5220b8ba44c68a9a7f7a7659e864dd73692e417ef0211bea133c7b74e031eeb9" dependencies = [ "bitflags 2.9.0", "libc", @@ -850,9 +951,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +checksum = "75249d144030531f8dee69fe9cea04d3edf809a017ae445e2abdff6629e86633" dependencies = [ "atomic-waker", "bytes", @@ -860,7 +961,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.8.0", + "indexmap 2.9.0", "slab", "tokio", "tokio-util", @@ -869,9 +970,9 @@ dependencies = [ [[package]] name = "half" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -888,6 +989,9 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] [[package]] name = "heck" @@ -913,6 +1017,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "http" version = "1.3.1" @@ -953,6 +1066,12 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.6.0" @@ -966,6 +1085,7 @@ dependencies = [ "http", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -973,6 +1093,24 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-timeout" version = "0.5.2" @@ -988,9 +1126,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" dependencies = [ "bytes", "futures-channel", @@ -998,6 +1136,7 @@ dependencies = [ "http", "http-body", "hyper", + "libc", "pin-project-lite", "socket2", "tokio", @@ -1067,11 +1206,13 @@ dependencies = [ "lazy_static", "libc", "log", + "metrics", + "metrics-exporter-prometheus", + "metrics-util", "mshv-bindings 0.2.1", "mshv-bindings 0.3.2", "mshv-ioctls 0.2.1", "mshv-ioctls 0.3.2", - "once_cell", "opentelemetry", "opentelemetry-otlp", "opentelemetry-semantic-conventions", @@ -1079,7 +1220,6 @@ dependencies = [ "page_size", "paste", "proc-maps", - "prometheus", "proptest", "rand 0.9.0", "rust-embed", @@ -1090,7 +1230,6 @@ dependencies = [ "serial_test", "sha256", "signal-hook-registry", - "strum", "tempfile", "termcolor", "thiserror 2.0.12", @@ -1138,16 +1277,17 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.61" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows-core 0.52.0", + "windows-core", ] [[package]] @@ -1200,9 +1340,9 @@ dependencies = [ [[package]] name = "icu_locid_transform_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" [[package]] name = "icu_normalizer" @@ -1224,9 +1364,9 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" [[package]] name = "icu_properties" @@ -1245,9 +1385,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" [[package]] name = "icu_provider" @@ -1310,9 +1450,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -1350,6 +1490,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -1376,9 +1525,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.4" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d699bc6dfc879fb1bf9bdff0d4c56f0884fc6f0d0eb0fba397a6d00cd9a6b85e" +checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0" dependencies = [ "jiff-static", "log", @@ -1389,9 +1538,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.4" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d16e75759ee0aa64c57a56acbf43916987b20c77373cb7e808979e02b93c9f9" +checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605" dependencies = [ "proc-macro2", "quote", @@ -1400,10 +1549,11 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ + "getrandom 0.3.2", "libc", ] @@ -1444,6 +1594,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.172" @@ -1462,9 +1618,9 @@ dependencies = [ [[package]] name = "libgit2-sys" -version = "0.18.0+1.9.0" +version = "0.18.1+1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1a117465e7e1597e8febea8bb0c410f1c7fb93b1e1cddf34363f8390367ffec" +checksum = "e1dcb20f84ffcdd825c7a311ae347cce604a6f084a767dec4a4929829645290e" dependencies = [ "cc", "libc", @@ -1488,7 +1644,7 @@ version = "0.14.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78a09b56be5adbcad5aa1197371688dc6bb249a26da3bca2011ee2fb987ebfb" dependencies = [ - "bindgen", + "bindgen 0.70.1", "errno", "libc", ] @@ -1517,9 +1673,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" @@ -1573,6 +1735,57 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "metrics" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a7deb012b3b2767169ff203fadb4c6b0b82b947512e5eb9e0b78c2e186ad9e3" +dependencies = [ + "ahash", + "portable-atomic", +] + +[[package]] +name = "metrics-exporter-prometheus" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7399781913e5393588a8d8c6a2867bf85fb38eaf2502fdce465aad2dc6f034" +dependencies = [ + "base64", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "indexmap 2.9.0", + "ipnet", + "metrics", + "metrics-util", + "quanta", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "metrics-util" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd4884b1dd24f7d6628274a2f5ae22465c337c5ba065ec9b6edccddf8acc673" +dependencies = [ + "aho-corasick", + "crossbeam-epoch", + "crossbeam-utils", + "hashbrown 0.15.2", + "indexmap 2.9.0", + "metrics", + "ordered-float", + "quanta", + "radix_trie", + "rand 0.8.5", + "rand_xoshiro", + "sketches-ddsketch", +] + [[package]] name = "mime" version = "0.3.17" @@ -1587,9 +1800,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -1626,7 +1839,7 @@ dependencies = [ "libc", "num_enum", "vmm-sys-util 0.13.0", - "zerocopy 0.8.23", + "zerocopy 0.8.24", ] [[package]] @@ -1653,6 +1866,15 @@ dependencies = [ "vmm-sys-util 0.13.0", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + [[package]] name = "nom" version = "7.1.3" @@ -1723,6 +1945,12 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + [[package]] name = "opentelemetry" version = "0.29.1" @@ -1815,6 +2043,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + [[package]] name = "overload" version = "0.1.1" @@ -1959,14 +2196,14 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.23", + "zerocopy 0.8.24", ] [[package]] name = "prettyplease" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", "syn", @@ -1988,28 +2225,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3db44c5aa60e193a25fcd93bb9ed27423827e8f118897866f946e2cf936c44fb" dependencies = [ "anyhow", - "bindgen", + "bindgen 0.70.1", "libc", "libproc", "mach2", "winapi", ] -[[package]] -name = "prometheus" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca5326d8d0b950a9acd87e6a3f94745394f62e4dae1b1ee22b2bc0c394af43a" -dependencies = [ - "cfg-if", - "fnv", - "lazy_static", - "memchr", - "parking_lot", - "protobuf", - "thiserror 2.0.12", -] - [[package]] name = "proptest" version = "1.6.0" @@ -2054,23 +2276,18 @@ dependencies = [ ] [[package]] -name = "protobuf" -version = "3.7.2" +name = "quanta" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" +checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" dependencies = [ + "crossbeam-utils", + "libc", "once_cell", - "protobuf-support", - "thiserror 1.0.69", -] - -[[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", + "raw-cpuid", + "wasi 0.11.0+wasi-snapshot-preview1", + "web-sys", + "winapi", ] [[package]] @@ -2088,6 +2305,22 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -2107,7 +2340,7 @@ checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.23", + "zerocopy 0.8.24", ] [[package]] @@ -2145,7 +2378,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.2", ] [[package]] @@ -2157,6 +2390,24 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_xoshiro" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "raw-cpuid" +version = "11.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df7ab838ed27997ba19a4664507e6f82b41fe6e20be42929332156e5e85146" +dependencies = [ + "bitflags 2.9.0", +] + [[package]] name = "rayon" version = "1.10.0" @@ -2179,22 +2430,22 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ "bitflags 2.9.0", ] [[package]] name = "redox_users" -version = "0.4.6" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror 1.0.69", + "thiserror 2.0.12", ] [[package]] @@ -2243,9 +2494,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.14" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e327e510263980e231de548a33e63d34962d29ae61b467389a1a09627a254" +checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" dependencies = [ "base64", "bytes", @@ -2278,6 +2529,20 @@ dependencies = [ "windows-registry", ] +[[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.15", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rust-embed" version = "8.7.0" @@ -2337,17 +2602,74 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.2" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags 2.9.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.15", "windows-sys 0.59.0", ] +[[package]] +name = "rustix" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +dependencies = [ + "bitflags 2.9.0", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df51b5869f3a441595eac5e8ff14d486ff285f7b8c0df8770e49c3b56351f0f0" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + +[[package]] +name = "rustls-webpki" +version = "0.103.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.20" @@ -2390,6 +2712,15 @@ dependencies = [ "sdd", ] +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2407,9 +2738,9 @@ dependencies = [ [[package]] name = "scroll_derive" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" +checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" dependencies = [ "proc-macro2", "quote", @@ -2431,6 +2762,29 @@ dependencies = [ "libc", ] +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.9.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.26" @@ -2496,7 +2850,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.9.0", "itoa", "ryu", "serde", @@ -2563,9 +2917,9 @@ dependencies = [ [[package]] name = "shellexpand" -version = "3.1.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" dependencies = [ "dirs", ] @@ -2585,6 +2939,12 @@ dependencies = [ "libc", ] +[[package]] +name = "sketches-ddsketch" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e9a774a6c28142ac54bb25d25562e6bcf957493a184f15ad4eebccb23e410a" + [[package]] name = "slab" version = "0.4.9" @@ -2596,15 +2956,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "socket2" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2662,6 +3022,12 @@ dependencies = [ "syn", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.100" @@ -2700,9 +3066,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" dependencies = [ "fastrand", - "getrandom 0.3.1", + "getrandom 0.3.2", "once_cell", - "rustix", + "rustix 1.0.5", "windows-sys 0.59.0", ] @@ -2814,6 +3180,16 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.17" @@ -2865,7 +3241,7 @@ version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -3091,6 +3467,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.4" @@ -3126,7 +3508,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.3.1", + "getrandom 0.3.2", "serde", ] @@ -3204,9 +3586,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -3302,6 +3684,18 @@ dependencies = [ "wasm-bindgen", ] +[[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 = "winapi" version = "0.3.9" @@ -3340,7 +3734,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419" dependencies = [ "windows-collections", - "windows-core 0.61.0", + "windows-core", "windows-future", "windows-link", "windows-numerics", @@ -3352,16 +3746,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.0", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", + "windows-core", ] [[package]] @@ -3383,7 +3768,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] @@ -3421,7 +3806,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.0", + "windows-core", "windows-link", ] @@ -3463,15 +3848,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -3490,21 +3866,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -3546,12 +3907,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -3564,12 +3919,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -3582,12 +3931,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3612,12 +3955,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -3630,12 +3967,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -3648,12 +3979,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -3666,12 +3991,6 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3686,18 +4005,18 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags 2.9.0", ] @@ -3750,11 +4069,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" dependencies = [ - "zerocopy-derive 0.8.23", + "zerocopy-derive 0.8.24", ] [[package]] @@ -3770,9 +4089,9 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.8.23" +version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" dependencies = [ "proc-macro2", "quote", @@ -3800,6 +4119,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + [[package]] name = "zerovec" version = "0.10.4" diff --git a/Justfile b/Justfile index 8b02d63d2..5b0209ad4 100644 --- a/Justfile +++ b/Justfile @@ -89,12 +89,11 @@ test-rust target=default-target features="": (test-rust-int "rust" target featur # ignored tests - these tests need to run serially or with specific properties cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} test_trace -p hyperlight-host --lib -- --ignored cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} test_drop -p hyperlight-host --lib -- --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} hypervisor::metrics::tests::test_gather_metrics -p hyperlight-host --lib -- --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} sandbox::metrics::tests::test_gather_metrics -p hyperlight-host --lib -- --ignored - cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} test_metrics -p hyperlight-host --lib -- --ignored cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} --test integration_test log_message -- --ignored cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} sandbox::uninitialized::tests::test_log_trace -p hyperlight-host --lib -- --ignored cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} hypervisor::hypervisor_handler::tests::create_1000_sandboxes -p hyperlight-host --lib -- --ignored + cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- metrics::tests::test_metrics_are_emitted --exact --ignored + cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F function_call_metrics," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} -p hyperlight-host --lib -- metrics::tests::test_metrics_are_emitted --exact --ignored {{ set-trace-env-vars }} cargo test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} --lib sandbox::outb::tests::test_log_outb_log -- --ignored test-seccomp target=default-target features="": diff --git a/docs/hyperlight-metrics-logs-and-traces.md b/docs/hyperlight-metrics-logs-and-traces.md index 700b8117e..7427962c5 100644 --- a/docs/hyperlight-metrics-logs-and-traces.md +++ b/docs/hyperlight-metrics-logs-and-traces.md @@ -2,46 +2,31 @@ Hyperlight provides the following observability features: -* [Metrics](#metrics) are provided using Prometheus. +* [Metrics](#metrics) are provided using the [metrics](https://docs.rs/metrics/latest/metrics/index.html) crate, which is a lightweight metrics facade. * [Logs](#logs) are provided using the Rust [log crate](https://docs.rs/log/0.4.6/log/), and can be consumed by any Rust logger implementation, including LogTracer which can be used to emit log records as tracing events. * [Tracing](#tracing) is provided using the Rust [tracing crate](https://docs.rs/tracing/0.1.37/tracing/), and can be consumed by any Rust tracing implementation. In addition, the [log feature](https://docs.rs/tracing/latest/tracing/#crate-feature-flags) is enabled which means that should a hyperlight host application not want to consume tracing events, you can still consume them as logs. ## Metrics -Hyperlight provides metrics using Prometheus. The metrics are registered using either the [default_registry](https://docs.rs/prometheus/latest/prometheus/fn.default_registry.html) or a registry instance provided by the host application. +Metrics are provided using the [metrics](https://docs.rs/metrics/latest/metrics/index.html) crate, which is a lightweight metrics facade. When an executable installs a [recorder](https://docs.rs/metrics/latest/metrics/trait.Recorder.html), Hyperlight will emit its metrics to that recorder, which allows library authors to seamless emit their own metrics without knowing or caring which exporter implementation is chosen, or even if one is installed. In case where no recorder is installed, the metrics will be emitted to the default recorder, which is a no-op implementation with minimal overhead. -To provide a registry to Hyperlight, use the `set_metrics_registry` function and pass a reference to a registry with `static` lifetime: - -```rust -use hyperlight_host::metrics::set_metrics_registry; -use prometheus::Registry; -use lazy_static::lazy_static; - -lazy_static! { - static ref REGISTRY: Registry = Registry::new(); -} - -set_metrics_registry(®ISTRY); -``` +There are many different implementations of recorders. One example is the [prometheus exporter](https://docs.rs/metrics-exporter-prometheus/latest/metrics_exporter_prometheus/) which can be used to export metrics to a Prometheus server. An example of how to use this is provided in the [examples/metrics](../src/hyperlight_host/examples/metrics) directory. The following metrics are provided and are enabled by default: -* `hyperlight_guest_error_count` - a vector of counters that tracks the number of guest errors by code and message. -* `hyperlight_number_of_cancelled_guest_execution` - a counter that tracks the number of guest executions that have been cancelled because the execution time exceeded the time allowed. +* `guest_errors_total` - Counter that tracks the number of guest errors by error code. +* `guest_cancellations_total` - Counter that tracks the number of guest executions that have been cancelled because the execution time exceeded the time allowed. -The following metrics are provided but are disabled by default and require the feature `function_call_metrics` to be enabled: +The following metrics are provided but are disabled by default: -* `hyperlight_guest_function_call_duration_microseconds` - a vector of histograms that tracks the execution time of guest functions in microseconds by function name. The histogram also tracks the number of calls to each function. -* `hyperlight_host_function_calls_duration_microseconds` - a vector of histograms that tracks the execution time of host functions in microseconds by function name. The histogram also tracks the number of calls to each function. +* `guest_call_duration_seconds` - Histogram that tracks the execution time of guest functions in seconds by function name. The histogram also tracks the number of calls to each function. +* `host_call_duration_seconds` - Histogram that tracks the execution time of host functions in seconds by function name. The histogram also tracks the number of calls to each function. The rationale for disabling the function call metrics by default is that: - * A Hyperlight host may wish to provide its own metrics for function calls. * Enabling a trace subscriber will cause the function call metrics to be emitted as trace events, which may be sufficient for some use cases. - -There is an example of how to gather metrics in the [examples/metrics](../src/hyperlight_host/examples/metrics) directory. - -The metrics capabilities provided by Hyperlight can also be used by a library or host that is using Hyperlight to provide additional metrics, see the [hypervisor metrics module](../src/hyperlight_host/src/hypervisor/metrics.rs) for an example of how to define metrics. +* These 2 metrics require string clones for the function names, which may be too expensive for some use cases. +We might consider enabling these metrics by default in the future. ## Logs diff --git a/src/hyperlight_common/src/flatbuffer_wrappers/guest_error.rs b/src/hyperlight_common/src/flatbuffer_wrappers/guest_error.rs index 00857af21..e39586af1 100644 --- a/src/hyperlight_common/src/flatbuffer_wrappers/guest_error.rs +++ b/src/hyperlight_common/src/flatbuffer_wrappers/guest_error.rs @@ -28,7 +28,7 @@ use crate::flatbuffers::hyperlight::generated::{ ErrorCode as FbErrorCode, GuestError as FbGuestError, GuestErrorArgs, }; -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[repr(C)] /// `ErrorCode` represents an error that occurred in the Hyperlight Guest. pub enum ErrorCode { @@ -222,7 +222,7 @@ impl TryFrom<&GuestError> for Vec { let guest_error_fb = FbGuestError::create( &mut builder, &GuestErrorArgs { - code: value.code.clone().into(), + code: value.code.into(), message: Some(message), }, ); diff --git a/src/hyperlight_host/Cargo.toml b/src/hyperlight_host/Cargo.toml index 2dec9ad55..5750a66c4 100644 --- a/src/hyperlight_host/Cargo.toml +++ b/src/hyperlight_host/Cargo.toml @@ -34,7 +34,6 @@ lazy_static = "1.4.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" log = "0.4.27" -once_cell = { version = "1.21.3" } tracing = { version = "0.1.41", features = ["log"] } tracing-log = "0.2.0" tracing-core = "0.1.33" @@ -43,11 +42,10 @@ vmm-sys-util = "0.13.0" crossbeam = "0.8.0" crossbeam-channel = "0.5.15" thiserror = "2.0.12" -prometheus = "0.14.0" -strum = { version = "0.27", features = ["derive"] } tempfile = { version = "3.19", optional = true } serde_yaml = "0.9" anyhow = "1.0" +metrics = "0.24.1" [target.'cfg(windows)'.dependencies] windows = { version = "0.61", features = [ @@ -103,6 +101,8 @@ opentelemetry_sdk = { version = "0.29", features = ["rt-tokio"] } tokio = { version = "1.44.2", features = ["full"] } criterion = "0.5.1" tracing-chrome = "0.7.2" +metrics-util = "0.19.0" +metrics-exporter-prometheus = "0.16.2" [target.'cfg(windows)'.dev-dependencies] windows = { version = "0.61", features = [ diff --git a/src/hyperlight_host/examples/metrics/main.rs b/src/hyperlight_host/examples/metrics/main.rs index 36817b546..9fa814346 100644 --- a/src/hyperlight_host/examples/metrics/main.rs +++ b/src/hyperlight_host/examples/metrics/main.rs @@ -22,22 +22,29 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, Ret use hyperlight_host::sandbox::uninitialized::UninitializedSandbox; use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; use hyperlight_host::sandbox_state::transition::Noop; -use hyperlight_host::{set_metrics_registry, GuestBinary, MultiUseSandbox, Result}; +use hyperlight_host::{GuestBinary, MultiUseSandbox, Result}; use hyperlight_testing::simple_guest_as_string; -use lazy_static::lazy_static; -use prometheus::Registry; -lazy_static! { - static ref HOST_REGISTRY: Registry = Registry::new(); -} -fn fn_writer(_msg: String) -> Result { - Ok(0) -} +// Run this rust example with the flag --features "function_call_metrics" to enable more metrics to be emitted + +fn main() { + // Install prometheus metrics exporter. + // We only install the metrics recorder here, but you can also use the + // `metrics_exporter_prometheus::PrometheusBuilder::new().install()` method + // to install a HTTP listener that serves the metrics. + let prometheus_handle = metrics_exporter_prometheus::PrometheusBuilder::new() + .install_recorder() + .expect("Failed to install Prometheus exporter"); -fn main() -> Result<()> { - // If this is not called then the default registry `prometheus::default_registry` will be used. - set_metrics_registry(&HOST_REGISTRY)?; + // Do some hyperlight stuff to generate metrics. + do_hyperlight_stuff(); + // Get the metrics and print them in prometheus exposition format. + let payload = prometheus_handle.render(); + println!("Prometheus metrics:\n{}", payload); +} + +fn do_hyperlight_stuff() { // Get the path to a simple guest binary. let hyperlight_guest_path = simple_guest_as_string().expect("Cannot find the guest binary at the expected location."); @@ -60,7 +67,7 @@ fn main() -> Result<()> { let no_op = Noop::::default(); - let mut multiuse_sandbox = usandbox.evolve(no_op)?; + let mut multiuse_sandbox = usandbox.evolve(no_op).expect("Failed to evolve sandbox"); // Call a guest function 5 times to generate some metrics. for _ in 0..5 { @@ -97,13 +104,14 @@ fn main() -> Result<()> { None, None, None, - )?; + ) + .expect("Failed to create UninitializedSandbox"); // Initialize the sandbox. let no_op = Noop::::default(); - let mut multiuse_sandbox = usandbox.evolve(no_op)?; + let mut multiuse_sandbox = usandbox.evolve(no_op).expect("Failed to evolve sandbox"); // Call a function that gets cancelled by the host function 5 times to generate some metrics. @@ -121,73 +129,8 @@ fn main() -> Result<()> { let result = join_handle.join(); assert!(result.is_ok()); } - - get_metrics(); - - Ok(()) } -fn get_metrics() { - // Get the metrics from the registry. - - let metrics = HOST_REGISTRY.gather(); - - // Print the metrics. - - print!("\nMETRICS:\n"); - - for metric in metrics.iter() { - match metric.get_field_type() { - prometheus::proto::MetricType::COUNTER => { - println!("Counter: {:?}", metric.help()); - metric.get_metric().iter().for_each(|metric| { - let pair = metric.get_label(); - for pair in pair.iter() { - println!("Label: {:?} Name: {:?}", pair.name(), pair.value()); - } - println!("Value: {:?}", metric.get_counter().value()); - }); - } - prometheus::proto::MetricType::GAUGE => { - println!("Gauge: {:?}", metric.help()); - metric.get_metric().iter().for_each(|metric| { - let pair = metric.get_label(); - for pair in pair.iter() { - println!("Label: {:?} Name: {:?}", pair.name(), pair.value()); - } - println!("Value: {:?}", metric.get_gauge().value()); - }); - } - prometheus::proto::MetricType::UNTYPED => { - println!("Metric: {:?}", metric.help()); - } - prometheus::proto::MetricType::HISTOGRAM => { - println!("Histogram: {:?}", metric.help()); - for metric in metric.get_metric() { - let pair = metric.get_label(); - for pair in pair.iter() { - println!("Label: {:?} Name: {:?}", pair.name(), pair.value()); - } - let count = metric.get_histogram().get_sample_count(); - println!("Number of observations: {:?}", count); - let sm = metric.get_histogram().get_sample_sum(); - println!("Sum of observations: {:?}", sm); - metric - .get_histogram() - .get_bucket() - .iter() - .for_each(|bucket| { - println!( - "Bucket: {:?} Count: {:?}", - bucket.upper_bound(), - bucket.cumulative_count() - ) - }); - } - } - prometheus::proto::MetricType::SUMMARY => { - println!("Summary: {:?}", metric.help()); - } - } - } +fn fn_writer(_msg: String) -> Result { + Ok(0) } diff --git a/src/hyperlight_host/src/error.rs b/src/hyperlight_host/src/error.rs index f2cde8626..b2ea080ce 100644 --- a/src/hyperlight_host/src/error.rs +++ b/src/hyperlight_host/src/error.rs @@ -237,10 +237,6 @@ pub enum HyperlightError { #[error("Failure processing PE File {0:?}")] PEFileProcessingFailure(#[from] goblin::error::Error), - /// a Prometheus error occurred - #[error("Prometheus Error {0:?}")] - Prometheus(#[from] prometheus::Error), - /// Raw pointer is less than base address #[error("Raw pointer ({0:?}) was less than the base address ({1})")] RawPointerLessThanBaseAddress(RawPtr, u64), diff --git a/src/hyperlight_host/src/func/guest_err.rs b/src/hyperlight_host/src/func/guest_err.rs index 58baa8c56..9e67ef536 100644 --- a/src/hyperlight_host/src/func/guest_err.rs +++ b/src/hyperlight_host/src/func/guest_err.rs @@ -14,15 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -use hyperlight_common::flatbuffer_wrappers::guest_error::{ - ErrorCode, GuestError as GuestErrorStruct, -}; +use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use crate::error::HyperlightError::{GuestError, OutBHandlingError, StackOverflow}; use crate::mem::shared_mem::HostSharedMemory; +use crate::metrics::{METRIC_GUEST_ERROR, METRIC_GUEST_ERROR_LABEL_CODE}; use crate::sandbox::mem_mgr::MemMgrWrapper; -use crate::sandbox::metrics::SandboxMetric::GuestErrorCount; -use crate::{int_counter_vec_inc, log_then_return, Result}; +use crate::{log_then_return, Result}; /// Check for a guest error and return an `Err` if one was found, /// and `Ok` if one was not found. pub(crate) fn check_for_guest_error(mgr: &MemMgrWrapper) -> Result<()> { @@ -31,7 +29,8 @@ pub(crate) fn check_for_guest_error(mgr: &MemMgrWrapper) -> Re ErrorCode::NoError => Ok(()), ErrorCode::OutbError => match mgr.as_ref().get_host_error()? { Some(host_err) => { - increment_guest_error_count(&guest_err); + metrics::counter!(METRIC_GUEST_ERROR, METRIC_GUEST_ERROR_LABEL_CODE => (guest_err.code as u64).to_string()).increment(1); + log_then_return!(OutBHandlingError( host_err.source.clone(), guest_err.message.clone() @@ -41,23 +40,12 @@ pub(crate) fn check_for_guest_error(mgr: &MemMgrWrapper) -> Re None => Ok(()), }, ErrorCode::StackOverflow => { - increment_guest_error_count(&guest_err.clone()); + metrics::counter!(METRIC_GUEST_ERROR, METRIC_GUEST_ERROR_LABEL_CODE => (guest_err.code as u64).to_string()).increment(1); log_then_return!(StackOverflow()); } _ => { - increment_guest_error_count(&guest_err.clone()); - log_then_return!(GuestError( - guest_err.code.clone(), - guest_err.message.clone() - )); + metrics::counter!(METRIC_GUEST_ERROR, METRIC_GUEST_ERROR_LABEL_CODE => (guest_err.code as u64).to_string()).increment(1); + log_then_return!(GuestError(guest_err.code, guest_err.message.clone())); } } } - -fn increment_guest_error_count(guest_err: &GuestErrorStruct) { - let guest_err_code_string: String = guest_err.code.clone().into(); - int_counter_vec_inc!( - &GuestErrorCount, - &[&guest_err_code_string, guest_err.message.clone().as_str()] - ); -} diff --git a/src/hyperlight_host/src/hypervisor/hypervisor_handler.rs b/src/hyperlight_host/src/hypervisor/hypervisor_handler.rs index 7b30d7c6e..591f43a07 100644 --- a/src/hyperlight_host/src/hypervisor/hypervisor_handler.rs +++ b/src/hyperlight_host/src/hypervisor/hypervisor_handler.rs @@ -37,8 +37,6 @@ use windows::Win32::System::Hypervisor::{WHvCancelRunVirtualProcessor, WHV_PARTI #[cfg(gdb)] use super::gdb::create_gdb_thread; -#[cfg(feature = "function_call_metrics")] -use crate::histogram_vec_observe; #[cfg(gdb)] use crate::hypervisor::handlers::DbgMemAccessHandlerWrapper; use crate::hypervisor::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper}; @@ -53,8 +51,6 @@ use crate::mem::shared_mem::{GuestSharedMemory, HostSharedMemory, SharedMemory}; #[cfg(gdb)] use crate::sandbox::config::DebugInfo; use crate::sandbox::hypervisor::{get_available_hypervisor, HypervisorType}; -#[cfg(feature = "function_call_metrics")] -use crate::sandbox::metrics::SandboxMetric::GuestFunctionCallDurationMicroseconds; #[cfg(target_os = "linux")] use crate::signal_handlers::setup_signal_handlers; use crate::HyperlightError::{ @@ -435,38 +431,22 @@ impl HypervisorHandler { })? .shared_mem .lock - .try_read(); + .try_read(); - let res = { - #[cfg(feature = "function_call_metrics")] - { - let start = std::time::Instant::now(); - let result = hv.dispatch_call_from_host( + let res = crate::metrics::maybe_time_and_emit_guest_call( + &function_name, + || { + hv.dispatch_call_from_host( dispatch_function_addr, configuration.outb_handler.clone(), configuration.mem_access_handler.clone(), Some(hv_handler_clone.clone()), #[cfg(gdb)] configuration.dbg_mem_access_handler.clone(), - ); - histogram_vec_observe!( - &GuestFunctionCallDurationMicroseconds, - &[function_name.as_str()], - start.elapsed().as_micros() as f64 - ); - result - } + ) + }, + ); - #[cfg(not(feature = "function_call_metrics"))] - hv.dispatch_call_from_host( - dispatch_function_addr, - configuration.outb_handler.clone(), - configuration.mem_access_handler.clone(), - Some(hv_handler_clone.clone()), - #[cfg(gdb)] - configuration.dbg_mem_access_handler.clone(), - ) - }; drop(mem_lock_guard); drop(evar_lock_guard); diff --git a/src/hyperlight_host/src/hypervisor/metrics.rs b/src/hyperlight_host/src/hypervisor/metrics.rs deleted file mode 100644 index a02d19ed0..000000000 --- a/src/hyperlight_host/src/hypervisor/metrics.rs +++ /dev/null @@ -1,165 +0,0 @@ -/* -Copyright 2024 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*! -This module contains the definitions and implementations of the metrics used by the hypervisor module -*/ -use std::collections::HashMap; -use std::sync::Once; - -use once_cell::sync::OnceCell; -use strum::{EnumIter, IntoStaticStr, VariantNames}; -use tracing::{instrument, Span}; - -use crate::metrics::{ - HyperlightMetric, HyperlightMetricDefinition, HyperlightMetricEnum, HyperlightMetricType, -}; - -// This is required to ensure that the metrics are only initialized once -static INIT_METRICS: Once = Once::new(); -// This contains a hashmap of all the metrics using metric names defined below as keys -static METRICS: OnceCell> = OnceCell::new(); - -// This is the definition of all the metrics used by the sandbox module -static HYPERVISOR_METRIC_DEFINITIONS: &[HyperlightMetricDefinition] = - &[HyperlightMetricDefinition { - name: "number_of_cancelled_guest_executions", - help: "Number of guest executions that have been cancelled", - metric_type: HyperlightMetricType::IntCounter, - labels: &[], - buckets: &[], - }]; - -/// There is an enum variant for each error metric in the module -/// the names of the variant take the form of CamelCase, but the metric names are snake_case -/// so for example, the enum variant CurrentNumberOfMultiUseSandboxes corresponds to the -/// metric name current_number_of_multi_use_sandboxes. -/// At runtime we location the correct metric by looking up the enum variant name in the hashmap -/// the conversion of the enum variant name to the metric name is done by the IntoStaticStr derive macro -/// along with the strum(serialize_all = "snake_case") attribute. -/// -/// This enum contains all the metrics used by the sandbox module -/// The enum is required to derive from EnumIter, EnumVariantNames, IntoStaticStr -/// and strum(serialize_all = "snake_case") performs the name conversion from CamelCase to snake_case -/// when the enum variant is serialized to a string -#[derive(Debug, EnumIter, VariantNames, IntoStaticStr)] -#[strum(serialize_all = "snake_case")] -pub(super) enum HypervisorMetric { - NumberOfCancelledGuestExecutions, -} - -// It is required for the enum to implement HyperlightMetricEnum -impl HyperlightMetricEnum for HypervisorMetric { - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn get_init_metrics() -> &'static Once { - &INIT_METRICS - } - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn get_metrics() -> &'static OnceCell> { - &METRICS - } - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn get_metric_definitions() -> &'static [HyperlightMetricDefinition] { - HYPERVISOR_METRIC_DEFINITIONS - } -} - -#[cfg(test)] -mod tests { - use lazy_static::lazy_static; - use prometheus::Registry; - use strum::{IntoEnumIterator, VariantNames}; - - use super::*; - use crate::metrics::get_metrics_registry; - use crate::metrics::tests::HyperlightMetricEnumTest; - use crate::{int_counter_get, int_counter_inc, int_counter_inc_by, int_counter_reset}; - impl HyperlightMetricEnumTest for HypervisorMetric { - fn get_enum_variant_names() -> &'static [&'static str] { - HypervisorMetric::VARIANTS - } - } - - #[test] - fn test_enum_has_variant_for_all_metrics() { - >::enum_has_variant_for_all_metrics(); - } - #[test] - fn test_metric_definitions() { - >::check_metric_definitions(); - } - - #[test] - #[ignore] - /// This test is ignored because as it uses real counters if it runs at the same time as other tests it may fail. - /// - /// Marking this test as ignored means that running `cargo test` will not - /// run it. This will allow a developer who runs that command - /// from their workstation to be successful without needing to know about - /// test interdependencies. This test will, however, be run explicitly as a - /// part of the CI pipeline. - fn test_metrics() { - let iter: HypervisorMetricIter = HypervisorMetric::iter(); - for sandbox_metric in iter { - match sandbox_metric.get_hyperlight_metric() { - Ok(hyperlight_metric) => match hyperlight_metric { - HyperlightMetric::IntCounter(int_counter) => { - let counter = - >::get_intcounter_metric(int_counter.name); - assert!(counter.is_ok()); - let counter = counter.unwrap(); - int_counter_reset!(&sandbox_metric); - assert_eq!(counter.get(), 0); - int_counter_inc!(&sandbox_metric); - assert_eq!(counter.get(), 1); - int_counter_inc_by!(&sandbox_metric, 5); - assert_eq!(counter.get(), 6); - int_counter_reset!(&sandbox_metric); - assert_eq!(counter.get(), 0); - let result = int_counter_get!(&sandbox_metric); - assert_eq!(result, 0); - } - _ => { - panic!("metric is not an IntCounter"); - } - }, - Err(e) => { - panic!("error getting metric: {}", e); - } - } - } - } - #[test] - #[ignore] - /// This test is ignored because it is requires that metrics and registry have not been set or initialised yet. - /// - /// Marking this test as ignored means that running `cargo test` will not - /// run it. This will allow a developer who runs that command - /// from their workstation to be successful without needing to know about - /// test interdependencies. This test will, however, be run explicitly as a - /// part of the CI pipeline. - fn test_gather_metrics() { - lazy_static! { - static ref REGISTRY: Registry = Registry::default(); - } - test_metrics(); - let registry = get_metrics_registry(); - let result = registry.gather(); - assert_eq!(result.len(), 1); - } -} diff --git a/src/hyperlight_host/src/hypervisor/mod.rs b/src/hyperlight_host/src/hypervisor/mod.rs index bec42f350..bf85d3956 100644 --- a/src/hyperlight_host/src/hypervisor/mod.rs +++ b/src/hyperlight_host/src/hypervisor/mod.rs @@ -18,9 +18,9 @@ use log::LevelFilter; use tracing::{instrument, Span}; use crate::error::HyperlightError::ExecutionCanceledByHost; -use crate::hypervisor::metrics::HypervisorMetric::NumberOfCancelledGuestExecutions; use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags}; -use crate::{int_counter_inc, log_then_return, new_error, HyperlightError, Result}; +use crate::metrics::METRIC_GUEST_CANCELLATION; +use crate::{log_then_return, new_error, HyperlightError, Result}; /// Util for handling x87 fpu state #[cfg(any(kvm, mshv, target_os = "windows"))] @@ -45,8 +45,6 @@ pub mod inprocess; #[cfg(kvm)] /// Functionality to manipulate KVM-based virtual machines pub mod kvm; -/// Metric definitions for Hypervisor module. -mod metrics; #[cfg(target_os = "windows")] /// Hyperlight Surrogate Process pub(crate) mod surrogate_process; @@ -313,7 +311,7 @@ impl VirtualCPU { #[cfg(target_os = "linux")] hvh.set_run_cancelled(true); } - int_counter_inc!(&NumberOfCancelledGuestExecutions); + metrics::counter!(METRIC_GUEST_CANCELLATION).increment(1); log_then_return!(ExecutionCanceledByHost()); } Ok(HyperlightExit::Unknown(reason)) => { diff --git a/src/hyperlight_host/src/lib.rs b/src/hyperlight_host/src/lib.rs index 1ae0b1801..4716eb00c 100644 --- a/src/hyperlight_host/src/lib.rs +++ b/src/hyperlight_host/src/lib.rs @@ -87,8 +87,6 @@ pub(crate) mod testing; /// The re-export for the `HyperlightError` type pub use error::HyperlightError; -/// The re-export for the set_registry function -pub use metrics::set_metrics_registry; /// The re-export for the `is_hypervisor_present` type pub use sandbox::is_hypervisor_present; /// The re-export for the `GuestBinary` type diff --git a/src/hyperlight_host/src/metrics/histogram.rs b/src/hyperlight_host/src/metrics/histogram.rs deleted file mode 100644 index 82ddefdfa..000000000 --- a/src/hyperlight_host/src/metrics/histogram.rs +++ /dev/null @@ -1,153 +0,0 @@ -/* -Copyright 2024 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use prometheus::{register_histogram_with_registry, Histogram as PHistogram}; -use tracing::{instrument, Span}; - -use super::{ - get_histogram_opts, get_metrics_registry, GetHyperlightMetric, HyperlightMetric, - HyperlightMetricOps, -}; -use crate::{new_error, HyperlightError, Result}; - -/// A named histogram -#[derive(Debug)] -pub struct Histogram { - histogram: PHistogram, - /// The name of the histogram - pub name: &'static str, -} - -impl Histogram { - /// Creates a new histogram and registers it with the metric registry - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn new(name: &'static str, help: &str, buckets: Vec) -> Result { - let registry = get_metrics_registry(); - let opts = get_histogram_opts(name, help, buckets); - let histogram = register_histogram_with_registry!(opts, registry)?; - Ok(Self { histogram, name }) - } - /// Observes a value for a Histogram - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn observe(&self, val: f64) { - self.histogram.observe(val) - } - /// Gets the sum of values of an Histogram - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn get_sample_sum(&self) -> f64 { - self.histogram.get_sample_sum() - } - /// Gets the count of values of an Histogram - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn get_sample_count(&self) -> u64 { - self.histogram.get_sample_count() - } -} - -impl GetHyperlightMetric for S { - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn metric(&self) -> Result<&Histogram> { - let metric = self.get_metric()?; - <&HyperlightMetric as TryInto<&Histogram>>::try_into(metric) - } -} - -impl<'a> TryFrom<&'a HyperlightMetric> for &'a Histogram { - type Error = HyperlightError; - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn try_from(metric: &'a HyperlightMetric) -> Result { - match metric { - HyperlightMetric::Histogram(histogram) => Ok(histogram), - _ => Err(new_error!("metric is not a Histogram")), - } - } -} - -impl From for HyperlightMetric { - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn from(histogram: Histogram) -> Self { - HyperlightMetric::Histogram(histogram) - } -} - -/// Observes a value for a Histogram -#[macro_export] -macro_rules! histogram_observe { - ($metric:expr, $val:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::Histogram>::metric($metric) { - Ok(val) => { - if let Err(e) = val.observe($val) { - log::error!("error calling observe with value {} on metric {} ", $val, e,) - } - } - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} - -/// Gets the sum of values of an Histogram or logs an error if the metric is not found -/// Returns 0.0 if the metric is not found -#[macro_export] -macro_rules! histogram_sample_sum { - ($metric:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::Histogram>::metric($metric) { - Ok(val) => match val.get_sample_sum() { - Ok(val) => val, - Err(e) => { - log::error!("error getting samples sum of metric {}", e,); - 0.0 - } - }, - - Err(e) => { - log::error!("error getting metric: {}", e); - 0.0 - } - } - }}; -} - -/// Gets the count of values of an Histogram or logs an error if the metric is not found -/// Returns 0 if the metric is not found -#[macro_export] -macro_rules! histogram_sample_count { - ($metric:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::Histogram>::metric($metric) { - Ok(val) => match val.get_sample_count() { - Ok(val) => val, - Err(e) => { - log::error!("error getting samples count of metric {}", e,); - 0 - } - }, - - Err(e) => { - log::error!("error getting metric: {}", e); - 0 - } - } - }}; -} -/// Observe the time it takes to execute an expression, record that time in microseconds in a Histogram and return the result of that expression -#[macro_export] -macro_rules! histogram_time_micros { - ($metric:expr, $expr:expr) => {{ - let start = std::time::Instant::now(); - let result = $expr; - histogram_observe!($metric, start.elapsed().as_micros() as f64); - result - }}; -} diff --git a/src/hyperlight_host/src/metrics/histogram_vec.rs b/src/hyperlight_host/src/metrics/histogram_vec.rs deleted file mode 100644 index 0c4ff9141..000000000 --- a/src/hyperlight_host/src/metrics/histogram_vec.rs +++ /dev/null @@ -1,183 +0,0 @@ -/* -Copyright 2024 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use prometheus::{register_histogram_vec_with_registry, HistogramVec as PHistogramVec}; -use tracing::{instrument, Span}; - -use super::{ - get_histogram_opts, get_metrics_registry, GetHyperlightMetric, HyperlightMetric, - HyperlightMetricOps, -}; -use crate::{new_error, HyperlightError, Result}; - -/// A named bundle of histograms -#[derive(Debug)] -pub struct HistogramVec { - histogram: PHistogramVec, - /// The name of the histogram vec - pub name: &'static str, -} - -impl HistogramVec { - /// Creates a new histogram vec and registers it with the metric registry - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn new(name: &'static str, help: &str, labels: &[&str], buckets: Vec) -> Result { - let registry = get_metrics_registry(); - let opts = get_histogram_opts(name, help, buckets); - let histogram = register_histogram_vec_with_registry!(opts, labels, registry)?; - Ok(Self { histogram, name }) - } - - /// Observes a value for a HistogramVec - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn observe(&self, label_vals: &[&str], val: f64) -> Result<()> { - self.histogram - .get_metric_with_label_values(label_vals)? - .observe(val); - Ok(()) - } - - /// Gets the sum of values of an HistogramVec - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn get_sample_sum(&self, label_vals: &[&str]) -> Result { - Ok(self - .histogram - .get_metric_with_label_values(label_vals)? - .get_sample_sum()) - } - - /// Gets the count of values of an HistogramVec - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn get_sample_count(&self, label_vals: &[&str]) -> Result { - Ok(self - .histogram - .get_metric_with_label_values(label_vals)? - .get_sample_count()) - } -} - -impl GetHyperlightMetric for S { - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn metric(&self) -> Result<&HistogramVec> { - let metric = self.get_metric()?; - <&HyperlightMetric as TryInto<&HistogramVec>>::try_into(metric) - } -} - -impl<'a> TryFrom<&'a HyperlightMetric> for &'a HistogramVec { - type Error = HyperlightError; - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn try_from(metric: &'a HyperlightMetric) -> Result { - match metric { - HyperlightMetric::HistogramVec(histogram) => Ok(histogram), - _ => Err(new_error!("metric is not a HistogramVec")), - } - } -} - -impl From for HyperlightMetric { - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn from(histogram_vec: HistogramVec) -> Self { - HyperlightMetric::HistogramVec(histogram_vec) - } -} - -/// Observes a value for a HistogramVec -#[macro_export] -macro_rules! histogram_vec_observe { - ($metric:expr, $label_vals:expr, $val:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::HistogramVec>::metric($metric) - { - Ok(val) => { - if let Err(e) = val.observe($label_vals, $val) { - log::error!( - "error calling observe with {} on metric with labels: {} {:?}", - $val, - e, - $label_vals - ) - } - } - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} - -/// Gets the sum of values of an HistogramVec or logs an error if the metric is not found -/// Returns 0.0 if the metric is not found -#[macro_export] -macro_rules! histogram_vec_sample_sum { - ($metric:expr, $label_vals:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::HistogramVec>::metric($metric) - { - Ok(val) => match val.get_sample_sum($label_vals) { - Ok(val) => val, - Err(e) => { - log::error!( - "error getting samples sum of metric with labels: {} {:?}", - e, - $label_vals - ); - 0.0 - } - }, - - Err(e) => { - log::error!("error getting metric: {}", e); - 0.0 - } - } - }}; -} - -/// Gets the count of values of an HistogramVec or logs an error if the metric is not found -/// Returns 0 if the metric is not found -#[macro_export] -macro_rules! histogram_vec_sample_count { - ($metric:expr, $label_vals:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::HistogramVec>::metric($metric) - { - Ok(val) => match val.get_sample_count($label_vals) { - Ok(val) => val, - Err(e) => { - log::error!( - "error getting samples count of metric with labels: {} {:?}", - e, - $label_vals - ); - 0 - } - }, - - Err(e) => { - log::error!("error getting metric: {}", e); - 0 - } - } - }}; -} - -/// Observe the time it takes to execute an expression, record that time in microseconds in a -/// `HistogramVec`, and return the result of that expression -#[macro_export] -macro_rules! histogram_vec_time_micros { - ($metric:expr, $label_vals:expr, $expr:expr) => {{ - let start = std::time::Instant::now(); - let result = $expr; - $crate::histogram_vec_observe!($metric, $label_vals, start.elapsed().as_micros() as f64); - result - }}; -} diff --git a/src/hyperlight_host/src/metrics/int_counter.rs b/src/hyperlight_host/src/metrics/int_counter.rs deleted file mode 100644 index b164f50ca..000000000 --- a/src/hyperlight_host/src/metrics/int_counter.rs +++ /dev/null @@ -1,137 +0,0 @@ -/* -Copyright 2024 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use prometheus::core::{AtomicU64, GenericCounter}; -use prometheus::register_int_counter_with_registry; -use tracing::{instrument, Span}; - -use super::{ - get_metric_opts, get_metrics_registry, GetHyperlightMetric, HyperlightMetric, - HyperlightMetricOps, -}; -use crate::{new_error, HyperlightError, Result}; - -/// A named counter backed by an `AtomicU64` -#[derive(Debug)] -pub struct IntCounter { - counter: GenericCounter, - /// The name of the counter - pub name: &'static str, -} - -impl IntCounter { - /// Creates a new counter and registers it with the metric registry - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn new(name: &'static str, help: &str) -> Result { - let registry = get_metrics_registry(); - let opts = get_metric_opts(name, help); - let counter = register_int_counter_with_registry!(opts, registry)?; - Ok(Self { counter, name }) - } - /// Increments a counter by 1 - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn inc(&self) { - self.counter.inc(); - } - /// Increments a counter by a value - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn inc_by(&self, val: u64) { - self.counter.inc_by(val); - } - /// Gets the value of a counter - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn get(&self) -> u64 { - self.counter.get() - } - /// Resets a counter - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn reset(&self) { - self.counter.reset(); - } -} - -impl GetHyperlightMetric for S { - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn metric(&self) -> Result<&IntCounter> { - let metric = self.get_metric()?; - <&HyperlightMetric as TryInto<&IntCounter>>::try_into(metric) - } -} - -impl<'a> TryFrom<&'a HyperlightMetric> for &'a IntCounter { - type Error = HyperlightError; - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn try_from(metric: &'a HyperlightMetric) -> Result { - match metric { - HyperlightMetric::IntCounter(counter) => Ok(counter), - _ => Err(new_error!("metric is not a IntCounter")), - } - } -} - -impl From for HyperlightMetric { - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn from(counter: IntCounter) -> Self { - HyperlightMetric::IntCounter(counter) - } -} - -/// Increments an IntCounter by 1 or logs an error if the metric is not found -#[macro_export] -macro_rules! int_counter_inc { - ($metric:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntCounter>::metric($metric) { - Ok(val) => val.inc(), - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} - -/// Increments an IntCounter by a given value or logs an error if the metric is not found -#[macro_export] -macro_rules! int_counter_inc_by { - ($metric:expr, $val:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntCounter>::metric($metric) { - Ok(val) => val.inc_by($val), - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} - -/// Gets the value of an IntCounter or logs an error if the metric is not found -#[macro_export] -macro_rules! int_counter_get { - ($metric:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntCounter>::metric($metric) { - Ok(val) => val.get(), - Err(e) => { - log::error!("error getting metric: {}", e); - 0 - } - } - }}; -} - -/// Resets an IntCounter or logs an error if the metric is not found -#[macro_export] -macro_rules! int_counter_reset { - ($metric:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntCounter>::metric($metric) { - Ok(val) => val.reset(), - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} diff --git a/src/hyperlight_host/src/metrics/int_counter_vec.rs b/src/hyperlight_host/src/metrics/int_counter_vec.rs deleted file mode 100644 index 84ed14050..000000000 --- a/src/hyperlight_host/src/metrics/int_counter_vec.rs +++ /dev/null @@ -1,185 +0,0 @@ -/* -Copyright 2024 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use prometheus::core::{AtomicU64, GenericCounterVec}; -use prometheus::register_int_counter_vec_with_registry; -use tracing::{instrument, Span}; - -use super::{ - get_metric_opts, get_metrics_registry, GetHyperlightMetric, HyperlightMetric, - HyperlightMetricOps, -}; -use crate::{new_error, HyperlightError, Result}; - -/// A 64-bit counter -#[derive(Debug)] -pub struct IntCounterVec { - counter: GenericCounterVec, - /// The name of the counter - pub name: &'static str, -} - -impl IntCounterVec { - /// Creates a new counter and registers it with the metric registry - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn new(name: &'static str, help: &str, labels: &[&str]) -> Result { - let registry = get_metrics_registry(); - let opts = get_metric_opts(name, help); - let counter = register_int_counter_vec_with_registry!(opts, labels, registry)?; - Ok(Self { counter, name }) - } - /// Increments a counter by 1 - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn inc(&self, label_vals: &[&str]) -> Result<()> { - self.counter.get_metric_with_label_values(label_vals)?.inc(); - Ok(()) - } - /// Increments a counter by a value - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn inc_by(&self, label_vals: &[&str], val: u64) -> Result<()> { - self.counter - .get_metric_with_label_values(label_vals)? - .inc_by(val); - Ok(()) - } - /// Gets the value of a counter - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn get(&self, label_vals: &[&str]) -> Result { - Ok(self.counter.get_metric_with_label_values(label_vals)?.get()) - } - /// Resets a counter - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn reset(&self, label_vals: &[&str]) -> Result<()> { - self.counter - .get_metric_with_label_values(label_vals)? - .reset(); - Ok(()) - } -} - -impl GetHyperlightMetric for S { - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn metric(&self) -> Result<&IntCounterVec> { - let metric = self.get_metric()?; - <&HyperlightMetric as TryInto<&IntCounterVec>>::try_into(metric) - } -} - -impl<'a> TryFrom<&'a HyperlightMetric> for &'a IntCounterVec { - type Error = HyperlightError; - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn try_from(metric: &'a HyperlightMetric) -> Result { - match metric { - HyperlightMetric::IntCounterVec(counter) => Ok(counter), - _ => Err(new_error!("metric is not a IntCounterVec")), - } - } -} - -impl From for HyperlightMetric { - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn from(counter: IntCounterVec) -> Self { - HyperlightMetric::IntCounterVec(counter) - } -} - -/// Increments an IntCounterVec by 1 or logs an error if the metric is not found -#[macro_export] -macro_rules! int_counter_vec_inc { - ($metric:expr, $label_vals:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntCounterVec>::metric( - $metric, - ) { - Ok(val) => { - if let Err(e) = val.inc($label_vals) { - log::error!( - "error incrementing metric with labels: {} {:?}", - e, - $label_vals - ) - } - } - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} - -/// Increments an IntCounterVec by a value or logs an error if the metric is not found -#[macro_export] -macro_rules! int_counter_vec_inc_by { - ($metric:expr, $label_vals:expr, $val:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntCounterVec>::metric( - $metric, - ) { - Ok(val) => { - if let Err(e) = val.inc_by($label_vals, $val) { - log::error!( - "error incrementing metric by {} with labels: {} {:?}", - $val, - e, - $label_vals - ) - } - } - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} - -/// Gets the value of an IntCounterVec or logs an error if the metric is not found -/// Returns 0 if the metric is not found -#[macro_export] -macro_rules! int_counter_vec_get { - ($metric:expr, $label_vals:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntCounterVec>::metric( - $metric, - ) { - Ok(val) => match val.get($label_vals) { - Ok(val) => val, - Err(e) => { - log::error!("error getting metric with labels: {} {:?}", e, $label_vals); - 0 - } - }, - - Err(e) => { - log::error!("error getting metric: {}", e); - 0 - } - } - }}; -} - -/// Resets an IntCounterVec or logs an error if the metric is not found -#[macro_export] -macro_rules! int_counter_vec_reset { - ($metric:expr, $label_vals:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntCounterVec>::metric( - $metric, - ) { - Ok(val) => { - if let Err(e) = val.reset($label_vals) { - log::error!( - "error resetting metric with labels: {} {:?}", - e, - $label_vals - ) - } - } - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} diff --git a/src/hyperlight_host/src/metrics/int_gauge.rs b/src/hyperlight_host/src/metrics/int_gauge.rs deleted file mode 100644 index ed595d462..000000000 --- a/src/hyperlight_host/src/metrics/int_gauge.rs +++ /dev/null @@ -1,170 +0,0 @@ -/* -Copyright 2024 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use prometheus::core::{AtomicI64, GenericGauge}; -use prometheus::register_int_gauge_with_registry; -use tracing::{instrument, Span}; - -use super::{ - get_metric_opts, get_metrics_registry, GetHyperlightMetric, HyperlightMetric, - HyperlightMetricOps, -}; -use crate::{new_error, HyperlightError, Result}; - -/// A gauge backed by an `AtomicI64` -#[derive(Debug)] -pub struct IntGauge { - gauge: GenericGauge, - /// The name of the gauge - pub name: &'static str, -} - -impl IntGauge { - /// Creates a new gauge and registers it with the metric registry - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn new(name: &'static str, help: &str) -> Result { - let registry = get_metrics_registry(); - let opts = get_metric_opts(name, help); - let gauge = register_int_gauge_with_registry!(opts, registry)?; - Ok(Self { gauge, name }) - } - /// Increments a gauge by 1 - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn inc(&self) { - self.gauge.inc(); - } - /// Decrements a gauge by 1 - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn dec(&self) { - self.gauge.dec(); - } - /// Gets the value of a gauge - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn set(&self, val: i64) { - self.gauge.set(val); - } - /// Resets a gauge - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn get(&self) -> i64 { - self.gauge.get() - } - /// Adds a value to a gauge - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn add(&self, val: i64) { - self.gauge.add(val); - } - /// Subtracts a value from a gauge - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn sub(&self, val: i64) { - self.gauge.sub(val) - } -} - -impl GetHyperlightMetric for S { - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn metric(&self) -> Result<&IntGauge> { - let metric = self.get_metric()?; - <&HyperlightMetric as TryInto<&IntGauge>>::try_into(metric) - } -} - -impl<'a> TryFrom<&'a HyperlightMetric> for &'a IntGauge { - type Error = HyperlightError; - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn try_from(metric: &'a HyperlightMetric) -> Result { - match metric { - HyperlightMetric::IntGauge(gauge) => Ok(gauge), - _ => Err(new_error!("metric is not a IntGauge")), - } - } -} - -impl From for HyperlightMetric { - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn from(gauge: IntGauge) -> Self { - HyperlightMetric::IntGauge(gauge) - } -} - -/// Increments an IntGauge by 1 or logs an error if the metric is not found -#[macro_export] -macro_rules! int_gauge_inc { - ($metric:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntGauge>::metric($metric) { - Ok(val) => val.inc(), - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} - -/// Decrements an IntGauge by 1 or logs an error if the metric is not found -#[macro_export] -macro_rules! int_gauge_dec { - ($metric:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntGauge>::metric($metric) { - Ok(val) => val.dec(), - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} - -/// Sets an IntGauge to value or logs an error if the metric is not found -#[macro_export] -macro_rules! int_gauge_set { - ($metric:expr, $val:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntGauge>::metric($metric) { - Ok(val) => val.set($val), - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} - -/// Gets the value of an IntGauge logs an error -/// and returns 0 if the metric is not found -#[macro_export] -macro_rules! int_gauge_get { - ($metric:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntGauge>::metric($metric) { - Ok(val) => val.get(), - Err(e) => { - log::error!("error getting metric: {}", e); - 0 - } - } - }}; -} - -/// Adds a value to an IntGauge or logs an error if the metric is not found -#[macro_export] -macro_rules! int_gauge_add { - ($metric:expr, $val:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntGauge>::metric($metric) { - Ok(val) => val.add($val), - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} - -/// Subtracts a value from an IntGauge or logs an error if the metric is not found -#[macro_export] -macro_rules! int_gauge_sub { - ($metric:expr, $val:expr) => {{ - match $crate::metrics::GetHyperlightMetric::<$crate::metrics::IntGauge>::metric($metric) { - Ok(val) => val.sub($val), - Err(e) => log::error!("error getting metric: {}", e), - }; - }}; -} diff --git a/src/hyperlight_host/src/metrics/int_gauge_vec.rs b/src/hyperlight_host/src/metrics/int_gauge_vec.rs deleted file mode 100644 index d9e757429..000000000 --- a/src/hyperlight_host/src/metrics/int_gauge_vec.rs +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright 2024 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use prometheus::core::{AtomicI64, GenericGaugeVec}; -use prometheus::register_int_gauge_vec_with_registry; -use tracing::{error, instrument, Span}; - -use super::{ - get_metric_opts, get_metrics_registry, GetHyperlightMetric, HyperlightMetric, - HyperlightMetricOps, -}; -use crate::{new_error, HyperlightError, Result}; - -/// A list of gauges, each backed by an `AtomicI64` -#[derive(Debug)] -pub struct IntGaugeVec { - gauge: GenericGaugeVec, - /// The name of the gauge vec - pub name: &'static str, -} - -impl IntGaugeVec { - /// Creates a new gauge vec and registers it with the metric registry - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub fn new(name: &'static str, help: &str, labels: &[&str]) -> Result { - let registry = get_metrics_registry(); - let opts = get_metric_opts(name, help); - let gauge = register_int_gauge_vec_with_registry!(opts, labels, registry)?; - Ok(Self { gauge, name }) - } - /// Increments a gauge by 1 - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn inc(&self, label_vals: &[&str]) { - match self.gauge.get_metric_with_label_values(label_vals) { - Ok(gauge) => gauge.inc(), - Err(e) => error!("Failed to get metric with label values: {}", e), - } - } - /// Decrements a gauge by 1 - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn dec(&self, label_vals: &[&str]) { - match self.gauge.get_metric_with_label_values(label_vals) { - Ok(gauge) => gauge.dec(), - Err(e) => error!("Failed to get metric with label values: {}", e), - } - } - /// Gets the value of a gauge - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn get(&self, label_vals: &[&str]) -> i64 { - match self.gauge.get_metric_with_label_values(label_vals) { - Ok(gauge) => gauge.get(), - Err(e) => { - error!("Failed to get metric with label values: {}", e); - 0 - } - } - } - /// Resets a gauge - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn set(&self, label_vals: &[&str], val: i64) { - match self.gauge.get_metric_with_label_values(label_vals) { - Ok(gauge) => gauge.set(val), - Err(e) => error!("Failed to get metric with label values: {}", e), - } - } - /// Adds a value to a gauge - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn add(&self, label_vals: &[&str], val: i64) { - match self.gauge.get_metric_with_label_values(label_vals) { - Ok(gauge) => gauge.add(val), - Err(e) => error!("Failed to get metric with label values: {}", e), - } - } - /// Subtracts a value from a gauge - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn sub(&self, label_vals: &[&str], val: i64) { - match self.gauge.get_metric_with_label_values(label_vals) { - Ok(gauge) => gauge.sub(val), - Err(e) => error!("Failed to get metric with label values: {}", e), - } - } -} - -impl GetHyperlightMetric for S { - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn metric(&self) -> Result<&IntGaugeVec> { - let metric = self.get_metric()?; - <&HyperlightMetric as TryInto<&IntGaugeVec>>::try_into(metric) - } -} - -impl<'a> TryFrom<&'a HyperlightMetric> for &'a IntGaugeVec { - type Error = HyperlightError; - #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn try_from(metric: &'a HyperlightMetric) -> Result { - match metric { - HyperlightMetric::IntGaugeVec(gauge) => Ok(gauge), - _ => Err(new_error!("metric is not a IntGaugeVec")), - } - } -} - -impl From for HyperlightMetric { - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn from(gauge: IntGaugeVec) -> Self { - HyperlightMetric::IntGaugeVec(gauge) - } -} diff --git a/src/hyperlight_host/src/metrics/mod.rs b/src/hyperlight_host/src/metrics/mod.rs index 1502128b5..7db1ed4cc 100644 --- a/src/hyperlight_host/src/metrics/mod.rs +++ b/src/hyperlight_host/src/metrics/mod.rs @@ -14,342 +14,207 @@ See the License for the specific language governing permissions and limitations under the License. */ -use std::collections::HashMap; -use std::sync::Once; +// Counter metric that counter number of times a guest error occurred +pub(crate) static METRIC_GUEST_ERROR: &str = "guest_errors_total"; +pub(crate) static METRIC_GUEST_ERROR_LABEL_CODE: &str = "code"; -use log::error; -use once_cell::sync::OnceCell; -use prometheus::{default_registry, histogram_opts, opts, HistogramOpts, Opts, Registry}; -use strum::{IntoEnumIterator, VariantNames}; +// Counter metric that counts the number of times a guest function was called due to timing out +pub(crate) static METRIC_GUEST_CANCELLATION: &str = "guest_cancellations_total"; -use crate::error::HyperlightError::{Error, MetricNotFound}; -use crate::{log_then_return, new_error, Result}; -mod int_gauge_vec; -/// An Integer Gauge Metric for Hyperlight -/// -pub use int_gauge_vec::IntGaugeVec; -mod int_gauge; -/// An Integer Gauge Metric for Hyperlight -pub use int_gauge::IntGauge; -mod int_counter_vec; -/// An Integer Counter Vec for Hyperlight -pub use int_counter_vec::IntCounterVec; -mod int_counter; -/// An Integer Counter for Hyperlight -pub use int_counter::IntCounter; -mod histogram_vec; -/// A Histogram Vec for Hyperlight -pub use histogram_vec::HistogramVec; -mod histogram; -/// AHistogram for Hyperlight -pub use histogram::Histogram; -/// A trait that should be implemented by all enums that represent hyperlight metrics -pub trait HyperlightMetricEnum: - IntoEnumIterator + VariantNames + From + Into<&'static str> -where - &'static str: From, - &'static str: for<'a> From<&'a Self>, -{ - /// A function that should return a static reference to a Once that is used to guard the initialization of the metrics hashmap. - fn get_init_metrics() -> &'static Once; - /// A function that should return a static reference to a OnceCell that is used to store the metrics hashmap. - fn get_metrics() -> &'static OnceCell>; - /// A function that should return a static reference to a slice of HyperlightMetricDefinitions that are used to initialize the metrics hashmap. - fn get_metric_definitions() -> &'static [HyperlightMetricDefinition]; - - /// Gets a HyperlightMetric from the hashmap using the enum variant name as the key - #[inline] - fn get_hyperlight_metric(&self) -> Result<&HyperlightMetric> { - Self::get_init_metrics().call_once(|| { - let result = init_metrics(Self::get_metric_definitions(), Self::get_metrics()); - if let Err(e) = result { - error!("Error initializing metrics : {0:?}", e); - } - }); - let key: &'static str = <&Self as Into<&'static str>>::into(self); - HyperlightMetric::get_metric_using_key(key, Self::get_hash_map()?) - } - /// Gets the hashmap using the containing the metrics - #[inline] - fn get_hash_map() -> Result<&'static HashMap<&'static str, HyperlightMetric>> { - Self::get_metrics() - .get() - .ok_or_else(|| Error("metrics hashmap not initialized".to_string())) - } -} -/// A trait that should be implemented by all enums that represent hyperlight metrics to -/// convert the enum into a HyperlightMetric -pub trait HyperlightMetricOps { - /// Converts the enum into a HyperlightMetric - fn get_metric(&self) -> Result<&HyperlightMetric>; -} +// Histogram metric that measures the duration of guest function calls +#[cfg(feature = "function_call_metrics")] +pub(crate) static METRIC_GUEST_FUNC_DURATION: &str = "guest_call_duration_seconds"; -/// A trait that should be implemented by all hyperlight metric definitions to convert the metric into a HyperlightMetric -pub trait GetHyperlightMetric { - /// Converts the metric into a HyperlightMetric - fn metric(&self) -> Result<&T>; -} - -impl> HyperlightMetricOps for T -where - &'static str: From, - for<'a> &'static str: From<&'a T>, -{ - fn get_metric(&self) -> Result<&HyperlightMetric> { - self.get_hyperlight_metric() - } -} - -/// Initializes the metrics hashmap using the metric definitions -#[inline] -fn init_metrics( - metric_definitions: &[HyperlightMetricDefinition], - metrics: &OnceCell>, -) -> Result<()> { - let mut hash_map: HashMap<&'static str, HyperlightMetric> = HashMap::new(); - register_metrics(metric_definitions, &mut hash_map)?; - // the only failure case is if the metrics hashmap is already set which should not be possible - // but if it were to happen we dont care. - if let Err(e) = metrics.set(hash_map) { - error!("metrics hashmap already set : {0:?}", e); - } - Ok(()) -} -//TODO: Remove this when we have uses of all metric types -#[allow(dead_code)] -#[derive(Debug)] -/// The types of Hyperlight metrics that can be created -pub enum HyperlightMetricType { - /// A counter that can only be incremented - IntCounter, - /// A counter that can only be incremented and has labels - IntCounterVec, - /// A gauge that can be incremented, decremented, set, added to, and subtracted from - IntGauge, - /// A gauge that can be incremented, decremented, set, added to, and subtracted from and has labels - IntGaugeVec, - /// A histogram that can observe values for activities - Histogram, - /// A histogram that can observe values for activities and has labels - HistogramVec, -} +// Histogram metric that measures the duration of host function calls +#[cfg(feature = "function_call_metrics")] +pub(crate) static METRIC_HOST_FUNC_DURATION: &str = "host_call_duration_seconds"; -/// The definition of a Hyperlight metric -pub struct HyperlightMetricDefinition { - /// The name of the metric - pub name: &'static str, - /// The help text for the metric - pub help: &'static str, - /// The type of the metric - pub metric_type: HyperlightMetricType, - /// The labels for the metric - pub labels: &'static [&'static str], - /// The buckets for the metric - pub buckets: &'static [f64], -} - -fn register_metrics( - metric_definitions: &[HyperlightMetricDefinition], - hash_map: &mut HashMap<&'static str, HyperlightMetric>, -) -> Result<()> { - for metric_definition in metric_definitions { - let metric: HyperlightMetric = match &metric_definition.metric_type { - HyperlightMetricType::IntGauge => { - IntGauge::new(metric_definition.name, metric_definition.help)?.into() - } - - HyperlightMetricType::IntCounterVec => IntCounterVec::new( - metric_definition.name, - metric_definition.help, - metric_definition.labels, - )? - .into(), - - HyperlightMetricType::IntCounter => { - IntCounter::new(metric_definition.name, metric_definition.help)?.into() - } - HyperlightMetricType::HistogramVec => HistogramVec::new( - metric_definition.name, - metric_definition.help, - metric_definition.labels, - metric_definition.buckets.to_vec(), - )? - .into(), - HyperlightMetricType::Histogram => Histogram::new( - metric_definition.name, - metric_definition.help, - metric_definition.buckets.to_vec(), - )? - .into(), - HyperlightMetricType::IntGaugeVec => IntGaugeVec::new( - metric_definition.name, - metric_definition.help, - metric_definition.labels, - )? - .into(), - }; - - hash_map.insert(metric_definition.name, metric); - } - Ok(()) -} - -#[derive(Debug)] -/// An instance of a Hyperlight metric -pub enum HyperlightMetric { - /// A counter that can only be incremented - IntCounter(IntCounter), - /// A counter that can only be incremented and has labels - IntCounterVec(IntCounterVec), - /// A gauge that can be incremented, decremented, set, added to, and subtracted from - IntGauge(IntGauge), - /// A gauge that can be incremented, decremented, set, added to, and subtracted from and has labels - IntGaugeVec(IntGaugeVec), - /// A histogram that can observe values for activities - Histogram(Histogram), - /// A histogram that can observe values for activities and has labels - HistogramVec(HistogramVec), -} - -impl HyperlightMetric { - #[inline] - fn get_metric_using_key<'a>( - key: &'static str, - hash_map: &'a HashMap<&'static str, HyperlightMetric>, - ) -> Result<&'a HyperlightMetric> { - hash_map.get(key).ok_or_else(|| MetricNotFound(key)) +/// If the the `function_call_metrics` feature is enabled, this function measures +/// the time it takes to execute the given closure, and will then emit a guest call metric +/// with the given function name. +/// +/// If the feature is not enabled, the given closure is executed without any additional metrics being emitted, +/// and the result of the closure is returned directly. +pub(crate) fn maybe_time_and_emit_guest_call T>( + #[allow(unused_variables)] name: &str, + f: F, +) -> T { + cfg_if::cfg_if! { + if #[cfg(feature = "function_call_metrics")] { + use std::time::Instant; + + let start = Instant::now(); + let result = f(); + let duration = start.elapsed(); + + static LABEL_GUEST_FUNC_NAME: &str = "function_name"; + metrics::histogram!(METRIC_GUEST_FUNC_DURATION, LABEL_GUEST_FUNC_NAME => name.to_string()).record(duration); + result + } else { + f() + } } } -// The registry used for all metrics, this can be set by the user of the library, if its not set then the default will be used. - -static REGISTRY: OnceCell<&Registry> = OnceCell::new(); - -/// Get the registry to be used for all metrics, this can be set by the user of the library, if its not set then the default registry will be used. -#[inline] -pub fn get_metrics_registry() -> &'static Registry { - REGISTRY.get_or_init(default_registry) -} -/// Set the registry to be used for all metrics, this can be set by the user of the library, if its not set then the default registry will be used. -/// This function should be called before any other function in this module is called. +/// If the the `function_call_metrics` feature is enabled, this function measures +/// the time it takes to execute the given closure, and will then emit a host call metric +/// with the given function name. /// -/// The user of can then use the registry to gather metrics from the library. -pub fn set_metrics_registry(registry: &'static Registry) -> Result<()> { - match REGISTRY.get() { - Some(_) => { - log_then_return!("Registry was already set"); - } - None => { - REGISTRY - .set(registry) - // This should be impossible - .map_err(|e| new_error!("Registry already set : {0:?}", e)) +/// If the feature is not enabled, the given closure is executed without any additional metrics being emitted, +/// and the result of the closure is returned directly. +pub(crate) fn maybe_time_and_emit_host_call T>( + #[allow(unused_variables)] name: &str, + f: F, +) -> T { + cfg_if::cfg_if! { + if #[cfg(feature = "function_call_metrics")] { + use std::time::Instant; + + let start = Instant::now(); + let result = f(); + let duration = start.elapsed(); + + static LABEL_HOST_FUNC_NAME: &str = "function_name"; + metrics::histogram!(METRIC_HOST_FUNC_DURATION, LABEL_HOST_FUNC_NAME => name.to_string()).record(duration); + result + } else { + f() } } } -fn get_metric_opts(name: &str, help: &str) -> Opts { - let opts = opts!(name, help); - opts.namespace("hyperlight") -} - -fn get_histogram_opts(name: &str, help: &str, buckets: Vec) -> HistogramOpts { - let mut opts = histogram_opts!(name, help); - opts = opts.namespace("hyperlight"); - opts.buckets(buckets) -} - -#[allow(clippy::disallowed_macros)] -/// Provides functionality to help with testing Hyperlight Metrics -pub mod tests { - use std::collections::HashSet; +#[cfg(test)] +mod tests { + use hyperlight_common::flatbuffer_wrappers::function_types::{ParameterValue, ReturnType}; + use hyperlight_testing::simple_guest_as_string; + use metrics::Key; + use metrics_util::CompositeKey; use super::*; + use crate::sandbox_state::sandbox::EvolvableSandbox; + use crate::sandbox_state::transition::Noop; + use crate::{GuestBinary, UninitializedSandbox}; + + #[test] + #[ignore = "This test needs to be run separately to avoid having other tests interfere with it"] + fn test_metrics_are_emitted() { + // Set up the recorder and snapshotter + let recorder = metrics_util::debugging::DebuggingRecorder::new(); + let snapshotter = recorder.snapshotter(); + + // we cannot use with_local_recorder, since that won't capture the metrics + // emitted by the hypervisor-thread (which is all of them) + recorder.install().unwrap(); + + let snapshot = { + let uninit = UninitializedSandbox::new( + GuestBinary::FilePath(simple_guest_as_string().unwrap()), + None, + None, + None, + ) + .unwrap(); + + let mut multi = uninit.evolve(Noop::default()).unwrap(); + + multi + .call_guest_function_by_name( + "PrintOutput", + ReturnType::Int, + Some(vec![ParameterValue::String("Hello".to_string())]), + ) + .unwrap(); + + multi + .call_guest_function_by_name("Spin", ReturnType::Int, None) + .unwrap_err(); + + snapshotter.snapshot() + }; - /// A trait that provides test helper functions for Hyperlight Metrics - pub trait HyperlightMetricEnumTest: - HyperlightMetricEnum + From + Into<&'static str> - where - &'static str: From, - &'static str: for<'a> From<&'a Self>, - { - /// Defines a function that should return the names of all the metric enum variants - fn get_enum_variant_names() -> &'static [&'static str]; - - /// Provides a function to test that all hyperlight metric definitions in a module have a corresponding enum variant - /// Should be called in tests in modules that define hyperlight metrics. - #[track_caller] - fn enum_has_variant_for_all_metrics() { - let metric_definitions = Self::get_metric_definitions().iter(); - for metric_definition in metric_definitions { - let metric_definition_name = metric_definition.name; + // Convert snapshot into a hashmap for easier lookup + #[expect(clippy::mutable_key_type)] + let snapshot = snapshot.into_hashmap(); + + cfg_if::cfg_if! { + if #[cfg(feature = "function_call_metrics")] { + use metrics::Label; + // Verify that the histogram metrics are recorded correctly + assert_eq!(snapshot.len(), 4, "Expected two metrics in the snapshot"); + + // 1. Host print duration + let histogram_key = CompositeKey::new( + metrics_util::MetricKind::Histogram, + Key::from_parts( + METRIC_HOST_FUNC_DURATION, + vec![Label::new("function_name", "HostPrint")], + ), + ); + let histogram_value = &snapshot.get(&histogram_key).unwrap().2; assert!( - Self::get_enum_variant_names().contains(&metric_definition_name), - "Metric Definition Name {} not found", - metric_definition_name, + matches!( + histogram_value, + metrics_util::debugging::DebugValue::Histogram(ref histogram) if histogram.len() == 1 + ), + "Histogram metric does not match expected value" ); - } - } - /// Provides a function to test that all hyperlight metric definitions have a unique help text - /// and that there are the same number of enum variants as metric definitions - /// Should be called in tests in modules that define hyperlight metrics. - #[track_caller] - fn check_metric_definitions() { - let sandbox_metric_definitions = Self::get_metric_definitions(); - let metric_definitions = sandbox_metric_definitions.iter(); - let mut help_text = HashSet::new(); - for metric_definition in metric_definitions { + // 2. Guest call duration + let histogram_key = CompositeKey::new( + metrics_util::MetricKind::Histogram, + Key::from_parts( + METRIC_GUEST_FUNC_DURATION, + vec![Label::new("function_name", "PrintOutput")], + ), + ); + let histogram_value = &snapshot.get(&histogram_key).unwrap().2; assert!( - help_text.insert(metric_definition.help), - "duplicate metric help definition for {}", - metric_definition.name + matches!( + histogram_value, + metrics_util::debugging::DebugValue::Histogram(ref histogram) if histogram.len() == 1 + ), + "Histogram metric does not match expected value" ); - } - assert_eq!( - Self::get_enum_variant_names().len(), - sandbox_metric_definitions.len() - ); - } - - /// Gets a named int gauge metric - fn get_intguage_metric(name: &str) -> Result<&IntGauge> { - Self::get_metrics() - .get() - .ok_or_else(|| new_error!("metrics hashmap not initialized"))? - .get(name) - .ok_or_else(|| new_error!("metric not found : {0:?}", name))? - .try_into() - } - /// Gets a named int counter vec metric - fn get_intcountervec_metric(name: &str) -> Result<&IntCounterVec> { - Self::get_metrics() - .get() - .ok_or_else(|| new_error!("metrics hashmap not initialized"))? - .get(name) - .ok_or_else(|| new_error!("metric not found : {0:?}", name))? - .try_into() - } + // 3. Guest cancellation + let counter_key = CompositeKey::new( + metrics_util::MetricKind::Counter, + Key::from_name(METRIC_GUEST_CANCELLATION), + ); + assert_eq!( + snapshot.get(&counter_key).unwrap().2, + metrics_util::debugging::DebugValue::Counter(1) + ); - /// Gets a named int counter metric - fn get_intcounter_metric(name: &str) -> Result<&IntCounter> { - Self::get_metrics() - .get() - .ok_or_else(|| new_error!("metrics hashmap not initialized"))? - .get(name) - .ok_or_else(|| new_error!("metric not found : {0:?}", name))? - .try_into() - } + // 4. Guest call duration + let histogram_key = CompositeKey::new( + metrics_util::MetricKind::Histogram, + Key::from_parts( + METRIC_GUEST_FUNC_DURATION, + vec![Label::new("function_name", "Spin")], + ), + ); + let histogram_value = &snapshot.get(&histogram_key).unwrap().2; + assert!( + matches!( + histogram_value, + metrics_util::debugging::DebugValue::Histogram(ref histogram) if histogram.len() == 1 + ), + "Histogram metric does not match expected value" + ); + } else { + // Verify that the counter metrics are recorded correctly + assert_eq!(snapshot.len(), 1, "Expected two metrics in the snapshot"); - /// Gets a named histogram vec metric - fn get_histogramvec_metric(name: &str) -> Result<&HistogramVec> { - Self::get_metrics() - .get() - .ok_or_else(|| new_error!("metrics hashmap not initialized"))? - .get(name) - .ok_or_else(|| new_error!("metric not found : {0:?}", name))? - .try_into() + let counter_key = CompositeKey::new( + metrics_util::MetricKind::Counter, + Key::from_name(METRIC_GUEST_CANCELLATION), + ); + assert_eq!( + snapshot.get(&counter_key).unwrap().2, + metrics_util::debugging::DebugValue::Counter(1) + ); + } } } } diff --git a/src/hyperlight_host/src/sandbox/host_funcs.rs b/src/hyperlight_host/src/sandbox/host_funcs.rs index 15ef9a46f..b0d3fa05c 100644 --- a/src/hyperlight_host/src/sandbox/host_funcs.rs +++ b/src/hyperlight_host/src/sandbox/host_funcs.rs @@ -181,20 +181,7 @@ fn call_host_func_impl( seccompiler::apply_filter(&seccomp_filter)?; } - #[cfg(feature = "function_call_metrics")] - { - let start = std::time::Instant::now(); - let result = func.call(args.clone()); - crate::histogram_vec_observe!( - &crate::sandbox::metrics::SandboxMetric::HostFunctionCallsDurationMicroseconds, - &[name], - start.elapsed().as_micros() as f64 - ); - result - } - - #[cfg(not(feature = "function_call_metrics"))] - func.call(args) + crate::metrics::maybe_time_and_emit_host_call(name, || func.call(args)) } cfg_if::cfg_if! { diff --git a/src/hyperlight_host/src/sandbox/metrics.rs b/src/hyperlight_host/src/sandbox/metrics.rs deleted file mode 100644 index 05ef9bae1..000000000 --- a/src/hyperlight_host/src/sandbox/metrics.rs +++ /dev/null @@ -1,259 +0,0 @@ -/* -Copyright 2024 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/*! -This module contains the definitions and implementations of the metrics used by the sandbox module -*/ -use std::collections::HashMap; -use std::sync::Once; - -use once_cell::sync::OnceCell; -use strum::{EnumIter, IntoStaticStr, VariantNames}; -use tracing::{instrument, Span}; - -use crate::metrics::{ - HyperlightMetric, HyperlightMetricDefinition, HyperlightMetricEnum, HyperlightMetricType, -}; - -// This is required to ensure that the metrics are only initialized once -static INIT_METRICS: Once = Once::new(); -// This contains a hashmap of all the metrics using metric names defined below as keys -static METRICS: OnceCell> = OnceCell::new(); - -// This is the definition of all the metrics used by the sandbox module -static SANDBOX_METRIC_DEFINITIONS: &[HyperlightMetricDefinition] = &[ - HyperlightMetricDefinition { - name: "guest_error_count", - help: "Number of guest errors encountered", - metric_type: HyperlightMetricType::IntCounterVec, - labels: &["error_code", "error_message"], - buckets: &[], - }, - #[cfg(feature = "function_call_metrics")] - HyperlightMetricDefinition { - name: "guest_function_call_duration_microseconds", - help: "Duration of guest function calls in microseconds", - metric_type: HyperlightMetricType::HistogramVec, - labels: &["function_name"], - buckets: &[ - 50.00, 150.0, 250.0, 350.0, 450.0, 550.0, 650.0, 750.0, 850.0, 950.0, 1050.00, 1150.00, - 1250.00, 1350.00, 1450.00, 1550.00, 1650.00, 1750.00, 1850.00, 1950.00, 2050.00, - 2150.00, 2250.00, 2350.00, 2450.00, 2550.00, 2650.00, 2750.00, 2850.00, 2950.00, - 3050.00, 3150.00, 3250.00, 3350.00, 3450.00, 3550.00, 3650.00, 3750.00, 3850.00, - 3950.00, 4050.00, 4150.00, 4250.00, 4350.00, 4450.00, 4550.00, 4650.00, 4750.00, - 4850.00, 4950.00, 5050.00, 5150.00, 5250.00, 5350.00, 5450.00, 5550.00, 5650.00, - 5750.00, 5850.00, 5950.00, 6050.00, - ], - }, - #[cfg(feature = "function_call_metrics")] - HyperlightMetricDefinition { - name: "host_function_calls_duration_microseconds", - help: "Duration of host function calls in Microseconds", - metric_type: HyperlightMetricType::HistogramVec, - labels: &["function_name"], - buckets: &[ - 50.00, 150.0, 250.0, 350.0, 450.0, 550.0, 650.0, 750.0, 850.0, 950.0, 1050.00, 1150.00, - 1250.00, 1350.00, 1450.00, 1550.00, 1650.00, 1750.00, 1850.00, 1950.00, 2050.00, - 2150.00, 2250.00, 2350.00, 2450.00, 2550.00, 2650.00, 2750.00, 2850.00, 2950.00, - 3050.00, 4050.00, 4150.00, 4250.00, 4350.00, 4450.00, 4550.00, 4650.00, 4750.00, - 4850.00, 4950.00, 5050.00, 5150.00, 5250.00, 5350.00, 5450.00, 5550.00, 5650.00, - 5750.00, 5850.00, 5950.00, 6050.00, - ], - }, -]; - -/// There is an enum variant for each error metric in the module -/// the names of the variant take the form of CamelCase, but the metric names are snake_case -/// so for example, the enum variant CurrentNumberOfMultiUseSandboxes corresponds to the -/// metric name current_number_of_multi_use_sandboxes. -/// At runtime we location the correct metric by looking up the enum variant name in the hashmap -/// the conversion of the enum variant name to the metric name is done by the IntoStaticStr derive macro -/// along with the strum(serialize_all = "snake_case") attribute. -/// This enum contains all the metrics used by the sandbox module -/// -/// The enum is required to derive from EnumIter, EnumVariantNames, IntoStaticStr -/// and strum(serialize_all = "snake_case") performs the name conversion from CamelCase to snake_case -/// when the enum variant is serialized to a string -#[derive(Debug, EnumIter, VariantNames, IntoStaticStr)] -#[strum(serialize_all = "snake_case")] -pub(crate) enum SandboxMetric { - GuestErrorCount, - #[cfg(feature = "function_call_metrics")] - GuestFunctionCallDurationMicroseconds, - #[cfg(feature = "function_call_metrics")] - HostFunctionCallsDurationMicroseconds, -} - -// It is required for the enum to implement HyperlightMetricEnum -impl HyperlightMetricEnum for SandboxMetric { - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn get_init_metrics() -> &'static Once { - &INIT_METRICS - } - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn get_metrics() -> &'static OnceCell> { - &METRICS - } - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn get_metric_definitions() -> &'static [HyperlightMetricDefinition] { - SANDBOX_METRIC_DEFINITIONS - } -} - -#[cfg(test)] -mod tests { - use lazy_static::lazy_static; - use prometheus::Registry; - use strum::{IntoEnumIterator, VariantNames}; - - use super::*; - use crate::metrics::get_metrics_registry; - use crate::metrics::tests::HyperlightMetricEnumTest; - use crate::{ - histogram_vec_observe, histogram_vec_sample_count, histogram_vec_sample_sum, - int_counter_vec_get, int_counter_vec_inc, int_counter_vec_inc_by, int_counter_vec_reset, - int_gauge_add, int_gauge_dec, int_gauge_get, int_gauge_inc, int_gauge_set, int_gauge_sub, - }; - - impl HyperlightMetricEnumTest for SandboxMetric { - fn get_enum_variant_names() -> &'static [&'static str] { - SandboxMetric::VARIANTS - } - } - - #[test] - fn test_enum_has_variant_for_all_metrics() { - >::enum_has_variant_for_all_metrics(); - } - #[test] - fn test_metric_definitions() { - >::check_metric_definitions( - ); - } - #[test] - #[ignore] - /// This test is ignored because as it uses real counters if it runs at the same time as other tests it may fail. - /// - /// Marking this test as ignored means that running `cargo test` will not - /// run it. This feature will allow a developer who runs that command - /// from their workstation to be successful without needing to know about - /// test interdependencies. This test will, however, be run explicitly as a - /// part of the CI pipeline. - fn test_metrics() { - let iter: SandboxMetricIter = SandboxMetric::iter(); - for sandbox_metric in iter { - match sandbox_metric.get_hyperlight_metric() { - Ok(hyperlight_metric) => match hyperlight_metric { - HyperlightMetric::IntGauge(int_gauge) => { - let gauge = >::get_intguage_metric(int_gauge.name); - assert!(gauge.is_ok()); - let gauge = gauge.unwrap(); - int_gauge_set!(&sandbox_metric, 0); - assert_eq!(gauge.get(), 0); - int_gauge_inc!(&sandbox_metric); - assert_eq!(gauge.get(), 1); - int_gauge_dec!(&sandbox_metric); - assert_eq!(gauge.get(), 0); - int_gauge_add!(&sandbox_metric, 5); - assert_eq!(gauge.get(), 5); - int_gauge_sub!(&sandbox_metric, 2); - assert_eq!(gauge.get(), 3); - int_gauge_set!(&sandbox_metric, 10); - assert_eq!(gauge.get(), 10); - let val = int_gauge_get!(&sandbox_metric); - assert_eq!(val, 10); - } - HyperlightMetric::IntCounterVec(int_counter_vec) => { - let counter = >::get_intcountervec_metric( - int_counter_vec.name - ); - assert!(counter.is_ok()); - let counter = counter.unwrap(); - let label_vals = ["test", "test2"]; - int_counter_vec_reset!(&sandbox_metric, &label_vals); - let value = counter.get(&label_vals); - assert!(value.is_ok()); - let value = value.unwrap(); - assert_eq!(value, 0); - int_counter_vec_inc!(&sandbox_metric, &label_vals); - let value = counter.get(&label_vals); - assert!(value.is_ok()); - let value = value.unwrap(); - assert_eq!(value, 1); - int_counter_vec_inc_by!(&sandbox_metric, &label_vals, 5); - let value = counter.get(&label_vals); - assert!(value.is_ok()); - let value = value.unwrap(); - assert_eq!(value, 6); - int_counter_vec_reset!(&sandbox_metric, &label_vals); - let value = int_counter_vec_get!(&sandbox_metric, &label_vals); - assert_eq!(value, 0); - } - HyperlightMetric::HistogramVec(histogram_vec) => { - let histogram = >::get_histogramvec_metric( - histogram_vec.name - ); - assert!(histogram.is_ok()); - let histogram = histogram.unwrap(); - let label_vals = ["test"]; - histogram_vec_observe!(&sandbox_metric, &label_vals, 1.0); - let result = histogram_vec_sample_sum!(&sandbox_metric, &label_vals); - assert_eq!(result, 1.0); - assert!(histogram.get_sample_count(&label_vals).is_ok()); - assert_eq!(histogram.get_sample_count(&label_vals).unwrap(), 1); - let result = histogram_vec_sample_count!(&sandbox_metric, &label_vals); - assert_eq!(result, 1); - assert!(histogram.get_sample_sum(&label_vals).is_ok()); - assert_eq!(histogram.get_sample_sum(&label_vals).unwrap(), 1.0); - } - _ => { - panic!("metric is not an IntGauge,IntCounterVec or HistogramVec"); - } - }, - Err(e) => { - panic!("error getting metric: {}", e); - } - } - } - } - #[test] - #[ignore] - /// This test is ignored because it is requires that metrics and registry have not been set or initialised yet. - /// - /// Marking this test as ignored means that running `cargo test` will not - /// run it. This feature will allow a developer who runs that command - /// from their workstation to be successful without needing to know about - /// test interdependencies. This test will, however, be run explicitly as a - /// part of the CI pipeline. - fn test_gather_metrics() { - lazy_static! { - static ref REGISTRY: Registry = Registry::default(); - } - test_metrics(); - let registry = get_metrics_registry(); - let result = registry.gather(); - #[cfg(feature = "function_call_metrics")] - assert_eq!(result.len(), 3); - #[cfg(not(feature = "function_call_metrics"))] - assert_eq!(result.len(), 1); - } -} diff --git a/src/hyperlight_host/src/sandbox/mod.rs b/src/hyperlight_host/src/sandbox/mod.rs index a88556878..2b45864f0 100644 --- a/src/hyperlight_host/src/sandbox/mod.rs +++ b/src/hyperlight_host/src/sandbox/mod.rs @@ -45,9 +45,6 @@ pub mod uninitialized; /// initialized `Sandbox`es. pub(crate) mod uninitialized_evolve; -/// Metric definitions for Sandbox module. -pub(crate) mod metrics; - use std::collections::HashMap; /// Re-export for `SandboxConfiguration` type diff --git a/src/tests/rust_guests/callbackguest/Cargo.lock b/src/tests/rust_guests/callbackguest/Cargo.lock index 45c7e59af..e65396646 100644 --- a/src/tests/rust_guests/callbackguest/Cargo.lock +++ b/src/tests/rust_guests/callbackguest/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "autocfg" diff --git a/src/tests/rust_guests/simpleguest/Cargo.lock b/src/tests/rust_guests/simpleguest/Cargo.lock index 7a0077535..9d1d1c7eb 100644 --- a/src/tests/rust_guests/simpleguest/Cargo.lock +++ b/src/tests/rust_guests/simpleguest/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "autocfg"