diff --git a/Cargo.lock b/Cargo.lock index 518512286..d77749922 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,7 +8,14 @@ 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]] @@ -108,12 +115,24 @@ version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +[[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-stream" version = "0.3.6" @@ -133,7 +152,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -144,7 +163,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -244,7 +263,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn", + "syn 2.0.87", ] [[package]] @@ -274,6 +293,19 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "blake3" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8ee0c1824c4dea5b5f81736aff91bae041d2c07ee1192bec91054e10e3e601e" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -330,6 +362,31 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" +[[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" @@ -350,7 +407,7 @@ dependencies = [ "quote", "serde", "serde_json", - "syn", + "syn 2.0.87", "tempfile", "toml", ] @@ -375,6 +432,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.0" @@ -472,12 +539,73 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +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" 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.15" @@ -487,6 +615,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.5.1" @@ -634,7 +771,19 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", +] + +[[package]] +name = "dwrote" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70182709525a3632b2ba96b6569225467b18ecb4a77f46d255f713a6bebf05fd" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", ] [[package]] @@ -703,6 +852,15 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +[[package]] +name = "fdeflate" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" +dependencies = [ + "simd-adler32", +] + [[package]] name = "flatbuffers" version = "24.3.25" @@ -713,12 +871,37 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[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" @@ -798,7 +981,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -862,6 +1045,39 @@ dependencies = [ "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.19.0" @@ -875,6 +1091,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.1" @@ -894,6 +1157,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.9.2" @@ -1113,6 +1387,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bitflags 2.6.0", + "blake3", "built", "cfg-if", "cfg_aliases", @@ -1346,7 +1621,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -1449,6 +1724,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.10.0" @@ -1610,6 +1894,12 @@ 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 = "matchit" version = "0.7.3" @@ -1622,6 +1912,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + [[package]] name = "mime" version = "0.3.17" @@ -1641,6 +1940,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -1726,7 +2026,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -1735,7 +2035,9 @@ version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ + "flate2", "memchr", + "ruzstd", ] [[package]] @@ -1845,6 +2147,59 @@ dependencies = [ "winapi", ] +[[package]] +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.3" @@ -1893,6 +2248,93 @@ 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 = "9dd8497cc0bcfecb1e14e027428c5e3eaf9af6e14761176e1212006d8bdba387" +dependencies = [ + "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 = "piet-coregraphics" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a819b41d2ddb1d8abf3e45e49422f866cba281b4abb5e2fb948bba06e2c3d3f7" +dependencies = [ + "associative-cache", + "core-foundation", + "core-foundation-sys", + "core-graphics", + "core-text", + "foreign-types", + "piet", +] + +[[package]] +name = "piet-direct2d" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd00e91df4f987be40eb13042afe6ee9e54468466bdb7486390b40d4fef0993e" +dependencies = [ + "associative-cache", + "dwrote", + "piet", + "utf16_lit", + "winapi", + "wio", +] + +[[package]] +name = "piet-web" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a560232a94e535979923d49062d1c6d5407b3804bcd0d0b4cb9e25a9b41db1e" +dependencies = [ + "js-sys", + "piet", + "unicode-segmentation", + "wasm-bindgen", + "web-sys", + "xi-unicode", +] + [[package]] name = "pin-project" version = "1.1.7" @@ -1910,7 +2352,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -1965,6 +2407,19 @@ dependencies = [ "plotters-backend", ] +[[package]] +name = "png" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -1981,7 +2436,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.87", +] + +[[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]] @@ -2062,7 +2551,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -2230,7 +2719,7 @@ dependencies = [ "quote", "rust-embed-utils", "shellexpand", - "syn", + "syn 2.0.87", "walkdir", ] @@ -2297,6 +2786,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.18" @@ -2344,7 +2842,7 @@ checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -2385,7 +2883,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -2444,7 +2942,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -2504,6 +3002,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 = "slab" version = "0.4.9" @@ -2544,6 +3048,12 @@ 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" @@ -2569,7 +3079,18 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn", + "syn 2.0.87", +] + +[[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]] @@ -2603,9 +3124,28 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] +[[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.14.0" @@ -2654,7 +3194,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -2665,7 +3205,7 @@ checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -2724,7 +3264,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -2760,7 +3300,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_edit 0.22.22", ] [[package]] @@ -2772,6 +3312,17 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.6.0", + "toml_datetime", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.22" @@ -2782,7 +3333,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -2861,6 +3412,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" @@ -2881,7 +3441,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -2983,6 +3543,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.17.0" @@ -2995,12 +3571,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.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unsafe-libyaml" version = "0.2.11" @@ -3024,6 +3657,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" +[[package]] +name = "utf16_lit" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14706d2a800ee8ff38c1d3edb873cd616971ea59eb7c0d046bb44ef59b06a1ae" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -3058,6 +3697,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" @@ -3130,7 +3775,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -3152,7 +3797,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3254,7 +3899,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -3265,7 +3910,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -3435,6 +4080,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.6.20" @@ -3444,6 +4098,15 @@ 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 = "write16" version = "1.0.0" @@ -3456,6 +4119,12 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "xi-unicode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" + [[package]] name = "yoke" version = "0.7.4" @@ -3476,7 +4145,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", "synstructure", ] @@ -3498,7 +4167,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] [[package]] @@ -3518,7 +4187,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", "synstructure", ] @@ -3541,5 +4210,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.87", ] diff --git a/Cargo.toml b/Cargo.toml index b73e6aff4..c344498dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "src/hyperlight_guest_capi", "src/hyperlight_testing", "src/hyperlight_host/fuzz", + "src/trace_dump", ] # Because hyperlight-guest has custom linker flags, # we exclude it from the default-members list 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..c187efac1 --- /dev/null +++ b/src/trace_dump/main.rs @@ -0,0 +1,831 @@ +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::{kurbo, Color, RenderContext, Text, TextLayout, TextLayoutBuilder}; + +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(()) +} + +// 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 read_file( + state: &mut State, + mut handle_state: S, + handle_ident: I, + handle_unwind: U, + handle_alloc: A, + handle_free: F, +) -> 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<()>, +{ + 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 { + 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, + )?; + 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, + ); +} + +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(()) => (), + 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, + ); + 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(); +}