diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f011f6c1..210cfdf1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,8 @@ jobs: - run: cargo fetch - name: cargo clippy run: cargo clippy --all-targets -- -D warnings + - name: cargo clippy wasm32-unknown-unknown WITHOUT web feature + run: cargo clippy -p puffin --target wasm32-unknown-unknown --no-default-features -- -D warnings - name: cargo clippy wasm32 run: cargo clippy -p puffin_viewer --target wasm32-unknown-unknown --all-features --lib @@ -62,31 +64,30 @@ jobs: - name: cargo test build run: cargo build --tests --release --all-features - name: cargo test - run: cargo test -p puffin -p puffin_egui -p puffin_http -p puffin-imgui --release --all-features + run: cargo test -p puffin -p puffin_egui -p puffin_http --release --all-features - name: cargo test --doc run: cargo test --workspace --doc - name: cargo doc - run: cargo doc -p puffin -p puffin_egui -p puffin_http -p puffin-imgui -p --lib --no-deps --all-features + run: cargo doc -p puffin -p puffin_egui -p puffin_http -p --lib --no-deps --all-features cargo-vet: name: Vet Dependencies runs-on: ubuntu-20.04-16core env: - # there is no published release for v0.7 yet, build from git revision - CARGO_VET_REVISION: 088586c + CARGO_VET_VERSION: 0.9.1 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - uses: actions/cache@v3 with: path: ${{ runner.tool_cache }}/cargo-vet - key: cargo-vet-bin-${{ env.CARGO_VET_REVISION }} + key: cargo-vet-bin-${{ env.CARGO_VET_VERSION }} - name: Add the tool cache directory to the search path run: echo "${{ runner.tool_cache }}/cargo-vet/bin" >> $GITHUB_PATH - name: Ensure that the tool cache is populated with the cargo-vet binary # build from source, as are not published binaries yet :( # tracked in https://github.com/mozilla/cargo-vet/issues/484 - run: cargo install --root ${{ runner.tool_cache }}/cargo-vet --git https://github.com/mozilla/cargo-vet.git --rev ${{ env.CARGO_VET_REVISION }} cargo-vet + run: cargo install --root ${{ runner.tool_cache }}/cargo-vet --version ${{ env.CARGO_VET_VERSION }} cargo-vet - name: Invoke cargo-vet run: | cargo vet --locked diff --git a/Cargo.lock b/Cargo.lock index c9202a95..e02cfa0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ab_glyph" -version = "0.2.21" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5110f1c78cf582855d895ecd0746b653db010cec6d9f5575293f27934d980a39" +checksum = "80179d7dd5d7e8c285d67c4a1e652972a92de7475beddfb92028c76463b13225" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -20,23 +20,14 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c98a5d094590335462354da402d754fe2cb78f0e6ce5024611c28ed539c1de" +checksum = "6cb10ed32c63247e4e39a8f42e8e30fb9442fbf7878c8e4a9849e7e381619bea" dependencies = [ "enumn", "serde", ] -[[package]] -name = "addr2line" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" -dependencies = [ - "gimli", -] - [[package]] name = "adler" version = "1.0.2" @@ -45,34 +36,38 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.3" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", + "getrandom", "once_cell", "serde", "version_check", + "zerocopy", ] [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "android-activity" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c77a0045eda8b888c76ea473c2b0515ba6f471d318f8927c5c72240937035a6" +checksum = "39b801912a977c3fd52d80511fe1c0c8480c6f957f21ae2ce1b92ffe970cf4b9" dependencies = [ "android-properties", - "bitflags", + "bitflags 2.4.1", "cc", + "cesu8", + "jni", "jni-sys", "libc", "log", @@ -80,6 +75,7 @@ dependencies = [ "ndk-context", "ndk-sys", "num_enum", + "thiserror", ] [[package]] @@ -96,28 +92,27 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" -version = "1.0.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "arboard" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6041616acea41d67c4a984709ddab1587fd0b10efe5cc563fee954d2f011854" +checksum = "aafb29b107435aa276664c1db8954ac27a6e105cdad3c88287a199eb0e313c08" dependencies = [ "clipboard-win", "log", "objc", "objc-foundation", "objc_id", - "once_cell", "parking_lot", "thiserror", "winapi", @@ -126,9 +121,9 @@ dependencies = [ [[package]] name = "argh" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab257697eb9496bf75526f0217b5ed64636a9cfafa78b8365c71bd283fcef93e" +checksum = "7af5ba06967ff7214ce4c7419c7d185be7ecd6cc4965a8f6e1d8ce0398aad219" dependencies = [ "argh_derive", "argh_shared", @@ -136,39 +131,30 @@ dependencies = [ [[package]] name = "argh_derive" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b382dbd3288e053331f03399e1db106c9fb0d8562ad62cb04859ae926f324fa6" +checksum = "56df0aeedf6b7a2fc67d06db35b09684c3e8da0c95f8f27685cb17e08413d87a" dependencies = [ "argh_shared", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "argh_shared" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f" - -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.5.2" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +checksum = "5693f39141bda5760ecc4111ab08da40565d1771038c4a0250f03457ec707531" +dependencies = [ + "serde", +] [[package]] -name = "arrayvec" -version = "0.7.2" +name = "as-raw-xcb-connection" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" [[package]] name = "atk-sys" @@ -183,21 +169,10 @@ dependencies = [ ] [[package]] -name = "atomic_refcell" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d6dc922a2792b006573f60b2648076355daeae5ce9cb59507e5908c9625d31" - -[[package]] -name = "atty" -version = "0.2.14" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" @@ -205,26 +180,11 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" -[[package]] -name = "backtrace" -version = "0.3.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide 0.6.2", - "object", - "rustc-demangle", -] - [[package]] name = "base64" -version = "0.13.1" +version = "0.21.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9" [[package]] name = "bincode" @@ -241,6 +201,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] + [[package]] name = "block" version = "0.1.6" @@ -249,60 +218,60 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "block-sys" -version = "0.1.0-beta.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" dependencies = [ "objc-sys", ] [[package]] name = "block2" -version = "0.2.0-alpha.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68" dependencies = [ "block-sys", - "objc2-encode", + "objc2", ] [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytemuck" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdde5c9cd29ebd706ce1b35600920a33550e402fc998a2e53ad3b42c3c47a192" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn", ] [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cairo-sys-rs" @@ -316,16 +285,28 @@ dependencies = [ [[package]] name = "calloop" -version = "0.10.6" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" +checksum = "7b50b5a44d59a98c55a9eeb518f39bf7499ba19fd98ee7d22618687f3f10adbf" dependencies = [ - "bitflags", + "bitflags 2.4.1", "log", - "nix 0.25.1", - "slotmap", + "polling", + "rustix", + "slab", "thiserror", - "vec_map", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02" +dependencies = [ + "calloop", + "rustix", + "wayland-backend", + "wayland-client", ] [[package]] @@ -336,11 +317,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "jobserver", + "libc", ] [[package]] @@ -351,9 +333,9 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-expr" -version = "0.15.2" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e70d3ad08698a0568b0562f22710fe6bfc1f4a61a367c77d0398c562eadd453a" +checksum = "6100bc57b6209840798d95cb2775684849d332f7bd788db2a8c8caf7ef82a41a" dependencies = [ "smallvec", "target-lexicon", @@ -380,12 +362,6 @@ dependencies = [ "libc", ] -[[package]] -name = "chlorine" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75476fe966a8af7c0ceae2a3e514afa87d4451741fcdfab8bfaa07ad301842ec" - [[package]] name = "ciborium" version = "0.2.1" @@ -415,29 +391,28 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.2" +version = "4.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "401a4694d2bf92537b6867d94de48c4842089645fdcdf6c71865b175d836e9c2" +checksum = "33e92c5c1a78c62968ec57dbc2440366a2d6e5a23faf829970ff1585dc6b18e2" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.3.1" +version = "4.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980" +checksum = "f4323769dc8a61e2c39ad7dc26f6f2800524691a44d74fe3d1071a5c24db6370" dependencies = [ "anstyle", - "bitflags", "clap_lex", ] [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "clipboard-win" @@ -450,42 +425,32 @@ dependencies = [ "winapi", ] -[[package]] -name = "cmake" -version = "0.1.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" -dependencies = [ - "cc", -] - [[package]] name = "cocoa" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "cocoa-foundation", "core-foundation", "core-graphics", - "foreign-types 0.3.2", + "foreign-types", "libc", "objc", ] [[package]] name = "cocoa-foundation" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" +checksum = "8c6234cbb2e4c785b456c0644748b1ac416dd045799740356f8363dfe00c93f7" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "core-foundation", "core-graphics-types", - "foreign-types 0.3.2", "libc", "objc", ] @@ -498,13 +463,12 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colored" -version = "2.0.0" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" dependencies = [ - "atty", "lazy_static", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -517,11 +481,20 @@ dependencies = [ "memchr", ] +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -529,44 +502,31 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core-graphics" -version = "0.22.3" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +checksum = "970a29baf4110c26fedbc7f82107d42c23f7e88e404c4577ed73fe99ff85a212" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-graphics-types", - "foreign-types 0.3.2", + "foreign-types", "libc", ] [[package]] name = "core-graphics-types" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" -dependencies = [ - "bitflags", - "core-foundation", - "foreign-types 0.3.2", - "libc", -] - -[[package]] -name = "core-text" -version = "19.2.0" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ + "bitflags 1.3.2", "core-foundation", - "core-graphics", - "foreign-types 0.3.2", "libc", ] @@ -617,110 +577,43 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.8" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", - "memoffset 0.8.0", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossfont" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21fd3add36ea31aba1520aa5288714dd63be506106753226d0eb387a93bc9c45" -dependencies = [ - "cocoa", - "core-foundation", - "core-foundation-sys", - "core-graphics", - "core-text", - "dwrote", - "foreign-types 0.5.0", - "freetype-rs", - "libc", - "log", - "objc", - "once_cell", - "pkg-config", - "servo-fontconfig", - "winapi", -] - -[[package]] -name = "cty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.4" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.109", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] -name = "darling_macro" -version = "0.13.4" +name = "cursor-icon" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.109", -] +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" [[package]] name = "directories-next" @@ -755,34 +648,29 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" dependencies = [ - "libloading 0.8.0", + "libloading 0.8.1", ] [[package]] -name = "downcast-rs" -version = "1.2.0" +name = "document-features" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95" +dependencies = [ + "litrs", +] [[package]] -name = "dwrote" -version = "0.11.0" +name = "downcast-rs" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" -dependencies = [ - "lazy_static", - "libc", - "serde", - "serde_derive", - "winapi", - "wio", -] +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "ecolor" -version = "0.22.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e479a7fa3f23d4e794f8b2f8b3568dd4e47886ad1b12c9c095e141cb591eb63" +checksum = "fb152797942f72b84496eb2ebeff0060240e0bf55096c4525ffa22dd54722d86" dependencies = [ "bytemuck", "serde", @@ -790,40 +678,45 @@ dependencies = [ [[package]] name = "eframe" -version = "0.22.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4596583a2c680c55b6feaa748f74890c4f9cb9c7cb69d6117110444cb65b2f" +checksum = "3bcc8e06df6f0a6cf09a3247ff7e85fdfffc28dda4fe5561e05314bf7618a918" dependencies = [ "bytemuck", "cocoa", "directories-next", + "document-features", "egui", "egui-winit", "egui_glow", "glow", - "glutin 0.30.8", + "glutin", "glutin-winit", "image", "js-sys", "log", "objc", + "parking_lot", "percent-encoding", "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "ron", "serde", + "static_assertions", "thiserror", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "web-time", "winapi", - "winit 0.28.6", + "winit", ] [[package]] name = "egui" -version = "0.22.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3aef8ec3ae1b772f340170c65bf27d5b8c28f543a0116c844d2ac08d01123e7" +checksum = "6d1b8cc14b0b260aa6bd124ef12c8a94f57ffe8e40aa970f3db710c21bb945f3" dependencies = [ "accesskit", "ahash", @@ -836,47 +729,47 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.22.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a49155fd4a0a4fb21224407a91de0030847972ef90fc64edb63621caea61cb2" +checksum = "3733435d6788c760bb98ce4cb1b8b7a2d953a3a7b421656ba8b3e014019be3d0" dependencies = [ "arboard", "egui", - "instant", "log", - "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "serde", "smithay-clipboard", + "web-time", "webbrowser", - "winit 0.28.6", + "winit", ] [[package]] name = "egui_glow" -version = "0.22.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8c2752cdf1b0ef5fcda59a898cacabad974d4f5880e92a420b2c917022da64" +checksum = "f933e9e64c4d074c78ce71785a5778f648453c2b2a3efd28eea189dac3f19c28" dependencies = [ "bytemuck", "egui", "glow", "log", - "memoffset 0.6.5", + "memoffset 0.9.1", "wasm-bindgen", "web-sys", ] [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "emath" -version = "0.22.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3857d743a6e0741cdd60b622a74c7a36ea75f5f8f11b793b41d905d2c9721a4b" +checksum = "555a7cbfcc52c81eb5f8f898190c840fa1c435f67f30b7ef77ce7cf6b7dcd987" dependencies = [ "bytemuck", "serde", @@ -884,20 +777,20 @@ dependencies = [ [[package]] name = "enumn" -version = "0.1.8" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48016319042fb7c87b78d2993084a831793a897a5cd1a2a67cab9d1eeb4b7d76" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn", ] [[package]] name = "env_logger" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" dependencies = [ "humantime", "is-terminal", @@ -908,13 +801,12 @@ dependencies = [ [[package]] name = "epaint" -version = "0.22.0" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09333964d4d57f40a85338ba3ca5ed4716070ab184dcfed966b35491c5c64f3b" +checksum = "bd63c37156e949bda80f7e39cc11508bc34840aecf52180567e67cdb2bf1a5fe" dependencies = [ "ab_glyph", "ahash", - "atomic_refcell", "bytemuck", "ecolor", "emath", @@ -925,24 +817,19 @@ dependencies = [ ] [[package]] -name = "errno" -version = "0.3.1" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "errno-dragonfly" -version = "0.1.2" +name = "errno" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "cc", "libc", + "windows-sys 0.52.0", ] [[package]] @@ -955,48 +842,23 @@ dependencies = [ "str-buf", ] -[[package]] -name = "expat-sys" -version = "2.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" -dependencies = [ - "cmake", - "pkg-config", -] - [[package]] name = "fdeflate" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd" dependencies = [ "simd-adler32", ] [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", - "miniz_oxide 0.7.1", -] - -[[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 0.1.1", + "miniz_oxide", ] [[package]] @@ -1006,7 +868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared 0.3.1", + "foreign-types-shared", ] [[package]] @@ -1017,15 +879,9 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn", ] -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -1034,35 +890,13 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] -[[package]] -name = "freetype-rs" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74eadec9d0a5c28c54bb9882e54787275152a4e36ce206b45d7451384e5bf5fb" -dependencies = [ - "bitflags", - "freetype-sys", - "libc", -] - -[[package]] -name = "freetype-sys" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" -dependencies = [ - "cmake", - "libc", - "pkg-config", -] - [[package]] name = "gdk-pixbuf-sys" version = "0.15.10" @@ -1095,9 +929,9 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" +checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177" dependencies = [ "libc", "winapi", @@ -1105,21 +939,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", "wasi", ] -[[package]] -name = "gimli" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" - [[package]] name = "gio-sys" version = "0.15.10" @@ -1154,27 +982,11 @@ dependencies = [ "system-deps", ] -[[package]] -name = "glium" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2766728ecb86014b91d3d687614b32d65aacbbdc887f424a7b03cba3ab593bf" -dependencies = [ - "backtrace", - "fnv", - "gl_generator", - "glutin 0.29.1", - "lazy_static", - "memoffset 0.6.5", - "smallvec", - "takeable-option", -] - [[package]] name = "glow" -version = "0.12.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "807edf58b70c0b5b2181dd39fe1839dbdb3ba02645630dc5f753e23da307f762" +checksum = "886c2a30b160c4c6fec8f987430c26b526b7988ca71f664e6a699ddf6f9601e4" dependencies = [ "js-sys", "slotmap", @@ -1184,110 +996,55 @@ dependencies = [ [[package]] name = "glutin" -version = "0.29.1" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444c9ad294fdcaf20ccf6726b78f380b5450275540c9b68ab62f49726ad1c713" +checksum = "005459a22af86adc706522d78d360101118e2638ec21df3852fcc626e0dbb212" dependencies = [ + "bitflags 2.4.1", + "cfg_aliases", "cgl", - "cocoa", "core-foundation", - "glutin_egl_sys 0.1.6", - "glutin_gles2_sys", - "glutin_glx_sys 0.1.8", - "glutin_wgl_sys 0.1.5", - "libloading 0.7.4", - "log", - "objc", + "dispatch", + "glutin_egl_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "icrate", + "libloading 0.8.1", + "objc2", "once_cell", - "osmesa-sys", - "parking_lot", "raw-window-handle 0.5.2", - "wayland-client", - "wayland-egl", - "winapi", - "winit 0.27.5", + "wayland-sys", + "windows-sys 0.48.0", + "x11-dl", ] [[package]] -name = "glutin" -version = "0.30.8" +name = "glutin-winit" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f9b771a65f0a1e3ddb6aa16f867d87dc73c922411c255e6c4ab7f6d45c7327" +checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735" dependencies = [ - "bitflags", "cfg_aliases", - "cgl", - "core-foundation", - "dispatch", - "glutin_egl_sys 0.5.0", - "glutin_glx_sys 0.4.0", - "glutin_wgl_sys 0.4.0", - "libloading 0.7.4", - "objc2", - "once_cell", + "glutin", "raw-window-handle 0.5.2", - "wayland-sys 0.30.1", - "windows-sys 0.45.0", - "x11-dl", -] - -[[package]] -name = "glutin-winit" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629a873fc04062830bfe8f97c03773bcd7b371e23bcc465d0a61448cd1588fa4" -dependencies = [ - "cfg_aliases", - "glutin 0.30.8", - "raw-window-handle 0.5.2", - "winit 0.28.6", + "winit", ] [[package]] name = "glutin_egl_sys" -version = "0.1.6" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68900f84b471f31ea1d1355567eb865a2cf446294f06cef8d653ed7bcf5f013d" +checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd" dependencies = [ "gl_generator", - "winapi", -] - -[[package]] -name = "glutin_egl_sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b3bcbddc51573b977fc6dca5d93867e4f29682cdbaf5d13e48f4fa4346d4d87" -dependencies = [ - "gl_generator", - "windows-sys 0.45.0", -] - -[[package]] -name = "glutin_gles2_sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103" -dependencies = [ - "gl_generator", - "objc", -] - -[[package]] -name = "glutin_glx_sys" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93d0575865098580c5b3a423188cd959419912ea60b1e48e8b3b526f6d02468" -dependencies = [ - "gl_generator", - "x11-dl", + "windows-sys 0.48.0", ] [[package]] name = "glutin_glx_sys" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b53cb5fe568964aa066a3ba91eac5ecbac869fb0842cd0dc9e412434f1a1494" +checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f" dependencies = [ "gl_generator", "x11-dl", @@ -1295,18 +1052,9 @@ dependencies = [ [[package]] name = "glutin_wgl_sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" -dependencies = [ - "gl_generator", -] - -[[package]] -name = "glutin_wgl_sys" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef89398e90033fc6bc65e9bd42fd29bbbfd483bda5b56dc5562f455550618165" +checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" dependencies = [ "gl_generator", ] @@ -1348,9 +1096,9 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -1360,35 +1108,17 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "home" -version = "0.5.5" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1398,16 +1128,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] -name = "ident_case" -version = "1.0.1" +name = "icrate" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319" +dependencies = [ + "block2", + "dispatch", + "objc2", +] [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1415,9 +1150,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.6" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" dependencies = [ "bytemuck", "byteorder", @@ -1427,95 +1162,26 @@ dependencies = [ "png", ] -[[package]] -name = "imgui" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cea19ca90247d9c994663a9f68edd9792b26ad0af390ceb8b43e881c90121bc" -dependencies = [ - "bitflags", - "cfg-if", - "imgui-sys", - "mint", - "parking_lot", -] - -[[package]] -name = "imgui-glium-renderer" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e25883dd9f02125af7167a07effe4aab4072e9f3857a63fb3c3f5572f7af5ac" -dependencies = [ - "glium", - "imgui", -] - -[[package]] -name = "imgui-sys" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612ceabf43b8c293b165f987539018b3fe6e30dd8af0c890dee1461c75250adb" -dependencies = [ - "cc", - "cfg-if", - "chlorine", - "mint", -] - -[[package]] -name = "imgui-winit-support" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fde5f2092b5852a838ef13eb5dfbd58c18aa8754bbea45cffabd53efb584f3a" -dependencies = [ - "imgui", - "winit 0.27.5", -] - [[package]] name = "indexmap" -version = "1.9.3" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ - "autocfg", + "equivalent", "hashbrown", "serde", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "is-terminal" -version = "0.4.7" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", + "hermit-abi", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1529,9 +1195,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "jni" @@ -1557,18 +1223,18 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.26" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.63" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -1587,9 +1253,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.146" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libloading" @@ -1603,9 +1269,9 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", "windows-sys 0.48.0", @@ -1613,25 +1279,42 @@ dependencies = [ [[package]] name = "libmimalloc-sys" -version = "0.1.33" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4ac0e912c8ef1b735e92369695618dc5b1819f5a7bf3f167301a3ba1cea515e" +checksum = "3979b5c37ece694f1f5e51e7ecc871fdb0f517ed04ee45f88d15d6d553cb9664" dependencies = [ "cc", "libc", ] +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + +[[package]] +name = "litrs" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -1639,15 +1322,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lz4_flex" -version = "0.10.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8c72594ac26bfd34f2d99dfced2edfaddfe8a476e3ff2ca0eb293d925c4f83" +checksum = "912b45c753ff5f7f5208307e8ace7d2a2e30d024e26d3509f3dce546c044ce15" [[package]] name = "malloc_buf" @@ -1660,61 +1343,46 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "memmap2" -version = "0.5.10" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +checksum = "45fd3a57831bf88bc63f8cebc0cf956116276e97fef3966103e96416209f7c92" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.5" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "mimalloc" -version = "0.1.37" +version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2894987a3459f3ffb755608bd82188f8ed00d0ae077f1edea29c068d639d98" +checksum = "fa01922b5ea280a911e323e4d2fd24b7fe5cc4042e0d2cda3c40775cdc4bdc9c" dependencies = [ "libmimalloc-sys", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.7.1" @@ -1725,24 +1393,6 @@ dependencies = [ "simd-adler32", ] -[[package]] -name = "mint" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" - -[[package]] -name = "mio" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "natord" version = "1.0.9" @@ -1751,15 +1401,17 @@ checksum = "308d96db8debc727c3fd9744aac51751243420e46edf401010908da7f8d5e57c" [[package]] name = "ndk" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags", + "bitflags 2.4.1", "jni-sys", + "log", "ndk-sys", "num_enum", "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "thiserror", ] @@ -1769,67 +1421,25 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" -[[package]] -name = "ndk-glue" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" -dependencies = [ - "libc", - "log", - "ndk", - "ndk-context", - "ndk-macro", - "ndk-sys", - "once_cell", - "parking_lot", -] - -[[package]] -name = "ndk-macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" -dependencies = [ - "darling", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "ndk-sys" -version = "0.4.1+23.1.7779620" +version = "0.5.0+25.2.9519653" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" dependencies = [ "jni-sys", ] [[package]] name = "nix" -version = "0.24.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ - "bitflags", - "cfg-if", - "libc", - "memoffset 0.6.5", -] - -[[package]] -name = "nix" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" -dependencies = [ - "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", - "memoffset 0.6.5", + "memoffset 0.7.1", ] [[package]] @@ -1838,16 +1448,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - [[package]] name = "num-integer" version = "0.1.45" @@ -1871,42 +1471,32 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - [[package]] name = "num_enum" -version = "0.5.11" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.11" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -1940,29 +1530,25 @@ dependencies = [ [[package]] name = "objc-sys" -version = "0.2.0-beta.2" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" +checksum = "c7c71324e4180d0899963fc83d9d241ac39e699609fc1025a850aadac8257459" [[package]] name = "objc2" -version = "0.3.0-beta.3.patch-leaks.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d" dependencies = [ - "block2", "objc-sys", "objc2-encode", ] [[package]] name = "objc2-encode" -version = "2.0.0-pre.2" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" -dependencies = [ - "objc-sys", -] +checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666" [[package]] name = "objc_id" @@ -1973,20 +1559,11 @@ dependencies = [ "objc", ] -[[package]] -name = "object" -version = "0.30.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" @@ -2003,20 +1580,11 @@ dependencies = [ "redox_syscall 0.3.5", ] -[[package]] -name = "osmesa-sys" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" -dependencies = [ - "shared_library", -] - [[package]] name = "owned_ttf_parser" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4" +checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7" dependencies = [ "ttf-parser", ] @@ -2045,34 +1613,46 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "smallvec", - "windows-targets 0.48.0", + "windows-targets 0.48.5", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "plotters" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", @@ -2083,61 +1663,73 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] [[package]] name = "png" -version = "0.17.8" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.7.1", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +dependencies = [ + "cfg-if", + "concurrent-queue", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", ] [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "6b2685dd208a3771337d8d386a89840f0f43cd68be8dae90a5f8c2384effc9cd" dependencies = [ - "once_cell", "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.60" +version = "1.0.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" dependencies = [ "unicode-ident", ] [[package]] name = "puffin" -version = "0.16.0" +version = "0.19.0" dependencies = [ "anyhow", "bincode", "byteorder", "cfg-if", "criterion", - "instant", "itertools", "js-sys", "lz4_flex", @@ -2145,53 +1737,44 @@ dependencies = [ "parking_lot", "ruzstd", "serde", + "web-time", "zstd", ] -[[package]] -name = "puffin-imgui" -version = "0.22.0" -dependencies = [ - "glium", - "imgui", - "imgui-glium-renderer", - "imgui-winit-support", - "mint", - "natord", - "puffin", - "serde", -] - [[package]] name = "puffin_egui" -version = "0.22.0" +version = "0.27.1" dependencies = [ "eframe", "egui", "indexmap", - "instant", "natord", "once_cell", + "parking_lot", "puffin", "serde", "time", "vec1", + "web-time", ] [[package]] name = "puffin_http" -version = "0.13.0" +version = "0.16.0" dependencies = [ "anyhow", "crossbeam-channel", "log", + "once_cell", + "parking_lot", + "paste", "puffin", "simple_logger", ] [[package]] name = "puffin_viewer" -version = "0.16.0" +version = "0.21.1" dependencies = [ "argh", "eframe", @@ -2206,21 +1789,21 @@ dependencies = [ ] [[package]] -name = "quote" -version = "1.0.28" +name = "quick-xml" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" dependencies = [ - "proc-macro2", + "memchr", ] [[package]] -name = "raw-window-handle" -version = "0.4.3" +name = "quote" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ - "cty", + "proc-macro2", ] [[package]] @@ -2229,11 +1812,17 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + [[package]] name = "rayon" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" dependencies = [ "either", "rayon-core", @@ -2241,42 +1830,40 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "redox_syscall" -version = "0.3.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "redox_syscall 0.2.16", + "libredox", "thiserror", ] @@ -2293,9 +1880,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.2" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rfd" @@ -2323,33 +1910,27 @@ dependencies = [ [[package]] name = "ron" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300a51053b1cb55c80b7a9fde4120726ddf25ca241a1cbb926626f62fb136bff" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64", - "bitflags", + "bitflags 2.4.1", "serde", + "serde_derive", ] -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - [[package]] name = "rustix" -version = "0.37.19" +version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ - "bitflags", + "bitflags 2.4.1", "errno", - "io-lifetimes", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2365,18 +1946,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" - -[[package]] -name = "safe_arch" -version = "0.5.2" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" -dependencies = [ - "bytemuck", -] +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "same-file" @@ -2395,60 +1967,35 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "sctk-adwaita" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61270629cc6b4d77ec1907db1033d5c2e1a404c412743621981a871dc9c12339" -dependencies = [ - "crossfont", - "log", - "smithay-client-toolkit", - "tiny-skia 0.7.0", -] - -[[package]] -name = "sctk-adwaita" -version = "0.5.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" -dependencies = [ - "ab_glyph", - "log", - "memmap2", - "smithay-client-toolkit", - "tiny-skia 0.8.4", -] +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.164" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.164" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", @@ -2457,105 +2004,98 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" dependencies = [ "serde", ] -[[package]] -name = "servo-fontconfig" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" -dependencies = [ - "libc", - "servo-fontconfig-sys", -] - -[[package]] -name = "servo-fontconfig-sys" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" -dependencies = [ - "expat-sys", - "freetype-sys", - "pkg-config", -] - -[[package]] -name = "shared_library" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" -dependencies = [ - "lazy_static", - "libc", -] - [[package]] name = "simd-adler32" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "simple_logger" -version = "2.3.0" +version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48047e77b528151aaf841a10a9025f9459da80ba820e425ff7eb005708a76dc7" +checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" dependencies = [ - "atty", "colored", "log", "time", - "winapi", + "windows-sys 0.48.0", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", ] [[package]] name = "slotmap" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" dependencies = [ "version_check", ] [[package]] name = "smallvec" -version = "1.10.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "smithay-client-toolkit" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" +checksum = "60e3d9941fa3bacf7c2bf4b065304faa14164151254cd16ce1b1bc8fc381600f" dependencies = [ - "bitflags", + "bitflags 2.4.1", "calloop", - "dlib", - "lazy_static", + "calloop-wayland-source", + "cursor-icon", + "libc", "log", "memmap2", - "nix 0.24.3", - "pkg-config", + "rustix", + "thiserror", + "wayland-backend", "wayland-client", + "wayland-csd-frame", "wayland-cursor", "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", ] [[package]] name = "smithay-clipboard" -version = "0.6.6" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8" +checksum = "0bb62b280ce5a5cba847669933a0948d00904cf83845c944eae96a4738cea1a6" dependencies = [ + "libc", "smithay-client-toolkit", - "wayland-client", + "wayland-backend", +] + +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", ] [[package]] @@ -2570,34 +2110,11 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" -[[package]] -name = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[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.18" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -2606,9 +2123,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.1.0" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5fa6fb9ee296c0dc2df41a656ca7948546d061958115ddb0bcaae43ad0d17d2" +checksum = "2a2d580ff6a20c55dfb86be5f9c238f67835d0e81cbdea8bf5680e0897320331" dependencies = [ "cfg-expr", "heck", @@ -2617,65 +2134,59 @@ dependencies = [ "version-compare", ] -[[package]] -name = "takeable-option" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ae8932fcfea38b7d3883ae2ab357b0d57a02caaa18ebb4f5ece08beaec4aa0" - [[package]] name = "target-lexicon" -version = "0.12.7" +version = "0.12.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd1ba337640d60c3e96bc6f0638a939b9c9a7f2c316a1598c279828b3d1dc8c5" +checksum = "69758bda2e78f098e4ccb393021a0963bb3442eac05f135c30f61b7370bbafae" [[package]] name = "termcolor" -version = "1.2.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-core" -version = "1.0.38" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d97345f6437bb2004cd58819d8a9ef8e36cdd7661c2abc4bbde0a7c40d9f497" +checksum = "c001ee18b7e5e3f62cbf58c7fe220119e68d902bb7443179c0c8aef30090e999" dependencies = [ "thiserror-core-impl", ] [[package]] name = "thiserror-core-impl" -version = "1.0.38" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac" +checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn", ] [[package]] @@ -2707,56 +2218,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "tiny-skia" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642680569bb895b16e4b9d181c60be1ed136fa0c9c7f11d004daf053ba89bf82" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "bytemuck", - "cfg-if", - "png", - "safe_arch", - "tiny-skia-path 0.7.0", -] - -[[package]] -name = "tiny-skia" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" -dependencies = [ - "arrayref", - "arrayvec 0.7.2", - "bytemuck", - "cfg-if", - "png", - "tiny-skia-path 0.8.4", -] - -[[package]] -name = "tiny-skia-path" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c114d32f0c2ee43d585367cb013dfaba967ab9f62b90d9af0d696e955e70fa6c" -dependencies = [ - "arrayref", - "bytemuck", -] - -[[package]] -name = "tiny-skia-path" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - [[package]] name = "tinytemplate" version = "1.2.1" @@ -2784,9 +2245,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.7.4" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" dependencies = [ "serde", "serde_spanned", @@ -2796,18 +2257,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.2" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.10" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ "indexmap", "serde", @@ -2816,11 +2277,27 @@ dependencies = [ "winnow", ] +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" + [[package]] name = "ttf-parser" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44dcf002ae3b32cd25400d6df128c5babec3927cd1eb7ce813cfff20eb6c3746" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" [[package]] name = "twox-hash" @@ -2834,15 +2311,15 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" @@ -2853,11 +2330,17 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + [[package]] name = "url" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -2870,12 +2353,6 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bda7c41ca331fe9a1c278a9e7ee055f4be7f5eb1c2b72f079b4ff8b5fce9d5c" -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - [[package]] name = "version-compare" version = "0.1.1" @@ -2890,9 +2367,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -2906,9 +2383,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.86" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2916,24 +2393,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.86" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.18", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.36" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1985d03709c53167ce907ff394f5316aa22cb4e12761295c5dc57dacb6297e" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -2943,9 +2420,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.86" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2953,123 +2430,147 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.86" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.18", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.86" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] -name = "wayland-client" -version = "0.29.5" +name = "wayland-backend" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +checksum = "19152ddd73f45f024ed4534d9ca2594e0ef252c1847695255dae47f34df9fbe4" dependencies = [ - "bitflags", + "cc", "downcast-rs", - "libc", - "nix 0.24.3", + "nix", "scoped-tls", - "wayland-commons", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca7d52347346f5473bf2f56705f360e8440873052e575e55890c4fa57843ed3" +dependencies = [ + "bitflags 2.4.1", + "nix", + "wayland-backend", "wayland-scanner", - "wayland-sys 0.29.5", ] [[package]] -name = "wayland-commons" -version = "0.29.5" +name = "wayland-csd-frame" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "nix 0.24.3", - "once_cell", - "smallvec", - "wayland-sys 0.29.5", + "bitflags 2.4.1", + "cursor-icon", + "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.29.5" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +checksum = "a44aa20ae986659d6c77d64d808a046996a932aa763913864dc40c359ef7ad5b" dependencies = [ - "nix 0.24.3", + "nix", "wayland-client", "xcursor", ] [[package]] -name = "wayland-egl" -version = "0.29.5" +name = "wayland-protocols" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402de949f81a012926d821a2d659f930694257e76dd92b6e0042ceb27be4107d" +checksum = "e253d7107ba913923dc253967f35e8561a3c65f914543e46843c88ddd729e21c" dependencies = [ + "bitflags 2.4.1", + "wayland-backend", "wayland-client", - "wayland-sys 0.29.5", + "wayland-scanner", ] [[package]] -name = "wayland-protocols" -version = "0.29.5" +name = "wayland-protocols-plasma" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" +dependencies = [ + "bitflags 2.4.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags", + "bitflags 2.4.1", + "wayland-backend", "wayland-client", - "wayland-commons", + "wayland-protocols", "wayland-scanner", ] [[package]] name = "wayland-scanner" -version = "0.29.5" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +checksum = "fb8e28403665c9f9513202b7e1ed71ec56fde5c107816843fb14057910b2c09c" dependencies = [ "proc-macro2", + "quick-xml", "quote", - "xml-rs", ] [[package]] name = "wayland-sys" -version = "0.29.5" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" dependencies = [ "dlib", - "lazy_static", + "log", + "once_cell", "pkg-config", ] [[package]] -name = "wayland-sys" -version = "0.30.1" +name = "web-sys" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ - "dlib", - "lazy_static", - "log", - "pkg-config", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "web-sys" -version = "0.3.63" +name = "web-time" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" dependencies = [ "js-sys", "wasm-bindgen", @@ -3077,9 +2578,9 @@ dependencies = [ [[package]] name = "webbrowser" -version = "0.8.10" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd222aa310eb7532e3fd427a5d7db7e44bc0b0cf1c1e21139c345325511a85b6" +checksum = "82b2391658b02c27719fc5a0a73d6e696285138e8b12fba9d4baa70451023c71" dependencies = [ "core-foundation", "home", @@ -3110,9 +2611,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -3147,33 +2648,29 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", + "windows-targets 0.42.2", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.42.2", + "windows-targets 0.48.5", ] [[package]] name = "windows-sys" -version = "0.48.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.48.0", + "windows-targets 0.52.0", ] [[package]] @@ -3193,17 +2690,32 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -3214,15 +2726,15 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" +name = "windows_aarch64_gnullvm" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" @@ -3238,15 +2750,15 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] -name = "windows_i686_gnu" -version = "0.36.1" +name = "windows_aarch64_msvc" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" @@ -3262,15 +2774,15 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] -name = "windows_i686_msvc" -version = "0.36.1" +name = "windows_i686_gnu" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" @@ -3286,15 +2798,15 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" +name = "windows_i686_msvc" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" @@ -3310,9 +2822,15 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" @@ -3322,15 +2840,15 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" +name = "windows_x86_64_gnullvm" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" @@ -3346,96 +2864,73 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] -name = "winit" -version = "0.27.5" +name = "windows_x86_64_msvc" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb796d6fbd86b2fd896c9471e6f04d39d750076ebe5680a3958f00f5ab97657c" -dependencies = [ - "bitflags", - "cocoa", - "core-foundation", - "core-graphics", - "dispatch", - "instant", - "libc", - "log", - "mio", - "ndk", - "ndk-glue", - "objc", - "once_cell", - "parking_lot", - "percent-encoding", - "raw-window-handle 0.4.3", - "raw-window-handle 0.5.2", - "sctk-adwaita 0.4.3", - "smithay-client-toolkit", - "wasm-bindgen", - "wayland-client", - "wayland-protocols", - "web-sys", - "windows-sys 0.36.1", - "x11-dl", -] +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winit" -version = "0.28.6" +version = "0.29.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866db3f712fffba75d31bf0cdecf357c8aeafd158c5b7ab51dba2a2b2d47f196" +checksum = "d25d662bb83b511acd839534bb2d88521b0bbc81440969cb077d23c4db9e62c7" dependencies = [ + "ahash", "android-activity", - "bitflags", + "atomic-waker", + "bitflags 2.4.1", + "bytemuck", + "calloop", "cfg_aliases", "core-foundation", "core-graphics", - "dispatch", - "instant", + "cursor-icon", + "icrate", + "js-sys", "libc", "log", - "mio", + "memmap2", "ndk", + "ndk-sys", "objc2", "once_cell", "orbclient", "percent-encoding", "raw-window-handle 0.5.2", + "raw-window-handle 0.6.0", "redox_syscall 0.3.5", - "sctk-adwaita 0.5.4", + "rustix", "smithay-client-toolkit", + "smol_str", + "unicode-segmentation", "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", "wayland-client", - "wayland-commons", "wayland-protocols", - "wayland-scanner", + "wayland-protocols-plasma", "web-sys", - "windows-sys 0.45.0", + "web-time", + "windows-sys 0.48.0", "x11-dl", + "x11rb", + "xkbcommon-dl", ] [[package]] name = "winnow" -version = "0.4.6" +version = "0.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" 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 = "x11-dl" version = "2.21.0" @@ -3449,12 +2944,16 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.10.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" +checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a" dependencies = [ + "as-raw-xcb-connection", "gethostname", - "nix 0.24.3", + "libc", + "libloading 0.7.4", + "nix", + "once_cell", "winapi", "winapi-wsapoll", "x11rb-protocol", @@ -3462,42 +2961,78 @@ dependencies = [ [[package]] name = "x11rb-protocol" -version = "0.10.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" +checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc" dependencies = [ - "nix 0.24.3", + "nix", ] [[package]] name = "xcursor" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924668544c48c0133152e7eec86d644a056ca3d09275eb8d5cdb9855f9d8699" dependencies = [ - "nom", + "bitflags 2.4.1", + "dlib", + "log", + "once_cell", + "xkeysym", ] +[[package]] +name = "xkeysym" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" + [[package]] name = "xml-rs" -version = "0.8.14" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52839dc911083a8ef63efa4d039d1f58b5e409f923e44c80828f206f66e5541c" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", @@ -3505,11 +3040,10 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.9+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index b9c79f0e..f53eb88b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,125 @@ [workspace] resolver = "2" -members = [ - "puffin", - "puffin_egui", - "puffin_http", - "puffin-imgui", - "puffin_viewer", -] +members = ["puffin", "puffin_egui", "puffin_http", "puffin_viewer"] + +[workspace.lints.rust] +future_incompatible = "warn" +nonstandard_style = "warn" +rust_2018_idioms = "warn" +trivial_numeric_casts = "warn" +unsafe_op_in_unsafe_fn = "warn" + +[workspace.lints.clippy] +all = "warn" +await_holding_lock = "warn" +char_lit_as_u8 = "warn" +checked_conversions = "warn" +clear_with_drain = "warn" +dbg_macro = "warn" +debug_assert_with_mut_call = "warn" +disallowed_methods = "warn" +disallowed_types = "warn" +doc_markdown = "warn" +empty_enum = "warn" +enum_glob_use = "warn" +exit = "warn" +expl_impl_clone_on_copy = "warn" +explicit_deref_methods = "warn" +explicit_into_iter_loop = "warn" +fallible_impl_from = "warn" +filter_map_next = "warn" +flat_map_option = "warn" +float_cmp_const = "warn" +fn_params_excessive_bools = "warn" +from_iter_instead_of_collect = "warn" +if_let_mutex = "warn" +implicit_clone = "warn" +imprecise_flops = "warn" +inefficient_to_string = "warn" +invalid_upcast_comparisons = "warn" +large_digit_groups = "warn" +large_stack_arrays = "warn" +large_types_passed_by_value = "warn" +let_unit_value = "warn" +linkedlist = "warn" +lossy_float_literal = "warn" +macro_use_imports = "warn" +manual_ok_or = "warn" +map_err_ignore = "warn" +map_flatten = "warn" +map_unwrap_or = "warn" +match_on_vec_items = "warn" +match_same_arms = "warn" +match_wild_err_arm = "warn" +match_wildcard_for_single_variants = "warn" +mem_forget = "warn" +mismatched_target_os = "warn" +missing_enforced_import_renames = "warn" +mut_mut = "warn" +mutex_integer = "warn" +needless_borrow = "warn" +needless_continue = "warn" +needless_for_each = "warn" +option_option = "warn" +path_buf_push_overwrite = "warn" +ptr_as_ptr = "warn" +ptr_cast_constness = "warn" +rc_mutex = "warn" +ref_option_ref = "warn" +rest_pat_in_fully_bound_structs = "warn" +same_functions_in_if_condition = "warn" +semicolon_if_nothing_returned = "warn" +single_match_else = "warn" +string_add_assign = "warn" +string_add = "warn" +string_lit_as_bytes = "warn" +string_to_string = "warn" +todo = "warn" +unimplemented = "warn" +unnecessary_box_returns = "warn" +unnested_or_patterns = "warn" +unused_self = "warn" +useless_transmute = "warn" +verbose_file_reads = "warn" +zero_sized_map_values = "warn" + +branches_sharing_code = "warn" +cast_lossless = "warn" +default_union_representation = "warn" +disallowed_script_idents = "warn" +doc_link_with_quotes = "warn" +empty_line_after_outer_attr = "warn" +equatable_if_let = "warn" +fn_to_numeric_cast_any = "warn" +implied_bounds_in_impls = "warn" +index_refutable_slice = "warn" +iter_not_returning_iterator = "warn" +large_include_file = "warn" +manual_instant_elapsed = "warn" +manual_let_else = "warn" +mod_module_files = "warn" +needless_pass_by_ref_mut = "warn" +negative_feature_names = "warn" +nonstandard_macro_braces = "warn" +print_stderr = "warn" +print_stdout = "warn" +readonly_write_lock = "warn" +should_panic_without_expect = "warn" +string_lit_chars_any = "warn" +trailing_empty_array = "warn" +transmute_ptr_to_ptr = "warn" +undocumented_unsafe_blocks = "warn" +unnecessary_safety_comment = "warn" +unnecessary_safety_doc = "warn" +unnecessary_self_imports = "warn" +unnecessary_wraps = "warn" +use_self = "warn" +useless_let_if_seq = "warn" +wildcard_enum_match_arm = "warn" [patch.crates-io] +# eframe = { git = "https://github.com/emilk/egui.git", rev = "44ff29b01237975313606b91e7f3808e49d36d69" } # egui master 2023-11-19 +# egui = { git = "https://github.com/emilk/egui.git", rev = "44ff29b01237975313606b91e7f3808e49d36d69" } # egui master 2023-11-19 + +# eframe = { path = "../../egui/crates/eframe" } +# egui = { path = "../../egui/crates/egui" } diff --git a/README.md b/README.md index bcc4ecf8..93d10c77 100644 --- a/README.md +++ b/README.md @@ -30,23 +30,38 @@ fn my_function() { } ``` -The Puffin macros write data to a thread-local data stream. When the outermost scope of a thread is closed, the data stream is sent to a global profiler collector. The scopes are pretty light-weight, costing around 100-200 nanoseconds. +The Puffin macros write data to a thread-local data stream. When the outermost scope of a thread is closed, the data stream is sent to a global profiler collector. The scopes are pretty light-weight, costing around 60 ns on an M1 MacBook Pro. -You have to turn on the profiler before it captures any data with a call to `puffin::set_scopes_on(true);`. When the profiler is off the profiler scope macros only has an overhead of 1-2 ns (and some stack space); +You have to turn on the profiler before it captures any data with a call to `puffin::set_scopes_on(true);`. When the profiler is off the profiler scope macros only has an overhead of 1 ns on an M1 MacBook Pro (plus some stack space). Once per frame you need to call `puffin::GlobalProfiler::lock().new_frame();`. -## UI +![Puffin Flamegraph using puffin_egui](puffin_egui.gif) -To view the profile data in-game you can use [`puffin_egui`](https://github.com/EmbarkStudios/puffin/tree/main/puffin_egui). +## Remote profiling -![Puffin Flamegraph using puffin_egui](puffin_egui.gif) +You can use [`puffin_http`](https://github.com/EmbarkStudios/puffin/tree/main/puffin_http) to send profile events over TCP to [`puffin_viewer`](https://github.com/EmbarkStudios/puffin/tree/main/puffin_viewer). This is as easy as: -If you are using the [`imgui`](https://crates.io/crates/imgui) crate, there is also [`puffin-imgui`](https://github.com/EmbarkStudios/puffin/tree/main/puffin-imgui). +```rs +fn main() { + let server_addr = format!("127.0.0.1:{}", puffin_http::DEFAULT_PORT); + let _puffin_server = puffin_http::Server::new(&server_addr).unwrap(); + eprintln!("Run this to view profiling data: puffin_viewer {server_addr}"); + puffin::set_scopes_on(true); -## Remote profiling + … + + // You also need to periodically call + // `puffin::GlobalProfiler::lock().new_frame();` + // to flush the profiling events. +} +``` + +## [egui](https://github.com/emilk/egui) integration + +To view the profile data in-game you can use [`puffin_egui`](https://github.com/EmbarkStudios/puffin/tree/main/puffin_egui). -You can use [`puffin_http`](https://github.com/EmbarkStudios/puffin/tree/main/puffin_http) to send profile events over TCP to [`puffin_viewer`](https://github.com/EmbarkStudios/puffin/tree/main/puffin_viewer). +If you are using [`eframe`](https://crates.io/crates/eframe) you can look at [this example](https://github.com/emilk/egui/tree/master/examples/puffin_profiler). ## Other @@ -60,6 +75,13 @@ We welcome community contributions to this project. Please read our [Contributor Guide](CONTRIBUTING.md) for more information on how to get started. +## Releasing + +We use the [cargo release](https://github.com/crate-ci/cargo-release) tool to manage changelogs, git tags and publishing crates. + +Each substantial pull request should add a changelog entry under the `[Unreleased]` section (see [keep a changelog](https://keepachangelog.com) +and previous changelog entries). The crate version in `Cargo.toml` is never updated manually in a PR as it's handled by `cargo release`. + ## License Licensed under either of diff --git a/check.sh b/check.sh index 5b26c0c3..972fc3cc 100755 --- a/check.sh +++ b/check.sh @@ -4,15 +4,17 @@ set -eux # Checks all tests, lints etc. # Basically does what the CI does. -cargo check --workspace --all-targets -cargo test --workspace --doc -cargo check --workspace --all-targets --all-features -cargo check -p puffin_viewer --lib --target wasm32-unknown-unknown --all-features -cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::all -cargo test --workspace --all-targets --all-features +export RUSTDOCFLAGS="-D warnings" # https://github.com/emilk/egui/pull/1454 + +cargo check --quiet --workspace --all-targets +cargo test --quiet --workspace --doc +cargo check --quiet --workspace --all-targets --all-features +cargo check --quiet -p puffin_viewer --lib --target wasm32-unknown-unknown --all-features +cargo clippy --quiet --workspace --all-targets --all-features -- -D warnings -W clippy::all +cargo test --quiet --workspace --all-targets --all-features cargo fmt --all -- --check -cargo doc -p puffin -p puffin_egui -p puffin-imgui -p puffin_http -p puffin_viewer --lib --no-deps --all-features +cargo doc --quiet -p puffin -p puffin_egui -p puffin_http -p puffin_viewer --lib --no-deps --all-features -(cd puffin && cargo check --no-default-features --features "ruzstd") -(cd puffin && cargo check --no-default-features --features "packing") +(cd puffin && cargo check --quiet --no-default-features --features "zstd") +(cd puffin && cargo check --quiet --no-default-features --features "serialization") diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000..d395dedf --- /dev/null +++ b/clippy.toml @@ -0,0 +1,25 @@ +msrv = "1.75.0" + +avoid-breaking-exported-api = false +allow-expect-in-tests = true +allow-unwrap-in-tests = true + +# for `disallowed_methods`: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods +disallowed-methods = [ + # std functions + { path = "std::env::var_os", reason = "use std::env::var and UTF-8 strings instead, including for paths" }, + { path = "std::env::env_os", reason = "use std::env::var and UTF-8 strings instead, including for paths" }, + { path = "std::env::args_os", reason = "use std::env::var and UTF-8 strings instead, including for paths" }, + { path = "std::env::set_var", reason = "this is unsound in (at least) glibc, and its usage in multi-threaded programs is _heavily_ discouraged" }, + { path = "std::thread::spawn", reason = "spawn threads with std::thread::Builder instead to ensure they have a name specified" }, +] + +# for `disallowed_types`: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types +disallowed-types = [ + { path = "std::sync::Mutex", reason = "Use the faster & simpler non-poisonable primitives in `parking_lot` instead" }, + { path = "std::sync::RwLock", reason = "Use the faster & simpler non-poisonable primitives in `parking_lot` instead" }, + { path = "std::sync::Condvar", reason = "Use the faster & simpler non-poisonable primitives in `parking_lot` instead" }, + { path = "std::sync::Once", reason = "Use the faster & simpler non-poisonable primitives in `parking_lot` instead" }, + + { path = "ring::digest::SHA1_FOR_LEGACY_USE_ONLY", reason = "SHA-1 is cryptographically broken, and we are building new code so should not use it" }, +] diff --git a/puffin-imgui/CHANGELOG.md b/puffin-imgui/CHANGELOG.md deleted file mode 100644 index 93cb169c..00000000 --- a/puffin-imgui/CHANGELOG.md +++ /dev/null @@ -1,124 +0,0 @@ - - -# Changelog -All notable changes to `puffin-imgui` will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - - -## [Unreleased] - ReleaseDate -## [0.22.0] - 2023-05-24 - -- Upgrade to `puffin` 0.16 - -## [0.21.0] - 2023-04-24 -## [0.20.1] - 2023-01-30 -- Upgrade to `puffin` 0.14.2 - -## [0.20.0] - 2023-01-20 - -- Upgrade to `imgui` 0.10.0 - -## [0.19.0] - 2022-12-13 - -- Upgrade to `imgui` 0.9.0 - -## [0.18.0] - 2022-11-08 - -- Require `puffin` 0.14.0 - -## [0.17.0] - 2022-06-22 -### Changed -- [PR#87](https://github.com/EmbarkStudios/puffin/pull/87) Only run pack passes if packing is enabled on the view - -## [0.16.0] - 2022-02-07 -### Changed -- [PR#64](https://github.com/EmbarkStudios/puffin/pull/64) updated dependencies and cleaned up crate metadata. - -## [0.15.0] - 2021-11-16 -### Changed -- In-memory compression of frames to use up less RAM. - -## [0.14.0] - 2021-11-12 -### Added -- Add slider for controlling number of frames recorded. -- Show total frames recorded and their total size. -- Add checkbox to toggle the profiling scopes. - -### Changed -- Lower the default number of recorded frames to 600. - -## [0.13.4] - 2021-11-05 -### Fixed -- Normalize frame height based on what frames are visible. - -## [0.13.3] - 2021-11-02 -### Fixed -- Fix occasional flickering when viewing merged scopes. - -## [0.13.2] - 2021-10-28 -### Added -- Add `ProfilerUi::global_frame_view` to access the profiler data. - -## [0.13.1] - 2021-10-21 -### Added -- Add a scope filter to focus on certain scopes. - -## [0.13.0] - 2021-10-12 -### Fixed -- Nothing new - -## [0.12.0] - 2021-09-20 -### Changed -- Update to imgui 0.8.0 - -## [0.11.0] - 2021-09-06 -### Changed -- Update puffin - -## [0.10.0] - 2021-08-23 -### Changed -- Show frame index. - -### Fixed -- Fix "Toggle with spacebar." tooltip always showing. - -## [0.9.0] -### Changed -- Paint flamegraph top-down -- Scrollable flamegraph -- More compact UI - -### Added -- Option to sort threads by name -- Drag with right mouse button to zoom -- Toggle play/pause with spacebar -- Show all scopes (even tiny ones) - -## [0.8.0] -### Added -- Select frames from recent history or from among the slowest ever. -- Nicer colors. -- Simpler interaction (drag to pan, scroll to zoom, click to focus, double-click to reset). - - -[Unreleased]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.22.0...HEAD -[0.22.0]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.21.0...puffin-imgui-0.22.0 -[0.21.0]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.20.1...puffin-imgui-0.21.0 -[0.20.1]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.20.0...puffin-imgui-0.20.1 -[0.20.0]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.19.0...puffin-imgui-0.20.0 -[0.19.0]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.16.0...puffin-imgui-0.19.0 -[0.16.0]: https://github.com/EmbarkStudios/puffin/compare/0.15.0...puffin-imgui-0.16.0 -[0.15.0]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.14.0...puffin-imgui-0.15.0 -[0.14.0]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.13.4...puffin-imgui-0.14.0 -[0.13.4]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.13.3...puffin-imgui-0.13.4 -[0.13.3]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.13.2...puffin-imgui-0.13.3 -[0.13.2]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.13.1...puffin-imgui-0.13.2 -[0.13.1]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.13.0...puffin-imgui-0.13.1 -[0.13.0]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.12.0...puffin-imgui-0.13.0 -[0.12.0]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.11.0...puffin-imgui-0.12.0 -[0.11.0]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.10.0...puffin-imgui-0.11.0 -[0.10.0]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.9.0...puffin-imgui-0.10.0 -[0.9.0]: https://github.com/EmbarkStudios/puffin/compare/puffin-imgui-0.8.0...puffin-imgui-0.9.0 -[0.8.0]: https://github.com/EmbarkStudios/puffin/releases/tag/puffin-imgui-0.8.0 diff --git a/puffin-imgui/Cargo.toml b/puffin-imgui/Cargo.toml deleted file mode 100644 index 7a618794..00000000 --- a/puffin-imgui/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "puffin-imgui" -version = "0.22.0" -authors = ["Embark "] -description = "ImGui GUI bindings for the Puffin profiler" -license = "MIT OR Apache-2.0" -edition = "2018" -homepage = "https://github.com/EmbarkStudios/puffin" -repository = "https://github.com/EmbarkStudios/puffin" -readme = "README.md" -categories = ["development-tools::profiling", "gui"] -keywords = ["profiler", "instrumentation", "gamedev"] -include = ["**/*.rs", "Cargo.toml", "README.md"] - -[dependencies] -imgui = "0.10" -natord = "1.0.9" -puffin = { version = "0.16.0", path = "../puffin", features = ["packing"] } -serde = { version = "1.0", features = ["derive"] } -mint = "0.5.6" - -[dev-dependencies] -glium = { version = "0.32", default-features = true } -imgui-glium-renderer = "0.10" -imgui-winit-support = { version = "0.10", default-features = false } diff --git a/puffin-imgui/README.md b/puffin-imgui/README.md deleted file mode 100644 index 1465579f..00000000 --- a/puffin-imgui/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Puffin ImGui Flamegraph - -[![Embark](https://img.shields.io/badge/embark-open%20source-blueviolet.svg)](https://embark.dev) -[![Embark](https://img.shields.io/badge/discord-ark-%237289da.svg?logo=discord)](https://discord.gg/dAuKfZS) -[![Crates.io](https://img.shields.io/crates/v/puffin-imgui.svg)](https://crates.io/crates/puffin-imgui) -[![Docs](https://docs.rs/puffin-imgui/badge.svg)](https://docs.rs/puffin-imgui) - -This crate provides a flamegraph view of the data collected by the Puffin profiler. - -![Example view](flamegraph.png) - -``` rust -fn main() { - puffin::set_scopes_on(true); // you may want to control this with a flag - let mut puffin_ui = puffin_imgui::ProfilerUi::default(); - - // game loop - loop { - puffin::GlobalProfiler::lock().new_frame(); - - { - puffin::profile_scope!("slow_code"); - slow_code(); - } - - puffin_ui.window(ui); - } -} -``` diff --git a/puffin-imgui/examples/imgui.rs b/puffin-imgui/examples/imgui.rs deleted file mode 100644 index a82cd24f..00000000 --- a/puffin-imgui/examples/imgui.rs +++ /dev/null @@ -1,186 +0,0 @@ -//! Based on https://github.com/imgui-rs/imgui-rs/tree/master/imgui-examples - -use glium::glutin; -use glium::glutin::event::{Event, WindowEvent}; -use glium::glutin::event_loop::{ControlFlow, EventLoop}; -use glium::glutin::window::WindowBuilder; -use glium::{Display, Surface}; -use imgui::{Context, FontConfig, FontSource, Ui}; -use imgui_glium_renderer::Renderer; -use imgui_winit_support::{HiDpiMode, WinitPlatform}; -use std::path::Path; -use std::time::Instant; - -pub struct System { - pub event_loop: EventLoop<()>, - pub display: glium::Display, - pub imgui: Context, - pub platform: WinitPlatform, - pub renderer: Renderer, - pub font_size: f32, -} - -pub fn init(title: &str) -> System { - let title = match Path::new(&title).file_name() { - Some(file_name) => file_name.to_str().unwrap(), - None => title, - }; - let event_loop = EventLoop::new(); - let context = glutin::ContextBuilder::new().with_vsync(true); - let builder = WindowBuilder::new() - .with_title(title.to_owned()) - .with_inner_size(glutin::dpi::LogicalSize::new(1024f64, 768f64)); - let display = - Display::new(builder, context, &event_loop).expect("Failed to initialize display"); - - let mut imgui = Context::create(); - imgui.set_ini_filename(None); - - let mut platform = WinitPlatform::init(&mut imgui); - { - let gl_window = display.gl_window(); - let window = gl_window.window(); - platform.attach_window(imgui.io_mut(), window, HiDpiMode::Rounded); - } - - let hidpi_factor = platform.hidpi_factor(); - let font_size = (13.0 * hidpi_factor) as f32; - imgui.fonts().add_font(&[FontSource::DefaultFontData { - config: Some(FontConfig { - size_pixels: font_size, - ..FontConfig::default() - }), - }]); - - imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32; - - let renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer"); - - System { - event_loop, - display, - imgui, - platform, - renderer, - font_size, - } -} - -impl System { - pub fn main_loop(self, mut run_ui: F) { - let System { - event_loop, - display, - mut imgui, - mut platform, - mut renderer, - .. - } = self; - let mut last_frame = Instant::now(); - - event_loop.run(move |event, _, control_flow| match event { - Event::NewEvents(_) => { - let now = Instant::now(); - imgui.io_mut().update_delta_time(now - last_frame); - last_frame = now; - } - Event::MainEventsCleared => { - let gl_window = display.gl_window(); - platform - .prepare_frame(imgui.io_mut(), gl_window.window()) - .expect("Failed to prepare frame"); - gl_window.window().request_redraw(); - } - Event::RedrawRequested(_) => { - let ui = imgui.frame(); - - let mut run = true; - run_ui(&mut run, ui); - if !run { - *control_flow = ControlFlow::Exit; - } - - let gl_window = display.gl_window(); - let mut target = display.draw(); - target.clear_color_srgb(1.0, 1.0, 1.0, 1.0); - platform.prepare_render(ui, gl_window.window()); - let draw_data = imgui.render(); - renderer - .render(&mut target, draw_data) - .expect("Rendering failed"); - target.finish().expect("Failed to swap buffers"); - } - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - event => { - let gl_window = display.gl_window(); - platform.handle_event(imgui.io_mut(), gl_window.window(), &event); - } - }) - } -} - -fn main() { - puffin::set_scopes_on(true); // Remember to call this, or puffin will be disabled! - let mut profiler_ui = puffin_imgui::ProfilerUi::default(); - let mut frame_counter = 0; - - let system = init(file!()); - system.main_loop(move |_run, ui| { - puffin::profile_function!(); - puffin::GlobalProfiler::lock().new_frame(); // call once per frame! - - profiler_ui.window(ui); - - // Give us something to inspect: - - std::thread::Builder::new() - .name("Other thread".to_owned()) - .spawn(|| { - sleep_ms(5); - }) - .unwrap(); - - sleep_ms(14); - if frame_counter % 49 == 0 { - puffin::profile_scope!("Spike"); - std::thread::sleep(std::time::Duration::from_millis(20)) - } - if frame_counter % 343 == 0 { - puffin::profile_scope!("Big spike"); - std::thread::sleep(std::time::Duration::from_millis(50)) - } - if frame_counter % 55 == 0 { - // test to verify these spikes timers are not merged together as they have different data - for (name, ms) in [("First".to_string(), 20), ("Second".to_string(), 15)] { - puffin::profile_scope!("Spike", name); - std::thread::sleep(std::time::Duration::from_millis(ms)) - } - // these are however fine to merge together as data is the same - for (_name, ms) in [("First".to_string(), 20), ("Second".to_string(), 15)] { - puffin::profile_scope!("Spike"); - std::thread::sleep(std::time::Duration::from_millis(ms)) - } - } - - for _ in 0..1000 { - puffin::profile_scope!("very thin"); - } - - frame_counter += 1; - }); -} - -fn sleep_ms(ms: usize) { - puffin::profile_function!(); - match ms { - 0 => {} - 1 => std::thread::sleep(std::time::Duration::from_millis(1)), - _ => { - sleep_ms(ms / 2); - sleep_ms(ms - (ms / 2)); - } - } -} diff --git a/puffin-imgui/flamegraph.png b/puffin-imgui/flamegraph.png deleted file mode 100644 index 72e98be8..00000000 Binary files a/puffin-imgui/flamegraph.png and /dev/null differ diff --git a/puffin-imgui/src/lib.rs b/puffin-imgui/src/lib.rs deleted file mode 100644 index ba1d2fdd..00000000 --- a/puffin-imgui/src/lib.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! An [imgui-rs](https://crates.io/crates/imgui-rs) interface for [`puffin`]. -//! -//! Usage: -//! -//! ``` ignored -//! fn main() { -//! puffin::set_scopes_on(true); // you may want to control this with a flag -//! let mut puffin_ui = puffin_imgui::ProfilerUi::default(); -//! -//! // game loop -//! loop { -//! puffin::GlobalProfiler::lock().new_frame(); -//! -//! { -//! puffin::profile_scope!("slow_code"); -//! slow_code(); -//! } -//! -//! puffin_ui.window(ui); -//! } -//! } -//! -//! # fn slow_code(){} -//! ``` - -// BEGIN - Embark standard lints v5 for Rust 1.55+ -// do not change or add/remove here, but one can add exceptions after this section -// for more info see: -#![deny(unsafe_code)] -#![warn( - clippy::all, - clippy::await_holding_lock, - clippy::char_lit_as_u8, - clippy::checked_conversions, - clippy::dbg_macro, - clippy::debug_assert_with_mut_call, - clippy::disallowed_methods, - clippy::disallowed_types, - clippy::doc_markdown, - clippy::empty_enum, - clippy::enum_glob_use, - clippy::exit, - clippy::expl_impl_clone_on_copy, - clippy::explicit_deref_methods, - clippy::explicit_into_iter_loop, - clippy::fallible_impl_from, - clippy::filter_map_next, - clippy::flat_map_option, - clippy::float_cmp_const, - clippy::fn_params_excessive_bools, - clippy::from_iter_instead_of_collect, - clippy::if_let_mutex, - clippy::implicit_clone, - clippy::imprecise_flops, - clippy::inefficient_to_string, - clippy::invalid_upcast_comparisons, - clippy::large_digit_groups, - clippy::large_stack_arrays, - clippy::large_types_passed_by_value, - clippy::let_unit_value, - clippy::linkedlist, - clippy::lossy_float_literal, - clippy::macro_use_imports, - clippy::manual_ok_or, - clippy::map_err_ignore, - clippy::map_flatten, - clippy::map_unwrap_or, - clippy::match_on_vec_items, - clippy::match_same_arms, - clippy::match_wild_err_arm, - clippy::match_wildcard_for_single_variants, - clippy::mem_forget, - clippy::mismatched_target_os, - clippy::missing_enforced_import_renames, - clippy::mut_mut, - clippy::mutex_integer, - clippy::needless_borrow, - clippy::needless_continue, - clippy::needless_for_each, - clippy::option_option, - clippy::path_buf_push_overwrite, - clippy::ptr_as_ptr, - clippy::rc_mutex, - clippy::ref_option_ref, - clippy::rest_pat_in_fully_bound_structs, - clippy::same_functions_in_if_condition, - clippy::semicolon_if_nothing_returned, - clippy::single_match_else, - clippy::string_add_assign, - clippy::string_add, - clippy::string_lit_as_bytes, - clippy::string_to_string, - clippy::todo, - clippy::trait_duplication_in_bounds, - clippy::unimplemented, - clippy::unnested_or_patterns, - clippy::unused_self, - clippy::useless_transmute, - clippy::verbose_file_reads, - clippy::zero_sized_map_values, - future_incompatible, - nonstandard_style, - rust_2018_idioms -)] -// END - Embark standard lints v0.5 for Rust 1.55+ -// crate-specific exceptions: - -mod ui; -pub use ui::*; diff --git a/puffin-imgui/src/ui.rs b/puffin-imgui/src/ui.rs deleted file mode 100644 index 99f2cc51..00000000 --- a/puffin-imgui/src/ui.rs +++ /dev/null @@ -1,1302 +0,0 @@ -use imgui::*; -use mint::Vector2; -use puffin::*; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; -use std::sync::Arc; - -// ---------------------------------------------------------------------------- - -#[derive(Clone, Copy, Debug)] -struct Vec2 { - pub x: f32, - pub y: f32, -} - -impl Vec2 { - pub fn new(x: f32, y: f32) -> Self { - Self { x, y } - } - - pub fn min(self, other: Self) -> Self { - Self { - x: self.x.min(other.x), - y: self.y.min(other.y), - } - } - - pub fn max(self, other: Self) -> Self { - Self { - x: self.x.max(other.x), - y: self.y.max(other.y), - } - } -} - -impl From<[f32; 2]> for Vec2 { - fn from(v: [f32; 2]) -> Self { - Self::new(v[0], v[1]) - } -} - -impl From for [f32; 2] { - fn from(v: Vec2) -> Self { - [v.x, v.y] - } -} - -impl std::ops::Add for Vec2 { - type Output = Vec2; - fn add(self, rhs: Vec2) -> Self::Output { - Self { - x: self.x + rhs.x, - y: self.y + rhs.y, - } - } -} - -// ---------------------------------------------------------------------------- - -const ERROR_COLOR: [f32; 4] = [1.0, 0.0, 0.0, 1.0]; -const HOVER_COLOR: [f32; 4] = [0.8, 0.8, 0.8, 1.0]; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub enum SortBy { - Time, - Name, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)] -pub struct Sorting { - pub sort_by: SortBy, - pub reversed: bool, -} - -impl Default for Sorting { - fn default() -> Self { - Self { - sort_by: SortBy::Time, - reversed: false, - } - } -} - -impl Sorting { - fn sort( - self, - thread_streams: &BTreeMap>, - ) -> Vec<(ThreadInfo, Arc)> { - let mut vec: Vec<_> = thread_streams - .iter() - .map(|(info, stream)| (info.clone(), stream.clone())) - .collect(); - - match self.sort_by { - SortBy::Time => { - vec.sort_by_key(|(info, _)| info.start_time_ns); - } - SortBy::Name => { - vec.sort_by(|(a, _), (b, _)| natord::compare_ignore_case(&a.name, &b.name)); - } - } - if self.reversed { - vec.reverse(); - } - vec - } - - fn ui(&mut self, ui: &imgui::Ui) { - ui.text("Sort threads by:"); - ui.same_line(); - - let dir = if self.reversed { '^' } else { 'v' }; - - for &sort_by in &[SortBy::Time, SortBy::Name] { - let selected = self.sort_by == sort_by; - - let label = if selected { - format!("{sort_by:?} {dir}") - } else { - format!("{sort_by:?}") - }; - - if ui.radio_button_bool(label, selected) { - if selected { - self.reversed = !self.reversed; - } else { - self.sort_by = sort_by; - self.reversed = false; - } - } - - ui.same_line(); - } - ui.new_line(); - } -} - -#[derive(Clone, Debug, Default)] -struct Filter { - filter: String, -} - -impl Filter { - fn ui(&mut self, ui: &imgui::Ui) { - ui.text("Scope filter:"); - ui.same_line(); - ui.input_text("##scopefilter", &mut self.filter).build(); - self.filter = self.filter.to_lowercase(); - ui.same_line(); - if ui.button("X") { - self.filter.clear(); - } - } - - /// if true, show everything - fn is_empty(&self) -> bool { - self.filter.is_empty() - } - - fn include(&self, id: &str) -> bool { - if self.filter.is_empty() { - true - } else { - id.to_lowercase().contains(&self.filter) - } - } -} - -/// The frames we can select between -#[derive(Clone)] -pub struct Frames { - pub recent: Vec>, - pub slowest: Vec>, -} - -impl Frames { - fn all_uniq(&self) -> Vec> { - let mut all = self.slowest.clone(); - all.extend(self.recent.iter().cloned()); - all.sort_by_key(|frame| frame.frame_index()); - all.dedup_by_key(|frame| frame.frame_index()); - all - } -} - -#[derive(Clone)] -pub struct Paused { - /// The frame we are viewing. - selected_frame: Arc, - /// All the frames we had when paused. - frames: Frames, -} - -#[derive(Deserialize, Serialize)] -#[serde(default)] -pub struct ProfilerUi { - pub options: Options, - - #[serde(skip)] - frame_view: GlobalFrameView, - - // interaction: - #[serde(skip)] - is_panning: bool, - #[serde(skip)] - is_zooming: bool, - - /// If `None`, we show the latest frames. - #[serde(skip)] - paused: Option, - - /// How we normalize the frame view: - slowest_frame: f32, - - /// When did we last run a pass to pack all the frames? - #[serde(skip)] - last_pack_pass: Option, -} - -impl Default for ProfilerUi { - fn default() -> Self { - let frame_view = GlobalFrameView::default(); - frame_view.lock().set_max_recent(60 * 10); // We can't currently scroll back anyway - - Self { - options: Default::default(), - frame_view, - is_panning: false, - is_zooming: false, - paused: None, - slowest_frame: 0.17, - last_pack_pass: None, - } - } -} - -#[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(default)] -pub struct Options { - // -------------------- - // View: - /// Controls zoom - pub canvas_width_ns: f32, - - /// How much we have panned sideways: - pub sideways_pan_in_pixels: f32, - - // -------------------- - // Visuals: - /// Events shorter than this many pixels aren't painted - pub cull_width: f32, - /// Draw each item with at least this width (only makes sense if [`Self::cull_width`] is 0) - pub min_width: f32, - - pub rect_height: f32, - pub spacing: f32, - pub rounding: f32, - - /// Aggregate child scopes with the same id? - pub merge_scopes: bool, - - pub sorting: Sorting, - #[serde(skip)] - filter: Filter, - - /// Size of a frame in the frame-view, including padding - pub frame_width: f32, - - /// Set when user clicks a scope. - /// First part is `now()`, second is range. - #[serde(skip)] - zoom_to_relative_ns_range: Option<(f64, (NanoSecond, NanoSecond))>, -} - -impl Default for Options { - fn default() -> Self { - Self { - canvas_width_ns: 0.0, - sideways_pan_in_pixels: 0.0, - - // cull_width: 0.5, // save some CPU? - cull_width: 0.0, // no culling - min_width: 1.0, - - rect_height: 16.0, - spacing: 4.0, - rounding: 4.0, - - merge_scopes: true, - - sorting: Default::default(), - filter: Default::default(), - - frame_width: 10.0, - - zoom_to_relative_ns_range: None, - } - } -} - -/// Context for painting a frame. -struct Info<'a> { - // Bounding box of canvas in pixels: - canvas_min: Vec2, - canvas_max: Vec2, - - mouse_pos: Vec2, - - ui: &'a Ui, - draw_list: &'a DrawListMut<'a>, - font_size: f32, - - /// Time of first event - start_ns: NanoSecond, -} - -#[derive(Clone, Copy, Eq, PartialEq)] -enum PaintResult { - Culled, - Hovered, - Normal, -} - -impl<'ui> Info<'ui> { - fn canvas_width(&self) -> f32 { - self.canvas_max.x - self.canvas_min.x - } - - fn pixel_from_ns(&self, options: &Options, ns: NanoSecond) -> f32 { - self.canvas_min.x - + options.sideways_pan_in_pixels - + self.canvas_width() * ((ns - self.start_ns) as f32) / options.canvas_width_ns - } -} - -impl ProfilerUi { - /// The frames we are looking at. - pub fn global_frame_view(&self) -> &GlobalFrameView { - &self.frame_view - } - - /// Show a [`imgui::Window`] with the profiler contents. - /// If you want to control the window yourself, use [`Self::ui`] instead. - pub fn window(&mut self, ui: &Ui) -> bool { - let mut open = true; - ui.window("Profiler") - .position([10.0, 25.0], Condition::FirstUseEver) - .size([800.0, 600.0], Condition::FirstUseEver) - .bg_alpha(0.99) // Transparency can be distracting - .always_auto_resize(false) - .opened(&mut open) - .build(|| self.ui(ui)); - open - } - - fn latest_frames(&self) -> Frames { - let view = self.frame_view.lock(); - Frames { - recent: view.recent_frames().cloned().collect(), - slowest: view.slowest_frames_chronological().cloned().collect(), - } - } - - /// The frames we can select between - fn frames(&self) -> Frames { - self.paused - .as_ref() - .map_or_else(|| self.latest_frames(), |paused| paused.frames.clone()) - } - - /// Pause on the specific frame - fn pause_and_select(&mut self, selected_frame: Arc) { - if let Some(paused) = &mut self.paused { - paused.selected_frame = selected_frame; - } else { - self.paused = Some(Paused { - selected_frame, - frames: self.frames(), - }); - } - } - - fn selected_frame(&self) -> Option> { - self.paused - .as_ref() - .map(|paused| paused.selected_frame.clone()) - .or_else(|| self.frame_view.lock().latest_frame()) - } - - fn selected_frame_index(&self) -> Option { - self.selected_frame().map(|frame| frame.frame_index()) - } - - fn all_known_frames(&self) -> Vec> { - let mut all = self - .frame_view - .lock() - .all_uniq() - .cloned() - .collect::>(); - - if let Some(paused) = &self.paused { - all.append(&mut paused.frames.all_uniq()); - } - - all.sort_by_key(|frame| frame.frame_index()); - all.dedup_by_key(|frame| frame.frame_index()); - all - } - - fn run_pack_pass_if_needed(&mut self) { - if !self.frame_view.lock().pack_frames() { - return; - } - let last_pack_pass = self - .last_pack_pass - .get_or_insert_with(std::time::Instant::now); - let time_since_last_pack = last_pack_pass.elapsed(); - if time_since_last_pack > std::time::Duration::from_secs(1) { - puffin::profile_scope!("pack_pass"); - for frame in self.all_known_frames() { - if Some(frame.frame_index()) != self.selected_frame_index() { - frame.pack(); - } - } - self.last_pack_pass = Some(std::time::Instant::now()); - } - } - - /// Show the profiler. - /// - /// Call this from within an [`imgui::Window`], or use [`Self::window`] instead. - pub fn ui(&mut self, ui: &Ui) { - #![allow(clippy::collapsible_else_if)] - - puffin::profile_function!(); - - self.run_pack_pass_if_needed(); - - let mut scopes_on = puffin::are_scopes_on(); - ui.checkbox("Profiling enabled", &mut scopes_on); - puffin::set_scopes_on(scopes_on); - - if !puffin::are_scopes_on() { - ui.same_line(); - ui.text_colored(ERROR_COLOR, "No new scopes are being recorded!"); - } - - let mut hovered_frame = None; - if imgui::CollapsingHeader::new("Frames") - .default_open(false) - .build(ui) - { - ui.indent(); - hovered_frame = self.show_frames(ui); - ui.unindent(); - } - - let frame = hovered_frame.or_else(|| self.selected_frame()); - let frame = if let Some(frame) = frame { - frame - } else { - ui.text("No profiling data"); - return; - }; - - let frame = match frame.unpacked() { - Ok(frame) => frame, - Err(err) => { - ui.text_colored(ERROR_COLOR, format!("Bad frame: {err}")); - return; - } - }; - - // TODO: show age of data - - let (min_ns, max_ns) = frame.range_ns(); - - ui.button("Help!"); - if ui.is_item_hovered() { - ui.tooltip_text( - "Drag to pan. \n\ - Zoom: drag up/down with secondary mouse button. \n\ - Click on a scope to zoom to it.\n\ - Double-click background to reset view.\n\ - Press spacebar to pause/resume.", - ); - } - - ui.same_line(); - - let play_pause_button_size = [54.0, 0.0]; - if self.paused.is_some() { - if ui.button_with_size("Resume", play_pause_button_size) - || ui.is_key_pressed(imgui::Key::Space) - { - self.paused = None; - } - } else { - if ui.button_with_size("Pause", play_pause_button_size) - || ui.is_key_pressed(imgui::Key::Space) - { - let latest = self.frame_view.lock().latest_frame(); - if let Some(latest) = latest { - self.pause_and_select(latest); - } - } - } - if ui.is_item_hovered() { - ui.tooltip_text("Toggle with spacebar."); - } - - ui.same_line(); - ui.checkbox( - "Merge children with same ID", - &mut self.options.merge_scopes, - ); - - ui.text(format!( - "Showing frame #{}, {:.1} ms, {} threads, {} scopes.", - frame.frame_index(), - (max_ns - min_ns) as f64 * 1e-6, - frame.thread_streams.len(), - frame.meta.num_scopes, - )); - - // The number of threads can change between frames, so always show this even if there currently is only one thread: - self.options.sorting.ui(ui); - - self.options.filter.ui(ui); - - ui.separator(); - - let content_min: Vec2 = ui.cursor_screen_pos().into(); - let content_region_avail: Vec2 = ui.content_region_avail().into(); - let content_max = content_min + content_region_avail; - - let draw_list = ui.get_window_draw_list(); - - // Make it scrollable: - ui.child_window("flamegraph").build(|| { - let info = Info { - start_ns: min_ns, - canvas_min: content_min, - canvas_max: content_max, - mouse_pos: ui.io().mouse_pos.into(), - ui, - draw_list: &draw_list, - font_size: ui.current_font_size(), - }; - - draw_list.with_clip_rect_intersect( - Vector2::from_slice(&[content_min.x, content_min.y]), - Vector2::from_slice(&[content_max.x, content_max.y]), - || { - let max_y = self.ui_canvas(&info, &frame, (min_ns, max_ns)); - let used_space = Vec2::new( - content_region_avail.x, - content_region_avail.y.max(max_y - content_min.y), - ); - - // An invisible button for the canvas allows us to catch input for it. - ui.invisible_button( - "canvas", - Vector2::from_slice(&[used_space.x, used_space.y]), - ); - self.interact_with_canvas(ui, max_ns - min_ns, (content_min, content_max)); - }, - ); - }); - } - - fn ui_canvas( - &mut self, - info: &Info<'_>, - frame: &Arc, - (min_ns, max_ns): (NanoSecond, NanoSecond), - ) -> f32 { - puffin::profile_function!(); - - if self.options.canvas_width_ns <= 0.0 { - self.options.canvas_width_ns = (max_ns - min_ns) as f32; - self.options.zoom_to_relative_ns_range = None; - } - - paint_timeline(info, &self.options, min_ns); - - // We paint the threads top-down - let mut cursor_y = info.canvas_min.y - info.ui.scroll_y(); - cursor_y += info.font_size; // Leave room for time labels - - let thread_streams = self.options.sorting.sort(&frame.thread_streams); - - for (thread_info, stream_info) in &thread_streams { - cursor_y += 2.0; - let line_y = cursor_y; - cursor_y += 2.0; - - let text_pos = [info.canvas_min.x, cursor_y]; - paint_thread_info(info, thread_info, text_pos); - cursor_y += info.font_size; - - // Visual separator between threads: - info.draw_list - .add_line( - [info.canvas_min.x, line_y], - [info.canvas_max.x, line_y], - [1.0, 1.0, 1.0, 0.5], - ) - .build(); - - let mut paint_stream = || -> Result<()> { - if self.options.merge_scopes { - let frames = vec![frame.clone()]; - let merges = { - puffin::profile_scope!("merge_scopes"); - puffin::merge_scopes_for_thread(&frames, thread_info)? - }; - for merge in merges { - paint_merge_scope(info, &mut self.options, 0, &merge, 0, cursor_y)?; - } - } else { - let top_scopes = Reader::from_start(&stream_info.stream).read_top_scopes()?; - for scope in top_scopes { - paint_scope( - info, - &mut self.options, - &stream_info.stream, - &scope, - 0, - cursor_y, - )?; - } - } - Ok(()) - }; - if let Err(err) = paint_stream() { - let text = format!("Profiler stream error: {err:?}"); - info.draw_list - .add_text([info.canvas_min.x, cursor_y], ERROR_COLOR, text); - } - - cursor_y += - stream_info.depth as f32 * (self.options.rect_height + self.options.spacing); - - cursor_y += info.font_size; // Extra spacing between threads - } - - cursor_y + info.ui.scroll_y() - } - - /// Returns hovered, if any - fn show_frames(&mut self, ui: &Ui) -> Option> { - puffin::profile_function!(); - let frames = self.frames(); - - let mut hovered_frame = None; - - max_memory_controls(ui, &frames, &self.frame_view); - - ui.columns(2, "columns", false); - ui.set_column_width(0, 64.0); - - ui.text("Recent:"); - ui.next_column(); - - let slowest_visible = self.show_frame_list( - ui, - "Recent frames", - &frames.recent, - &mut hovered_frame, - self.slowest_frame, - ); - // quickly, but smoothly, normalize frame height: - self.slowest_frame = lerp(self.slowest_frame..=slowest_visible as f32, 0.2); - ui.next_column(); - - ui.text("Slowest:"); - if ui.button("Clear") { - self.frame_view.lock().clear_slowest(); - } - ui.next_column(); - { - let num_fit = (ui.content_region_avail()[0] / self.options.frame_width).floor(); - let num_fit = (num_fit as usize).clamp(1, frames.slowest.len()); - let slowest_of_the_slow = puffin::select_slowest(&frames.slowest, num_fit); - - let mut slowest_frame = 0; - for frame in &slowest_of_the_slow { - slowest_frame = frame.duration_ns().max(slowest_frame); - } - - self.show_frame_list( - ui, - "Slow spikes", - &slowest_of_the_slow, - &mut hovered_frame, - slowest_frame as f32, - ); - } - ui.next_column(); - - ui.columns(1, "", false); - - hovered_frame - } - - /// Returns the slowest visible frame - fn show_frame_list( - &mut self, - ui: &Ui, - label: &str, - frames: &[Arc], - hovered_frame: &mut Option>, - slowest_frame: f32, - ) -> NanoSecond { - let min: Vec2 = ui.cursor_screen_pos().into(); - let size = Vec2::new(ui.content_region_avail()[0], 48.0); - let max = min + size; - - let frame_width_including_spacing = self.options.frame_width; - let frame_spacing = 2.0; - let frame_width = frame_width_including_spacing - frame_spacing; - - ui.invisible_button(ImString::new(label), Vector2::from_slice(&[size.x, size.y])); - let draw_list = ui.get_window_draw_list(); - - let selected_frame_index = self.selected_frame_index(); - - let mouse_pos: Vec2 = ui.io().mouse_pos.into(); - - let mut slowest_visible_frame = 0; - - draw_list.with_clip_rect_intersect( - Vector2::from_slice(&[min.x, min.y]), - Vector2::from_slice(&[max.x, max.y]), - || { - for (i, frame) in frames.iter().enumerate() { - let x = - max.x - (frames.len() as f32 - i as f32) * frame_width_including_spacing; - let mut rect_min = Vec2::new(x, min.y); - let rect_max = Vec2::new(x + frame_width, max.y); - - let is_visible = min.x <= rect_max.x && rect_min.x <= max.x; - if is_visible { - let duration = frame.duration_ns(); - slowest_visible_frame = duration.max(slowest_visible_frame); - - let is_selected = Some(frame.frame_index()) == selected_frame_index; - - let is_hovered = rect_min.x - 0.5 * frame_spacing <= mouse_pos.x - && mouse_pos.x < rect_max.x + 0.5 * frame_spacing - && rect_min.y <= mouse_pos.y - && mouse_pos.y <= rect_max.y; - - if is_hovered { - *hovered_frame = Some(frame.clone()); - ui.tooltip_text(format!("{:.1} ms", frame.duration_ns() as f64 * 1e-6)); - } - if is_hovered && ui.is_mouse_clicked(MouseButton::Left) { - self.pause_and_select(frame.clone()); - } - - let mut color = if is_selected { - [1.0; 4] - } else if is_hovered { - HOVER_COLOR - } else { - [0.6, 0.6, 0.4, 1.0] - }; - - // Transparent, full height: - color[3] = if is_selected || is_hovered { 0.6 } else { 0.25 }; - draw_list - .add_rect( - Vector2::from_slice(&[rect_min.x, rect_min.y]), - Vector2::from_slice(&[rect_max.x, rect_max.y]), - color, - ) - .filled(true) - .build(); - - // Opaque, height based on duration: - color[3] = 1.0; - rect_min.y = lerp(max.y..=min.y, duration as f32 / slowest_frame); - draw_list - .add_rect( - Vector2::from_slice(&[rect_min.x, rect_min.y]), - Vector2::from_slice(&[rect_max.x, rect_max.y]), - color, - ) - .filled(true) - .build(); - } - } - }, - ); - - slowest_visible_frame - } - - fn interact_with_canvas( - &mut self, - ui: &Ui, - duration_ns: NanoSecond, - (canvas_min, canvas_max): (Vec2, Vec2), - ) { - // note: imgui scroll coordinates are not pixels. - // for `mouse_wheel` one unit scrolls "about 5 lines of text", - // and `mouse_wheel_h` is unspecified. - // So let's not use them. - - let pan_button = MouseButton::Left; - self.is_panning |= ui.is_item_hovered() && ui.is_mouse_clicked(pan_button); - self.is_panning &= !ui.is_mouse_released(pan_button); - - let zoom_button = MouseButton::Right; - self.is_zooming |= ui.is_item_hovered() && ui.is_mouse_clicked(zoom_button); - self.is_zooming &= !ui.is_mouse_released(zoom_button); - - let pan_delta = if self.is_panning { - let pan_delta = ui.mouse_drag_delta_with_button(pan_button); - ui.reset_mouse_drag_delta(pan_button); - pan_delta - } else { - [0.0, 0.0] - }; - - if self.is_panning && pan_delta[0] != 0.0 { - self.options.sideways_pan_in_pixels += pan_delta[0]; - self.options.zoom_to_relative_ns_range = None; - } - - let zoom_factor = if self.is_zooming { - let zoom_delta = ui.mouse_drag_delta_with_button(zoom_button)[1]; - ui.reset_mouse_drag_delta(zoom_button); - (zoom_delta * 0.01).exp() - } else { - 0.0 - }; - - if zoom_factor != 0.0 { - self.options.canvas_width_ns /= zoom_factor; - - let zoom_center = ui.io().mouse_pos[0] - canvas_min.x; - self.options.sideways_pan_in_pixels = - (self.options.sideways_pan_in_pixels - zoom_center) * zoom_factor + zoom_center; - - self.options.zoom_to_relative_ns_range = None; - } - - if ui.is_item_hovered() && ui.is_mouse_double_clicked(MouseButton::Left) { - // Reset view - self.options.zoom_to_relative_ns_range = Some((now(), (0, duration_ns))); - } - - if let Some((start_time, (start_ns, end_ns))) = self.options.zoom_to_relative_ns_range { - const ZOOM_DURATION: f32 = 0.75; - let t = ((now() - start_time) as f32 / ZOOM_DURATION).min(1.0); - - let canvas_width = canvas_max.x - canvas_min.x; - - let target_canvas_width_ns = (end_ns - start_ns) as f32; - let target_pan_in_pixels = -canvas_width * start_ns as f32 / target_canvas_width_ns; - - // self.options.canvas_width_ns = - // lerp(self.options.canvas_width_ns..=target_canvas_width_ns, t); - self.options.canvas_width_ns = lerp( - self.options.canvas_width_ns.recip()..=target_canvas_width_ns.recip(), - t, - ) - .recip(); - self.options.sideways_pan_in_pixels = lerp( - self.options.sideways_pan_in_pixels..=target_pan_in_pixels, - t, - ); - - if t >= 1.0 { - self.options.zoom_to_relative_ns_range = None; - } - } - } -} - -/// Current time in seconds -fn now() -> f64 { - std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap_or_default() - .as_secs_f64() -} - -fn paint_timeline(info: &Info<'_>, options: &Options, start_ns: NanoSecond) { - if options.canvas_width_ns <= 0.0 { - return; - } - - let alpha_multiplier = if options.filter.is_empty() { 1.0 } else { 0.3 }; - - // We show all measurements relative to start_ns - - let max_lines = 300.0; - let mut grid_spacing_ns = 1_000; - while options.canvas_width_ns / (grid_spacing_ns as f32) > max_lines { - grid_spacing_ns *= 10; - } - - // We fade in lines as we zoom in: - let num_tiny_lines = options.canvas_width_ns / (grid_spacing_ns as f32); - let zoom_factor = remap_clamp(num_tiny_lines, (0.1 * max_lines)..=max_lines, 1.0..=0.0); - let zoom_factor = zoom_factor * zoom_factor; - let big_alpha = remap_clamp(zoom_factor, 0.0..=1.0, 0.5..=1.0); - let medium_alpha = remap_clamp(zoom_factor, 0.0..=1.0, 0.1..=0.5); - let tiny_alpha = remap_clamp(zoom_factor, 0.0..=1.0, 0.0..=0.1); - - let mut grid_ns = 0; - - loop { - let line_x = info.pixel_from_ns(options, start_ns + grid_ns); - if line_x > info.canvas_max.x { - break; - } - - if info.canvas_min.x <= line_x { - let big_line = grid_ns % (grid_spacing_ns * 100) == 0; - let medium_line = grid_ns % (grid_spacing_ns * 10) == 0; - - let line_alpha = if big_line { - big_alpha - } else if medium_line { - medium_alpha - } else { - tiny_alpha - }; - let line_color = [1.0, 1.0, 1.0, line_alpha * alpha_multiplier]; - - info.draw_list - .add_line( - [line_x, info.canvas_min.y], - [line_x, info.canvas_max.y], - line_color, - ) - .build(); - - let text_alpha = if big_line { - medium_alpha - } else if medium_line { - tiny_alpha - } else { - 0.0 - }; - - if text_alpha > 0.0 { - let text = grid_text(grid_ns); - let text_x = line_x + 4.0; - let text_color = [1.0, 1.0, 1.0, (text_alpha * 2.0).min(1.0)]; - - // Text at top: - info.draw_list - .add_text([text_x, info.canvas_min.y], text_color, &text); - - // Text at bottom: - info.draw_list.add_text( - [text_x, info.canvas_max.y - info.font_size], - text_color, - &text, - ); - } - } - - grid_ns += grid_spacing_ns; - } -} - -fn grid_text(grid_ns: NanoSecond) -> String { - let grid_ms = to_ms(grid_ns); - if grid_ns % 1_000_000 == 0 { - format!("{grid_ms:.0} ms") - } else if grid_ns % 100_000 == 0 { - format!("{grid_ms:.1} ms") - } else if grid_ns % 10_000 == 0 { - format!("{grid_ms:.2} ms") - } else { - format!("{grid_ms:.3} ms") - } -} - -fn paint_record( - info: &Info<'_>, - options: &mut Options, - prefix: &str, - record: &Record<'_>, - top_y: f32, -) -> PaintResult { - let mut start_x = info.pixel_from_ns(options, record.start_ns); - let mut stop_x = info.pixel_from_ns(options, record.stop_ns()); - if info.canvas_max.x < start_x - || stop_x < info.canvas_min.x - || stop_x - start_x < options.cull_width - { - return PaintResult::Culled; - } - - let mut min_width = options.min_width; - - let bottom_y = top_y + options.rect_height; - - let is_hovered = start_x <= info.mouse_pos.x - && info.mouse_pos.x <= stop_x - && top_y <= info.mouse_pos.y - && info.mouse_pos.y <= bottom_y; - - if is_hovered && info.ui.is_mouse_clicked(MouseButton::Left) { - options.zoom_to_relative_ns_range = Some(( - now(), - ( - record.start_ns - info.start_ns, - record.stop_ns() - info.start_ns, - ), - )); - } - - let mut rect_color = if is_hovered { - HOVER_COLOR - } else { - color_from_duration(record.duration_ns) - }; - - if !options.filter.is_empty() { - if options.filter.include(record.id) { - // keep full opacity - min_width *= 2.0; // make it more visible even when thin - } else { - rect_color[3] *= 0.3; // fade to highlight others - } - } - - if stop_x - start_x < min_width { - // Make sure it is visible: - let center = 0.5 * (start_x + stop_x); - start_x = center - 0.5 * min_width; - stop_x = center + 0.5 * min_width; - } - - let rect_min = Vec2::new(start_x, top_y); - let rect_max = Vec2::new(stop_x, bottom_y); - - let text_color = [0.0, 0.0, 0.0, 1.0]; - - info.draw_list - .add_rect( - Vector2::from_slice(&[rect_min.x, rect_min.y]), - Vector2::from_slice(&[rect_max.x, rect_max.y]), - rect_color, - ) - .filled(true) - .rounding(options.rounding) - .build(); - - let wide_enough_for_text = stop_x - start_x > 32.0; - if wide_enough_for_text { - let rect_min = rect_min.max(info.canvas_min); - let rect_max = rect_max.min(info.canvas_max); - - info.draw_list.with_clip_rect_intersect( - Vector2::from_slice(&[rect_min.x, rect_min.y]), - Vector2::from_slice(&[rect_max.x, rect_max.y]), - || { - let duration_ms = to_ms(record.duration_ns); - let text = if record.data.is_empty() { - format!("{}{} {:6.3} ms", prefix, record.id, duration_ms) - } else { - format!( - "{}{} {:?} {:6.3} ms", - prefix, record.id, record.data, duration_ms - ) - }; - info.draw_list.add_text( - [ - start_x + 4.0, - top_y + 0.5 * (options.rect_height - info.font_size), - ], - text_color, - text, - ); - }, - ); - } - - if is_hovered { - PaintResult::Hovered - } else { - PaintResult::Normal - } -} - -fn color_from_duration(ns: NanoSecond) -> [f32; 4] { - let ms = to_ms(ns) as f32; - // Brighter = more time. - // So we start with dark colors (blue) and later bright colors (green). - let b = remap_clamp(ms, 0.0..=5.0, 1.0..=0.3); - let r = remap_clamp(ms, 0.0..=10.0, 0.5..=0.8); - let g = remap_clamp(ms, 10.0..=33.3, 0.1..=0.8); - let a = 0.9; - [r, g, b, a] -} - -fn to_ms(ns: NanoSecond) -> f64 { - ns as f64 * 1e-6 -} - -use std::ops::{Add, Mul, RangeInclusive}; - -fn lerp(range: RangeInclusive, t: f32) -> T -where - f32: Mul, - T: Add + Copy, -{ - (1.0 - t) * *range.start() + t * *range.end() -} - -fn remap_clamp(x: f32, from: RangeInclusive, to: RangeInclusive) -> f32 { - let t = if x <= *from.start() { - 0.0 - } else if x >= *from.end() { - 1.0 - } else { - (x - from.start()) / (from.end() - from.start()) - }; - lerp(to, t) -} - -fn paint_scope( - info: &Info<'_>, - options: &mut Options, - stream: &Stream, - scope: &Scope<'_>, - depth: usize, - min_y: f32, -) -> Result { - let top_y = min_y + (depth as f32) * (options.rect_height + options.spacing); - - let result = paint_record(info, options, "", &scope.record, top_y); - - if result != PaintResult::Culled { - let mut num_children = 0; - for child_scope in Reader::with_offset(stream, scope.child_begin_position)? { - paint_scope(info, options, stream, &child_scope?, depth + 1, min_y)?; - num_children += 1; - } - - if result == PaintResult::Hovered { - let ui = info.ui; - ui.tooltip(|| { - ui.text(format!("id: {}", scope.record.id)); - if !scope.record.location.is_empty() { - ui.text(format!("location: {}", scope.record.location)); - } - if !scope.record.data.is_empty() { - ui.text(format!("data: {}", scope.record.data)); - } - ui.text(format!( - "duration: {:6.3} ms", - to_ms(scope.record.duration_ns) - )); - ui.text(format!("children: {num_children}")); - }); - } - } - - Ok(result) -} - -fn paint_merge_scope( - info: &Info<'_>, - options: &mut Options, - ns_offset: NanoSecond, - merge: &MergeScope<'_>, - depth: usize, - min_y: f32, -) -> Result { - let top_y = min_y + (depth as f32) * (options.rect_height + options.spacing); - - let prefix = if merge.num_pieces <= 1 { - String::default() - } else { - format!("{}x ", merge.num_pieces) - }; - - let record = Record { - start_ns: ns_offset + merge.relative_start_ns, - duration_ns: merge.duration_per_frame_ns, - id: &merge.id, - location: &merge.location, - data: &merge.data, - }; - - let result = paint_record(info, options, &prefix, &record, top_y); - - if result != PaintResult::Culled { - for merged_child in &merge.children { - paint_merge_scope( - info, - options, - record.start_ns, - merged_child, - depth + 1, - min_y, - )?; - } - - if result == PaintResult::Hovered { - let ui = info.ui; - ui.tooltip(|| merge_scope_tooltip(ui, merge)); - } - } - - Ok(result) -} - -fn merge_scope_tooltip(ui: &Ui, merge: &MergeScope<'_>) { - ui.text(format!("id: {}", merge.id)); - if !merge.location.is_empty() { - ui.text(format!("location: {}", merge.location)); - } - if !merge.data.is_empty() { - ui.text(format!("data: {}", merge.data)); - } - - if merge.num_pieces <= 1 { - ui.text(format!( - "duration: {:6.3} ms", - to_ms(merge.duration_per_frame_ns) - )); - } else { - ui.text(format!("sum of: {} scopes", merge.num_pieces)); - ui.text(format!( - "total: {:6.3} ms", - to_ms(merge.duration_per_frame_ns) - )); - ui.text(format!( - "mean: {:6.3} ms", - to_ms(merge.duration_per_frame_ns) / (merge.num_pieces as f64), - )); - ui.text(format!("max: {:6.3} ms", to_ms(merge.max_duration_ns))); - } -} - -fn paint_thread_info(info: &Info<'_>, thread_info: &ThreadInfo, pos: [f32; 2]) { - let text = &thread_info.name; - let text_size = info.ui.calc_text_size(ImString::new(text)); - - info.draw_list - .add_rect( - pos, - [pos[0] + text_size[0], pos[1] + text_size[1]], - [0.0, 0.0, 0.0, 0.5], - ) - .filled(true) - .rounding(0.0) - .build(); - - info.draw_list.add_text(pos, [0.9, 0.9, 0.9, 1.0], text); -} - -fn max_memory_controls(ui: &Ui, frames: &Frames, frame_view: &GlobalFrameView) { - let uniq = frames.all_uniq(); - - let mut bytes = 0; - let mut unpacked = 0; - for frame in &uniq { - bytes += frame.bytes_of_ram_used(); - unpacked += frame.has_unpacked() as usize; - } - ui.text(format!( - "{} frames ({} unpacked) using approximately {:.1} MB.", - uniq.len(), - unpacked, - bytes as f64 * 1e-6 - )); - - let frames_per_second = if let (Some(first), Some(last)) = (uniq.first(), uniq.last()) { - let nanos = last.range_ns().1 - first.range_ns().0; - let seconds = nanos as f64 * 1e-9; - let frames = last.frame_index() - first.frame_index() + 1; - frames as f64 / seconds - } else { - 60.0 - }; - - let mut memory_length = frame_view.lock().max_recent() as u64; - - ui.slider_config("Max recent frames to store", 10, 100_000) - .display_format(format!( - "%d (~ {:.1} minutes, ~ {:.0} MB)", - memory_length as f64 / 60.0 / frames_per_second, - memory_length as f64 * bytes as f64 / uniq.len() as f64 * 1e-6, - )) - .build(&mut memory_length); - - frame_view.lock().set_max_recent(memory_length as _); -} diff --git a/CHANGELOG.md b/puffin/CHANGELOG.md similarity index 74% rename from CHANGELOG.md rename to puffin/CHANGELOG.md index b5202563..9249fa27 100644 --- a/CHANGELOG.md +++ b/puffin/CHANGELOG.md @@ -8,6 +8,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate + +## [0.19.0] - 2024-01-17 + +- [PR#169](https://github.com/EmbarkStudios/puffin/pull/169) Stream scope information only once, drastically reduce bandwidth and increased performance. Allow better usage of static strings in profile scopes. Breaking change! See PR for migration guide. +- [PR#179](https://github.com/EmbarkStudios/puffin/pull/179) Update egui to v0.25 and a updates many other dependencies in process. +- [PR#181](https://github.com/EmbarkStudios/puffin/pull/181) Measure profile scope start time after serialization functions. + +## [0.18.1] - 2023-12-11 + +- [PR#175](https://github.com/EmbarkStudios/puffin/pull/175) Remove accidental `::{{closure}}` suffix from all `profile_function` scopes. + +## [0.18.0] - 2023-11-21 + +- [PR#165](https://github.com/EmbarkStudios/puffin/pull/165) Faster profiling, add line numbers, better paths, and better function names + +## [0.17.1] - 2023-11-20 - YANKED + +- Accidentally introduced a breaking change in [PR#165](https://github.com/EmbarkStudios/puffin/pull/165) + +## [0.17.0] - 2023-09-28 + +- [PR#140](https://github.com/EmbarkStudios/puffin/pull/140) Remove imgui support for +- [PR#158](https://github.com/EmbarkStudios/puffin/pull/158) Remove file I/O from +- [PR#157](https://github.com/EmbarkStudios/puffin/pull/157) Remove `instant` dependency when not building for web + ## [0.16.0] - 2023-05-24 - [PR#136](https://github.com/EmbarkStudios/puffin/pull/136) Upgrade `ruzstd` to 0.4 @@ -22,7 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.14.2] - 2023-01-30 -- [PR#123](https://github.com/EmbarkStudios/puffin/pull/123) Fix `puffin` build for non-web wasm enviroments. +- [PR#123](https://github.com/EmbarkStudios/puffin/pull/123) Fix `puffin` build for non-web wasm environments. ## [0.14.1] - 2022-12-13 @@ -112,7 +137,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `GlobalProfiler` now store recent history and the slowest frames. -[Unreleased]: https://github.com/EmbarkStudios/puffin/compare/0.16.0...HEAD +[Unreleased]: https://github.com/EmbarkStudios/puffin/compare/0.19.0...HEAD +[0.19.0]: https://github.com/EmbarkStudios/puffin/compare/0.18.1...0.19.0 +[0.18.1]: https://github.com/EmbarkStudios/puffin/compare/0.18.0...0.18.1 +[0.18.0]: https://github.com/EmbarkStudios/puffin/compare/0.17.1...0.18.0 +[0.17.1]: https://github.com/EmbarkStudios/puffin/compare/0.17.0...0.17.1 +[0.17.0]: https://github.com/EmbarkStudios/puffin/compare/0.16.0...0.17.0 [0.16.0]: https://github.com/EmbarkStudios/puffin/compare/0.15.0...0.16.0 [0.15.0]: https://github.com/EmbarkStudios/puffin/compare/0.14.3...0.15.0 [0.14.3]: https://github.com/EmbarkStudios/puffin/compare/0.14.2...0.14.3 diff --git a/puffin/Cargo.toml b/puffin/Cargo.toml index 3376ccd9..33633851 100644 --- a/puffin/Cargo.toml +++ b/puffin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "puffin" -version = "0.16.0" +version = "0.19.0" authors = ["Embark "] license = "MIT OR Apache-2.0" description = "Simple instrumentation profiler for games" @@ -10,7 +10,7 @@ repository = "https://github.com/EmbarkStudios/puffin" readme = "README.md" categories = ["development-tools::profiling"] keywords = ["profiler", "instrumentation", "gamedev"] -include = ["**/*.rs", "Cargo.toml", "README.md"] +include = ["**/*.rs", "Cargo.toml", "README.md", "../puffin.jpg"] [package.metadata.docs.rs] all-features = true @@ -18,7 +18,7 @@ all-features = true [features] default = [] -packing = ["dep:bincode", "dep:parking_lot", "lz4", "serde"] +packing = ["dep:bincode", "lz4", "serde"] # Support lz4 compression. Fast, and lightweight dependency. # If both `lz4` and `zstd` are enabled, lz4 will be used for compression. @@ -31,21 +31,20 @@ zstd = ["dep:zstd", "dep:ruzstd"] serialization = ["packing"] # Enable this to be able to run puffin inside a browser when compiling to wasm -web = ["instant/wasm-bindgen", "dep:js-sys"] +web = ["dep:js-sys", "dep:web-time"] [dependencies] byteorder = { version = "1.0" } cfg-if = "1.0" -instant = { version = "0.1" } itertools = "0.10" once_cell = "1.0" +parking_lot = { version = "0.12"} # Optional: anyhow = { version = "1.0" } bincode = { version = "1.3", optional = true } -lz4_flex = { version = "0.10", optional = true, default-features = false } -parking_lot = { version = "0.12", optional = true } +lz4_flex = { version = "0.11", optional = true, default-features = false } serde = { version = "1.0", features = ["derive", "rc"], optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] @@ -55,6 +54,7 @@ zstd = { version = "0.12.3", optional = true } # native only [target.'cfg(target_arch = "wasm32")'.dependencies] js-sys = { version = "0.3", optional = true } ruzstd = { version = "0.4.0", optional = true } # works on wasm +web-time = { version = "0.2", optional = true } [dev-dependencies] diff --git a/puffin-imgui/release.toml b/puffin/release.toml similarity index 79% rename from puffin-imgui/release.toml rename to puffin/release.toml index 649bb105..b9379f8a 100644 --- a/puffin-imgui/release.toml +++ b/puffin/release.toml @@ -1,6 +1,6 @@ -pre-release-commit-message = "Release puffin-imgui-{{version}}" -tag-message = "Release puffin-imgui-{{version}}" -tag-name = "puffin-imgui-{{version}}" +pre-release-commit-message = "Release puffin-{{version}}" +tag-message = "Release puffin-{{version}}" +tag-name = "puffin-{{version}}" pre-release-replacements = [ { file = "CHANGELOG.md", search = "Unreleased", replace = "{{version}}" }, { file = "CHANGELOG.md", search = "\\.\\.\\.HEAD", replace = "...{{tag_name}}" }, diff --git a/puffin/src/data.rs b/puffin/src/data.rs index 79488f60..ffc9528e 100644 --- a/puffin/src/data.rs +++ b/puffin/src/data.rs @@ -4,77 +4,142 @@ //! //! Each scope start consists of: //! +//! ```ignore //! '(' byte Sentinel +//! scope id u32 Unique monolithic identifier for a scope //! time_ns i64 Time stamp of when scope started -//! id str Scope name. Human readable, e.g. a function name. Never the empty string. -//! location str File name or similar. Could be the empty string. //! data str Resource that is being processed, e.g. name of image being loaded. Could be the empty string. //! scope_size u64 Number of bytes of child scope +//! ``` //! //! This is followed by `scope_size` number of bytes of data //! containing any child scopes. The scope is then closed by: //! +//! ```ignore //! ')' byte Sentinel //! time_ns i64 Time stamp of when scope finished +//! ``` //! //! Integers are encoded in little endian. //! Strings are encoded as a single u8 length + that many bytes of UTF8. //! At the moment strings may be at most 127 bytes long. use super::*; +use anyhow::Context; use byteorder::{LittleEndian as LE, ReadBytesExt, WriteBytesExt}; use std::mem::size_of; const SCOPE_BEGIN: u8 = b'('; const SCOPE_END: u8 = b')'; -/// Used to encode number of bytes covered by a scope. -#[derive(Clone, Copy, Eq, PartialEq)] -struct ScopeSize(u64); +/// Used when parsing a Stream. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct ScopeRecord<'s> { + /// The start oif this scope in nanoseconds. + pub start_ns: NanoSecond, + /// The duration of this scope in nanoseconds. + pub duration_ns: NanoSecond, + /// e.g. function argument, like a mesh name. Optional. + /// Example: "image.png". + pub data: &'s str, +} -impl ScopeSize { - /// Special value to indicate that this profile scope was never closed - pub fn unfinished() -> Self { - Self(u64::MAX) +impl<'s> ScopeRecord<'s> { + /// The end of this scope in nanoseconds. + #[inline] + pub fn stop_ns(&self) -> NanoSecond { + self.start_ns + self.duration_ns } } -/// Errors that can happen when parsing a [`Stream`] of profile data. -#[derive(Debug)] -pub enum Error { - PrematureEnd, - InvalidStream, - ScopeNeverEnded, - InvalidOffset, - Empty, +/// Used when parsing a Stream. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct Scope<'s> { + /// Unique identifier for the profile scope. + /// More detailed scope information can be requested via [`FrameView::scope_collection()`]. + pub id: ScopeId, + /// Some dynamic data that is passed into the profiler scope. + pub record: ScopeRecord<'s>, + /// Stream offset for first child. + pub child_begin_position: u64, + /// Stream offset after last child. + pub child_end_position: u64, + /// Stream offset for next sibling (if any). + pub next_sibling_position: u64, } -pub type Result = std::result::Result; +/// Stream of profiling events from one thread. +#[derive(Clone, Default)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct Stream(Vec); + +impl Stream { + /// Returns if stream is empty. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Returns the length in bytes of this steam. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns the bytes of this steam + pub fn bytes(&self) -> &[u8] { + &self.0 + } + + /// Clears the steam of all bytes. + pub fn clear(&mut self) { + self.0.clear(); + } + + /// Extends the stream with the given bytes. + fn extend(&mut self, bytes: &[u8]) { + self.0.extend(bytes); + } +} -// ---------------------------------------------------------------------------- +impl From> for Stream { + fn from(v: Vec) -> Self { + Self(v) + } +} impl Stream { + /// Marks the beginning of the scope. /// Returns position where to write scope size once the scope is closed #[inline] - pub fn begin_scope( + pub fn begin_scope i64>( &mut self, - start_ns: NanoSecond, - id: &str, - location: &str, + now_ns: F, + scope_id: ScopeId, data: &str, - ) -> usize { + ) -> (usize, NanoSecond) { self.0.push(SCOPE_BEGIN); - self.0.write_i64::(start_ns).expect("can't fail"); - self.write_str(id); - self.write_str(location); - self.write_str(data); + self.write_scope_id(scope_id); + let time_stamp_offset = self.0.len(); + self.0 + .write_i64::(NanoSecond::default()) + .expect("can't fail"); + + self.write_str(data); // Put place-holder value for total scope size. let offset = self.0.len(); self.write_scope_size(ScopeSize::unfinished()); - offset + + // Do the timing last such that it doesn't include serialization + let mut time_stamp_dest = + &mut self.0[time_stamp_offset..time_stamp_offset + size_of::()]; + let start_ns = now_ns(); + time_stamp_dest + .write_i64::(start_ns) + .expect("can't fail"); + (offset, start_ns) } + /// Marks the end of the scope. #[inline] pub fn end_scope(&mut self, start_offset: usize, stop_ns: NanoSecond) { // Write total scope size where scope was started: @@ -101,6 +166,14 @@ impl Stream { self.0.write_u64::(nanos.0).expect("can't fail"); } + #[inline] + fn write_scope_id(&mut self, scope_id: ScopeId) { + // Could potentially use varint encoding. + self.0 + .write_u32::(scope_id.0.get()) + .expect("can't fail"); + } + #[inline] fn write_str(&mut self, s: &str) { // Future-proof: we may want to use VLQs later. @@ -111,16 +184,155 @@ impl Stream { } } -// ---------------------------------------------------------------------------- +/// A [`Stream`] plus some info about it. +#[derive(Clone)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct StreamInfo { + /// The raw profile data. + pub stream: Stream, + + /// Total number of scopes in the stream. + pub num_scopes: usize, + + /// The depth of the deepest scope. + /// `0` mean no scopes, `1` some scopes without children, etc. + pub depth: usize, + + /// The smallest and largest nanosecond value in the stream. + /// + /// The default value is ([`NanoSecond::MAX`], [`NanoSecond::MIN`]) which indicates an empty stream. + pub range_ns: (NanoSecond, NanoSecond), +} + +impl Default for StreamInfo { + fn default() -> Self { + Self { + stream: Default::default(), + num_scopes: 0, + depth: 0, + range_ns: (NanoSecond::MAX, NanoSecond::MIN), + } + } +} + +impl StreamInfo { + /// Parse a stream to count the depth, number of scopes in it etc. + /// + /// Try to avoid calling this, and instead keep score while collecting a [`StreamInfo`]. + pub fn parse(stream: Stream) -> Result { + let top_scopes = Reader::from_start(&stream).read_top_scopes()?; + if top_scopes.is_empty() { + Ok(StreamInfo { + stream, + num_scopes: 0, + depth: 0, + range_ns: (NanoSecond::MAX, NanoSecond::MIN), + }) + } else { + let (num_scopes, depth) = Reader::count_scope_and_depth(&stream)?; + let min_ns = top_scopes.first().unwrap().record.start_ns; + let max_ns = top_scopes.last().unwrap().record.stop_ns(); + + Ok(StreamInfo { + stream, + num_scopes, + depth, + range_ns: (min_ns, max_ns), + }) + } + } + + /// Extends this [`StreamInfo`] with another [`StreamInfo`]. + pub fn extend(&mut self, other: &StreamInfoRef<'_>) { + self.stream.extend(other.stream); + self.num_scopes += other.num_scopes; + self.depth = self.depth.max(other.depth); + self.range_ns.0 = self.range_ns.0.min(other.range_ns.0); + self.range_ns.1 = self.range_ns.1.max(other.range_ns.1); + } + + /// Clears the contents of this [`StreamInfo`]. + pub fn clear(&mut self) { + let Self { + stream, + num_scopes, + depth, + range_ns, + } = self; + stream.clear(); + *num_scopes = 0; + *depth = 0; + *range_ns = (NanoSecond::MAX, NanoSecond::MIN); + } + + /// Returns a reference to the contents of this [`StreamInfo`]. + pub fn as_stream_into_ref(&self) -> StreamInfoRef<'_> { + StreamInfoRef { + stream: self.stream.bytes(), + num_scopes: self.num_scopes, + depth: self.depth, + range_ns: self.range_ns, + } + } +} + +/// A reference to the contents of a [`StreamInfo`]. +#[derive(Clone, Copy)] +pub struct StreamInfoRef<'a> { + /// The raw profile data. + pub stream: &'a [u8], + + /// Total number of scopes in the stream. + pub num_scopes: usize, + + /// The depth of the deepest scope. + /// `0` mean no scopes, `1` some scopes without children, etc. + pub depth: usize, + + /// The smallest and largest nanosecond value in the stream. + /// + /// The default value is ([`NanoSecond::MAX`], [`NanoSecond::MIN`]) which indicates an empty stream. + pub range_ns: (NanoSecond, NanoSecond), +} + +/// Used to encode number of bytes covered by a scope. +#[derive(Clone, Copy, Eq, PartialEq)] +struct ScopeSize(u64); + +impl ScopeSize { + /// Special value to indicate that this profile scope was never closed + pub fn unfinished() -> Self { + Self(u64::MAX) + } +} + +/// Errors that can happen when parsing a [`Stream`] of profile data. +#[derive(Debug)] +pub enum Error { + /// Could not read data from the stream because it ended prematurely. + PrematureEnd, + /// The stream is invalid. + InvalidStream, + /// The stream was not ended. + ScopeNeverEnded, + /// The offset into the stream is invalid. + InvalidOffset, + /// Empty stream. + Empty, +} +/// Custom puffin result type. +pub type Result = std::result::Result; /// Parses a [`Stream`] of profiler data. pub struct Reader<'s>(std::io::Cursor<&'s [u8]>); impl<'s> Reader<'s> { + /// Returns a reader that starts reading from the start of the stream. pub fn from_start(stream: &'s Stream) -> Self { Self(std::io::Cursor::new(&stream.0[..])) } + /// Returns a reader that starts reading from an offset into the stream. pub fn with_offset(stream: &'s Stream, offset: u64) -> Result { if offset <= stream.len() as u64 { let mut cursor = std::io::Cursor::new(&stream.0[..]); @@ -139,14 +351,11 @@ impl<'s> Reader<'s> { self.parse_u8() .expect("swallowing already peeked SCOPE_BEGIN"); } - Some(_) | None => { - return Ok(None); - } + Some(_) | None => return Ok(None), } + let scope_id = self.parse_scope_id()?; let start_ns = self.parse_nanos()?; - let id = self.parse_string()?; - let location = self.parse_string()?; let data = self.parse_string()?; let scope_size = self.parse_scope_size()?; if scope_size == ScopeSize::unfinished() { @@ -165,11 +374,10 @@ impl<'s> Reader<'s> { } Ok(Some(Scope { - record: Record { + id: scope_id, + record: ScopeRecord { start_ns, duration_ns: stop_ns - start_ns, - id, - location, data, }, child_begin_position, @@ -199,6 +407,15 @@ impl<'s> Reader<'s> { self.0.read_u8().map_err(|_err| Error::PrematureEnd) } + fn parse_scope_id(&mut self) -> Result { + self.0 + .read_u32::() + .context("Can not parse scope id") + .and_then(|x| NonZeroU32::new(x).context("Not a `NonZeroU32` scope id")) + .map(ScopeId) + .map_err(|_err| Error::PrematureEnd) + } + fn parse_nanos(&mut self) -> Result { self.0.read_i64::().map_err(|_err| Error::PrematureEnd) } @@ -265,8 +482,6 @@ fn longest_valid_utf8_prefix(data: &[u8]) -> &str { } } -// ---------------------------------------------------------------------------- - /// Read each top-level sibling scopes impl<'s> Iterator for Reader<'s> { type Item = Result>; @@ -275,16 +490,32 @@ impl<'s> Iterator for Reader<'s> { } } -// ---------------------------------------------------------------------------- +#[test] +fn write_scope() { + let mut stream: Stream = Stream::default(); + let start = stream.begin_scope(|| 100, ScopeId::new(1), "data"); + stream.end_scope(start.0, 300); + + let scopes = Reader::from_start(&stream).read_top_scopes().unwrap(); + assert_eq!(scopes.len(), 1); + assert_eq!( + scopes[0].record, + ScopeRecord { + start_ns: 100, + duration_ns: 200, + data: "data" + } + ); +} #[test] fn test_profile_data() { let stream = { let mut stream = Stream::default(); - let t0 = stream.begin_scope(100, "top", "top.rs", "data_top"); - let m1 = stream.begin_scope(200, "middle_0", "middle.rs", "data_middle_0"); + let (t0, _) = stream.begin_scope(|| 100, ScopeId::new(1), "data_top"); + let (m1, _) = stream.begin_scope(|| 200, ScopeId::new(2), "data_middle_0"); stream.end_scope(m1, 300); - let m1 = stream.begin_scope(300, "middle_1", "middle.rs:42", "data_middle_1"); + let (m1, _) = stream.begin_scope(|| 300, ScopeId::new(3), "data_middle_1"); stream.end_scope(m1, 400); stream.end_scope(t0, 400); stream @@ -292,46 +523,36 @@ fn test_profile_data() { let top_scopes = Reader::from_start(&stream).read_top_scopes().unwrap(); assert_eq!(top_scopes.len(), 1); - let middle_scopes = Reader::with_offset(&stream, top_scopes[0].child_begin_position) - .unwrap() - .read_top_scopes() - .unwrap(); - assert_eq!( top_scopes[0].record, - Record { + ScopeRecord { start_ns: 100, duration_ns: 300, - id: "top", - location: "top.rs", - data: "data_top", + data: "data_top" } ); - assert_eq!( - top_scopes[0].next_sibling_position, - stream.len() as u64, - "Top scope has no siblings" - ); + + let middle_scopes = Reader::with_offset(&stream, top_scopes[0].child_begin_position) + .unwrap() + .read_top_scopes() + .unwrap(); assert_eq!(middle_scopes.len(), 2); + assert_eq!( middle_scopes[0].record, - Record { + ScopeRecord { start_ns: 200, duration_ns: 100, - id: "middle_0", - location: "middle.rs", - data: "data_middle_0", + data: "data_middle_0" } ); assert_eq!( middle_scopes[1].record, - Record { + ScopeRecord { start_ns: 300, duration_ns: 100, - id: "middle_1", - location: "middle.rs:42", - data: "data_middle_1", + data: "data_middle_1" } ); } diff --git a/puffin/src/frame_data.rs b/puffin/src/frame_data.rs index 6eed46ed..fd55c323 100644 --- a/puffin/src/frame_data.rs +++ b/puffin/src/frame_data.rs @@ -1,5 +1,5 @@ +use crate::ScopeDetails; use crate::{Error, FrameIndex, NanoSecond, Result, StreamInfo, ThreadInfo}; - #[cfg(feature = "packing")] use parking_lot::RwLock; @@ -31,11 +31,14 @@ pub struct FrameMeta { /// /// More often encoded as a [`FrameData`]. pub struct UnpackedFrameData { + /// Frame metadata. pub meta: FrameMeta, + /// The streams of profiling data for each thread. pub thread_streams: ThreadStreams, } impl UnpackedFrameData { + /// Create a new [`UnpackedFrameData`]. pub fn new( frame_index: FrameIndex, thread_streams: BTreeMap, @@ -72,14 +75,17 @@ impl UnpackedFrameData { } } + /// The index of this frame. pub fn frame_index(&self) -> u64 { self.meta.frame_index } + /// The range in nanoseconds of the entire profile frame. pub fn range_ns(&self) -> (NanoSecond, NanoSecond) { self.meta.range_ns } + /// The duration in nanoseconds of the entire profile frame. pub fn duration_ns(&self) -> NanoSecond { let (min, max) = self.meta.range_ns; max - min @@ -95,6 +101,11 @@ impl UnpackedFrameData { #[cfg(not(feature = "packing"))] pub struct FrameData { unpacked_frame: Arc, + /// Scopes that were registered during this frame. + pub scope_delta: Vec>, + /// Does [`Self::scope_delta`] contain all the scopes up to this point? + /// If `false`, it just contains the new scopes since last frame data. + pub full_delta: bool, } #[cfg(not(feature = "packing"))] @@ -102,31 +113,49 @@ pub enum Never {} #[cfg(not(feature = "packing"))] impl FrameData { + /// Create a new [`FrameData`]. pub fn new( frame_index: FrameIndex, thread_streams: BTreeMap, + scope_delta: Vec>, + full_delta: bool, ) -> Result { - Ok(Self::from_unpacked(Arc::new(UnpackedFrameData::new( - frame_index, - thread_streams, - )?))) - } - - fn from_unpacked(unpacked_frame: Arc) -> Self { - Self { unpacked_frame } + Ok(Self::from_unpacked( + Arc::new(UnpackedFrameData::new(frame_index, thread_streams)?), + scope_delta, + full_delta, + )) + } + + fn from_unpacked( + unpacked_frame: Arc, + scope_delta: Vec>, + full_delta: bool, + ) -> Self { + Self { + unpacked_frame, + scope_delta, + full_delta, + } } + /// Returns meta data from this frame. #[inline] pub fn meta(&self) -> &FrameMeta { &self.unpacked_frame.meta } + /// Always returns `None`. pub fn packed_size(&self) -> Option { None } + + /// Number of bytes used when unpacked. pub fn unpacked_size(&self) -> Option { Some(self.unpacked_frame.meta.num_bytes) } + + /// Bytes currently used by the unpacked data. pub fn bytes_of_ram_used(&self) -> usize { self.unpacked_frame.meta.num_bytes } @@ -137,15 +166,22 @@ impl FrameData { packed_size: None, } } + /// Always returns `false`. pub fn has_packed(&self) -> bool { false } + + /// Always returns `true`. pub fn has_unpacked(&self) -> bool { true } + + /// Return the unpacked data. pub fn unpacked(&self) -> std::result::Result, Never> { Ok(self.unpacked_frame.clone()) } + + /// Does nothing because this [`FrameData`] is unpacked by default. pub fn pack(&self) {} } @@ -157,19 +193,24 @@ compile_error!( // ---------------------------------------------------------------------------- /// See for pros-and-cons of different compression algorithms. +#[cfg(feature = "packing")] #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum CompressionKind { Uncompressed = 0, /// Very fast, and lightweight dependency + #[allow(dead_code)] // with some feature sets Lz4 = 1, /// Big dependency, slow compression, but compresses better than lz4 + #[allow(dead_code)] // with some feature sets Zstd = 2, } +#[cfg(feature = "packing")] impl CompressionKind { + #[cfg(feature = "serialization")] fn from_u8(value: u8) -> anyhow::Result { match value { 0 => Ok(Self::Uncompressed), @@ -285,7 +326,17 @@ impl PackedStreams { #[cfg(feature = "packing")] pub struct FrameData { meta: FrameMeta, - inner: RwLock, + + /// Encapsulates the frame data in its current state, which can be + /// uncompressed, compressed, or a combination of both + data: RwLock, + + /// Scopes that were registered during this frame. + pub scope_delta: Vec>, + + /// Does [`Self::scope_delta`] contain all the scopes up to this point? + /// If `false`, it just contains the new scopes since last frame data. + pub full_delta: bool, } #[derive(Clone, Copy, Debug)] @@ -297,7 +348,7 @@ pub struct PackingInfo { } #[cfg(feature = "packing")] -enum Inner { +enum FrameDataState { /// Unpacked data. Unpacked(Arc), @@ -309,30 +360,34 @@ enum Inner { } #[cfg(feature = "packing")] -impl Inner { +impl FrameDataState { fn unpacked_size(&self) -> Option { match self { - Inner::Packed(_) => None, - Inner::Unpacked(unpacked) | Inner::Both(unpacked, _) => Some(unpacked.meta.num_bytes), + FrameDataState::Packed(_) => None, + FrameDataState::Unpacked(unpacked) | FrameDataState::Both(unpacked, _) => { + Some(unpacked.meta.num_bytes) + } } } fn unpacked(&self) -> Option> { match self { - Inner::Packed(_) => None, - Inner::Unpacked(unpacked) | Inner::Both(unpacked, _) => Some(unpacked.clone()), + FrameDataState::Packed(_) => None, + FrameDataState::Unpacked(unpacked) | FrameDataState::Both(unpacked, _) => { + Some(unpacked.clone()) + } } } fn unpack(&mut self, unpacked: Arc) { let temp = std::mem::replace( self, - Inner::Packed(PackedStreams::new(CompressionKind::Uncompressed, vec![])), + FrameDataState::Packed(PackedStreams::new(CompressionKind::Uncompressed, vec![])), ); - if let Inner::Packed(packed) = temp { + if let FrameDataState::Packed(packed) = temp { // Transform only if we don't have unpacked already - *self = Inner::Both(unpacked, packed); + *self = FrameDataState::Both(unpacked, packed); } else { // Restore the original value if it was not Inner::Packed *self = temp; @@ -341,27 +396,31 @@ impl Inner { fn packed_size(&self) -> Option { match self { - Inner::Unpacked(_) => None, - Inner::Packed(packed) | Inner::Both(_, packed) => Some(packed.num_bytes()), + FrameDataState::Unpacked(_) => None, + FrameDataState::Packed(packed) | FrameDataState::Both(_, packed) => { + Some(packed.num_bytes()) + } } } fn packed(&self) -> Option<&PackedStreams> { match self { - Inner::Unpacked(_) => None, - Inner::Packed(packed) | Inner::Both(_, packed) => Some(packed), + FrameDataState::Unpacked(_) => None, + FrameDataState::Packed(packed) | FrameDataState::Both(_, packed) => Some(packed), } } fn pack_and_remove(&mut self) { - if let Inner::Unpacked(ref unpacked) | Inner::Both(ref unpacked, _) = *self { + if let FrameDataState::Unpacked(ref unpacked) | FrameDataState::Both(ref unpacked, _) = + *self + { let packed = PackedStreams::pack(&unpacked.thread_streams); *self = Self::Packed(packed); } } fn pack_and_keep(&mut self) { - if let Inner::Unpacked(ref unpacked) = *self { + if let FrameDataState::Unpacked(ref unpacked) = *self { let packed = PackedStreams::pack(&unpacked.thread_streams); *self = Self::Packed(packed); } @@ -372,11 +431,11 @@ impl Inner { } fn has_packed(&self) -> bool { - matches!(self, Inner::Packed(_) | Inner::Both(..)) + matches!(self, FrameDataState::Packed(_) | FrameDataState::Both(..)) } fn has_unpacked(&self) -> bool { - matches!(self, Inner::Unpacked(_) | Inner::Both(..)) + matches!(self, FrameDataState::Unpacked(_) | FrameDataState::Both(..)) } fn packing_info(&self) -> PackingInfo { @@ -389,23 +448,34 @@ impl Inner { #[cfg(feature = "packing")] impl FrameData { + /// Create a new [`FrameData`]. pub fn new( frame_index: FrameIndex, thread_streams: BTreeMap, + scope_delta: Vec>, + full_delta: bool, ) -> Result { - Ok(Self::from_unpacked(Arc::new(UnpackedFrameData::new( - frame_index, - thread_streams, - )?))) - } - - fn from_unpacked(unpacked_frame: Arc) -> Self { + Ok(Self::from_unpacked( + Arc::new(UnpackedFrameData::new(frame_index, thread_streams)?), + scope_delta, + full_delta, + )) + } + + fn from_unpacked( + unpacked_frame: Arc, + scope_delta: Vec>, + full_delta: bool, + ) -> Self { Self { meta: unpacked_frame.meta, - inner: RwLock::new(Inner::Unpacked(unpacked_frame)), + data: RwLock::new(FrameDataState::Unpacked(unpacked_frame)), + scope_delta, + full_delta, } } + /// Returns meta data from this frame. #[inline] pub fn meta(&self) -> &FrameMeta { &self.meta @@ -413,27 +483,27 @@ impl FrameData { /// Number of bytes used by the packed data, if packed. pub fn packed_size(&self) -> Option { - self.inner.read().packed_size() + self.data.read().packed_size() } /// Number of bytes used when unpacked, if known. pub fn unpacked_size(&self) -> Option { - self.inner.read().unpacked_size() + self.data.read().unpacked_size() } /// bytes currently used by the unpacked and packed data. pub fn bytes_of_ram_used(&self) -> usize { - self.inner.read().bytes_of_ram_used() + self.data.read().bytes_of_ram_used() } /// Do we have a packed version stored internally? pub fn has_packed(&self) -> bool { - self.inner.read().has_packed() + self.data.read().has_packed() } /// Do we have a unpacked version stored internally? pub fn has_unpacked(&self) -> bool { - self.inner.read().has_unpacked() + self.data.read().has_unpacked() } /// Provides an overview of the frame's packing status. @@ -443,7 +513,7 @@ impl FrameData { /// minimize the number of lock accesses by consolidating information retrieval into a single /// operation. pub fn packing_info(&self) -> PackingInfo { - self.inner.read().packing_info() + self.data.read().packing_info() } /// Return the unpacked data. @@ -453,10 +523,10 @@ impl FrameData { /// Returns `Err` if failing to decode the packed data. pub fn unpacked(&self) -> anyhow::Result> { let unpacked = { - let inner_guard = self.inner.read(); - let Inner::Packed(ref packed) = *inner_guard else { + let inner_guard = self.data.read(); + let FrameDataState::Packed(ref packed) = *inner_guard else { // Safe to unwrap, variant has to contain unpacked if NOT `Packed` - return Ok(self.inner.read().unpacked().unwrap()); + return Ok(self.data.read().unpacked().unwrap()); }; crate::profile_scope!("unpack_puffin_frame"); @@ -467,41 +537,56 @@ impl FrameData { }) }; - self.inner.write().unpack(unpacked.clone()); + self.data.write().unpack(unpacked.clone()); Ok(unpacked) } /// Make the [`FrameData`] use up less memory. /// Idempotent. pub fn pack(&self) { - self.inner.write().pack_and_remove(); + self.data.write().pack_and_remove(); } /// Create a packed storage without freeing the unpacked storage. fn create_packed(&self) { - self.inner.write().pack_and_keep(); + self.data.write().pack_and_keep(); } /// Writes one [`FrameData`] into a stream, prefixed by its length ([`u32`] le). #[cfg(not(target_arch = "wasm32"))] // compression not supported on wasm #[cfg(feature = "serialization")] - pub fn write_into(&self, write: &mut impl std::io::Write) -> anyhow::Result<()> { + pub fn write_into( + &self, + scope_collection: &crate::ScopeCollection, + send_all_scopes: bool, + write: &mut impl std::io::Write, + ) -> anyhow::Result<()> { use bincode::Options as _; - use byteorder::WriteBytesExt as _; + use byteorder::{WriteBytesExt as _, LE}; + let meta_serialized = bincode::options().serialize(&self.meta)?; - write.write_all(b"PFD3")?; + write.write_all(b"PFD4")?; write.write_all(&(meta_serialized.len() as u32).to_le_bytes())?; write.write_all(&meta_serialized)?; self.create_packed(); - let packed_streams_lock = self.inner.read(); + let packed_streams_lock = self.data.read(); let packed_streams = packed_streams_lock.packed().unwrap(); // We just called create_packed write.write_all(&(packed_streams.num_bytes() as u32).to_le_bytes())?; write.write_u8(packed_streams.compression_kind as u8)?; write.write_all(&packed_streams.bytes)?; + let to_serialize_scopes: Vec<_> = if send_all_scopes { + scope_collection.scopes_by_id().values().cloned().collect() + } else { + self.scope_delta.clone() + }; + + let serialized_scopes = bincode::options().serialize(&to_serialize_scopes)?; + write.write_u32::(serialized_scopes.len() as u32)?; + write.write_all(&serialized_scopes)?; Ok(()) } @@ -513,7 +598,7 @@ impl FrameData { pub fn read_next(read: &mut impl std::io::Read) -> anyhow::Result> { use anyhow::Context as _; use bincode::Options as _; - use byteorder::ReadBytesExt; + use byteorder::{ReadBytesExt, LE}; let mut header = [0_u8; 4]; if let Err(err) = read.read_exact(&mut header) { @@ -554,7 +639,11 @@ impl FrameData { } fn into_frame_data(self) -> FrameData { - FrameData::from_unpacked(Arc::new(self.into_unpacked_frame_data())) + FrameData::from_unpacked( + Arc::new(self.into_unpacked_frame_data()), + Default::default(), + false, + ) } } @@ -612,7 +701,9 @@ impl FrameData { Ok(Some(Self { meta, - inner: RwLock::new(Inner::Packed(packed_streams)), + data: RwLock::new(FrameDataState::Packed(packed_streams)), + scope_delta: Default::default(), + full_delta: false, })) } else if &header == b"PFD3" { // Added 2023-05-13: CompressionKind field @@ -641,7 +732,48 @@ impl FrameData { Ok(Some(Self { meta, - inner: RwLock::new(Inner::Packed(packed_streams)), + data: RwLock::new(FrameDataState::Packed(packed_streams)), + scope_delta: Default::default(), + full_delta: false, + })) + } else if &header == b"PFD4" { + // Added 2024-01-08: Split up stream scope details from the record stream. + let meta_length = read.read_u32::()? as usize; + let meta = { + let mut meta = vec![0_u8; meta_length]; + read.read_exact(&mut meta)?; + bincode::options() + .deserialize(&meta) + .context("bincode deserialize")? + }; + + let streams_compressed_length = read.read_u32::()? as usize; + let compression_kind = CompressionKind::from_u8(read.read_u8()?)?; + let streams_compressed = { + let mut streams_compressed = vec![0_u8; streams_compressed_length]; + read.read_exact(&mut streams_compressed)?; + PackedStreams::new(compression_kind, streams_compressed) + }; + + let serialized_scope_len = read.read_u32::()?; + let deserialized_scopes: Vec = { + let mut serialized_scopes = vec![0; serialized_scope_len as usize]; + read.read_exact(&mut serialized_scopes)?; + bincode::options() + .deserialize_from(serialized_scopes.as_slice()) + .context("Can not deserialize scope details")? + }; + + let new_scopes: Vec<_> = deserialized_scopes + .into_iter() + .map(|x| Arc::new(x.clone())) + .collect(); + + Ok(Some(Self { + meta, + data: RwLock::new(FrameDataState::Packed(streams_compressed)), + scope_delta: new_scopes, + full_delta: false, })) } else { anyhow::bail!("Failed to decode: this data is newer than this reader. Please update your puffin version!"); @@ -663,14 +795,17 @@ impl FrameData { // ---------------------------------------------------------------------------- impl FrameData { + /// The index of this frame. pub fn frame_index(&self) -> u64 { self.meta().frame_index } + /// The range in nanoseconds of the entire profile frame. pub fn range_ns(&self) -> (NanoSecond, NanoSecond) { self.meta().range_ns } + /// The duration in nanoseconds of the entire profile frame. pub fn duration_ns(&self) -> NanoSecond { let (min, max) = self.meta().range_ns; max - min diff --git a/puffin/src/global_profiler.rs b/puffin/src/global_profiler.rs new file mode 100644 index 00000000..44185f0b --- /dev/null +++ b/puffin/src/global_profiler.rs @@ -0,0 +1,178 @@ +use std::{collections::BTreeMap, sync::Arc}; + +use once_cell::sync::Lazy; + +use crate::{ + fetch_add_scope_id, Error, FrameData, FrameIndex, FrameSinkId, ScopeCollection, ScopeDetails, + ScopeId, StreamInfo, StreamInfoRef, ThreadInfo, +}; + +/// Add these to [`GlobalProfiler`] with [`GlobalProfiler::add_sink()`]. +pub type FrameSink = Box) + Send>; + +/// Singleton. Collects profiling data from multiple threads +/// and passes them on to different [`FrameSink`]s. +pub struct GlobalProfiler { + current_frame_index: FrameIndex, + current_frame: BTreeMap, + + next_sink_id: FrameSinkId, + sinks: std::collections::HashMap, + // When true will propagate a full snapshot from `scope_collection` to every sink. + propagate_all_scope_details: bool, + // The new scopes' details, or also the first time macro or external library detected a scope. + new_scopes: Vec>, + // Store an absolute collection of scope details such that sinks can request a total state by setting `propagate_all_scope_details`. + // This should not be mutable accessible to external applications as frame views store there own copy. + scope_collection: ScopeCollection, +} + +impl Default for GlobalProfiler { + fn default() -> Self { + Self { + current_frame_index: 0, + current_frame: Default::default(), + next_sink_id: FrameSinkId(1), + sinks: Default::default(), + propagate_all_scope_details: Default::default(), + new_scopes: Default::default(), + scope_collection: Default::default(), + } + } +} + +impl GlobalProfiler { + /// Access to the global profiler singleton. + pub fn lock() -> parking_lot::MutexGuard<'static, Self> { + static GLOBAL_PROFILER: Lazy> = + Lazy::new(Default::default); + GLOBAL_PROFILER.lock() + } + + /// You need to call this once at the start of every frame. + /// + /// It is fine to call this from within a profile scope. + /// + /// This takes all completed profiling scopes from all threads, + /// and sends it to the sinks. + pub fn new_frame(&mut self) { + let current_frame_index = self.current_frame_index; + self.current_frame_index += 1; + + let mut scope_deltas = Vec::with_capacity(self.new_scopes.len()); + + // Firstly add the new registered scopes. + for scope_detail in self.new_scopes.drain(..) { + scope_deltas.push(scope_detail); + } + + let current_frame_scope = std::mem::take(&mut self.current_frame); + + // Secondly add a full snapshot of all scopes if requested. + // Could potentially do this per sink. + let propagate_full_delta = std::mem::take(&mut self.propagate_all_scope_details); + + if propagate_full_delta { + scope_deltas.extend(self.scope_collection.scopes_by_id().values().cloned()); + } + + let new_frame = match FrameData::new( + current_frame_index, + current_frame_scope, + scope_deltas, + propagate_full_delta, + ) { + Ok(new_frame) => Arc::new(new_frame), + Err(Error::Empty) => { + return; // don't warn about empty frames, just ignore them + } + Err(err) => { + eprintln!("puffin ERROR: Bad frame: {err:?}"); + return; + } + }; + + self.add_frame(new_frame); + } + + /// Manually add frame data. + pub fn add_frame(&mut self, new_frame: Arc) { + for delta in &new_frame.scope_delta { + self.scope_collection.insert(delta.clone()); + } + + for sink in self.sinks.values() { + sink(new_frame.clone()); + } + } + + /// Inserts user scopes into puffin. + /// Returns the scope id for every inserted scope in the same order as input slice. + /// + /// Scopes details should only be registered once for each scope and need be inserted before being reported to puffin. + /// This function is relevant when you're registering measurement not performed using the puffin profiler macros. + /// Scope id is always supposed to be `None` as it will be set by puffin. + pub fn register_user_scopes(&mut self, scopes: &[ScopeDetails]) -> Vec { + let mut new_scopes = Vec::with_capacity(scopes.len()); + for scope_detail in scopes { + let new_scope_id = fetch_add_scope_id(); + let scope = self.scope_collection.insert(Arc::new( + (*scope_detail).clone().with_scope_id(new_scope_id), + )); + new_scopes.push(scope); + } + let new_scope_ids = new_scopes.iter().filter_map(|x| x.scope_id).collect(); + self.new_scopes.extend(new_scopes); + new_scope_ids + } + + /// Reports some profiling data. Called from [`ThreadProfiler`]. + pub fn report( + &mut self, + info: ThreadInfo, + scope_details: &[ScopeDetails], + stream_scope_times: &StreamInfoRef<'_>, + ) { + if !scope_details.is_empty() { + // Here we can run slightly heavy logic as its only ran once for each scope. + self.new_scopes + .extend(scope_details.iter().map(|x| Arc::new(x.clone()))); + } + + self.current_frame + .entry(info) + .or_default() + .extend(stream_scope_times); + } + + /// Reports user scopes to puffin profiler. + /// Every scope reported should first be registered by [`Self::register_user_scopes`]. + pub fn report_user_scopes(&mut self, info: ThreadInfo, stream_scope_times: &StreamInfoRef<'_>) { + self.current_frame + .entry(info) + .or_default() + .extend(stream_scope_times); + } + + /// Tells [`GlobalProfiler`] to call this function with each new finished frame. + /// + /// The returned [`FrameSinkId`] can be used to remove the sink with [`Self::remove_sink()`]. + /// If the sink is registered later in the application make sure to call [`Self::emit_scope_snapshot()`] to send a snapshot of all scopes. + pub fn add_sink(&mut self, sink: FrameSink) -> FrameSinkId { + let id = self.next_sink_id; + self.next_sink_id.0 += 1; + self.sinks.insert(id, sink); + id + } + + /// Removes a sink from the global profiler. + pub fn remove_sink(&mut self, id: FrameSinkId) -> Option { + self.sinks.remove(&id) + } + + /// Sends a snapshot of all scopes to all sinks via the frame data. + /// This is useful for if a sink is initialized after scopes are registered. + pub fn emit_scope_snapshot(&mut self) { + self.propagate_all_scope_details = true; + } +} diff --git a/puffin/src/lib.rs b/puffin/src/lib.rs index ef6638c8..4687bc06 100644 --- a/puffin/src/lib.rs +++ b/puffin/src/lib.rs @@ -19,103 +19,30 @@ //! # fn slow_code(){} //! ``` -// BEGIN - Embark standard lints v5 for Rust 1.55+ -// do not change or add/remove here, but one can add exceptions after this section -// for more info see: -#![deny(unsafe_code)] -#![warn( - clippy::all, - clippy::await_holding_lock, - clippy::char_lit_as_u8, - clippy::checked_conversions, - clippy::dbg_macro, - clippy::debug_assert_with_mut_call, - clippy::disallowed_methods, - clippy::disallowed_types, - clippy::doc_markdown, - clippy::empty_enum, - clippy::enum_glob_use, - clippy::exit, - clippy::expl_impl_clone_on_copy, - clippy::explicit_deref_methods, - clippy::explicit_into_iter_loop, - clippy::fallible_impl_from, - clippy::filter_map_next, - clippy::flat_map_option, - clippy::float_cmp_const, - clippy::fn_params_excessive_bools, - clippy::from_iter_instead_of_collect, - clippy::if_let_mutex, - clippy::implicit_clone, - clippy::imprecise_flops, - clippy::inefficient_to_string, - clippy::invalid_upcast_comparisons, - clippy::large_digit_groups, - clippy::large_stack_arrays, - clippy::large_types_passed_by_value, - clippy::let_unit_value, - clippy::linkedlist, - clippy::lossy_float_literal, - clippy::macro_use_imports, - clippy::manual_ok_or, - clippy::map_err_ignore, - clippy::map_flatten, - clippy::map_unwrap_or, - clippy::match_on_vec_items, - clippy::match_same_arms, - clippy::match_wild_err_arm, - clippy::match_wildcard_for_single_variants, - clippy::mem_forget, - clippy::mismatched_target_os, - clippy::missing_enforced_import_renames, - clippy::mut_mut, - clippy::mutex_integer, - clippy::needless_borrow, - clippy::needless_continue, - clippy::needless_for_each, - clippy::option_option, - clippy::path_buf_push_overwrite, - clippy::ptr_as_ptr, - clippy::rc_mutex, - clippy::ref_option_ref, - clippy::rest_pat_in_fully_bound_structs, - clippy::same_functions_in_if_condition, - clippy::semicolon_if_nothing_returned, - clippy::single_match_else, - clippy::string_add_assign, - clippy::string_add, - clippy::string_lit_as_bytes, - clippy::string_to_string, - clippy::todo, - clippy::trait_duplication_in_bounds, - clippy::unimplemented, - clippy::unnested_or_patterns, - clippy::unused_self, - clippy::useless_transmute, - clippy::verbose_file_reads, - clippy::zero_sized_map_values, - future_incompatible, - nonstandard_style, - rust_2018_idioms -)] -// END - Embark standard lints v0.5 for Rust 1.55+ -// crate-specific exceptions: +#![forbid(unsafe_code)] +#![deny(missing_docs)] mod data; mod frame_data; +mod global_profiler; mod merge; mod profile_view; +mod scope_details; +mod thread_profiler; +mod utils; + +use std::num::NonZeroU32; +use std::sync::atomic::{AtomicBool, Ordering}; -pub use data::*; +/// TODO: Improve encapsulation. +pub use data::{Error, Reader, Result, Scope, ScopeRecord, Stream, StreamInfo, StreamInfoRef}; pub use frame_data::{FrameData, FrameMeta, UnpackedFrameData}; -pub use merge::*; +pub use global_profiler::{FrameSink, GlobalProfiler}; +pub use merge::{merge_scopes_for_thread, MergeScope}; pub use profile_view::{select_slowest, FrameStats, FrameView, GlobalFrameView}; - -use std::collections::BTreeMap; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - Arc, -}; +pub use scope_details::{ScopeCollection, ScopeDetails, ScopeType}; +pub use thread_profiler::{ThreadInfo, ThreadProfiler}; +pub use utils::{clean_function_name, short_file_name, shorten_rust_function_name, type_name_of}; static MACROS_ON: AtomicBool = AtomicBool::new(false); @@ -135,404 +62,26 @@ pub fn are_scopes_on() -> bool { /// All times are expressed as integer nanoseconds since some event. pub type NanoSecond = i64; -// ---------------------------------------------------------------------------- - -/// Stream of profiling events from one thread. -#[derive(Clone, Default)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct Stream(Vec); - -impl Stream { - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn bytes(&self) -> &[u8] { - &self.0 - } - - pub fn clear(&mut self) { - self.0.clear(); - } - - fn extend(&mut self, bytes: &[u8]) { - self.0.extend(bytes); - } -} - -impl From> for Stream { - fn from(v: Vec) -> Self { - Self(v) - } -} - -// ---------------------------------------------------------------------------- - -/// Used when parsing a Stream. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Record<'s> { - pub start_ns: NanoSecond, - pub duration_ns: NanoSecond, - - /// e.g. function name. Mandatory. Used to identify records. - /// Does not need to be globally unique, just unique in the parent scope. - /// Example: "load_image" - pub id: &'s str, - - /// e.g. file name. Optional. Used for finding the location of the profiler scope. - /// Example: "my_library/image_loader.rs:52" - pub location: &'s str, - - /// e.g. function argument, like a mesh name. Optional. - /// Example: "image.png". - pub data: &'s str, -} - -impl<'s> Record<'s> { - #[inline] - pub fn stop_ns(&self) -> NanoSecond { - self.start_ns + self.duration_ns - } -} - -/// Used when parsing a Stream. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Scope<'s> { - pub record: Record<'s>, - /// Stream offset for first child. - pub child_begin_position: u64, - /// Stream offset after last child. - pub child_end_position: u64, - /// Stream offset for next sibling (if any). - pub next_sibling_position: u64, -} - -/// Used to identify one source of profiling data. -#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct ThreadInfo { - /// Useful for ordering threads. - pub start_time_ns: Option, - /// Name of the thread - pub name: String, -} - -// ---------------------------------------------------------------------------- - +/// An incremental monolithic counter to identify frames. pub type FrameIndex = u64; -/// A [`Stream`] plus some info about it. -#[derive(Clone)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] -pub struct StreamInfo { - /// The raw profile data. - pub stream: Stream, - - /// Total number of scopes in the stream. - pub num_scopes: usize, - - /// The depth of the deepest scope. - /// `0` mean no scopes, `1` some scopes without children, etc. - pub depth: usize, - - /// The smallest and largest nanosecond value in the stream. - /// - /// The default value is ([`NanoSecond::MAX`], [`NanoSecond::MIN`]) which indicates an empty stream. - pub range_ns: (NanoSecond, NanoSecond), -} - -impl Default for StreamInfo { - fn default() -> Self { - Self { - stream: Default::default(), - num_scopes: 0, - depth: 0, - range_ns: (NanoSecond::MAX, NanoSecond::MIN), - } - } -} - -impl StreamInfo { - /// Parse a stream to count the depth, number of scopes in it etc. - /// - /// Try to avoid calling this, and instead keep score while collecting a [`StreamInfo`]. - pub fn parse(stream: Stream) -> Result { - let top_scopes = Reader::from_start(&stream).read_top_scopes()?; - if top_scopes.is_empty() { - Ok(StreamInfo { - stream, - num_scopes: 0, - depth: 0, - range_ns: (NanoSecond::MAX, NanoSecond::MIN), - }) - } else { - let (num_scopes, depth) = Reader::count_scope_and_depth(&stream)?; - let min_ns = top_scopes.first().unwrap().record.start_ns; - let max_ns = top_scopes.last().unwrap().record.stop_ns(); - - Ok(StreamInfo { - stream, - num_scopes, - depth, - range_ns: (min_ns, max_ns), - }) - } - } - - pub fn extend(&mut self, other: &StreamInfoRef<'_>) { - self.stream.extend(other.stream); - self.num_scopes += other.num_scopes; - self.depth = self.depth.max(other.depth); - self.range_ns.0 = self.range_ns.0.min(other.range_ns.0); - self.range_ns.1 = self.range_ns.1.max(other.range_ns.1); - } - - pub fn clear(&mut self) { - let Self { - stream, - num_scopes, - depth, - range_ns, - } = self; - stream.clear(); - *num_scopes = 0; - *depth = 0; - *range_ns = (NanoSecond::MAX, NanoSecond::MIN); - } - - pub fn as_stream_into_ref(&self) -> StreamInfoRef<'_> { - StreamInfoRef { - stream: self.stream.bytes(), - num_scopes: self.num_scopes, - depth: self.depth, - range_ns: self.range_ns, - } - } -} - -/// A reference to the contents of a [`StreamInfo`]. -#[derive(Clone, Copy)] -pub struct StreamInfoRef<'a> { - /// The raw profile data. - pub stream: &'a [u8], - - /// Total number of scopes in the stream. - pub num_scopes: usize, - - /// The depth of the deepest scope. - /// `0` mean no scopes, `1` some scopes without children, etc. - pub depth: usize, - - /// The smallest and largest nanosecond value in the stream. - /// - /// The default value is ([`NanoSecond::MAX`], [`NanoSecond::MIN`]) which indicates an empty stream. - pub range_ns: (NanoSecond, NanoSecond), -} - -// ---------------------------------------------------------------------------- - type NsSource = fn() -> NanoSecond; -type ThreadReporter = fn(ThreadInfo, &StreamInfoRef<'_>); -/// Report a stream of profile data from a thread to the [`GlobalProfiler`] singleton. -pub fn global_reporter(info: ThreadInfo, stream_info: &StreamInfoRef<'_>) { - GlobalProfiler::lock().report(info, stream_info); -} +/// Incremental monolithic counter to identify scopes. +static SCOPE_ID_TRACKER: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32::new(1); -/// Collects profiling data for one thread -pub struct ThreadProfiler { - stream_info: StreamInfo, - /// Current depth. - depth: usize, - now_ns: NsSource, - reporter: ThreadReporter, - start_time_ns: Option, -} - -impl Default for ThreadProfiler { - fn default() -> Self { - Self { - stream_info: Default::default(), - depth: 0, - now_ns: crate::now_ns, - reporter: global_reporter, - start_time_ns: None, - } - } +fn fetch_add_scope_id() -> ScopeId { + let new_id = SCOPE_ID_TRACKER.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + ScopeId( + NonZeroU32::new(new_id) + .expect("safe because integer is retrieved from fetch-add atomic operation"), + ) } -impl ThreadProfiler { - /// Explicit initialize with custom callbacks. - /// - /// If not called, each thread will use the default nanosecond source ([`now_ns()`]) - /// and report scopes to the global profiler ([`global_reporter()`]). - /// - /// For instance, when compiling for WASM the default timing function ([`now_ns()`]) won't work, - /// so you'll want to call `puffin::ThreadProfiler::initialize(my_timing_function, puffin::global_reporter);`. - pub fn initialize(now_ns: NsSource, reporter: ThreadReporter) { - ThreadProfiler::call(|tp| { - tp.now_ns = now_ns; - tp.reporter = reporter; - }); - } - - /// Returns position where to write scope size once the scope is closed. - #[must_use] - pub fn begin_scope(&mut self, id: &str, location: &str, data: &str) -> usize { - let now_ns = (self.now_ns)(); - self.start_time_ns = Some(self.start_time_ns.unwrap_or(now_ns)); - - self.depth += 1; - - self.stream_info.range_ns.0 = self.stream_info.range_ns.0.min(now_ns); - self.stream_info - .stream - .begin_scope(now_ns, id, location, data) - } - - pub fn end_scope(&mut self, start_offset: usize) { - let now_ns = (self.now_ns)(); - self.stream_info.depth = self.stream_info.depth.max(self.depth); - self.stream_info.num_scopes += 1; - self.stream_info.range_ns.1 = self.stream_info.range_ns.1.max(now_ns); - - if self.depth > 0 { - self.depth -= 1; - } else { - eprintln!("puffin ERROR: Mismatched scope begin/end calls"); - } - - self.stream_info.stream.end_scope(start_offset, now_ns); - - if self.depth == 0 { - // We have no open scopes. - // This is a good time to report our profiling stream to the global profiler: - let info = ThreadInfo { - start_time_ns: self.start_time_ns, - name: std::thread::current().name().unwrap_or_default().to_owned(), - }; - (self.reporter)(info, &self.stream_info.as_stream_into_ref()); - self.stream_info.clear(); - } - } - - /// Do something with the thread local [`ThreadProfiler`] - #[inline] - pub fn call(f: impl Fn(&mut Self) -> R) -> R { - thread_local! { - pub static THREAD_PROFILER: std::cell::RefCell = Default::default(); - } - THREAD_PROFILER.with(|p| f(&mut p.borrow_mut())) - } -} - -// ---------------------------------------------------------------------------- - -/// Add these to [`GlobalProfiler`] with [`GlobalProfiler::add_sink()`]. -pub type FrameSink = Box) + Send>; - /// Identifies a specific [`FrameSink`] when added to [`GlobalProfiler`]. #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] pub struct FrameSinkId(u64); -/// Singleton. Collects profiling data from multiple threads -/// and passes them on to different [`FrameSink`]s. -pub struct GlobalProfiler { - current_frame_index: FrameIndex, - current_frame: BTreeMap, - - next_sink_id: FrameSinkId, - sinks: std::collections::HashMap, -} - -impl Default for GlobalProfiler { - fn default() -> Self { - Self { - current_frame_index: 0, - current_frame: Default::default(), - next_sink_id: FrameSinkId(1), - sinks: Default::default(), - } - } -} - -impl GlobalProfiler { - /// Access to the global profiler singleton. - #[cfg(feature = "parking_lot")] - pub fn lock() -> parking_lot::MutexGuard<'static, Self> { - use once_cell::sync::Lazy; - static GLOBAL_PROFILER: Lazy> = - Lazy::new(Default::default); - GLOBAL_PROFILER.lock() - } - - /// Access to the global profiler singleton. - #[cfg(not(feature = "parking_lot"))] - pub fn lock() -> std::sync::MutexGuard<'static, Self> { - use once_cell::sync::Lazy; - static GLOBAL_PROFILER: Lazy> = - Lazy::new(Default::default); - GLOBAL_PROFILER.lock().expect("poisoned mutex") - } - - /// You need to call this once at the start of every frame. - /// - /// It is fine to call this from within a profile scope. - pub fn new_frame(&mut self) { - let current_frame_index = self.current_frame_index; - self.current_frame_index += 1; - - let new_frame = - match FrameData::new(current_frame_index, std::mem::take(&mut self.current_frame)) { - Ok(new_frame) => Arc::new(new_frame), - Err(Error::Empty) => { - return; // don't warn about empty frames, just ignore them - } - Err(err) => { - eprintln!("puffin ERROR: Bad frame: {err:?}"); - return; - } - }; - - self.add_frame(new_frame); - } - - /// Manually add frame data. - pub fn add_frame(&mut self, new_frame: Arc) { - for sink in self.sinks.values() { - sink(new_frame.clone()); - } - } - - /// Report some profiling data. Called from [`ThreadProfiler`]. - pub fn report(&mut self, info: ThreadInfo, stream_info: &StreamInfoRef<'_>) { - self.current_frame - .entry(info) - .or_default() - .extend(stream_info); - } - - /// Tells [`GlobalProfiler`] to call this function with each new finished frame. - /// - /// The returned [`FrameSinkId`] can be used to remove the sink with [`Self::remove_sink()`]. - pub fn add_sink(&mut self, sink: FrameSink) -> FrameSinkId { - let id = self.next_sink_id; - self.next_sink_id.0 += 1; - self.sinks.insert(id, sink); - id - } - - pub fn remove_sink(&mut self, id: FrameSinkId) -> Option { - self.sinks.remove(&id) - } -} - -// ---------------------------------------------------------------------------- - /// Returns a high-precision, monotonically increasing nanosecond count since unix epoch. #[inline] #[cfg(any(not(target_arch = "wasm32"), feature = "web"))] @@ -552,14 +101,18 @@ pub fn now_ns() -> NanoSecond { } // This can maybe be optimized - use instant::Instant; - use once_cell::sync::Lazy; - static START_TIME: Lazy<(NanoSecond, Instant)> = - Lazy::new(|| (nanos_since_epoch(), Instant::now())); + #[cfg(not(target_arch = "wasm32"))] + use std::time::Instant; + #[cfg(target_arch = "wasm32")] + use web_time::Instant; + + static START_TIME: once_cell::sync::Lazy<(NanoSecond, Instant)> = + once_cell::sync::Lazy::new(|| (nanos_since_epoch(), Instant::now())); START_TIME.0 + START_TIME.1.elapsed().as_nanos() as NanoSecond } +/// Should not be used. #[inline] #[cfg(all(target_arch = "wasm32", not(feature = "web")))] pub fn now_ns() -> NanoSecond { @@ -567,8 +120,6 @@ pub fn now_ns() -> NanoSecond { panic!("Wasm without the `web` feature requires passing a custom source of time via `ThreadProfiler::initialize`"); } -// ---------------------------------------------------------------------------- - // We currently store an Option on the stack (None when profiling is off). // This currently takes up 16 bytes of stack space. TODO: get this down to 4 bytes. /// Created by the `puffin::profile*!(...)` macros. @@ -583,15 +134,12 @@ pub struct ProfilerScope { } impl ProfilerScope { - /// The `id` doesn't need to be static, but it should be unchanging, - /// and this is a good way to enforce it. + /// The scope id identifies which scopes' time is being reported. /// `data` can be changing, i.e. a name of a mesh or a texture. #[inline] - pub fn new(id: &'static str, location: &str, data: impl AsRef) -> Self { + pub fn new(scope_id: ScopeId, data: impl AsRef) -> Self { Self { - start_stream_offset: ThreadProfiler::call(|tp| { - tp.begin_scope(id, location, data.as_ref()) - }), + start_stream_offset: ThreadProfiler::call(|tp| tp.begin_scope(scope_id, data.as_ref())), _dont_send_me: Default::default(), } } @@ -604,10 +152,19 @@ impl Drop for ProfilerScope { } } -#[doc(hidden)] -#[inline(always)] -pub fn type_name_of(_: T) -> &'static str { - std::any::type_name::() +/// A unique id for each scope and [`ScopeDetails`]. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +#[cfg_attr( + feature = "serialization", + derive(serde::Serialize, serde::Deserialize) +)] +pub struct ScopeId(pub NonZeroU32); + +impl ScopeId { + #[cfg(test)] + pub(crate) fn new(id: u32) -> Self { + ScopeId(NonZeroU32::new(id).expect("Scope id was not non-zero u32")) + } } /// Returns the name of the calling function without a long module path prefix. @@ -615,68 +172,10 @@ pub fn type_name_of(_: T) -> &'static str { macro_rules! current_function_name { () => {{ fn f() {} - let name = $crate::type_name_of(f); - // Remove "::f" from the name: - let name = &name.get(..name.len() - 3).unwrap(); - $crate::clean_function_name(name) + $crate::type_name_of(f) }}; } -#[doc(hidden)] -#[inline] -pub fn clean_function_name(name: &str) -> &str { - if let Some(colon) = name.rfind("::") { - if let Some(colon) = name[..colon].rfind("::") { - // "foo::bar::baz::function_name" -> "baz::function_name" - &name[colon + 2..] - } else { - // "foo::function_name" -> "foo::function_name" - name - } - } else { - name - } -} - -#[test] -fn test_clean_function_name() { - assert_eq!(clean_function_name(""), ""); - assert_eq!(clean_function_name("foo"), "foo"); - assert_eq!(clean_function_name("foo::bar"), "foo::bar"); - assert_eq!(clean_function_name("foo::bar::baz"), "bar::baz"); -} - -/// Returns a shortened path to the current file. -#[macro_export] -macro_rules! current_file_name { - () => { - $crate::short_file_name(file!()) - }; -} - -/// Removes long path prefix to focus on the last parts of the path (and the file name). -#[doc(hidden)] -#[inline] -pub fn short_file_name(name: &str) -> &str { - // TODO: "foo/bar/src/lib.rs" -> "bar/src/lib.rs" - - if let Some(separator) = name.rfind(&['/', '\\'][..]) { - // "foo/bar/baz.rs" -> "baz.rs" - &name[separator + 1..] - } else { - name - } -} - -#[test] -fn test_short_file_name() { - assert_eq!(short_file_name(""), ""); - assert_eq!(short_file_name("foo.rs"), "foo.rs"); - assert_eq!(short_file_name("foo/bar.rs"), "bar.rs"); - assert_eq!(short_file_name("foo/bar/baz.rs"), "baz.rs"); - assert_eq!(short_file_name(r"C:\\windows\is\weird\src.rs"), "src.rs"); -} - #[allow(clippy::doc_markdown)] // clippy wants to put "MacBook" in ticks 🙄 /// Automatically name the profiling scope based on function name. /// @@ -705,7 +204,7 @@ fn test_short_file_name() { /// } /// ``` /// -/// Overhead: around 210 ns on 2020 Intel MacBook Pro. +/// Overhead: around 54 ns on Macbook Pro with Apple M1 Max. #[macro_export] macro_rules! profile_function { () => { @@ -713,17 +212,84 @@ macro_rules! profile_function { }; ($data:expr) => { let _profiler_scope = if $crate::are_scopes_on() { - Some($crate::ProfilerScope::new( - $crate::current_function_name!(), - $crate::current_file_name!(), - $data, - )) + static SCOPE_ID: std::sync::OnceLock<$crate::ScopeId> = std::sync::OnceLock::new(); + let scope_id = SCOPE_ID.get_or_init(|| { + $crate::ThreadProfiler::call(|tp| { + let id = tp.register_function_scope( + $crate::clean_function_name($crate::current_function_name!()), + $crate::short_file_name(file!()), + line!(), + ); + id + }) + }); + + Some($crate::ProfilerScope::new(*scope_id, $data)) } else { None }; }; } +/// Profile the current scope with the given name (unique in the parent scope). +/// +/// This macro is identical to [profile_scope], except that it expands to the expression +/// containing the profiling scope, as opposed to [profile_scope] which expands to a +/// variable (which cannot be accessed due to macro hygiene). +/// +/// This allows for profiling scopes to persist for a custom duration. +/// +/// # Example +/// +/// ```rust +/// # use std::iter::FromIterator as _; +/// # +/// # pub mod rayon { pub mod prelude { +/// # pub fn for_each_init(vec: &std::vec::Vec, init: fn() -> I, body: fn ((I, T)) -> ()) { +/// # } +/// # } } +/// # +/// let some_large_vec = Vec::from_iter(0..1000); +/// +/// // Use rayon's parallel for loop over our large iterator +/// rayon::prelude::for_each_init( +/// &some_large_vec, +/// // This gets called to init each work segment, and is passed into the calls +/// // Rayon keeps the profiling scope stored for the entire duration of the work segment +/// // So we can track the entire segment as one, instead of each loop iteration +/// || puffin::profile_scope_custom!("rayon_work_segment"), +/// |((_profiler_scope), i)| { +/// // All calls here gets profiled on the same scope +/// println!("{i}") +/// }, +/// ); +/// ``` +#[macro_export] +macro_rules! profile_scope_custom { + ($name:expr) => { + $crate::profile_scope_custom!($name, "") + }; + ($name:expr, $data:expr) => {{ + if $crate::are_scopes_on() { + static SCOPE_ID: std::sync::OnceLock<$crate::ScopeId> = std::sync::OnceLock::new(); + let scope_id = SCOPE_ID.get_or_init(|| { + $crate::ThreadProfiler::call(|tp| { + let id = tp.register_named_scope( + $name, + $crate::clean_function_name($crate::current_function_name!()), + $crate::short_file_name(file!()), + line!(), + ); + id + }) + }); + Some($crate::ProfilerScope::new(*scope_id, $data)) + } else { + None + } + }}; +} + #[allow(clippy::doc_markdown)] // clippy wants to put "MacBook" in ticks 🙄 /// Profile the current scope with the given name (unique in the parent scope). /// @@ -734,21 +300,136 @@ macro_rules! profile_function { /// An optional second argument can be a string (e.g. a mesh name) to help diagnose what was slow. /// Example: `profile_scope!("load_mesh", mesh_name);` /// -/// Overhead: around 140 ns on 2020 Intel MacBook Pro. +/// Overhead: around 54 ns on Macbook Pro with Apple M1 Max. #[macro_export] macro_rules! profile_scope { - ($id:expr) => { - $crate::profile_scope!($id, ""); + ($name:expr) => { + $crate::profile_scope!($name, ""); }; - ($id:expr, $data:expr) => { - let _profiler_scope = if $crate::are_scopes_on() { - Some($crate::ProfilerScope::new( - $id, - $crate::current_file_name!(), - $data, - )) - } else { - None - }; + ($name:expr, $data:expr) => { + let _profiler_scope = $crate::profile_scope_custom!($name, $data); + }; +} + +#[cfg(test)] +mod tests { + use std::borrow::Cow; + + use crate::{ + clean_function_name, set_scopes_on, short_file_name, utils::USELESS_SCOPE_NAME_SUFFIX, + GlobalFrameView, GlobalProfiler, ScopeId, }; + + #[test] + fn test_short_file_name() { + for (before, after) in [ + ("", ""), + ("foo.rs", "foo.rs"), + ("foo/bar.rs", "foo/bar.rs"), + ("foo/bar/baz.rs", "bar/baz.rs"), + ("crates/cratename/src/main.rs", "cratename/src/main.rs"), + ("crates/cratename/src/module/lib.rs", "cratename/…/module/lib.rs"), + ("workspace/cratename/examples/hello_world.rs", "examples/hello_world.rs"), + ("/rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/ops/function.rs", "core/…/function.rs"), + ("/Users/emilk/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.24.1/src/runtime/runtime.rs", "tokio-1.24.1/…/runtime.rs"), + ] + { + assert_eq!(short_file_name(before), after); + } + } + + #[test] + fn test_clean_function_name() { + assert_eq!(clean_function_name(""), ""); + assert_eq!( + clean_function_name(&format!("foo{}", USELESS_SCOPE_NAME_SUFFIX)), + "foo" + ); + assert_eq!( + clean_function_name(&format!("foo::bar{}", USELESS_SCOPE_NAME_SUFFIX)), + "foo::bar" + ); + assert_eq!( + clean_function_name(&format!("foo::bar::baz{}", USELESS_SCOPE_NAME_SUFFIX)), + "bar::baz" + ); + assert_eq!( + clean_function_name(&format!( + "some::GenericThing<_, _>::function_name{}", + USELESS_SCOPE_NAME_SUFFIX + )), + "GenericThing<_, _>::function_name" + ); + assert_eq!( + clean_function_name(&format!( + "::function_name{}", + USELESS_SCOPE_NAME_SUFFIX + )), + "::function_name" + ); + } + + #[test] + fn profile_macros_test() { + set_scopes_on(true); + + let frame_view = GlobalFrameView::default(); + + GlobalProfiler::lock().add_sink(Box::new(|data| { + if data.frame_index() == 0 { + assert_eq!(data.frame_index(), 0); + assert_eq!(data.meta().num_scopes, 2); + assert_eq!(data.meta().num_bytes, 62); + } else if data.frame_index() == 1 { + assert_eq!(data.frame_index(), 1); + assert_eq!(data.meta().num_scopes, 2); + assert_eq!(data.meta().num_bytes, 62); + } else { + panic!("Only two frames in this test"); + } + })); + + let line_nr_fn = line!() + 3; + let line_nr_scope = line!() + 4; + fn a() { + profile_function!(); + { + profile_scope!("my-scope"); + } + } + + a(); + + // First frame + GlobalProfiler::lock().new_frame(); + + let lock = frame_view.lock(); + let scope_details = lock + .scope_collection() + .fetch_by_id(&ScopeId::new(1)) + .unwrap(); + assert_eq!(scope_details.file_path, "puffin/src/lib.rs"); + assert_eq!(scope_details.function_name, "profile_macros_test::a"); + assert_eq!(scope_details.line_nr, line_nr_fn); + + let scope_details = lock + .scope_collection() + .fetch_by_id(&ScopeId::new(2)) + .unwrap(); + + assert_eq!(scope_details.file_path, "puffin/src/lib.rs"); + assert_eq!(scope_details.function_name, "profile_macros_test::a"); + assert_eq!(scope_details.scope_name, Some(Cow::Borrowed("my-scope"))); + assert_eq!(scope_details.line_nr, line_nr_scope); + + let scope_details = lock.scope_collection().fetch_by_name("my-scope").unwrap(); + assert_eq!(scope_details, &ScopeId::new(2)); + + drop(lock); + + // Second frame + a(); + + GlobalProfiler::lock().new_frame(); + } } diff --git a/puffin/src/merge.rs b/puffin/src/merge.rs index 91d848a3..bd135a96 100644 --- a/puffin/src/merge.rs +++ b/puffin/src/merge.rs @@ -1,10 +1,13 @@ -use crate::{NanoSecond, Reader, Result, Scope, Stream, ThreadInfo, UnpackedFrameData}; -use std::collections::BTreeMap; +use crate::{ + NanoSecond, Reader, Result, Scope, ScopeCollection, ScopeId, Stream, ThreadInfo, + UnpackedFrameData, +}; +use std::{collections::BTreeMap, hash::Hash}; /// Temporary structure while building a [`MergeScope`]. #[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)] struct MergeId<'s> { - id: &'s str, + id: ScopeId, data: &'s str, } @@ -18,7 +21,6 @@ struct MergeNode<'s> { /// indexed by their id+data children: BTreeMap, MergeNode<'s>>, } - #[derive(Clone, Copy, Debug, PartialEq)] struct MergePiece<'s> { /// The start of the scope relative to its *parent* [`Scope`]. @@ -41,16 +43,15 @@ pub struct MergeScope<'s> { /// Number of pieces that got merged together to us. pub num_pieces: usize, /// The common identifier that we merged using. - pub id: std::borrow::Cow<'s, str>, - /// only set if all children had the same - pub location: std::borrow::Cow<'s, str>, + pub id: ScopeId, /// only set if all children had the same pub data: std::borrow::Cow<'s, str>, - + /// The merged children of this merged scope. pub children: Vec>, } impl<'s> MergeScope<'s> { + /// Clones the merge scope. pub fn into_owned(self) -> MergeScope<'static> { MergeScope::<'static> { relative_start_ns: self.relative_start_ns, @@ -58,8 +59,7 @@ impl<'s> MergeScope<'s> { duration_per_frame_ns: self.duration_per_frame_ns, max_duration_ns: self.max_duration_ns, num_pieces: self.num_pieces, - id: std::borrow::Cow::Owned(self.id.into_owned()), - location: std::borrow::Cow::Owned(self.location.into_owned()), + id: self.id, data: std::borrow::Cow::Owned(self.data.into_owned()), children: self.children.into_iter().map(Self::into_owned).collect(), } @@ -72,9 +72,10 @@ impl<'s> MergeNode<'s> { for child in Reader::with_offset(stream, piece.scope.child_begin_position)? { let child = child?; + self.children .entry(MergeId { - id: child.record.id, + id: child.id, data: child.record.data, }) .or_default() @@ -90,14 +91,13 @@ impl<'s> MergeNode<'s> { Ok(()) } - fn build(self, num_frames: i64) -> MergeScope<'s> { + fn build(self, scope_collection: &ScopeCollection, num_frames: i64) -> MergeScope<'s> { assert!(!self.pieces.is_empty()); let mut relative_start_ns = self.pieces[0].relative_start_ns; let mut total_duration_ns = 0; let mut slowest_ns = 0; let num_pieces = self.pieces.len(); - let id = self.pieces[0].scope.record.id; - let mut location = self.pieces[0].scope.record.location; + let id = self.pieces[0].scope.id; let mut data = self.pieces[0].scope.record.data; for piece in &self.pieces { @@ -106,13 +106,10 @@ impl<'s> MergeNode<'s> { total_duration_ns += piece.scope.record.duration_ns; slowest_ns = slowest_ns.max(piece.scope.record.duration_ns); - assert_eq!(id, piece.scope.record.id); + assert_eq!(id, piece.scope.id); if data != piece.scope.record.data { data = ""; // different in different pieces } - if location != piece.scope.record.location { - location = ""; // different in different pieces - } } MergeScope { @@ -121,18 +118,21 @@ impl<'s> MergeNode<'s> { duration_per_frame_ns: total_duration_ns / num_frames, max_duration_ns: slowest_ns, num_pieces, - id: id.into(), - location: location.into(), + id, data: data.into(), - children: build(self.children, num_frames), + children: build(scope_collection, self.children, num_frames), } } } -fn build<'s>(nodes: BTreeMap, MergeNode<'s>>, num_frames: i64) -> Vec> { +fn build<'s>( + scope_collection: &ScopeCollection, + nodes: BTreeMap, MergeNode<'s>>, + num_frames: i64, +) -> Vec> { let mut scopes: Vec<_> = nodes .into_values() - .map(|node| node.build(num_frames)) + .map(|node| node.build(scope_collection, num_frames)) .collect(); // Earliest first: @@ -150,6 +150,7 @@ fn build<'s>(nodes: BTreeMap, MergeNode<'s>>, num_frames: i64) -> Ve /// For the given thread, merge all scopes with the same id+data path. pub fn merge_scopes_for_thread<'s>( + scope_collection: &ScopeCollection, frames: &'s [std::sync::Arc], thread_info: &ThreadInfo, ) -> Result>> { @@ -163,7 +164,7 @@ pub fn merge_scopes_for_thread<'s>( for scope in top_scopes { top_nodes .entry(MergeId { - id: scope.record.id, + id: scope.id, data: scope.record.data, }) .or_default() @@ -178,109 +179,128 @@ pub fn merge_scopes_for_thread<'s>( } } - Ok(build(top_nodes, frames.len() as _)) + Ok(build(scope_collection, top_nodes, frames.len() as _)) } -// ---------------------------------------------------------------------------- - -#[test] -fn test_merge() { - use crate::*; - - let stream = { - let mut stream = Stream::default(); - - for i in 0..2 { - let ns = 1000 * i; - let a = stream.begin_scope(ns + 100, "a", "", ""); - stream.end_scope(a, ns + 200); - - let b = stream.begin_scope(ns + 200, "b", "", ""); - - let ba = stream.begin_scope(ns + 400, "ba", "", ""); - stream.end_scope(ba, ns + 600); - - let bb = stream.begin_scope(ns + 600, "bb", "", ""); - let bba = stream.begin_scope(ns + 600, "bba", "", ""); - stream.end_scope(bba, ns + 700); - stream.end_scope(bb, ns + 800); - stream.end_scope(b, ns + 900); - } +#[cfg(test)] +mod tests { + use std::{collections::BTreeMap, sync::Arc}; + + #[test] + fn test_merge() { + use crate::*; + + let mut scope_collection = ScopeCollection::default(); + // top scopes + scope_collection.insert(Arc::new( + ScopeDetails::from_scope_id(ScopeId::new(1)).with_function_name("a"), + )); + scope_collection.insert(Arc::new( + ScopeDetails::from_scope_id(ScopeId::new(2)).with_function_name("b"), + )); + + // middle scopes + scope_collection.insert(Arc::new( + ScopeDetails::from_scope_id(ScopeId::new(3)).with_function_name("ba"), + )); + scope_collection.insert(Arc::new( + ScopeDetails::from_scope_id(ScopeId::new(4)).with_function_name("bb"), + )); + scope_collection.insert(Arc::new( + ScopeDetails::from_scope_id(ScopeId::new(5)).with_function_name("bba"), + )); + + let stream = { + let mut stream = Stream::default(); + + for i in 0..2 { + let ns = 1000 * i; + + let (a, _) = stream.begin_scope(|| ns + 100, ScopeId::new(1), ""); + stream.end_scope(a, ns + 200); + + let (b, _) = stream.begin_scope(|| ns + 200, ScopeId::new(2), ""); + + let (ba, _) = stream.begin_scope(|| ns + 400, ScopeId::new(3), ""); + stream.end_scope(ba, ns + 600); + + let (bb, _) = stream.begin_scope(|| ns + 600, ScopeId::new(4), ""); + let (bba, _) = stream.begin_scope(|| ns + 600, ScopeId::new(5), ""); + stream.end_scope(bba, ns + 700); + stream.end_scope(bb, ns + 800); + stream.end_scope(b, ns + 900); + } - stream - }; - - let stream_info = StreamInfo::parse(stream).unwrap(); - let mut thread_streams = BTreeMap::new(); - let thread_info = ThreadInfo { - start_time_ns: Some(0), - name: "main".to_owned(), - }; - thread_streams.insert(thread_info.clone(), stream_info); - let frame = UnpackedFrameData::new(0, thread_streams).unwrap(); - let frames = [Arc::new(frame)]; - let merged = merge_scopes_for_thread(&frames, &thread_info).unwrap(); - - let expected = vec![ - MergeScope { - relative_start_ns: 100, - total_duration_ns: 2 * 100, - duration_per_frame_ns: 2 * 100, - max_duration_ns: 100, - num_pieces: 2, - id: "a".into(), - location: "".into(), - data: "".into(), - children: vec![], - }, - MergeScope { - relative_start_ns: 300, // moved forward to make place for "a" (as are all children) - total_duration_ns: 2 * 700, - duration_per_frame_ns: 2 * 700, - max_duration_ns: 700, - num_pieces: 2, - id: "b".into(), - location: "".into(), - data: "".into(), - children: vec![ - MergeScope { - relative_start_ns: 200, - total_duration_ns: 2 * 200, - duration_per_frame_ns: 2 * 200, - max_duration_ns: 200, - num_pieces: 2, - id: "ba".into(), - location: "".into(), - data: "".into(), - children: vec![], - }, - MergeScope { - relative_start_ns: 600, - total_duration_ns: 2 * 200, - duration_per_frame_ns: 2 * 200, - max_duration_ns: 200, - num_pieces: 2, - id: "bb".into(), - location: "".into(), - data: "".into(), - children: vec![MergeScope { - relative_start_ns: 0, - total_duration_ns: 2 * 100, - duration_per_frame_ns: 2 * 100, - max_duration_ns: 100, + stream + }; + + let stream_info = StreamInfo::parse(stream).unwrap(); + let mut thread_streams = BTreeMap::new(); + let thread_info = ThreadInfo { + start_time_ns: Some(0), + name: "main".to_owned(), + }; + thread_streams.insert(thread_info.clone(), stream_info); + let frame = UnpackedFrameData::new(0, thread_streams).unwrap(); + let frames = [Arc::new(frame)]; + let merged = merge_scopes_for_thread(&scope_collection, &frames, &thread_info).unwrap(); + + let expected = vec![ + MergeScope { + relative_start_ns: 100, + total_duration_ns: 2 * 100, + duration_per_frame_ns: 2 * 100, + max_duration_ns: 100, + num_pieces: 2, + id: ScopeId::new(1), + data: "".into(), + children: vec![], + }, + MergeScope { + relative_start_ns: 300, // moved forward to make place for "a" (as are all children) + total_duration_ns: 2 * 700, + duration_per_frame_ns: 2 * 700, + max_duration_ns: 700, + num_pieces: 2, + id: ScopeId::new(2), + data: "".into(), + children: vec![ + MergeScope { + relative_start_ns: 200, + total_duration_ns: 2 * 200, + duration_per_frame_ns: 2 * 200, + max_duration_ns: 200, num_pieces: 2, - id: "bba".into(), - location: "".into(), + id: ScopeId::new(3), data: "".into(), children: vec![], - }], - }, - ], - }, - ]; - - assert_eq!( - merged, expected, - "\nGot:\n{merged:#?}\n\n!=\nExpected:\n{expected:#?}", - ); + }, + MergeScope { + relative_start_ns: 600, + total_duration_ns: 2 * 200, + duration_per_frame_ns: 2 * 200, + max_duration_ns: 200, + num_pieces: 2, + id: ScopeId::new(4), + data: "".into(), + children: vec![MergeScope { + relative_start_ns: 0, + total_duration_ns: 2 * 100, + duration_per_frame_ns: 2 * 100, + max_duration_ns: 100, + num_pieces: 2, + id: ScopeId::new(5), + data: "".into(), + children: vec![], + }], + }, + ], + }, + ]; + + assert_eq!( + merged, expected, + "\nGot:\n{merged:#?}\n\n!=\nExpected:\n{expected:#?}", + ); + } } diff --git a/puffin/src/profile_view.rs b/puffin/src/profile_view.rs index 2fc9d1dd..e01d6b87 100644 --- a/puffin/src/profile_view.rs +++ b/puffin/src/profile_view.rs @@ -1,10 +1,7 @@ -use std::cmp::Ordering; - -use std::sync::{Arc, Mutex}; - use itertools::Itertools; +use std::{cmp::Ordering, sync::Arc}; -use crate::{FrameData, FrameSinkId}; +use crate::{FrameData, FrameSinkId, ScopeCollection}; /// A view of recent and slowest frames, used by GUIs. #[derive(Clone)] @@ -24,13 +21,14 @@ pub struct FrameView { /// Maintain stats as we add/remove frames stats: FrameStats, + + scope_collection: ScopeCollection, } impl Default for FrameView { fn default() -> Self { let max_recent = 60 * 60 * 5; let max_slow = 256; - let stats = Default::default(); Self { recent: std::collections::VecDeque::with_capacity(max_recent), @@ -39,17 +37,31 @@ impl Default for FrameView { slowest_by_duration: std::collections::BTreeSet::new(), max_slow, pack_frames: true, - stats, + stats: Default::default(), + scope_collection: Default::default(), } } } impl FrameView { + /// Returns `true` if there are no recent or slowest frames. pub fn is_empty(&self) -> bool { self.recent.is_empty() && self.slowest_by_duration.is_empty() } + /// Returns the collection of scope details. + /// This can be used to fetch more information about a specific scope. + pub fn scope_collection(&self) -> &ScopeCollection { + &self.scope_collection + } + + /// Adds a new frame to the view. pub fn add_frame(&mut self, new_frame: Arc) { + // Register all scopes from the new frame into the scope collection. + for new_scope in &new_frame.scope_delta { + self.scope_collection.insert(new_scope.clone()); + } + if let Some(last) = self.recent.iter().last() { if new_frame.frame_index() <= last.0.frame_index() { // A frame from the past!? @@ -183,10 +195,13 @@ impl FrameView { self.max_slow = max_slow; } + /// Returns if frames are packed (compressed). pub fn pack_frames(&self) -> bool { self.pack_frames } + /// Sets wether frames should be packed (compressed). + /// Packing frames will increase CPU time and decrease memory usage. pub fn set_pack_frames(&mut self, pack_frames: bool) { self.pack_frames = pack_frames; } @@ -203,36 +218,21 @@ impl FrameView { FrameStats::from_frames(self.all_uniq().map(Arc::as_ref)) } - /// Export profile data as a `.puffin` file. - #[cfg(feature = "serialization")] - #[cfg(not(target_arch = "wasm32"))] // compression not supported on wasm - pub fn save_to_path(&self, path: &std::path::Path) -> anyhow::Result<()> { - let mut file = std::fs::File::create(path)?; - self.save_to_writer(&mut file) - } - - /// Export profile data as a `.puffin` file. + /// Export profile data as a `.puffin` file/stream. #[cfg(feature = "serialization")] #[cfg(not(target_arch = "wasm32"))] // compression not supported on wasm - pub fn save_to_writer(&self, write: &mut impl std::io::Write) -> anyhow::Result<()> { + pub fn write(&self, write: &mut impl std::io::Write) -> anyhow::Result<()> { write.write_all(b"PUF0")?; for frame in self.all_uniq() { - frame.write_into(write)?; + frame.write_into(&self.scope_collection, false, write)?; } Ok(()) } - /// Import profile data from a `.puffin` file. - #[cfg(feature = "serialization")] - pub fn load_path(path: &std::path::Path) -> anyhow::Result { - let mut file = std::fs::File::open(path)?; - Self::load_reader(&mut file) - } - - /// Import profile data from a `.puffin` file. + /// Import profile data from a `.puffin` file/stream. #[cfg(feature = "serialization")] - pub fn load_reader(read: &mut impl std::io::Read) -> anyhow::Result { + pub fn read(read: &mut impl std::io::Read) -> anyhow::Result { let mut magic = [0_u8; 4]; read.read_exact(&mut magic)?; if &magic != b"PUF0" { @@ -329,15 +329,15 @@ impl PartialEq for OrderedByIndex { /// Automatically connects to [`crate::GlobalProfiler`]. pub struct GlobalFrameView { sink_id: FrameSinkId, - view: Arc>, + view: Arc>, } impl Default for GlobalFrameView { fn default() -> Self { - let view = Arc::new(Mutex::new(FrameView::default())); + let view = Arc::new(parking_lot::Mutex::new(FrameView::default())); let view_clone = view.clone(); let sink_id = crate::GlobalProfiler::lock().add_sink(Box::new(move |frame| { - view_clone.lock().unwrap().add_frame(frame); + view_clone.lock().add_frame(frame); })); Self { sink_id, view } } @@ -350,9 +350,14 @@ impl Drop for GlobalFrameView { } impl GlobalFrameView { + /// Sink ID + pub fn sink_id(&self) -> FrameSinkId { + self.sink_id + } + /// View the latest profiling data. - pub fn lock(&self) -> std::sync::MutexGuard<'_, FrameView> { - self.view.lock().unwrap() + pub fn lock(&self) -> parking_lot::MutexGuard<'_, FrameView> { + self.view.lock() } } @@ -375,6 +380,7 @@ pub struct FrameStats { } impl FrameStats { + /// Creates a `FrameStats` instance from an iterator of frames. pub fn from_frames<'a>(frames: impl Iterator) -> Self { let mut stats = FrameStats::default(); @@ -385,6 +391,7 @@ impl FrameStats { stats } + /// Adds a frame's statistics to the `FrameStats`. fn add(&mut self, frame: &FrameData) { let (total, unpacked) = stats_entry(frame); @@ -393,6 +400,7 @@ impl FrameStats { self.unique_frames = self.unique_frames.saturating_add(1); } + /// Removes a frame's statistics from the `FrameStats`. fn remove(&mut self, frame: &FrameData) { let (total, unpacked) = stats_entry(frame); @@ -401,18 +409,22 @@ impl FrameStats { self.unique_frames = self.unique_frames.saturating_sub(1); } + /// Returns the number of unique frames. pub fn frames(&self) -> usize { self.unique_frames } + /// Returns the number of unpacked frames. pub fn unpacked_frames(&self) -> usize { self.unpacked_frames } + /// Returns the total bytes of RAM used. pub fn bytes_of_ram_used(&self) -> usize { self.total_ram_used } + /// Clears all statistics in `FrameStats`. pub fn clear(&mut self) { self.unique_frames = 0; self.unpacked_frames = 0; diff --git a/puffin/src/scope_details.rs b/puffin/src/scope_details.rs new file mode 100644 index 00000000..c1cc819d --- /dev/null +++ b/puffin/src/scope_details.rs @@ -0,0 +1,205 @@ +use crate::ScopeId; +use std::{borrow::Cow, collections::HashMap, sync::Arc}; + +#[derive(Default, Clone)] +struct Inner { + // Store a both-way map, memory wise this can be a bit redundant but allows for faster access of information by external libs. + pub(crate) scope_id_to_details: HashMap>, + pub(crate) type_to_scope_id: HashMap, ScopeId>, +} + +/// A collection of scope details containing more information about a recorded profile scope. +#[derive(Default, Clone)] +pub struct ScopeCollection(Inner); + +impl ScopeCollection { + /// Fetches scope details by scope id. + #[inline] + pub fn fetch_by_id(&self, scope_id: &ScopeId) -> Option<&Arc> { + self.0.scope_id_to_details.get(scope_id) + } + + /// Fetches scope details by scope name. + #[inline] + pub fn fetch_by_name(&self, scope_name: &str) -> Option<&ScopeId> { + self.0.type_to_scope_id.get(scope_name) + } + + /// Insert a scope into the collection. + /// This method asserts the scope id is set which only puffin should do. + /// Custom sinks might use this method to store new scope details received from puffin. + pub fn insert(&mut self, scope_details: Arc) -> Arc { + let scope_id = scope_details + .scope_id + .expect("`ScopeDetails` missing `ScopeId`"); + + self.0 + .type_to_scope_id + .insert(scope_details.name().clone(), scope_id); + self.0 + .scope_id_to_details + .entry(scope_id) + .or_insert(scope_details) + .clone() + } + + /// Fetches all registered scopes and their ids. + /// Useful for fetching scope id by it's scope name. + /// For profiler scopes and user scopes this is the manual provided name. + /// For function profiler scopes this is the function name. + #[inline] + pub fn scopes_by_name(&self) -> &HashMap, ScopeId> { + &self.0.type_to_scope_id + } + + /// Fetches all registered scopes. + /// Useful for fetching scope details by a scope id. + #[inline] + pub fn scopes_by_id(&self) -> &HashMap> { + &self.0.scope_id_to_details + } +} + +/// Scopes are identified by user-provided name while functions are identified by the function name. +#[derive(Debug, Clone, PartialEq, Hash, PartialOrd, Ord, Eq)] +#[cfg_attr( + feature = "serialization", + derive(serde::Serialize, serde::Deserialize) +)] +pub enum ScopeType { + /// The scope is a function profile scope generated by `puffin::profile_function!`. + Function, + /// The named scope is a profile scope inside a function generated by `puffin::profile_scope!` or registered manually. + /// It is identified by a unique name. + Named, +} + +impl ScopeType { + /// Returns a string representation of this scope type. + pub fn type_str(&self) -> &'static str { + match self { + ScopeType::Function => "function scope", + ScopeType::Named => "named", + } + } +} + +#[derive(Debug, Default, Clone, PartialEq, Hash, PartialOrd, Ord, Eq)] +#[cfg_attr( + feature = "serialization", + derive(serde::Serialize, serde::Deserialize) +)] +/// Detailed information about a scope. +pub struct ScopeDetails { + /// Unique scope identifier. + /// Always initialized once registered. + /// It is `None` when an external library has yet to register this scope. + pub(crate) scope_id: Option, + /// A name for a profile scope, a function profile scope does not have a custom provided name. + pub scope_name: Option>, + /// The function name of the function in which this scope is contained. + /// The name might be slightly modified to represent a short descriptive representation. + pub function_name: Cow<'static, str>, + /// The file path in which this scope is contained. + /// The path might be slightly modified to represent a short descriptive representation. + pub file_path: Cow<'static, str>, + /// The exact line number at which this scope is located. + pub line_nr: u32, +} + +impl ScopeDetails { + /// Creates a new user scope with a unique name. + pub fn from_scope_name(scope_name: T) -> Self + where + T: Into>, + { + Self { + scope_id: None, + scope_name: Some(scope_name.into()), + function_name: Default::default(), + file_path: Default::default(), + line_nr: Default::default(), + } + } + + /// Creates a new user scope with a unique id allocated by puffin. + /// This function should not be exposed as only puffin should allocate ids for scopes. + pub(crate) fn from_scope_id(scope_id: ScopeId) -> Self { + Self { + scope_id: Some(scope_id), + scope_name: None, + function_name: Default::default(), + file_path: Default::default(), + line_nr: Default::default(), + } + } + + /// Scope in a function. + #[inline] + pub fn with_function_name(mut self, name: T) -> Self + where + T: Into>, + { + self.function_name = name.into(); + self + } + + /// Scope in a file. + #[inline] + pub fn with_file(mut self, file: T) -> Self + where + T: Into>, + { + self.file_path = file.into(); + self + } + + /// Scope at a line number. + #[inline] + pub fn with_line_nr(mut self, line_nr: u32) -> Self { + self.line_nr = line_nr; + self + } + + /// Returns the scope name if this is a profile scope or else the function name. + pub fn name(&self) -> &Cow<'static, str> { + self.scope_name.as_ref().map_or(&self.function_name, |x| x) + } + + /// Returns what type of scope this is. + pub fn scope_type(&self) -> ScopeType { + // scope name is only set for named scopes. + if self.scope_name.is_some() { + ScopeType::Named + } else { + ScopeType::Function + } + } + + /// Returns the exact location of the profile scope formatted as `file:line_nr` + #[inline] + pub fn location(&self) -> String { + if self.line_nr != 0 { + format!("{}:{}", self.file_path, self.line_nr) + } else { + format!("{}", self.file_path) + } + } + + // This function should not be exposed as only puffin should allocate ids. + #[inline] + pub(crate) fn with_scope_id(mut self, scope_id: ScopeId) -> Self { + self.scope_id = Some(scope_id); + self + } + + // This function should not be exposed as users are supposed to provide scope name in constructor. + #[inline] + pub(crate) fn with_scope_name(mut self, scope_name: T) -> Self + where + T: Into>, + { + self.scope_name = Some(scope_name.into()); + self + } +} diff --git a/puffin/src/thread_profiler.rs b/puffin/src/thread_profiler.rs new file mode 100644 index 00000000..079b728e --- /dev/null +++ b/puffin/src/thread_profiler.rs @@ -0,0 +1,174 @@ +use std::borrow::Cow; + +use crate::GlobalProfiler; +use crate::NanoSecond; +use crate::NsSource; + +use crate::fetch_add_scope_id; +use crate::ScopeDetails; +use crate::ScopeId; +use crate::StreamInfo; +use crate::StreamInfoRef; + +/// Report a stream of profile data from a thread to the [`GlobalProfiler`] singleton. +/// This is used for internal purposes only +pub(crate) fn internal_profile_reporter( + info: ThreadInfo, + scope_details: &[ScopeDetails], + stream_scope_times: &StreamInfoRef<'_>, +) { + GlobalProfiler::lock().report(info, scope_details, stream_scope_times); +} + +/// Collects profiling data for one thread +pub struct ThreadProfiler { + stream_info: StreamInfo, + scope_details: Vec, + /// Current depth. + depth: usize, + now_ns: NsSource, + reporter: ThreadReporter, + start_time_ns: Option, +} + +impl Default for ThreadProfiler { + fn default() -> Self { + Self { + stream_info: Default::default(), + scope_details: Default::default(), + depth: 0, + now_ns: crate::now_ns, + reporter: internal_profile_reporter, + start_time_ns: None, + } + } +} + +impl ThreadProfiler { + /// Explicit initialize with custom callbacks. + /// + /// If not called, each thread will use the default nanosecond source ([`now_ns()`]) + /// and report scopes to the global profiler ([`internal_profile_reporter()`]). + /// + /// For instance, when compiling for WASM the default timing function ([`now_ns()`]) won't work, + /// so you'll want to call `puffin::ThreadProfiler::initialize(my_timing_function, internal_profile_reporter);`. + pub fn initialize(now_ns: NsSource, reporter: ThreadReporter) { + ThreadProfiler::call(|tp| { + tp.now_ns = now_ns; + tp.reporter = reporter; + }); + } + + /// Register a function scope. + #[must_use] + pub fn register_function_scope( + &mut self, + function_name: impl Into>, + file_path: impl Into>, + line_nr: u32, + ) -> ScopeId { + let new_id = fetch_add_scope_id(); + self.scope_details.push( + ScopeDetails::from_scope_id(new_id) + .with_function_name(function_name) + .with_file(file_path) + .with_line_nr(line_nr), + ); + new_id + } + + /// Register a named scope. + #[must_use] + pub fn register_named_scope( + &mut self, + scope_name: impl Into>, + function_name: impl Into>, + file_path: impl Into>, + line_nr: u32, + ) -> ScopeId { + let new_id = fetch_add_scope_id(); + self.scope_details.push( + ScopeDetails::from_scope_id(new_id) + .with_scope_name(scope_name) + .with_function_name(function_name) + .with_file(file_path) + .with_line_nr(line_nr), + ); + new_id + } + + /// Marks the beginning of the scope. + /// Returns position where to write scope size once the scope is closed. + #[must_use] + pub fn begin_scope(&mut self, scope_id: ScopeId, data: &str) -> usize { + self.depth += 1; + + let (offset, start_ns) = self + .stream_info + .stream + .begin_scope(self.now_ns, scope_id, data); + + self.stream_info.range_ns.0 = self.stream_info.range_ns.0.min(start_ns); + self.start_time_ns = Some(self.start_time_ns.unwrap_or(start_ns)); + + offset + } + + /// Marks the end of the scope. + /// Returns the current depth. + pub fn end_scope(&mut self, start_offset: usize) { + let now_ns = (self.now_ns)(); + self.stream_info.depth = self.stream_info.depth.max(self.depth); + self.stream_info.num_scopes += 1; + self.stream_info.range_ns.1 = self.stream_info.range_ns.1.max(now_ns); + + if self.depth > 0 { + self.depth -= 1; + } else { + eprintln!("puffin ERROR: Mismatched scope begin/end calls"); + } + + self.stream_info.stream.end_scope(start_offset, now_ns); + + if self.depth == 0 { + // We have no open scopes. + // This is a good time to report our profiling stream to the global profiler: + let info = ThreadInfo { + start_time_ns: self.start_time_ns, + name: std::thread::current().name().unwrap_or_default().to_owned(), + }; + (self.reporter)( + info, + &self.scope_details, + &self.stream_info.as_stream_into_ref(), + ); + + self.scope_details.clear(); + self.stream_info.clear(); + } + } + + /// Do something with the thread local [`ThreadProfiler`] + #[inline] + pub fn call(f: impl Fn(&mut Self) -> R) -> R { + thread_local! { + pub static THREAD_PROFILER: std::cell::RefCell = Default::default(); + } + THREAD_PROFILER.with(|p| f(&mut p.borrow_mut())) + } +} + +/// Used to identify one source of profiling data. +#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct ThreadInfo { + /// Useful for ordering threads. + pub start_time_ns: Option, + /// Name of the thread + pub name: String, +} + +// Function interface for reporting thread local scope details. +// The scope details array will contain information about a scope the first time it is seen. +// The stream will always contain the scope timing details. +type ThreadReporter = fn(ThreadInfo, &[ScopeDetails], &StreamInfoRef<'_>); diff --git a/puffin/src/utils.rs b/puffin/src/utils.rs new file mode 100644 index 00000000..f7c1175e --- /dev/null +++ b/puffin/src/utils.rs @@ -0,0 +1,143 @@ +// The macro defines 'f()' at the place where macro is called. +// This code is located at the place of call and two closures deep. +// Strip away this useless suffix. +pub(crate) const USELESS_SCOPE_NAME_SUFFIX: &str = "::{{closure}}::{{closure}}::f"; + +#[doc(hidden)] +#[inline(never)] +pub fn clean_function_name(name: &str) -> String { + let Some(name) = name.strip_suffix(USELESS_SCOPE_NAME_SUFFIX) else { + // Probably the user registered a user scope name. + return name.to_owned(); + }; + shorten_rust_function_name(name) +} + +/// Shorten a rust function name by removing the leading parts of module paths. +/// +/// While the puffin profiling macros takes care of this internally, this function can be +/// useful for those registering custom scopes for rust functions. +/// +/// # Example +/// ``` +/// use puffin::shorten_rust_function_name; +/// +/// assert_eq!(shorten_rust_function_name("foo::bar::baz::function_name"), "baz::function_name"); +/// assert_eq!(shorten_rust_function_name("::function_name"), "::function_name"); +/// ``` +pub fn shorten_rust_function_name(name: &str) -> String { + // "foo::bar::baz" -> "baz" + fn last_part(name: &str) -> &str { + if let Some(colon) = name.rfind("::") { + &name[colon + 2..] + } else { + name + } + } + + // look for: ::function_name + if let Some(end_caret) = name.rfind('>') { + if let Some(trait_as) = name.rfind(" as ") { + if trait_as < end_caret { + let concrete_name = if let Some(start_caret) = name[..trait_as].rfind('<') { + &name[start_caret + 1..trait_as] + } else { + name + }; + + let trait_name = &name[trait_as + 4..end_caret]; + + let concrete_name = last_part(concrete_name); + let trait_name = last_part(trait_name); + + let dubcolon_function_name = &name[end_caret + 1..]; + return format!("<{concrete_name} as {trait_name}>{dubcolon_function_name}"); + } + } + } + + if let Some(colon) = name.rfind("::") { + if let Some(colon) = name[..colon].rfind("::") { + // "foo::bar::baz::function_name" -> "baz::function_name" + name[colon + 2..].to_owned() + } else { + // "foo::function_name" -> "foo::function_name" + name.to_owned() + } + } else { + name.to_owned() + } +} + +/// Shortens a long `file!()` path to the essentials. +/// +/// We want to keep it short for two reasons: readability, and bandwidth +#[doc(hidden)] +#[inline(never)] +pub fn short_file_name(path: &str) -> String { + if path.is_empty() { + return "".to_string(); + } + + let path = path.replace('\\', "/"); // Handle Windows + let components: Vec<&str> = path.split('/').collect(); + if components.len() <= 2 { + return path; + } + + // Look for `src` folder: + + let mut src_idx = None; + for (i, c) in components.iter().enumerate() { + if *c == "src" { + src_idx = Some(i); + } + } + + if let Some(src_idx) = src_idx { + // Before `src` comes the name of the crate - let's include that: + let crate_index = src_idx.saturating_sub(1); + let file_index = components.len() - 1; + + if crate_index + 2 == file_index { + // Probably "crate/src/lib.rs" - include it all + format!( + "{}/{}/{}", + components[crate_index], + components[crate_index + 1], + components[file_index] + ) + } else if components[file_index] == "lib.rs" { + // "lib.rs" is very unhelpful - include folder name: + let folder_index = file_index - 1; + + if crate_index + 1 == folder_index { + format!( + "{}/{}/{}", + components[crate_index], components[folder_index], components[file_index] + ) + } else { + // Ellide for brevity: + format!( + "{}/…/{}/{}", + components[crate_index], components[folder_index], components[file_index] + ) + } + } else { + // Ellide for brevity: + format!("{}/…/{}", components[crate_index], components[file_index]) + } + } else { + // No `src` directory found - could be an example (`examples/hello_world.rs`). + // Include the folder and file name. + let n = components.len(); + // NOTE: we've already checked that n > 1 easily in the function + format!("{}/{}", components[n - 2], components[n - 1]) + } +} + +#[doc(hidden)] +#[inline(always)] +pub fn type_name_of(_: T) -> &'static str { + std::any::type_name::() +} diff --git a/puffin_egui/CHANGELOG.md b/puffin_egui/CHANGELOG.md index dab45a9b..e9d178df 100644 --- a/puffin_egui/CHANGELOG.md +++ b/puffin_egui/CHANGELOG.md @@ -1,11 +1,39 @@ -# `egui_puffin` changelog +# `puffin_egui` changelog All notable changes to the egui crate will be documented in this file. ## [Unreleased] - ReleaseDate + +- [PR#218](https://github.com/EmbarkStudios/puffin/pull/218) Fix flamegraph click intersection + +## [0.27.1] - 2024-06-16 + +- [PR#211](https://github.com/EmbarkStudios/puffin/pull/211/) Fix broken flamegraph interaction with egui 0.27.1 + +## [0.27.0] - 2024-04-06 +- [PR#201](https://github.com/EmbarkStudios/puffin/pull/201) Update to egui `0.27` + +## [0.26.0] - 2024-02-14 + +- [PR#188](https://github.com/EmbarkStudios/puffin/pull/188) Update to egui `0.26` +- [PR#186](https://github.com/EmbarkStudios/puffin/pull/186) Change default value of the `merge_scopes` flamegraph option from `true` to `false` + +## [0.25.0] - 2024-01-17 + +- [PR#179](https://github.com/EmbarkStudios/puffin/pull/179) Update to egui `0.25` + +## [0.24.0] - 2023-11-24 + +- [PR#166](https://github.com/EmbarkStudios/puffin/pull/166) Add `show_viewport_if_enabled` method, which will show the profiler UI in a separate viewport, if possible. +- [PR#161](https://github.com/EmbarkStudios/puffin/pull/166) Update to egui `0.24` + +## [0.23.0] - 2023-09-28 + +- [PR#161](https://github.com/EmbarkStudios/puffin/pull/161) Update to egui `0.23` + ## [0.22.0] - 2023-05-24 * [PR#137](https://github.com/EmbarkStudios/puffin/pull/137) Upgrade to `egui` and `eframe` 0.22 @@ -152,7 +180,13 @@ All notable changes to the egui crate will be documented in this file. - The view supports viewing merged sibling scopes. -[Unreleased]: https://github.com/EmbarkStudios/puffin/compare/puffin_egui-0.22.0...HEAD +[Unreleased]: https://github.com/EmbarkStudios/puffin/compare/puffin_egui-0.27.1...HEAD +[0.27.1]: https://github.com/EmbarkStudios/puffin/compare/puffin_egui-0.27.0...puffin_egui-0.27.1 +[0.27.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_egui-0.26.0...puffin_egui-0.27.0 +[0.26.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_egui-0.25.0...puffin_egui-0.26.0 +[0.25.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_egui-0.24.0...puffin_egui-0.25.0 +[0.24.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_egui-0.23.0...puffin_egui-0.24.0 +[0.23.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_egui-0.22.0...puffin_egui-0.23.0 [0.22.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_egui-0.21.0...puffin_egui-0.22.0 [0.21.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_egui-0.20.0...puffin_egui-0.21.0 [0.20.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_egui-0.19.2...puffin_egui-0.20.0 diff --git a/puffin_egui/Cargo.toml b/puffin_egui/Cargo.toml index 71e0a8a6..3d30cc57 100644 --- a/puffin_egui/Cargo.toml +++ b/puffin_egui/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "puffin_egui" -version = "0.22.0" +version = "0.27.1" authors = ["Emil Ernerfeldt "] description = "Show puffin profiler flamegraph in-game using egui" edition = "2018" @@ -21,26 +21,26 @@ include = [ # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -egui = { version = "0.22.0", default-features = false } -indexmap = { version = "1.9.1", features = ["serde"] } -instant = "0.1" +egui = { version = "0.27.1", default-features = false } +indexmap = { version = "2.1.0", features = ["serde"] } natord = "1.0.9" once_cell = "1.7" -puffin = { version = "0.16.0", path = "../puffin", features = ["packing"] } +parking_lot = "0.12" +puffin = { version = "0.19.0", path = "../puffin", features = ["packing"] } serde = { version = "1.0", features = ["derive"], optional = true } time = { version = "0.3.17", default-features = false, features = [ "formatting", "macros", ] } vec1 = "1.8" +web-time = "0.2" [dev-dependencies] -eframe = { version = "0.22.0", default-features = false, features = [ +eframe = { version = "0.27.1", default-features = false, features = [ "default_fonts", "glow", - "persistence", ] } -# Enable example when this dependency is updated to egui 0.22 +# Enable example when this dependency is updated to latest egui # egui-macroquad = "0.12.0" # macroquad = "0.3" diff --git a/puffin_egui/examples/eframe.rs b/puffin_egui/examples/eframe.rs index 7fa80643..7cf4f372 100644 --- a/puffin_egui/examples/eframe.rs +++ b/puffin_egui/examples/eframe.rs @@ -1,9 +1,10 @@ use eframe::egui; fn main() -> eframe::Result<()> { - puffin::set_scopes_on(true); // Remember to call this, or puffin will be disabled! - - let native_options = Default::default(); + let native_options = eframe::NativeOptions { + viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]), + ..Default::default() + }; eframe::run_native( "puffin egui eframe", native_options, @@ -14,23 +15,38 @@ fn main() -> eframe::Result<()> { #[derive(Default)] pub struct ExampleApp { frame_counter: u64, + keep_repainting: bool, } impl eframe::App for ExampleApp { - fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { puffin::profile_function!(); - puffin::GlobalProfiler::lock().new_frame(); // call once per frame! + puffin::GlobalProfiler::lock().new_frame(); // If you use the `puffin` feature of `eframe` you don't need to call this egui::CentralPanel::default().show(ctx, |ui| { + let mut profile = puffin::are_scopes_on(); + ui.checkbox(&mut profile, "Show profiler window"); + puffin::set_scopes_on(profile); // controls both the profile capturing, and the displaying of it + + ui.horizontal(|ui| { + ui.checkbox(&mut self.keep_repainting, "Keep repainting this window"); + if self.keep_repainting { + ui.spinner(); + ui.ctx().request_repaint(); + } + }); + if ui.button("Quit").clicked() { - frame.close() + ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close); } }); - puffin_egui::profiler_window(ctx); + // This call does nothing if profiling is disabled + puffin_egui::show_viewport_if_enabled(ctx); - // Give us something to inspect: + // ---------------------------------------------------------------- + // Give us something to inspect: std::thread::Builder::new() .name("Other thread".to_owned()) .spawn(|| { diff --git a/puffin_egui/src/flamegraph.rs b/puffin_egui/src/flamegraph.rs index c5f628f9..4b4be7c3 100644 --- a/puffin_egui/src/flamegraph.rs +++ b/puffin_egui/src/flamegraph.rs @@ -150,7 +150,7 @@ impl Default for Options { frame_list_height: 48.0, frame_width: 10.0, - merge_scopes: true, + merge_scopes: false, // off, because it really only works well for single-threaded profiling sorting: Default::default(), filter: Default::default(), @@ -162,7 +162,7 @@ impl Default for Options { } /// Context for painting a frame. -struct Info { +struct Info<'a> { ctx: egui::Context, /// Bounding box of canvas in points: canvas: Rect, @@ -178,6 +178,8 @@ struct Info { num_frames: usize, font_id: FontId, + + scope_collection: &'a ScopeCollection, } #[derive(Clone, Copy, Eq, PartialEq)] @@ -187,7 +189,7 @@ enum PaintResult { Normal, } -impl Info { +impl<'a> Info<'a> { fn point_from_ns(&self, options: &Options, ns: NanoSecond) -> f32 { self.canvas.min.x + options.sideways_pan_in_points @@ -196,7 +198,12 @@ impl Info { } /// Show the flamegraph. -pub fn ui(ui: &mut egui::Ui, options: &mut Options, frames: &SelectedFrames) { +pub fn ui( + ui: &mut egui::Ui, + options: &mut Options, + scope_collection: &ScopeCollection, + frames: &SelectedFrames, +) { puffin::profile_function!(); let mut reset_view = false; @@ -214,40 +221,42 @@ pub fn ui(ui: &mut egui::Ui, options: &mut Options, frames: &SelectedFrames) { ui.memory_mut(|m| m.data.insert_temp(num_frames_id, num_frames)); } - ui.columns(2, |ui| { - ui[0].horizontal(|ui| { - ui.colored_label(ui.visuals().widgets.inactive.text_color(), "❓") - .on_hover_text( - "Drag to pan.\n\ + ui.horizontal(|ui| { + ui.vertical(|ui| { + ui.horizontal(|ui| { + ui.colored_label(ui.visuals().widgets.inactive.text_color(), "❓") + .on_hover_text( + "Drag to pan.\n\ Zoom: Ctrl/cmd + scroll, or drag with secondary mouse button.\n\ Click on a scope to zoom to it.\n\ Double-click to reset view.\n\ Press spacebar to pause/resume.", - ); + ); + + ui.separator(); + + ui.horizontal(|ui| { + let changed = ui + .checkbox(&mut options.merge_scopes, "Merge children with same ID") + .changed(); + // If we have multiple frames selected this will toggle + // if we view all the frames, or an average of them, + // and that difference is pretty massive, so help the user: + if changed && num_frames > 1 { + reset_view = true; + } + }); - ui.separator(); + ui.separator(); - ui.horizontal(|ui| { - let changed = ui - .checkbox(&mut options.merge_scopes, "Merge children with same ID") - .changed(); - // If we have multiple frames selected this will toggle - // if we view all the frames, or an average of them, - // and that difference is pretty massive, so help the user: - if changed && num_frames > 1 { - reset_view = true; - } + // The number of threads can change between frames, so always show this even if there currently is only one thread: + options.sorting.ui(ui); }); - ui.separator(); - - // The number of threads can change between frames, so always show this even if there currently is only one thread: - options.sorting.ui(ui); + options.filter.ui(ui); }); - options.filter.ui(&mut ui[0]); - - ui[1].collapsing("Visible Threads", |ui| { + ui.collapsing("Visible Threads", |ui| { egui::ScrollArea::vertical() .max_height(150.0) .id_source("f") @@ -256,7 +265,7 @@ pub fn ui(ui: &mut egui::Ui, options: &mut Options, frames: &SelectedFrames) { let entry = options .flamegraph_threads .entry(f.name.clone()) - .or_insert_with(ThreadVisualizationSettings::default); + .or_default(); ui.checkbox(&mut entry.flamegraph_show, f.name.clone()); } }); @@ -270,7 +279,7 @@ pub fn ui(ui: &mut egui::Ui, options: &mut Options, frames: &SelectedFrames) { ScrollArea::vertical().show(ui, |ui| { let mut canvas = ui.available_rect_before_wrap(); canvas.max.y = f32::INFINITY; - let response = ui.interact(canvas, ui.id(), Sense::click_and_drag()); + let response = ui.interact(canvas, ui.id().with("canvas"), Sense::click_and_drag()); let (min_ns, max_ns) = if options.merge_scopes { frames.merged_range_ns @@ -288,6 +297,7 @@ pub fn ui(ui: &mut egui::Ui, options: &mut Options, frames: &SelectedFrames) { stop_ns: max_ns, num_frames: frames.frames.len(), font_id: TextStyle::Body.resolve(ui.style()), + scope_collection, }; if reset_view { @@ -320,7 +330,7 @@ pub fn ui(ui: &mut egui::Ui, options: &mut Options, frames: &SelectedFrames) { fn ui_canvas( options: &mut Options, - info: &Info, + info: &Info<'_>, frames: &SelectedFrames, (min_ns, max_ns): (NanoSecond, NanoSecond), ) -> f32 { @@ -342,7 +352,7 @@ fn ui_canvas( let thread_visualization = options .flamegraph_threads .entry(thread_info.name.clone()) - .or_insert_with(ThreadVisualizationSettings::default); + .or_default(); if !thread_visualization.flamegraph_show { continue; @@ -377,7 +387,7 @@ fn ui_canvas( let mut paint_streams = || -> Result<()> { if options.merge_scopes { for merge in &frames.threads[&thread_info].merged_scopes { - paint_merge_scope(info, options, 0, merge, 0, cursor_y)?; + paint_merge_scope(info, options, 0, merge, 0, cursor_y); } } else { for stream_info in &frames.threads[&thread_info].streams { @@ -411,7 +421,7 @@ fn ui_canvas( cursor_y } -fn interact_with_canvas(options: &mut Options, response: &Response, info: &Info) { +fn interact_with_canvas(options: &mut Options, response: &Response, info: &Info<'_>) { if response.drag_delta().x != 0.0 { options.sideways_pan_in_points += response.drag_delta().x; options.zoom_to_relative_ns_range = None; @@ -419,8 +429,8 @@ fn interact_with_canvas(options: &mut Options, response: &Response, info: &Info) if response.hovered() { // Sideways pan with e.g. a touch pad: - if info.ctx.input(|i| i.scroll_delta.x != 0.0) { - options.sideways_pan_in_points += info.ctx.input(|i| i.scroll_delta.x); + if info.ctx.input(|i| i.smooth_scroll_delta.x != 0.0) { + options.sideways_pan_in_points += info.ctx.input(|i| i.smooth_scroll_delta.x); options.zoom_to_relative_ns_range = None; } @@ -476,7 +486,7 @@ fn interact_with_canvas(options: &mut Options, response: &Response, info: &Info) } fn paint_timeline( - info: &Info, + info: &Info<'_>, canvas: Rect, options: &Options, start_ns: NanoSecond, @@ -588,16 +598,18 @@ fn grid_text(grid_ns: NanoSecond) -> String { } } +#[allow(clippy::too_many_arguments)] fn paint_record( - info: &Info, + info: &Info<'_>, options: &mut Options, prefix: &str, suffix: &str, - record: &Record<'_>, + scope_id: ScopeId, + scope_data: &ScopeRecord<'_>, top_y: f32, ) -> PaintResult { - let start_x = info.point_from_ns(options, record.start_ns); - let stop_x = info.point_from_ns(options, record.stop_ns()); + let start_x = info.point_from_ns(options, scope_data.start_ns); + let stop_x = info.point_from_ns(options, scope_data.stop_ns()); if info.canvas.max.x < start_x || stop_x < info.canvas.min.x || stop_x - start_x < options.cull_width @@ -615,18 +627,22 @@ fn paint_record( false }; + let Some(scope_details) = info.scope_collection.fetch_by_id(&scope_id) else { + return PaintResult::Culled; + }; + if info.response.double_clicked() { if let Some(mouse_pos) = info.response.interact_pointer_pos() { if rect.contains(mouse_pos) { - options.filter.set_filter(record.id.to_string()); + options.filter.set_filter(scope_details.name().to_string()); } } } else if is_hovered && info.response.clicked() { options.zoom_to_relative_ns_range = Some(( info.ctx.input(|i| i.time), ( - record.start_ns - info.start_ns, - record.stop_ns() - info.start_ns, + scope_data.start_ns - info.start_ns, + scope_data.stop_ns() - info.start_ns, ), )); } @@ -634,17 +650,18 @@ fn paint_record( let mut rect_color = if is_hovered { HOVER_COLOR } else { - color_from_duration(record.duration_ns) + color_from_duration(scope_data.duration_ns) }; let mut min_width = options.min_width; if !options.filter.is_empty() { - if options.filter.include(record.id) { + if options.filter.include(scope_details.name()) { // keep full opacity min_width *= 2.0; // make it more visible even when thin } else { - rect_color = rect_color.multiply(0.075); // fade to highlight others + // fade to highlight others + rect_color = lerp(Rgba::BLACK..=rect_color, 0.075); } } @@ -662,13 +679,26 @@ fn paint_record( if wide_enough_for_text { let painter = info.painter.with_clip_rect(rect.intersect(info.canvas)); - let duration_ms = to_ms(record.duration_ns); - let text = if record.data.is_empty() { - format!("{}{} {:6.3} ms {}", prefix, record.id, duration_ms, suffix) + let scope_name = scope_details.name(); + + let duration_ms = to_ms(scope_data.duration_ns); + let text = if scope_data.data.is_empty() { + format!( + "{}{} {:6.3} ms {}", + prefix, + scope_name.as_str(), + duration_ms, + suffix + ) } else { + // Note: we don't escape the scope data (`{:?}`), because that often leads to ugly extra backslashes. format!( - "{}{} {:?} {:6.3} ms {}", - prefix, record.id, record.data, duration_ms, suffix + "{}{} '{}' {:6.3} ms {}", + prefix, + scope_name.as_str(), + scope_data.data, + duration_ms, + suffix ) }; let pos = pos2( @@ -709,7 +739,7 @@ fn to_ms(ns: NanoSecond) -> f64 { } fn paint_scope( - info: &Info, + info: &Info<'_>, options: &mut Options, stream: &Stream, scope: &Scope<'_>, @@ -718,7 +748,7 @@ fn paint_scope( ) -> Result { let top_y = min_y + (depth as f32) * (options.rect_height + options.spacing); - let result = paint_record(info, options, "", "", &scope.record, top_y); + let result = paint_record(info, options, "", "", scope.id, &scope.record, top_y); if result != PaintResult::Culled { let mut num_children = 0; @@ -728,19 +758,17 @@ fn paint_scope( } if result == PaintResult::Hovered { + let Some(scope_details) = info.scope_collection.fetch_by_id(&scope.id) else { + return Ok(PaintResult::Culled); + }; egui::show_tooltip_at_pointer(&info.ctx, Id::new("puffin_profiler_tooltip"), |ui| { - ui.monospace(format!("id: {}", scope.record.id)); - if !scope.record.location.is_empty() { - ui.monospace(format!("location: {}", scope.record.location)); - } - if !scope.record.data.is_empty() { - ui.monospace(format!("data: {}", scope.record.data)); - } + paint_scope_details(ui, scope.id, scope.record.data, scope_details); + add_space(ui); ui.monospace(format!( "duration: {:7.3} ms", to_ms(scope.record.duration_ns) )); - ui.monospace(format!("children: {num_children}")); + ui.monospace(format!("children: {num_children:3}")); }); } } @@ -749,13 +777,13 @@ fn paint_scope( } fn paint_merge_scope( - info: &Info, + info: &Info<'_>, options: &mut Options, ns_offset: NanoSecond, merge: &MergeScope<'_>, depth: usize, min_y: f32, -) -> Result { +) -> PaintResult { let top_y = min_y + (depth as f32) * (options.rect_height + options.spacing); let prefix = if info.num_frames <= 1 { @@ -779,42 +807,80 @@ fn paint_merge_scope( "per frame" }; - let record = Record { + let record = ScopeRecord { start_ns: ns_offset + merge.relative_start_ns, duration_ns: merge.duration_per_frame_ns, - id: &merge.id, - location: &merge.location, data: &merge.data, }; - let result = paint_record(info, options, &prefix, suffix, &record, top_y); + let result = paint_record(info, options, &prefix, suffix, merge.id, &record, top_y); if result != PaintResult::Culled { for child in &merge.children { - paint_merge_scope(info, options, record.start_ns, child, depth + 1, min_y)?; + paint_merge_scope(info, options, record.start_ns, child, depth + 1, min_y); } if result == PaintResult::Hovered { egui::show_tooltip_at_pointer(&info.ctx, Id::new("puffin_profiler_tooltip"), |ui| { - merge_scope_tooltip(ui, merge, info.num_frames); + merge_scope_tooltip(ui, info.scope_collection, merge, info.num_frames); }); } } - Ok(result) + result } -fn merge_scope_tooltip(ui: &mut egui::Ui, merge: &MergeScope<'_>, num_frames: usize) { +fn paint_scope_details(ui: &mut Ui, scope_id: ScopeId, data: &str, scope_details: &ScopeDetails) { + egui::Grid::new("scope_details_tooltip") + .num_columns(2) + .show(ui, |ui| { + ui.monospace("id"); + ui.monospace(format!("{}", scope_id.0)); + ui.end_row(); + + ui.monospace("function name"); + ui.monospace(scope_details.function_name.as_str()); + ui.end_row(); + + if let Some(scope_name) = &scope_details.scope_name { + ui.monospace("scope name"); + ui.monospace(scope_name.as_str()); + ui.end_row(); + } + + if !scope_details.file_path.is_empty() { + ui.monospace("location"); + ui.monospace(scope_details.location()); + ui.end_row(); + } + + if !data.is_empty() { + ui.monospace("data"); + ui.monospace(data.as_str()); + ui.end_row(); + } + + ui.monospace("scope type"); + ui.monospace(scope_details.scope_type().type_str()); + ui.end_row(); + }); +} + +fn merge_scope_tooltip( + ui: &mut egui::Ui, + scope_collection: &ScopeCollection, + merge: &MergeScope<'_>, + num_frames: usize, +) { #![allow(clippy::collapsible_else_if)] - ui.monospace(format!("id: {}", merge.id)); - if !merge.location.is_empty() { - ui.monospace(format!("location: {}", merge.location)); - } - if !merge.data.is_empty() { - ui.monospace(format!("data: {}", merge.data)); - } - ui.add_space(8.0); + let Some(scope_details) = scope_collection.fetch_by_id(&merge.id) else { + return; + }; + + paint_scope_details(ui, merge.id, &merge.data, scope_details); + + add_space(ui); if num_frames <= 1 { if merge.num_pieces <= 1 { @@ -863,18 +929,17 @@ fn merge_scope_tooltip(ui: &mut egui::Ui, merge: &MergeScope<'_>, num_frames: us "{:7.3} ms for slowest call", to_ms(merge.max_duration_ns) )); - // } } } -fn paint_thread_info(info: &Info, thread: &ThreadInfo, pos: Pos2, collapsed: &mut bool) { +fn paint_thread_info(info: &Info<'_>, thread: &ThreadInfo, pos: Pos2, collapsed: &mut bool) { let collapsed_symbol = if *collapsed { "⏵" } else { "⏷" }; let galley = info.ctx.fonts(|f| { f.layout_no_wrap( format!("{} {}", collapsed_symbol, thread.name.clone()), info.font_id.clone(), - Rgba::from_white_alpha(0.9).into(), + egui::Color32::PLACEHOLDER, ) }); @@ -898,9 +963,14 @@ fn paint_thread_info(info: &Info, thread: &ThreadInfo, pos: Pos2, collapsed: &mu }; info.painter.rect_filled(rect.expand(2.0), 0.0, back_color); - info.painter.galley_with_color(rect.min, galley, text_color); + info.painter.galley(rect.min, galley, text_color); if is_hovered && info.response.clicked() { *collapsed = !(*collapsed); } } + +fn add_space(ui: &mut Ui) { + // a separator will make the parent tooltip unnecessarily wide (immediate mode problems) + ui.add_space(8.0); +} diff --git a/puffin_egui/src/lib.rs b/puffin_egui/src/lib.rs index cbb5e2a2..4f8b7e5d 100644 --- a/puffin_egui/src/lib.rs +++ b/puffin_egui/src/lib.rs @@ -7,86 +7,7 @@ //! puffin_egui::profiler_window(&egui_ctx); //! ``` -// BEGIN - Embark standard lints v5 for Rust 1.55+ -// do not change or add/remove here, but one can add exceptions after this section -// for more info see: -#![deny(unsafe_code)] -#![warn( - clippy::all, - clippy::await_holding_lock, - clippy::char_lit_as_u8, - clippy::checked_conversions, - clippy::dbg_macro, - clippy::debug_assert_with_mut_call, - clippy::disallowed_methods, - clippy::disallowed_types, - clippy::doc_markdown, - clippy::empty_enum, - clippy::enum_glob_use, - clippy::exit, - clippy::expl_impl_clone_on_copy, - clippy::explicit_deref_methods, - clippy::explicit_into_iter_loop, - clippy::fallible_impl_from, - clippy::filter_map_next, - clippy::flat_map_option, - clippy::float_cmp_const, - clippy::fn_params_excessive_bools, - clippy::from_iter_instead_of_collect, - clippy::if_let_mutex, - clippy::implicit_clone, - clippy::imprecise_flops, - clippy::inefficient_to_string, - clippy::invalid_upcast_comparisons, - clippy::large_digit_groups, - clippy::large_stack_arrays, - clippy::large_types_passed_by_value, - clippy::let_unit_value, - clippy::linkedlist, - clippy::lossy_float_literal, - clippy::macro_use_imports, - clippy::manual_ok_or, - clippy::map_err_ignore, - clippy::map_flatten, - clippy::map_unwrap_or, - clippy::match_on_vec_items, - clippy::match_same_arms, - clippy::match_wild_err_arm, - clippy::match_wildcard_for_single_variants, - clippy::mem_forget, - clippy::mismatched_target_os, - clippy::missing_enforced_import_renames, - clippy::mut_mut, - clippy::mutex_integer, - clippy::needless_borrow, - clippy::needless_continue, - clippy::needless_for_each, - clippy::option_option, - clippy::path_buf_push_overwrite, - clippy::ptr_as_ptr, - clippy::rc_mutex, - clippy::ref_option_ref, - clippy::rest_pat_in_fully_bound_structs, - clippy::same_functions_in_if_condition, - clippy::semicolon_if_nothing_returned, - clippy::single_match_else, - clippy::string_add_assign, - clippy::string_add, - clippy::string_lit_as_bytes, - clippy::string_to_string, - clippy::todo, - clippy::trait_duplication_in_bounds, - clippy::unimplemented, - clippy::unnested_or_patterns, - clippy::unused_self, - clippy::useless_transmute, - clippy::verbose_file_reads, - clippy::zero_sized_map_values, - future_incompatible, - nonstandard_style, - rust_2018_idioms -)] -// END - Embark standard lints v0.5 for Rust 1.55+ +#![forbid(unsafe_code)] // crate-specific exceptions: #![allow(clippy::float_cmp, clippy::manual_range_contains)] @@ -102,7 +23,7 @@ use puffin::*; use std::{ collections::{BTreeMap, BTreeSet}, fmt::Write as _, - sync::{Arc, Mutex}, + sync::Arc, }; use time::OffsetDateTime; @@ -111,6 +32,42 @@ const HOVER_COLOR: Rgba = Rgba::from_rgb(0.8, 0.8, 0.8); // ---------------------------------------------------------------------------- +/// Show the puffin profiler if [`puffin::are_scopes_on`] is true, +/// i.e. if profiling is enabled for your app. +/// +/// The profiler will be shown in its own viewport (native window) +/// if the egui backend supports it (e.g. when using `eframe`); +/// else it will be shown in a floating [`egui::Window`]. +/// +/// Closing the viewport or window will call `puffin::set_scopes_on(false)`. +pub fn show_viewport_if_enabled(ctx: &egui::Context) { + if !puffin::are_scopes_on() { + return; + } + + ctx.show_viewport_deferred( + egui::ViewportId::from_hash_of("puffin_profiler"), + egui::ViewportBuilder::default().with_title("Puffin Profiler"), + move |ctx, class| { + if class == egui::ViewportClass::Embedded { + // Viewports not supported. Show it as a floating egui window instead. + let mut open = true; + egui::Window::new("Puffin Profiler") + .default_size([1024.0, 600.0]) + .open(&mut open) + .show(ctx, profiler_ui); + puffin::set_scopes_on(open); + } else { + // A proper viewport! + egui::CentralPanel::default().show(ctx, profiler_ui); + if ctx.input(|i| i.viewport().close_requested()) { + puffin::set_scopes_on(false); + } + } + }, + ); +} + /// Show an [`egui::Window`] with the profiler contents. /// /// If you want to control the window yourself, use [`profiler_ui`] instead. @@ -126,14 +83,14 @@ pub fn profiler_window(ctx: &egui::Context) -> bool { open } -static PROFILE_UI: once_cell::sync::Lazy> = +static PROFILE_UI: once_cell::sync::Lazy> = once_cell::sync::Lazy::new(Default::default); /// Show the profiler. /// /// Call this from within an [`egui::Window`], or use [`profiler_window`] instead. pub fn profiler_ui(ui: &mut egui::Ui) { - let mut profile_ui = PROFILE_UI.lock().unwrap(); + let mut profile_ui = PROFILE_UI.lock(); profile_ui.ui(ui); } @@ -209,7 +166,11 @@ pub struct Streams { } impl Streams { - fn new(frames: &[Arc], thread_info: &ThreadInfo) -> Self { + fn new( + scope_collection: &ScopeCollection, + frames: &[Arc], + thread_info: &ThreadInfo, + ) -> Self { crate::profile_function!(); let mut streams = vec![]; @@ -221,7 +182,7 @@ impl Streams { let merges = { puffin::profile_scope!("merge_scopes_for_thread"); - puffin::merge_scopes_for_thread(frames, thread_info).unwrap() + puffin::merge_scopes_for_thread(scope_collection, frames, thread_info).unwrap() }; let merges = merges.into_iter().map(|ms| ms.into_owned()).collect(); @@ -250,12 +211,18 @@ pub struct SelectedFrames { } impl SelectedFrames { - fn try_from_vec(frames: Vec>) -> Option { + fn try_from_vec( + scope_collection: &ScopeCollection, + frames: Vec>, + ) -> Option { let frames = vec1::Vec1::try_from_vec(frames).ok()?; - Some(Self::from_vec1(frames)) + Some(Self::from_vec1(scope_collection, frames)) } - fn from_vec1(mut frames: vec1::Vec1>) -> Self { + fn from_vec1( + scope_collection: &ScopeCollection, + mut frames: vec1::Vec1>, + ) -> Self { puffin::profile_function!(); frames.sort_by_key(|f| f.frame_index()); frames.dedup_by_key(|f| f.frame_index()); @@ -269,7 +236,7 @@ impl SelectedFrames { let threads: BTreeMap = threads .iter() - .map(|ti| (ti.clone(), Streams::new(&frames, ti))) + .map(|ti| (ti.clone(), Streams::new(scope_collection, &frames, ti))) .collect(); let mut merged_min_ns = NanoSecond::MAX; @@ -323,11 +290,14 @@ impl Default for View { #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] #[cfg_attr(feature = "serde", serde(default))] pub struct ProfilerUi { + /// Options for configuring how the flamegraph is displayed. #[cfg_attr(feature = "serde", serde(alias = "options"))] pub flamegraph_options: flamegraph::Options, + /// Options for configuring how the stats page is displayed. #[cfg_attr(feature = "serde", serde(skip))] pub stats_options: stats::Options, + /// What view is active. pub view: View, /// If `None`, we show the latest frames. @@ -342,7 +312,7 @@ pub struct ProfilerUi { /// When did we last run a pass to pack all the frames? #[cfg_attr(feature = "serde", serde(skip))] - last_pack_pass: Option, + last_pack_pass: Option, } impl Default for ProfilerUi { @@ -437,16 +407,16 @@ impl ProfilerUi { } let last_pack_pass = self .last_pack_pass - .get_or_insert_with(instant::Instant::now); + .get_or_insert_with(web_time::Instant::now); let time_since_last_pack = last_pack_pass.elapsed(); - if time_since_last_pack > instant::Duration::from_secs(1) { + if time_since_last_pack > web_time::Duration::from_secs(1) { puffin::profile_scope!("pack_pass"); for frame in self.all_known_frames(frame_view) { if !self.is_selected(frame_view, frame.frame_index()) { frame.pack(); } } - self.last_pack_pass = Some(instant::Instant::now()); + self.last_pack_pass = Some(web_time::Instant::now()); } } @@ -479,7 +449,9 @@ impl ProfilerUi { let frames = if let Some(frame) = hovered_frame { match frame.unpacked() { - Ok(frame) => SelectedFrames::try_from_vec(vec![frame]), + Ok(frame) => { + SelectedFrames::try_from_vec(frame_view.scope_collection(), vec![frame]) + } Err(err) => { ui.colored_label(ERROR_COLOR, format!("Failed to load hovered frame: {err}")); return; @@ -490,13 +462,12 @@ impl ProfilerUi { } else { puffin::profile_scope!("select_latest_frames"); let latest = frame_view.latest_frames(self.max_num_latest); - SelectedFrames::try_from_vec( - latest - .into_iter() - .map(|frame| frame.unpacked()) - .filter_map(|unpacked| unpacked.ok()) - .collect(), - ) + let unpacked: Vec> = latest + .into_iter() + .map(|frame| frame.unpacked()) + .filter_map(|unpacked| unpacked.ok()) + .collect(); + SelectedFrames::try_from_vec(frame_view.scope_collection(), unpacked) }; let frames = if let Some(frames) = frames { @@ -508,12 +479,15 @@ impl ProfilerUi { ui.horizontal(|ui| { let play_pause_button_size = Vec2::splat(24.0); + let space_pressed = ui.input(|i| i.key_pressed(egui::Key::Space)) + && ui.memory(|m| m.focused().is_none()); + if self.paused.is_some() { if ui .add_sized(play_pause_button_size, egui::Button::new("▶")) .on_hover_text("Show latest data. Toggle with space.") .clicked() - || ui.input(|i| i.key_pressed(egui::Key::Space)) + || space_pressed { self.paused = None; } @@ -523,14 +497,17 @@ impl ProfilerUi { .add_sized(play_pause_button_size, egui::Button::new("⏸")) .on_hover_text("Pause on this frame. Toggle with space.") .clicked() - || ui.input(|i| i.key_pressed(egui::Key::Space)) + || space_pressed { let latest = frame_view.latest_frame(); if let Some(latest) = latest { if let Ok(latest) = latest.unpacked() { self.pause_and_select( frame_view, - SelectedFrames::from_vec1(vec1::vec1![latest]), + SelectedFrames::from_vec1( + frame_view.scope_collection(), + vec1::vec1![latest], + ), ); } } @@ -555,8 +532,18 @@ impl ProfilerUi { ui.separator(); match self.view { - View::Flamegraph => flamegraph::ui(ui, &mut self.flamegraph_options, &frames), - View::Stats => stats::ui(ui, &mut self.stats_options, &frames.frames), + View::Flamegraph => flamegraph::ui( + ui, + &mut self.flamegraph_options, + frame_view.scope_collection(), + &frames, + ), + View::Stats => stats::ui( + ui, + &mut self.stats_options, + frame_view.scope_collection(), + &frames.frames, + ), } } @@ -676,7 +663,7 @@ impl ProfilerUi { }; let desired_size = Vec2::new(desired_width, self.flamegraph_options.frame_list_height); - let (response, painter) = ui.allocate_painter(desired_size, Sense::click_and_drag()); + let (response, painter) = ui.allocate_painter(desired_size, Sense::drag()); let rect = response.rect; let frame_spacing = 2.0; @@ -704,7 +691,8 @@ impl ProfilerUi { let frame_rect = Rect::from_min_max( Pos2::new(x, rect.top()), Pos2::new(x + frame_width, rect.bottom()), - ); + ) + .expand2(vec2(0.5 * frame_spacing, 0.0)); if ui.clip_rect().intersects(frame_rect) { let duration = frame.duration_ns(); @@ -713,11 +701,7 @@ impl ProfilerUi { let is_selected = self.is_selected(frame_view, frame.frame_index()); let is_hovered = if let Some(mouse_pos) = response.hover_pos() { - response.hovered() - && !response.dragged() - && frame_rect - .expand2(vec2(0.5 * frame_spacing, 0.0)) - .contains(mouse_pos) + !response.dragged() && frame_rect.contains(mouse_pos) } else { false }; @@ -757,21 +741,27 @@ impl ProfilerUi { Rgba::from_rgb(0.6, 0.6, 0.4) }; + // Shrink the rect as the visual representation of the frame rect includes empty + // space between each bar + let visual_rect = frame_rect.expand2(vec2(-0.5 * frame_spacing, 0.0)); + // Transparent, full height: - let alpha = if is_selected || is_hovered { 0.6 } else { 0.25 }; - painter.rect_filled(frame_rect, 0.0, color * alpha); + let alpha: f32 = if is_selected || is_hovered { 0.6 } else { 0.25 }; + painter.rect_filled(visual_rect, 0.0, color * alpha); // Opaque, height based on duration: - let mut short_rect = frame_rect; + let mut short_rect = visual_rect; short_rect.min.y = lerp( - frame_rect.bottom_up_range(), + visual_rect.bottom_up_range(), duration as f32 / slowest_frame, ); painter.rect_filled(short_rect, 0.0, color); } } - if let Some(new_selection) = SelectedFrames::try_from_vec(new_selection) { + if let Some(new_selection) = + SelectedFrames::try_from_vec(frame_view.scope_collection(), new_selection) + { self.pause_and_select(frame_view, new_selection); } @@ -835,7 +825,7 @@ fn format_time(nanos: NanoSecond) -> Option { } } -fn max_frames_ui(ui: &mut egui::Ui, frame_view: &mut FrameView, uniq: &Vec>) { +fn max_frames_ui(ui: &mut egui::Ui, frame_view: &mut FrameView, uniq: &[Arc]) { let stats = frame_view.stats(); let bytes = stats.bytes_of_ram_used(); diff --git a/puffin_egui/src/stats.rs b/puffin_egui/src/stats.rs index 5cf77c48..4661016e 100644 --- a/puffin_egui/src/stats.rs +++ b/puffin_egui/src/stats.rs @@ -1,3 +1,4 @@ +use egui::TextBuffer; use puffin::*; use crate::filter::Filter; @@ -7,7 +8,12 @@ pub struct Options { filter: Filter, } -pub fn ui(ui: &mut egui::Ui, options: &mut Options, frames: &[std::sync::Arc]) { +pub fn ui( + ui: &mut egui::Ui, + options: &mut Options, + scope_infos: &ScopeCollection, + frames: &[std::sync::Arc], +) { let mut threads = std::collections::HashSet::<&ThreadInfo>::new(); let mut stats = Stats::default(); @@ -56,7 +62,8 @@ pub fn ui(ui: &mut egui::Ui, options: &mut Options, frames: &[std::sync::Arc5}", stats.count)); ui.monospace(format!("{:>6.1} kB", stats.bytes as f32 * 1e-3)); ui.monospace(format!("{:>8.1} µs", stats.total_self_ns as f32 * 1e-3)); @@ -87,14 +104,13 @@ pub fn ui(ui: &mut egui::Ui, options: &mut Options, frames: &[std::sync::Arc { - scopes: std::collections::HashMap, ScopeStats>, +struct Stats { + scopes: std::collections::HashMap, } #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] -struct Key<'s> { - id: &'s str, - location: &'s str, +struct Key { + id: ScopeId, thread_name: String, } @@ -110,10 +126,10 @@ struct ScopeStats { max_ns: NanoSecond, } -fn collect_stream<'s>( - stats: &mut Stats<'s>, +fn collect_stream( + stats: &mut Stats, thread_name: &str, - stream: &'s puffin::Stream, + stream: &puffin::Stream, ) -> puffin::Result<()> { for scope in puffin::Reader::from_start(stream) { collect_scope(stats, thread_name, stream, &scope?)?; @@ -122,7 +138,7 @@ fn collect_stream<'s>( } fn collect_scope<'s>( - stats: &mut Stats<'s>, + stats: &mut Stats, thread_name: &str, stream: &'s puffin::Stream, scope: &puffin::Scope<'s>, @@ -137,8 +153,7 @@ fn collect_scope<'s>( let self_time = scope.record.duration_ns.saturating_sub(ns_used_by_children); let key = Key { - id: scope.record.id, - location: scope.record.location, + id: scope.id, thread_name: thread_name.to_owned(), }; let scope_stats = stats.scopes.entry(key).or_default(); @@ -153,9 +168,8 @@ fn collect_scope<'s>( fn scope_byte_size(scope: &puffin::Scope<'_>) -> usize { 1 + // `(` sentinel 8 + // start time - 1 + scope.record.id.len() + // - 1 + scope.record.location.len() + // - 1 + scope.record.data.len() + // + 8 + // scope id + 1 + scope.record.data.len() + // dynamic data len 8 + // scope size 1 + // `)` sentinel 8 // stop time diff --git a/puffin_http/CHANGELOG.md b/puffin_http/CHANGELOG.md index b95a6f4a..98fdaf6d 100644 --- a/puffin_http/CHANGELOG.md +++ b/puffin_http/CHANGELOG.md @@ -6,6 +6,9 @@ All notable changes to `puffin_http` will be documented in this file. ## [Unreleased] - ReleaseDate +## [0.16.0] - 2024-01-17 +## [0.15.0] - 2023-11-21 +## [0.14.0] - 2023-09-28 ## [0.13.0] - 2023-05-24 - Upgrade to `puffin` 0.16 @@ -69,7 +72,10 @@ All notable changes to `puffin_http` will be documented in this file. - Initial release -[Unreleased]: https://github.com/EmbarkStudios/puffin/compare/puffin_http-0.13.0...HEAD +[Unreleased]: https://github.com/EmbarkStudios/puffin/compare/puffin_http-0.16.0...HEAD +[0.16.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_http-0.15.0...puffin_http-0.16.0 +[0.15.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_http-0.14.0...puffin_http-0.15.0 +[0.14.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_http-0.13.0...puffin_http-0.14.0 [0.13.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_http-0.12.0...puffin_http-0.13.0 [0.12.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_http-0.11.1...puffin_http-0.12.0 [0.11.1]: https://github.com/EmbarkStudios/puffin/compare/puffin_http-0.11.0...puffin_http-0.11.1 diff --git a/puffin_http/Cargo.toml b/puffin_http/Cargo.toml index c7e9c4a7..0989d655 100644 --- a/puffin_http/Cargo.toml +++ b/puffin_http/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "puffin_http" -version = "0.13.0" +version = "0.16.0" authors = ["Embark "] description = "TCP server/client for puffin profiler data" license = "MIT OR Apache-2.0" @@ -16,11 +16,14 @@ include = ["**/*.rs", "Cargo.toml", "README.md"] anyhow = "1.0" crossbeam-channel = "0.5" log = "0.4" -puffin = { version = "0.16.0", path = "../puffin", features = [ +parking_lot = "0.12" +puffin = { version = "0.19.0", path = "../puffin", features = [ "packing", "lz4", "serialization", ] } [dev-dependencies] -simple_logger = "2.1" +simple_logger = "4.2" +paste = "1.0.15" +once_cell = "1.19.0" \ No newline at end of file diff --git a/puffin_http/README.md b/puffin_http/README.md index 1720418d..ef04eb6e 100644 --- a/puffin_http/README.md +++ b/puffin_http/README.md @@ -11,13 +11,17 @@ You can view them using [`puffin_viewer`](https://github.com/EmbarkStudios/puffi ## How to use Add a `puffin_http` `Server` to the profiled application -When the server is started, [`puffin_viewer`](https://crates.io/crates/puffin_viewer) application can connect to it and display profiling informations. +When the server is started, [`puffin_viewer`](https://crates.io/crates/puffin_viewer) application can connect to it and display profiling information. ``` rust fn main() { let server_addr = format!("0.0.0.0:{}", puffin_http::DEFAULT_PORT); - puffin_http::Server::new(&server_addr).unwrap(); + let _puffin_server = puffin_http::Server::new(&server_addr).unwrap(); + eprintln!("Serving demo profile data on {server_addr}. Run `puffin_viewer` to view it."); + puffin::set_scopes_on(true); + + // … } ``` -You can checkout the examples/server.rs for a more complete example. \ No newline at end of file +You can checkout the examples/server.rs for a more complete example. diff --git a/puffin_http/examples/server.rs b/puffin_http/examples/server.rs index 2e7422ef..6a8c5ddc 100644 --- a/puffin_http/examples/server.rs +++ b/puffin_http/examples/server.rs @@ -5,11 +5,9 @@ fn main() { .init() .ok(); - let server_addr = format!("0.0.0.0:{}", puffin_http::DEFAULT_PORT); - eprintln!("Serving demo profile data on {server_addr}"); - + let server_addr = format!("127.0.0.1:{}", puffin_http::DEFAULT_PORT); let _puffin_server = puffin_http::Server::new(&server_addr).unwrap(); - + eprintln!("Serving demo profile data on {server_addr}. Run `puffin_viewer` to view it."); puffin::set_scopes_on(true); let mut frame_counter = 0; diff --git a/puffin_http/src/client.rs b/puffin_http/src/client.rs index f03a5ac9..ba391e94 100644 --- a/puffin_http/src/client.rs +++ b/puffin_http/src/client.rs @@ -1,6 +1,6 @@ use std::sync::{ atomic::{AtomicBool, Ordering::SeqCst}, - Arc, Mutex, + Arc, }; use puffin::{FrameData, FrameView}; @@ -13,7 +13,7 @@ pub struct Client { addr: String, connected: Arc, alive: Arc, - frame_view: Arc>, + frame_view: Arc>, } impl Drop for Client { @@ -27,7 +27,7 @@ impl Client { /// that is then fed to [`puffin::GlobalProfiler`]. /// /// You can then view the data with - /// [`puffin_egui`](https://crates.io/crates/puffin_egui) or [`puffin-imgui`](https://crates.io/crates/puffin-imgui). + /// [`puffin_egui`](https://crates.io/crates/puffin_egui). /// /// ``` no_run /// puffin_http::Client::new("127.0.0.1:8585".to_owned()); @@ -35,7 +35,7 @@ impl Client { pub fn new(addr: String) -> Self { let alive = Arc::new(AtomicBool::new(true)); let connected = Arc::new(AtomicBool::new(false)); - let frame_view = Arc::new(Mutex::new(FrameView::default())); + let frame_view = Arc::new(parking_lot::Mutex::new(FrameView::default())); let client = Self { addr: addr.clone(), @@ -44,39 +44,40 @@ impl Client { frame_view: frame_view.clone(), }; - std::thread::spawn(move || { - log::info!("Connecting to {}…", addr); - while alive.load(SeqCst) { - match std::net::TcpStream::connect(&addr) { - Ok(mut stream) => { - log::info!("Connected to {}", addr); - connected.store(true, SeqCst); - while alive.load(SeqCst) { - match consume_message(&mut stream) { - Ok(frame_data) => { - frame_view - .lock() - .unwrap() - .add_frame(std::sync::Arc::new(frame_data)); - } - Err(err) => { - log::warn!( - "Connection to puffin server closed: {}", - error_display_chain(err.as_ref()) - ); - connected.store(false, SeqCst); - break; + let _ = std::thread::Builder::new() + .name("http_client_thread".to_string()) + .spawn(move || { + log::info!("Connecting to {}…", addr); + while alive.load(SeqCst) { + match std::net::TcpStream::connect(&addr) { + Ok(mut stream) => { + log::info!("Connected to {}", addr); + connected.store(true, SeqCst); + while alive.load(SeqCst) { + match consume_message(&mut stream) { + Ok(frame_data) => { + frame_view + .lock() + .add_frame(std::sync::Arc::new(frame_data)); + } + Err(err) => { + log::warn!( + "Connection to puffin server closed: {}", + error_display_chain(err.as_ref()) + ); + connected.store(false, SeqCst); + break; + } } } } - } - Err(err) => { - log::debug!("Failed to connect to {}: {}", addr, err); - std::thread::sleep(std::time::Duration::from_secs(1)); + Err(err) => { + log::debug!("Failed to connect to {}: {}", addr, err); + std::thread::sleep(std::time::Duration::from_secs(1)); + } } } - } - }); + }); client } @@ -92,8 +93,8 @@ impl Client { } /// Get the current data. - pub fn frame_view(&self) -> std::sync::MutexGuard<'_, FrameView> { - self.frame_view.lock().unwrap() + pub fn frame_view(&self) -> parking_lot::MutexGuard<'_, FrameView> { + self.frame_view.lock() } } @@ -122,6 +123,7 @@ pub fn consume_message(stream: &mut impl std::io::Read) -> anyhow::Result -#![deny(unsafe_code)] -#![warn( - clippy::all, - clippy::await_holding_lock, - clippy::char_lit_as_u8, - clippy::checked_conversions, - clippy::dbg_macro, - clippy::debug_assert_with_mut_call, - clippy::disallowed_methods, - clippy::disallowed_types, - clippy::doc_markdown, - clippy::empty_enum, - clippy::enum_glob_use, - clippy::exit, - clippy::expl_impl_clone_on_copy, - clippy::explicit_deref_methods, - clippy::explicit_into_iter_loop, - clippy::fallible_impl_from, - clippy::filter_map_next, - clippy::flat_map_option, - clippy::float_cmp_const, - clippy::fn_params_excessive_bools, - clippy::from_iter_instead_of_collect, - clippy::if_let_mutex, - clippy::implicit_clone, - clippy::imprecise_flops, - clippy::inefficient_to_string, - clippy::invalid_upcast_comparisons, - clippy::large_digit_groups, - clippy::large_stack_arrays, - clippy::large_types_passed_by_value, - clippy::let_unit_value, - clippy::linkedlist, - clippy::lossy_float_literal, - clippy::macro_use_imports, - clippy::manual_ok_or, - clippy::map_err_ignore, - clippy::map_flatten, - clippy::map_unwrap_or, - clippy::match_on_vec_items, - clippy::match_same_arms, - clippy::match_wild_err_arm, - clippy::match_wildcard_for_single_variants, - clippy::mem_forget, - clippy::mismatched_target_os, - clippy::missing_enforced_import_renames, - clippy::mut_mut, - clippy::mutex_integer, - clippy::needless_borrow, - clippy::needless_continue, - clippy::needless_for_each, - clippy::option_option, - clippy::path_buf_push_overwrite, - clippy::ptr_as_ptr, - clippy::rc_mutex, - clippy::ref_option_ref, - clippy::rest_pat_in_fully_bound_structs, - clippy::same_functions_in_if_condition, - clippy::semicolon_if_nothing_returned, - clippy::single_match_else, - clippy::string_add_assign, - clippy::string_add, - clippy::string_lit_as_bytes, - clippy::string_to_string, - clippy::todo, - clippy::trait_duplication_in_bounds, - clippy::unimplemented, - clippy::unnested_or_patterns, - clippy::unused_self, - clippy::useless_transmute, - clippy::verbose_file_reads, - clippy::zero_sized_map_values, - future_incompatible, - nonstandard_style, - rust_2018_idioms -)] -// END - Embark standard lints v0.5 for Rust 1.55+ -// crate-specific exceptions: -#![deny(missing_docs)] - /// Bumped on protocol breakage. -pub const PROTOCOL_VERSION: u16 = 1; +pub const PROTOCOL_VERSION: u16 = 2; /// The default TCP port used. pub const DEFAULT_PORT: u16 = 8585; diff --git a/puffin_http/src/server.rs b/puffin_http/src/server.rs index 1fa08615..6b89fc8f 100644 --- a/puffin_http/src/server.rs +++ b/puffin_http/src/server.rs @@ -1,5 +1,5 @@ use anyhow::Context as _; -use puffin::GlobalProfiler; +use puffin::{FrameView, GlobalProfiler}; use std::{ io::Write, net::{SocketAddr, TcpListener, TcpStream}, @@ -16,15 +16,216 @@ const MAX_FRAMES_IN_QUEUE: usize = 30; /// and streams them puffin profiler data. /// /// Drop to stop transmitting and listening for new connections. +#[must_use = "When Server is dropped, the server is closed, so keep it around!"] pub struct Server { sink_id: puffin::FrameSinkId, join_handle: Option>, num_clients: Arc, + sink_remove: fn(puffin::FrameSinkId) -> (), } impl Server { /// Start listening for connections on this addr (e.g. "0.0.0.0:8585") + /// + /// Connects to the [GlobalProfiler] pub fn new(bind_addr: &str) -> anyhow::Result { + fn global_add(sink: puffin::FrameSink) -> puffin::FrameSinkId { + GlobalProfiler::lock().add_sink(sink) + } + fn global_remove(id: puffin::FrameSinkId) { + GlobalProfiler::lock().remove_sink(id); + } + + Self::new_custom(bind_addr, global_add, global_remove) + } + + /// Starts a new puffin server, with a custom function for installing the server's sink + /// + /// # Arguments + /// * `bind_addr` - The address to bind to, when listening for connections + /// (e.g. "localhost:8585" or "127.0.0.1:8585") + /// * `sink_install` - A function that installs the [Server]'s sink into + /// a [GlobalProfiler], and then returns the [FrameSinkId] so that the sink can be removed later + /// * `sink_remove` - A function that reverts `sink_install`. + /// This should be a call to remove the sink from the profiler ([GlobalProfiler::remove_sink]) + /// + /// # Example + /// + /// Using this is slightly complicated, but it is possible to use this to set a custom profiler per-thread, + /// such that threads can be grouped together and profiled separately. E.g. you could have one profiling server + /// instance for the main UI loop, and another for the background worker loop, and events/frames from those thread(s) + /// would be completely separated. You can then hook up two separate instances of `puffin_viewer` and profile them separately. + /// + /// ## Per-Thread Profiling + /// ``` + /// # use puffin::GlobalProfiler; + /// # use puffin::{StreamInfoRef, ThreadInfo, ScopeDetails}; + /// # use puffin_http::Server; + /// # use puffin::ThreadProfiler; + /// # + /// # pub fn main() { + /// # + /// # + /// // Initialise the profiling server for the main app + /// let default_server = Server::new("localhost:8585").expect("failed to create default profiling server"); + /// puffin::profile_scope!("main_scope"); + /// + /// // Create a new [GlobalProfiler] instance. This is where we will be sending the events to for our threads. + /// // [OnceLock] and [Mutex] are there so that we can safely get exclusive mutable access. + /// static CUSTOM_PROFILER: std::sync::OnceLock> = std::sync::OnceLock::new(); + /// // Helper function to access the profiler + /// fn get_custom_profiler() -> std::sync::MutexGuard<'static, GlobalProfiler> { + /// CUSTOM_PROFILER.get_or_init(|| std::sync::Mutex::new(GlobalProfiler::default())) + /// .lock().expect("failed to lock custom profiler") + /// } + /// // Create the custom profiling server that uses our custom profiler instead of the global/default one + /// let thread_server = Server::new_custom( + /// "localhost:6969", + /// // Adds the [Server]'s sink to our custom profiler + /// |sink| get_custom_profiler().add_sink(sink), + /// // Remove + /// |id| _ = get_custom_profiler().remove_sink(id) + /// ); + /// + /// // Create some custom threads where we use the custom profiler and server + /// std::thread::scope(|scope| { + /// scope.spawn(move ||{ + /// // Tell this thread to use the custom profiler + /// let _ = ThreadProfiler::initialize( + /// // Use the same time source as default puffin + /// puffin::now_ns, + /// // However redirect the events to our `custom_profiler`, instead of the default + /// // which would be the one returned by [GlobalProfiler::lock()] + /// |info: ThreadInfo, details: &[ScopeDetails], stream: &StreamInfoRef<'_>| + /// get_custom_profiler().report(info, details, stream) + /// ); + /// + /// // Do work + /// { + /// puffin::profile_scope!("inside_thread"); + /// println!("hello from the thread"); + /// std::thread::sleep(std::time::Duration::from_secs(1)); + /// } + /// + /// // Tell our profiler that we are done with this frame + /// // This will be sent to the server on port 6969 + /// get_custom_profiler().new_frame(); + /// }); + /// }); + /// + /// // New frame for the global profiler. This is completely separate from the scopes with the custom profiler + /// GlobalProfiler::lock().new_frame(); + /// # + /// # + /// # } + /// ``` + /// + /// ## Helpful Macro + /// ```rust + /// # use std::thread::sleep; + /// # use std::time::Duration; + /// + /// /// This macro makes it much easier to define profilers + /// /// + /// /// This macro makes use of the `paste` crate to generate unique identifiers, and `tracing` to log events + /// macro_rules! profiler { + /// ($( + /// {name: $name:ident, port: $port:expr $(,install: |$install_var:ident| $install:block, drop: |$drop_var:ident| $drop:block)? $(,)?} + /// ),* $(,)?) + /// => { + /// $( + /// profiler!(@inner { name: $name, port: $port $(,install: |$install_var| $install, drop: |$drop_var| $drop)? }); + /// )* + /// }; + /// + /// (@inner { name: $name:ident, port: $port:expr }) => { + /// paste::paste!{ + /// #[doc = concat!("The address to bind the ", std::stringify!([< $name:lower >]), " thread profilers' server to")] + /// pub const [< $name:upper _PROFILER_ADDR >] : &'static str + /// = concat!("127.0.0.1:", $port); + /// + /// /// Installs the server's sink into the custom profiler + /// #[doc(hidden)] + /// fn [< $name:lower _profiler_server_install >](sink: puffin::FrameSink) -> puffin::FrameSinkId { + /// [< $name:lower _profiler_lock >]().add_sink(sink) + /// } + /// + /// /// Drops the server's sink and removes from profiler + /// #[doc(hidden)] + /// fn [< $name:lower _profiler_server_drop >](id: puffin::FrameSinkId){ + /// [< $name:lower _profiler_lock >]().remove_sink(id); + /// } + /// + /// #[doc = concat!("The instance of the ", std::stringify!([< $name:lower >]), " thread profiler's server")] + /// pub static [< $name:upper _PROFILER_SERVER >] : once_cell::sync::Lazy> + /// = once_cell::sync::Lazy::new(|| { + /// eprintln!( + /// "starting puffin_http server for {} profiler at {}", + /// std::stringify!([<$name:lower>]), + /// [< $name:upper _PROFILER_ADDR >] + /// ); + /// std::sync::Mutex::new( + /// puffin_http::Server::new_custom( + /// [< $name:upper _PROFILER_ADDR >], + /// // Can't use closures in a const context, use fn-pointers instead + /// [< $name:lower _profiler_server_install >], + /// [< $name:lower _profiler_server_drop >], + /// ) + /// .expect(&format!("{} puffin_http server failed to start", std::stringify!([<$name:lower>]))) + /// ) + /// }); + /// + /// #[doc = concat!("A custom reporter for the ", std::stringify!([< $name:lower >]), " thread reporter")] + /// pub fn [< $name:lower _profiler_reporter >] (info: puffin::ThreadInfo, details: &[puffin::ScopeDetails], stream: &puffin::StreamInfoRef<'_>) { + /// [< $name:lower _profiler_lock >]().report(info, details, stream) + /// } + /// + /// #[doc = concat!("Accessor for the ", std::stringify!([< $name:lower >]), " thread reporter")] + /// pub fn [< $name:lower _profiler_lock >]() -> std::sync::MutexGuard<'static, puffin::GlobalProfiler> { + /// static [< $name _PROFILER >] : once_cell::sync::Lazy> = once_cell::sync::Lazy::new(Default::default); + /// [< $name _PROFILER >].lock().expect("poisoned std::sync::mutex") + /// } + /// + /// #[doc = concat!("Initialises the ", std::stringify!([< $name:lower >]), " thread reporter and server.\ + /// Call this on each different thread you want to register with this profiler")] + /// pub fn [< $name:lower _profiler_init >]() { + /// eprintln!("init thread profiler \"{}\"", std::stringify!([<$name:lower>])); + /// std::mem::drop([< $name:upper _PROFILER_SERVER >].lock()); + /// eprintln!("set thread custom profiler \"{}\"", std::stringify!([<$name:lower>])); + /// puffin::ThreadProfiler::initialize(::puffin::now_ns, [< $name:lower _profiler_reporter >]); + /// } + /// } + /// }; + /// } + /// + /// profiler! { + /// { name: UI, port: "2a" }, + /// { name: RENDERER, port: 8586 }, + /// { name: BACKGROUND, port: 8587 }, + /// } + /// + /// pub fn demo() { + /// std::thread::spawn(|| { + /// // Initialise the custom profiler for this thread + /// // Now all puffin events are sent to the custom profiling server instead + /// // + /// background_profiler_init(); + /// + /// for i in 0..100{ + /// puffin::profile_scope!("test"); + /// sleep(Duration::from_millis(i)); + /// } + /// + /// // Mark a new frame so the data is flushed to the server + /// background_profiler_lock().new_frame(); + /// }); + /// } + /// ``` + pub fn new_custom( + bind_addr: &str, + sink_install: fn(puffin::FrameSink) -> puffin::FrameSinkId, + sink_remove: fn(puffin::FrameSinkId) -> (), + ) -> anyhow::Result { let tcp_listener = TcpListener::bind(bind_addr).context("binding server TCP socket")?; tcp_listener .set_nonblocking(true) @@ -47,12 +248,16 @@ impl Server { tcp_listener, clients: Default::default(), num_clients: num_clients_cloned, + send_all_scopes: false, + frame_view: Default::default(), }; while let Ok(frame) = rx.recv() { + server_impl.frame_view.add_frame(frame.clone()); if let Err(err) = server_impl.accept_new_clients() { log::warn!("puffin server failure: {}", err); } + if let Err(err) = server_impl.send(&frame) { log::warn!("puffin server failure: {}", err); } @@ -60,7 +265,8 @@ impl Server { }) .context("Couldn't spawn thread")?; - let sink_id = GlobalProfiler::lock().add_sink(Box::new(move |frame| { + // Call the `install` function to add ourselves as a sink + let sink_id = sink_install(Box::new(move |frame| { tx.send(frame).ok(); })); @@ -68,6 +274,7 @@ impl Server { sink_id, join_handle: Some(join_handle), num_clients, + sink_remove, }) } @@ -79,7 +286,8 @@ impl Server { impl Drop for Server { fn drop(&mut self) { - GlobalProfiler::lock().remove_sink(self.sink_id); + // Remove ourselves from the profiler + (self.sink_remove)(self.sink_id); // Take care to send everything before we shut down: if let Some(join_handle) = self.join_handle.take() { @@ -116,6 +324,8 @@ struct PuffinServerImpl { tcp_listener: TcpListener, clients: Vec, num_clients: Arc, + send_all_scopes: bool, + frame_view: FrameView, } impl PuffinServerImpl { @@ -136,6 +346,8 @@ impl PuffinServerImpl { .spawn(move || client_loop(packet_rx, client_addr, tcp_stream)) .context("Couldn't spawn thread")?; + // Send all scopes when new client connects. + self.send_all_scopes = true; self.clients.push(Client { client_addr, packet_tx: Some(packet_tx), @@ -161,12 +373,19 @@ impl PuffinServerImpl { puffin::profile_function!(); let mut packet = vec![]; + packet .write_all(&crate::PROTOCOL_VERSION.to_le_bytes()) .unwrap(); + frame - .write_into(&mut packet) + .write_into( + self.frame_view.scope_collection(), + self.send_all_scopes, + &mut packet, + ) .context("Encode puffin frame")?; + self.send_all_scopes = false; let packet: Packet = packet.into(); diff --git a/puffin_viewer/CHANGELOG.md b/puffin_viewer/CHANGELOG.md index d41ea925..1beb4691 100644 --- a/puffin_viewer/CHANGELOG.md +++ b/puffin_viewer/CHANGELOG.md @@ -6,11 +6,34 @@ All notable changes to `puffin_viewer` will be documented in this file. ## [Unreleased] - ReleaseDate +## [0.21.1] - 2024-06-16 + +- [PR#211](https://github.com/EmbarkStudios/puffin/pull/211/) Fix broken flamegraph interaction with egui 0.27.1 () + +## [0.21.0] - 2024-04-06 +- [PR#201](https://github.com/EmbarkStudios/puffin/pull/201) Update to egui `0.27` + +## [0.20.0] - 2024-02-14 + +- [PR#188](https://github.com/EmbarkStudios/puffin/pull/188) Update to `puffin_egui` version `0.26` +- [PR#184](https://github.com/EmbarkStudios/puffin/pull/184) Propagate errors from `eframe::run_native` + +## [0.19.0] - 2024-01-17 + +- [PR#179](https://github.com/EmbarkStudios/puffin/pull/179) Update to `puffin_egui` version `0.25` + +## [0.18.0] - 2023-11-24 +- [PR#161](https://github.com/EmbarkStudios/puffin/pull/166) Update to egui and eframe `0.24` + +## [0.17.0] - 2023-09-28 + +- [PR#161](https://github.com/EmbarkStudios/puffin/pull/161) Update to egui `0.23` + ## [0.16.0] - 2023-05-24 * Upgrade to `eframe` 0.22 - Upgrade to `puffin` 0.16 -- +- ## [0.15.0] - 2023-04-24 ## [0.14.0] - 2023-02-09 * Upgrade to `puffin_egui` 0.21 @@ -42,7 +65,7 @@ All notable changes to `puffin_viewer` will be documented in this file. ## [0.10.1] - 2022-01-11 ### Changed - Update to latest `eframe` and `winit` -- Swich renderer from `egui_glium` to `egui_glow`. +- Switch renderer from `egui_glium` to `egui_glow`. ## [0.10.0] - 2021-11-16 ### Changed @@ -100,7 +123,13 @@ All notable changes to `puffin_viewer` will be documented in this file. First release: connect to a `puffin_server` over HTTP to live view a profiler stream -[Unreleased]: https://github.com/EmbarkStudios/puffin/compare/puffin_viewer-0.16.0...HEAD +[Unreleased]: https://github.com/EmbarkStudios/puffin/compare/puffin_viewer-0.21.1...HEAD +[0.21.1]: https://github.com/EmbarkStudios/puffin/compare/puffin_viewer-0.21.0...puffin_viewer-0.21.1 +[0.21.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_viewer-0.20.0...puffin_viewer-0.21.0 +[0.20.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_viewer-0.19.0...puffin_viewer-0.20.0 +[0.19.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_viewer-0.18.0...puffin_viewer-0.19.0 +[0.18.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_viewer-0.17.0...puffin_viewer-0.18.0 +[0.17.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_viewer-0.16.0...puffin_viewer-0.17.0 [0.16.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_viewer-0.15.0...puffin_viewer-0.16.0 [0.15.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_viewer-0.14.0...puffin_viewer-0.15.0 [0.14.0]: https://github.com/EmbarkStudios/puffin/compare/puffin_viewer-0.13.2...puffin_viewer-0.14.0 diff --git a/puffin_viewer/Cargo.toml b/puffin_viewer/Cargo.toml index 71c6957e..77a89ca3 100644 --- a/puffin_viewer/Cargo.toml +++ b/puffin_viewer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "puffin_viewer" -version = "0.16.0" +version = "0.21.1" authors = ["Embark "] description = "Viewer GUI for puffin profiler data" license = "MIT OR Apache-2.0" @@ -16,17 +16,19 @@ include = ["**/*.rs", "Cargo.toml", "README.md", "icon.png"] crate-type = ["cdylib", "rlib"] [dependencies] -puffin_egui = { version = "0.22.0", path = "../puffin_egui" } -puffin = { version = "0.16.0", path = "../puffin", features = [ +puffin_egui = { version = "0.27.1", path = "../puffin_egui", features = [ + "serde", +] } +puffin = { version = "0.19.0", path = "../puffin", features = [ "packing", "serialization", "lz4", "zstd", # Support zstd in order to load old puffin files (before 0.16.0) ] } -puffin_http = { version = "0.13.0", path = "../puffin_http" } +puffin_http = { version = "0.16.0", path = "../puffin_http" } argh = "0.1" -eframe = { version = "0.22.0", default-features = false, features = [ +eframe = { version = "0.27.1", default-features = false, features = [ "default_fonts", "glow", "persistence", diff --git a/puffin_viewer/src/lib.rs b/puffin_viewer/src/lib.rs index 61cf192d..e5a5655e 100644 --- a/puffin_viewer/src/lib.rs +++ b/puffin_viewer/src/lib.rs @@ -1,85 +1,6 @@ //! Remote puffin viewer, connecting to a [`puffin_http::Server`]. -// BEGIN - Embark standard lints v5 for Rust 1.55+ -// do not change or add/remove here, but one can add exceptions after this section -// for more info see: -#![deny(unsafe_code)] -#![warn( - clippy::all, - clippy::await_holding_lock, - clippy::char_lit_as_u8, - clippy::checked_conversions, - clippy::dbg_macro, - clippy::debug_assert_with_mut_call, - clippy::disallowed_methods, - clippy::disallowed_types, - clippy::doc_markdown, - clippy::empty_enum, - clippy::enum_glob_use, - clippy::exit, - clippy::expl_impl_clone_on_copy, - clippy::explicit_deref_methods, - clippy::explicit_into_iter_loop, - clippy::fallible_impl_from, - clippy::filter_map_next, - clippy::flat_map_option, - clippy::float_cmp_const, - clippy::fn_params_excessive_bools, - clippy::from_iter_instead_of_collect, - clippy::if_let_mutex, - clippy::implicit_clone, - clippy::imprecise_flops, - clippy::inefficient_to_string, - clippy::invalid_upcast_comparisons, - clippy::large_digit_groups, - clippy::large_stack_arrays, - clippy::large_types_passed_by_value, - clippy::let_unit_value, - clippy::linkedlist, - clippy::lossy_float_literal, - clippy::macro_use_imports, - clippy::manual_ok_or, - clippy::map_err_ignore, - clippy::map_flatten, - clippy::map_unwrap_or, - clippy::match_on_vec_items, - clippy::match_same_arms, - clippy::match_wild_err_arm, - clippy::match_wildcard_for_single_variants, - clippy::mem_forget, - clippy::mismatched_target_os, - clippy::missing_enforced_import_renames, - clippy::mut_mut, - clippy::mutex_integer, - clippy::needless_borrow, - clippy::needless_continue, - clippy::needless_for_each, - clippy::option_option, - clippy::path_buf_push_overwrite, - clippy::ptr_as_ptr, - clippy::rc_mutex, - clippy::ref_option_ref, - clippy::rest_pat_in_fully_bound_structs, - clippy::same_functions_in_if_condition, - clippy::semicolon_if_nothing_returned, - clippy::single_match_else, - clippy::string_add_assign, - clippy::string_add, - clippy::string_lit_as_bytes, - clippy::string_to_string, - clippy::todo, - clippy::trait_duplication_in_bounds, - clippy::unimplemented, - clippy::unnested_or_patterns, - clippy::unused_self, - clippy::useless_transmute, - clippy::verbose_file_reads, - clippy::zero_sized_map_values, - future_incompatible, - nonstandard_style, - rust_2018_idioms -)] -// END - Embark standard lints v0.5 for Rust 1.55+ +#![forbid(unsafe_code)] // crate-specific exceptions: #![allow(clippy::exit)] #![cfg_attr(target_arch = "wasm32", allow(clippy::unused_unit))] @@ -137,9 +58,13 @@ pub struct PuffinViewer { } impl PuffinViewer { - pub fn new(source: Source) -> Self { + pub fn new(source: Source, storage: Option<&dyn eframe::Storage>) -> Self { + let profiler_ui = storage + .and_then(|storage| eframe::get_value(storage, eframe::APP_KEY)) + .unwrap_or_default(); + Self { - profiler_ui: Default::default(), + profiler_ui, source, error: None, profile_self: false, @@ -153,8 +78,16 @@ impl PuffinViewer { .add_filter("puffin", &["puffin"]) .save_file() { - if let Err(error) = self.source.frame_view().save_to_path(&path) { - self.error = Some(format!("Failed to export: {error}")); + let mut file = match std::fs::File::create(path) { + Ok(file) => file, + Err(error) => { + self.error = Some(format!("Failed to create file: {error:#}")); + return; + } + }; + + if let Err(error) = self.source.frame_view().write(&mut file) { + self.error = Some(format!("Failed to export: {error:#}")); } else { self.error = None; } @@ -173,14 +106,23 @@ impl PuffinViewer { fn open_puffin_path(&mut self, path: std::path::PathBuf) { puffin::profile_function!(); - match FrameView::load_path(&path) { + + let mut file = match std::fs::File::open(&path) { + Ok(bytes) => bytes, + Err(err) => { + self.error = Some(format!("Failed to open {}: {err:#}", path.display())); + return; + } + }; + + match FrameView::read(&mut file) { Ok(frame_view) => { self.profiler_ui.reset(); self.source = Source::FilePath(path, frame_view); self.error = None; } Err(err) => { - self.error = Some(format!("Failed to load {}: {}", path.display(), err)); + self.error = Some(format!("Failed to load {}: {err:#}", path.display())); } } } @@ -188,7 +130,7 @@ impl PuffinViewer { fn open_puffin_bytes(&mut self, name: String, bytes: &[u8]) { puffin::profile_function!(); let mut reader = std::io::Cursor::new(bytes); - match FrameView::load_reader(&mut reader) { + match FrameView::read(&mut reader) { Ok(frame_view) => { self.profiler_ui.reset(); self.source = Source::FileName(name, frame_view); @@ -201,7 +143,7 @@ impl PuffinViewer { } #[cfg(not(target_arch = "wasm32"))] - fn ui_menu_bar(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { + fn ui_menu_bar(&mut self, ctx: &egui::Context) { if ctx.input(|i| i.modifiers.command && i.key_pressed(egui::Key::O)) { self.open_dialog(); } @@ -225,7 +167,7 @@ impl PuffinViewer { } if ui.button("Quit").clicked() { - frame.close(); + ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close); } }); ui.menu_button("View", |ui| { @@ -273,12 +215,16 @@ impl PuffinViewer { } impl eframe::App for PuffinViewer { + fn save(&mut self, storage: &mut dyn eframe::Storage) { + eframe::set_value(storage, eframe::APP_KEY, &self.profiler_ui); + } + fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { puffin::GlobalProfiler::lock().new_frame(); #[cfg(not(target_arch = "wasm32"))] { - self.ui_menu_bar(ctx, _frame); + self.ui_menu_bar(ctx); } #[cfg(target_arch = "wasm32")] diff --git a/puffin_viewer/src/main.rs b/puffin_viewer/src/main.rs index c032c638..000699ef 100644 --- a/puffin_viewer/src/main.rs +++ b/puffin_viewer/src/main.rs @@ -4,7 +4,7 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; #[cfg(not(target_arch = "wasm32"))] -fn main() { +fn main() -> Result<(), eframe::Error> { env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`). /// puffin profile viewer. @@ -19,13 +19,15 @@ fn main() { /// what .puffin file to open, e.g. `my/recording.puffin`. #[argh(positional)] - file: Option, + file: Option, } fn default_url() -> String { format!("127.0.0.1:{}", puffin_http::DEFAULT_PORT) } + use std::path::PathBuf; + use puffin::FrameView; use puffin_viewer::{PuffinViewer, Source}; @@ -33,12 +35,19 @@ fn main() { puffin::set_scopes_on(true); // so we can profile ourselves - let source = if let Some(file) = opt.file { - let path = std::path::PathBuf::from(file); - match FrameView::load_path(&path) { + let source = if let Some(path) = opt.file { + let mut file = match std::fs::File::open(&path) { + Ok(file) => file, + Err(err) => { + log::error!("Failed to open {:?}: {err:#}", path.display()); + std::process::exit(1); + } + }; + + match FrameView::read(&mut file) { Ok(frame_view) => Source::FilePath(path, frame_view), Err(err) => { - log::error!("Failed to load {:?}: {}", path.display(), err); + log::error!("Failed to load {:?}: {err:#}", path.display()); std::process::exit(1); } } @@ -46,20 +55,20 @@ fn main() { Source::Http(puffin_http::Client::new(opt.url)) }; + let icon = eframe::icon_data::from_png_bytes(include_bytes!("../icon.png")).unwrap(); let native_options = eframe::NativeOptions { - app_id: Some("puffin_viewer".to_owned()), - icon_data: Some( - eframe::IconData::try_from_png_bytes(include_bytes!("../icon.png")).unwrap(), - ), - drag_and_drop_support: true, + viewport: eframe::egui::ViewportBuilder::default() + .with_app_id("puffin_viewer") + .with_drag_and_drop(true) + .with_icon(icon), ..Default::default() }; - let _ = eframe::run_native( + eframe::run_native( "puffin viewer", native_options, - Box::new(|_cc| Box::new(PuffinViewer::new(source))), - ); + Box::new(|cc| Box::new(PuffinViewer::new(source, cc.storage))), + ) } #[cfg(target_arch = "wasm32")] diff --git a/puffin_viewer/src/web.rs b/puffin_viewer/src/web.rs index 3ac5c629..52fdcaee 100644 --- a/puffin_viewer/src/web.rs +++ b/puffin_viewer/src/web.rs @@ -18,7 +18,7 @@ pub async fn start(canvas_id: &str) -> Result<(), eframe::wasm_bindgen::JsValue> .start( canvas_id, web_options, - Box::new(|_cc| Box::new(crate::PuffinViewer::new(crate::Source::None))), + Box::new(|cc| Box::new(crate::PuffinViewer::new(crate::Source::None, cc.storage))), ) .await?; diff --git a/release.toml b/release.toml deleted file mode 100644 index c033cf0c..00000000 --- a/release.toml +++ /dev/null @@ -1,10 +0,0 @@ -pre-release-commit-message = "Release puffin-{{version}}" -tag-message = "Release puffin-{{version}}" -tag-name = "{{version}}" -pre-release-replacements = [ - { file = "../CHANGELOG.md", search = "Unreleased", replace = "{{version}}" }, - { file = "../CHANGELOG.md", search = "\\.\\.\\.HEAD", replace = "...{{tag_name}}" }, - { file = "../CHANGELOG.md", search = "ReleaseDate", replace = "{{date}}" }, - { file = "../CHANGELOG.md", search = "", replace = "\n## [Unreleased] - ReleaseDate" }, - { file = "../CHANGELOG.md", search = "", replace = "\n[Unreleased]: https://github.com/EmbarkStudios/puffin/compare/{{tag_name}}...HEAD" }, -] diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 767f233b..f2e84e3e 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -11,7 +11,7 @@ end = "2024-06-08" [[trusted.bytes]] criteria = "safe-to-deploy" -user-id = 6741 +user-id = 6741 # Alice Ryhl (Darksonn) start = "2021-01-11" end = "2024-06-08" @@ -131,7 +131,7 @@ end = "2024-06-08" [[trusted.num_cpus]] criteria = "safe-to-deploy" -user-id = 359 # Sean McArthur (seanmonstar) +user-id = 359 start = "2019-06-10" end = "2024-06-08" diff --git a/supply-chain/config.toml b/supply-chain/config.toml index e1474aba..2ff0a637 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -2,7 +2,7 @@ # cargo-vet config file [cargo-vet] -version = "0.7" +version = "0.9" [imports.embark] url = "https://raw.githubusercontent.com/EmbarkStudios/rust-ecosystem/main/audits.toml" @@ -31,10 +31,6 @@ url = "https://raw.githubusercontent.com/bytecodealliance/wasmtime/main/supply-c [imports.zcash] url = "https://raw.githubusercontent.com/zcash/rust-ecosystem/main/supply-chain/audits.toml" -[policy.errno-dragonfly] -criteria = [] -notes = "Not used, unsupported target" - [policy.orbclient] criteria = [] notes = "Not used, Redox OS-only" @@ -42,9 +38,6 @@ notes = "Not used, Redox OS-only" [policy.puffin] audit-as-crates-io = false -[policy.puffin-imgui] -audit-as-crates-io = false - [policy.puffin_egui] audit-as-crates-io = false @@ -66,24 +59,24 @@ notes = "Not used, Redox OS-only" criteria = [] notes = "Not used, unsupported target" +[[exemptions.ab_glyph]] +version = "0.2.23" +criteria = "safe-to-deploy" + [[exemptions.ab_glyph_rasterizer]] version = "0.1.8" criteria = "safe-to-deploy" [[exemptions.accesskit]] -version = "0.11.0" -criteria = "safe-to-deploy" - -[[exemptions.adler]] -version = "1.0.2" +version = "0.12.2" criteria = "safe-to-deploy" [[exemptions.ahash]] -version = "0.8.3" +version = "0.8.6" criteria = "safe-to-deploy" -[[exemptions.aho-corasick]] -version = "1.0.2" +[[exemptions.android-activity]] +version = "0.5.1" criteria = "safe-to-deploy" [[exemptions.android-properties]] @@ -91,19 +84,23 @@ version = "0.2.2" criteria = "safe-to-deploy" [[exemptions.arboard]] -version = "3.2.0" +version = "3.3.0" criteria = "safe-to-deploy" -[[exemptions.arrayvec]] -version = "0.5.2" -criteria = "safe-to-run" +[[exemptions.as-raw-xcb-connection]] +version = "1.0.1" +criteria = "safe-to-deploy" [[exemptions.atk-sys]] version = "0.15.1" criteria = "safe-to-deploy" -[[exemptions.atomic_refcell]] -version = "0.1.10" +[[exemptions.atomic-waker]] +version = "1.1.2" +criteria = "safe-to-deploy" + +[[exemptions.base64]] +version = "0.21.6" criteria = "safe-to-deploy" [[exemptions.bincode]] @@ -119,15 +116,15 @@ version = "0.1.6" criteria = "safe-to-deploy" [[exemptions.block-sys]] -version = "0.1.0-beta.1" +version = "0.2.1" criteria = "safe-to-deploy" [[exemptions.block2]] -version = "0.2.0-alpha.6" +version = "0.3.0" criteria = "safe-to-deploy" -[[exemptions.bytemuck_derive]] -version = "1.4.1" +[[exemptions.bytemuck]] +version = "1.14.0" criteria = "safe-to-deploy" [[exemptions.cairo-sys-rs]] @@ -135,12 +132,12 @@ version = "0.15.1" criteria = "safe-to-deploy" [[exemptions.calloop]] -version = "0.10.6" +version = "0.12.3" criteria = "safe-to-deploy" -[[exemptions.cast]] -version = "0.3.0" -criteria = "safe-to-run" +[[exemptions.calloop-wayland-source]] +version = "0.2.0" +criteria = "safe-to-deploy" [[exemptions.cesu8]] version = "1.1.0" @@ -150,10 +147,6 @@ criteria = "safe-to-deploy" version = "0.3.2" criteria = "safe-to-deploy" -[[exemptions.chlorine]] -version = "1.0.10" -criteria = "safe-to-deploy" - [[exemptions.ciborium]] version = "0.2.1" criteria = "safe-to-run" @@ -170,14 +163,26 @@ criteria = "safe-to-run" version = "4.5.0" criteria = "safe-to-deploy" +[[exemptions.cocoa]] +version = "0.25.0" +criteria = "safe-to-deploy" + +[[exemptions.cocoa-foundation]] +version = "0.1.2" +criteria = "safe-to-deploy" + [[exemptions.colored]] -version = "2.0.0" +version = "2.1.0" criteria = "safe-to-run" [[exemptions.combine]] version = "4.6.6" criteria = "safe-to-deploy" +[[exemptions.concurrent-queue]] +version = "2.4.0" +criteria = "safe-to-deploy" + [[exemptions.crc32fast]] version = "1.3.2" criteria = "safe-to-deploy" @@ -190,30 +195,18 @@ criteria = "safe-to-run" version = "0.5.0" criteria = "safe-to-run" -[[exemptions.crossfont]] -version = "0.5.1" -criteria = "safe-to-run" - -[[exemptions.darling]] -version = "0.13.4" -criteria = "safe-to-run" - -[[exemptions.darling_core]] -version = "0.13.4" -criteria = "safe-to-run" +[[exemptions.crossbeam-utils]] +version = "0.8.8" +criteria = "safe-to-deploy" -[[exemptions.darling_macro]] -version = "0.13.4" -criteria = "safe-to-run" +[[exemptions.cursor-icon]] +version = "1.1.0" +criteria = "safe-to-deploy" [[exemptions.directories-next]] version = "2.0.0" criteria = "safe-to-deploy" -[[exemptions.dirs-sys-next]] -version = "0.1.2" -criteria = "safe-to-deploy" - [[exemptions.dispatch]] version = "0.2.0" criteria = "safe-to-deploy" @@ -222,46 +215,18 @@ criteria = "safe-to-deploy" version = "0.5.2" criteria = "safe-to-deploy" -[[exemptions.downcast-rs]] -version = "1.2.0" +[[exemptions.env_logger]] +version = "0.10.1" criteria = "safe-to-deploy" [[exemptions.error-code]] version = "2.3.1" criteria = "safe-to-deploy" -[[exemptions.expat-sys]] -version = "2.1.6" -criteria = "safe-to-run" - [[exemptions.fdeflate]] -version = "0.3.0" +version = "0.3.3" criteria = "safe-to-deploy" -[[exemptions.foreign-types]] -version = "0.5.0" -criteria = "safe-to-run" - -[[exemptions.foreign-types-macros]] -version = "0.2.3" -criteria = "safe-to-run" - -[[exemptions.foreign-types-shared]] -version = "0.3.1" -criteria = "safe-to-run" - -[[exemptions.form_urlencoded]] -version = "1.2.0" -criteria = "safe-to-deploy" - -[[exemptions.freetype-rs]] -version = "0.26.0" -criteria = "safe-to-run" - -[[exemptions.freetype-sys]] -version = "0.13.1" -criteria = "safe-to-run" - [[exemptions.gdk-pixbuf-sys]] version = "0.15.10" criteria = "safe-to-deploy" @@ -271,7 +236,7 @@ version = "0.15.1" criteria = "safe-to-deploy" [[exemptions.gethostname]] -version = "0.2.3" +version = "0.3.0" criteria = "safe-to-deploy" [[exemptions.gio-sys]] @@ -286,52 +251,28 @@ criteria = "safe-to-deploy" version = "0.15.10" criteria = "safe-to-deploy" -[[exemptions.glium]] -version = "0.32.1" -criteria = "safe-to-run" - [[exemptions.glow]] -version = "0.12.2" +version = "0.13.0" criteria = "safe-to-deploy" [[exemptions.glutin]] -version = "0.29.1" -criteria = "safe-to-run" - -[[exemptions.glutin]] -version = "0.30.8" +version = "0.31.2" criteria = "safe-to-deploy" [[exemptions.glutin-winit]] -version = "0.3.0" +version = "0.4.2" criteria = "safe-to-deploy" [[exemptions.glutin_egl_sys]] -version = "0.1.6" -criteria = "safe-to-run" - -[[exemptions.glutin_egl_sys]] -version = "0.5.0" +version = "0.6.0" criteria = "safe-to-deploy" -[[exemptions.glutin_gles2_sys]] -version = "0.1.5" -criteria = "safe-to-run" - [[exemptions.glutin_glx_sys]] -version = "0.1.8" -criteria = "safe-to-run" - -[[exemptions.glutin_glx_sys]] -version = "0.4.0" +version = "0.5.0" criteria = "safe-to-deploy" [[exemptions.glutin_wgl_sys]] -version = "0.1.5" -criteria = "safe-to-run" - -[[exemptions.glutin_wgl_sys]] -version = "0.4.0" +version = "0.5.0" criteria = "safe-to-deploy" [[exemptions.gobject-sys]] @@ -343,119 +284,79 @@ version = "0.15.3" criteria = "safe-to-deploy" [[exemptions.hermit-abi]] -version = "0.1.19" -criteria = "safe-to-run" - -[[exemptions.hermit-abi]] -version = "0.3.1" +version = "0.3.3" criteria = "safe-to-deploy" [[exemptions.home]] -version = "0.5.5" +version = "0.5.9" criteria = "safe-to-deploy" [[exemptions.humantime]] version = "2.1.0" criteria = "safe-to-deploy" -[[exemptions.image]] -version = "0.24.6" -criteria = "safe-to-deploy" - -[[exemptions.imgui]] -version = "0.10.0" -criteria = "safe-to-deploy" - -[[exemptions.imgui-glium-renderer]] -version = "0.10.0" -criteria = "safe-to-run" - -[[exemptions.imgui-sys]] -version = "0.10.0" +[[exemptions.icrate]] +version = "0.0.4" criteria = "safe-to-deploy" -[[exemptions.imgui-winit-support]] -version = "0.10.0" -criteria = "safe-to-run" - -[[exemptions.instant]] -version = "0.1.12" +[[exemptions.image]] +version = "0.24.7" criteria = "safe-to-deploy" -[[exemptions.io-lifetimes]] -version = "1.0.11" +[[exemptions.is-terminal]] +version = "0.4.10" criteria = "safe-to-deploy" [[exemptions.jni-sys]] version = "0.3.0" criteria = "safe-to-deploy" +[[exemptions.jobserver]] +version = "0.1.27" +criteria = "safe-to-deploy" + [[exemptions.khronos_api]] version = "3.1.0" criteria = "safe-to-deploy" -[[exemptions.libloading]] -version = "0.7.3" +[[exemptions.libc]] +version = "0.2.152" criteria = "safe-to-deploy" [[exemptions.libloading]] -version = "0.8.0" +version = "0.7.3" criteria = "safe-to-deploy" [[exemptions.libmimalloc-sys]] -version = "0.1.33" +version = "0.1.35" criteria = "safe-to-deploy" [[exemptions.lz4_flex]] -version = "0.10.0" -criteria = "safe-to-deploy" - -[[exemptions.memmap2]] -version = "0.5.10" +version = "0.11.2" criteria = "safe-to-deploy" [[exemptions.memoffset]] -version = "0.6.5" +version = "0.9.1" criteria = "safe-to-deploy" -[[exemptions.minimal-lexical]] -version = "0.2.1" -criteria = "safe-to-deploy" - -[[exemptions.mint]] -version = "0.5.9" -criteria = "safe-to-deploy" - -[[exemptions.mio]] -version = "0.8.8" +[[exemptions.mimalloc]] +version = "0.1.39" criteria = "safe-to-deploy" [[exemptions.ndk]] -version = "0.7.0" +version = "0.8.0" criteria = "safe-to-deploy" -[[exemptions.ndk-glue]] -version = "0.7.0" -criteria = "safe-to-run" - -[[exemptions.ndk-macro]] -version = "0.3.0" -criteria = "safe-to-run" - [[exemptions.ndk-sys]] -version = "0.4.1+23.1.7779620" -criteria = "safe-to-deploy" - -[[exemptions.nix]] -version = "0.15.0" +version = "0.5.0+25.2.9519653" criteria = "safe-to-deploy" -[[exemptions.nix]] -version = "0.24.3" +[[exemptions.num_enum]] +version = "0.7.2" criteria = "safe-to-deploy" -[[exemptions.nom]] -version = "7.1.1" +[[exemptions.num_enum_derive]] +version = "0.7.2" criteria = "safe-to-deploy" [[exemptions.objc]] @@ -467,63 +368,59 @@ version = "0.1.1" criteria = "safe-to-deploy" [[exemptions.objc-sys]] -version = "0.2.0-beta.2" +version = "0.3.2" criteria = "safe-to-deploy" [[exemptions.objc2]] -version = "0.3.0-beta.3.patch-leaks.3" +version = "0.4.1" criteria = "safe-to-deploy" [[exemptions.objc2-encode]] -version = "2.0.0-pre.2" +version = "3.0.0" criteria = "safe-to-deploy" [[exemptions.objc_id]] version = "0.1.1" criteria = "safe-to-deploy" -[[exemptions.object]] -version = "0.30.4" -criteria = "safe-to-run" - -[[exemptions.osmesa-sys]] -version = "0.1.2" -criteria = "safe-to-run" - [[exemptions.owned_ttf_parser]] -version = "0.19.0" +version = "0.20.0" criteria = "safe-to-deploy" [[exemptions.pango-sys]] version = "0.15.10" criteria = "safe-to-deploy" -[[exemptions.percent-encoding]] -version = "2.3.0" -criteria = "safe-to-deploy" +[[exemptions.paste]] +version = "1.0.15" +criteria = "safe-to-run" [[exemptions.pkg-config]] -version = "0.3.27" +version = "0.3.28" criteria = "safe-to-deploy" [[exemptions.plotters]] -version = "0.3.4" +version = "0.3.5" criteria = "safe-to-run" [[exemptions.plotters-backend]] -version = "0.3.4" +version = "0.3.5" criteria = "safe-to-run" -[[exemptions.proc-macro-crate]] -version = "1.2.1" +[[exemptions.plotters-svg]] +version = "0.3.5" +criteria = "safe-to-run" + +[[exemptions.png]] +version = "0.17.10" criteria = "safe-to-deploy" -[[exemptions.raw-window-handle]] -version = "0.4.3" -criteria = "safe-to-run" +[[exemptions.polling]] +version = "3.3.1" +criteria = "safe-to-deploy" -[[exemptions.raw-window-handle]] -version = "0.5.2" +[[exemptions.proc-macro-crate]] +version = "3.0.0" criteria = "safe-to-deploy" [[exemptions.regex-syntax]] @@ -542,172 +439,136 @@ criteria = "safe-to-deploy" version = "0.4.0" criteria = "safe-to-deploy" -[[exemptions.safe_arch]] -version = "0.5.2" -criteria = "safe-to-run" - [[exemptions.scoped-tls]] version = "1.0.1" criteria = "safe-to-deploy" -[[exemptions.sctk-adwaita]] -version = "0.4.3" -criteria = "safe-to-run" - -[[exemptions.sctk-adwaita]] -version = "0.5.4" -criteria = "safe-to-deploy" - -[[exemptions.servo-fontconfig]] -version = "0.5.1" -criteria = "safe-to-run" - -[[exemptions.servo-fontconfig-sys]] -version = "5.1.0" -criteria = "safe-to-run" - -[[exemptions.shared_library]] -version = "0.1.9" -criteria = "safe-to-run" - [[exemptions.simd-adler32]] -version = "0.3.5" +version = "0.3.7" criteria = "safe-to-deploy" [[exemptions.simple_logger]] -version = "2.3.0" +version = "4.3.3" criteria = "safe-to-run" +[[exemptions.slab]] +version = "0.4.9" +criteria = "safe-to-deploy" + [[exemptions.slotmap]] -version = "1.0.6" +version = "1.0.7" criteria = "safe-to-deploy" [[exemptions.smithay-client-toolkit]] -version = "0.16.0" +version = "0.18.0" criteria = "safe-to-deploy" [[exemptions.smithay-clipboard]] -version = "0.6.6" +version = "0.7.0" criteria = "safe-to-deploy" -[[exemptions.str-buf]] -version = "1.0.6" +[[exemptions.smol_str]] +version = "0.2.0" criteria = "safe-to-deploy" -[[exemptions.strict-num]] -version = "0.1.1" +[[exemptions.str-buf]] +version = "1.0.6" criteria = "safe-to-deploy" -[[exemptions.strsim]] -version = "0.10.0" -criteria = "safe-to-run" - [[exemptions.system-deps]] -version = "6.1.0" +version = "6.2.0" criteria = "safe-to-deploy" -[[exemptions.takeable-option]] -version = "0.5.0" -criteria = "safe-to-run" - [[exemptions.target-lexicon]] -version = "0.12.7" +version = "0.12.13" criteria = "safe-to-deploy" [[exemptions.thiserror-core]] -version = "1.0.38" +version = "1.0.50" criteria = "safe-to-deploy" [[exemptions.thiserror-core-impl]] -version = "1.0.38" +version = "1.0.50" criteria = "safe-to-deploy" [[exemptions.time]] version = "0.3.22" criteria = "safe-to-deploy" -[[exemptions.time-core]] -version = "0.1.1" -criteria = "safe-to-deploy" - [[exemptions.time-macros]] version = "0.2.9" criteria = "safe-to-deploy" -[[exemptions.tiny-skia]] -version = "0.7.0" -criteria = "safe-to-run" - -[[exemptions.tiny-skia]] -version = "0.8.4" +[[exemptions.toml]] +version = "0.8.8" criteria = "safe-to-deploy" -[[exemptions.tiny-skia-path]] -version = "0.7.0" -criteria = "safe-to-run" - -[[exemptions.tiny-skia-path]] -version = "0.8.4" +[[exemptions.toml_datetime]] +version = "0.6.5" criteria = "safe-to-deploy" -[[exemptions.tinytemplate]] -version = "1.2.1" -criteria = "safe-to-run" - [[exemptions.toml_edit]] -version = "0.19.10" +version = "0.21.0" +criteria = "safe-to-deploy" + +[[exemptions.tracing]] +version = "0.1.40" criteria = "safe-to-deploy" -[[exemptions.twox-hash]] -version = "1.6.3" +[[exemptions.tracing-core]] +version = "0.1.21" criteria = "safe-to-deploy" -[[exemptions.unicode-bidi]] -version = "0.3.13" +[[exemptions.ttf-parser]] +version = "0.20.0" criteria = "safe-to-deploy" -[[exemptions.url]] +[[exemptions.walkdir]] version = "2.4.0" criteria = "safe-to-deploy" +[[exemptions.wayland-backend]] +version = "0.3.2" +criteria = "safe-to-deploy" + [[exemptions.wayland-client]] -version = "0.29.5" +version = "0.31.1" criteria = "safe-to-deploy" -[[exemptions.wayland-commons]] -version = "0.29.5" +[[exemptions.wayland-csd-frame]] +version = "0.3.0" criteria = "safe-to-deploy" [[exemptions.wayland-cursor]] -version = "0.29.5" +version = "0.31.0" criteria = "safe-to-deploy" -[[exemptions.wayland-egl]] -version = "0.29.5" -criteria = "safe-to-run" - [[exemptions.wayland-protocols]] -version = "0.29.5" +version = "0.31.0" criteria = "safe-to-deploy" -[[exemptions.wayland-scanner]] -version = "0.29.5" +[[exemptions.wayland-protocols-plasma]] +version = "0.2.0" criteria = "safe-to-deploy" -[[exemptions.wayland-sys]] -version = "0.29.5" +[[exemptions.wayland-protocols-wlr]] +version = "0.2.0" +criteria = "safe-to-deploy" + +[[exemptions.wayland-scanner]] +version = "0.31.0" criteria = "safe-to-deploy" [[exemptions.wayland-sys]] -version = "0.30.1" +version = "0.31.1" criteria = "safe-to-deploy" -[[exemptions.webbrowser]] -version = "0.8.10" +[[exemptions.web-time]] +version = "0.2.4" criteria = "safe-to-deploy" -[[exemptions.winapi]] -version = "0.3.9" +[[exemptions.webbrowser]] +version = "0.8.12" criteria = "safe-to-deploy" [[exemptions.winapi-i686-pc-windows-gnu]] @@ -723,69 +584,77 @@ version = "0.4.0" criteria = "safe-to-deploy" [[exemptions.windows-targets]] -version = "0.42.1" +version = "0.42.2" criteria = "safe-to-deploy" [[exemptions.windows-targets]] -version = "0.48.0" +version = "0.48.5" +criteria = "safe-to-deploy" + +[[exemptions.windows-targets]] +version = "0.52.0" criteria = "safe-to-deploy" [[exemptions.windows_aarch64_gnullvm]] -version = "0.42.0" +version = "0.42.2" criteria = "safe-to-deploy" [[exemptions.windows_aarch64_gnullvm]] -version = "0.48.0" +version = "0.48.5" +criteria = "safe-to-deploy" + +[[exemptions.windows_aarch64_gnullvm]] +version = "0.52.0" criteria = "safe-to-deploy" [[exemptions.windows_x86_64_gnullvm]] -version = "0.42.0" +version = "0.42.2" criteria = "safe-to-deploy" [[exemptions.windows_x86_64_gnullvm]] -version = "0.48.0" +version = "0.48.5" criteria = "safe-to-deploy" -[[exemptions.winit]] -version = "0.27.5" -criteria = "safe-to-run" +[[exemptions.windows_x86_64_gnullvm]] +version = "0.52.0" +criteria = "safe-to-deploy" [[exemptions.winit]] -version = "0.28.6" +version = "0.29.4" criteria = "safe-to-deploy" -[[exemptions.wio]] -version = "0.2.2" -criteria = "safe-to-run" - [[exemptions.x11-dl]] version = "2.21.0" criteria = "safe-to-deploy" [[exemptions.x11rb]] -version = "0.10.1" +version = "0.12.0" criteria = "safe-to-deploy" [[exemptions.x11rb-protocol]] -version = "0.10.0" +version = "0.12.0" criteria = "safe-to-deploy" [[exemptions.xcursor]] -version = "0.3.4" +version = "0.3.5" +criteria = "safe-to-deploy" + +[[exemptions.xkbcommon-dl]] +version = "0.4.1" +criteria = "safe-to-deploy" + +[[exemptions.xkeysym]] +version = "0.2.0" criteria = "safe-to-deploy" [[exemptions.xml-rs]] -version = "0.8.14" +version = "0.8.19" criteria = "safe-to-deploy" [[exemptions.zstd]] -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" criteria = "safe-to-deploy" [[exemptions.zstd-safe]] -version = "6.0.5+zstd.1.5.4" -criteria = "safe-to-deploy" - -[[exemptions.zstd-sys]] -version = "2.0.8+zstd.1.5.5" +version = "6.0.6" criteria = "safe-to-deploy" diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index a0461e2f..634adf2e 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -2,53 +2,46 @@ # cargo-vet imports lock [[publisher.bumpalo]] -version = "3.13.0" -when = "2023-05-22" +version = "3.14.0" +when = "2023-09-14" user-id = 696 user-login = "fitzgen" user-name = "Nick Fitzgerald" [[publisher.byteorder]] -version = "1.4.3" -when = "2021-03-10" +version = "1.5.0" +when = "2023-10-06" user-id = 189 user-login = "BurntSushi" user-name = "Andrew Gallant" +[[publisher.bytes]] +version = "1.5.0" +when = "2023-09-07" +user-id = 6741 +user-login = "Darksonn" +user-name = "Alice Ryhl" + [[publisher.cfg-expr]] -version = "0.15.2" -when = "2023-06-02" +version = "0.15.6" +when = "2024-01-02" user-id = 52553 user-login = "embark-studios" [[publisher.clap]] -version = "4.3.2" -when = "2023-06-05" +version = "4.4.14" +when = "2024-01-08" user-id = 6743 user-login = "epage" user-name = "Ed Page" [[publisher.clap_builder]] -version = "4.3.1" -when = "2023-06-02" +version = "4.4.14" +when = "2024-01-08" user-id = 6743 user-login = "epage" user-name = "Ed Page" -[[publisher.cocoa]] -version = "0.24.1" -when = "2022-11-01" -user-id = 5946 -user-login = "jrmuizel" -user-name = "Jeff Muizelaar" - -[[publisher.cocoa-foundation]] -version = "0.1.1" -when = "2023-03-16" -user-id = 5946 -user-login = "jrmuizel" -user-name = "Jeff Muizelaar" - [[publisher.core-foundation]] version = "0.9.3" when = "2022-02-07" @@ -77,125 +70,104 @@ user-id = 2396 user-login = "jdm" user-name = "Josh Matthews" -[[publisher.core-text]] -version = "19.2.0" -when = "2021-02-14" -user-id = 5946 -user-login = "jrmuizel" -user-name = "Jeff Muizelaar" - [[publisher.ecolor]] -version = "0.22.0" -when = "2023-05-23" +version = "0.27.1" +when = "2024-03-29" user-id = 5619 user-login = "emilk" user-name = "Emil Ernerfeldt" [[publisher.eframe]] -version = "0.22.0" -when = "2023-05-23" +version = "0.27.1" +when = "2024-03-29" user-id = 5619 user-login = "emilk" user-name = "Emil Ernerfeldt" [[publisher.egui]] -version = "0.22.0" -when = "2023-05-23" +version = "0.27.1" +when = "2024-03-29" user-id = 5619 user-login = "emilk" user-name = "Emil Ernerfeldt" [[publisher.egui-winit]] -version = "0.22.0" -when = "2023-05-23" +version = "0.27.1" +when = "2024-03-29" user-id = 5619 user-login = "emilk" user-name = "Emil Ernerfeldt" [[publisher.egui_glow]] -version = "0.22.0" -when = "2023-05-23" +version = "0.27.1" +when = "2024-03-29" user-id = 5619 user-login = "emilk" user-name = "Emil Ernerfeldt" [[publisher.emath]] -version = "0.22.0" -when = "2023-05-23" +version = "0.27.1" +when = "2024-03-29" user-id = 5619 user-login = "emilk" user-name = "Emil Ernerfeldt" +[[publisher.enumn]] +version = "0.1.13" +when = "2024-01-02" +user-id = 3618 +user-login = "dtolnay" +user-name = "David Tolnay" + [[publisher.epaint]] -version = "0.22.0" -when = "2023-05-23" +version = "0.27.1" +when = "2024-03-29" user-id = 5619 user-login = "emilk" user-name = "Emil Ernerfeldt" [[publisher.indexmap]] -version = "1.9.3" -when = "2023-03-24" +version = "2.1.0" +when = "2023-10-31" user-id = 539 user-login = "cuviper" user-name = "Josh Stone" [[publisher.itoa]] -version = "1.0.6" -when = "2023-03-03" +version = "1.0.10" +when = "2023-12-09" user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" -[[publisher.jobserver]] -version = "0.1.26" -when = "2023-02-28" -user-id = 1 -user-login = "alexcrichton" -user-name = "Alex Crichton" - [[publisher.js-sys]] -version = "0.3.63" -when = "2023-05-15" +version = "0.3.66" +when = "2023-11-27" user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" -[[publisher.libc]] -version = "0.2.146" -when = "2023-06-06" -user-id = 2915 -user-login = "Amanieu" -user-name = "Amanieu d'Antras" - [[publisher.linux-raw-sys]] -version = "0.3.8" -when = "2023-05-19" +version = "0.4.12" +when = "2023-11-30" user-id = 6825 user-login = "sunfishcode" user-name = "Dan Gohman" [[publisher.lock_api]] -version = "0.4.10" -when = "2023-06-05" +version = "0.4.11" +when = "2023-10-17" user-id = 2915 user-login = "Amanieu" user-name = "Amanieu d'Antras" [[publisher.memchr]] -version = "2.5.0" -when = "2022-04-30" +version = "2.7.1" +when = "2023-12-28" user-id = 189 user-login = "BurntSushi" user-name = "Andrew Gallant" -[[publisher.num_cpus]] -version = "1.15.0" -when = "2022-12-20" -user-id = 359 -user-login = "seanmonstar" -user-name = "Sean McArthur" - [[publisher.parking_lot]] version = "0.12.1" when = "2022-05-31" @@ -204,29 +176,29 @@ user-login = "Amanieu" user-name = "Amanieu d'Antras" [[publisher.parking_lot_core]] -version = "0.9.8" -when = "2023-06-05" +version = "0.9.9" +when = "2023-10-17" user-id = 2915 user-login = "Amanieu" user-name = "Amanieu d'Antras" [[publisher.proc-macro2]] -version = "1.0.60" -when = "2023-06-08" +version = "1.0.76" +when = "2024-01-06" user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" [[publisher.rayon]] -version = "1.7.0" -when = "2023-03-04" +version = "1.8.0" +when = "2023-09-20" user-id = 539 user-login = "cuviper" user-name = "Josh Stone" [[publisher.rayon-core]] -version = "1.11.0" -when = "2023-03-04" +version = "1.12.0" +when = "2023-09-20" user-id = 539 user-login = "cuviper" user-name = "Josh Stone" @@ -239,120 +211,134 @@ user-login = "BurntSushi" user-name = "Andrew Gallant" [[publisher.rustix]] -version = "0.37.19" -when = "2023-05-04" +version = "0.38.28" +when = "2023-12-09" user-id = 6825 user-login = "sunfishcode" user-name = "Dan Gohman" [[publisher.ryu]] -version = "1.0.13" -when = "2023-03-03" +version = "1.0.16" +when = "2023-12-09" user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" [[publisher.serde]] -version = "1.0.164" -when = "2023-06-08" +version = "1.0.195" +when = "2024-01-06" user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" [[publisher.serde_derive]] -version = "1.0.164" -when = "2023-06-08" +version = "1.0.195" +when = "2024-01-06" user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" [[publisher.serde_json]] -version = "1.0.96" -when = "2023-04-12" +version = "1.0.111" +when = "2024-01-04" user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" [[publisher.serde_spanned]] -version = "0.6.2" -when = "2023-05-18" +version = "0.6.5" +when = "2023-12-19" user-id = 6743 user-login = "epage" user-name = "Ed Page" [[publisher.smallvec]] -version = "1.10.0" -when = "2022-10-02" +version = "1.11.2" +when = "2023-11-09" user-id = 2017 user-login = "mbrubeck" user-name = "Matt Brubeck" [[publisher.syn]] -version = "1.0.109" -when = "2023-02-24" +version = "2.0.48" +when = "2024-01-04" user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" -[[publisher.syn]] -version = "2.0.18" -when = "2023-05-26" +[[publisher.thiserror]] +version = "1.0.56" +when = "2024-01-02" +user-id = 3618 +user-login = "dtolnay" +user-name = "David Tolnay" + +[[publisher.thiserror-impl]] +version = "1.0.56" +when = "2024-01-02" user-id = 3618 user-login = "dtolnay" user-name = "David Tolnay" +[[publisher.unicode-segmentation]] +version = "1.10.1" +when = "2023-01-31" +user-id = 1139 +user-login = "Manishearth" +user-name = "Manish Goregaokar" + [[publisher.wasm-bindgen]] -version = "0.2.86" -when = "2023-05-15" +version = "0.2.89" +when = "2023-11-27" user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" [[publisher.wasm-bindgen-backend]] -version = "0.2.86" -when = "2023-05-15" +version = "0.2.89" +when = "2023-11-27" user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" [[publisher.wasm-bindgen-futures]] -version = "0.4.36" -when = "2023-05-15" +version = "0.4.39" +when = "2023-11-27" user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" [[publisher.wasm-bindgen-macro]] -version = "0.2.86" -when = "2023-05-15" +version = "0.2.89" +when = "2023-11-27" user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" [[publisher.wasm-bindgen-macro-support]] -version = "0.2.86" -when = "2023-05-15" +version = "0.2.89" +when = "2023-11-27" user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" [[publisher.wasm-bindgen-shared]] -version = "0.2.86" -when = "2023-05-15" +version = "0.2.89" +when = "2023-11-27" user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" [[publisher.web-sys]] -version = "0.3.63" -when = "2023-05-15" +version = "0.3.66" +when = "2023-11-28" user-id = 1 user-login = "alexcrichton" user-name = "Alex Crichton" [[publisher.winapi-util]] -version = "0.1.5" -when = "2020-04-20" +version = "0.1.6" +when = "2023-09-20" user-id = 189 user-login = "BurntSushi" user-name = "Andrew Gallant" @@ -364,13 +350,6 @@ user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" -[[publisher.windows-sys]] -version = "0.36.1" -when = "2022-04-27" -user-id = 64539 -user-login = "kennykerr" -user-name = "Kenny Kerr" - [[publisher.windows-sys]] version = "0.45.0" when = "2023-01-21" @@ -385,9 +364,9 @@ user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" -[[publisher.windows_aarch64_msvc]] -version = "0.36.1" -when = "2022-04-27" +[[publisher.windows-sys]] +version = "0.52.0" +when = "2023-11-15" user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" @@ -407,15 +386,15 @@ user-login = "kennykerr" user-name = "Kenny Kerr" [[publisher.windows_aarch64_msvc]] -version = "0.48.0" -when = "2023-03-31" +version = "0.48.5" +when = "2023-08-18" user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" -[[publisher.windows_i686_gnu]] -version = "0.36.1" -when = "2022-04-27" +[[publisher.windows_aarch64_msvc]] +version = "0.52.0" +when = "2023-11-15" user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" @@ -435,15 +414,15 @@ user-login = "kennykerr" user-name = "Kenny Kerr" [[publisher.windows_i686_gnu]] -version = "0.48.0" -when = "2023-03-31" +version = "0.48.5" +when = "2023-08-18" user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" -[[publisher.windows_i686_msvc]] -version = "0.36.1" -when = "2022-04-27" +[[publisher.windows_i686_gnu]] +version = "0.52.0" +when = "2023-11-15" user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" @@ -463,15 +442,15 @@ user-login = "kennykerr" user-name = "Kenny Kerr" [[publisher.windows_i686_msvc]] -version = "0.48.0" -when = "2023-03-31" +version = "0.48.5" +when = "2023-08-18" user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" -[[publisher.windows_x86_64_gnu]] -version = "0.36.1" -when = "2022-04-27" +[[publisher.windows_i686_msvc]] +version = "0.52.0" +when = "2023-11-15" user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" @@ -491,15 +470,15 @@ user-login = "kennykerr" user-name = "Kenny Kerr" [[publisher.windows_x86_64_gnu]] -version = "0.48.0" -when = "2023-03-31" +version = "0.48.5" +when = "2023-08-18" user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" -[[publisher.windows_x86_64_msvc]] -version = "0.36.1" -when = "2022-04-27" +[[publisher.windows_x86_64_gnu]] +version = "0.52.0" +when = "2023-11-15" user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" @@ -519,43 +498,33 @@ user-login = "kennykerr" user-name = "Kenny Kerr" [[publisher.windows_x86_64_msvc]] -version = "0.48.0" -when = "2023-03-31" +version = "0.48.5" +when = "2023-08-18" +user-id = 64539 +user-login = "kennykerr" +user-name = "Kenny Kerr" + +[[publisher.windows_x86_64_msvc]] +version = "0.52.0" +when = "2023-11-15" user-id = 64539 user-login = "kennykerr" user-name = "Kenny Kerr" [[publisher.winnow]] -version = "0.4.6" -when = "2023-05-02" +version = "0.5.34" +when = "2024-01-10" user-id = 6743 user-login = "epage" user-name = "Ed Page" -[[audits.embark.audits.ab_glyph]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "0.2.21" -notes = "No unsafe usage or ambient capabilities" - -[[audits.embark.audits.android-activity]] -who = "Robert Bragg " +[[audits.embark.wildcard-audits.cfg-expr]] +who = "Jake Shadle " criteria = "safe-to-deploy" -version = "0.4.1" -notes = """ -Some unsafe usage for JNI/FFI, such as implementing extern \"C\" functions for -NativeActivity and to use the `ndk_sys` FFI bindings for the Android NDK libraries. - -The GameActivity backend depends on around 2k lines of third-party C/C++ code from Google -as well as around 500 lines of C++ code for the GameText (input method) support. -The C/C++ code is compiled with the `cc` crate. - -Although I have reviewed all of the C/C++ code for GameActivity + GameText there -could be unknown soundness issues in there or potentially in any of the Android -NDK APIs used, which are generally also implemented in C/C++. - -Written by Robert Bragg who now works at Embark Studios. -""" +user-id = 52553 # embark-studios +start = "2020-01-01" +end = "2024-05-23" +notes = "Maintained by Embark. No unsafe usage or ambient capabilities" [[audits.embark.audits.cfg_aliases]] who = "Johan Andersson " @@ -563,24 +532,12 @@ criteria = "safe-to-deploy" version = "0.1.1" notes = "No unsafe usage or ambient capabilities" -[[audits.embark.audits.cty]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "0.2.2" -notes = "Inspected it and is a tiny crate with just type definitions" - [[audits.embark.audits.epaint]] who = "Johan Andersson " criteria = "safe-to-deploy" violation = "<0.20.0" notes = "Specified crate license does not include licenses of embedded fonts if using default features or the `default_fonts` feature. Tracked in: https://github.com/emilk/egui/issues/2321" -[[audits.embark.audits.ident_case]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "1.0.1" -notes = "No unsafe usage or ambient capabilities" - [[audits.embark.audits.idna]] who = "Johan Andersson " criteria = "safe-to-deploy" @@ -636,12 +593,6 @@ https://github.com/signalapp/libsignal/blob/main/rust/bridge/jni/Cargo.toml code may expose the JVM to invalid booleans with undefined behaviour """ -[[audits.embark.audits.mimalloc]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "0.1.37" -notes = "Tiny allocator layer on top of the C sys crate. No ambient capabilities" - [[audits.embark.audits.natord]] who = "Johan Andersson " criteria = "safe-to-deploy" @@ -660,72 +611,18 @@ criteria = "safe-to-deploy" version = "0.2.0" notes = "Tiny crate with no dependencies, no unsafe, and no ambient capabilities" -[[audits.embark.audits.num_enum]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "0.5.11" -notes = "No unsafe usage or ambient capabilities" - -[[audits.embark.audits.num_enum_derive]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "0.5.11" -notes = "Proc macro that generates some unsafe code for conversion but looks sound, no ambient capabilities" - -[[audits.embark.audits.png]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "0.17.8" -notes = "Forbids unsafe, no ambient capabilities" - -[[audits.embark.audits.thiserror]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "1.0.40" -notes = "Wrapper over implementation crate, found no unsafe or ambient capabilities used" - -[[audits.embark.audits.thiserror-impl]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "1.0.40" -notes = "Found no unsafe or ambient capabilities used" - [[audits.embark.audits.tinyvec_macros]] who = "Johan Andersson " criteria = "safe-to-deploy" version = "0.1.0" notes = "Inspected it and is a tiny crate with single safe macro" -[[audits.embark.audits.toml]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "0.7.4" -notes = "No unsafe usage or ambient capabilities" - -[[audits.embark.audits.toml_datetime]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -delta = "0.6.1 -> 0.6.2" -notes = "No notable changes" - -[[audits.embark.audits.ttf-parser]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "0.19.0" -notes = "No unsafe usage (forbidden) or ambient capabilities" - [[audits.embark.audits.vec1]] who = "Johan Andersson " criteria = "safe-to-deploy" version = "1.10.1" notes = "No unsafe usage or ambient capabilities" -[[audits.embark.audits.vec_map]] -who = "Johan Andersson " -criteria = "safe-to-deploy" -version = "0.8.2" -notes = "No unsafe usage or ambient capabilities" - [[audits.embark.audits.version-compare]] who = "Johan Andersson " criteria = "safe-to-deploy" @@ -737,29 +634,6 @@ who = "Radu Matei " criteria = "safe-to-run" version = "11.1.3" -[[audits.fermyon.audits.plotters-svg]] -who = "Radu Matei " -criteria = "safe-to-run" -version = "0.3.3" - -[[audits.firefox.wildcard-audits.cocoa]] -who = "Bobby Holley " -criteria = "safe-to-deploy" -user-id = 5946 # Jeff Muizelaar (jrmuizel) -start = "2022-11-01" -end = "2023-05-04" -renew = false -notes = "I've reviewed every source contribution that was neither authored nor reviewed by Mozilla." - -[[audits.firefox.wildcard-audits.cocoa-foundation]] -who = "Bobby Holley " -criteria = "safe-to-deploy" -user-id = 5946 # Jeff Muizelaar (jrmuizel) -start = "2023-03-16" -end = "2023-05-04" -renew = false -notes = "I've reviewed every source contribution that was neither authored nor reviewed by Mozilla." - [[audits.firefox.wildcard-audits.core-foundation]] who = "Bobby Holley " criteria = "safe-to-deploy" @@ -796,14 +670,13 @@ end = "2023-05-04" renew = false notes = "I've reviewed every source contribution that was neither authored nor reviewed by Mozilla." -[[audits.firefox.wildcard-audits.core-text]] -who = "Bobby Holley " +[[audits.firefox.wildcard-audits.unicode-segmentation]] +who = "Manish Goregaokar " criteria = "safe-to-deploy" -user-id = 5946 # Jeff Muizelaar (jrmuizel) -start = "2021-02-14" -end = "2023-05-04" -renew = false -notes = "I've reviewed every source contribution that was neither authored nor reviewed by Mozilla." +user-id = 1139 # Manish Goregaokar (Manishearth) +start = "2019-05-15" +end = "2024-05-03" +notes = "All code written or reviewed by Manish" [[audits.firefox.audits.anyhow]] who = "Mike Hommey " @@ -816,22 +689,82 @@ criteria = "safe-to-deploy" version = "1.1.0" notes = "All code written or reviewed by Josh Stone." -[[audits.firefox.audits.dwrote]] -who = "Bobby Holley " +[[audits.firefox.audits.bitflags]] +who = "Teodor Tanasoaia " criteria = "safe-to-deploy" -version = "0.11.0" -notes = "All code written or reviewed by Mozilla staff." +delta = "2.2.1 -> 2.3.2" -[[audits.firefox.audits.env_logger]] -who = "Nicolas Silva " +[[audits.firefox.audits.cc]] +who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.9.3 -> 0.10.0" +delta = "1.0.73 -> 1.0.78" -[[audits.firefox.audits.fnv]] -who = "Bobby Holley " +[[audits.firefox.audits.core-foundation]] +who = "Teodor Tanasoaia " +criteria = "safe-to-deploy" +delta = "0.9.3 -> 0.9.4" +notes = "I've reviewed every source contribution that was neither authored nor reviewed by Mozilla." + +[[audits.firefox.audits.core-graphics]] +who = "Teodor Tanasoaia " +criteria = "safe-to-deploy" +delta = "0.22.3 -> 0.23.1" + +[[audits.firefox.audits.core-graphics-types]] +who = "Teodor Tanasoaia " +criteria = "safe-to-deploy" +delta = "0.1.1 -> 0.1.2" + +[[audits.firefox.audits.core-graphics-types]] +who = "Teodor Tanasoaia " +criteria = "safe-to-deploy" +delta = "0.1.2 -> 0.1.3" +notes = "I've reviewed every source contribution that was neither authored nor reviewed by Mozilla." + +[[audits.firefox.audits.crossbeam-utils]] +who = "Mike Hommey " +criteria = "safe-to-deploy" +delta = "0.8.8 -> 0.8.11" + +[[audits.firefox.audits.crossbeam-utils]] +who = "Mike Hommey " +criteria = "safe-to-deploy" +delta = "0.8.11 -> 0.8.14" + +[[audits.firefox.audits.document-features]] +who = "Erich Gubler " +criteria = "safe-to-deploy" +version = "0.2.8" + +[[audits.firefox.audits.errno]] +who = "Mike Hommey " +criteria = "safe-to-deploy" +delta = "0.3.1 -> 0.3.3" + +[[audits.firefox.audits.foreign-types]] +who = "Teodor Tanasoaia " +criteria = "safe-to-deploy" +delta = "0.3.2 -> 0.5.0" + +[[audits.firefox.audits.foreign-types-macros]] +who = "Teodor Tanasoaia " +criteria = "safe-to-deploy" +version = "0.2.3" + +[[audits.firefox.audits.foreign-types-shared]] +who = "Teodor Tanasoaia " +criteria = "safe-to-deploy" +delta = "0.1.1 -> 0.3.1" + +[[audits.firefox.audits.form_urlencoded]] +who = "Valentin Gosu " +criteria = "safe-to-deploy" +version = "1.2.0" + +[[audits.firefox.audits.form_urlencoded]] +who = "Valentin Gosu " criteria = "safe-to-deploy" -version = "1.0.7" -notes = "Simple hasher implementation with no unsafe code." +delta = "1.2.0 -> 1.2.1" [[audits.firefox.audits.half]] who = "John M. Schanck " @@ -843,26 +776,20 @@ format. I've reviewed these and found no issues. There are no uses of ambient capabilities. """ -[[audits.firefox.audits.hashbrown]] -who = "Mike Hommey " -criteria = "safe-to-deploy" -version = "0.12.3" -notes = "This version is used in rust's libstd, so effectively we're already trusting it" - [[audits.firefox.audits.heck]] who = "Mike Hommey " criteria = "safe-to-deploy" delta = "0.4.0 -> 0.4.1" -[[audits.firefox.audits.hermit-abi]] -who = "Mike Hommey " +[[audits.firefox.audits.idna]] +who = "Valentin Gosu " criteria = "safe-to-deploy" -delta = "0.1.19 -> 0.2.6" +delta = "0.4.0 -> 0.5.0" -[[audits.firefox.audits.libloading]] -who = "Mike Hommey " +[[audits.firefox.audits.litrs]] +who = "Erich Gubler " criteria = "safe-to-deploy" -delta = "0.7.3 -> 0.7.4" +version = "0.4.1" [[audits.firefox.audits.log]] who = "Mike Hommey " @@ -880,26 +807,15 @@ not entirely certain is technically sound, but in either case I am reasonably co it's not exploitable. """ -[[audits.firefox.audits.memoffset]] -who = "Gabriele Svelto " -criteria = "safe-to-deploy" -delta = "0.6.5 -> 0.7.1" - -[[audits.firefox.audits.nix]] -who = "Gabriele Svelto " -criteria = "safe-to-deploy" -delta = "0.15.0 -> 0.25.0" -notes = "Plenty of new bindings but also several important bug fixes (including buffer overflows). New unsafe sections are restricted to wrappers and are no more dangerous than calling the C functions." - -[[audits.firefox.audits.nix]] +[[audits.firefox.audits.memmap2]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "0.25.0 -> 0.25.1" +delta = "0.8.0 -> 0.9.3" -[[audits.firefox.audits.nom]] -who = "Mike Hommey " +[[audits.firefox.audits.memoffset]] +who = "Gabriele Svelto " criteria = "safe-to-deploy" -delta = "7.1.1 -> 7.1.3" +delta = "0.6.5 -> 0.7.1" [[audits.firefox.audits.num-integer]] who = "Josh Stone " @@ -919,15 +835,83 @@ criteria = "safe-to-deploy" version = "0.2.15" notes = "All code written or reviewed by Josh Stone." -[[audits.firefox.audits.termcolor]] +[[audits.firefox.audits.percent-encoding]] +who = "Valentin Gosu " +criteria = "safe-to-deploy" +delta = "2.2.0 -> 2.3.0" + +[[audits.firefox.audits.percent-encoding]] +who = "Valentin Gosu " +criteria = "safe-to-deploy" +delta = "2.3.0 -> 2.3.1" + +[[audits.firefox.audits.raw-window-handle]] +who = "Jim Blandy " +criteria = "safe-to-deploy" +version = "0.5.0" +notes = "I looked through all the sources of the v0.5.0 crate." + +[[audits.firefox.audits.raw-window-handle]] who = "Mike Hommey " criteria = "safe-to-deploy" -delta = "1.1.3 -> 1.2.0" +delta = "0.5.0 -> 0.5.2" -[[audits.google.audits.addr2line]] -who = "George Burgess IV " +[[audits.firefox.audits.raw-window-handle]] +who = "Nicolas Silva " +criteria = "safe-to-deploy" +delta = "0.5.2 -> 0.6.0" + +[[audits.firefox.audits.ron]] +who = "Mike Hommey " +criteria = "safe-to-deploy" +delta = "0.8.0 -> 0.8.1" + +[[audits.firefox.audits.time-core]] +who = "Kershaw Chang " +criteria = "safe-to-deploy" +version = "0.1.0" + +[[audits.firefox.audits.unicode-bidi]] +who = "Makoto Kato " +criteria = "safe-to-deploy" +delta = "0.3.8 -> 0.3.13" + +[[audits.firefox.audits.unicode-bidi]] +who = "Jonathan Kew " +criteria = "safe-to-deploy" +delta = "0.3.13 -> 0.3.14" +notes = "I am the author of the bulk of the upstream changes in this version, and also checked the remaining post-0.3.13 changes." + +[[audits.firefox.audits.url]] +who = "Valentin Gosu " +criteria = "safe-to-deploy" +version = "2.4.0" + +[[audits.firefox.audits.url]] +who = "Valentin Gosu " +criteria = "safe-to-deploy" +delta = "2.4.0 -> 2.4.1" + +[[audits.firefox.audits.url]] +who = "Valentin Gosu " +criteria = "safe-to-deploy" +delta = "2.4.1 -> 2.5.0" + +[[audits.google.audits.aho-corasick]] +who = "danakj@chromium.org" +criteria = "safe-to-deploy" +version = "1.1.2" +notes = """ +Reviewed in https://crrev.com/c/5171063 + +Previously reviewed during security review and the audit is grandparented in. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.anstyle]] +who = "Yu-An Wang " criteria = "safe-to-deploy" -version = "0.19.0" +version = "1.0.4" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" [[audits.google.audits.anyhow]] @@ -942,77 +926,76 @@ criteria = "safe-to-deploy" version = "0.1.10" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.argh_derive]] -who = "ChromeOS" +[[audits.google.audits.argh]] +who = "George Burgess IV " criteria = "safe-to-deploy" -version = "0.1.10" +delta = "0.1.10 -> 0.1.12" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.argh_shared]] +[[audits.google.audits.argh_derive]] who = "ChromeOS" criteria = "safe-to-deploy" version = "0.1.10" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.atty]] -who = "Android Legacy" -criteria = "safe-to-deploy" -version = "0.2.14" -aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" - -[[audits.google.audits.backtrace]] +[[audits.google.audits.argh_derive]] who = "George Burgess IV " criteria = "safe-to-deploy" -version = "0.3.67" +delta = "0.1.10 -> 0.1.12" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.base64]] -who = "George Burgess IV " +[[audits.google.audits.argh_shared]] +who = "ChromeOS" criteria = "safe-to-deploy" -version = "0.13.1" +version = "0.1.10" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.bytemuck]] +[[audits.google.audits.argh_shared]] who = "George Burgess IV " criteria = "safe-to-deploy" -version = "1.13.1" +delta = "0.1.10 -> 0.1.12" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.bytes]] -who = "George Burgess IV " +[[audits.google.audits.bitflags]] +who = "Dennis Kempin " criteria = "safe-to-deploy" -version = "1.4.0" +delta = "1.3.2 -> 2.2.1" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.cc]] +[[audits.google.audits.bitflags]] who = "George Burgess IV " criteria = "safe-to-deploy" -version = "1.0.79" +delta = "2.3.2 -> 2.4.0" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.cfg-if]] -who = "Android Legacy" +[[audits.google.audits.bytemuck_derive]] +who = "Bastian Kersting " criteria = "safe-to-deploy" -version = "1.0.0" +version = "1.5.0" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.clap_lex]] +[[audits.google.audits.cast]] who = "George Burgess IV " criteria = "safe-to-deploy" -version = "0.4.1" +version = "0.3.0" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.cmake]] +[[audits.google.audits.cfg-if]] who = "George Burgess IV " criteria = "safe-to-deploy" -version = "0.1.49" +version = "1.0.0" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.cmake]] -who = "George Burgess IV " +[[audits.google.audits.clap_lex]] +who = "danakj@chromium.org" criteria = "safe-to-deploy" -delta = "0.1.49 -> 0.1.50" -aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +version = "0.6.0" +notes = """ +Reviewed in https://crrev.com/c/5171063 + +Previously reviewed during security review and the audit is grandparented in. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" [[audits.google.audits.color_quant]] who = "George Burgess IV " @@ -1044,10 +1027,22 @@ criteria = "safe-to-deploy" version = "0.9.14" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.crossbeam-utils]] +[[audits.google.audits.crossbeam-epoch]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +delta = "0.9.14 -> 0.9.15" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.dirs-sys-next]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "0.1.2" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.downcast-rs]] who = "George Burgess IV " criteria = "safe-to-deploy" -version = "0.8.15" +version = "1.2.0" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" [[audits.google.audits.either]] @@ -1056,16 +1051,28 @@ criteria = "safe-to-deploy" version = "1.8.1" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.enumn]] +[[audits.google.audits.either]] who = "George Burgess IV " criteria = "safe-to-deploy" -version = "0.1.8" +delta = "1.8.1 -> 1.9.0" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.env_logger]] +[[audits.google.audits.equivalent]] who = "George Burgess IV " criteria = "safe-to-deploy" -version = "0.9.3" +version = "1.0.1" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.errno]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "0.2.8" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.errno]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +delta = "0.2.8 -> 0.3.1" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" [[audits.google.audits.flate2]] @@ -1086,10 +1093,29 @@ criteria = "safe-to-deploy" version = "0.1.1" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.gimli]] +[[audits.google.audits.getrandom]] +who = "Android Legacy" +criteria = "safe-to-deploy" +version = "0.2.2" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.getrandom]] +who = "David Koloski " +criteria = "safe-to-deploy" +delta = "0.2.2 -> 0.2.12" +notes = "Audited at https://fxrev.dev/932979" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.hashbrown]] +who = "Nicholas Bishop " +criteria = "safe-to-deploy" +version = "0.13.2" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.hashbrown]] who = "George Burgess IV " criteria = "safe-to-deploy" -version = "0.27.2" +delta = "0.13.2 -> 0.14.3" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" [[audits.google.audits.heck]] @@ -1116,10 +1142,28 @@ criteria = "safe-to-deploy" version = "1.4.0" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.libloading]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +version = "0.7.4" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.log]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +delta = "0.4.17 -> 0.4.20" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.memmap2]] +who = "Ying Hsu " +criteria = "safe-to-deploy" +version = "0.8.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.memoffset]] who = "George Burgess IV " criteria = "safe-to-deploy" -delta = "0.7.1 -> 0.8.0" +version = "0.6.5" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" [[audits.google.audits.miniz_oxide]] @@ -1134,6 +1178,32 @@ criteria = "safe-to-deploy" delta = "0.6.2 -> 0.7.1" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.nix]] +who = "David Koloski " +criteria = "safe-to-deploy" +version = "0.26.2" +notes = """ +Reviewed on https://fxrev.dev/780283 +Issues: +- https://github.com/nix-rust/nix/issues/1975 +- https://github.com/nix-rust/nix/issues/1977 +- https://github.com/nix-rust/nix/pull/1978 +- https://github.com/nix-rust/nix/pull/1979 +- https://github.com/nix-rust/nix/issues/1980 +- https://github.com/nix-rust/nix/issues/1981 +- https://github.com/nix-rust/nix/pull/1983 +- https://github.com/nix-rust/nix/issues/1990 +- https://github.com/nix-rust/nix/pull/1992 +- https://github.com/nix-rust/nix/pull/1993 +""" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.num-traits]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +delta = "0.2.15 -> 0.2.16" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.num_threads]] who = "George Burgess IV " criteria = "safe-to-deploy" @@ -1146,12 +1216,61 @@ criteria = "safe-to-deploy" version = "1.17.0" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.once_cell]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +delta = "1.17.0 -> 1.18.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.percent-encoding]] +who = "ChromeOS" +criteria = "safe-to-deploy" +version = "2.2.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.pin-project-lite]] +who = "ChromeOS" +criteria = "safe-to-deploy" +version = "0.2.9" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.pin-project-lite]] +who = "David Koloski " +criteria = "safe-to-deploy" +delta = "0.2.9 -> 0.2.13" +notes = "Audited at https://fxrev.dev/946396" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.quick-xml]] +who = "Matthew DeVore " +criteria = "safe-to-deploy" +version = "0.30.0" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.quote]] who = "ChromeOS" criteria = "safe-to-deploy" version = "1.0.23" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.quote]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +delta = "1.0.23 -> 1.0.26" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.quote]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +delta = "1.0.26 -> 1.0.28" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.quote]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +delta = "1.0.28 -> 1.0.31" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + [[audits.google.audits.same-file]] who = "Android Legacy" criteria = "safe-to-deploy" @@ -1171,9 +1290,40 @@ version = "1.1.0" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" [[audits.google.audits.termcolor]] -who = "George Burgess IV " +who = "danakj@chromium.org" +criteria = "safe-to-deploy" +version = "1.4.0" +notes = """ +Reviewed in https://crrev.com/c/5171063 + +Previously reviewed during security review and the audit is grandparented in. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.termcolor]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.4.0 -> 1.4.1" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.tinytemplate]] +who = "Ying Hsu " +criteria = "safe-to-deploy" +version = "1.2.1" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.tracing-core]] +who = "David Koloski " +criteria = "safe-to-deploy" +delta = "0.1.21 -> 0.1.31" +notes = "Reviewed on https://fxrev.dev/906816" +aggregated-from = "https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/third_party/rust_crates/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.twox-hash]] +who = "Dennis Kempin " criteria = "safe-to-deploy" -version = "1.1.3" +version = "1.6.3" +notes = "Non-cyptographic hashing function" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" [[audits.google.audits.unicode-normalization]] @@ -1188,49 +1338,98 @@ criteria = "safe-to-deploy" version = "0.9.4" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.google.audits.walkdir]] -who = "Android Legacy" +[[audits.google.audits.winapi]] +who = "danakj@chromium.org" +criteria = "safe-to-deploy" +version = "0.3.9" +notes = """ +Reviewed in https://crrev.com/c/5171063 + +Previously reviewed during security review and the audit is grandparented in. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.zerocopy]] +who = "ChromeOS" criteria = "safe-to-deploy" -version = "2.3.2" +version = "0.7.0-alpha.1" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.isrg.audits.anstyle]] -who = "Brandon Pitman " -criteria = "safe-to-run" -version = "1.0.0" +[[audits.google.audits.zerocopy]] +who = "Daniel Verkamp " +criteria = "safe-to-deploy" +delta = "0.7.0-alpha.1 -> 0.7.8" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" -[[audits.isrg.audits.clap_lex]] -who = "Brandon Pitman " -criteria = "safe-to-run" -delta = "0.4.1 -> 0.5.0" +[[audits.google.audits.zerocopy]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +delta = "0.7.8 -> 0.7.32" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.zerocopy-derive]] +who = "ChromeOS" +criteria = "safe-to-deploy" +version = "0.3.2" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.zerocopy-derive]] +who = "Daniel Verkamp " +criteria = "safe-to-deploy" +delta = "0.3.2 -> 0.7.8" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.zerocopy-derive]] +who = "George Burgess IV " +criteria = "safe-to-deploy" +delta = "0.7.8 -> 0.7.32" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" + +[[audits.google.audits.zstd-sys]] +who = "Matt Turner " +criteria = "safe-to-deploy" +version = "2.0.9+zstd.1.5.5" +notes = "Includes an implementation of xxhash (a non-cyptographic hashing function) as part of the zstd C sources" +aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" [[audits.isrg.audits.criterion]] who = "Brandon Pitman " criteria = "safe-to-run" delta = "0.4.0 -> 0.5.1" +[[audits.isrg.audits.num-traits]] +who = "Ameer Ghani " +criteria = "safe-to-deploy" +delta = "0.2.16 -> 0.2.17" + [[audits.isrg.audits.once_cell]] who = "Brandon Pitman " criteria = "safe-to-deploy" -delta = "1.17.1 -> 1.17.2" +delta = "1.18.0 -> 1.19.0" -[[audits.isrg.audits.once_cell]] -who = "David Cook " +[[audits.mozilla.audits.bitflags]] +who = "Jan-Erik Rediger " criteria = "safe-to-deploy" -delta = "1.17.2 -> 1.18.0" +delta = "2.4.0 -> 2.4.1" +notes = "Only allowing new clippy lints" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" -[[audits.mozilla.audits.log]] +[[audits.mozilla.audits.cc]] who = "Jan-Erik Rediger " criteria = "safe-to-deploy" -delta = "0.4.17 -> 0.4.18" -notes = "One dependency removed, others updated (which we don't rely on), some APIs (which we don't use) changed." +delta = "1.0.78 -> 1.0.83" aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" -[[audits.mozilla.audits.quote]] +[[audits.mozilla.audits.crossbeam-channel]] who = "Jan-Erik Rediger " criteria = "safe-to-deploy" -delta = "1.0.27 -> 1.0.28" -notes = "Enabled on wasm targets" +delta = "0.5.8 -> 0.5.11" +aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" + +[[audits.mozilla.audits.crossbeam-utils]] +who = "Jan-Erik Rediger " +criteria = "safe-to-deploy" +delta = "0.8.14 -> 0.8.19" aggregated-from = "https://raw.githubusercontent.com/mozilla/glean/main/supply-chain/audits.toml" [[audits.mozilla.audits.unicode-ident]] @@ -1247,6 +1446,12 @@ user-id = 696 # Nick Fitzgerald (fitzgen) start = "2019-03-16" end = "2024-03-10" +[[audits.wasmtime.audits.adler]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "1.0.2" +notes = "This is a small crate which forbids unsafe code and is a straightforward implementation of the adler hashing algorithm." + [[audits.wasmtime.audits.anes]] who = "Pat Hickey " criteria = "safe-to-deploy" @@ -1258,55 +1463,31 @@ who = "Pat Hickey " criteria = "safe-to-deploy" delta = "1.0.69 -> 1.0.71" -[[audits.wasmtime.audits.arrayref]] -who = "Nick Fitzgerald " -criteria = "safe-to-deploy" -version = "0.3.6" -notes = """ -Unsafe code, but its logic looks good to me. Necessary given what it is -doing. Well tested, has quickchecks. -""" - -[[audits.wasmtime.audits.arrayvec]] -who = "Nick Fitzgerald " -criteria = "safe-to-deploy" -version = "0.7.2" -notes = """ -Well documented invariants, good assertions for those invariants in unsafe code, -and tested with MIRI to boot. LGTM. -""" - -[[audits.wasmtime.audits.errno]] -who = "Dan Gohman " -criteria = "safe-to-deploy" -version = "0.3.0" -notes = "This crate uses libc and windows-sys APIs to get and set the raw OS error value." - -[[audits.wasmtime.audits.errno]] -who = "Dan Gohman " +[[audits.wasmtime.audits.cc]] +who = "Alex Crichton " criteria = "safe-to-deploy" -delta = "0.3.0 -> 0.3.1" -notes = "Just a dependency version bump and a bug fix for redox" +version = "1.0.73" +notes = "I am the author of this crate." -[[audits.wasmtime.audits.is-terminal]] +[[audits.wasmtime.audits.core-foundation-sys]] who = "Dan Gohman " criteria = "safe-to-deploy" -version = "0.4.7" +delta = "0.8.4 -> 0.8.6" notes = """ -The is-terminal implementation code is now sync'd up with the prototype -implementation in the Rust standard library. +The changes here are all typical bindings updates: new functions, types, and +constants. I have not audited all the bindings for ABI conformance. """ -[[audits.wasmtime.audits.quote]] -who = "Pat Hickey " +[[audits.wasmtime.audits.flate2]] +who = "Andrew Brown " criteria = "safe-to-deploy" -delta = "1.0.23 -> 1.0.27" +delta = "1.0.26 -> 1.0.28" +notes = "No new `unsafe` and no large changes in function. This diff is mostly refactoring with a lot of docs, CI, test changes. Adds some defensive clearing out of certain variables as a safeguard." -[[audits.wasmtime.audits.rustc-demangle]] -who = "Alex Crichton " +[[audits.wasmtime.audits.libloading]] +who = "Iceber Gu " criteria = "safe-to-deploy" -version = "0.1.21" -notes = "I am the author of this crate." +delta = "0.7.3 -> 0.8.1" [[audits.wasmtime.audits.tinyvec]] who = "Alex Crichton " @@ -1318,116 +1499,163 @@ without `unsafe`. Skimming the crate everything looks reasonable and what one would expect from idiomatic safe collections in Rust. """ +[[audits.wasmtime.audits.unicode-bidi]] +who = "Alex Crichton " +criteria = "safe-to-deploy" +version = "0.3.8" +notes = """ +This crate has no unsafe code and does not use `std::*`. Skimming the crate it +does not attempt to out of the bounds of what it's already supposed to be doing. +""" + [[audits.wasmtime.audits.unicode-ident]] who = "Pat Hickey " criteria = "safe-to-deploy" version = "1.0.8" -[[audits.wasmtime.audits.walkdir]] -who = "Andrew Brown " +[[audits.zcash.audits.ahash]] +who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "2.3.2 -> 2.3.3" -notes = "No significant changes: minor refactoring and removes the need to use `winapi`." +delta = "0.8.6 -> 0.8.7" +notes = "Build-time `stdsimd` detection is replaced with a nightly-only feature flag." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.arrayref]] -who = "Sean Bowe " +[[audits.zcash.audits.anyhow]] +who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "0.3.6 -> 0.3.7" +delta = "1.0.71 -> 1.0.75" +notes = """ +`unsafe` changes are migrating from `core::any::Demand` to `std::error::Request` when the +nightly features are available. +""" aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.once_cell]] -who = "Jack Grigg " +[[audits.zcash.audits.anyhow]] +who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "1.17.0 -> 1.17.1" +delta = "1.0.75 -> 1.0.77" notes = """ -Small refactor that reduces the overall amount of `unsafe` code. The new strict provenance -approach looks reasonable. +- Build script changes are to rerun cargo if the `RUSTC_BOOTSTRAP` env variable + changes, and enable a few more `rustc` config flags. +- Some `unsafe fn`s were altered to add `unsafe` blocks, to make the safety + contracts in the code clearer (instead of using the `unsafe fn`'s implicit + `unsafe` block); no actual `unsafe` changes were made. """ aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.proc-macro-crate]] -who = "Jack Grigg " +[[audits.zcash.audits.anyhow]] +who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "1.2.1 -> 1.3.0" -notes = "Migrates from `toml` to `toml_edit`." +delta = "1.0.77 -> 1.0.79" +notes = """ +Build script changes are to refactor the existing probe into a separate file +(which removes a filesystem write), and adjust how it gets rerun in response to +changes in the build environment. +""" aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.proc-macro-crate]] +[[audits.zcash.audits.crossbeam-deque]] who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "1.3.0 -> 1.3.1" -notes = "Bumps MSRV to 1.60." +delta = "0.8.3 -> 0.8.4" aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.rustc-demangle]] -who = "Sean Bowe " +[[audits.zcash.audits.crossbeam-deque]] +who = "Daira-Emma Hopwood " criteria = "safe-to-deploy" -delta = "0.1.21 -> 0.1.22" +delta = "0.8.4 -> 0.8.5" +notes = "Changes to `unsafe` code look okay." aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.rustc-demangle]] +[[audits.zcash.audits.crossbeam-epoch]] who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "0.1.22 -> 0.1.23" +delta = "0.9.15 -> 0.9.16" +notes = "Moved an `unsafe` block while removing `scopeguard` dependency." aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.tinyvec_macros]] -who = "Jack Grigg " +[[audits.zcash.audits.crossbeam-epoch]] +who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "0.1.0 -> 0.1.1" -notes = "Adds `#![forbid(unsafe_code)]` and license files." +delta = "0.9.16 -> 0.9.17" +notes = """ +Changes to `unsafe` code are to replace manual pointer logic with equivalent +`unsafe` stdlib methods, now that MSRV is high enough to use them. +""" aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.toml_datetime]] -who = "Jack Grigg " +[[audits.zcash.audits.crossbeam-epoch]] +who = "Daira-Emma Hopwood " criteria = "safe-to-deploy" -version = "0.5.1" -notes = "Crate has `#![forbid(unsafe_code)]`, no `unwrap / expect / panic`, no ambient capabilities." +delta = "0.9.17 -> 0.9.18" aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.toml_datetime]] +[[audits.zcash.audits.errno]] who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "0.5.1 -> 0.6.1" -notes = "Fixes a bug in parsing negative minutes in datetime string offsets." +delta = "0.3.3 -> 0.3.8" aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.windows-targets]] +[[audits.zcash.audits.nix]] who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "0.42.1 -> 0.42.2" +delta = "0.26.2 -> 0.26.4" +notes = """ +Most of the `unsafe` changes are cleaning up their usage: +- Replacing `data.len() * std::mem::size_of::<$ty>()` with `std::mem::size_of_val(data)`. +- Removing some `mem::transmute`s. +- Using `*mut` instead of `*const` to convey intended semantics. + +A new unsafe trait method `SockaddrLike::set_length` is added; it's impls look fine. +""" aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.windows_aarch64_gnullvm]] -who = "Jack Grigg " +[[audits.zcash.audits.quote]] +who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "0.42.0 -> 0.42.1" -notes = """ -This is a Windows API bindings library maintained by Microsoft themselves. -Changes are to a bundled binary library; it looks like these were accidentally left out of 0.42.0. -""" +delta = "1.0.31 -> 1.0.33" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.quote]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.0.33 -> 1.0.35" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.regex-syntax]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.7.2 -> 0.7.5" +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.scopeguard]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "1.1.0 -> 1.2.0" +notes = "Only change to an `unsafe` block is to replace a `mem::forget` with `ManuallyDrop`." aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.windows_aarch64_gnullvm]] +[[audits.zcash.audits.time-core]] who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "0.42.1 -> 0.42.2" -notes = "This is an opaque Windows API bindings library maintained by Microsoft." +delta = "0.1.0 -> 0.1.1" aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.windows_x86_64_gnullvm]] +[[audits.zcash.audits.tinyvec_macros]] who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "0.42.0 -> 0.42.1" -notes = """ -This is a Windows API bindings library maintained by Microsoft themselves. -Changes are to a bundled binary library; it looks like these were accidentally left out of 0.42.0. -""" +delta = "0.1.0 -> 0.1.1" +notes = "Adds `#![forbid(unsafe_code)]` and license files." +aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" + +[[audits.zcash.audits.tracing-core]] +who = "Jack Grigg " +criteria = "safe-to-deploy" +delta = "0.1.31 -> 0.1.32" aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml" -[[audits.zcash.audits.windows_x86_64_gnullvm]] +[[audits.zcash.audits.unicode-ident]] who = "Jack Grigg " criteria = "safe-to-deploy" -delta = "0.42.1 -> 0.42.2" -notes = "This is an opaque Windows API bindings library maintained by Microsoft." +delta = "1.0.9 -> 1.0.12" aggregated-from = "https://raw.githubusercontent.com/zcash/zcash/master/qa/supply-chain/audits.toml"