diff --git a/Cargo.lock b/Cargo.lock index 96c71c3ee..4b091702c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,25 +8,32 @@ version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ + "cpp_demangle", + "fallible-iterator", "gimli", + "memmap2", + "object", + "rustc-demangle", + "smallvec", + "typed-arena", ] [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy 0.7.35", + "zerocopy 0.8.26", ] [[package]] @@ -61,9 +68,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -76,36 +83,36 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", - "once_cell", + "once_cell_polyfill", "windows-sys 0.59.0", ] @@ -124,6 +131,24 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "associative-cache" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46016233fc1bb55c23b856fe556b7db6ccd05119a0a392e04f0b3b7c79058f16" + [[package]] name = "async-trait" version = "0.1.88" @@ -132,20 +157,20 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -179,7 +204,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 2.0.104", ] [[package]] @@ -209,6 +234,19 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "blake3" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -249,9 +287,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" @@ -265,6 +303,31 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cairo-rs" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "cast" version = "0.3.0" @@ -278,14 +341,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "975982cdb7ad6a142be15bdf84aea7ec6a9e5d4d797c004d43185b24cfe4e684" dependencies = [ "clap", - "heck", + "heck 0.5.0", "indexmap", "log", "proc-macro2", "quote", "serde", "serde_json", - "syn", + "syn 2.0.104", "tempfile", "toml", ] @@ -310,6 +373,16 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.1" @@ -376,18 +449,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.36" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.36" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -397,15 +470,31 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "constant_time_eq" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] [[package]] name = "core-foundation-sys" @@ -413,6 +502,51 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "cpp_demangle" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -422,6 +556,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "criterion" version = "0.6.0" @@ -500,9 +643,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -522,7 +665,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -564,7 +707,19 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "dwrote" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe1f192fcce01590bd8d839aca53ce0d11d803bf291b2a6c4ad925a8f0024be" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", ] [[package]] @@ -616,6 +771,15 @@ dependencies = [ "log", ] +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -624,20 +788,35 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "flatbuffers" version = "25.2.10" @@ -648,6 +827,16 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -660,6 +849,21 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -669,6 +873,20 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "framehop" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a5a3f0acb82df800ca3aa50c0d60d286c5d13d4cfc3114b3a9663f13b032fe" +dependencies = [ + "arrayvec", + "cfg-if", + "fallible-iterator", + "gimli", + "macho-unwind-info", + "pe-unwind-info", +] + [[package]] name = "futures" version = "0.3.31" @@ -725,7 +943,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -784,15 +1002,16 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bd114ceda131d3b1d665eba35788690ad37f5916457286b32ab6fd3c438dd" +checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" dependencies = [ + "cc", "cfg-if", "libc", "log", "rustversion", - "windows 0.58.0", + "windows", ] [[package]] @@ -807,20 +1026,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] name = "getrandom" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", "libc", @@ -833,12 +1052,49 @@ name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +dependencies = [ + "fallible-iterator", + "stable_deref_trait", +] + +[[package]] +name = "gio" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a1c84b4534a290a29160ef5c6eff2a9c95833111472e824fc5cb78b513dd092" +dependencies = [ + "bitflags 1.3.2", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] [[package]] name = "git2" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5220b8ba44c68a9a7f7a7659e864dd73692e417ef0211bea133c7b74e031eeb9" +checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" dependencies = [ "bitflags 2.9.1", "libc", @@ -847,6 +1103,53 @@ dependencies = [ "url", ] +[[package]] +name = "glib" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16aa2475c9debed5a32832cb5ff2af5a3f9e1ab9e69df58eaadc1ab2004d6eba" +dependencies = [ + "bitflags 1.3.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1a9325847aa46f1e96ffea37611b9d51fc4827e67f79e7de502a297560a67b" +dependencies = [ + "anyhow", + "heck 0.4.1", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "glib-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" +dependencies = [ + "libc", + "system-deps", +] + [[package]] name = "glob" version = "0.3.2" @@ -866,6 +1169,17 @@ dependencies = [ "regex-syntax 0.8.5", ] +[[package]] +name = "gobject-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "goblin" version = "0.10.0" @@ -889,14 +1203,20 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.2" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "foldhash", "serde", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" @@ -1015,7 +1335,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.104", "wasmparser", ] @@ -1028,7 +1348,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.104", "wasmparser", ] @@ -1047,6 +1367,8 @@ version = "0.7.0" dependencies = [ "anyhow", "hyperlight-common", + "hyperlight-guest-tracing", + "hyperlight-guest-tracing-macro", "serde_json", ] @@ -1060,16 +1382,36 @@ dependencies = [ "glob", "hyperlight-common", "hyperlight-guest", + "hyperlight-guest-tracing", + "hyperlight-guest-tracing-macro", "log", "spin 0.10.0", ] +[[package]] +name = "hyperlight-guest-tracing" +version = "0.7.0" +dependencies = [ + "hyperlight-common", + "spin 0.10.0", +] + +[[package]] +name = "hyperlight-guest-tracing-macro" +version = "0.7.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "hyperlight-host" version = "0.7.0" dependencies = [ "anyhow", "bitflags 2.9.1", + "blake3", "built", "cfg-if", "cfg_aliases", @@ -1079,12 +1421,16 @@ dependencies = [ "crossbeam-queue", "elfcore", "env_logger", + "envy", + "fallible-iterator", "flatbuffers", + "framehop", "gdbstub", "gdbstub_arch", "goblin", "hyperlight-common", "hyperlight-component-macro", + "hyperlight-guest-tracing", "hyperlight-testing", "kvm-bindings", "kvm-ioctls", @@ -1128,8 +1474,8 @@ dependencies = [ "tracing-tracy", "uuid", "vmm-sys-util", - "windows 0.61.3", - "windows-result 0.3.4", + "windows", + "windows-result", "windows-sys 0.60.2", "windows-version", ] @@ -1172,7 +1518,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.2", + "windows-core", ] [[package]] @@ -1186,21 +1532,22 @@ dependencies = [ [[package]] name = "icu_collections" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", + "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] -name = "icu_locid" -version = "1.5.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", "litemap", @@ -1209,31 +1556,11 @@ dependencies = [ "zerovec", ] -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" - [[package]] name = "icu_normalizer" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", "icu_collections", @@ -1241,67 +1568,54 @@ dependencies = [ "icu_properties", "icu_provider", "smallvec", - "utf16_iter", - "utf8_iter", - "write16", "zerovec", ] [[package]] name = "icu_normalizer_data" -version = "1.5.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] name = "icu_properties" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", "icu_collections", - "icu_locid_transform", + "icu_locale_core", "icu_properties_data", "icu_provider", - "tinystr", + "potential_utf", + "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" -version = "1.5.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "1.5.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" dependencies = [ "displaydoc", - "icu_locid", - "icu_provider_macros", + "icu_locale_core", "stable_deref_trait", "tinystr", "writeable", "yoke", "zerofrom", + "zerotrie", "zerovec", ] -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "idna" version = "1.0.3" @@ -1315,9 +1629,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", @@ -1325,9 +1639,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", @@ -1402,9 +1716,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.8" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0" +checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" dependencies = [ "jiff-static", "log", @@ -1415,13 +1729,13 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.8" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605" +checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1430,7 +1744,7 @@ version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "libc", ] @@ -1444,6 +1758,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kurbo" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" +dependencies = [ + "arrayvec", +] + [[package]] name = "kvm-bindings" version = "0.13.0" @@ -1479,9 +1802,9 @@ checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libfuzzer-sys" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" dependencies = [ "arbitrary", "cc", @@ -1489,9 +1812,9 @@ dependencies = [ [[package]] name = "libgit2-sys" -version = "0.18.1+1.9.0" +version = "0.18.2+1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1dcb20f84ffcdd825c7a311ae347cce604a6f084a767dec4a4929829645290e" +checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222" dependencies = [ "cc", "libc", @@ -1501,12 +1824,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.53.2", ] [[package]] @@ -1522,9 +1845,9 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" dependencies = [ "bitflags 2.9.1", "libc", @@ -1550,15 +1873,15 @@ checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "litemap" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -1585,13 +1908,24 @@ dependencies = [ [[package]] name = "mach2" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" dependencies = [ "libc", ] +[[package]] +name = "macho-unwind-info" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4bdc8b0ce69932332cf76d24af69c3a155242af95c226b2ab6c2e371ed1149" +dependencies = [ + "thiserror 2.0.12", + "zerocopy 0.8.26", + "zerocopy-derive 0.8.26", +] + [[package]] name = "managed" version = "0.8.0" @@ -1607,11 +1941,26 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] [[package]] name = "metrics" @@ -1665,22 +2014,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] @@ -1704,7 +2054,7 @@ dependencies = [ "libc", "num_enum", "vmm-sys-util", - "zerocopy 0.8.24", + "zerocopy 0.8.26", ] [[package]] @@ -1782,22 +2132,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -1806,7 +2157,9 @@ version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ + "flate2", "memchr", + "ruzstd", ] [[package]] @@ -1815,6 +2168,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "oorandom" version = "11.1.5" @@ -1932,39 +2291,192 @@ dependencies = [ ] [[package]] -name = "parking_lot" -version = "0.12.3" +name = "pango" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94" +dependencies = [ + "bitflags 1.3.2", + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e134909a9a293e04d2cc31928aa95679c5e4df954d0b85483159bd20d8f047f" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "pangocairo" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ad2ec87789371b551fd2367c10aa37060412ffd3e60abd99491b21b93a3f9b" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "glib", + "libc", + "pango", + "pangocairo-sys", +] + +[[package]] +name = "pangocairo-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "848d2df9b7f1a8c7a19d994de443bcbe5d4382610ccb8e64247f932be74fcf76" +dependencies = [ + "cairo-sys-rs", + "glib-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pe-unwind-info" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500fa4cdeacd98997c5865e3d0d1cb8fe7e9d7d75ecc775e07989a433a9a9a59" +dependencies = [ + "arrayvec", + "bitflags 2.9.1", + "thiserror 2.0.12", + "zerocopy 0.8.26", + "zerocopy-derive 0.8.26", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "piet" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e381186490a3e2017a506d62b759ea8eaf4be14666b13ed53973e8ae193451b1" +dependencies = [ + "kurbo", + "unic-bidi", +] + +[[package]] +name = "piet-cairo" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc0b38ac300c79deb9bfc8c7f91a08f2b080338648f7202981094b22321bb9" +dependencies = [ + "cairo-rs", + "pango", + "pangocairo", + "piet", + "unicode-segmentation", + "xi-unicode", +] + +[[package]] +name = "piet-common" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "9dd8497cc0bcfecb1e14e027428c5e3eaf9af6e14761176e1212006d8bdba387" dependencies = [ - "lock_api", - "parking_lot_core", + "cairo-rs", + "cairo-sys-rs", + "cfg-if", + "core-graphics", + "piet", + "piet-cairo", + "piet-coregraphics", + "piet-direct2d", + "piet-web", + "png", + "wasm-bindgen", + "web-sys", ] [[package]] -name = "parking_lot_core" -version = "0.9.10" +name = "piet-coregraphics" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "a819b41d2ddb1d8abf3e45e49422f866cba281b4abb5e2fb948bba06e2c3d3f7" dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.52.6", + "associative-cache", + "core-foundation", + "core-foundation-sys", + "core-graphics", + "core-text", + "foreign-types", + "piet", ] [[package]] -name = "paste" -version = "1.0.15" +name = "piet-direct2d" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "dd00e91df4f987be40eb13042afe6ee9e54468466bdb7486390b40d4fef0993e" +dependencies = [ + "associative-cache", + "dwrote", + "piet", + "utf16_lit", + "winapi", + "wio", +] [[package]] -name = "percent-encoding" -version = "2.3.1" +name = "piet-web" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "3a560232a94e535979923d49062d1c6d5407b3804bcd0d0b4cb9e25a9b41db1e" +dependencies = [ + "js-sys", + "piet", + "unicode-segmentation", + "wasm-bindgen", + "web-sys", + "xi-unicode", +] [[package]] name = "pin-project" @@ -1983,7 +2495,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -2038,11 +2550,24 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "portable-atomic" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" [[package]] name = "portable-atomic-util" @@ -2053,13 +2578,22 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.24", + "zerocopy 0.8.26", ] [[package]] @@ -2069,7 +2603,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", ] [[package]] @@ -2135,20 +2703,20 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] name = "quanta" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd1fe6824cea6538803de3ff1bc0cf3949024db3d43c9643024bfb33a807c0e" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" dependencies = [ "crossbeam-utils", "libc", "once_cell", "raw-cpuid", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "web-sys", "winapi", ] @@ -2170,9 +2738,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "radix_trie" @@ -2210,7 +2778,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", ] [[package]] @@ -2262,9 +2830,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.11" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags 2.9.1", ] @@ -2275,7 +2843,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", "libredox", "thiserror 2.0.12", ] @@ -2326,9 +2894,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.21" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8cea6b35bcceb099f30173754403d2eba0a5dc18cea3630fccd88251909288" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64", "bytes", @@ -2379,7 +2947,7 @@ dependencies = [ "quote", "rust-embed-utils", "shellexpand", - "syn", + "syn 2.0.104", "walkdir", ] @@ -2396,9 +2964,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -2417,9 +2985,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.5" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ "bitflags 2.9.1", "errno", @@ -2430,9 +2998,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "rusty-fork" @@ -2446,6 +3014,15 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "ruzstd" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f" +dependencies = [ + "twox-hash", +] + [[package]] name = "ryu" version = "1.0.20" @@ -2463,9 +3040,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.3.3" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea091f6cac2595aa38993f04f4ee692ed43757035c36e67c180b6828356385b1" +checksum = "22b2d775fb28f245817589471dd49c5edf64237f4a19d10ce9a92ff4651a27f4" dependencies = [ "sdd", ] @@ -2499,7 +3076,7 @@ checksum = "22fc4f90c27b57691bbaf11d8ecc7cfbfe98a4da6dbe60226115d322aa80c06e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -2540,7 +3117,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -2557,9 +3134,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" dependencies = [ "serde", ] @@ -2598,14 +3175,14 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -2658,6 +3235,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "sketches-ddsketch" version = "0.3.0" @@ -2666,24 +3249,21 @@ checksum = "c1e9a774a6c28142ac54bb25d25562e6bcf957493a184f15ad4eebccb23e410a" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2713,12 +3293,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.104" @@ -2741,15 +3338,34 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", ] +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tempfile" version = "3.20.0" @@ -2757,7 +3373,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.3.3", "once_cell", "rustix", "windows-sys 0.59.0", @@ -2798,7 +3414,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -2809,24 +3425,23 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] name = "tinystr" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", "zerovec", @@ -2870,7 +3485,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -2886,38 +3501,56 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.20" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.27", ] [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow", + "toml_write", + "winnow 0.7.11", ] +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + [[package]] name = "tonic" version = "0.13.1" @@ -2984,6 +3617,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" +[[package]] +name = "trace_dump" +version = "0.0.0" +dependencies = [ + "addr2line", + "blake3", + "piet-common", +] + [[package]] name = "tracing" version = "0.1.41" @@ -2998,13 +3640,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -3113,9 +3755,9 @@ dependencies = [ [[package]] name = "tracy-client" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d90a2c01305b02b76fdd89ac8608bae27e173c829a35f7d76a345ab5d33836db" +checksum = "ef54005d3d760186fd662dad4b7bb27ecd5531cdef54d1573ebd3f20a9205ed7" dependencies = [ "loom", "once_cell", @@ -3124,9 +3766,9 @@ dependencies = [ [[package]] name = "tracy-client-sys" -version = "0.24.3" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fff37da548239c3bf9e64a12193d261e8b22b660991c6fd2df057c168f435f" +checksum = "5f9612d9503675b07b244922ea6f6f3cdd88c43add1b3498084613fc88cdf69d" dependencies = [ "cc", "windows-targets 0.52.6", @@ -3138,6 +3780,22 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typenum" version = "1.18.0" @@ -3150,12 +3808,69 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unic-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1356b759fb6a82050666f11dce4b6fe3571781f1449f3ef78074e408d468ec09" +dependencies = [ + "matches", + "unic-ucd-bidi", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-bidi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "url" version = "2.5.4" @@ -3168,10 +3883,10 @@ dependencies = [ ] [[package]] -name = "utf16_iter" -version = "1.0.5" +name = "utf16_lit" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" +checksum = "14706d2a800ee8ff38c1d3edb873cd616971ea59eb7c0d046bb44ef59b06a1ae" [[package]] name = "utf8_iter" @@ -3191,7 +3906,7 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ - "getrandom 0.3.2", + "getrandom 0.3.3", "js-sys", "serde", "wasm-bindgen", @@ -3209,6 +3924,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + [[package]] name = "version_check" version = "0.9.5" @@ -3255,9 +3976,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -3290,7 +4011,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn", + "syn 2.0.104", "wasm-bindgen-shared", ] @@ -3325,7 +4046,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3403,16 +4124,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" -dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows" version = "0.61.3" @@ -3420,7 +4131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ "windows-collections", - "windows-core 0.61.2", + "windows-core", "windows-future", "windows-link", "windows-numerics", @@ -3432,20 +4143,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" dependencies = [ - "windows-core 0.61.2", -] - -[[package]] -name = "windows-core" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" -dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", + "windows-core", ] [[package]] @@ -3454,11 +4152,11 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", + "windows-implement", + "windows-interface", "windows-link", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-result", + "windows-strings", ] [[package]] @@ -3467,22 +4165,11 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ - "windows-core 0.61.2", + "windows-core", "windows-link", "windows-threading", ] -[[package]] -name = "windows-implement" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-implement" version = "0.60.0" @@ -3491,18 +4178,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -3513,7 +4189,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -3528,19 +4204,10 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ - "windows-core 0.61.2", + "windows-core", "windows-link", ] -[[package]] -name = "windows-result" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-result" version = "0.3.4" @@ -3550,16 +4217,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-strings" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" -dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", -] - [[package]] name = "windows-strings" version = "0.4.2" @@ -3744,13 +4401,31 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.6" +version = "0.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +dependencies = [ + "memchr", +] + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" @@ -3761,22 +4436,22 @@ dependencies = [ ] [[package]] -name = "write16" -version = "1.0.0" +name = "writeable" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] -name = "writeable" -version = "0.5.5" +name = "xi-unicode" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" [[package]] name = "yoke" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", @@ -3786,13 +4461,13 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.7.5" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", "synstructure", ] @@ -3808,11 +4483,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ - "zerocopy-derive 0.8.24", + "zerocopy-derive 0.8.26", ] [[package]] @@ -3823,18 +4498,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] [[package]] @@ -3854,15 +4529,26 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", "synstructure", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + [[package]] name = "zerovec" -version = "0.10.4" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ "yoke", "zerofrom", @@ -3871,11 +4557,11 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.10.3" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.104", ] diff --git a/Cargo.toml b/Cargo.toml index 2c16795c3..18a311405 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,11 +10,14 @@ members = [ "src/hyperlight_guest", "src/hyperlight_host", "src/hyperlight_guest_capi", + "src/hyperlight_guest_tracing", + "src/hyperlight_guest_tracing_macro", "src/hyperlight_testing", "fuzz", "src/hyperlight_guest_bin", "src/hyperlight_component_util", "src/hyperlight_component_macro", + "src/trace_dump", ] # Guests have custom linker flags, so we need to exclude them from the workspace exclude = [ @@ -39,6 +42,8 @@ hyperlight-host = { path = "src/hyperlight_host", version = "0.7.0", default-fea hyperlight-guest = { path = "src/hyperlight_guest", version = "0.7.0", default-features = false } hyperlight-guest-bin = { path = "src/hyperlight_guest_bin", version = "0.7.0", default-features = false } hyperlight-testing = { path = "src/hyperlight_testing", default-features = false } +hyperlight-guest-tracing = { path = "src/hyperlight_guest_tracing", default-features = false } +hyperlight-guest-tracing-macro = { path = "src/hyperlight_guest_tracing_macro", default-features = false } hyperlight-component-util = { path = "src/hyperlight_component_util", version = "0.7.0", default-features = false } hyperlight-component-macro = { path = "src/hyperlight_component_macro", version = "0.7.0", default-features = false } diff --git a/Justfile b/Justfile index 66b06a587..cb2890592 100644 --- a/Justfile +++ b/Justfile @@ -38,11 +38,11 @@ witguest-wit: cargo install --locked wasm-tools cd src/tests/rust_guests/witguest && wasm-tools component wit guest.wit -w -o interface.wasm -build-rust-guests target=default-target: (witguest-wit) - cd src/tests/rust_guests/callbackguest && cargo build --profile={{ if target == "debug" { "dev" } else { target } }} - cd src/tests/rust_guests/simpleguest && cargo build --profile={{ if target == "debug" { "dev" } else { target } }} - cd src/tests/rust_guests/dummyguest && cargo build --profile={{ if target == "debug" { "dev" } else { target } }} - cd src/tests/rust_guests/witguest && cargo build --profile={{ if target == "debug" { "dev" } else { target } }} +build-rust-guests target=default-target features="": (witguest-wit) + cd src/tests/rust_guests/callbackguest && cargo build {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} + cd src/tests/rust_guests/simpleguest && cargo build {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} + cd src/tests/rust_guests/dummyguest && cargo build {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} + cd src/tests/rust_guests/witguest && cargo build {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} @move-rust-guests target=default-target: cp {{ callbackguest_source }}/{{ target }}/callbackguest* {{ rust_guests_bin_dir }}/{{ target }}/ diff --git a/docs/hyperlight-metrics-logs-and-traces.md b/docs/hyperlight-metrics-logs-and-traces.md index 9158b43e2..ab72fe69d 100644 --- a/docs/hyperlight-metrics-logs-and-traces.md +++ b/docs/hyperlight-metrics-logs-and-traces.md @@ -91,3 +91,50 @@ NOTE: when running this on windows that this is a linux container, so you will n ``` Once the container or the exe is running, the trace output can be viewed in the jaeger UI at [http://localhost:16686/search](http://localhost:16686/search). + +## Guest Tracing, Unwinding, and Memory Profiling + +Hyperlight provides advanced observability features for guest code running inside micro virtual machines. You can enable guest-side tracing, stack unwinding, and memory profiling using the `trace_guest`, `unwind_guest`, and `mem_profile` features. This section explains how to build, run, and inspect guest traces. + +### Building a Guest with Tracing Support + +To build a guest with tracing enabled, use the following commands: + +```bash +just build-rust-guests debug trace_guest +just move-rust-guests debug +``` + +This will build the guest binaries with the `trace_guest` feature enabled and move them to the appropriate location for use by the host. + +### Running a Hyperlight Example with Guest Tracing + +Once the guest is built, you can run a Hyperlight example with guest tracing enabled. For example: + +```bash +cargo run --example hello-world --features trace_guest +``` + +This will execute the `hello-world` example, loading the guest with tracing enabled. During execution, trace data will be collected and written to a file in the `trace` directory. + +### Inspecting Guest Trace Files + +To inspect the trace file generated by the guest, use the `trace_dump` crate. You will need the path to the guest symbols and the trace file. Run the following command: + +```bash +cargo run -p trace_dump list_frames +``` + +Replace `` with the path to the guest binary or symbol file, and `` with the path to the trace file in the `trace` directory. + +This command will list the stack frames and tracing information captured during guest execution, allowing you to analyze guest behavior, stack traces, and memory usage. + +#### Example + +```bash +cargo run -p trace_dump ./src/tests/rust_guests/bin/debug/simpleguest ./trace/.trace list_frames +``` + +You can use additional features such as `unwind_guest` and `mem_profile` by enabling them during the build and run steps. Refer to the guest and host documentation for more details on these features. + +> **Note:** Make sure to follow the build and run steps in order, and ensure that the guest binaries are up to date before running the host example. diff --git a/src/hyperlight_common/Cargo.toml b/src/hyperlight_common/Cargo.toml index 1afea5022..ac83d6322 100644 --- a/src/hyperlight_common/Cargo.toml +++ b/src/hyperlight_common/Cargo.toml @@ -25,6 +25,9 @@ spin = "0.10.0" [features] default = ["tracing"] fuzzing = ["dep:arbitrary"] +trace_guest = [] +unwind_guest = [] +mem_profile = [] std = [] [dev-dependencies] @@ -32,4 +35,4 @@ hyperlight-testing = { workspace = true } [lib] bench = false # see https://bheisler.github.io/criterion.rs/book/faq.html#cargo-bench-gives-unrecognized-option-errors-for-valid-command-line-options -doctest = false # reduce noise in test output \ No newline at end of file +doctest = false # reduce noise in test output diff --git a/src/hyperlight_common/src/outb.rs b/src/hyperlight_common/src/outb.rs index 1d15cfb90..54bba6f74 100644 --- a/src/hyperlight_common/src/outb.rs +++ b/src/hyperlight_common/src/outb.rs @@ -90,11 +90,23 @@ impl TryFrom for Exception { /// - CallFunction: makes a call to a host function, /// - Abort: aborts the execution of the guest, /// - DebugPrint: prints a message to the host +/// - TraceRecordStack: records the stack trace of the guest +/// - TraceMemoryAlloc: records memory allocation events +/// - TraceMemoryFree: records memory deallocation events +/// - TraceRecord: records a trace event in the guest pub enum OutBAction { Log = 99, CallFunction = 101, Abort = 102, DebugPrint = 103, + #[cfg(feature = "unwind_guest")] + TraceRecordStack = 104, + #[cfg(feature = "mem_profile")] + TraceMemoryAlloc = 105, + #[cfg(feature = "mem_profile")] + TraceMemoryFree = 106, + #[cfg(feature = "trace_guest")] + TraceRecord = 107, } impl TryFrom for OutBAction { @@ -105,6 +117,14 @@ impl TryFrom for OutBAction { 101 => Ok(OutBAction::CallFunction), 102 => Ok(OutBAction::Abort), 103 => Ok(OutBAction::DebugPrint), + #[cfg(feature = "unwind_guest")] + 104 => Ok(OutBAction::TraceRecordStack), + #[cfg(feature = "mem_profile")] + 105 => Ok(OutBAction::TraceMemoryAlloc), + #[cfg(feature = "mem_profile")] + 106 => Ok(OutBAction::TraceMemoryFree), + #[cfg(feature = "trace_guest")] + 107 => Ok(OutBAction::TraceRecord), _ => Err(anyhow::anyhow!("Invalid OutBAction value: {}", val)), } } diff --git a/src/hyperlight_guest/Cargo.toml b/src/hyperlight_guest/Cargo.toml index 8fce35293..d55aa4f92 100644 --- a/src/hyperlight_guest/Cargo.toml +++ b/src/hyperlight_guest/Cargo.toml @@ -15,3 +15,9 @@ Provides only the essential building blocks for interacting with the host enviro anyhow = { version = "1.0.98", default-features = false } serde_json = { version = "1.0", default-features = false, features = ["alloc"] } hyperlight-common = { workspace = true } +hyperlight-guest-tracing = { workspace = true, optional = true } +hyperlight-guest-tracing-macro = { workspace = true} + +[features] +default = [] +trace_guest = ["dep:hyperlight-guest-tracing"] diff --git a/src/hyperlight_guest/src/exit.rs b/src/hyperlight_guest/src/exit.rs index 82d8a9545..3043a7023 100644 --- a/src/hyperlight_guest/src/exit.rs +++ b/src/hyperlight_guest/src/exit.rs @@ -21,17 +21,22 @@ use hyperlight_common::outb::OutBAction; /// Halt the execution of the guest and returns control to the host. #[inline(never)] +#[hyperlight_guest_tracing_macro::trace_function] pub fn halt() { + // Ensure all tracing data is flushed before halting + hyperlight_guest_tracing_macro::flush!(); unsafe { asm!("hlt", options(nostack)) } } /// Exits the VM with an Abort OUT action and code 0. #[unsafe(no_mangle)] +#[hyperlight_guest_tracing_macro::trace_function] pub extern "C" fn abort() -> ! { abort_with_code(&[0, 0xFF]) } /// Exits the VM with an Abort OUT action and a specific code. +#[hyperlight_guest_tracing_macro::trace_function] pub fn abort_with_code(code: &[u8]) -> ! { outb(OutBAction::Abort as u16, code); outb(OutBAction::Abort as u16, &[0xFF]); // send abort terminator (if not included in code) @@ -42,6 +47,7 @@ pub fn abort_with_code(code: &[u8]) -> ! { /// /// # Safety /// This function is unsafe because it dereferences a raw pointer. +#[hyperlight_guest_tracing_macro::trace_function] pub unsafe fn abort_with_code_and_message(code: &[u8], message_ptr: *const c_char) -> ! { unsafe { // Step 1: Send abort code (typically 1 byte, but `code` allows flexibility) @@ -63,6 +69,8 @@ pub unsafe fn abort_with_code_and_message(code: &[u8], message_ptr: *const c_cha /// OUT bytes to the host through multiple exits. pub(crate) fn outb(port: u16, data: &[u8]) { + // Ensure all tracing data is flushed before sending OUT bytes + hyperlight_guest_tracing_macro::flush!(); unsafe { let mut i = 0; while i < data.len() { diff --git a/src/hyperlight_guest/src/guest_handle/host_comm.rs b/src/hyperlight_guest/src/guest_handle/host_comm.rs index 97e90f3a6..431415752 100644 --- a/src/hyperlight_guest/src/guest_handle/host_comm.rs +++ b/src/hyperlight_guest/src/guest_handle/host_comm.rs @@ -35,6 +35,7 @@ use crate::exit::out32; impl GuestHandle { /// Get user memory region as bytes. + #[hyperlight_guest_tracing_macro::trace_function] pub fn read_n_bytes_from_user_memory(&self, num: u64) -> Result> { let peb_ptr = self.peb().unwrap(); let user_memory_region_ptr = unsafe { (*peb_ptr).init_data.ptr as *mut u8 }; @@ -63,6 +64,7 @@ impl GuestHandle { /// /// When calling `call_host_function`, this function is called /// internally to get the return value. + #[hyperlight_guest_tracing_macro::trace_function] pub fn get_host_return_value>(&self) -> Result { let return_value = self .try_pop_shared_input_data_into::() @@ -83,6 +85,7 @@ impl GuestHandle { /// /// Note: The function return value must be obtained by calling /// `get_host_return_value`. + #[hyperlight_guest_tracing_macro::trace_function] pub fn call_host_function_without_returning_result( &self, function_name: &str, @@ -114,6 +117,7 @@ impl GuestHandle { /// sends it to the host, and then retrieves the return value. /// /// The return value is deserialized into the specified type `T`. + #[hyperlight_guest_tracing_macro::trace_function] pub fn call_host_function>( &self, function_name: &str, @@ -124,6 +128,7 @@ impl GuestHandle { self.get_host_return_value::() } + #[hyperlight_guest_tracing_macro::trace_function] pub fn get_host_function_details(&self) -> HostFunctionDetails { let peb_ptr = self.peb().unwrap(); let host_function_details_buffer = @@ -140,6 +145,7 @@ impl GuestHandle { } /// Write an error to the shared output data buffer. + #[hyperlight_guest_tracing_macro::trace_function] pub fn write_error(&self, error_code: ErrorCode, message: Option<&str>) { let guest_error: GuestError = GuestError::new( error_code.clone(), @@ -155,6 +161,7 @@ impl GuestHandle { } /// Log a message with the specified log level, source, caller, source file, and line number. + #[hyperlight_guest_tracing_macro::trace_function] pub fn log_message( &self, log_level: LogLevel, diff --git a/src/hyperlight_guest/src/guest_handle/io.rs b/src/hyperlight_guest/src/guest_handle/io.rs index ce8c82b0c..6c6fe7ae4 100644 --- a/src/hyperlight_guest/src/guest_handle/io.rs +++ b/src/hyperlight_guest/src/guest_handle/io.rs @@ -27,6 +27,7 @@ use crate::error::{HyperlightGuestError, Result}; impl GuestHandle { /// Pops the top element from the shared input data buffer and returns it as a T + #[hyperlight_guest_tracing_macro::trace_function] pub fn try_pop_shared_input_data_into(&self) -> Result where T: for<'a> TryFrom<&'a [u8]>, @@ -78,8 +79,10 @@ impl GuestHandle { } }; + hyperlight_guest_tracing_macro::trace!("Start copy of data"); // update the stack pointer to point to the element we just popped of since that is now free idb[..8].copy_from_slice(&last_element_offset_rel.to_le_bytes()); + hyperlight_guest_tracing_macro::trace!("Finish copy of data"); // zero out popped off buffer idb[last_element_offset_rel as usize..stack_ptr_rel as usize].fill(0); @@ -88,6 +91,7 @@ impl GuestHandle { } /// Pushes the given data onto the shared output data buffer. + #[hyperlight_guest_tracing_macro::trace_function] pub fn push_shared_output_data(&self, data: Vec) -> Result<()> { let peb_ptr = self.peb().unwrap(); let output_stack_size = unsafe { (*peb_ptr).output_stack.size as usize }; @@ -133,7 +137,9 @@ impl GuestHandle { } // write the actual data + hyperlight_guest_tracing_macro::trace!("Start copy of data"); odb[stack_ptr_rel as usize..stack_ptr_rel as usize + data.len()].copy_from_slice(&data); + hyperlight_guest_tracing_macro::trace!("Finish copy of data"); // write the offset to the newly written data, to the top of the stack let bytes: [u8; 8] = stack_ptr_rel.to_le_bytes(); diff --git a/src/hyperlight_guest_bin/Cargo.toml b/src/hyperlight_guest_bin/Cargo.toml index 1b8202f51..ff30980b4 100644 --- a/src/hyperlight_guest_bin/Cargo.toml +++ b/src/hyperlight_guest_bin/Cargo.toml @@ -17,10 +17,14 @@ and third-party code used by our C-API needed to build a native hyperlight-guest default = ["libc", "printf"] libc = [] # compile musl libc printf = [] # compile printf +trace_guest = ["hyperlight-common/trace_guest", "dep:hyperlight-guest-tracing", "hyperlight-guest/trace_guest"] +mem_profile = ["hyperlight-common/unwind_guest","hyperlight-common/mem_profile"] [dependencies] hyperlight-guest = { workspace = true, default-features = false } hyperlight-common = { workspace = true, default-features = false } +hyperlight-guest-tracing = { workspace = true, optional = true } +hyperlight-guest-tracing-macro = { workspace = true } buddy_system_allocator = "0.11.0" log = { version = "0.4", default-features = false } spin = "0.10.0" diff --git a/src/hyperlight_guest_bin/src/exceptions/gdt.rs b/src/hyperlight_guest_bin/src/exceptions/gdt.rs index 0a3b2cfb6..0da032321 100644 --- a/src/hyperlight_guest_bin/src/exceptions/gdt.rs +++ b/src/hyperlight_guest_bin/src/exceptions/gdt.rs @@ -72,6 +72,7 @@ struct GdtPointer { } /// Load the GDT +#[hyperlight_guest_tracing_macro::trace_function] pub unsafe fn load_gdt() { unsafe { let gdt_ptr = GdtPointer { diff --git a/src/hyperlight_guest_bin/src/exceptions/handler.rs b/src/hyperlight_guest_bin/src/exceptions/handler.rs index 5bc1a7e09..beca91247 100644 --- a/src/hyperlight_guest_bin/src/exceptions/handler.rs +++ b/src/hyperlight_guest_bin/src/exceptions/handler.rs @@ -23,6 +23,7 @@ use hyperlight_guest::exit::abort_with_code_and_message; /// Exception handler #[unsafe(no_mangle)] +#[hyperlight_guest_tracing_macro::trace_function] pub extern "C" fn hl_exception_handler( stack_pointer: u64, exception_number: u64, diff --git a/src/hyperlight_guest_bin/src/exceptions/idt.rs b/src/hyperlight_guest_bin/src/exceptions/idt.rs index 3107c4df4..820e96861 100644 --- a/src/hyperlight_guest_bin/src/exceptions/idt.rs +++ b/src/hyperlight_guest_bin/src/exceptions/idt.rs @@ -71,6 +71,7 @@ impl IdtEntry { // Architectures Software Developer's Manual). pub(crate) static mut IDT: [IdtEntry; 256] = unsafe { core::mem::zeroed() }; +#[hyperlight_guest_tracing_macro::trace_function] pub(crate) fn init_idt() { set_idt_entry(Exception::DivideByZero as usize, _do_excp0); // Divide by zero set_idt_entry(Exception::Debug as usize, _do_excp1); // Debug diff --git a/src/hyperlight_guest_bin/src/exceptions/idtr.rs b/src/hyperlight_guest_bin/src/exceptions/idtr.rs index dc547a199..5e388efef 100644 --- a/src/hyperlight_guest_bin/src/exceptions/idtr.rs +++ b/src/hyperlight_guest_bin/src/exceptions/idtr.rs @@ -40,6 +40,7 @@ impl Idtr { } } +#[hyperlight_guest_tracing_macro::trace_function] pub(crate) unsafe fn load_idt() { unsafe { init_idt(); diff --git a/src/hyperlight_guest_bin/src/guest_function/call.rs b/src/hyperlight_guest_bin/src/guest_function/call.rs index bdaed4212..66ff6e833 100644 --- a/src/hyperlight_guest_bin/src/guest_function/call.rs +++ b/src/hyperlight_guest_bin/src/guest_function/call.rs @@ -27,6 +27,7 @@ use crate::{GUEST_HANDLE, REGISTERED_GUEST_FUNCTIONS}; type GuestFunc = fn(&FunctionCall) -> Result>; +#[hyperlight_guest_tracing_macro::trace_function] pub(crate) fn call_guest_function(function_call: FunctionCall) -> Result> { // Validate this is a Guest Function Call if function_call.function_call_type() != FunctionCallType::Guest { @@ -60,6 +61,7 @@ pub(crate) fn call_guest_function(function_call: FunctionCall) -> Result core::mem::transmute::(function_pointer) }; + hyperlight_guest_tracing_macro::trace!("Calling guest function"); p_function(&function_call) } else { // The given function is not registered. The guest should implement a function called guest_dispatch_function to handle this. @@ -71,6 +73,7 @@ pub(crate) fn call_guest_function(function_call: FunctionCall) -> Result fn guest_dispatch_function(function_call: FunctionCall) -> Result>; } + hyperlight_guest_tracing_macro::trace!("Guest function {} not found"); unsafe { guest_dispatch_function(function_call) } } } @@ -79,6 +82,7 @@ pub(crate) fn call_guest_function(function_call: FunctionCall) -> Result // and we will leak memory as the epilogue will not be called as halt() is not going to return. #[unsafe(no_mangle)] #[inline(never)] +#[hyperlight_guest_tracing_macro::trace_function] fn internal_dispatch_function() -> Result<()> { let handle = unsafe { GUEST_HANDLE }; @@ -99,6 +103,7 @@ fn internal_dispatch_function() -> Result<()> { // This is implemented as a separate function to make sure that epilogue in the internal_dispatch_function is called before the halt() // which if it were included in the internal_dispatch_function cause the epilogue to not be called because the halt() would not return // when running in the hypervisor. +#[hyperlight_guest_tracing_macro::trace_function] pub(crate) extern "C" fn dispatch_function() { let _ = internal_dispatch_function(); halt(); diff --git a/src/hyperlight_guest_bin/src/lib.rs b/src/hyperlight_guest_bin/src/lib.rs index 473bfbfc1..15a683b33 100644 --- a/src/hyperlight_guest_bin/src/lib.rs +++ b/src/hyperlight_guest_bin/src/lib.rs @@ -28,8 +28,11 @@ use guest_function::register::GuestFunctionRegister; use guest_logger::init_logger; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use hyperlight_common::mem::HyperlightPEB; +#[cfg(feature = "mem_profile")] +use hyperlight_common::outb::OutBAction; use hyperlight_guest::exit::{abort_with_code_and_message, halt}; use hyperlight_guest::guest_handle::handle::GuestHandle; +use hyperlight_guest_tracing_macro::{trace, trace_function}; use log::LevelFilter; use spin::Once; @@ -53,9 +56,69 @@ pub mod guest_logger; pub mod host_comm; pub mod memory; +// Globals +#[cfg(feature = "mem_profile")] +struct ProfiledLockedHeap(LockedHeap); +#[cfg(feature = "mem_profile")] +unsafe impl alloc::alloc::GlobalAlloc for ProfiledLockedHeap { + unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 { + let addr = unsafe { self.0.alloc(layout) }; + unsafe { + core::arch::asm!("out dx, al", + in("dx") OutBAction::TraceMemoryAlloc as u16, + in("rax") layout.size() as u64, + in("rcx") addr as u64); + } + addr + } + unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) { + unsafe { + core::arch::asm!("out dx, al", + in("dx") OutBAction::TraceMemoryFree as u16, + in("rax") layout.size() as u64, + in("rcx") ptr as u64); + self.0.dealloc(ptr, layout) + } + } + unsafe fn alloc_zeroed(&self, layout: core::alloc::Layout) -> *mut u8 { + let addr = unsafe { self.0.alloc_zeroed(layout) }; + unsafe { + core::arch::asm!("out dx, al", + in("dx") OutBAction::TraceMemoryAlloc as u16, + in("rax") layout.size() as u64, + in("rcx") addr as u64); + } + addr + } + unsafe fn realloc( + &self, + ptr: *mut u8, + layout: core::alloc::Layout, + new_size: usize, + ) -> *mut u8 { + let new_ptr = unsafe { self.0.realloc(ptr, layout, new_size) }; + unsafe { + core::arch::asm!("out dx, al", + in("dx") OutBAction::TraceMemoryFree as u16, + in("rax") layout.size() as u64, + in("rcx") ptr); + core::arch::asm!("out dx, al", + in("dx") OutBAction::TraceMemoryAlloc as u16, + in("rax") new_size as u64, + in("rcx") new_ptr); + } + new_ptr + } +} + // === Globals === +#[cfg(not(feature = "mem_profile"))] #[global_allocator] pub(crate) static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::empty(); +#[cfg(feature = "mem_profile")] +#[global_allocator] +pub(crate) static HEAP_ALLOCATOR: ProfiledLockedHeap<32> = + ProfiledLockedHeap(LockedHeap::<32>::empty()); pub(crate) static mut GUEST_HANDLE: GuestHandle = GuestHandle::new(); pub(crate) static mut REGISTERED_GUEST_FUNCTIONS: GuestFunctionRegister = @@ -92,6 +155,7 @@ unsafe extern "C" { static INIT: Once = Once::new(); #[unsafe(no_mangle)] +#[trace_function] pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_level: u64) { if peb_address == 0 { panic!("PEB address is null"); @@ -128,7 +192,11 @@ pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_leve let heap_start = (*peb_ptr).guest_heap.ptr as usize; let heap_size = (*peb_ptr).guest_heap.size as usize; - HEAP_ALLOCATOR + #[cfg(not(feature = "mem_profile"))] + let heap_allocator = &HEAP_ALLOCATOR; + #[cfg(feature = "mem_profile")] + let heap_allocator = &HEAP_ALLOCATOR.0; + heap_allocator .try_lock() .expect("Failed to access HEAP_ALLOCATOR") .init(heap_start, heap_size); @@ -137,6 +205,7 @@ pub extern "C" fn entrypoint(peb_address: u64, seed: u64, ops: u64, max_log_leve (*peb_ptr).guest_function_dispatch_ptr = dispatch_function as usize as u64; + trace!("hyperlight_main"); hyperlight_main(); } }); diff --git a/src/hyperlight_guest_tracing/Cargo.toml b/src/hyperlight_guest_tracing/Cargo.toml new file mode 100644 index 000000000..e77144eaa --- /dev/null +++ b/src/hyperlight_guest_tracing/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "hyperlight-guest-tracing" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +readme.workspace = true +description = """Provides the tracing functionality for the hyperlight guest.""" + +[dependencies] +hyperlight-common = { workspace = true, default-features = false, features = ["trace_guest"] } +spin = "0.10.0" + +[lints] +workspace = true diff --git a/src/hyperlight_guest_tracing/src/lib.rs b/src/hyperlight_guest_tracing/src/lib.rs new file mode 100644 index 000000000..941ac7600 --- /dev/null +++ b/src/hyperlight_guest_tracing/src/lib.rs @@ -0,0 +1,161 @@ +/* +Copyright 2025 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. +*/ +#![no_std] + +// === Dependencies === +extern crate alloc; + +use core::mem::MaybeUninit; + +use hyperlight_common::outb::OutBAction; +use spin::Mutex; + +/// Global trace buffer for storing trace records. +static TRACE_BUFFER: Mutex = Mutex::new(TraceBuffer::new()); + +/// Maximum number of entries in the trace buffer. +const MAX_NO_OF_ENTRIES: usize = 16; + +/// Maximum length of a trace message in bytes. +pub const MAX_TRACE_MSG_LEN: usize = 64; + +#[derive(Debug, Copy, Clone)] +/// Represents a trace record of a guest with a number of cycles and a message. +pub struct TraceRecord { + /// The number of CPU cycles returned by the invariant TSC. + pub cycles: u64, + /// The length of the message in bytes. + pub msg_len: usize, + /// The message associated with the trace record. + pub msg: [u8; MAX_TRACE_MSG_LEN], +} + +/// A buffer for storing trace records. +struct TraceBuffer { + /// The entries in the trace buffer. + entries: [TraceRecord; MAX_NO_OF_ENTRIES], + /// The index where the next entry will be written. + write_index: usize, +} + +impl TraceBuffer { + /// Creates a new `TraceBuffer` with uninitialized entries. + const fn new() -> Self { + Self { + entries: unsafe { [MaybeUninit::zeroed().assume_init(); MAX_NO_OF_ENTRIES] }, + write_index: 0, + } + } + + /// Push a new trace record into the buffer. + /// If the buffer is full, it sends the records to the host. + fn push(&mut self, entry: TraceRecord) { + let mut write_index = self.write_index; + + self.entries[write_index] = entry; + write_index = (write_index + 1) % MAX_NO_OF_ENTRIES; + + self.write_index = write_index; + + if write_index == 0 { + // If buffer is full send to host + self.send_to_host(MAX_NO_OF_ENTRIES); + } + } + + /// Flush the trace buffer, sending any remaining records to the host. + fn flush(&mut self) { + if self.write_index > 0 { + self.send_to_host(self.write_index); + self.write_index = 0; // Reset write index after flushing + } + } + + /// Send the trace records to the host. + fn send_to_host(&self, count: usize) { + unsafe { + core::arch::asm!("out dx, al", + in("dx") OutBAction::TraceRecord as u16, + in("rax") count as u64, + in("rcx") &self.entries as * const _ as u64); + } + } +} + +/// Module for checking invariant TSC support and reading the timestamp counter +pub mod invariant_tsc { + use core::arch::x86_64::{__cpuid, _rdtsc}; + + /// Check if the processor supports invariant TSC + /// + /// Returns true if CPUID.80000007H:EDX[8] is set, indicating invariant TSC support + pub fn has_invariant_tsc() -> bool { + // Check if extended CPUID functions are available + let max_extended = unsafe { __cpuid(0x80000000) }; + if max_extended.eax < 0x80000007 { + return false; + } + + // Query CPUID.80000007H for invariant TSC support + let cpuid_result = unsafe { __cpuid(0x80000007) }; + + // Check bit 8 of EDX register for invariant TSC support + (cpuid_result.edx & (1 << 8)) != 0 + } + + /// Read the timestamp counter + /// + /// This function provides a high-performance timestamp by reading the TSC. + /// Should only be used when invariant TSC is supported for reliable timing. + /// + /// # Safety + /// This function uses unsafe assembly instructions but is safe to call. + /// However, the resulting timestamp is only meaningful if invariant TSC is supported. + pub fn read_tsc() -> u64 { + unsafe { _rdtsc() } + } +} + +/// Create a trace record with the given message. +/// +/// Note: The message must not exceed `MAX_TRACE_MSG_LEN` bytes. +/// If the message is too long, it will be skipped. +pub fn create_trace_record(msg: &str) { + if msg.len() > MAX_TRACE_MSG_LEN { + return; // Message too long, skip tracing + } + + let cycles = invariant_tsc::read_tsc(); + + let entry = TraceRecord { + cycles, + msg: { + let mut arr = [0u8; MAX_TRACE_MSG_LEN]; + arr[..msg.len()].copy_from_slice(msg.as_bytes()); + arr + }, + msg_len: msg.len(), + }; + + let mut buffer = TRACE_BUFFER.lock(); + buffer.push(entry); +} + +/// Flush the trace buffer to send any remaining trace records to the host. +pub fn flush_trace_buffer() { + let mut buffer = TRACE_BUFFER.lock(); + buffer.flush(); +} diff --git a/src/hyperlight_guest_tracing_macro/Cargo.toml b/src/hyperlight_guest_tracing_macro/Cargo.toml new file mode 100644 index 000000000..6e749ac20 --- /dev/null +++ b/src/hyperlight_guest_tracing_macro/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "hyperlight-guest-tracing-macro" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +readme.workspace = true +description = """Provides the tracing macros for the hyperlight guest, enabling structured logging and tracing capabilities.""" + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0.40" +syn = { version = "2.0.104", features = ["full"] } + +[features] +default = [] + +[lib] +proc-macro = true + +[lints] +workspace = true diff --git a/src/hyperlight_guest_tracing_macro/src/lib.rs b/src/hyperlight_guest_tracing_macro/src/lib.rs new file mode 100644 index 000000000..bc05d1c72 --- /dev/null +++ b/src/hyperlight_guest_tracing_macro/src/lib.rs @@ -0,0 +1,129 @@ +/* +Copyright 2025 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 proc_macro::TokenStream; +use quote::quote; +use syn::parse::{Parse, ParseStream}; +use syn::{ItemFn, Lit, parse_macro_input}; + +/// A procedural macro attribute for tracing function calls. +/// Usage: +/// ```rust +/// #[trace_function] +/// fn my_function() { +/// // // Function body +/// } +/// ``` +/// +/// This macro will create a trace record when the function is called, if the `trace_guest` +/// feature is enabled. +/// +/// The trace record will contain the function name as a string. +/// Note: This macro is intended to be used with the `hyperlight_guest_tracing` crate. +#[proc_macro_attribute] +pub fn trace_function(_attr: TokenStream, item: TokenStream) -> TokenStream { + let input_fn = parse_macro_input!(item as ItemFn); + + let fn_name = &input_fn.sig.ident; + let fn_name_str = fn_name.to_string(); + let fn_vis = &input_fn.vis; + let fn_sig = &input_fn.sig; + let fn_block = &input_fn.block; + let fn_attrs = &input_fn.attrs; + + let expanded = quote! { + #(#fn_attrs)* + #fn_vis #fn_sig { + #[cfg(feature = "trace_guest")] + hyperlight_guest_tracing::create_trace_record(#fn_name_str); + + // Call the original function body + #fn_block + } + }; + + TokenStream::from(expanded) +} + +/// Input structure for the trace macro +struct TraceInput { + message: Lit, +} + +impl Parse for TraceInput { + fn parse(input: ParseStream) -> syn::Result { + Ok(TraceInput { + message: input.parse()?, + }) + } +} + +/// This macro creates a trace record with a message. +/// +/// Usage: +/// ```rust +/// trace!("message"); +/// ``` +#[proc_macro] +pub fn trace(input: TokenStream) -> TokenStream { + // Convert to proc_macro2::TokenStream for parsing + let input2: proc_macro2::TokenStream = input.clone().into(); + + // Try to parse as message + if let Ok(parsed) = syn::parse2::(input2) { + let trace_message = match parsed.message { + Lit::Str(lit_str) => lit_str.value(), + _ => "expression".to_string(), + }; + + let expanded = quote! { + { + #[cfg(feature = "trace_guest")] + hyperlight_guest_tracing::create_trace_record(#trace_message); + } + }; + + return TokenStream::from(expanded); + } + + // Fallback: treat the entire input as an expression with default message + let expanded = quote! { + { + #[cfg(feature = "trace_guest")] + hyperlight_guest_tracing::create_trace_record("expression"); + } + }; + + TokenStream::from(expanded) +} + +/// This macro flushes the trace buffer, sending any remaining trace records to the host. +/// +/// Usage: +/// ```rust +/// flush!(); +/// ``` +#[proc_macro] +pub fn flush(_input: TokenStream) -> TokenStream { + let expanded = quote! { + { + #[cfg(feature = "trace_guest")] + hyperlight_guest_tracing::flush_trace_buffer(); + } + }; + + TokenStream::from(expanded) +} diff --git a/src/hyperlight_host/Cargo.toml b/src/hyperlight_host/Cargo.toml index bc744c05c..0f3949628 100644 --- a/src/hyperlight_host/Cargo.toml +++ b/src/hyperlight_host/Cargo.toml @@ -28,6 +28,9 @@ rand = { version = "0.9" } cfg-if = { version = "1.0.1" } libc = { version = "0.2.174" } flatbuffers = "25.2.10" +framehop = { version = "0.13.1", optional = true } +fallible-iterator = { version = "0.3.0", optional = true } +blake3 = "1.8.2" page_size = "0.6.0" termcolor = "1.2.0" bitflags = "2.9.1" @@ -36,6 +39,7 @@ tracing = { version = "0.1.41", features = ["log"] } tracing-log = "0.2.0" tracing-core = "0.1.34" hyperlight-common = { workspace = true, default-features = true, features = [ "std" ] } +hyperlight-guest-tracing = { workspace = true, default-features = true, optional = true } vmm-sys-util = "0.14.0" crossbeam-channel = "0.5.15" thiserror = "2.0.12" @@ -44,6 +48,7 @@ anyhow = "1.0" metrics = "0.24.2" serde_json = "1.0" elfcore = "2.0" +uuid = { version = "1.17.0", features = ["v4"] } [target.'cfg(windows)'.dependencies] windows = { version = "0.61", features = [ @@ -79,6 +84,7 @@ mshv-ioctls3 = { package="mshv-ioctls", version = "=0.3.2", optional = true} [dev-dependencies] uuid = { version = "1.17.0", features = ["v4"] } signal-hook-registry = "1.4.5" +envy = { version = "0.4.2" } serde = "1.0" proptest = "1.7.0" tempfile = "3.20.0" @@ -126,6 +132,11 @@ executable_heap = [] print_debug = [] # Dumps the VM state to a file on unexpected errors or crashes. The path of the file will be printed on stdout and logged. crashdump = ["dep:chrono"] +trace_guest = ["hyperlight-common/trace_guest", "dep:hyperlight-guest-tracing"] +# This feature enables unwinding the guest stack from the host, in +# order to produce stack traces for debugging or profiling. +unwind_guest = [ "trace_guest", "dep:framehop", "dep:fallible-iterator", "hyperlight-common/unwind_guest" ] +mem_profile = [ "unwind_guest", "hyperlight-common/mem_profile" ] kvm = ["dep:kvm-bindings", "dep:kvm-ioctls"] mshv2 = ["dep:mshv-bindings2", "dep:mshv-ioctls2"] mshv3 = ["dep:mshv-bindings3", "dep:mshv-ioctls3"] diff --git a/src/hyperlight_host/src/hypervisor/handlers.rs b/src/hyperlight_host/src/hypervisor/handlers.rs index 9a091b2b1..186c030e2 100644 --- a/src/hyperlight_host/src/hypervisor/handlers.rs +++ b/src/hyperlight_host/src/hypervisor/handlers.rs @@ -18,14 +18,21 @@ use std::sync::{Arc, Mutex}; use tracing::{Span, instrument}; +#[cfg(feature = "trace_guest")] +use super::Hypervisor; use crate::{Result, new_error}; /// The trait representing custom logic to handle the case when /// a Hypervisor's virtual CPU (vCPU) informs Hyperlight the guest /// has initiated an outb operation. -pub trait OutBHandlerCaller: Sync + Send { +pub(crate) trait OutBHandlerCaller: Sync + Send { /// Function that gets called when an outb operation has occurred. - fn call(&mut self, port: u16, payload: u32) -> Result<()>; + fn call( + &mut self, + #[cfg(feature = "trace_guest")] hv: &mut dyn Hypervisor, + port: u16, + payload: u32, + ) -> Result<()>; } /// A convenient type representing a common way `OutBHandler` implementations @@ -34,8 +41,12 @@ pub trait OutBHandlerCaller: Sync + Send { /// Note: This needs to be wrapped in a Mutex to be able to grab a mutable /// reference to the underlying data (i.e., handle_outb in `Sandbox` takes /// a &mut self). -pub type OutBHandlerWrapper = Arc>; +pub(crate) type OutBHandlerWrapper = Arc>; +#[cfg(feature = "trace_guest")] +pub(crate) type OutBHandlerFunction = + Box Result<()> + Send>; +#[cfg(not(feature = "trace_guest"))] pub(crate) type OutBHandlerFunction = Box Result<()> + Send>; /// A `OutBHandler` implementation using a `OutBHandlerFunction` @@ -52,12 +63,22 @@ impl From for OutBHandler { impl OutBHandlerCaller for OutBHandler { #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - fn call(&mut self, port: u16, payload: u32) -> Result<()> { + fn call( + &mut self, + #[cfg(feature = "trace_guest")] hv: &mut dyn Hypervisor, + port: u16, + payload: u32, + ) -> Result<()> { let mut func = self .0 .try_lock() .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?; - func(port, payload) + func( + #[cfg(feature = "trace_guest")] + hv, + port, + payload, + ) } } diff --git a/src/hyperlight_host/src/hypervisor/hyperv_linux.rs b/src/hyperlight_host/src/hypervisor/hyperv_linux.rs index db5037106..3117dda30 100644 --- a/src/hyperlight_host/src/hypervisor/hyperv_linux.rs +++ b/src/hyperlight_host/src/hypervisor/hyperv_linux.rs @@ -48,11 +48,18 @@ use mshv_bindings::{ hv_partition_property_code_HV_PARTITION_PROPERTY_SYNTHETIC_PROC_FEATURES, hv_partition_synthetic_processor_features, }; +#[cfg(feature = "trace_guest")] +use mshv_bindings::{ + hv_register_name, hv_register_name_HV_X64_REGISTER_RAX, hv_register_name_HV_X64_REGISTER_RBP, + hv_register_name_HV_X64_REGISTER_RCX, hv_register_name_HV_X64_REGISTER_RSP, +}; use mshv_ioctls::{Mshv, MshvError, VcpuFd, VmFd}; use tracing::{Span, instrument}; #[cfg(crashdump)] use {super::crashdump, std::path::Path}; +#[cfg(feature = "trace_guest")] +use super::TraceRegister; use super::fpu::{FP_CONTROL_WORD_DEFAULT, FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT}; #[cfg(gdb)] use super::gdb::{ @@ -72,6 +79,8 @@ use crate::HyperlightError; use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags}; use crate::mem::ptr::{GuestPtr, RawPtr}; use crate::sandbox::SandboxConfiguration; +#[cfg(feature = "trace_guest")] +use crate::sandbox::TraceInfo; #[cfg(crashdump)] use crate::sandbox::uninitialized::SandboxRuntimeConfig; use crate::{Result, log_then_return, new_error}; @@ -310,6 +319,9 @@ pub(crate) struct HypervLinuxDriver { gdb_conn: Option>, #[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig, + #[cfg(feature = "trace_guest")] + #[allow(dead_code)] + trace_info: TraceInfo, } impl HypervLinuxDriver { @@ -321,6 +333,8 @@ impl HypervLinuxDriver { /// the underlying virtual CPU after this function returns. Call the /// `apply_registers` method to do that, or more likely call /// `initialise` to do it for you. + #[allow(clippy::too_many_arguments)] + // TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg #[instrument(skip_all, parent = Span::current(), level = "Trace")] pub(crate) fn new( mem_regions: Vec, @@ -330,6 +344,7 @@ impl HypervLinuxDriver { config: &SandboxConfiguration, #[cfg(gdb)] gdb_conn: Option>, #[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig, + #[cfg(feature = "trace_guest")] trace_info: TraceInfo, ) -> Result { let mshv = Mshv::new()?; let pr = Default::default(); @@ -436,6 +451,8 @@ impl HypervLinuxDriver { gdb_conn, #[cfg(crashdump)] rt_cfg, + #[cfg(feature = "trace_guest")] + trace_info, }; // Send the interrupt handle to the GDB thread if debugging is enabled @@ -513,6 +530,19 @@ impl Debug for HypervLinuxDriver { } } +#[cfg(feature = "trace_guest")] +impl From for hv_register_name { + fn from(r: TraceRegister) -> Self { + match r { + TraceRegister::RAX => hv_register_name_HV_X64_REGISTER_RAX, + TraceRegister::RCX => hv_register_name_HV_X64_REGISTER_RCX, + TraceRegister::RIP => hv_register_name_HV_X64_REGISTER_RIP, + TraceRegister::RSP => hv_register_name_HV_X64_REGISTER_RSP, + TraceRegister::RBP => hv_register_name_HV_X64_REGISTER_RBP, + } + } +} + impl Hypervisor for HypervLinuxDriver { #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")] fn initialise( @@ -611,7 +641,12 @@ impl Hypervisor for HypervLinuxDriver { outb_handle_fn .try_lock() .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))? - .call(port, val)?; + .call( + #[cfg(feature = "trace_guest")] + self, + port, + val, + )?; // update rip self.vcpu_fd.set_reg(&[hv_register_assoc { @@ -1019,6 +1054,26 @@ impl Hypervisor for HypervLinuxDriver { Ok(()) } + + #[cfg(feature = "trace_guest")] + fn read_trace_reg(&self, reg: TraceRegister) -> Result { + let mut assoc = [hv_register_assoc { + name: reg.into(), + ..Default::default() + }]; + self.vcpu_fd.get_reg(&mut assoc)?; + // safety: all registers that we currently support are 64-bit + unsafe { Ok(assoc[0].value.reg64) } + } + + #[cfg(feature = "trace_guest")] + fn trace_info_as_ref(&self) -> &TraceInfo { + &self.trace_info + } + #[cfg(feature = "trace_guest")] + fn trace_info_as_mut(&mut self) -> &mut TraceInfo { + &mut self.trace_info + } } impl Drop for HypervLinuxDriver { @@ -1038,6 +1093,8 @@ impl Drop for HypervLinuxDriver { #[cfg(test)] mod tests { use super::*; + #[cfg(feature = "unwind_guest")] + use crate::mem::exe::DummyUnwindInfo; use crate::mem::memory_region::MemoryRegionVecBuilder; use crate::mem::shared_mem::{ExclusiveSharedMemory, SharedMemory}; @@ -1105,6 +1162,12 @@ mod tests { #[cfg(crashdump)] guest_core_dump: true, }, + #[cfg(feature = "trace_guest")] + TraceInfo::new( + #[cfg(feature = "unwind_guest")] + Arc::new(DummyUnwindInfo {}), + ) + .unwrap(), ) .unwrap(); } diff --git a/src/hyperlight_host/src/hypervisor/hyperv_windows.rs b/src/hyperlight_host/src/hypervisor/hyperv_windows.rs index 288b5bf5b..55c44ae1e 100644 --- a/src/hyperlight_host/src/hypervisor/hyperv_windows.rs +++ b/src/hyperlight_host/src/hypervisor/hyperv_windows.rs @@ -41,6 +41,8 @@ use { std::sync::Mutex, }; +#[cfg(feature = "trace_guest")] +use super::TraceRegister; use super::fpu::{FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT}; use super::handlers::{MemAccessHandlerWrapper, OutBHandlerWrapper}; use super::surrogate_process::SurrogateProcess; @@ -57,6 +59,8 @@ use crate::hypervisor::fpu::FP_CONTROL_WORD_DEFAULT; use crate::hypervisor::wrappers::WHvGeneralRegisters; use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags}; use crate::mem::ptr::{GuestPtr, RawPtr}; +#[cfg(feature = "trace_guest")] +use crate::sandbox::TraceInfo; #[cfg(crashdump)] use crate::sandbox::uninitialized::SandboxRuntimeConfig; use crate::{Result, debug, new_error}; @@ -280,6 +284,9 @@ pub(crate) struct HypervWindowsDriver { gdb_conn: Option>, #[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig, + #[cfg(feature = "trace_guest")] + #[allow(dead_code)] + trace_info: TraceInfo, } /* This does not automatically impl Send/Sync because the host * address of the shared memory region is a raw pointer, which are @@ -291,6 +298,7 @@ unsafe impl Sync for HypervWindowsDriver {} impl HypervWindowsDriver { #[allow(clippy::too_many_arguments)] + // TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")] pub(crate) fn new( mem_regions: Vec, @@ -301,6 +309,7 @@ impl HypervWindowsDriver { mmap_file_handle: HandleWrapper, #[cfg(gdb)] gdb_conn: Option>, #[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig, + #[cfg(feature = "trace_guest")] trace_info: TraceInfo, ) -> Result { // create and setup hypervisor partition let mut partition = VMPartition::new(1)?; @@ -351,6 +360,8 @@ impl HypervWindowsDriver { gdb_conn, #[cfg(crashdump)] rt_cfg, + #[cfg(feature = "trace_guest")] + trace_info, }; // Send the interrupt handle to the GDB thread if debugging is enabled @@ -659,7 +670,12 @@ impl Hypervisor for HypervWindowsDriver { outb_handle_fn .try_lock() .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))? - .call(port, val)?; + .call( + #[cfg(feature = "trace_guest")] + self, + port, + val, + )?; let mut regs = self.processor.get_regs()?; regs.rip = rip + instruction_length; @@ -1005,6 +1021,27 @@ impl Hypervisor for HypervWindowsDriver { Ok(()) } + + #[cfg(feature = "trace_guest")] + fn read_trace_reg(&self, reg: TraceRegister) -> Result { + let regs = self.processor.get_regs()?; + match reg { + TraceRegister::RAX => Ok(regs.rax), + TraceRegister::RCX => Ok(regs.rcx), + TraceRegister::RIP => Ok(regs.rip), + TraceRegister::RSP => Ok(regs.rsp), + TraceRegister::RBP => Ok(regs.rbp), + } + } + + #[cfg(feature = "trace_guest")] + fn trace_info_as_ref(&self) -> &TraceInfo { + &self.trace_info + } + #[cfg(feature = "trace_guest")] + fn trace_info_as_mut(&mut self) -> &mut TraceInfo { + &mut self.trace_info + } } impl Drop for HypervWindowsDriver { diff --git a/src/hyperlight_host/src/hypervisor/kvm.rs b/src/hyperlight_host/src/hypervisor/kvm.rs index d85a6a838..c9fad12b6 100644 --- a/src/hyperlight_host/src/hypervisor/kvm.rs +++ b/src/hyperlight_host/src/hypervisor/kvm.rs @@ -29,6 +29,8 @@ use tracing::{Span, instrument}; #[cfg(crashdump)] use {super::crashdump, std::path::Path}; +#[cfg(feature = "trace_guest")] +use super::TraceRegister; use super::fpu::{FP_CONTROL_WORD_DEFAULT, FP_TAG_WORD_DEFAULT, MXCSR_DEFAULT}; #[cfg(gdb)] use super::gdb::{DebugCommChannel, DebugMsg, DebugResponse, GuestDebug, KvmDebug, VcpuStopReason}; @@ -46,6 +48,8 @@ use crate::HyperlightError; use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags}; use crate::mem::ptr::{GuestPtr, RawPtr}; use crate::sandbox::SandboxConfiguration; +#[cfg(feature = "trace_guest")] +use crate::sandbox::TraceInfo; #[cfg(crashdump)] use crate::sandbox::uninitialized::SandboxRuntimeConfig; use crate::{Result, log_then_return, new_error}; @@ -297,12 +301,17 @@ pub(crate) struct KVMDriver { gdb_conn: Option>, #[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig, + #[cfg(feature = "trace_guest")] + #[allow(dead_code)] + trace_info: TraceInfo, } impl KVMDriver { /// Create a new instance of a `KVMDriver`, with only control registers /// set. Standard registers will not be set, and `initialise` must /// be called to do so. + #[allow(clippy::too_many_arguments)] + // TODO: refactor this function to take fewer arguments. Add trace_info to rt_cfg #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")] pub(crate) fn new( mem_regions: Vec, @@ -312,6 +321,7 @@ impl KVMDriver { config: &SandboxConfiguration, #[cfg(gdb)] gdb_conn: Option>, #[cfg(crashdump)] rt_cfg: SandboxRuntimeConfig, + #[cfg(feature = "trace_guest")] trace_info: TraceInfo, ) -> Result { let kvm = Kvm::new()?; @@ -390,6 +400,8 @@ impl KVMDriver { gdb_conn, #[cfg(crashdump)] rt_cfg, + #[cfg(feature = "trace_guest")] + trace_info, }; // Send the interrupt handle to the GDB thread if debugging is enabled @@ -555,7 +567,12 @@ impl Hypervisor for KVMDriver { outb_handle_fn .try_lock() .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))? - .call(port, value)?; + .call( + #[cfg(feature = "trace_guest")] + self, + port, + value, + )?; } Ok(()) @@ -595,6 +612,13 @@ impl Hypervisor for KVMDriver { { Err(kvm_ioctls::Error::new(libc::EINTR)) } else { + #[cfg(feature = "trace_guest")] + if self.trace_info.guest_start_epoch.is_none() { + // Set the guest start epoch to the current time, before running the vcpu + crate::debug!("KVM - Guest Start Epoch set"); + self.trace_info.guest_start_epoch = Some(std::time::Instant::now()); + } + // Note: if a `InterruptHandle::kill()` called while this thread is **here** // Then the vcpu will run, but we will keep sending signals to this thread // to interrupt it until `running` is set to false. The `vcpu_fd::run()` call will @@ -916,6 +940,27 @@ impl Hypervisor for KVMDriver { Ok(()) } + + #[cfg(feature = "trace_guest")] + fn read_trace_reg(&self, reg: TraceRegister) -> Result { + let regs = self.vcpu_fd.get_regs()?; + Ok(match reg { + TraceRegister::RAX => regs.rax, + TraceRegister::RCX => regs.rcx, + TraceRegister::RIP => regs.rip, + TraceRegister::RSP => regs.rsp, + TraceRegister::RBP => regs.rbp, + }) + } + + #[cfg(feature = "trace_guest")] + fn trace_info_as_ref(&self) -> &TraceInfo { + &self.trace_info + } + #[cfg(feature = "trace_guest")] + fn trace_info_as_mut(&mut self) -> &mut TraceInfo { + &mut self.trace_info + } } impl Drop for KVMDriver { diff --git a/src/hyperlight_host/src/hypervisor/mod.rs b/src/hyperlight_host/src/hypervisor/mod.rs index 0a31ee468..49851a99b 100644 --- a/src/hyperlight_host/src/hypervisor/mod.rs +++ b/src/hyperlight_host/src/hypervisor/mod.rs @@ -20,6 +20,8 @@ use tracing::{Span, instrument}; use crate::error::HyperlightError::ExecutionCanceledByHost; use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags}; use crate::metrics::METRIC_GUEST_CANCELLATION; +#[cfg(feature = "trace_guest")] +use crate::sandbox::TraceInfo; use crate::{HyperlightError, Result, log_then_return, new_error}; /// Util for handling x87 fpu state @@ -116,6 +118,21 @@ pub enum HyperlightExit { Retry(), } +/// Registers which may be useful for tracing/stack unwinding +#[cfg(feature = "trace_guest")] +pub enum TraceRegister { + /// RAX + RAX, + /// RCX + RCX, + /// RIP + RIP, + /// RSP + RSP, + /// RBP + RBP, +} + /// A common set of hypervisor functionality pub(crate) trait Hypervisor: Debug + Sync + Send { /// Initialise the internally stored vCPU with the given PEB address and @@ -242,6 +259,17 @@ pub(crate) trait Hypervisor: Debug + Sync + Send { ) -> Result<()> { unimplemented!() } + + /// Read a register for trace/unwind purposes + #[cfg(feature = "trace_guest")] + fn read_trace_reg(&self, reg: TraceRegister) -> Result; + + /// Get a reference of the trace info for the guest + #[cfg(feature = "trace_guest")] + fn trace_info_as_ref(&self) -> &TraceInfo; + /// Get a mutable reference of the trace info for the guest + #[cfg(feature = "trace_guest")] + fn trace_info_as_mut(&mut self) -> &mut TraceInfo; } /// A virtual CPU that can be run until an exit occurs @@ -250,7 +278,7 @@ pub struct VirtualCPU {} impl VirtualCPU { /// Run the given hypervisor until a halt instruction is reached #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")] - pub fn run( + pub(crate) fn run( hv: &mut dyn Hypervisor, outb_handle_fn: Arc>, mem_access_fn: Arc>, @@ -494,7 +522,7 @@ pub(crate) mod tests { use hyperlight_testing::dummy_guest_as_string; - use super::handlers::{MemAccessHandler, OutBHandler}; + use super::handlers::{MemAccessHandler, OutBHandler, OutBHandlerFunction}; #[cfg(gdb)] use crate::hypervisor::DbgMemAccessHandlerCaller; use crate::mem::ptr::RawPtr; @@ -529,11 +557,6 @@ pub(crate) mod tests { return Ok(()); } - let outb_handler: Arc> = { - let func: Box Result<()> + Send> = - Box::new(|_, _| -> Result<()> { Ok(()) }); - Arc::new(Mutex::new(OutBHandler::from(func))) - }; let mem_access_handler = { let func: Box Result<()> + Send> = Box::new(|| -> Result<()> { Ok(()) }); Arc::new(Mutex::new(MemAccessHandler::from(func))) @@ -554,7 +577,16 @@ pub(crate) mod tests { &config, #[cfg(any(crashdump, gdb))] &rt_cfg, + sandbox.load_info, )?; + let outb_handler: Arc> = { + #[cfg(feature = "trace_guest")] + #[allow(clippy::type_complexity)] + let func: OutBHandlerFunction = Box::new(|_, _, _| -> Result<()> { Ok(()) }); + #[cfg(not(feature = "trace_guest"))] + let func: OutBHandlerFunction = Box::new(|_, _| -> Result<()> { Ok(()) }); + Arc::new(Mutex::new(OutBHandler::from(func))) + }; vm.initialise( RawPtr::from(0x230000), 1234567890, diff --git a/src/hyperlight_host/src/hypervisor/surrogate_process.rs b/src/hyperlight_host/src/hypervisor/surrogate_process.rs index caaa79d00..d026362d2 100644 --- a/src/hyperlight_host/src/hypervisor/surrogate_process.rs +++ b/src/hyperlight_host/src/hypervisor/surrogate_process.rs @@ -83,7 +83,6 @@ impl Drop for SurrogateProcess { "Failed to return surrogate process to surrogate process manager when dropping : {:?}", e ); - return; } }, Err(e) => { @@ -91,7 +90,6 @@ impl Drop for SurrogateProcess { "Failed to get surrogate process manager when dropping SurrogateProcess: {:?}", e ); - return; } } } diff --git a/src/hyperlight_host/src/mem/elf.rs b/src/hyperlight_host/src/mem/elf.rs index 3efe09b4f..ff83d42c8 100644 --- a/src/hyperlight_host/src/mem/elf.rs +++ b/src/hyperlight_host/src/mem/elf.rs @@ -14,6 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ +#[cfg(feature = "unwind_guest")] +use std::sync::Arc; + #[cfg(target_arch = "aarch64")] use goblin::elf::reloc::{R_AARCH64_NONE, R_AARCH64_RELATIVE}; #[cfg(target_arch = "x86_64")] @@ -26,13 +29,85 @@ use goblin::elf64::program_header::PT_LOAD; use crate::{Result, log_then_return, new_error}; +#[cfg(feature = "unwind_guest")] +struct ResolvedSectionHeader { + name: String, + addr: u64, + offset: u64, + size: u64, +} + pub(crate) struct ElfInfo { payload: Vec, phdrs: ProgramHeaders, + #[cfg(feature = "unwind_guest")] + shdrs: Vec, entry: u64, relocs: Vec, } +#[cfg(feature = "unwind_guest")] +struct UnwindInfo { + payload: Vec, + load_addr: u64, + va_size: u64, + base_svma: u64, + shdrs: Vec, +} + +#[cfg(feature = "unwind_guest")] +impl super::exe::UnwindInfo for UnwindInfo { + fn as_module(&self) -> framehop::Module> { + framehop::Module::new( + // TODO: plumb through a name from from_file if this + // came from a file + "guest".to_string(), + self.load_addr..self.load_addr + self.va_size, + self.load_addr, + self, + ) + } + fn hash(&self) -> blake3::Hash { + blake3::hash(&self.payload) + } +} + +#[cfg(feature = "unwind_guest")] +impl UnwindInfo { + fn resolved_section_header(&self, name: &[u8]) -> Option<&ResolvedSectionHeader> { + self.shdrs + .iter() + .find(|&sh| sh.name.as_bytes()[0..core::cmp::min(name.len(), sh.name.len())] == *name) + } +} + +#[cfg(feature = "unwind_guest")] +impl framehop::ModuleSectionInfo> for &UnwindInfo { + fn base_svma(&self) -> u64 { + self.base_svma + } + fn section_svma_range(&mut self, name: &[u8]) -> Option> { + let shdr = self.resolved_section_header(name)?; + Some(shdr.addr..shdr.addr + shdr.size) + } + fn section_data(&mut self, name: &[u8]) -> Option> { + if name == b".eh_frame" && self.resolved_section_header(b".debug_frame").is_some() { + /* Rustc does not always emit enough information for stack + * unwinding in .eh_frame, presumably because we use panic = + * abort in the guest. Framehop defaults to ignoring + * .debug_frame if .eh_frame exists, but we want the opposite + * behaviour here, since .debug_frame will actually contain + * frame information whereas .eh_frame often doesn't because + * of the aforementioned behaviour. Consequently, we hack + * around this by pretending that .eh_frame doesn't exist if + * .debug_frame does. */ + return None; + } + let shdr = self.resolved_section_header(name)?; + Some(self.payload[shdr.offset as usize..(shdr.offset + shdr.size) as usize].to_vec()) + } +} + impl ElfInfo { pub(crate) fn new(bytes: &[u8]) -> Result { let elf = Elf::parse(bytes)?; @@ -47,6 +122,19 @@ impl ElfInfo { Ok(ElfInfo { payload: bytes.to_vec(), phdrs: elf.program_headers, + #[cfg(feature = "unwind_guest")] + shdrs: elf + .section_headers + .iter() + .filter_map(|sh| { + Some(ResolvedSectionHeader { + name: elf.shdr_strtab.get_at(sh.sh_name)?.to_string(), + addr: sh.sh_addr, + offset: sh.sh_offset, + size: sh.sh_size, + }) + }) + .collect(), entry: elf.entry, relocs, }) @@ -73,7 +161,11 @@ impl ElfInfo { .unwrap(); (max_phdr.p_vaddr + max_phdr.p_memsz - self.get_base_va()) as usize } - pub(crate) fn load_at(&self, load_addr: usize, target: &mut [u8]) -> Result<()> { + pub(crate) fn load_at( + self, + load_addr: usize, + target: &mut [u8], + ) -> Result { let base_va = self.get_base_va(); for phdr in self.phdrs.iter().filter(|phdr| phdr.p_type == PT_LOAD) { let start_va = (phdr.p_vaddr - base_va) as usize; @@ -113,6 +205,20 @@ impl ElfInfo { } } } - Ok(()) + cfg_if::cfg_if! { + if #[cfg(feature = "unwind_guest")] { + let va_size = self.get_va_size() as u64; + let base_svma = self.get_base_va(); + Ok(Arc::new(UnwindInfo { + payload: self.payload, + load_addr: load_addr as u64, + va_size, + base_svma, + shdrs: self.shdrs, + })) + } else { + Ok(()) + } + } } } diff --git a/src/hyperlight_host/src/mem/exe.rs b/src/hyperlight_host/src/mem/exe.rs index bf1724317..064d58cde 100644 --- a/src/hyperlight_host/src/mem/exe.rs +++ b/src/hyperlight_host/src/mem/exe.rs @@ -16,6 +16,8 @@ limitations under the License. use std::fs::File; use std::io::Read; +#[cfg(feature = "unwind_guest")] +use std::sync::Arc; use std::vec::Vec; use super::elf::ElfInfo; @@ -37,6 +39,41 @@ pub enum ExeInfo { const DEFAULT_ELF_STACK_RESERVE: u64 = 65536; const DEFAULT_ELF_HEAP_RESERVE: u64 = 131072; +#[cfg(feature = "unwind_guest")] +pub(crate) trait UnwindInfo: Send + Sync { + fn as_module(&self) -> framehop::Module>; + fn hash(&self) -> blake3::Hash; +} + +#[cfg(feature = "unwind_guest")] +pub(crate) struct DummyUnwindInfo {} +#[cfg(feature = "unwind_guest")] +impl UnwindInfo for DummyUnwindInfo { + fn as_module(&self) -> framehop::Module> { + framehop::Module::new("unsupported".to_string(), 0..0, 0, self) + } + fn hash(&self) -> blake3::Hash { + blake3::Hash::from_bytes([0; 32]) + } +} +#[cfg(feature = "unwind_guest")] +impl framehop::ModuleSectionInfo for &DummyUnwindInfo { + fn base_svma(&self) -> u64 { + 0 + } + fn section_svma_range(&mut self, _name: &[u8]) -> Option> { + None + } + fn section_data(&mut self, _name: &[u8]) -> Option { + None + } +} + +#[cfg(feature = "unwind_guest")] +pub(crate) type LoadInfo = Arc; +#[cfg(not(feature = "unwind_guest"))] +pub(crate) type LoadInfo = (); + impl ExeInfo { pub fn from_file(path: &str) -> Result { let mut file = File::open(path)?; @@ -71,12 +108,9 @@ impl ExeInfo { // copying into target, but the PE loader chooses to apply // relocations in its owned representation of the PE contents, // which requires it to be &mut. - pub fn load(&mut self, load_addr: usize, target: &mut [u8]) -> Result<()> { + pub fn load(self, load_addr: usize, target: &mut [u8]) -> Result { match self { - ExeInfo::Elf(elf) => { - elf.load_at(load_addr, target)?; - } + ExeInfo::Elf(elf) => elf.load_at(load_addr, target), } - Ok(()) } } diff --git a/src/hyperlight_host/src/mem/layout.rs b/src/hyperlight_host/src/mem/layout.rs index 04edc9bcc..a8a1e5fc9 100644 --- a/src/hyperlight_host/src/mem/layout.rs +++ b/src/hyperlight_host/src/mem/layout.rs @@ -381,6 +381,7 @@ impl SandboxMemoryLayout { /// Get the offset in guest memory to the OutB pointer. #[instrument(skip_all, parent = Span::current(), level= "Trace")] + #[allow(dead_code)] pub(super) fn get_outb_pointer_offset(&self) -> usize { // The outb pointer is immediately after the code pointer // in the `CodeAndOutBPointers` struct which is a u64 @@ -389,6 +390,7 @@ impl SandboxMemoryLayout { /// Get the offset in guest memory to the OutB context. #[instrument(skip_all, parent = Span::current(), level= "Trace")] + #[allow(dead_code)] pub(super) fn get_outb_context_offset(&self) -> usize { // The outb context is immediately after the outb pointer // in the `CodeAndOutBPointers` struct which is a u64 @@ -416,6 +418,7 @@ impl SandboxMemoryLayout { /// This function exists to accommodate the macro that generates C API /// compatible functions. #[instrument(skip_all, parent = Span::current(), level= "Trace")] + #[allow(dead_code)] pub(crate) fn get_output_data_offset(&self) -> usize { self.output_data_buffer_offset } @@ -452,6 +455,7 @@ impl SandboxMemoryLayout { /// Get the offset in guest memory to the PEB address #[instrument(skip_all, parent = Span::current(), level= "Trace")] + #[allow(dead_code)] pub(super) fn get_in_process_peb_offset(&self) -> usize { self.peb_offset } @@ -486,6 +490,7 @@ impl SandboxMemoryLayout { /// Get the offset to the guest guard page #[instrument(skip_all, parent = Span::current(), level= "Trace")] + #[allow(dead_code)] pub fn get_guard_page_offset(&self) -> usize { self.guard_page_offset } diff --git a/src/hyperlight_host/src/mem/mgr.rs b/src/hyperlight_host/src/mem/mgr.rs index 7910d9dc2..0b82f38d8 100644 --- a/src/hyperlight_host/src/mem/mgr.rs +++ b/src/hyperlight_host/src/mem/mgr.rs @@ -310,6 +310,7 @@ where /// `shared_mem` to indicate the address of the outb pointer and context /// for calling outb function #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] + #[allow(dead_code)] pub(crate) fn set_outb_address_and_context(&mut self, addr: u64, context: u64) -> Result<()> { let pointer_offset = self.layout.get_outb_pointer_offset(); let context_offset = self.layout.get_outb_context_offset(); @@ -336,17 +337,17 @@ impl SandboxMemoryManager { #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] pub(crate) fn load_guest_binary_into_memory( cfg: SandboxConfiguration, - exe_info: &mut ExeInfo, + exe_info: ExeInfo, guest_blob: Option<&GuestBlob>, - ) -> Result { + ) -> Result<(Self, super::exe::LoadInfo)> { let guest_blob_size = guest_blob.map(|b| b.data.len()).unwrap_or(0); let guest_blob_mem_flags = guest_blob.map(|b| b.permissions); let layout = SandboxMemoryLayout::new( cfg, exe_info.loaded_size(), - usize::try_from(cfg.get_stack_size(exe_info))?, - usize::try_from(cfg.get_heap_size(exe_info))?, + usize::try_from(cfg.get_stack_size(&exe_info))?, + usize::try_from(cfg.get_heap_size(&exe_info))?, guest_blob_size, guest_blob_mem_flags, )?; @@ -364,12 +365,15 @@ impl SandboxMemoryManager { shared_mem.write_u64(offset, load_addr_u64)?; } - exe_info.load( + let load_info = exe_info.load( load_addr.clone().try_into()?, &mut shared_mem.as_mut_slice()[layout.get_guest_code_offset()..], )?; - Ok(Self::new(layout, shared_mem, load_addr, entrypoint_offset)) + Ok(( + Self::new(layout, shared_mem, load_addr, entrypoint_offset), + load_info, + )) } /// Writes host function details to memory diff --git a/src/hyperlight_host/src/mem/ptr_offset.rs b/src/hyperlight_host/src/mem/ptr_offset.rs index 673767e36..a8105ed70 100644 --- a/src/hyperlight_host/src/mem/ptr_offset.rs +++ b/src/hyperlight_host/src/mem/ptr_offset.rs @@ -32,11 +32,13 @@ pub(crate) struct Offset(u64); impl Offset { /// Get the offset representing `0` #[instrument(skip_all, parent = Span::current(), level= "Trace")] + #[allow(dead_code)] pub(super) fn zero() -> Self { Self::default() } /// round up to the nearest multiple of `alignment` + #[allow(dead_code)] pub(super) fn round_up_to(self, alignment: u64) -> Self { let remainder = self.0 % alignment; let multiples = self.0 / alignment; diff --git a/src/hyperlight_host/src/mem/shared_mem.rs b/src/hyperlight_host/src/mem/shared_mem.rs index 50c809f44..23d0b7fcf 100644 --- a/src/hyperlight_host/src/mem/shared_mem.rs +++ b/src/hyperlight_host/src/mem/shared_mem.rs @@ -502,6 +502,7 @@ impl ExclusiveSharedMemory { }) } + #[allow(dead_code)] pub(super) fn make_memory_executable(&self) -> Result<()> { #[cfg(target_os = "windows")] { @@ -616,6 +617,7 @@ impl ExclusiveSharedMemory { /// Return the address of memory at an offset to this `SharedMemory` checking /// that the memory is within the bounds of the `SharedMemory`. #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] + #[allow(dead_code)] pub(crate) fn calculate_address(&self, offset: usize) -> Result { bounds_check!(offset, 0, self.mem_size()); Ok(self.base_addr() + offset) diff --git a/src/hyperlight_host/src/mem/shared_mem_snapshot.rs b/src/hyperlight_host/src/mem/shared_mem_snapshot.rs index d5cf565de..cd22f1740 100644 --- a/src/hyperlight_host/src/mem/shared_mem_snapshot.rs +++ b/src/hyperlight_host/src/mem/shared_mem_snapshot.rs @@ -39,7 +39,7 @@ impl SharedMemorySnapshot { /// Take another snapshot of the internally-stored `SharedMemory`, /// then store it internally. #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - + #[allow(dead_code)] pub(super) fn replace_snapshot(&mut self, shared_mem: &mut S) -> Result<()> { self.snapshot = shared_mem.with_exclusivity(|e| e.copy_all_to_vec())??; Ok(()) diff --git a/src/hyperlight_host/src/sandbox/host_funcs.rs b/src/hyperlight_host/src/sandbox/host_funcs.rs index 96751f391..6d3c8d98a 100644 --- a/src/hyperlight_host/src/sandbox/host_funcs.rs +++ b/src/hyperlight_host/src/sandbox/host_funcs.rs @@ -93,6 +93,7 @@ impl FunctionRegistry { /// Return `Ok` if the function was found and was of the right signature, /// and `Err` otherwise. #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")] + #[allow(dead_code)] pub(super) fn host_print(&mut self, msg: String) -> Result { let res = self.call_host_func_impl("HostPrint", vec![ParameterValue::String(msg)])?; res.try_into() diff --git a/src/hyperlight_host/src/sandbox/mod.rs b/src/hyperlight_host/src/sandbox/mod.rs index c6e33c84b..34bc0c08c 100644 --- a/src/hyperlight_host/src/sandbox/mod.rs +++ b/src/hyperlight_host/src/sandbox/mod.rs @@ -40,10 +40,17 @@ pub(crate) mod uninitialized_evolve; /// Trait used by the macros to paper over the differences between hyperlight and hyperlight-wasm mod callable; +#[cfg(feature = "unwind_guest")] +use std::io::Write; +#[cfg(feature = "trace_guest")] +use std::sync::{Arc, Mutex}; + /// Trait used by the macros to paper over the differences between hyperlight and hyperlight-wasm pub use callable::Callable; /// Re-export for `SandboxConfiguration` type pub use config::SandboxConfiguration; +#[cfg(feature = "unwind_guest")] +use framehop::Unwinder; /// Re-export for the `MultiUseSandbox` type pub use initialized_multi_use::MultiUseSandbox; use tracing::{Span, instrument}; @@ -86,6 +93,102 @@ pub fn is_hypervisor_present() -> bool { hypervisor::get_available_hypervisor().is_some() } +#[cfg(feature = "trace_guest")] +#[derive(Clone)] +/// The information that trace collection requires in order to write +/// an accurate trace. +pub(crate) struct TraceInfo { + /// The epoch against which trace events are timed; at least as + /// early as the creation of the sandbox being traced. + #[allow(dead_code)] + pub epoch: std::time::Instant, + /// The frequency of the timestamp counter. + #[allow(dead_code)] + pub tsc_freq: u64, + /// The epoch at which the guest started, if it has started. + /// This is used to calculate the time spent in the guest relative to the + /// time of the host. + #[allow(dead_code)] + pub guest_start_epoch: Option, + /// The start guest time, in TSC cycles, for the current guest. + #[allow(dead_code)] + pub guest_start_tsc: Option, + /// The file to which the trace is being written + #[allow(dead_code)] + pub file: Arc>, + /// The unwind information for the current guest + #[cfg(feature = "unwind_guest")] + #[allow(dead_code)] + pub unwind_module: Arc, + /// The framehop unwinder for the current guest + #[cfg(feature = "unwind_guest")] + pub unwinder: framehop::x86_64::UnwinderX86_64>, + /// The framehop cache + #[cfg(feature = "unwind_guest")] + pub unwind_cache: Arc>, +} +#[cfg(feature = "trace_guest")] +impl TraceInfo { + /// Create a new TraceInfo by saving the current time as the epoch + /// and generating a random filename. + pub fn new( + #[cfg(feature = "unwind_guest")] unwind_module: Arc, + ) -> crate::Result { + let mut path = std::env::current_dir()?; + path.push("trace"); + path.push(uuid::Uuid::new_v4().to_string()); + path.set_extension("trace"); + #[cfg(feature = "unwind_guest")] + let hash = unwind_module.hash(); + #[cfg(feature = "unwind_guest")] + let (unwinder, unwind_cache) = { + let mut unwinder = framehop::x86_64::UnwinderX86_64::new(); + unwinder.add_module(unwind_module.clone().as_module()); + let cache = framehop::x86_64::CacheX86_64::new(); + (unwinder, Arc::new(Mutex::new(cache))) + }; + let tsc_freq = Self::calculate_tsc_freq()?; + + let ret = Self { + epoch: std::time::Instant::now(), + tsc_freq, + guest_start_epoch: None, + guest_start_tsc: None, + file: Arc::new(Mutex::new(std::fs::File::create_new(path)?)), + #[cfg(feature = "unwind_guest")] + unwind_module, + #[cfg(feature = "unwind_guest")] + unwinder, + #[cfg(feature = "unwind_guest")] + unwind_cache, + }; + /* write a frame identifying the binary */ + #[cfg(feature = "unwind_guest")] + self::outb::record_trace_frame(&ret, 0, |f| { + let _ = f.write_all(hash.as_bytes()); + })?; + Ok(ret) + } + + /// Calculate the TSC frequency based on the RDTSC instruction. + fn calculate_tsc_freq() -> crate::Result { + if !hyperlight_guest_tracing::invariant_tsc::has_invariant_tsc() { + return Err(crate::new_error!( + "Invariant TSC is not supported on this platform" + )); + } + let start = hyperlight_guest_tracing::invariant_tsc::read_tsc(); + let start_time = std::time::Instant::now(); + // Sleep for 1 second to get a good sample + std::thread::sleep(std::time::Duration::from_secs(1)); + let end = hyperlight_guest_tracing::invariant_tsc::read_tsc(); + let end_time = std::time::Instant::now(); + let elapsed = end_time.duration_since(start_time).as_secs_f64(); + + Ok(((end - start) as f64 / elapsed) as u64) + } +} + pub(crate) trait WrapperGetter { #[allow(dead_code)] fn get_mgr_wrapper(&self) -> &MemMgrWrapper; diff --git a/src/hyperlight_host/src/sandbox/outb.rs b/src/hyperlight_host/src/sandbox/outb.rs index dcdd96589..ddd660eb6 100644 --- a/src/hyperlight_host/src/sandbox/outb.rs +++ b/src/hyperlight_host/src/sandbox/outb.rs @@ -14,12 +14,20 @@ See the License for the specific language governing permissions and limitations under the License. */ +#[cfg(feature = "trace_guest")] +use std::io::Write; use std::sync::{Arc, Mutex}; +#[cfg(feature = "unwind_guest")] +use fallible_iterator::FallibleIterator; +#[cfg(feature = "unwind_guest")] +use framehop::Unwinder; use hyperlight_common::flatbuffer_wrappers::function_types::ParameterValue; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use hyperlight_common::flatbuffer_wrappers::guest_log_data::GuestLogData; use hyperlight_common::outb::{Exception, OutBAction}; +#[cfg(feature = "trace_guest")] +use hyperlight_guest_tracing::TraceRecord; use log::{Level, Record}; use tracing::{Span, instrument}; use tracing_log::format_trace; @@ -27,9 +35,13 @@ use tracing_log::format_trace; use super::host_funcs::FunctionRegistry; use super::mem_mgr::MemMgrWrapper; use crate::hypervisor::handlers::{OutBHandler, OutBHandlerFunction, OutBHandlerWrapper}; +#[cfg(feature = "trace_guest")] +use crate::mem::layout::SandboxMemoryLayout; use crate::mem::mgr::SandboxMemoryManager; use crate::mem::shared_mem::HostSharedMemory; use crate::{HyperlightError, Result, new_error}; +#[cfg(feature = "trace_guest")] +use crate::{hypervisor::Hypervisor, sandbox::TraceInfo}; #[instrument(err(Debug), skip_all, parent = Span::current(), level="Trace")] pub(super) fn outb_log(mgr: &mut SandboxMemoryManager) -> Result<()> { @@ -140,7 +152,102 @@ fn outb_abort(mem_mgr: &mut MemMgrWrapper, data: u32) -> Resul buffer.push(b); } + Ok(()) +} + +#[cfg(feature = "unwind_guest")] +fn unwind( + hv: &dyn Hypervisor, + mem: &SandboxMemoryManager, + trace_info: &TraceInfo, +) -> Result> { + let mut read_stack = |addr| { + mem.shared_mem + .read::((addr - SandboxMemoryLayout::BASE_ADDRESS as u64) as usize) + .map_err(|_| ()) + }; + let mut cache = trace_info + .unwind_cache + .try_lock() + .map_err(|e| new_error!("could not lock unwinder cache {}\n", e))?; + let iter = trace_info.unwinder.iter_frames( + hv.read_trace_reg(crate::hypervisor::TraceRegister::RIP)?, + framehop::x86_64::UnwindRegsX86_64::new( + hv.read_trace_reg(crate::hypervisor::TraceRegister::RIP)?, + hv.read_trace_reg(crate::hypervisor::TraceRegister::RSP)?, + hv.read_trace_reg(crate::hypervisor::TraceRegister::RBP)?, + ), + &mut *cache, + &mut read_stack, + ); + iter.map(|f| Ok(f.address() - mem.layout.get_guest_code_address() as u64)) + .collect() + .map_err(|e| new_error!("couldn't unwind: {}", e)) +} +#[cfg(feature = "unwind_guest")] +fn write_stack(out: &mut std::fs::File, stack: &[u64]) { + let _ = out.write_all(&stack.len().to_ne_bytes()); + for frame in stack { + let _ = out.write_all(&frame.to_ne_bytes()); + } +} + +#[cfg(feature = "unwind_guest")] +pub(super) fn record_trace_frame( + trace_info: &TraceInfo, + frame_id: u64, + write_frame: F, +) -> Result<()> { + let Ok(mut out) = trace_info.file.lock() else { + return Ok(()); + }; + // frame structure: + // 16 bytes timestamp + let now = std::time::Instant::now().saturating_duration_since(trace_info.epoch); + let _ = out.write_all(&now.as_micros().to_ne_bytes()); + // 8 bytes frame type id + let _ = out.write_all(&frame_id.to_ne_bytes()); + // frame data + write_frame(&mut out); + Ok(()) +} + +#[cfg(feature = "trace_guest")] +pub(super) fn record_guest_trace_frame( + trace_info: &TraceInfo, + frame_id: u64, + cycles: u64, + write_frame: F, +) -> Result<()> { + let Ok(mut out) = trace_info.file.lock() else { + return Ok(()); + }; + // frame structure: + // 16 bytes timestamp + // calculate time relative to the host epoch + + let cycles_spent = cycles + - trace_info + .guest_start_tsc + .as_ref() + .map_or_else(|| 0, |c| *c); + let micros = cycles_spent as f64 / trace_info.tsc_freq as f64 * 1_000_000f64; + let guest_duration = std::time::Duration::from_micros(micros as u64); + + let guest_start_time = trace_info + .guest_start_epoch + .as_ref() + .unwrap_or(&trace_info.epoch) + .saturating_duration_since(trace_info.epoch) + .checked_add(guest_duration) + .unwrap_or(guest_duration); + + let _ = out.write_all(&guest_start_time.as_micros().to_ne_bytes()); + // 8 bytes frame type id + let _ = out.write_all(&frame_id.to_ne_bytes()); + // frame data + write_frame(&mut out); Ok(()) } @@ -149,6 +256,7 @@ fn outb_abort(mem_mgr: &mut MemMgrWrapper, data: u32) -> Resul fn handle_outb_impl( mem_mgr: &mut MemMgrWrapper, host_funcs: Arc>, + #[cfg(feature = "trace_guest")] _hv: &mut dyn Hypervisor, port: u16, data: u32, ) -> Result<()> { @@ -180,6 +288,92 @@ fn handle_outb_impl( eprint!("{}", ch); Ok(()) } + #[cfg(feature = "unwind_guest")] + OutBAction::TraceRecordStack => { + let Ok(stack) = unwind(_hv, mem_mgr.as_ref(), _hv.trace_info_as_ref()) else { + return Ok(()); + }; + record_trace_frame(_hv.trace_info_as_ref(), 1u64, |f| { + write_stack(f, &stack); + }) + } + #[cfg(feature = "mem_profile")] + OutBAction::TraceMemoryAlloc => { + let Ok(stack) = unwind(_hv, mem_mgr.as_ref(), _hv.trace_info_as_ref()) else { + return Ok(()); + }; + let Ok(amt) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RAX) else { + return Ok(()); + }; + let Ok(ptr) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RCX) else { + return Ok(()); + }; + record_trace_frame(_hv.trace_info_as_ref(), 2u64, |f| { + let _ = f.write_all(&ptr.to_ne_bytes()); + let _ = f.write_all(&amt.to_ne_bytes()); + write_stack(f, &stack); + }) + } + #[cfg(feature = "mem_profile")] + OutBAction::TraceMemoryFree => { + let Ok(stack) = unwind(_hv, mem_mgr.as_ref(), _hv.trace_info_as_ref()) else { + return Ok(()); + }; + let Ok(ptr) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RCX) else { + return Ok(()); + }; + record_trace_frame(_hv.trace_info_as_ref(), 3u64, |f| { + let _ = f.write_all(&ptr.to_ne_bytes()); + write_stack(f, &stack); + }) + } + #[cfg(feature = "trace_guest")] + OutBAction::TraceRecord => { + let Ok(len) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RAX) else { + return Ok(()); + }; + let Ok(ptr) = _hv.read_trace_reg(crate::hypervisor::TraceRegister::RCX) else { + return Ok(()); + }; + let mut buffer = vec![0u8; len as usize * std::mem::size_of::()]; + let buffer = &mut buffer[..]; + + // Read the trace records from the guest memory + // TODO: maybe this can be done without copying? + mem_mgr + .as_ref() + .shared_mem + .copy_to_slice(buffer, ptr as usize - SandboxMemoryLayout::BASE_ADDRESS) + // .read::((addr - SandboxMemoryLayout::BASE_ADDRESS as u64) as usize) + .map_err(|e| { + new_error!( + "Failed to copy trace records from guest memory to host: {:?}", + e + ) + })?; + + let traces = unsafe { + std::slice::from_raw_parts(buffer.as_ptr() as *const TraceRecord, len as usize) + }; + + { + let trace_info = _hv.trace_info_as_mut(); + // Store the start guest cycles if not already set + // This is the `entrypoint` of the guest execution + if trace_info.guest_start_tsc.is_none() && !traces.is_empty() { + trace_info.guest_start_tsc = Some(traces[0].cycles); + } + } + + for record in traces { + record_guest_trace_frame(_hv.trace_info_as_ref(), 4u64, record.cycles, |f| { + let _ = f.write_all(&record.msg_len.to_ne_bytes()); + let _ = f.write_all(&record.msg[..record.msg_len]); + })? + } + + Ok(()) + } } } @@ -192,14 +386,17 @@ pub(crate) fn outb_handler_wrapper( mut mem_mgr_wrapper: MemMgrWrapper, host_funcs_wrapper: Arc>, ) -> OutBHandlerWrapper { - let outb_func: OutBHandlerFunction = Box::new(move |port, payload| { - handle_outb_impl( - &mut mem_mgr_wrapper, - host_funcs_wrapper.clone(), - port, - payload, - ) - }); + let outb_func: OutBHandlerFunction = + Box::new(move |#[cfg(feature = "trace_guest")] hv, port, payload| { + handle_outb_impl( + &mut mem_mgr_wrapper, + host_funcs_wrapper.clone(), + #[cfg(feature = "trace_guest")] + hv, + port, + payload, + ) + }); let outb_hdl = OutBHandler::from(outb_func); Arc::new(Mutex::new(outb_hdl)) } @@ -240,13 +437,10 @@ mod tests { let sandbox_cfg = SandboxConfiguration::default(); let new_mgr = || { - let mut exe_info = simple_guest_exe_info().unwrap(); - let mut mgr = SandboxMemoryManager::load_guest_binary_into_memory( - sandbox_cfg, - &mut exe_info, - None, - ) - .unwrap(); + let exe_info = simple_guest_exe_info().unwrap(); + let (mut mgr, _) = + SandboxMemoryManager::load_guest_binary_into_memory(sandbox_cfg, exe_info, None) + .unwrap(); let mem_size = mgr.get_shared_mem_mut().mem_size(); let layout = mgr.layout; let shared_mem = mgr.get_shared_mem_mut(); @@ -355,10 +549,10 @@ mod tests { let sandbox_cfg = SandboxConfiguration::default(); tracing::subscriber::with_default(subscriber.clone(), || { let new_mgr = || { - let mut exe_info = simple_guest_exe_info().unwrap(); - let mut mgr = SandboxMemoryManager::load_guest_binary_into_memory( + let exe_info = simple_guest_exe_info().unwrap(); + let (mut mgr, _) = SandboxMemoryManager::load_guest_binary_into_memory( sandbox_cfg, - &mut exe_info, + exe_info, None, ) .unwrap(); diff --git a/src/hyperlight_host/src/sandbox/uninitialized.rs b/src/hyperlight_host/src/sandbox/uninitialized.rs index e27f91ff2..1318f0eb4 100644 --- a/src/hyperlight_host/src/sandbox/uninitialized.rs +++ b/src/hyperlight_host/src/sandbox/uninitialized.rs @@ -80,6 +80,7 @@ pub struct UninitializedSandbox { pub(crate) config: SandboxConfiguration, #[cfg(any(crashdump, gdb))] pub(crate) rt_cfg: SandboxRuntimeConfig, + pub(crate) load_info: crate::mem::exe::LoadInfo, } impl crate::sandbox_state::sandbox::UninitializedSandbox for UninitializedSandbox { @@ -250,8 +251,8 @@ impl UninitializedSandbox { } }; - let mut mem_mgr_wrapper = { - let mut mgr = UninitializedSandbox::load_guest_binary( + let (mut mem_mgr_wrapper, load_info) = { + let (mut mgr, load_info) = UninitializedSandbox::load_guest_binary( sandbox_cfg, &guest_binary, guest_blob.as_ref(), @@ -259,7 +260,7 @@ impl UninitializedSandbox { let stack_guard = Self::create_stack_guard(); mgr.set_stack_guard(&stack_guard)?; - MemMgrWrapper::new(mgr, stack_guard) + (MemMgrWrapper::new(mgr, stack_guard), load_info) }; mem_mgr_wrapper.write_memory_layout()?; @@ -278,6 +279,7 @@ impl UninitializedSandbox { config: sandbox_cfg, #[cfg(any(crashdump, gdb))] rt_cfg, + load_info, }; // If we were passed a writer for host print register it otherwise use the default. @@ -308,13 +310,16 @@ impl UninitializedSandbox { cfg: SandboxConfiguration, guest_binary: &GuestBinary, guest_blob: Option<&GuestBlob>, - ) -> Result> { - let mut exe_info = match guest_binary { + ) -> Result<( + SandboxMemoryManager, + crate::mem::exe::LoadInfo, + )> { + let exe_info = match guest_binary { GuestBinary::FilePath(bin_path_str) => ExeInfo::from_file(bin_path_str)?, GuestBinary::Buffer(buffer) => ExeInfo::from_buf(buffer)?, }; - SandboxMemoryManager::load_guest_binary_into_memory(cfg, &mut exe_info, guest_blob) + SandboxMemoryManager::load_guest_binary_into_memory(cfg, exe_info, guest_blob) } /// Set the max log level to be used by the guest. diff --git a/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs b/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs index a37f747e2..7d0ed0af6 100644 --- a/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs +++ b/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs @@ -28,6 +28,7 @@ use super::uninitialized::SandboxRuntimeConfig; use crate::HyperlightError::NoHypervisorFound; use crate::hypervisor::Hypervisor; use crate::hypervisor::handlers::{MemAccessHandlerCaller, OutBHandlerCaller}; +use crate::mem::exe::LoadInfo; use crate::mem::layout::SandboxMemoryLayout; use crate::mem::mgr::SandboxMemoryManager; use crate::mem::ptr::{GuestPtr, RawPtr}; @@ -35,6 +36,8 @@ use crate::mem::ptr_offset::Offset; use crate::mem::shared_mem::GuestSharedMemory; #[cfg(feature = "init-paging")] use crate::mem::shared_mem::SharedMemory; +#[cfg(feature = "trace_guest")] +use crate::sandbox::TraceInfo; #[cfg(gdb)] use crate::sandbox::config::DebugInfo; use crate::sandbox::host_funcs::FunctionRegistry; @@ -78,6 +81,7 @@ where &u_sbox.config, #[cfg(any(crashdump, gdb))] &u_sbox.rt_cfg, + u_sbox.load_info, )?; let outb_hdl = outb_handler_wrapper(hshm.clone(), u_sbox.host_funcs.clone()); @@ -151,6 +155,7 @@ pub(crate) fn set_up_hypervisor_partition( mgr: &mut SandboxMemoryManager, #[cfg_attr(target_os = "windows", allow(unused_variables))] config: &SandboxConfiguration, #[cfg(any(crashdump, gdb))] rt_cfg: &SandboxRuntimeConfig, + _load_info: LoadInfo, ) -> Result> { #[cfg(feature = "init-paging")] let rsp_ptr = { @@ -209,6 +214,12 @@ pub(crate) fn set_up_hypervisor_partition( None }; + #[cfg(feature = "trace_guest")] + let trace_info = TraceInfo::new( + #[cfg(feature = "unwind_guest")] + _load_info, + )?; + match *get_available_hypervisor() { #[cfg(mshv)] Some(HypervisorType::Mshv) => { @@ -222,6 +233,8 @@ pub(crate) fn set_up_hypervisor_partition( gdb_conn, #[cfg(crashdump)] rt_cfg.clone(), + #[cfg(feature = "trace_guest")] + trace_info, )?; Ok(Box::new(hv)) } @@ -238,6 +251,8 @@ pub(crate) fn set_up_hypervisor_partition( gdb_conn, #[cfg(crashdump)] rt_cfg.clone(), + #[cfg(feature = "trace_guest")] + trace_info, )?; Ok(Box::new(hv)) } @@ -260,6 +275,8 @@ pub(crate) fn set_up_hypervisor_partition( gdb_conn, #[cfg(crashdump)] rt_cfg.clone(), + #[cfg(feature = "trace_guest")] + trace_info, )?; Ok(Box::new(hv)) } diff --git a/src/tests/rust_guests/callbackguest/Cargo.lock b/src/tests/rust_guests/callbackguest/Cargo.lock index 82979afeb..0015fb70d 100644 --- a/src/tests/rust_guests/callbackguest/Cargo.lock +++ b/src/tests/rust_guests/callbackguest/Cargo.lock @@ -85,6 +85,8 @@ version = "0.7.0" dependencies = [ "anyhow", "hyperlight-common", + "hyperlight-guest-tracing", + "hyperlight-guest-tracing-macro", "serde_json", ] @@ -98,10 +100,29 @@ dependencies = [ "glob", "hyperlight-common", "hyperlight-guest", + "hyperlight-guest-tracing", + "hyperlight-guest-tracing-macro", "log", "spin 0.10.0", ] +[[package]] +name = "hyperlight-guest-tracing" +version = "0.7.0" +dependencies = [ + "hyperlight-common", + "spin 0.10.0", +] + +[[package]] +name = "hyperlight-guest-tracing-macro" +version = "0.7.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "itoa" version = "1.0.15" @@ -132,9 +153,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -233,9 +254,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", diff --git a/src/tests/rust_guests/callbackguest/Cargo.toml b/src/tests/rust_guests/callbackguest/Cargo.toml index ccbdf6a0a..2d63452e4 100644 --- a/src/tests/rust_guests/callbackguest/Cargo.toml +++ b/src/tests/rust_guests/callbackguest/Cargo.toml @@ -6,4 +6,10 @@ edition = "2021" [dependencies] hyperlight-guest = { path = "../../../hyperlight_guest" } hyperlight-guest-bin = { path = "../../../hyperlight_guest_bin" } -hyperlight-common = { path = "../../../hyperlight_common", default-features = false } \ No newline at end of file +hyperlight-common = { path = "../../../hyperlight_common", default-features = false } + +[features] +default = [] +trace_guest = ["hyperlight-guest-bin/trace_guest"] +unwind_guest = ["hyperlight-common/unwind_guest"] +mem_profile = ["hyperlight-common/mem_profile", "hyperlight-guest-bin/mem_profile"] \ No newline at end of file diff --git a/src/tests/rust_guests/dummyguest/Cargo.lock b/src/tests/rust_guests/dummyguest/Cargo.lock index 3848de252..d87ea19cc 100644 --- a/src/tests/rust_guests/dummyguest/Cargo.lock +++ b/src/tests/rust_guests/dummyguest/Cargo.lock @@ -1,7 +1,269 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "buddy_system_allocator" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0108968a3a2dab95b089c0fc3f1afa7759aa5ebe6f1d86d206d6f7ba726eb" +dependencies = [ + "spin 0.9.8", +] + +[[package]] +name = "cc" +version = "1.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "dummyguest" version = "0.4.0" +dependencies = [ + "hyperlight-common", + "hyperlight-guest-bin", +] + +[[package]] +name = "flatbuffers" +version = "25.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1045398c1bfd89168b5fd3f1fc11f6e70b34f6f66300c87d44d3de849463abf1" +dependencies = [ + "bitflags", + "rustc_version", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "hyperlight-common" +version = "0.7.0" +dependencies = [ + "anyhow", + "flatbuffers", + "log", + "spin 0.10.0", +] + +[[package]] +name = "hyperlight-guest" +version = "0.7.0" +dependencies = [ + "anyhow", + "hyperlight-common", + "hyperlight-guest-tracing", + "hyperlight-guest-tracing-macro", + "serde_json", +] + +[[package]] +name = "hyperlight-guest-bin" +version = "0.7.0" +dependencies = [ + "buddy_system_allocator", + "cc", + "cfg-if", + "glob", + "hyperlight-common", + "hyperlight-guest", + "hyperlight-guest-tracing", + "hyperlight-guest-tracing-macro", + "log", + "spin 0.10.0", +] + +[[package]] +name = "hyperlight-guest-tracing" +version = "0.7.0" +dependencies = [ + "hyperlight-common", + "spin 0.10.0", +] + +[[package]] +name = "hyperlight-guest-tracing-macro" +version = "0.7.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/src/tests/rust_guests/dummyguest/Cargo.toml b/src/tests/rust_guests/dummyguest/Cargo.toml index 619031cae..3de8389e9 100644 --- a/src/tests/rust_guests/dummyguest/Cargo.toml +++ b/src/tests/rust_guests/dummyguest/Cargo.toml @@ -1,4 +1,15 @@ [package] name = "dummyguest" version = "0.4.0" -edition = "2021" \ No newline at end of file +edition = "2021" + + +[dependencies] +hyperlight-guest-bin = { path = "../../../hyperlight_guest_bin" } +hyperlight-common = { path = "../../../hyperlight_common", default-features = false } + +[features] +default = [] +trace_guest = ["hyperlight-guest-bin/trace_guest"] +unwind_guest = ["hyperlight-common/unwind_guest"] +mem_profile = ["hyperlight-common/mem_profile", "hyperlight-guest-bin/mem_profile"] \ No newline at end of file diff --git a/src/tests/rust_guests/simpleguest/Cargo.lock b/src/tests/rust_guests/simpleguest/Cargo.lock index b30d4ef5e..be07dfbf3 100644 --- a/src/tests/rust_guests/simpleguest/Cargo.lock +++ b/src/tests/rust_guests/simpleguest/Cargo.lock @@ -76,6 +76,8 @@ version = "0.7.0" dependencies = [ "anyhow", "hyperlight-common", + "hyperlight-guest-tracing", + "hyperlight-guest-tracing-macro", "serde_json", ] @@ -89,10 +91,29 @@ dependencies = [ "glob", "hyperlight-common", "hyperlight-guest", + "hyperlight-guest-tracing", + "hyperlight-guest-tracing-macro", "log", "spin 0.10.0", ] +[[package]] +name = "hyperlight-guest-tracing" +version = "0.7.0" +dependencies = [ + "hyperlight-common", + "spin 0.10.0", +] + +[[package]] +name = "hyperlight-guest-tracing-macro" +version = "0.7.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "itoa" version = "1.0.15" @@ -123,9 +144,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -234,9 +255,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", diff --git a/src/tests/rust_guests/simpleguest/Cargo.toml b/src/tests/rust_guests/simpleguest/Cargo.toml index f2e75ee1c..515b618d5 100644 --- a/src/tests/rust_guests/simpleguest/Cargo.toml +++ b/src/tests/rust_guests/simpleguest/Cargo.toml @@ -8,3 +8,10 @@ hyperlight-guest = { path = "../../../hyperlight_guest" } hyperlight-guest-bin = { path = "../../../hyperlight_guest_bin" } hyperlight-common = { path = "../../../hyperlight_common", default-features = false } log = {version = "0.4", default-features = false } + +[features] +default = [] +trace_guest = ["hyperlight-guest-bin/trace_guest"] +unwind_guest = ["hyperlight-common/unwind_guest"] +mem_profile = ["hyperlight-common/mem_profile", "hyperlight-guest-bin/mem_profile"] + diff --git a/src/tests/rust_guests/witguest/Cargo.lock b/src/tests/rust_guests/witguest/Cargo.lock index a1edc4b10..b6d65c1bf 100644 --- a/src/tests/rust_guests/witguest/Cargo.lock +++ b/src/tests/rust_guests/witguest/Cargo.lock @@ -219,6 +219,8 @@ version = "0.7.0" dependencies = [ "anyhow", "hyperlight-common", + "hyperlight-guest-tracing", + "hyperlight-guest-tracing-macro", "serde_json", ] @@ -232,10 +234,29 @@ dependencies = [ "glob", "hyperlight-common", "hyperlight-guest", + "hyperlight-guest-tracing", + "hyperlight-guest-tracing-macro", "log", "spin 0.10.0", ] +[[package]] +name = "hyperlight-guest-tracing" +version = "0.7.0" +dependencies = [ + "hyperlight-common", + "spin 0.10.0", +] + +[[package]] +name = "hyperlight-guest-tracing-macro" +version = "0.7.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "indexmap" version = "2.9.0" diff --git a/src/tests/rust_guests/witguest/Cargo.toml b/src/tests/rust_guests/witguest/Cargo.toml index 63b38fc13..0be5aec00 100644 --- a/src/tests/rust_guests/witguest/Cargo.toml +++ b/src/tests/rust_guests/witguest/Cargo.toml @@ -7,4 +7,10 @@ edition = "2021" hyperlight-guest = { path = "../../../hyperlight_guest" } hyperlight-guest-bin = { path = "../../../hyperlight_guest_bin" } hyperlight-common = { path = "../../../hyperlight_common", default-features = false } -hyperlight-component-macro = { path = "../../../hyperlight_component_macro" } \ No newline at end of file +hyperlight-component-macro = { path = "../../../hyperlight_component_macro" } + +[features] +default = [] +trace_guest = ["hyperlight-guest-bin/trace_guest"] +unwind_guest = ["hyperlight-common/unwind_guest"] +mem_profile = ["hyperlight-common/mem_profile", "hyperlight-guest-bin/mem_profile"] \ No newline at end of file diff --git a/src/trace_dump/Cargo.toml b/src/trace_dump/Cargo.toml new file mode 100644 index 000000000..0149980e8 --- /dev/null +++ b/src/trace_dump/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "trace_dump" +version = "0.0.0" +publish = false +edition = "2021" + +[dependencies] +addr2line = "0.24.2" +piet-common = { version = "0.6.2", features = [ "png" ] } +blake3 = { version = "1.5.5" } + +[[bin]] +name = "trace_dump" +path = "main.rs" diff --git a/src/trace_dump/main.rs b/src/trace_dump/main.rs new file mode 100644 index 000000000..dddb6d589 --- /dev/null +++ b/src/trace_dump/main.rs @@ -0,0 +1,885 @@ +/* +Copyright 2025 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 std::collections::HashMap; +use std::env; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom, Write}; +use std::path::PathBuf; +use std::rc::Rc; +use std::sync::Mutex; +use std::time::Duration; + +use piet_common::{Color, RenderContext, Text, TextLayout, TextLayoutBuilder, kurbo}; + +fn read_u8_vec(inf: &mut File) -> Option> { + let len = read_u64(inf)?; + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + vec.push(read_u8(inf)?); + } + Some(vec) +} +fn read_u8(inf: &mut File) -> Option { + let mut bytes: [u8; 1] = [0; 1]; + inf.read_exact(&mut bytes).ok()?; + Some(u8::from_ne_bytes(bytes)) +} +fn read_u128(inf: &mut File) -> Result { + let mut bytes: [u8; 16] = [0; 16]; + inf.read_exact(&mut bytes)?; + Ok(u128::from_ne_bytes(bytes)) +} +fn read_u64(inf: &mut File) -> Option { + let mut bytes: [u8; 8] = [0; 8]; + inf.read_exact(&mut bytes).ok()?; + Some(u64::from_ne_bytes(bytes)) +} +fn read_u64_vec(inf: &mut File) -> Option> { + let len = read_u64(inf)?; + let mut vec = Vec::with_capacity(len as usize); + for _ in 0..len { + vec.push(read_u64(inf)?); + } + Some(vec) +} +fn read_blake3_hash(inf: &mut File) -> Option { + let mut bytes: [u8; 32] = [0; 32]; + inf.read_exact(&mut bytes).ok()?; + Some(blake3::Hash::from_bytes(bytes)) +} + +fn dump_stack(state: &mut State, trace: Rc<[u64]>) { + for frame in &*trace { + println!(" {:x} {}", frame, state.symbol_cache.format_symbol(*frame)); + } +} + +fn dump_ident(_state: &mut State, _rs: &mut (), now: Duration, hash: blake3::Hash) -> Option<()> { + println!("\n[{:9?}] BLAKE3 hash of binary is {}", now, hash); + Some(()) +} + +fn dump_unwind(state: &mut State, _rs: &mut (), now: Duration, trace: Rc<[u64]>) -> Option<()> { + println!("\n[{:9?}] Guest requested stack trace", now); + dump_stack(state, trace); + Some(()) +} + +fn dump_alloc( + state: &mut State, + _rs: &mut (), + now: Duration, + ptr: u64, + amt: u64, + trace: Rc<[u64]>, +) -> Option<()> { + println!("\n[{:9?}] Allocated {} bytes at 0x{:x}", now, amt, ptr); + dump_stack(state, trace); + Some(()) +} + +fn dump_free( + state: &mut State, + _rs: &mut (), + now: Duration, + ptr: u64, + amt: u64, + trace: Rc<[u64]>, +) -> Option<()> { + println!("\n[{:9?}] Freed {} bytes at 0x{:x}", now, amt, ptr); + dump_stack(state, trace); + Some(()) +} + +fn dump_trace_record(_state: &mut State, _rs: &mut (), now: Duration, msg: Rc<[u8]>) -> Option<()> { + let msg = String::from_utf8_lossy(&msg); + println!("\n[{:9?}] {}", now, msg); + + Some(()) +} + +// todo: this should use something more reasonable than a hash table +// for each node. let's measure the out-degree and see if a small +// array is better, to start. +struct TraceTrie { + value: T, + children: HashMap>, +} +impl TraceTrie { + fn new() -> Self { + Self { + value: Default::default(), + children: HashMap::new(), + } + } + fn apply_path<'a, 'i, F: Fn(&mut T), I: Iterator>( + &'a mut self, + trace: I, + f: F, + ) { + let mut node = self; + for frame in trace { + f(&mut node.value); + node = (*node).children.entry(*frame).or_insert(Self::new()) + } + f(&mut node.value); + } +} + +struct SymbolCache { + loader: addr2line::Loader, + symbol_cache: HashMap)>>, +} +impl SymbolCache { + fn resolve_symbol<'c>(&'c mut self, addr: u64) -> &'c Option<(String, Option)> { + self.symbol_cache.entry(addr).or_insert_with(|| { + let frame = &self.loader.find_frames(addr).ok()?.next().ok()??; + let function = frame.function.as_ref()?; + let demangled = + addr2line::demangle_auto(function.name.to_string_lossy(), function.language) + .to_string(); + Some((demangled, frame.location.as_ref()?.line)) + }) + } + fn format_symbol(&mut self, addr: u64) -> String { + match self.resolve_symbol(addr) { + None => format!("{}", addr), + Some((f, None)) => f.clone(), + Some((f, Some(l))) => format!("{}:{}", f, l), + } + } +} + +enum Visualisation { + Bar, + Flame, +} +impl std::fmt::Display for Visualisation { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Visualisation::Bar => write!(f, "bar"), + Visualisation::Flame => write!(f, "flame"), + } + } +} + +struct State { + inf: File, + symbol_cache: SymbolCache, + out_dir: Option, + start_time: Option, + end_time: Option, + allocs: HashMap)>, + sites: HashMap, + traces: TraceTrie, + total: u64, + max_total: u64, + max_duration: Duration, + num_durations: u64, +} + +struct ViewParams { + margin: f64, + width: f64, + height: f64, + label_gap: f64, + amount_gap: f64, + bar_start: f64, + bar_height: f64, + bar_leading: f64, + bar_brush: R::Brush, +} + +fn draw_bg(render_context: &mut R, v: &ViewParams) { + let bg_brush = render_context.solid_brush(Color::rgb8(255, 255, 255)); + render_context.fill( + kurbo::Rect { + x0: 0.0, + y0: 0.0, + x1: v.width + v.margin * 2.0, + y1: v.height + v.margin * 2.0, + }, + &bg_brush, + ); +} +fn draw_bar( + render_context: &mut R, + v: &ViewParams, + stroke: bool, + n: u64, + label: String, + value: u64, + max_value: u64, +) -> Option<()> { + let left = v.margin + v.bar_start; + let top = v.margin + (n as f64) * (v.bar_height + v.bar_leading); + if stroke { + render_context.stroke( + kurbo::Rect { + x0: left, + y0: top, + //x1: v.margin + v.width, + x1: left + (v.width - v.bar_start), + y1: top + v.bar_height, + }, + &v.bar_brush, + 1.0, + ); + } + let right = left + (v.width - v.bar_start) * value as f64 / max_value as f64; + render_context.fill( + kurbo::Rect { + x0: left, + y0: top, + x1: right, + y1: top + v.bar_height, + }, + &v.bar_brush, + ); + let layout = render_context.text().new_text_layout(label).build().ok()?; + let metric = layout + .line_metric(0) + .expect("there must be at least one line metric"); + render_context.draw_text( + &layout, + kurbo::Point { + x: left - v.label_gap - layout.trailing_whitespace_width(), + y: top - metric.y_offset + (v.bar_height - metric.baseline) / 2.0, + }, + ); + let layout = render_context + .text() + .new_text_layout(format!("{}", value)) + .default_attribute(piet_common::TextAttribute::TextColor(Color::rgb8( + 255, 255, 255, + ))) + .build() + .ok()?; + let metric = layout + .line_metric(0) + .expect("there must be at least one line metric"); + if right - left > v.amount_gap + layout.trailing_whitespace_width() { + render_context.draw_text( + &layout, + kurbo::Point { + x: right - v.amount_gap - layout.trailing_whitespace_width(), + y: top + (v.bar_height - metric.height) / 2.0, + }, + ); + } else { + // hopefully the choice of colour doesn't affect the layout much + let layout = render_context + .text() + .new_text_layout(format!("{}", value)) + .build() + .ok()?; + let metric = layout + .line_metric(0) + .expect("there must be at least one line metric"); + render_context.draw_text( + &layout, + kurbo::Point { + x: right + v.amount_gap, + y: top + (v.bar_height - metric.height) / 2.0, + }, + ); + } + Some(()) +} + +trait RenderWrapper { + fn render(&mut self, ctx: &mut R, width: u64, height: u64) -> Option<()>; +} +fn render_bitmap( + mut out: O, + device: &mut piet_common::Device, + mut render: F, +) -> Option<()> { + let width = 1920; + let height = 1080; + let mut bitmap = device.bitmap_target(width, height, 1.0).ok()?; + { + let mut render_context = bitmap.render_context(); + render.render(&mut render_context, width as u64, height as u64)?; + render_context.finish().ok()?; + } + out.write_all( + bitmap + .to_image_buf(piet_common::ImageFormat::RgbaPremul) + .expect("unable to access image buffer") + .raw_pixels(), + ) + .expect("write to stdout"); + Some(()) +} + +impl ViewParams { + fn new(ctx: &mut R, ht: u64, wd: u64) -> Self { + let margin = 20.0; + let width = wd as f64 - margin * 2.0; + let height = ht as f64 - margin * 2.0; + let bar_brush = ctx.solid_brush(Color::rgb8(128, 0, 128)); + Self { + margin, + width, + height, + label_gap: 10.0, + amount_gap: 5.0, + bar_start: width / 4.0, + bar_height: 12.0, + bar_leading: 4.0, + bar_brush: bar_brush, + } + } +} + +struct BarRenderer<'r> { + state: &'r mut State, + now: Duration, +} +impl<'r, 'a, 's> RenderWrapper for BarRenderer<'r> { + fn render(&mut self, ctx: &mut R, wd: u64, ht: u64) -> Option<()> { + let v = ViewParams::new(ctx, ht, wd); + draw_bg(ctx, &v); + draw_bar( + ctx, + &v, + true, + 0, + "Execution time".to_string(), + self.now.as_micros() as u64, + self.state.max_duration.as_micros() as u64, + )?; + draw_bar( + ctx, + &v, + true, + 1, + "Total memory consumption".to_string(), + self.state.total, + self.state.max_total, + )?; + + let mut points: Vec<(&u64, &u64)> = self.state.sites.iter().collect(); + points.sort_by_key(|(_, size)| *size); + for (i, (site, size)) in points.iter().rev().enumerate() { + draw_bar( + ctx, + &v, + false, + (3 + i) as u64, + (&mut self.state.symbol_cache).format_symbol(**site), + **size, + self.state.total, + )?; + } + Some(()) + } +} + +struct FlameRenderer<'r> { + state: &'r mut State, + now: Duration, +} +#[derive(Clone, Copy)] +struct FlameView { + total_allocated: u64, + bottom: f64, + left: f64, + color: u8, +} +fn draw_flame( + ctx: &mut R, + v: &ViewParams, + fv: &FlameView, + sc: &mut SymbolCache, + t: &TraceTrie, + addr: Option, +) -> Option<()> { + let rect = kurbo::Rect { + x0: v.margin + fv.left, + y0: v.margin + fv.bottom - v.bar_height, + x1: v.margin + fv.left + (t.value as f64) * v.width / (fv.total_allocated as f64), + y1: v.margin + fv.bottom, + }; + ctx.fill(rect, &Color::rgb8(255, 0, fv.color)); + if let Some(addr) = addr { + ctx.save().ok()?; + ctx.clip(rect); + let layout = ctx + .text() + .new_text_layout(sc.format_symbol(addr)) + .default_attribute(piet_common::TextAttribute::FontSize(9.0)) + .build() + .ok()?; + ctx.draw_text( + &layout, + kurbo::Point { + x: v.margin + fv.left, + y: v.margin + fv.bottom - v.bar_height, + }, + ); + ctx.restore().ok()?; + } + let mut child_fv = FlameView { + total_allocated: fv.total_allocated, + bottom: fv.bottom - v.bar_height, + left: fv.left, + color: fv.color, + }; + for (addr, child) in &t.children { + draw_flame(ctx, v, &child_fv, sc, child, Some(*addr))?; + child_fv.left += (child.value as f64) * v.width / (fv.total_allocated as f64); + child_fv.color = child_fv.color.wrapping_add(85); + } + Some(()) +} +impl<'r, 'a, 's> RenderWrapper for FlameRenderer<'r> { + fn render(&mut self, ctx: &mut R, wd: u64, ht: u64) -> Option<()> { + let mut v = ViewParams::new(ctx, ht, wd); + v.bar_start = v.width / 8.0; + draw_bg(ctx, &v); + draw_bar( + ctx, + &v, + true, + 0, + "Execution time".to_string(), + self.now.as_micros() as u64, + self.state.max_duration.as_micros() as u64, + )?; + draw_bar( + ctx, + &v, + true, + 1, + "Total memory consumption".to_string(), + self.state.total, + self.state.max_total, + )?; + + let fv = FlameView { + total_allocated: self.state.total, + bottom: v.height, + left: 0.0, + color: 0, + }; + draw_flame( + ctx, + &v, + &fv, + &mut self.state.symbol_cache, + &self.state.traces, + None, + )?; + Some(()) + } +} + +struct RenderState<'a> { + device: &'a mut piet_common::Device, + bar_out: std::process::ChildStdin, + flame_out: std::process::ChildStdin, +} +fn render_state(state: &mut State, rs: &mut RenderState, now: Duration) -> Option<()> { + let late_enough = state.start_time.map(|t| now >= t).unwrap_or(true); + let early_enough = state.end_time.map(|t| now <= t).unwrap_or(true); + if late_enough && early_enough { + render_bitmap(&mut rs.bar_out, rs.device, BarRenderer { state, now })?; + render_bitmap(&mut rs.flame_out, rs.device, FlameRenderer { state, now })?; + } + Some(()) +} + +fn render_ident( + _state: &mut State, + _rs: &mut RenderState, + _now: Duration, + _hash: blake3::Hash, +) -> Option<()> { + Some(()) +} + +fn render_unwind( + _state: &mut State, + _rs: &mut RenderState, + _now: Duration, + _trace: Rc<[u64]>, +) -> Option<()> { + Some(()) +} + +fn render_alloc( + state: &mut State, + rs: &mut RenderState, + now: Duration, + _ptr: u64, + amt: u64, + trace: Rc<[u64]>, +) -> Option<()> { + for frame in trace.as_ref() { + *state.sites.entry(*frame).or_insert(0) += amt; + } + state.traces.apply_path(trace.iter().rev(), |t| *t += amt); + render_state(state, rs, now)?; + Some(()) +} + +fn render_free( + state: &mut State, + rs: &mut RenderState, + now: Duration, + ptr: u64, + _amt: u64, + _trace: Rc<[u64]>, +) -> Option<()> { + let (amt, trace) = state + .allocs + .get(&ptr) + .expect("free of un-allocated address"); + for frame in trace.as_ref() { + *state + .sites + .get_mut(frame) + .expect("free of un-allocated site") -= amt; + } + state.traces.apply_path(trace.iter().rev(), |t| *t -= amt); + render_state(state, rs, now)?; + Some(()) +} + +fn render_trace_record( + _state: &mut State, + _rs: &mut RenderState, + _now: Duration, + _msg: Rc<[u8]>, +) -> Option<()> { + Some(()) +} + +fn read_file( + state: &mut State, + mut handle_state: S, + handle_ident: I, + handle_unwind: U, + handle_alloc: A, + handle_free: F, + handle_trace_record: T, +) -> Option<()> +where + I: Fn(&mut State, &mut S, Duration, blake3::Hash) -> Option<()>, + U: Fn(&mut State, &mut S, Duration, Rc<[u64]>) -> Option<()>, + A: Fn(&mut State, &mut S, Duration, u64, u64, Rc<[u64]>) -> Option<()>, + F: Fn(&mut State, &mut S, Duration, u64, u64, Rc<[u64]>) -> Option<()>, + T: Fn(&mut State, &mut S, Duration, Rc<[u8]>) -> Option<()>, +{ + loop { + let time = match read_u128(&mut state.inf) { + Ok(t) => t, + Err(e) => { + if e.kind() == std::io::ErrorKind::UnexpectedEof { + break; + } else { + return None; + } + } + }; + let now = Duration::from_micros(time.try_into().expect("duration too large for u64")); + state.max_duration = std::cmp::max(state.max_duration, now); + state.num_durations += 1; + + let frame_id = read_u64(&mut state.inf)?; + + if frame_id == 0 { + let hash = read_blake3_hash(&mut state.inf)?; + handle_ident(state, &mut handle_state, now, hash)?; + } else if frame_id == 1 { + let trace: Rc<[u64]> = read_u64_vec(&mut state.inf)?.into(); + handle_unwind(state, &mut handle_state, now, trace)?; + } else if frame_id == 2 { + let ptr = read_u64(&mut state.inf)?; + let amt = read_u64(&mut state.inf)?; + let trace: Rc<[u64]> = read_u64_vec(&mut state.inf)?.into(); + state.allocs.insert(ptr, (amt, trace.clone())); + state.total += amt; + if state.total > state.max_total { + state.max_total = state.total; + } + handle_alloc(state, &mut handle_state, now, ptr, amt, trace)?; + } else if frame_id == 3 { + let ptr = read_u64(&mut state.inf)?; + let _ = read_u64_vec(&mut state.inf)?; + let amt_trace = state + .allocs + .get(&ptr) + .expect("free of un-allocated address"); + let amt = amt_trace.0; + let trace = amt_trace.1.clone(); + state.total -= amt; + handle_free(state, &mut handle_state, now, ptr, amt, trace)?; + } else if frame_id == 4 { + let msg = read_u8_vec(&mut state.inf)?.into(); + handle_trace_record(state, &mut handle_state, now, msg)?; + } else { + return None; + } + } + Some(()) +} + +fn mkv_for(out_dir: &PathBuf, vis: Visualisation, start: Duration) -> PathBuf { + out_dir.join(format!("{:08}.{}.mkv", start.as_micros(), vis)) +} +fn ffmpeg_for( + out_dir: &PathBuf, + vis: Visualisation, + start: Duration, +) -> Option { + let out = std::fs::File::create(out_dir.join(format!("{:08}.{}.out", start.as_micros(), vis))) + .ok()?; + let err = std::fs::File::create(out_dir.join(format!("{:08}.{}.err", start.as_micros(), vis))) + .ok()?; + let mkv = mkv_for(out_dir, vis, start); + let _ = std::fs::remove_file(&mkv); + std::process::Command::new("ffmpeg") + .args([ + "-f", + "rawvideo", + "-pix_fmt", + "rgba", + "-framerate", + "60", + "-video_size", + "1920x1080", + "-i", + "-", + "-c:v", + "libvpx-vp9", + "-crf", + "15", + "-b:v", + "0", + ]) + .arg(mkv) + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::from(out)) + .stderr(std::process::Stdio::from(err)) + .spawn() + .ok() +} + +fn spawn_render_thread( + state: &mut State, + exe_file_name: String, + in_file_name: String, + interval: (Duration, Duration), +) -> std::thread::JoinHandle> { + let out_dir = state.out_dir.clone(); + let max_total = state.max_total; + let max_duration = state.max_duration; + std::thread::spawn(move || { + let out_dir = out_dir?; + eprintln!( + "> {:08} -- {:08}", + interval.0.as_micros(), + interval.1.as_micros() + ); + let loader = addr2line::Loader::new(exe_file_name).ok()?; + let inf = File::open(in_file_name).expect("could not open dump file"); + let mut bar_ffmpeg = ffmpeg_for(&out_dir, Visualisation::Bar, interval.0)?; + let mut flame_ffmpeg = ffmpeg_for(&out_dir, Visualisation::Flame, interval.0)?; + let mut job_state = State { + inf: inf, + symbol_cache: SymbolCache { + loader, + symbol_cache: HashMap::new(), + }, + start_time: Some(interval.0), + end_time: Some(interval.1), + out_dir: Some(out_dir), + allocs: HashMap::new(), + sites: HashMap::new(), + traces: TraceTrie::new(), + total: 0, + max_total, + max_duration, + num_durations: 0, + }; + /* plot each individual frame */ + let mut device = piet_common::Device::new().expect("could not create Piet device"); + let rs = RenderState { + device: &mut device, + bar_out: bar_ffmpeg.stdin.take().expect("bar ffmpeg stdin"), + flame_out: flame_ffmpeg.stdin.take().expect("flame ffmpeg stdin"), + }; + read_file( + &mut job_state, + rs, + render_ident, + render_unwind, + render_alloc, + render_free, + render_trace_record, + )?; + bar_ffmpeg.wait().ok()?; + flame_ffmpeg.wait().ok()?; + Some(()) + }) +} + +fn main() { + let args: Vec = env::args().collect(); + let is_list = args.len() == 4 && args[3] == "list_frames"; + let is_plot = args.len() == 6 && args[3] == "plot_mem"; + if !is_list && !is_plot { + eprintln!("usage: {} list_frames", args[0]); + eprintln!( + "usage: {} plot_mem ", + args[0] + ); + return; + } + let Ok(loader) = addr2line::Loader::new(&args[1]) else { + eprintln!("could not load guest binary {}", args[1]); + return; + }; + let inf = File::open(args[2].clone()).expect("could not open trace file"); + let state = State { + inf: inf, + symbol_cache: SymbolCache { + loader: loader, + symbol_cache: HashMap::new(), + }, + start_time: None, + end_time: None, + out_dir: None, + allocs: HashMap::new(), + sites: HashMap::new(), + traces: TraceTrie::new(), + total: 0, + max_total: 0, + max_duration: Duration::ZERO, + num_durations: 0, + }; + if is_list { + dump_trace(state); + } else if is_plot { + plot_mem(args, state); + } +} + +fn dump_trace(mut state: State) { + read_file( + &mut state, + (), + dump_ident, + dump_unwind, + dump_alloc, + dump_free, + dump_trace_record, + ); +} + +fn plot_mem(args: Vec, mut state: State) { + let out_dir = PathBuf::from(args[4].clone()); + state.out_dir = Some(out_dir.clone()); + std::fs::create_dir_all(&out_dir).expect("could not create output dir"); + + /* first pass: compute the maximum memory usage */ + match read_file( + &mut state, + (), + |_, _, _, _| Some(()), + |_, _, _, _| Some(()), + |_, _, _, _, _, _| Some(()), + |_, _, _, _, _, _| Some(()), + |_, _, _, _| Some(()), + ) { + Some(()) => (), + None => { + eprintln!("i/o error encountered"); + () + } + } + eprintln!("max total memory used is {}", state.max_total); + state + .inf + .seek(SeekFrom::Start(0)) + .expect("couldn't seek back"); + state.allocs = HashMap::new(); + state.total = 0; + + /* second pass: compute fair durations so that each parallel job + * processes the same number of frames */ + let num_segments = str::parse::(&args[5]).expect("number of segments must be a number"); + let durations_per_segment = (state.num_durations - 1) / num_segments + 1; + state.num_durations = 0; + let jobs = Mutex::new(Vec::new()); + let start_duration = Mutex::new(Duration::ZERO); + let count_frame = |s: &mut State, _: &mut (), n: Duration, _, _, _| { + if s.num_durations == 1 { + *start_duration.lock().unwrap() = n; + } + if s.num_durations == durations_per_segment { + (*jobs.lock().unwrap()).push((*start_duration.lock().unwrap(), n)); + s.num_durations = 0; + } + Some(()) + }; + read_file( + &mut state, + (), + |_, _, _, _| Some(()), + |_, _, _, _| Some(()), + count_frame, + count_frame, + |_, _, _, _| Some(()), + ); + if state.num_durations > 0 { + (*jobs.lock().unwrap()).push((*start_duration.lock().unwrap(), state.max_duration)); + } + + /* third pass: render in parallel */ + let mut handles = Vec::new(); + for job in &*jobs.lock().unwrap() { + handles.push(spawn_render_thread( + &mut state, + args[1].clone(), + args[2].clone(), + *job, + )); + } + for handle in handles { + handle.join().expect("thread died"); + } + + /* merge all the parallel rendered segments */ + let mut merge_bar = std::process::Command::new("mkvmerge"); + merge_bar.arg("-o").arg(out_dir.join("bar.mkv")); + let mut merge_flame = std::process::Command::new("mkvmerge"); + merge_flame.arg("-o").arg(out_dir.join("flame.mkv")); + for (n, job) in (*jobs.lock().unwrap()).iter().enumerate() { + if n > 0 { + merge_bar.arg("+"); + merge_flame.arg("+"); + } + merge_bar.arg(mkv_for(&out_dir, Visualisation::Bar, job.0)); + merge_flame.arg(mkv_for(&out_dir, Visualisation::Flame, job.0)); + } + merge_bar.status().unwrap(); + merge_flame.status().unwrap(); +}